diff --git a/.devcontainer/.env.example b/.devcontainer/.env.example index 0b98c6c..6b4840d 100644 --- a/.devcontainer/.env.example +++ b/.devcontainer/.env.example @@ -1,26 +1,26 @@ -# ============================================================ -# Musher Dev Container Environment Configuration -# ============================================================ -# Copy this file to .env and customize as needed: -# cp .env.example .env -# ============================================================ - -# --- Service Profiles --- -# Comma-separated list of optional services to enable. -# Available: redis, minio, registry, azimutt, observability -COMPOSE_PROFILES= - -# --- PostgreSQL --- -POSTGRES_USER=postgres -POSTGRES_PASSWORD=postgres -POSTGRES_DB=app - -# --- MinIO (project-level S3 storage) --- -MINIO_ROOT_USER=minioadmin -MINIO_ROOT_PASSWORD=minioadmin - -# --- Observability MinIO --- -# NOTE: These must also match values in config/observability/tempo-config.yaml -# and config/observability/loki-config.yaml (those files cannot use env interpolation) -MINIO_OBS_ROOT_USER=minioadmin -MINIO_OBS_ROOT_PASSWORD=minioadmin +# ============================================================ +# Musher Dev Container Environment Configuration +# ============================================================ +# Copy this file to .env and customize as needed: +# cp .env.example .env +# ============================================================ + +# --- Service Profiles --- +# Comma-separated list of optional services to enable. +# Available: redis, minio, registry, azimutt, observability +COMPOSE_PROFILES= + +# --- PostgreSQL --- +POSTGRES_USER=postgres +POSTGRES_PASSWORD=postgres +POSTGRES_DB=app + +# --- MinIO (project-level S3 storage) --- +MINIO_ROOT_USER=minioadmin +MINIO_ROOT_PASSWORD=minioadmin + +# --- Observability MinIO --- +# NOTE: These must also match values in config/observability/tempo-config.yaml +# and config/observability/loki-config.yaml (those files cannot use env interpolation) +MINIO_OBS_ROOT_USER=minioadmin +MINIO_OBS_ROOT_PASSWORD=minioadmin diff --git a/.devcontainer/compose.yaml b/.devcontainer/compose.yaml index 1ff9e59..d11a61c 100644 --- a/.devcontainer/compose.yaml +++ b/.devcontainer/compose.yaml @@ -1,26 +1,26 @@ -# Musher Dev Container — Docker Compose orchestrator. -# Add service files under compose/ and include them here. -# Services run inside Docker-in-Docker via startup.sh. -# -# Port Allocation: -# 15432 PostgreSQL 15440 MinIO API (obs.) -# 15433 Redis 15441 MinIO Console (obs.) -# 15434 MinIO API 15442 Tempo -# 15435 MinIO Console 15443 Loki -# 15436 OCI Registry 15444 VictoriaMetrics -# 15460 Azimutt 15445 OTel HTTP -# 15446 OTel gRPC -# 15447 Grafana -# 15448 Pyroscope - -include: - - compose/postgres.yaml - - compose/redis.yaml - - compose/minio.yaml - - compose/registry.yaml - - compose/azimutt.yaml - - compose/observability.yaml - -networks: - default: - name: musher-dev +# Musher Dev Container — Docker Compose orchestrator. +# Add service files under compose/ and include them here. +# Services run inside Docker-in-Docker via startup.sh. +# +# Port Allocation: +# 15432 PostgreSQL 15440 MinIO API (obs.) +# 15433 Redis 15441 MinIO Console (obs.) +# 15434 MinIO API 15442 Tempo +# 15435 MinIO Console 15443 Loki +# 15436 OCI Registry 15444 VictoriaMetrics +# 15460 Azimutt 15445 OTel HTTP +# 15446 OTel gRPC +# 15447 Grafana +# 15448 Pyroscope + +include: + - compose/postgres.yaml + - compose/redis.yaml + - compose/minio.yaml + - compose/registry.yaml + - compose/azimutt.yaml + - compose/observability.yaml + +networks: + default: + name: musher-dev diff --git a/.devcontainer/compose/azimutt.yaml b/.devcontainer/compose/azimutt.yaml index 8258fa6..839d4b4 100644 --- a/.devcontainer/compose/azimutt.yaml +++ b/.devcontainer/compose/azimutt.yaml @@ -1,16 +1,16 @@ -# Azimutt — database explorer UI (profile-gated). -# Start with: docker compose --profile azimutt up -d - -services: - azimutt: - image: ghcr.io/azimuttapp/azimutt:main - platform: linux/amd64 - restart: unless-stopped - profiles: [azimutt] - ports: - - "127.0.0.1:15460:4000" - environment: - DATABASE_URL: postgresql://${POSTGRES_USER:-postgres}:${POSTGRES_PASSWORD:-postgres}@postgres:5432/${POSTGRES_DB:-app} - depends_on: - postgres: - condition: service_healthy +# Azimutt — database explorer UI (profile-gated). +# Start with: docker compose --profile azimutt up -d + +services: + azimutt: + image: ghcr.io/azimuttapp/azimutt:main + platform: linux/amd64 + restart: unless-stopped + profiles: [azimutt] + ports: + - "127.0.0.1:15460:4000" + environment: + DATABASE_URL: postgresql://${POSTGRES_USER:-postgres}:${POSTGRES_PASSWORD:-postgres}@postgres:5432/${POSTGRES_DB:-app} + depends_on: + postgres: + condition: service_healthy diff --git a/.devcontainer/compose/minio.yaml b/.devcontainer/compose/minio.yaml index c037a87..7db3df2 100644 --- a/.devcontainer/compose/minio.yaml +++ b/.devcontainer/compose/minio.yaml @@ -1,25 +1,25 @@ -# MinIO — S3-compatible object storage. - -services: - minio: - profiles: [minio] - image: minio/minio:latest - restart: unless-stopped - ports: - - "127.0.0.1:15434:9000" - - "127.0.0.1:15435:9001" - command: ["server", "/data", "--console-address", ":9001"] - environment: - MINIO_ROOT_USER: ${MINIO_ROOT_USER:-minioadmin} - MINIO_ROOT_PASSWORD: ${MINIO_ROOT_PASSWORD:-minioadmin} - volumes: - - musher-minio-data:/data - healthcheck: - test: ["CMD", "mc", "ready", "local"] - interval: 5s - timeout: 3s - retries: 5 - start_period: 10s - -volumes: - musher-minio-data: +# MinIO — S3-compatible object storage. + +services: + minio: + profiles: [minio] + image: minio/minio:latest + restart: unless-stopped + ports: + - "127.0.0.1:15434:9000" + - "127.0.0.1:15435:9001" + command: ["server", "/data", "--console-address", ":9001"] + environment: + MINIO_ROOT_USER: ${MINIO_ROOT_USER:-minioadmin} + MINIO_ROOT_PASSWORD: ${MINIO_ROOT_PASSWORD:-minioadmin} + volumes: + - musher-minio-data:/data + healthcheck: + test: ["CMD", "mc", "ready", "local"] + interval: 5s + timeout: 3s + retries: 5 + start_period: 10s + +volumes: + musher-minio-data: diff --git a/.devcontainer/compose/observability.yaml b/.devcontainer/compose/observability.yaml index 528f175..1109a7b 100644 --- a/.devcontainer/compose/observability.yaml +++ b/.devcontainer/compose/observability.yaml @@ -1,155 +1,155 @@ -# Observability stack (profile-gated). -# Start with: docker compose --profile observability up -d - -services: - minio-observability: - image: minio/minio:latest - profiles: [observability] - restart: unless-stopped - ports: - - "127.0.0.1:15440:9000" - - "127.0.0.1:15441:9001" - command: ["server", "/data", "--console-address", ":9001"] - environment: - MINIO_ROOT_USER: ${MINIO_OBS_ROOT_USER:-minioadmin} - MINIO_ROOT_PASSWORD: ${MINIO_OBS_ROOT_PASSWORD:-minioadmin} - volumes: - - musher-minio-observability-data:/data - healthcheck: - test: ["CMD", "mc", "ready", "local"] - interval: 5s - timeout: 3s - retries: 5 - start_period: 10s - - minio-observability-init: - image: minio/mc:latest - profiles: [observability] - depends_on: - minio-observability: - condition: service_healthy - entrypoint: > - /bin/sh -c " - mc alias set local http://minio-observability:9000 ${MINIO_OBS_ROOT_USER:-minioadmin} ${MINIO_OBS_ROOT_PASSWORD:-minioadmin} && - mc mb --ignore-existing local/tempo-traces && - mc mb --ignore-existing local/loki-data - " - restart: "no" - - tempo: - image: grafana/tempo:latest - profiles: [observability] - restart: unless-stopped - ports: - - "127.0.0.1:15442:3200" - command: ["-config.file=/etc/tempo/tempo-config.yaml"] - volumes: - - ../config/observability/tempo-config.yaml:/etc/tempo/tempo-config.yaml:ro - - musher-tempo-data:/var/tempo - depends_on: - minio-observability-init: - condition: service_completed_successfully - healthcheck: - test: ["CMD", "wget", "--spider", "-q", "http://localhost:3200/ready"] - interval: 5s - timeout: 3s - retries: 5 - start_period: 15s - - loki: - image: grafana/loki:latest - profiles: [observability] - restart: unless-stopped - ports: - - "127.0.0.1:15443:3100" - command: ["-config.file=/etc/loki/loki-config.yaml"] - volumes: - - ../config/observability/loki-config.yaml:/etc/loki/loki-config.yaml:ro - - musher-loki-data:/loki - depends_on: - minio-observability-init: - condition: service_completed_successfully - healthcheck: - test: ["CMD", "wget", "--spider", "-q", "http://localhost:3100/ready"] - interval: 5s - timeout: 3s - retries: 5 - start_period: 15s - - victoriametrics: - image: victoriametrics/victoria-metrics:latest - profiles: [observability] - restart: unless-stopped - ports: - - "127.0.0.1:15444:8428" - volumes: - - musher-victoriametrics-data:/victoria-metrics-data - healthcheck: - test: ["CMD", "wget", "--spider", "-q", "http://localhost:8428/health"] - interval: 5s - timeout: 3s - retries: 5 - start_period: 10s - - otel-collector: - image: otel/opentelemetry-collector-contrib:latest - profiles: [observability] - restart: unless-stopped - ports: - - "127.0.0.1:15445:4318" - - "127.0.0.1:15446:4317" - command: ["--config=/etc/otelcol/otel-collector-config.yaml"] - volumes: - - ../config/observability/otel-collector-config.yaml:/etc/otelcol/otel-collector-config.yaml:ro - depends_on: - tempo: - condition: service_healthy - loki: - condition: service_healthy - victoriametrics: - condition: service_healthy - - grafana: - image: grafana/grafana:latest - profiles: [observability] - restart: unless-stopped - ports: - - "127.0.0.1:15447:3000" - environment: - GF_AUTH_ANONYMOUS_ENABLED: "true" - GF_AUTH_ANONYMOUS_ORG_ROLE: Admin - GF_AUTH_DISABLE_LOGIN_FORM: "true" - GF_INSTALL_PLUGINS: grafana-pyroscope-app - volumes: - - ../config/observability/grafana/provisioning:/etc/grafana/provisioning:ro - - musher-grafana-data:/var/lib/grafana - depends_on: - tempo: - condition: service_healthy - loki: - condition: service_healthy - victoriametrics: - condition: service_healthy - - pyroscope: - image: grafana/pyroscope:latest - profiles: [observability] - restart: unless-stopped - ports: - - "127.0.0.1:15448:4040" - volumes: - - musher-pyroscope-data:/data - healthcheck: - test: ["CMD", "wget", "--spider", "-q", "http://localhost:4040/ready"] - interval: 5s - timeout: 3s - retries: 5 - start_period: 10s - -volumes: - musher-minio-observability-data: - musher-tempo-data: - musher-loki-data: - musher-victoriametrics-data: - musher-grafana-data: - musher-pyroscope-data: +# Observability stack (profile-gated). +# Start with: docker compose --profile observability up -d + +services: + minio-observability: + image: minio/minio:latest + profiles: [observability] + restart: unless-stopped + ports: + - "127.0.0.1:15440:9000" + - "127.0.0.1:15441:9001" + command: ["server", "/data", "--console-address", ":9001"] + environment: + MINIO_ROOT_USER: ${MINIO_OBS_ROOT_USER:-minioadmin} + MINIO_ROOT_PASSWORD: ${MINIO_OBS_ROOT_PASSWORD:-minioadmin} + volumes: + - musher-minio-observability-data:/data + healthcheck: + test: ["CMD", "mc", "ready", "local"] + interval: 5s + timeout: 3s + retries: 5 + start_period: 10s + + minio-observability-init: + image: minio/mc:latest + profiles: [observability] + depends_on: + minio-observability: + condition: service_healthy + entrypoint: > + /bin/sh -c " + mc alias set local http://minio-observability:9000 ${MINIO_OBS_ROOT_USER:-minioadmin} ${MINIO_OBS_ROOT_PASSWORD:-minioadmin} && + mc mb --ignore-existing local/tempo-traces && + mc mb --ignore-existing local/loki-data + " + restart: "no" + + tempo: + image: grafana/tempo:latest + profiles: [observability] + restart: unless-stopped + ports: + - "127.0.0.1:15442:3200" + command: ["-config.file=/etc/tempo/tempo-config.yaml"] + volumes: + - ../config/observability/tempo-config.yaml:/etc/tempo/tempo-config.yaml:ro + - musher-tempo-data:/var/tempo + depends_on: + minio-observability-init: + condition: service_completed_successfully + healthcheck: + test: ["CMD", "wget", "--spider", "-q", "http://localhost:3200/ready"] + interval: 5s + timeout: 3s + retries: 5 + start_period: 15s + + loki: + image: grafana/loki:latest + profiles: [observability] + restart: unless-stopped + ports: + - "127.0.0.1:15443:3100" + command: ["-config.file=/etc/loki/loki-config.yaml"] + volumes: + - ../config/observability/loki-config.yaml:/etc/loki/loki-config.yaml:ro + - musher-loki-data:/loki + depends_on: + minio-observability-init: + condition: service_completed_successfully + healthcheck: + test: ["CMD", "wget", "--spider", "-q", "http://localhost:3100/ready"] + interval: 5s + timeout: 3s + retries: 5 + start_period: 15s + + victoriametrics: + image: victoriametrics/victoria-metrics:latest + profiles: [observability] + restart: unless-stopped + ports: + - "127.0.0.1:15444:8428" + volumes: + - musher-victoriametrics-data:/victoria-metrics-data + healthcheck: + test: ["CMD", "wget", "--spider", "-q", "http://localhost:8428/health"] + interval: 5s + timeout: 3s + retries: 5 + start_period: 10s + + otel-collector: + image: otel/opentelemetry-collector-contrib:latest + profiles: [observability] + restart: unless-stopped + ports: + - "127.0.0.1:15445:4318" + - "127.0.0.1:15446:4317" + command: ["--config=/etc/otelcol/otel-collector-config.yaml"] + volumes: + - ../config/observability/otel-collector-config.yaml:/etc/otelcol/otel-collector-config.yaml:ro + depends_on: + tempo: + condition: service_healthy + loki: + condition: service_healthy + victoriametrics: + condition: service_healthy + + grafana: + image: grafana/grafana:latest + profiles: [observability] + restart: unless-stopped + ports: + - "127.0.0.1:15447:3000" + environment: + GF_AUTH_ANONYMOUS_ENABLED: "true" + GF_AUTH_ANONYMOUS_ORG_ROLE: Admin + GF_AUTH_DISABLE_LOGIN_FORM: "true" + GF_INSTALL_PLUGINS: grafana-pyroscope-app + volumes: + - ../config/observability/grafana/provisioning:/etc/grafana/provisioning:ro + - musher-grafana-data:/var/lib/grafana + depends_on: + tempo: + condition: service_healthy + loki: + condition: service_healthy + victoriametrics: + condition: service_healthy + + pyroscope: + image: grafana/pyroscope:latest + profiles: [observability] + restart: unless-stopped + ports: + - "127.0.0.1:15448:4040" + volumes: + - musher-pyroscope-data:/data + healthcheck: + test: ["CMD", "wget", "--spider", "-q", "http://localhost:4040/ready"] + interval: 5s + timeout: 3s + retries: 5 + start_period: 10s + +volumes: + musher-minio-observability-data: + musher-tempo-data: + musher-loki-data: + musher-victoriametrics-data: + musher-grafana-data: + musher-pyroscope-data: diff --git a/.devcontainer/compose/postgres.yaml b/.devcontainer/compose/postgres.yaml index fd75866..2fa3277 100644 --- a/.devcontainer/compose/postgres.yaml +++ b/.devcontainer/compose/postgres.yaml @@ -1,24 +1,24 @@ -# PostgreSQL with pgvector extension. - -services: - postgres: - image: pgvector/pgvector:pg17 - restart: unless-stopped - ports: - - "127.0.0.1:15432:5432" - environment: - POSTGRES_USER: ${POSTGRES_USER:-postgres} - POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-postgres} - POSTGRES_DB: ${POSTGRES_DB:-app} - volumes: - - musher-postgres-data:/var/lib/postgresql/data - - ../config/postgres:/docker-entrypoint-initdb.d:ro - healthcheck: - test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER:-postgres}"] - interval: 5s - timeout: 3s - retries: 5 - start_period: 10s - -volumes: - musher-postgres-data: +# PostgreSQL with pgvector extension. + +services: + postgres: + image: pgvector/pgvector:pg17 + restart: unless-stopped + ports: + - "127.0.0.1:15432:5432" + environment: + POSTGRES_USER: ${POSTGRES_USER:-postgres} + POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-postgres} + POSTGRES_DB: ${POSTGRES_DB:-app} + volumes: + - musher-postgres-data:/var/lib/postgresql/data + - ../config/postgres:/docker-entrypoint-initdb.d:ro + healthcheck: + test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER:-postgres}"] + interval: 5s + timeout: 3s + retries: 5 + start_period: 10s + +volumes: + musher-postgres-data: diff --git a/.devcontainer/compose/redis.yaml b/.devcontainer/compose/redis.yaml index 5dd0ce1..ea13083 100644 --- a/.devcontainer/compose/redis.yaml +++ b/.devcontainer/compose/redis.yaml @@ -1,21 +1,21 @@ -# Redis — in-memory data store. - -services: - redis: - profiles: [redis] - image: redis:7-alpine - restart: unless-stopped - ports: - - "127.0.0.1:15433:6379" - command: ["redis-server", "--appendonly", "yes"] - volumes: - - musher-redis-data:/data - healthcheck: - test: ["CMD", "redis-cli", "ping"] - interval: 5s - timeout: 3s - retries: 5 - start_period: 5s - -volumes: - musher-redis-data: +# Redis — in-memory data store. + +services: + redis: + profiles: [redis] + image: redis:7-alpine + restart: unless-stopped + ports: + - "127.0.0.1:15433:6379" + command: ["redis-server", "--appendonly", "yes"] + volumes: + - musher-redis-data:/data + healthcheck: + test: ["CMD", "redis-cli", "ping"] + interval: 5s + timeout: 3s + retries: 5 + start_period: 5s + +volumes: + musher-redis-data: diff --git a/.devcontainer/compose/registry.yaml b/.devcontainer/compose/registry.yaml index 01937c3..cbee88b 100644 --- a/.devcontainer/compose/registry.yaml +++ b/.devcontainer/compose/registry.yaml @@ -1,20 +1,20 @@ -# OCI container registry. - -services: - registry: - profiles: [registry] - image: registry:2 - restart: unless-stopped - ports: - - "127.0.0.1:15436:5000" - volumes: - - musher-registry-data:/var/lib/registry - healthcheck: - test: ["CMD", "wget", "--spider", "-q", "http://localhost:5000/v2/"] - interval: 5s - timeout: 3s - retries: 5 - start_period: 5s - -volumes: - musher-registry-data: +# OCI container registry. + +services: + registry: + profiles: [registry] + image: registry:2 + restart: unless-stopped + ports: + - "127.0.0.1:15436:5000" + volumes: + - musher-registry-data:/var/lib/registry + healthcheck: + test: ["CMD", "wget", "--spider", "-q", "http://localhost:5000/v2/"] + interval: 5s + timeout: 3s + retries: 5 + start_period: 5s + +volumes: + musher-registry-data: diff --git a/.devcontainer/config/observability/grafana/provisioning/dashboards/dashboards.yaml b/.devcontainer/config/observability/grafana/provisioning/dashboards/dashboards.yaml index f3cb439..849b493 100644 --- a/.devcontainer/config/observability/grafana/provisioning/dashboards/dashboards.yaml +++ b/.devcontainer/config/observability/grafana/provisioning/dashboards/dashboards.yaml @@ -1,12 +1,12 @@ -apiVersion: 1 - -providers: - - name: default - orgId: 1 - folder: "" - type: file - disableDeletion: false - editable: true - options: - path: /etc/grafana/provisioning/dashboards/json - foldersFromFilesStructure: false +apiVersion: 1 + +providers: + - name: default + orgId: 1 + folder: "" + type: file + disableDeletion: false + editable: true + options: + path: /etc/grafana/provisioning/dashboards/json + foldersFromFilesStructure: false diff --git a/.devcontainer/config/observability/grafana/provisioning/datasources/datasources.yaml b/.devcontainer/config/observability/grafana/provisioning/datasources/datasources.yaml index 69a657d..4be8676 100644 --- a/.devcontainer/config/observability/grafana/provisioning/datasources/datasources.yaml +++ b/.devcontainer/config/observability/grafana/provisioning/datasources/datasources.yaml @@ -1,23 +1,23 @@ -apiVersion: 1 - -datasources: - - name: VictoriaMetrics - type: prometheus - access: proxy - url: http://victoriametrics:8428 - isDefault: true - - - name: Tempo - type: tempo - access: proxy - url: http://tempo:3200 - - - name: Loki - type: loki - access: proxy - url: http://loki:3100 - - - name: Pyroscope - type: grafana-pyroscope-datasource - access: proxy - url: http://pyroscope:4040 +apiVersion: 1 + +datasources: + - name: VictoriaMetrics + type: prometheus + access: proxy + url: http://victoriametrics:8428 + isDefault: true + + - name: Tempo + type: tempo + access: proxy + url: http://tempo:3200 + + - name: Loki + type: loki + access: proxy + url: http://loki:3100 + + - name: Pyroscope + type: grafana-pyroscope-datasource + access: proxy + url: http://pyroscope:4040 diff --git a/.devcontainer/config/observability/loki-config.yaml b/.devcontainer/config/observability/loki-config.yaml index d2df387..dea8b74 100644 --- a/.devcontainer/config/observability/loki-config.yaml +++ b/.devcontainer/config/observability/loki-config.yaml @@ -1,38 +1,38 @@ -auth_enabled: false - -server: - http_listen_port: 3100 - -common: - ring: - instance_addr: 127.0.0.1 - kvstore: - store: inmemory - replication_factor: 1 - path_prefix: /loki - -schema_config: - configs: - - from: "2024-01-01" - store: tsdb - object_store: s3 - schema: v13 - index: - prefix: index_ - period: 24h - -storage_config: - tsdb_shipper: - active_index_directory: /loki/index - cache_location: /loki/index_cache - aws: - bucketnames: loki-data - endpoint: minio-observability:9000 - access_key_id: minioadmin - secret_access_key: minioadmin - insecure: true - s3forcepathstyle: true - -limits_config: - retention_period: 168h - allow_structured_metadata: true +auth_enabled: false + +server: + http_listen_port: 3100 + +common: + ring: + instance_addr: 127.0.0.1 + kvstore: + store: inmemory + replication_factor: 1 + path_prefix: /loki + +schema_config: + configs: + - from: "2024-01-01" + store: tsdb + object_store: s3 + schema: v13 + index: + prefix: index_ + period: 24h + +storage_config: + tsdb_shipper: + active_index_directory: /loki/index + cache_location: /loki/index_cache + aws: + bucketnames: loki-data + endpoint: minio-observability:9000 + access_key_id: minioadmin + secret_access_key: minioadmin + insecure: true + s3forcepathstyle: true + +limits_config: + retention_period: 168h + allow_structured_metadata: true diff --git a/.devcontainer/config/observability/otel-collector-config.yaml b/.devcontainer/config/observability/otel-collector-config.yaml index 69116f0..125d588 100644 --- a/.devcontainer/config/observability/otel-collector-config.yaml +++ b/.devcontainer/config/observability/otel-collector-config.yaml @@ -1,41 +1,41 @@ -receivers: - otlp: - protocols: - grpc: - endpoint: "0.0.0.0:4317" - http: - endpoint: "0.0.0.0:4318" - -processors: - batch: - timeout: 5s - send_batch_size: 1024 - -exporters: - otlphttp/tempo: - endpoint: http://tempo:4318 - tls: - insecure: true - - loki: - endpoint: http://loki:3100/loki/api/v1/push - - prometheusremotewrite: - endpoint: http://victoriametrics:8428/api/v1/write - tls: - insecure: true - -service: - pipelines: - traces: - receivers: [otlp] - processors: [batch] - exporters: [otlphttp/tempo] - logs: - receivers: [otlp] - processors: [batch] - exporters: [loki] - metrics: - receivers: [otlp] - processors: [batch] - exporters: [prometheusremotewrite] +receivers: + otlp: + protocols: + grpc: + endpoint: "0.0.0.0:4317" + http: + endpoint: "0.0.0.0:4318" + +processors: + batch: + timeout: 5s + send_batch_size: 1024 + +exporters: + otlphttp/tempo: + endpoint: http://tempo:4318 + tls: + insecure: true + + loki: + endpoint: http://loki:3100/loki/api/v1/push + + prometheusremotewrite: + endpoint: http://victoriametrics:8428/api/v1/write + tls: + insecure: true + +service: + pipelines: + traces: + receivers: [otlp] + processors: [batch] + exporters: [otlphttp/tempo] + logs: + receivers: [otlp] + processors: [batch] + exporters: [loki] + metrics: + receivers: [otlp] + processors: [batch] + exporters: [prometheusremotewrite] diff --git a/.devcontainer/config/observability/tempo-config.yaml b/.devcontainer/config/observability/tempo-config.yaml index 8a26e04..7753b08 100644 --- a/.devcontainer/config/observability/tempo-config.yaml +++ b/.devcontainer/config/observability/tempo-config.yaml @@ -1,36 +1,36 @@ -stream_over_http_enabled: true - -server: - http_listen_port: 3200 - -distributor: - receivers: - otlp: - protocols: - grpc: - endpoint: "0.0.0.0:4317" - http: - endpoint: "0.0.0.0:4318" - -storage: - trace: - backend: s3 - s3: - bucket: tempo-traces - endpoint: minio-observability:9000 - access_key: minioadmin - secret_key: minioadmin - insecure: true - forcepathstyle: true - wal: - path: /var/tempo/wal - block: - bloom_filter_false_positive: 0.05 - -compactor: - compaction: - block_retention: 48h - -metrics_generator: - storage: - path: /var/tempo/generator/wal +stream_over_http_enabled: true + +server: + http_listen_port: 3200 + +distributor: + receivers: + otlp: + protocols: + grpc: + endpoint: "0.0.0.0:4317" + http: + endpoint: "0.0.0.0:4318" + +storage: + trace: + backend: s3 + s3: + bucket: tempo-traces + endpoint: minio-observability:9000 + access_key: minioadmin + secret_key: minioadmin + insecure: true + forcepathstyle: true + wal: + path: /var/tempo/wal + block: + bloom_filter_false_positive: 0.05 + +compactor: + compaction: + block_retention: 48h + +metrics_generator: + storage: + path: /var/tempo/generator/wal diff --git a/.devcontainer/config/postgres/00-init.sql b/.devcontainer/config/postgres/00-init.sql index 8d22a8f..eee71c8 100644 --- a/.devcontainer/config/postgres/00-init.sql +++ b/.devcontainer/config/postgres/00-init.sql @@ -1,58 +1,58 @@ --- Database Initialization --- ====================== --- This script runs when the PostgreSQL container first starts. - --- Enable pgvector extension for vector similarity search -CREATE EXTENSION IF NOT EXISTS vector; - --- Enable additional useful extensions -CREATE EXTENSION IF NOT EXISTS "uuid-ossp"; -- For UUID generation -CREATE EXTENSION IF NOT EXISTS "pg_trgm"; -- For text similarity -CREATE EXTENSION IF NOT EXISTS "btree_gin"; -- For GIN indexes on btree types - --- Verify pgvector is installed -DO $$ -BEGIN - IF NOT EXISTS (SELECT 1 FROM pg_extension WHERE extname = 'vector') THEN - RAISE EXCEPTION 'pgvector extension failed to install'; - END IF; - RAISE NOTICE 'pgvector extension successfully installed'; -END -$$; - --- Create a function to check vector version -CREATE OR REPLACE FUNCTION vector_version() -RETURNS text AS $$ - SELECT extversion FROM pg_extension WHERE extname = 'vector'; -$$ LANGUAGE SQL; - --- Display installation info -DO $$ -DECLARE - vec_version text; -BEGIN - SELECT vector_version() INTO vec_version; - RAISE NOTICE 'Database initialized with pgvector %', vec_version; -END -$$; - --- Create azimutt database for Azimutt ERD tool --- (Azimutt stores its own application data here; databases to visualize are added via UI) -CREATE DATABASE azimutt; -DO $$ BEGIN RAISE NOTICE 'Azimutt database created'; END $$; - --- Create Atlas dev database for schema diffing --- Used instead of docker://postgres in Docker-in-Docker environments -CREATE DATABASE atlas_dev; -DO $$ BEGIN RAISE NOTICE 'Atlas dev database (atlas_dev) created'; END $$; - --- Create isolated database for integration tests --- Tests use this instead of `app` so dev data is never affected -CREATE DATABASE app_test; -\connect app_test; -CREATE EXTENSION IF NOT EXISTS vector; -CREATE EXTENSION IF NOT EXISTS "uuid-ossp"; -CREATE EXTENSION IF NOT EXISTS "pg_trgm"; -CREATE EXTENSION IF NOT EXISTS "btree_gin"; -\connect app; -DO $$ BEGIN RAISE NOTICE 'Integration test database (app_test) created'; END $$; +-- Database Initialization +-- ====================== +-- This script runs when the PostgreSQL container first starts. + +-- Enable pgvector extension for vector similarity search +CREATE EXTENSION IF NOT EXISTS vector; + +-- Enable additional useful extensions +CREATE EXTENSION IF NOT EXISTS "uuid-ossp"; -- For UUID generation +CREATE EXTENSION IF NOT EXISTS "pg_trgm"; -- For text similarity +CREATE EXTENSION IF NOT EXISTS "btree_gin"; -- For GIN indexes on btree types + +-- Verify pgvector is installed +DO $$ +BEGIN + IF NOT EXISTS (SELECT 1 FROM pg_extension WHERE extname = 'vector') THEN + RAISE EXCEPTION 'pgvector extension failed to install'; + END IF; + RAISE NOTICE 'pgvector extension successfully installed'; +END +$$; + +-- Create a function to check vector version +CREATE OR REPLACE FUNCTION vector_version() +RETURNS text AS $$ + SELECT extversion FROM pg_extension WHERE extname = 'vector'; +$$ LANGUAGE SQL; + +-- Display installation info +DO $$ +DECLARE + vec_version text; +BEGIN + SELECT vector_version() INTO vec_version; + RAISE NOTICE 'Database initialized with pgvector %', vec_version; +END +$$; + +-- Create azimutt database for Azimutt ERD tool +-- (Azimutt stores its own application data here; databases to visualize are added via UI) +CREATE DATABASE azimutt; +DO $$ BEGIN RAISE NOTICE 'Azimutt database created'; END $$; + +-- Create Atlas dev database for schema diffing +-- Used instead of docker://postgres in Docker-in-Docker environments +CREATE DATABASE atlas_dev; +DO $$ BEGIN RAISE NOTICE 'Atlas dev database (atlas_dev) created'; END $$; + +-- Create isolated database for integration tests +-- Tests use this instead of `app` so dev data is never affected +CREATE DATABASE app_test; +\connect app_test; +CREATE EXTENSION IF NOT EXISTS vector; +CREATE EXTENSION IF NOT EXISTS "uuid-ossp"; +CREATE EXTENSION IF NOT EXISTS "pg_trgm"; +CREATE EXTENSION IF NOT EXISTS "btree_gin"; +\connect app; +DO $$ BEGIN RAISE NOTICE 'Integration test database (app_test) created'; END $$; diff --git a/.devcontainer/config/postgres/01-project.sql.example b/.devcontainer/config/postgres/01-project.sql.example index 7a87a85..08a9452 100644 --- a/.devcontainer/config/postgres/01-project.sql.example +++ b/.devcontainer/config/postgres/01-project.sql.example @@ -1,15 +1,15 @@ --- Project-Specific Database Initialization --- ========================================= --- Rename this file to 01-project.sql (or any NN-name.sql) to add --- project-specific schema, seeds, or extensions. --- --- PostgreSQL processes files in /docker-entrypoint-initdb.d/ alphabetically, --- so 00-init.sql (base extensions and databases) always runs first. --- --- This script runs against the default database (app) unless you \connect elsewhere. --- --- Examples: --- CREATE TABLE users (id uuid PRIMARY KEY DEFAULT uuid_generate_v4(), ...); --- INSERT INTO ... VALUES ...; --- \connect app_test; --- CREATE TABLE users (...); -- mirror schema in test DB +-- Project-Specific Database Initialization +-- ========================================= +-- Rename this file to 01-project.sql (or any NN-name.sql) to add +-- project-specific schema, seeds, or extensions. +-- +-- PostgreSQL processes files in /docker-entrypoint-initdb.d/ alphabetically, +-- so 00-init.sql (base extensions and databases) always runs first. +-- +-- This script runs against the default database (app) unless you \connect elsewhere. +-- +-- Examples: +-- CREATE TABLE users (id uuid PRIMARY KEY DEFAULT uuid_generate_v4(), ...); +-- INSERT INTO ... VALUES ...; +-- \connect app_test; +-- CREATE TABLE users (...); -- mirror schema in test DB diff --git a/.devcontainer/config/shell/README.md b/.devcontainer/config/shell/README.md index 8cc25f4..6a68c30 100644 --- a/.devcontainer/config/shell/README.md +++ b/.devcontainer/config/shell/README.md @@ -1,23 +1,23 @@ -# Shell Customization - -Files in this directory are sourced by zsh on shell startup using the shared/local pattern. - -## Convention - -| Pattern | Tracked | Purpose | -|---|---|---| -| `*.shared.sh` | Yes | Team defaults — aliases, functions, plugin config | -| `*.local.sh` | No (gitignored) | Personal overrides — machine-specific settings | - -Shared files are sourced first, then local files, so local settings override team defaults. - -## Usage - -- Edit `aliases.shared.sh` to change team-wide aliases -- Create a `*.local.sh` file for personal customizations (e.g., `my.local.sh`) -- Open a new terminal — changes are picked up automatically - -## Notes - -- Files are sourced in alphabetical order within each group -- `*.local.sh` files are gitignored and will not be committed +# Shell Customization + +Files in this directory are sourced by zsh on shell startup using the shared/local pattern. + +## Convention + +| Pattern | Tracked | Purpose | +|---|---|---| +| `*.shared.sh` | Yes | Team defaults — aliases, functions, plugin config | +| `*.local.sh` | No (gitignored) | Personal overrides — machine-specific settings | + +Shared files are sourced first, then local files, so local settings override team defaults. + +## Usage + +- Edit `aliases.shared.sh` to change team-wide aliases +- Create a `*.local.sh` file for personal customizations (e.g., `my.local.sh`) +- Open a new terminal — changes are picked up automatically + +## Notes + +- Files are sourced in alphabetical order within each group +- `*.local.sh` files are gitignored and will not be committed diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index 2f9aff1..1c3156d 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -1,207 +1,217 @@ -// Musher Dev Container — Batteries-included template. -// Uncomment optional blocks as needed. Comment out what you don't use. -{ - "name": "Musher Dev", - "image": "mcr.microsoft.com/devcontainers/base:ubuntu", - - "workspaceMount": "source=${localWorkspaceFolder},target=/workspaces/${localWorkspaceFolderBasename},type=bind,consistency=cached", - "workspaceFolder": "/workspaces/${localWorkspaceFolderBasename}", - - "features": { - // --- Core --- - "ghcr.io/devcontainers/features/common-utils:2": { - "installZsh": true, - "configureZshAsDefaultShell": true, - "installOhMyZsh": true, - "installOhMyZshConfig": true, - "upgradePackages": true - }, - "ghcr.io/devcontainers/features/git:1": {}, - "ghcr.io/devcontainers/features/github-cli:1": {}, - - // --- Runtimes --- - "ghcr.io/devcontainers/features/node:1": { - "version": "lts" - }, - "ghcr.io/devcontainers/features/python:1": { - "version": "3.13" - }, - "ghcr.io/devcontainers/features/go:1": {}, - - // --- Docker --- - "ghcr.io/devcontainers/features/docker-in-docker:2": {}, - - // --- Package managers --- - "ghcr.io/devcontainers-extra/features/bun:1": {}, - "ghcr.io/devcontainers-extra/features/uv:1": {}, - - // --- Additional runtimes --- - "ghcr.io/devcontainers-extra/features/deno:1": {}, - "ghcr.io/devcontainers/features/java:1": { - "version": "17", - "installGradle": false, - "installMaven": false - }, - - // --- Database tooling --- - "ghcr.io/robbert229/devcontainer-features/postgresql-client:1": { - "version": "16" - }, - - // --- Task runner --- - "ghcr.io/devcontainers-extra/features/go-task:1": {}, - - // --- Linting --- - "ghcr.io/lukewiwa/features/shellcheck:0": { "version": "stable" }, - - // --- Optional: Infrastructure tooling (uncomment as needed) --- - // "ghcr.io/devcontainers-extra/features/opentofu:1": {}, - // "ghcr.io/devcontainers-extra/features/digitalocean-cli:1": {}, - // "ghcr.io/devcontainers-extra/features/cloudflared:1": {}, - // "ghcr.io/devcontainers/features/copilot-cli:1": {} - }, - - "containerUser": "vscode", - "remoteUser": "vscode", - - "init": true, - "shutdownAction": "stopContainer", - - // Allow debuggers (gdb, strace, delve) to attach to processes. - "capAdd": ["SYS_PTRACE"], - - "mounts": [ - "source=musher-${devcontainerId}-gh-config,target=/home/vscode/.config/gh,type=volume", - "source=musher-${devcontainerId}-claude-config,target=/home/vscode/.claude,type=volume", - "source=musher-${devcontainerId}-codex-config,target=/home/vscode/.codex,type=volume" - // Uncomment to persist Docker-in-Docker data across rebuilds: - // "source=musher-${devcontainerId}-dind-data,target=/var/lib/docker/volumes,type=volume" - ], - - "containerEnv": { - "PYTHONUNBUFFERED": "1", - "PYTHONDONTWRITEBYTECODE": "1", - "UV_LINK_MODE": "copy", - "XDG_CACHE_HOME": "/home/vscode/.cache", - "UV_CACHE_DIR": "/home/vscode/.cache/uv", - "RUFF_CACHE_DIR": "/home/vscode/.cache/ruff", - "PIP_CACHE_DIR": "/home/vscode/.cache/pip", - "MYPY_CACHE_DIR": "/home/vscode/.cache/mypy", - "NPM_CONFIG_CACHE": "/home/vscode/.cache/npm", - "DENO_DIR": "/home/vscode/.cache/deno", - "GOMODCACHE": "/home/vscode/.cache/go/mod", - "GOCACHE": "/home/vscode/.cache/go/build", - "BUN_INSTALL_CACHE_DIR": "/home/vscode/.cache/bun" - }, - - "remoteEnv": { - "PATH": "/home/vscode/.local/bin:${containerEnv:PATH}" - // Uncomment if Go feature auto-sets GOROOT incorrectly: - // "GOROOT": "" - }, - - "waitFor": "postCreateCommand", - "postCreateCommand": { - "fix-crlf": "find .devcontainer/scripts -type f -name '*.sh' -exec sed -i 's/\\r$//' {} +", - "setup": ["bash", ".devcontainer/scripts/post-create.sh"] - }, - "postStartCommand": ["bash", ".devcontainer/scripts/startup.sh"], - - "forwardPorts": [15432, 15433, 15434, 15435, 15436, 15440, 15441, 15442, 15443, 15444, 15445, 15446, 15447, 15448, 15460], - "portsAttributes": { - "15432": { "label": "PostgreSQL", "onAutoForward": "silent" }, - "15433": { "label": "Redis", "onAutoForward": "silent" }, - "15434": { "label": "MinIO API", "onAutoForward": "silent" }, - "15435": { "label": "MinIO Console", "onAutoForward": "silent" }, - "15436": { "label": "OCI Registry", "onAutoForward": "silent" }, - "15440": { "label": "MinIO API (Observability)", "onAutoForward": "silent" }, - "15441": { "label": "MinIO Console (Observability)", "onAutoForward": "silent" }, - "15442": { "label": "Tempo", "onAutoForward": "silent" }, - "15443": { "label": "Loki", "onAutoForward": "silent" }, - "15444": { "label": "VictoriaMetrics", "onAutoForward": "silent" }, - "15445": { "label": "OTel HTTP", "onAutoForward": "silent" }, - "15446": { "label": "OTel gRPC", "onAutoForward": "silent" }, - "15447": { "label": "Grafana", "onAutoForward": "silent" }, - "15448": { "label": "Pyroscope", "onAutoForward": "silent" }, - "15460": { "label": "Azimutt", "onAutoForward": "silent" } - }, - - "customizations": { - "vscode": { - "extensions": [ - // --- Core tooling --- - "redhat.vscode-yaml", - "tamasfe.even-better-toml", - "ms-azuretools.vscode-docker", - "esbenp.prettier-vscode", - - // --- Language support --- - "golang.go", - "ms-python.python", - "ms-python.vscode-pylance", - "charliermarsh.ruff", - "dbaeumer.vscode-eslint", - - // --- Shell linting --- - "timonwong.shellcheck", - - // --- Optional (comment out if not needed) --- - "eamodio.gitlens", - "github.copilot", - "github.copilot-chat", - "bierner.markdown-mermaid" - - // --- Optional: SQL client (uncomment if using PostgreSQL) --- - // "mtxr.sqltools", - // "mtxr.sqltools-driver-pg", - - // --- Optional: Additional extensions (uncomment as needed) --- - // "tomoki1207.pdf", - // "jebbs.plantuml", - // "contextmapper.context-mapper-vscode-extension", - // "ChaunceyKiwi.json-tree-view", - // "github.vscode-pull-request-github", - // "hashicorp.terraform", - // "wholroyd.jinja", - // "batisteo.vscode-django" - ], - "settings": { - // --- Editor --- - "editor.formatOnSave": true, - "editor.rulers": [80, 120], - "files.trimTrailingWhitespace": true, - "files.insertFinalNewline": true, - - // --- Terminal --- - "terminal.integrated.defaultProfile.linux": "zsh", - "terminal.integrated.profiles.linux": { - "bash": { "path": "/bin/bash" }, - "zsh": { "path": "/bin/zsh" } - }, - - // --- Go --- - "go.toolsManagement.autoUpdate": true, - - // --- Python --- - "[python]": { - "editor.defaultFormatter": "charliermarsh.ruff", - "editor.codeActionsOnSave": { - "source.fixAll": "explicit", - "source.organizeImports": "explicit" - } - }, - - // --- Prettier formatter overrides --- - "[javascript]": { - "editor.defaultFormatter": "esbenp.prettier-vscode" - }, - "[typescript]": { - "editor.defaultFormatter": "esbenp.prettier-vscode" - }, - "[json]": { - "editor.defaultFormatter": "esbenp.prettier-vscode" - } - } - } - } -} +// Musher Dev Container — Batteries-included template. +// Uncomment optional blocks as needed. Comment out what you don't use. +{ + "name": "Musher Dev", + "image": "mcr.microsoft.com/devcontainers/base:ubuntu", + + "workspaceMount": "source=${localWorkspaceFolder},target=/workspaces/${localWorkspaceFolderBasename},type=bind,consistency=cached", + "workspaceFolder": "/workspaces/${localWorkspaceFolderBasename}", + + "features": { + // --- Core --- + "ghcr.io/devcontainers/features/common-utils:2": { + "installZsh": true, + "configureZshAsDefaultShell": true, + "installOhMyZsh": true, + "installOhMyZshConfig": true, + "upgradePackages": true + }, + "ghcr.io/devcontainers/features/git:1": {}, + "ghcr.io/devcontainers/features/github-cli:1": {}, + + // --- Runtimes --- + "ghcr.io/devcontainers/features/node:1": { + "version": "lts" + }, + "ghcr.io/devcontainers/features/python:1": { + "version": "3.13" + }, + "ghcr.io/devcontainers/features/go:1": {}, + + // --- Docker --- + "ghcr.io/devcontainers/features/docker-in-docker:2": {}, + + // --- Package managers --- + "ghcr.io/devcontainers-extra/features/bun:1": {}, + "ghcr.io/devcontainers-extra/features/uv:1": {}, + + // --- Additional runtimes --- + "ghcr.io/devcontainers-extra/features/deno:1": {}, + "ghcr.io/devcontainers/features/java:1": { + "version": "17", + "installGradle": false, + "installMaven": false + }, + + // --- Database tooling --- + "ghcr.io/robbert229/devcontainer-features/postgresql-client:1": { + "version": "16" + }, + + // --- Task runner --- + "ghcr.io/devcontainers-extra/features/go-task:1": {}, + + // --- Linting --- + "ghcr.io/lukewiwa/features/shellcheck:0": { "version": "stable" } + + // --- Optional: Infrastructure tooling (uncomment as needed) --- + // "ghcr.io/devcontainers-extra/features/opentofu:1": {}, + // "ghcr.io/devcontainers-extra/features/digitalocean-cli:1": {}, + // "ghcr.io/devcontainers-extra/features/cloudflared:1": {}, + // "ghcr.io/devcontainers/features/copilot-cli:1": {} + }, + + "containerUser": "vscode", + "remoteUser": "vscode", + + "init": true, + "shutdownAction": "stopContainer", + + // Allow debuggers (gdb, strace, delve) to attach to processes. + "capAdd": ["SYS_PTRACE"], + + "mounts": [ + "source=musher-${devcontainerId}-gh-config,target=/home/vscode/.config/gh,type=volume", + "source=musher-${devcontainerId}-claude-config,target=/home/vscode/.claude,type=volume", + "source=musher-${devcontainerId}-codex-config,target=/home/vscode/.codex,type=volume" + // Uncomment to persist Docker-in-Docker data across rebuilds: + // "source=musher-${devcontainerId}-dind-data,target=/var/lib/docker/volumes,type=volume" + ], + + "containerEnv": { + "PYTHONUNBUFFERED": "1", + "PYTHONDONTWRITEBYTECODE": "1", + "UV_LINK_MODE": "copy", + "XDG_CACHE_HOME": "/home/vscode/.cache", + "UV_CACHE_DIR": "/home/vscode/.cache/uv", + "RUFF_CACHE_DIR": "/home/vscode/.cache/ruff", + "PIP_CACHE_DIR": "/home/vscode/.cache/pip", + "MYPY_CACHE_DIR": "/home/vscode/.cache/mypy", + "NPM_CONFIG_CACHE": "/home/vscode/.cache/npm", + "DENO_DIR": "/home/vscode/.cache/deno", + "GOMODCACHE": "/home/vscode/.cache/go/mod", + "GOCACHE": "/home/vscode/.cache/go/build", + "BUN_INSTALL_CACHE_DIR": "/home/vscode/.cache/bun" + }, + + "remoteEnv": { + "PATH": "/home/vscode/.local/bin:${containerEnv:PATH}" + // Uncomment if Go feature auto-sets GOROOT incorrectly: + // "GOROOT": "" + }, + + "waitFor": "postCreateCommand", + "postCreateCommand": [ + "bash", + "-lc", + "find .devcontainer/scripts -type f -name '*.sh' -exec sed -i 's/\\r$//' {} + && bash .devcontainer/scripts/post-create.sh" + ], + "postStartCommand": ["bash", ".devcontainer/scripts/startup.sh"], + + "forwardPorts": [ + 15432, 15433, 15434, 15435, 15436, 15440, 15441, 15442, 15443, 15444, 15445, + 15446, 15447, 15448, 15460 + ], + "portsAttributes": { + "15432": { "label": "PostgreSQL", "onAutoForward": "silent" }, + "15433": { "label": "Redis", "onAutoForward": "silent" }, + "15434": { "label": "MinIO API", "onAutoForward": "silent" }, + "15435": { "label": "MinIO Console", "onAutoForward": "silent" }, + "15436": { "label": "OCI Registry", "onAutoForward": "silent" }, + "15440": { + "label": "MinIO API (Observability)", + "onAutoForward": "silent" + }, + "15441": { + "label": "MinIO Console (Observability)", + "onAutoForward": "silent" + }, + "15442": { "label": "Tempo", "onAutoForward": "silent" }, + "15443": { "label": "Loki", "onAutoForward": "silent" }, + "15444": { "label": "VictoriaMetrics", "onAutoForward": "silent" }, + "15445": { "label": "OTel HTTP", "onAutoForward": "silent" }, + "15446": { "label": "OTel gRPC", "onAutoForward": "silent" }, + "15447": { "label": "Grafana", "onAutoForward": "silent" }, + "15448": { "label": "Pyroscope", "onAutoForward": "silent" }, + "15460": { "label": "Azimutt", "onAutoForward": "silent" } + }, + + "customizations": { + "vscode": { + "extensions": [ + // --- Core tooling --- + "redhat.vscode-yaml", + "tamasfe.even-better-toml", + "ms-azuretools.vscode-docker", + "esbenp.prettier-vscode", + + // --- Language support --- + "golang.go", + "ms-python.python", + "ms-python.vscode-pylance", + "charliermarsh.ruff", + "dbaeumer.vscode-eslint", + + // --- Shell linting --- + "timonwong.shellcheck", + + // --- Optional (comment out if not needed) --- + "eamodio.gitlens", + "github.copilot", + "github.copilot-chat", + "bierner.markdown-mermaid" + + // --- Optional: SQL client (uncomment if using PostgreSQL) --- + // "mtxr.sqltools", + // "mtxr.sqltools-driver-pg", + + // --- Optional: Additional extensions (uncomment as needed) --- + // "tomoki1207.pdf", + // "jebbs.plantuml", + // "contextmapper.context-mapper-vscode-extension", + // "ChaunceyKiwi.json-tree-view", + // "github.vscode-pull-request-github", + // "hashicorp.terraform", + // "wholroyd.jinja", + // "batisteo.vscode-django" + ], + "settings": { + // --- Editor --- + "editor.formatOnSave": true, + "editor.rulers": [80, 120], + "files.trimTrailingWhitespace": true, + "files.insertFinalNewline": true, + + // --- Terminal --- + "terminal.integrated.defaultProfile.linux": "zsh", + "terminal.integrated.profiles.linux": { + "bash": { "path": "/bin/bash" }, + "zsh": { "path": "/bin/zsh" } + }, + + // --- Go --- + "go.toolsManagement.autoUpdate": true, + + // --- Python --- + "[python]": { + "editor.defaultFormatter": "charliermarsh.ruff", + "editor.codeActionsOnSave": { + "source.fixAll": "explicit", + "source.organizeImports": "explicit" + } + }, + + // --- Prettier formatter overrides --- + "[javascript]": { + "editor.defaultFormatter": "esbenp.prettier-vscode" + }, + "[typescript]": { + "editor.defaultFormatter": "esbenp.prettier-vscode" + }, + "[json]": { + "editor.defaultFormatter": "esbenp.prettier-vscode" + } + } + } + } +} diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..6482e42 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,2 @@ +# Keep shell scripts on LF to avoid devcontainer/bash startup issues +*.sh text eol=lf diff --git a/.github/workflows/validate.yaml b/.github/workflows/validate.yaml index f5914c2..a75925e 100644 --- a/.github/workflows/validate.yaml +++ b/.github/workflows/validate.yaml @@ -1,40 +1,40 @@ -name: Validate - -on: - push: - branches: [main] - pull_request: - branches: [main] - -jobs: - shellcheck: - name: ShellCheck - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - name: Run ShellCheck - uses: ludeeus/action-shellcheck@2.0.0 - env: - SHELLCHECK_OPTS: -x --source-path=SCRIPTDIR - with: - scandir: .devcontainer/scripts - - compose: - name: Compose Config - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - name: Validate compose config - run: | - cp .devcontainer/.env.example .devcontainer/.env - docker compose -f .devcontainer/compose.yaml config --quiet - - build: - name: Devcontainer Build - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - name: Build devcontainer - uses: devcontainers/ci@v0.3 - with: - runCmd: echo "devcontainer smoke test passed" +name: Validate + +on: + push: + branches: [main] + pull_request: + branches: [main] + +jobs: + shellcheck: + name: ShellCheck + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Run ShellCheck + uses: ludeeus/action-shellcheck@2.0.0 + env: + SHELLCHECK_OPTS: -x --source-path=SCRIPTDIR + with: + scandir: .devcontainer/scripts + + compose: + name: Compose Config + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Validate compose config + run: | + cp .devcontainer/.env.example .devcontainer/.env + docker compose -f .devcontainer/compose.yaml config --quiet + + build: + name: Devcontainer Build + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Build devcontainer + uses: devcontainers/ci@v0.3 + with: + runCmd: echo "devcontainer smoke test passed" diff --git a/.gitignore b/.gitignore index 60d095a..21ff3ec 100644 --- a/.gitignore +++ b/.gitignore @@ -1,8 +1,11 @@ -# Dev container local environment overrides -.devcontainer/.env - -# Personal shell customizations (use *.shared.sh for team defaults) -.devcontainer/config/shell/*.local.sh - -# Claude local preferences -.claude/settings.local.json +# Dev container local environment overrides +.devcontainer/.env + +# Personal shell customizations (use *.shared.sh for team defaults) +.devcontainer/config/shell/*.local.sh + +# Claude local preferences +.claude/settings.local.json + +# Dependencies +node_modules/ diff --git a/CONFIGURATION.md b/CONFIGURATION.md index e0bbbd3..38365ab 100644 --- a/CONFIGURATION.md +++ b/CONFIGURATION.md @@ -1,376 +1,376 @@ -# Configuration Guide - -**Philosophy: One need, one place.** Every configuration concern maps to exactly one canonical location. If you're unsure where something goes, use the decision tree below. - -## Decision Tree - -``` -Where does my configuration go? - -Language runtime or system package? - → devcontainer.json → features block - -CLI tool installed via curl/npm? - → scripts/lib/base-setup.sh (add a function, call from post-create.sh) - -Infrastructure service (DB, cache, queue, storage)? - → compose/*.yaml (new file, add to compose.yaml includes) - -VS Code editor behavior or extension? - → devcontainer.json → customizations.vscode block - -Credential or per-developer toggle? - → .devcontainer/.env (copy from .env.example) - -Service-internal configuration (tuning, pipelines)? - → config// directory - -Shell aliases, oh-my-zsh plugins, or shell functions? - → config/shell/ directory - -One-time setup step? - → scripts/post-create.sh - -Runs on every container start? - → scripts/startup.sh -``` - -## Quick Reference - -| Category | Need | Canonical Location | -|---|---|---| -| **Runtimes & Tools** | Language runtimes (Node, Python, Go, Java, Deno) | `devcontainer.json` → `features` | -| | Package managers (bun, uv) | `devcontainer.json` → `features` | -| | CLI tools (Task, Codex) | `scripts/lib/base-setup.sh` | -| **Editor** | VS Code settings (formatters, rulers, whitespace) | `devcontainer.json` → `customizations.vscode.settings` | -| | VS Code extensions | `devcontainer.json` → `customizations.vscode.extensions` | -| | Debug launch configs | `.vscode/launch.json` (in consuming project) | -| **Shell & User** | Default shell, prompt, oh-my-zsh config | `devcontainer.json` → `common-utils` feature | -| | Shell aliases, functions, plugins | `.devcontainer/config/shell/` | -| | Git config | Host `.gitconfig` (auto-forwarded by devcontainers) | -| | Git hooks | Project repo (`.husky/` or `.githooks/`) | -| **Environment** | Runtime behavior vars (`PYTHONUNBUFFERED`, etc.) | `devcontainer.json` → `containerEnv` | -| | PATH extensions | `devcontainer.json` → `remoteEnv` | -| | Service credentials (dev-only) | `.devcontainer/.env` | -| | Service profiles/toggles | `.devcontainer/.env` → `COMPOSE_PROFILES` | -| | Secrets (API keys, tokens) | Host env forwarded via `remoteEnv` — never committed | -| **Services** | Infrastructure services | `.devcontainer/compose/*.yaml` | -| | Service enable/disable | `.devcontainer/.env` → `COMPOSE_PROFILES` | -| | Service tuning/config | `.devcontainer/config//` | -| **Networking** | Port allocation (container-side) | `.devcontainer/compose/*.yaml` → `ports:` | -| | Port forwarding (to host IDE) | `devcontainer.json` → `forwardPorts` + `portsAttributes` | -| | Service discovery | Automatic via Docker Compose `musher-dev` network | -| **Observability** | Telemetry pipeline config | `.devcontainer/config/observability/otel-collector-config.yaml` | -| | Grafana dashboards | `.devcontainer/config/observability/grafana/provisioning/dashboards/json/` | -| | Grafana datasources | `.devcontainer/config/observability/grafana/provisioning/datasources/` | -| **Data** | DB schema init (base) | `.devcontainer/config/postgres/00-init.sql` | -| | DB schema init (project) | `.devcontainer/config/postgres/01-project.sql` | -| | DB migrations | Project tooling (Atlas, Flyway — not in template) | -| | Data persistence | Compose files → named volumes | -| **Lifecycle** | One-time container setup | `scripts/post-create.sh` → `lib/base-setup.sh` | -| | Every-start tasks | `scripts/startup.sh` | -| | Task automation | `Taskfile.yml` (in consuming project) | -| **AI Tools** | AI CLI installation | `lib/base-setup.sh` (Claude, Codex) | -| | AI CLI config persistence | `devcontainer.json` → `mounts` (named volumes) | -| **Security** | Container capabilities | `devcontainer.json` → `capAdd` / `securityOpt` | -| | Network binding | Compose files → all ports bound to `127.0.0.1` | - ---- - -## Runtimes & Tools - -### Language Runtimes - -Add or remove runtimes in `devcontainer.json` → `features`: - -```jsonc -"features": { - "ghcr.io/devcontainers/features/node:1": { "version": "lts" }, - "ghcr.io/devcontainers/features/python:1": { "version": "3.13" }, - "ghcr.io/devcontainers/features/go:1": {}, - "ghcr.io/devcontainers/features/java:1": { "version": "17" }, - "ghcr.io/devcontainers-extra/features/deno:1": {} -} -``` - -Comment out any runtime you don't need. Also comment out the corresponding VS Code extension. - -### CLI Tools - -Tools installed via curl or npm go in `scripts/lib/base-setup.sh`. Each tool gets its own function: - -```bash -base_install_mytool() { - if has_cmd mytool; then - log "mytool already installed, skipping" - return 0 - fi - log "Installing mytool..." - retry 3 5 bash -c 'curl -fsSL https://mytool.dev/install.sh | bash' -} -``` - -Add the call to `base_setup()` and to `base_verify_tools`. - ---- - -## Editor - -### VS Code Settings - -All editor settings live in `devcontainer.json` → `customizations.vscode.settings`. Do not create a `.vscode/settings.json` in the template — that's for consuming projects. - -### VS Code Extensions - -All extensions live in `devcontainer.json` → `customizations.vscode.extensions`. Comment out extensions for runtimes you don't use. - ---- - -## Shell & User - -### Shell Customization - -Shell customization uses a shared/local pattern: - -| Pattern | Tracked | Purpose | -|---|---|---| -| `*.shared.sh` | Yes | Team defaults — aliases, functions, plugin config | -| `*.local.sh` | No (gitignored) | Personal overrides — machine-specific settings | - -Shared files are sourced first, then local files, so local settings override team defaults. Edit `aliases.shared.sh` for team-wide aliases, or create a `*.local.sh` file for personal customizations. - -### Git Config - -Git config is auto-forwarded from your host machine by the devcontainer CLI. No configuration needed in the template. - ---- - -## Environment Variables - -There are four distinct scopes for environment variables. Use the right one: - -| Scope | Location | When to Use | -|---|---|---| -| **Container-wide** | `devcontainer.json` → `containerEnv` | Runtime behavior (`PYTHONUNBUFFERED`, `UV_LINK_MODE`) | -| **Remote/IDE** | `devcontainer.json` → `remoteEnv` | PATH extensions, forwarded host secrets | -| **Compose services** | `.devcontainer/.env` | Service credentials, `COMPOSE_PROFILES` | -| **Service-specific** | `compose/*.yaml` → `environment:` | Internal service config (uses `${VAR:-default}` interpolation) | - -### Secrets - -Never commit secrets. Forward them from your host environment: - -```jsonc -"remoteEnv": { - "MY_API_KEY": "${localEnv:MY_API_KEY}" -} -``` - -### Service Credentials - -Dev-only credentials live in `.devcontainer/.env` (gitignored). Copy from `.env.example` on first use — `post-create.sh` does this automatically. - ---- - -## Services - -### Enabling/Disabling Services - -All services are included in `compose.yaml`. Optional services are gated by Compose profiles: - -| Service | Profile | Always On? | -|---|---|---| -| PostgreSQL | — | Yes | -| Redis | `redis` | No | -| MinIO | `minio` | No | -| OCI Registry | `registry` | No | -| Azimutt | `azimutt` | No | -| Observability stack | `observability` | No | - -Enable services by setting `COMPOSE_PROFILES` in `.devcontainer/.env`: - -```env -COMPOSE_PROFILES=redis,minio,observability -``` - -### Adding a New Service - -1. Create `compose/myservice.yaml` -2. Add `- compose/myservice.yaml` to `compose.yaml` `include:` -3. Optionally add `profiles: [myservice]` if it should be opt-in -4. Add port forwarding in `devcontainer.json` → `forwardPorts` and `portsAttributes` -5. Use `${VAR:-default}` for any credentials, and add them to `.env.example` - -### Service Configuration - -Service-specific config files go under `.devcontainer/config//`: - -``` -config/ - postgres/ SQL init scripts - observability/ OTel, Grafana, Tempo, Loki configs - shell/ Shell aliases and functions -``` - ---- - -## Networking - -### Port Allocation - -All ports are bound to `127.0.0.1` (localhost only) for security. The template uses the `154xx` range: - -| Port | Service | Protocol | -|---|---|---| -| 15432 | PostgreSQL | TCP | -| 15433 | Redis | TCP | -| 15434 | MinIO API | HTTP | -| 15435 | MinIO Console | HTTP | -| 15436 | OCI Registry | HTTP | -| 15440 | MinIO API (Observability) | HTTP | -| 15441 | MinIO Console (Observability) | HTTP | -| 15442 | Tempo | HTTP | -| 15443 | Loki | HTTP | -| 15444 | VictoriaMetrics | HTTP | -| 15445 | OTel Collector HTTP | HTTP | -| 15446 | OTel Collector gRPC | gRPC | -| 15447 | Grafana | HTTP | -| 15448 | Pyroscope | HTTP | -| 15460 | Azimutt | HTTP | - -### Service Discovery - -Services communicate via the `musher-dev` Docker network. Use the service name as the hostname (e.g., `postgres`, `redis`, `minio-observability`) with the container-internal port. - ---- - -## Observability - -The observability stack is profile-gated (`COMPOSE_PROFILES=observability`). It includes: - -- **Grafana** — Dashboards and visualization (port 15447) -- **Tempo** — Distributed tracing backend -- **Loki** — Log aggregation -- **VictoriaMetrics** — Metrics storage -- **OTel Collector** — Telemetry pipeline (receives OTLP on ports 15445/15446) -- **Pyroscope** — Continuous profiling -- **MinIO (Observability)** — Object storage for Tempo and Loki - -### Configuration Files - -| File | Purpose | -|---|---| -| `config/observability/otel-collector-config.yaml` | OTel Collector pipeline configuration | -| `config/observability/tempo-config.yaml` | Tempo storage and ingestion config | -| `config/observability/loki-config.yaml` | Loki storage and ingestion config | -| `config/observability/grafana/provisioning/datasources/` | Auto-provisioned Grafana datasources | -| `config/observability/grafana/provisioning/dashboards/json/` | Auto-provisioned Grafana dashboards | - -> **Note:** `tempo-config.yaml` and `loki-config.yaml` contain hardcoded MinIO credentials because they are native YAML configs that don't support environment variable interpolation. If you change `MINIO_OBS_ROOT_USER` or `MINIO_OBS_ROOT_PASSWORD` in `.env`, you must also update these files to match. - ---- - -## Data - -### Database Initialization - -SQL files in `.devcontainer/config/postgres/` are mounted into PostgreSQL's `docker-entrypoint-initdb.d/` and run in alphabetical order on first container creation: - -- `00-init.sql` — Base schema (extensions, shared types) -- `01-project.sql.example` — Project-specific schema (copy to `01-project.sql`) - -### Persistence - -All services use named Docker volumes (e.g., `musher-postgres-data`). Data persists across container restarts but is lost on full rebuild. For migrations, use project-level tooling (Atlas, Flyway, etc.). - -### Adding Volumes - -Follow the naming convention `musher-${devcontainerId}-`: - -```jsonc -"source=musher-${devcontainerId}-my-tool,target=/home/vscode/.my-tool,type=volume" -``` - ---- - -## Lifecycle - -| Hook | Runs | Use For | -|---|---|---| -| `postCreateCommand` | Once, on container creation | Tool installation, permissions, `.env` setup | -| `postStartCommand` | Every container start | `docker compose up`, health checks | - -### Skipping Base Steps - -Call individual functions instead of `base_setup`: - -```bash -main() { - log "Starting post-create setup..." - base_setup_config_dirs - base_fix_nvm_permissions - # Skip codex: base_install_codex - base_verify_tools - log "Post-create setup completed" -} -``` - -### Script Layers - -``` -post-create.sh ← Entry point (repo-specific customization) - └── lib/base-setup.sh ← Reusable orchestrator (AI CLIs, Task, NVM, config dirs) - └── lib/common.sh ← Shared utilities (log, retry, has_cmd, ensure_writable_dir) -``` - ---- - -## AI Tools - -### Installed CLIs - -- **Claude CLI** — Installed via the native installer in `base-setup.sh`, config persisted in `~/.claude` volume -- **Codex CLI** — Installed via npm in `base-setup.sh`, config persisted in `~/.codex` volume - -### Configuration Persistence - -AI CLI configs are stored in named volumes mounted via `devcontainer.json` → `mounts`. This preserves authentication and settings across container rebuilds. - ---- - -## Directory Map - -``` -.devcontainer/ - devcontainer.json Runtimes, extensions, settings, mounts, ports - compose.yaml Service orchestrator (includes compose/*.yaml) - .env.example Environment template (copy to .env) - .env Local overrides (gitignored) - compose/ - postgres.yaml PostgreSQL with pgvector (always on) - redis.yaml Redis (profile: redis) - minio.yaml MinIO S3 storage (profile: minio) - registry.yaml OCI Registry (profile: registry) - azimutt.yaml DB explorer UI (profile: azimutt) - observability.yaml Full observability stack (profile: observability) - config/ - postgres/ - 00-init.sql Base DB schema - 01-project.sql.example Project schema template - observability/ - otel-collector-config.yaml - tempo-config.yaml - loki-config.yaml - grafana/provisioning/ - datasources/ Auto-provisioned datasources - dashboards/json/ Auto-provisioned dashboards - shell/ - aliases.shared.sh Team-default shell aliases - README.md Shell customization docs - scripts/ - post-create.sh One-time setup entry point - startup.sh Every-start service launcher - lib/ - base-setup.sh Reusable tool installer - common.sh Shared utilities -``` +# Configuration Guide + +**Philosophy: One need, one place.** Every configuration concern maps to exactly one canonical location. If you're unsure where something goes, use the decision tree below. + +## Decision Tree + +``` +Where does my configuration go? + +Language runtime or system package? + → devcontainer.json → features block + +CLI tool installed via curl/npm? + → scripts/lib/base-setup.sh (add a function, call from post-create.sh) + +Infrastructure service (DB, cache, queue, storage)? + → compose/*.yaml (new file, add to compose.yaml includes) + +VS Code editor behavior or extension? + → devcontainer.json → customizations.vscode block + +Credential or per-developer toggle? + → .devcontainer/.env (copy from .env.example) + +Service-internal configuration (tuning, pipelines)? + → config// directory + +Shell aliases, oh-my-zsh plugins, or shell functions? + → config/shell/ directory + +One-time setup step? + → scripts/post-create.sh + +Runs on every container start? + → scripts/startup.sh +``` + +## Quick Reference + +| Category | Need | Canonical Location | +|---|---|---| +| **Runtimes & Tools** | Language runtimes (Node, Python, Go, Java, Deno) | `devcontainer.json` → `features` | +| | Package managers (bun, uv) | `devcontainer.json` → `features` | +| | CLI tools (Task, Codex) | `scripts/lib/base-setup.sh` | +| **Editor** | VS Code settings (formatters, rulers, whitespace) | `devcontainer.json` → `customizations.vscode.settings` | +| | VS Code extensions | `devcontainer.json` → `customizations.vscode.extensions` | +| | Debug launch configs | `.vscode/launch.json` (in consuming project) | +| **Shell & User** | Default shell, prompt, oh-my-zsh config | `devcontainer.json` → `common-utils` feature | +| | Shell aliases, functions, plugins | `.devcontainer/config/shell/` | +| | Git config | Host `.gitconfig` (auto-forwarded by devcontainers) | +| | Git hooks | Project repo (`.husky/` or `.githooks/`) | +| **Environment** | Runtime behavior vars (`PYTHONUNBUFFERED`, etc.) | `devcontainer.json` → `containerEnv` | +| | PATH extensions | `devcontainer.json` → `remoteEnv` | +| | Service credentials (dev-only) | `.devcontainer/.env` | +| | Service profiles/toggles | `.devcontainer/.env` → `COMPOSE_PROFILES` | +| | Secrets (API keys, tokens) | Host env forwarded via `remoteEnv` — never committed | +| **Services** | Infrastructure services | `.devcontainer/compose/*.yaml` | +| | Service enable/disable | `.devcontainer/.env` → `COMPOSE_PROFILES` | +| | Service tuning/config | `.devcontainer/config//` | +| **Networking** | Port allocation (container-side) | `.devcontainer/compose/*.yaml` → `ports:` | +| | Port forwarding (to host IDE) | `devcontainer.json` → `forwardPorts` + `portsAttributes` | +| | Service discovery | Automatic via Docker Compose `musher-dev` network | +| **Observability** | Telemetry pipeline config | `.devcontainer/config/observability/otel-collector-config.yaml` | +| | Grafana dashboards | `.devcontainer/config/observability/grafana/provisioning/dashboards/json/` | +| | Grafana datasources | `.devcontainer/config/observability/grafana/provisioning/datasources/` | +| **Data** | DB schema init (base) | `.devcontainer/config/postgres/00-init.sql` | +| | DB schema init (project) | `.devcontainer/config/postgres/01-project.sql` | +| | DB migrations | Project tooling (Atlas, Flyway — not in template) | +| | Data persistence | Compose files → named volumes | +| **Lifecycle** | One-time container setup | `scripts/post-create.sh` → `lib/base-setup.sh` | +| | Every-start tasks | `scripts/startup.sh` | +| | Task automation | `Taskfile.yml` (in consuming project) | +| **AI Tools** | AI CLI installation | `lib/base-setup.sh` (Claude, Codex) | +| | AI CLI config persistence | `devcontainer.json` → `mounts` (named volumes) | +| **Security** | Container capabilities | `devcontainer.json` → `capAdd` / `securityOpt` | +| | Network binding | Compose files → all ports bound to `127.0.0.1` | + +--- + +## Runtimes & Tools + +### Language Runtimes + +Add or remove runtimes in `devcontainer.json` → `features`: + +```jsonc +"features": { + "ghcr.io/devcontainers/features/node:1": { "version": "lts" }, + "ghcr.io/devcontainers/features/python:1": { "version": "3.13" }, + "ghcr.io/devcontainers/features/go:1": {}, + "ghcr.io/devcontainers/features/java:1": { "version": "17" }, + "ghcr.io/devcontainers-extra/features/deno:1": {} +} +``` + +Comment out any runtime you don't need. Also comment out the corresponding VS Code extension. + +### CLI Tools + +Tools installed via curl or npm go in `scripts/lib/base-setup.sh`. Each tool gets its own function: + +```bash +base_install_mytool() { + if has_cmd mytool; then + log "mytool already installed, skipping" + return 0 + fi + log "Installing mytool..." + retry 3 5 bash -c 'curl -fsSL https://mytool.dev/install.sh | bash' +} +``` + +Add the call to `base_setup()` and to `base_verify_tools`. + +--- + +## Editor + +### VS Code Settings + +All editor settings live in `devcontainer.json` → `customizations.vscode.settings`. Do not create a `.vscode/settings.json` in the template — that's for consuming projects. + +### VS Code Extensions + +All extensions live in `devcontainer.json` → `customizations.vscode.extensions`. Comment out extensions for runtimes you don't use. + +--- + +## Shell & User + +### Shell Customization + +Shell customization uses a shared/local pattern: + +| Pattern | Tracked | Purpose | +|---|---|---| +| `*.shared.sh` | Yes | Team defaults — aliases, functions, plugin config | +| `*.local.sh` | No (gitignored) | Personal overrides — machine-specific settings | + +Shared files are sourced first, then local files, so local settings override team defaults. Edit `aliases.shared.sh` for team-wide aliases, or create a `*.local.sh` file for personal customizations. + +### Git Config + +Git config is auto-forwarded from your host machine by the devcontainer CLI. No configuration needed in the template. + +--- + +## Environment Variables + +There are four distinct scopes for environment variables. Use the right one: + +| Scope | Location | When to Use | +|---|---|---| +| **Container-wide** | `devcontainer.json` → `containerEnv` | Runtime behavior (`PYTHONUNBUFFERED`, `UV_LINK_MODE`) | +| **Remote/IDE** | `devcontainer.json` → `remoteEnv` | PATH extensions, forwarded host secrets | +| **Compose services** | `.devcontainer/.env` | Service credentials, `COMPOSE_PROFILES` | +| **Service-specific** | `compose/*.yaml` → `environment:` | Internal service config (uses `${VAR:-default}` interpolation) | + +### Secrets + +Never commit secrets. Forward them from your host environment: + +```jsonc +"remoteEnv": { + "MY_API_KEY": "${localEnv:MY_API_KEY}" +} +``` + +### Service Credentials + +Dev-only credentials live in `.devcontainer/.env` (gitignored). Copy from `.env.example` on first use — `post-create.sh` does this automatically. + +--- + +## Services + +### Enabling/Disabling Services + +All services are included in `compose.yaml`. Optional services are gated by Compose profiles: + +| Service | Profile | Always On? | +|---|---|---| +| PostgreSQL | — | Yes | +| Redis | `redis` | No | +| MinIO | `minio` | No | +| OCI Registry | `registry` | No | +| Azimutt | `azimutt` | No | +| Observability stack | `observability` | No | + +Enable services by setting `COMPOSE_PROFILES` in `.devcontainer/.env`: + +```env +COMPOSE_PROFILES=redis,minio,observability +``` + +### Adding a New Service + +1. Create `compose/myservice.yaml` +2. Add `- compose/myservice.yaml` to `compose.yaml` `include:` +3. Optionally add `profiles: [myservice]` if it should be opt-in +4. Add port forwarding in `devcontainer.json` → `forwardPorts` and `portsAttributes` +5. Use `${VAR:-default}` for any credentials, and add them to `.env.example` + +### Service Configuration + +Service-specific config files go under `.devcontainer/config//`: + +``` +config/ + postgres/ SQL init scripts + observability/ OTel, Grafana, Tempo, Loki configs + shell/ Shell aliases and functions +``` + +--- + +## Networking + +### Port Allocation + +All ports are bound to `127.0.0.1` (localhost only) for security. The template uses the `154xx` range: + +| Port | Service | Protocol | +|---|---|---| +| 15432 | PostgreSQL | TCP | +| 15433 | Redis | TCP | +| 15434 | MinIO API | HTTP | +| 15435 | MinIO Console | HTTP | +| 15436 | OCI Registry | HTTP | +| 15440 | MinIO API (Observability) | HTTP | +| 15441 | MinIO Console (Observability) | HTTP | +| 15442 | Tempo | HTTP | +| 15443 | Loki | HTTP | +| 15444 | VictoriaMetrics | HTTP | +| 15445 | OTel Collector HTTP | HTTP | +| 15446 | OTel Collector gRPC | gRPC | +| 15447 | Grafana | HTTP | +| 15448 | Pyroscope | HTTP | +| 15460 | Azimutt | HTTP | + +### Service Discovery + +Services communicate via the `musher-dev` Docker network. Use the service name as the hostname (e.g., `postgres`, `redis`, `minio-observability`) with the container-internal port. + +--- + +## Observability + +The observability stack is profile-gated (`COMPOSE_PROFILES=observability`). It includes: + +- **Grafana** — Dashboards and visualization (port 15447) +- **Tempo** — Distributed tracing backend +- **Loki** — Log aggregation +- **VictoriaMetrics** — Metrics storage +- **OTel Collector** — Telemetry pipeline (receives OTLP on ports 15445/15446) +- **Pyroscope** — Continuous profiling +- **MinIO (Observability)** — Object storage for Tempo and Loki + +### Configuration Files + +| File | Purpose | +|---|---| +| `config/observability/otel-collector-config.yaml` | OTel Collector pipeline configuration | +| `config/observability/tempo-config.yaml` | Tempo storage and ingestion config | +| `config/observability/loki-config.yaml` | Loki storage and ingestion config | +| `config/observability/grafana/provisioning/datasources/` | Auto-provisioned Grafana datasources | +| `config/observability/grafana/provisioning/dashboards/json/` | Auto-provisioned Grafana dashboards | + +> **Note:** `tempo-config.yaml` and `loki-config.yaml` contain hardcoded MinIO credentials because they are native YAML configs that don't support environment variable interpolation. If you change `MINIO_OBS_ROOT_USER` or `MINIO_OBS_ROOT_PASSWORD` in `.env`, you must also update these files to match. + +--- + +## Data + +### Database Initialization + +SQL files in `.devcontainer/config/postgres/` are mounted into PostgreSQL's `docker-entrypoint-initdb.d/` and run in alphabetical order on first container creation: + +- `00-init.sql` — Base schema (extensions, shared types) +- `01-project.sql.example` — Project-specific schema (copy to `01-project.sql`) + +### Persistence + +All services use named Docker volumes (e.g., `musher-postgres-data`). Data persists across container restarts but is lost on full rebuild. For migrations, use project-level tooling (Atlas, Flyway, etc.). + +### Adding Volumes + +Follow the naming convention `musher-${devcontainerId}-`: + +```jsonc +"source=musher-${devcontainerId}-my-tool,target=/home/vscode/.my-tool,type=volume" +``` + +--- + +## Lifecycle + +| Hook | Runs | Use For | +|---|---|---| +| `postCreateCommand` | Once, on container creation | Tool installation, permissions, `.env` setup | +| `postStartCommand` | Every container start | `docker compose up`, health checks | + +### Skipping Base Steps + +Call individual functions instead of `base_setup`: + +```bash +main() { + log "Starting post-create setup..." + base_setup_config_dirs + base_fix_nvm_permissions + # Skip codex: base_install_codex + base_verify_tools + log "Post-create setup completed" +} +``` + +### Script Layers + +``` +post-create.sh ← Entry point (repo-specific customization) + └── lib/base-setup.sh ← Reusable orchestrator (AI CLIs, Task, NVM, config dirs) + └── lib/common.sh ← Shared utilities (log, retry, has_cmd, ensure_writable_dir) +``` + +--- + +## AI Tools + +### Installed CLIs + +- **Claude CLI** — Installed via the native installer in `base-setup.sh`, config persisted in `~/.claude` volume +- **Codex CLI** — Installed via npm in `base-setup.sh`, config persisted in `~/.codex` volume + +### Configuration Persistence + +AI CLI configs are stored in named volumes mounted via `devcontainer.json` → `mounts`. This preserves authentication and settings across container rebuilds. + +--- + +## Directory Map + +``` +.devcontainer/ + devcontainer.json Runtimes, extensions, settings, mounts, ports + compose.yaml Service orchestrator (includes compose/*.yaml) + .env.example Environment template (copy to .env) + .env Local overrides (gitignored) + compose/ + postgres.yaml PostgreSQL with pgvector (always on) + redis.yaml Redis (profile: redis) + minio.yaml MinIO S3 storage (profile: minio) + registry.yaml OCI Registry (profile: registry) + azimutt.yaml DB explorer UI (profile: azimutt) + observability.yaml Full observability stack (profile: observability) + config/ + postgres/ + 00-init.sql Base DB schema + 01-project.sql.example Project schema template + observability/ + otel-collector-config.yaml + tempo-config.yaml + loki-config.yaml + grafana/provisioning/ + datasources/ Auto-provisioned datasources + dashboards/json/ Auto-provisioned dashboards + shell/ + aliases.shared.sh Team-default shell aliases + README.md Shell customization docs + scripts/ + post-create.sh One-time setup entry point + startup.sh Every-start service launcher + lib/ + base-setup.sh Reusable tool installer + common.sh Shared utilities +``` diff --git a/README.md b/README.md index 93ddb20..329276e 100644 --- a/README.md +++ b/README.md @@ -1,89 +1,105 @@ -# Musher Demo - -A showcase repository for the [Musher](https://hub.musher.dev) platform. It contains a ready-to-publish bundle with practical skills and agents, plus Python and TypeScript SDK examples that consume those same assets — demonstrating the full end-to-end value proposition. - -## What's Inside - -``` -demo/ -├── bundle/ Musher bundle (skills + agents) -│ ├── musher.yaml Bundle manifest -│ ├── skills/ -│ │ ├── summarize-changes/ Summarize git history and diffs -│ │ └── write-commit-message/ Generate Conventional Commits messages -│ └── agents/ -│ └── code-reviewer.md Specialist read-only code-review agent -└── examples/ SDK usage examples - ├── python/ Python SDK (pull, install skills, use with Claude) - └── typescript/ TypeScript SDK (pull, use with Claude) -``` - -## The Bundle - -The `musher-dev/demo-starter` bundle ships two skills and one agent that solve everyday developer workflow problems. - -| Asset | Type | What it does | -|-------|------|--------------| -| `summarize-changes` | Skill | Summarize git history, staged diffs, or PR changes into themed summaries | -| `write-commit-message` | Skill | Generate Conventional Commits–compliant messages from staged diffs | -| `code-reviewer` | Agent | Read-only specialist that reviews code with severity-graded findings | - -### Use with a CLI harness - -Install via the [Mush CLI](https://github.com/musher-dev/mush) into any project: - -```bash -# Install and add to your project -mush bundle install musher-dev/demo-starter - -# Or run ephemerally -mush bundle load musher-dev/demo-starter -``` - -Then invoke from Claude Code, Codex, or any supported harness: - -``` -Summarize the changes since the last release -Write a commit message for my staged changes -Use the code-reviewer agent to review the auth module -``` - -## SDK Examples - -The `examples/` directory shows how to consume the same bundle assets programmatically. - -### Python - -```bash -cd examples/python -pip install -r requirements.txt -export MUSHER_API_KEY="mush_..." -python 01_pull_bundle.py # Inspect bundle contents -python 02_install_skills.py # Install skills into a Claude Code project -python 03_use_with_claude.py # Use skills as system prompts with Anthropic API -``` - -### TypeScript - -```bash -cd examples/typescript -npm install -export MUSHER_API_KEY="mush_..." -npm run pull-bundle # Inspect bundle contents -npm run use-with-claude # Use skills as system prompts with Anthropic SDK -``` - -## Related Repositories - -| Repo | Description | -|------|-------------| -| [musher-dev/mush](https://github.com/musher-dev/mush) | Mush CLI — install and run bundles locally | -| [musher-dev/musher-cli](https://github.com/musher-dev/musher-cli) | CLI for publishing bundles to the registry | -| [musher-dev/python-sdk](https://github.com/musher-dev/python-sdk) | Python SDK for the Musher platform | -| [musher-dev/typescript-sdk](https://github.com/musher-dev/typescript-sdk) | TypeScript SDK for the Musher platform | -| [musher-dev/specs](https://github.com/musher-dev/specs) | Bundle definition schemas and specifications | -| [musher-dev/bundles](https://github.com/musher-dev/bundles) | Community bundle collection | - -## Dev Container - -This repo includes a batteries-included dev container with Node, Python, Go, Claude CLI, Codex CLI, and all the tooling needed to work on the bundle and examples. See [CONFIGURATION.md](CONFIGURATION.md) for full details. +# Musher Demo + +A showcase repository for the [Musher](https://hub.musher.dev) platform. It contains a ready-to-publish bundle with practical skills and agents, plus Python and TypeScript SDK examples that consume those same assets — demonstrating the full end-to-end value proposition. + +## Start Here + +Install the [Musher CLI](https://github.com/musher-dev/musher-cli): + +```bash +curl -fsSL https://get.musher.dev | sh +``` + +## What's Inside + +``` +demo/ +├── bundle/ Musher bundle (skills + agents) +│ ├── musher.yaml Bundle manifest +│ ├── skills/ +│ │ ├── summarizing-changes/ Summarize git history and diffs +│ │ └── writing-commit-messages/ Generate Conventional Commits messages +│ └── agents/ +│ └── code-reviewer.md Specialist read-only code-review agent +└── examples/ SDK usage examples + ├── python/ Python SDK (pull, install skills, use with Claude) + └── typescript/ TypeScript SDK (pull, install skills, use with Claude) +``` + +## The Bundle + +The `musher-examples/demo-starter` bundle ships two skills and one agent that solve everyday developer workflow problems. + +| Asset | Type | What it does | +|-------|------|--------------| +| `summarizing-changes` | Skill | Summarize git history, staged diffs, or PR changes into themed summaries | +| `writing-commit-messages` | Skill | Generate Conventional Commits–compliant messages from staged diffs | +| `code-reviewer` | Agent | Read-only specialist that reviews code with severity-graded findings | + +### Use with a CLI harness + +Install via the [Musher CLI](https://github.com/musher-dev/musher-cli) into any project: + +```bash +# Install and add to your project +musher bundle pull musher-examples/demo-starter + +# Or run ephemerally +musher bundle load musher-examples/demo-starter +``` + +Then invoke from Claude Code, Codex, or any supported harness: + +``` +Summarize the changes since the last release +Write a commit message for my staged changes +Use the code-reviewer agent to review the auth module +``` + +## SDK Examples + +The `examples/` directory shows how to consume the same bundle assets programmatically. + +Both SDKs need a `MUSHER_API_KEY` — sign up at [hub.musher.dev](https://hub.musher.dev) and generate one under **Settings > API Keys**. Examples that call the Anthropic API also need an `ANTHROPIC_API_KEY` from [console.anthropic.com](https://console.anthropic.com/). + +> **Note — local source vs. published names:** +> The `bundle/` directory contains the *source* files for this bundle. Asset names in the +> source manifest (`musher.yaml`) may differ from names in the published bundle on +> [hub.musher.dev](https://hub.musher.dev). The SDK examples reference the **published** +> names (e.g. `summarizing-changes` rather than the source name `summarize-changes`). + +### Python + +```bash +cd examples/python +pip install -r requirements.txt +export MUSHER_API_KEY="mush_..." +python 01_pull_bundle.py # Inspect bundle contents +python 02_install_skills.py # Install skills into a Claude Code project +python 03_use_with_claude.py # Use skills as system prompts with Anthropic API +``` + +### TypeScript + +```bash +cd examples/typescript +npm install +export MUSHER_API_KEY="mush_..." +npm run pull-bundle # Inspect bundle contents +npm run install-skills # Install skills into a Claude Code project +npm run use-with-claude # Use skills as system prompts with Anthropic SDK +``` + +## Related Repositories + +| Repo | Description | +|------|-------------| +| [musher-dev/musher-cli](https://github.com/musher-dev/musher-cli) | Musher CLI — install, run, author, and publish bundles | +| [musher-dev/python-sdk](https://github.com/musher-dev/python-sdk) | Python SDK for the Musher platform | +| [musher-dev/typescript-sdk](https://github.com/musher-dev/typescript-sdk) | TypeScript SDK for the Musher platform | +| [musher-dev/specs](https://github.com/musher-dev/specs) | Bundle definition schemas and specifications | +| [musher-dev/bundles](https://github.com/musher-dev/bundles) | Community bundle collection | + +## Dev Container + +This repo includes a batteries-included dev container with Node, Python, Go, Claude CLI, Codex CLI, and all the tooling needed to work on the bundle and examples. See [CONFIGURATION.md](CONFIGURATION.md) for full details. diff --git a/bundle/README.md b/bundle/README.md index 6db632e..51b86e5 100644 --- a/bundle/README.md +++ b/bundle/README.md @@ -1,63 +1,63 @@ -# Demo Starter - -A demonstration bundle for the [Musher](https://hub.musher.dev) platform. Ships three practical assets — two skills and a specialist agent — that work across all supported CLI harnesses (Claude Code, Codex, OpenCode, Copilot, Gemini CLI). - -## Quick Start - -Install via the [Mush CLI](https://github.com/musher-dev/mush): - -```sh -mush bundle install musher-dev/demo-starter -``` - -Then invoke from any compatible harness: - -``` -Summarize the changes in the last 5 commits -``` - -``` -Write a conventional commit message for my staged changes -``` - -Or delegate to the specialist agent: - -``` -Use the code-reviewer agent to review my latest changes -``` - -## What's Inside - -### Skills - -| Skill | Description | -|-------|-------------| -| `summarize-changes` | Summarize git history, staged diffs, or PR changes into clear, human-readable summaries | -| `write-commit-message` | Generate Conventional Commits–compliant messages from staged diffs or a description of the change | - -### Agents - -| Agent | Description | -|-------|-------------| -| `code-reviewer` | A specialist read-only agent that reviews code for quality, correctness, and best-practice adherence with structured feedback | - -## Usage Examples - -**Summarize recent changes** -``` -Summarize the changes since the last release -``` - -**Write a commit message** -``` -Write a commit message for my staged changes -``` - -**Delegate a thorough code review** -``` -Use the code-reviewer agent to review the authentication module -``` - ---- - -**Domain:** developer-workflows · **Technologies:** Harness-agnostic · **Aliases:** demo, starter, git-workflows +# Demo Starter + +A demonstration bundle for the [Musher](https://hub.musher.dev) platform. Ships three practical assets — two skills and a specialist agent — that work across all supported CLI harnesses (Claude Code, Codex, OpenCode, Copilot, Gemini CLI). + +## Quick Start + +Install via the [Musher CLI](https://github.com/musher-dev/musher-cli): + +```sh +musher bundle pull musher-examples/demo-starter +``` + +Then invoke from any compatible harness: + +``` +Summarize the changes in the last 5 commits +``` + +``` +Write a conventional commit message for my staged changes +``` + +Or delegate to the specialist agent: + +``` +Use the code-reviewer agent to review my latest changes +``` + +## What's Inside + +### Skills + +| Skill | Description | +|-------|-------------| +| `summarizing-changes` | Summarize git history, staged diffs, or PR changes into clear, human-readable summaries | +| `writing-commit-messages` | Generate Conventional Commits–compliant messages from staged diffs or a description of the change | + +### Agents + +| Agent | Description | +|-------|-------------| +| `code-reviewer` | A specialist read-only agent that reviews code for quality, correctness, and best-practice adherence with structured feedback | + +## Usage Examples + +**Summarize recent changes** +``` +Summarize the changes since the last release +``` + +**Write a commit message** +``` +Write a commit message for my staged changes +``` + +**Delegate a thorough code review** +``` +Use the code-reviewer agent to review the authentication module +``` + +--- + +**Domain:** developer-workflows · **Technologies:** Harness-agnostic · **Aliases:** demo, starter, git-workflows diff --git a/bundle/agents/code-reviewer.md b/bundle/agents/code-reviewer.md index 382bef8..c5f09ac 100644 --- a/bundle/agents/code-reviewer.md +++ b/bundle/agents/code-reviewer.md @@ -1,94 +1,94 @@ ---- -name: code-reviewer -description: Review code for quality, correctness, security, and best-practice adherence. Produces structured feedback with severity levels and actionable suggestions. Use when reviewing pull requests, auditing new code, or validating a refactor before merging. Triggered by: review code, code review, review this PR, review my changes, audit code, check code quality, review diff, review file. -tools: Read, Grep, Glob, Bash -model: sonnet -permissionMode: default ---- - -# Code Reviewer - -You are a senior software engineer specializing in code review. Your role is to provide thorough, constructive, and actionable feedback on code changes. - -## Responsibilities - -1. **Correctness**: Identify logic errors, off-by-one errors, incorrect assumptions, and unhandled edge cases. -2. **Security**: Spot injection vulnerabilities, improper input validation, hardcoded secrets, and insecure defaults. -3. **Readability**: Flag unclear naming, missing or misleading comments, and overly complex logic that should be simplified. -4. **Maintainability**: Note violations of SOLID principles, excessive coupling, missing tests, and code that will be difficult to change. -5. **Performance**: Highlight unnecessary allocations, N+1 queries, blocking operations in hot paths, and missing indexes. -6. **Best Practices**: Identify deviations from established patterns in the codebase. - -## Process - -When activated: - -1. **Understand scope** — Determine what needs to be reviewed: - - If given specific files or paths, read those. - - If asked to review "staged changes" or a diff, run `git diff --cached` or `git diff `. - - If given a PR description, use it for additional context. - -2. **Read the code** — Use `Read`, `Grep`, and `Glob` to understand: - - The files being changed - - Related files that provide context (tests, interfaces, callers) - - Project conventions (look for `.editorconfig`, linting configs, existing patterns) - -3. **Analyze** — For each file or logical section, assess the five review dimensions above. - -4. **Produce findings** — Categorize each finding by severity (see below) and include a concrete suggestion. - -## Output Format - -Structure your review as follows: - ---- - -### Review Summary - -> Brief one-paragraph overview of the change and your overall assessment. - ---- - -### Findings - -Group findings by file. For each finding: - -``` -[SEVERITY] path/to/file.ext:LINE — Title - -Description of the issue and why it matters. - -Suggestion: - -``` - -**Severity levels:** - -| Level | Meaning | -|-------|---------| -| 🔴 CRITICAL | Must fix before merging — breaks correctness or introduces a security vulnerability | -| 🟠 MAJOR | Should fix — significant quality, maintainability, or performance problem | -| 🟡 MINOR | Nice to fix — code smell, readability improvement, or minor inefficiency | -| 🔵 NIT | Optional — style preference or very minor suggestion | -| ✅ PRAISE | Highlight something done well | - ---- - -### Verdict - -One of: -- ✅ **Approve** — Ready to merge as-is (or with optional nits addressed) -- 🟡 **Approve with suggestions** — Mergeable, but MINOR findings should be addressed -- 🔴 **Request changes** — CRITICAL or MAJOR findings must be resolved before merging - ---- - -## Guardrails - -- **DO NOT** modify any files. This agent is read-only. -- **DO NOT** run tests, build commands, or any commands that have side effects. -- **DO NOT** write vague feedback like "this could be better." Always explain why and suggest a fix. -- **ALWAYS** acknowledge what is done well alongside what needs improvement. -- **ALWAYS** be respectful and constructive. Assume good intent from the author. -- **ALWAYS** provide a concrete suggestion for every CRITICAL and MAJOR finding. -- If you cannot determine whether something is an issue without more context, ask a clarifying question rather than guessing. +--- +name: code-reviewer +description: Review code for quality, correctness, security, and best-practice adherence. Produces structured feedback with severity levels and actionable suggestions. Use when reviewing pull requests, auditing new code, or validating a refactor before merging. Triggered by: review code, code review, review this PR, review my changes, audit code, check code quality, review diff, review file. +tools: Read, Grep, Glob, Bash +model: sonnet +permissionMode: default +--- + +# Code Reviewer + +You are a senior software engineer specializing in code review. Your role is to provide thorough, constructive, and actionable feedback on code changes. + +## Responsibilities + +1. **Correctness**: Identify logic errors, off-by-one errors, incorrect assumptions, and unhandled edge cases. +2. **Security**: Spot injection vulnerabilities, improper input validation, hardcoded secrets, and insecure defaults. +3. **Readability**: Flag unclear naming, missing or misleading comments, and overly complex logic that should be simplified. +4. **Maintainability**: Note violations of SOLID principles, excessive coupling, missing tests, and code that will be difficult to change. +5. **Performance**: Highlight unnecessary allocations, N+1 queries, blocking operations in hot paths, and missing indexes. +6. **Best Practices**: Identify deviations from established patterns in the codebase. + +## Process + +When activated: + +1. **Understand scope** — Determine what needs to be reviewed: + - If given specific files or paths, read those. + - If asked to review "staged changes" or a diff, run `git diff --cached` or `git diff `. + - If given a PR description, use it for additional context. + +2. **Read the code** — Use `Read`, `Grep`, and `Glob` to understand: + - The files being changed + - Related files that provide context (tests, interfaces, callers) + - Project conventions (look for `.editorconfig`, linting configs, existing patterns) + +3. **Analyze** — For each file or logical section, assess the five review dimensions above. + +4. **Produce findings** — Categorize each finding by severity (see below) and include a concrete suggestion. + +## Output Format + +Structure your review as follows: + +--- + +### Review Summary + +> Brief one-paragraph overview of the change and your overall assessment. + +--- + +### Findings + +Group findings by file. For each finding: + +``` +[SEVERITY] path/to/file.ext:LINE — Title + +Description of the issue and why it matters. + +Suggestion: + +``` + +**Severity levels:** + +| Level | Meaning | +|-------|---------| +| 🔴 CRITICAL | Must fix before merging — breaks correctness or introduces a security vulnerability | +| 🟠 MAJOR | Should fix — significant quality, maintainability, or performance problem | +| 🟡 MINOR | Nice to fix — code smell, readability improvement, or minor inefficiency | +| 🔵 NIT | Optional — style preference or very minor suggestion | +| ✅ PRAISE | Highlight something done well | + +--- + +### Verdict + +One of: +- ✅ **Approve** — Ready to merge as-is (or with optional nits addressed) +- 🟡 **Approve with suggestions** — Mergeable, but MINOR findings should be addressed +- 🔴 **Request changes** — CRITICAL or MAJOR findings must be resolved before merging + +--- + +## Guardrails + +- **DO NOT** modify any files. This agent is read-only. +- **DO NOT** run tests, build commands, or any commands that have side effects. +- **DO NOT** write vague feedback like "this could be better." Always explain why and suggest a fix. +- **ALWAYS** acknowledge what is done well alongside what needs improvement. +- **ALWAYS** be respectful and constructive. Assume good intent from the author. +- **ALWAYS** provide a concrete suggestion for every CRITICAL and MAJOR finding. +- If you cannot determine whether something is an issue without more context, ask a clarifying question rather than guessing. diff --git a/bundle/musher.yaml b/bundle/musher.yaml index 72383c5..31d5858 100644 --- a/bundle/musher.yaml +++ b/bundle/musher.yaml @@ -1,28 +1,28 @@ -# yaml-language-server: $schema=https://schemas.musher.dev/bundle-definition/v1alpha1/schema.json -namespace: musher-dev -slug: demo-starter -version: 1.0.0 -name: Demo Starter -description: A demonstration bundle showcasing the Musher platform with practical skills and agents for everyday development workflows. -visibility: public -readme: README.md -license: MIT -repository: https://github.com/musher-dev/demo -keywords: - - demo - - git - - code-review - - commit - - changelog -assets: - - id: summarize-changes - src: skills/summarize-changes/SKILL.md - kind: skill - - - id: write-commit-message - src: skills/write-commit-message/SKILL.md - kind: skill - - - id: code-reviewer - src: agents/code-reviewer.md - kind: agent +# yaml-language-server: $schema=https://schemas.musher.dev/bundle-definition/v1alpha1/schema.json +namespace: musher-examples +slug: demo-starter +version: 1.0.0 +name: Demo Starter +description: A demonstration bundle showcasing the Musher platform with practical skills and agents for everyday development workflows. +visibility: public +readme: README.md +license: MIT +repository: https://github.com/musher-dev/demo +keywords: + - demo + - git + - code-review + - commit + - changelog +assets: + - id: summarizing-changes + src: skills/summarizing-changes/SKILL.md + kind: skill + + - id: writing-commit-messages + src: skills/writing-commit-messages/SKILL.md + kind: skill + + - id: code-reviewer + src: agents/code-reviewer.md + kind: agent diff --git a/bundle/skills/summarize-changes/SKILL.md b/bundle/skills/summarizing-changes/SKILL.md similarity index 96% rename from bundle/skills/summarize-changes/SKILL.md rename to bundle/skills/summarizing-changes/SKILL.md index 7b37d17..6ade0c5 100644 --- a/bundle/skills/summarize-changes/SKILL.md +++ b/bundle/skills/summarizing-changes/SKILL.md @@ -1,153 +1,153 @@ ---- -name: summarize-changes -version: 1.0.0 -user-invocable: true -description: Summarize git history, staged diffs, or pull request changes into clear, human-readable summaries grouped by theme. Use when you need to understand what changed, generate a release summary, or explain recent work to stakeholders. Triggered by: summarize changes, what changed, summarize commits, summarize diff, change summary, release notes, what's new, summarize PR. -argument-hint: "[||staged] [--format short|detailed|release]" -allowed-tools: - - Bash - - Read - - Grep - - Glob ---- - -# Summarize Changes - -Produce a clear, human-readable summary of git changes grouped by theme (features, fixes, refactors, chores). - -## Input - -**Arguments:** `$ARGUMENTS` - -Parse the arguments to determine scope and format: - -- **Scope** (what to summarize): - - A commit range like `HEAD~5..HEAD` or `v1.2.0..HEAD` - - A branch name to compare against the current branch's base - - The word `staged` to summarize only staged changes - - Empty → default to all changes since the last tag, or the last 10 commits if no tags exist - -- **Format** (`--format`): - - `short` — one-line bullets per logical change - - `detailed` — bullets with brief context for each change (default) - - `release` — formatted as a release notes section with a header and categorized lists - ---- - -## Phase 1: Discover What Changed - -Run the appropriate git commands based on the scope: - -**For a commit range or branch:** -```bash -git log --oneline --no-merges -git diff --stat -``` - -**For staged changes:** -```bash -git diff --cached --stat -git diff --cached -``` - -**For default (since last tag or last 10 commits):** -```bash -# Check if tags exist -git tag --sort=-version:refname | head -1 - -# If a tag exists, compare since that tag -git log ..HEAD --oneline --no-merges -git diff ..HEAD --stat - -# If no tags, use last 10 commits -git log HEAD~10..HEAD --oneline --no-merges -git diff HEAD~10..HEAD --stat -``` - ---- - -## Phase 2: Analyze and Group - -Read the commit messages and diff stats. Group changes into these categories (omit any empty category): - -| Category | What belongs here | -|----------|------------------| -| ✨ Features | New functionality, additions, enhancements | -| 🐛 Bug Fixes | Defect corrections, error handling improvements | -| ♻️ Refactors | Code restructuring without behavior change | -| 📚 Documentation | README updates, inline comments, docs changes | -| 🧪 Tests | New or updated tests | -| 🔧 Chores | Dependency updates, CI config, build tooling | -| 🗑️ Removals | Deleted files, deprecated feature removal | - -For each commit/change: -1. Determine the most appropriate category -2. Extract a concise description (drop implementation details, keep user-visible impact) -3. Note affected files or modules if relevant for `detailed` and `release` formats - ---- - -## Phase 3: Generate the Summary - -### Short format - -``` -✨ Features -- Added user authentication via OAuth2 -- New dark mode toggle in settings - -🐛 Bug Fixes -- Fixed race condition in cache invalidation - -🔧 Chores -- Updated dependencies to latest versions -``` - -### Detailed format - -``` -✨ Features -- **OAuth2 authentication** — Users can now sign in with Google and GitHub (src/auth/) -- **Dark mode** — New theme toggle persisted to user preferences (src/ui/settings/) - -🐛 Bug Fixes -- **Cache race condition** — Fixed concurrent write conflict in the Redis cache layer (src/cache/) - -🔧 Chores -- Bumped all npm dependencies to latest patch versions -``` - -### Release format - -```markdown -## What's Changed - -### ✨ New Features -- Users can now sign in with Google and GitHub via OAuth2 -- Dark mode is now available and persists across sessions - -### 🐛 Bug Fixes -- Fixed a race condition in the Redis cache layer that could cause stale reads under high concurrency - -### 🔧 Maintenance -- Updated all dependencies to latest patch versions -``` - ---- - -## Phase 4: Output - -Print the formatted summary. If the scope was empty and you defaulted to a range, briefly note which range was used at the top: - -> *Summarizing 10 commits (a1b2c3d → HEAD)* - -End with a one-line count: `X files changed, Y insertions(+), Z deletions(-)` (from `git diff --stat`). - ---- - -## Edge Cases - -- **No changes found** → Print "No changes found in the specified scope." and stop. -- **Binary files only** → Note them as "Binary file changes" in the Chores section. -- **Merge commits** → Skip merge commits; summarize only the substantive commits. -- **Very large diffs (>500 files)** → Summarize by directory rather than by individual file. +--- +name: summarizing-changes +version: 1.0.0 +user-invocable: true +description: Summarize git history, staged diffs, or pull request changes into clear, human-readable summaries grouped by theme. Use when you need to understand what changed, generate a release summary, or explain recent work to stakeholders. Triggered by: summarize changes, what changed, summarize commits, summarize diff, change summary, release notes, what's new, summarize PR. +argument-hint: "[||staged] [--format short|detailed|release]" +allowed-tools: + - Bash + - Read + - Grep + - Glob +--- + +# Summarize Changes + +Produce a clear, human-readable summary of git changes grouped by theme (features, fixes, refactors, chores). + +## Input + +**Arguments:** `$ARGUMENTS` + +Parse the arguments to determine scope and format: + +- **Scope** (what to summarize): + - A commit range like `HEAD~5..HEAD` or `v1.2.0..HEAD` + - A branch name to compare against the current branch's base + - The word `staged` to summarize only staged changes + - Empty → default to all changes since the last tag, or the last 10 commits if no tags exist + +- **Format** (`--format`): + - `short` — one-line bullets per logical change + - `detailed` — bullets with brief context for each change (default) + - `release` — formatted as a release notes section with a header and categorized lists + +--- + +## Phase 1: Discover What Changed + +Run the appropriate git commands based on the scope: + +**For a commit range or branch:** +```bash +git log --oneline --no-merges +git diff --stat +``` + +**For staged changes:** +```bash +git diff --cached --stat +git diff --cached +``` + +**For default (since last tag or last 10 commits):** +```bash +# Check if tags exist +git tag --sort=-version:refname | head -1 + +# If a tag exists, compare since that tag +git log ..HEAD --oneline --no-merges +git diff ..HEAD --stat + +# If no tags, use last 10 commits +git log HEAD~10..HEAD --oneline --no-merges +git diff HEAD~10..HEAD --stat +``` + +--- + +## Phase 2: Analyze and Group + +Read the commit messages and diff stats. Group changes into these categories (omit any empty category): + +| Category | What belongs here | +|----------|------------------| +| ✨ Features | New functionality, additions, enhancements | +| 🐛 Bug Fixes | Defect corrections, error handling improvements | +| ♻️ Refactors | Code restructuring without behavior change | +| 📚 Documentation | README updates, inline comments, docs changes | +| 🧪 Tests | New or updated tests | +| 🔧 Chores | Dependency updates, CI config, build tooling | +| 🗑️ Removals | Deleted files, deprecated feature removal | + +For each commit/change: +1. Determine the most appropriate category +2. Extract a concise description (drop implementation details, keep user-visible impact) +3. Note affected files or modules if relevant for `detailed` and `release` formats + +--- + +## Phase 3: Generate the Summary + +### Short format + +``` +✨ Features +- Added user authentication via OAuth2 +- New dark mode toggle in settings + +🐛 Bug Fixes +- Fixed race condition in cache invalidation + +🔧 Chores +- Updated dependencies to latest versions +``` + +### Detailed format + +``` +✨ Features +- **OAuth2 authentication** — Users can now sign in with Google and GitHub (src/auth/) +- **Dark mode** — New theme toggle persisted to user preferences (src/ui/settings/) + +🐛 Bug Fixes +- **Cache race condition** — Fixed concurrent write conflict in the Redis cache layer (src/cache/) + +🔧 Chores +- Bumped all npm dependencies to latest patch versions +``` + +### Release format + +```markdown +## What's Changed + +### ✨ New Features +- Users can now sign in with Google and GitHub via OAuth2 +- Dark mode is now available and persists across sessions + +### 🐛 Bug Fixes +- Fixed a race condition in the Redis cache layer that could cause stale reads under high concurrency + +### 🔧 Maintenance +- Updated all dependencies to latest patch versions +``` + +--- + +## Phase 4: Output + +Print the formatted summary. If the scope was empty and you defaulted to a range, briefly note which range was used at the top: + +> *Summarizing 10 commits (a1b2c3d → HEAD)* + +End with a one-line count: `X files changed, Y insertions(+), Z deletions(-)` (from `git diff --stat`). + +--- + +## Edge Cases + +- **No changes found** → Print "No changes found in the specified scope." and stop. +- **Binary files only** → Note them as "Binary file changes" in the Chores section. +- **Merge commits** → Skip merge commits; summarize only the substantive commits. +- **Very large diffs (>500 files)** → Summarize by directory rather than by individual file. diff --git a/bundle/skills/write-commit-message/SKILL.md b/bundle/skills/writing-commit-messages/SKILL.md similarity index 96% rename from bundle/skills/write-commit-message/SKILL.md rename to bundle/skills/writing-commit-messages/SKILL.md index d07b40e..54d386b 100644 --- a/bundle/skills/write-commit-message/SKILL.md +++ b/bundle/skills/writing-commit-messages/SKILL.md @@ -1,173 +1,173 @@ ---- -name: write-commit-message -version: 1.0.0 -user-invocable: true -description: Generate a Conventional Commits–compliant commit message from staged changes or a plain-language description. Use when you want a well-structured commit message, are unsure how to categorize a change, or want to ensure your commit history stays readable. Triggered by: write commit message, commit message, conventional commit, git commit, commit this, what should I commit. -argument-hint: "[description of the change]" -allowed-tools: - - Bash - - Read ---- - -# Write Commit Message - -Generate a clear, Conventional Commits–compliant commit message. - -## Conventional Commits Format - -``` -(): - -[optional body] - -[optional footer(s)] -``` - -**Types:** - -| Type | When to use | -|------|-------------| -| `feat` | A new feature or capability | -| `fix` | A bug fix | -| `docs` | Documentation changes only | -| `style` | Formatting, whitespace (no logic change) | -| `refactor` | Code restructuring without feature/fix | -| `test` | Adding or updating tests | -| `chore` | Build tooling, dependencies, CI, config | -| `perf` | Performance improvements | -| `ci` | CI/CD pipeline changes | -| `revert` | Reverting a previous commit | - ---- - -## Input - -**Arguments:** `$ARGUMENTS` - -- If arguments are provided, treat them as a plain-language description of the change. -- If arguments are empty, inspect staged changes. - ---- - -## Phase 1: Gather Context - -**If a description was provided in `$ARGUMENTS`:** -Use it directly to infer the type, scope, and short description. Skip to Phase 2. - -**If no description was provided:** -Inspect the staged diff: - -```bash -git diff --cached --stat -git diff --cached -``` - -If there are no staged changes, check if there are unstaged changes: - -```bash -git diff --stat -``` - -If no changes at all: -> "No staged or unstaged changes found. Stage your changes with `git add` first, or provide a description as an argument." - -Stop. - ---- - -## Phase 2: Determine Type and Scope - -Analyze the diff or description: - -1. **Type**: Choose the most specific matching type from the table above. When in doubt between `feat` and `fix`, prefer the one that matches the user's intent. - -2. **Scope** (optional): A short noun identifying the affected module, component, or domain. Examples: `auth`, `api`, `ui`, `db`, `cli`, `deps`. Omit if the change is broad or cross-cutting. - -3. **Breaking change**: If the change breaks backward compatibility, add `!` after the type/scope (e.g., `feat(api)!:`) and include a `BREAKING CHANGE:` footer. - ---- - -## Phase 3: Write the Subject Line - -The subject line must: -- Start with `():` (scope is optional) -- Use **imperative mood**: "add", "fix", "update", "remove" — not "added", "fixes", "updating" -- Be **72 characters or fewer** -- Not end with a period -- Be specific enough to understand the change without reading the body - -**Good examples:** -``` -feat(auth): add OAuth2 login with Google and GitHub -fix(cache): prevent race condition on concurrent writes -chore(deps): bump axios from 1.6.0 to 1.7.2 -docs(readme): add installation instructions for Windows -refactor(api): extract rate-limiting middleware into separate module -``` - -**Bad examples:** -``` -fix stuff ← too vague -feat: Added new thing. ← past tense, trailing period -feat(authentication-service): implement OAuth2 login flow with Google ← too long -``` - ---- - -## Phase 4: Write the Body (if needed) - -Add a body when any of these apply: -- The *why* behind the change is not obvious from the subject -- There are important implementation notes (e.g., "uses exponential backoff") -- The change has side effects developers should know about - -Body rules: -- Separate from the subject with a blank line -- Wrap at 72 characters per line -- Use imperative mood -- Explain *what* and *why*, not *how* (the code shows the how) - ---- - -## Phase 5: Write Footers (if needed) - -Add footers for: -- **Breaking changes**: `BREAKING CHANGE: ` -- **Issue references**: `Closes #123` or `Fixes #456` -- **Co-authors**: `Co-authored-by: Name ` - ---- - -## Phase 6: Output - -Present the final commit message in a code block: - -``` -feat(auth): add OAuth2 login with Google and GitHub - -Replaces the username/password-only flow with an OAuth2 provider -selection screen. Existing sessions remain valid; users who have -not yet linked a provider will be prompted on next login. - -Closes #42 -``` - -Then offer: -> Ready to commit? Run: -> ```bash -> git commit -m "feat(auth): add OAuth2 login with Google and GitHub" -> ``` -> Or for a multi-line message: -> ```bash -> git commit -> ``` -> (This opens your editor with the full message pre-filled by the harness.) - ---- - -## Edge Cases - -- **Multiple unrelated changes staged** → Warn: "Your staged changes touch unrelated areas. Consider splitting into separate commits for a cleaner history." Then generate the best single message anyway. -- **Only whitespace/formatting changes** → Use `style` type. -- **Dependency bump** → Use `chore(deps):` with the package name and version range in the subject. -- **Revert** → Use `revert:` and reference the original commit SHA in the body. +--- +name: writing-commit-messages +version: 1.0.0 +user-invocable: true +description: Generate a Conventional Commits–compliant commit message from staged changes or a plain-language description. Use when you want a well-structured commit message, are unsure how to categorize a change, or want to ensure your commit history stays readable. Triggered by: write commit message, commit message, conventional commit, git commit, commit this, what should I commit. +argument-hint: "[description of the change]" +allowed-tools: + - Bash + - Read +--- + +# Write Commit Message + +Generate a clear, Conventional Commits–compliant commit message. + +## Conventional Commits Format + +``` +(): + +[optional body] + +[optional footer(s)] +``` + +**Types:** + +| Type | When to use | +|------|-------------| +| `feat` | A new feature or capability | +| `fix` | A bug fix | +| `docs` | Documentation changes only | +| `style` | Formatting, whitespace (no logic change) | +| `refactor` | Code restructuring without feature/fix | +| `test` | Adding or updating tests | +| `chore` | Build tooling, dependencies, CI, config | +| `perf` | Performance improvements | +| `ci` | CI/CD pipeline changes | +| `revert` | Reverting a previous commit | + +--- + +## Input + +**Arguments:** `$ARGUMENTS` + +- If arguments are provided, treat them as a plain-language description of the change. +- If arguments are empty, inspect staged changes. + +--- + +## Phase 1: Gather Context + +**If a description was provided in `$ARGUMENTS`:** +Use it directly to infer the type, scope, and short description. Skip to Phase 2. + +**If no description was provided:** +Inspect the staged diff: + +```bash +git diff --cached --stat +git diff --cached +``` + +If there are no staged changes, check if there are unstaged changes: + +```bash +git diff --stat +``` + +If no changes at all: +> "No staged or unstaged changes found. Stage your changes with `git add` first, or provide a description as an argument." + +Stop. + +--- + +## Phase 2: Determine Type and Scope + +Analyze the diff or description: + +1. **Type**: Choose the most specific matching type from the table above. When in doubt between `feat` and `fix`, prefer the one that matches the user's intent. + +2. **Scope** (optional): A short noun identifying the affected module, component, or domain. Examples: `auth`, `api`, `ui`, `db`, `cli`, `deps`. Omit if the change is broad or cross-cutting. + +3. **Breaking change**: If the change breaks backward compatibility, add `!` after the type/scope (e.g., `feat(api)!:`) and include a `BREAKING CHANGE:` footer. + +--- + +## Phase 3: Write the Subject Line + +The subject line must: +- Start with `():` (scope is optional) +- Use **imperative mood**: "add", "fix", "update", "remove" — not "added", "fixes", "updating" +- Be **72 characters or fewer** +- Not end with a period +- Be specific enough to understand the change without reading the body + +**Good examples:** +``` +feat(auth): add OAuth2 login with Google and GitHub +fix(cache): prevent race condition on concurrent writes +chore(deps): bump axios from 1.6.0 to 1.7.2 +docs(readme): add installation instructions for Windows +refactor(api): extract rate-limiting middleware into separate module +``` + +**Bad examples:** +``` +fix stuff ← too vague +feat: Added new thing. ← past tense, trailing period +feat(authentication-service): implement OAuth2 login flow with Google ← too long +``` + +--- + +## Phase 4: Write the Body (if needed) + +Add a body when any of these apply: +- The *why* behind the change is not obvious from the subject +- There are important implementation notes (e.g., "uses exponential backoff") +- The change has side effects developers should know about + +Body rules: +- Separate from the subject with a blank line +- Wrap at 72 characters per line +- Use imperative mood +- Explain *what* and *why*, not *how* (the code shows the how) + +--- + +## Phase 5: Write Footers (if needed) + +Add footers for: +- **Breaking changes**: `BREAKING CHANGE: ` +- **Issue references**: `Closes #123` or `Fixes #456` +- **Co-authors**: `Co-authored-by: Name ` + +--- + +## Phase 6: Output + +Present the final commit message in a code block: + +``` +feat(auth): add OAuth2 login with Google and GitHub + +Replaces the username/password-only flow with an OAuth2 provider +selection screen. Existing sessions remain valid; users who have +not yet linked a provider will be prompted on next login. + +Closes #42 +``` + +Then offer: +> Ready to commit? Run: +> ```bash +> git commit -m "feat(auth): add OAuth2 login with Google and GitHub" +> ``` +> Or for a multi-line message: +> ```bash +> git commit +> ``` +> (This opens your editor with the full message pre-filled by the harness.) + +--- + +## Edge Cases + +- **Multiple unrelated changes staged** → Warn: "Your staged changes touch unrelated areas. Consider splitting into separate commits for a cleaner history." Then generate the best single message anyway. +- **Only whitespace/formatting changes** → Use `style` type. +- **Dependency bump** → Use `chore(deps):` with the package name and version range in the subject. +- **Revert** → Use `revert:` and reference the original commit SHA in the body. diff --git a/examples/README.md b/examples/README.md index 9cf7f36..734632f 100644 --- a/examples/README.md +++ b/examples/README.md @@ -1,40 +1,43 @@ -# Examples - -SDK usage examples that demonstrate the full Musher value proposition using the `musher-dev/demo-starter` bundle. - -## Structure - -``` -examples/ -├── python/ Python SDK examples -│ ├── 01_pull_bundle.py Pull and inspect a bundle -│ ├── 02_install_skills.py Install skills into a Claude Code project -│ └── 03_use_with_claude.py Use skills as system prompts via Anthropic API -└── typescript/ TypeScript SDK examples - ├── 01-pull-bundle.ts Pull and inspect a bundle - └── 02-use-with-claude.ts Use skills as system prompts via Anthropic SDK -``` - -## Quick Start - -**Python** -```bash -cd python -pip install -r requirements.txt -export MUSHER_API_KEY="mush_..." -python 01_pull_bundle.py -``` - -**TypeScript** -```bash -cd typescript -npm install -export MUSHER_API_KEY="mush_..." -npm run pull-bundle -``` - -## What These Examples Show - -1. **Pull a bundle** — Fetch the `musher-dev/demo-starter` bundle from the registry and inspect its skills, agents, and raw files. -2. **Install skills** *(Python only)* — Copy skills into a Claude Code project directory so they are auto-discovered by the harness. -3. **Use with Claude** — Load a skill's instructions as a system prompt and call the Anthropic API directly, demonstrating programmatic use of bundle assets. +# Examples + +SDK usage examples that demonstrate the full Musher value proposition using the `musher-examples/demo-starter` bundle. + +See [Python README](python/README.md) and [TypeScript README](typescript/README.md) for language-specific setup instructions and API key details. + +## Structure + +``` +examples/ +├── python/ Python SDK examples +│ ├── 01_pull_bundle.py Pull and inspect a bundle +│ ├── 02_install_skills.py Install skills into a Claude Code project +│ └── 03_use_with_claude.py Use skills as system prompts via Anthropic API +└── typescript/ TypeScript SDK examples + ├── 01-pull-bundle.ts Pull and inspect a bundle + ├── 02-install-skills.ts Install skills into a Claude Code project + └── 03-use-with-claude.ts Use skills as system prompts via Anthropic SDK +``` + +## Quick Start + +**Python** +```bash +cd python +pip install -r requirements.txt +export MUSHER_API_KEY="mush_..." +python 01_pull_bundle.py +``` + +**TypeScript** +```bash +cd typescript +npm install +export MUSHER_API_KEY="mush_..." +npm run pull-bundle +``` + +## What These Examples Show + +1. **Pull a bundle** — Fetch the `musher-examples/demo-starter` bundle from the registry and inspect its skills, agent specs, and raw files. +2. **Install skills** — Copy skills into a Claude Code project directory so they are auto-discovered by the harness. +3. **Use with Claude** — Load a skill's instructions as a system prompt and call the Anthropic API directly, demonstrating programmatic use of bundle assets. diff --git a/examples/python/01_pull_bundle.py b/examples/python/01_pull_bundle.py index 54f353a..60b8eea 100644 --- a/examples/python/01_pull_bundle.py +++ b/examples/python/01_pull_bundle.py @@ -1,47 +1,50 @@ -"""Example: Pull the demo-starter bundle and inspect its contents.""" - -import musher - -# Credentials are auto-discovered from the MUSHER_API_KEY environment variable, -# the OS keyring, or a credential file (~/.config/mush/credentials.yaml). -# To set credentials explicitly: musher.configure(token="mush_...") - -bundle = musher.pull("musher-dev/demo-starter:1.0.0") - -print(f"Bundle: {bundle.manifest.name} v{bundle.manifest.version}") -print(f"Description: {bundle.manifest.description}") -print() - -# List all files in the bundle -print(f"Files ({len(list(bundle.files()))}):") -for fh in bundle.files(): - print(f" {fh.logical_path} ({fh.media_type or 'unknown'})") -print() - -# List available skills -print(f"Skills ({len(bundle.skills())}):") -for skill in bundle.skills(): - print(f" {skill.name}") - # Read the SKILL.md content - content = skill.text() - # Extract the description from the frontmatter (first 120 chars of body) - lines = content.splitlines() - for line in lines: - if line.startswith("description:"): - desc = line.replace("description:", "").strip() - print(f" → {desc[:100]}...") - break -print() - -# List available agents -print(f"Agents ({len(bundle.agents())}):") -for agent in bundle.agents(): - print(f" {agent.name}") -print() - -# Access a specific skill by name -skill = bundle.skill("summarize-changes") -if skill: - print(f"Skill content preview ({skill.name}):") - print(skill.text()[:300]) - print("...") +"""Example: Pull the demo-starter bundle and inspect its contents.""" + +import musher + +# Credentials are auto-discovered from the MUSHER_API_KEY environment variable, +# the OS keyring, or a credential file (~/.config/musher/credentials.yaml). +# To set credentials explicitly: musher.configure(token="mush_...") + +bundle = musher.pull("musher-examples/demo-starter:1.0.0") + +print(f"Bundle: {bundle.ref} v{bundle.version}") +print() + +# List all files in the bundle +print(f"Files ({len(list(bundle.files()))}):") +for fh in bundle.files(): + print(f" {fh.logical_path} ({fh.media_type or 'unknown'})") +print() + +# List available skills +print(f"Skills ({len(bundle.skills())}):") +for skill in bundle.skills(): + print(f" {skill.name}") + # Show a preview of the skill's SKILL.md content + preview = skill.skill_md().text().strip().splitlines() + # Skip frontmatter, show first content line + in_frontmatter = False + for line in preview: + if line.strip() == "---": + in_frontmatter = not in_frontmatter + continue + if not in_frontmatter and line.strip(): + print(f" → {line.strip()[:100]}") + break +print() + +# List available agents +print(f"Agent specs ({len(bundle.agent_specs())}):") +for agent in bundle.agent_specs(): + print(f" {agent.name}") +print() + +# Access a specific skill by name +try: + skill = bundle.skill("summarizing-changes") + print(f"Skill content preview ({skill.name}):") + print(skill.skill_md().text()[:300]) + print("...") +except KeyError: + print("Skill 'summarizing-changes' not found in bundle") diff --git a/examples/python/02_install_skills.py b/examples/python/02_install_skills.py index 50fd435..74edc6e 100644 --- a/examples/python/02_install_skills.py +++ b/examples/python/02_install_skills.py @@ -1,32 +1,32 @@ -"""Example: Install skills from the demo-starter bundle into a Claude Code project.""" - -from pathlib import Path - -import musher - -# Pull the demo-starter bundle -bundle = musher.pull("musher-dev/demo-starter:1.0.0") - -# Target directory for Claude Code skills -# Relative to your project root — Claude Code discovers skills from .claude/skills/ -skills_dir = Path(".claude/skills") - -# Install specific skills from the bundle. -# clean=True removes stale Musher-managed installs for this bundle from the -# target directory; it does NOT affect manually placed skills. -bundle.install_claude_skills( - skills_dir, - skills=["summarize-changes", "write-commit-message"], - clean=True, -) - -print(f"Installed skills to {skills_dir}/") -for skill in bundle.skills(): - installed_path = skills_dir / f"{skill.name}.md" - status = "✓" if installed_path.exists() else "✗" - print(f" {status} {skill.name}") - -print() -print("Skills are now available in Claude Code.") -print("Try asking: 'Summarize the changes since the last release'") -print("Or: 'Write a commit message for my staged changes'") +"""Example: Install skills from the demo-starter bundle into a Claude Code project.""" + +from pathlib import Path + +import musher + +# Pull the demo-starter bundle +bundle = musher.pull("musher-examples/demo-starter:1.0.0") + +# Target directory for Claude Code skills +# Relative to your project root — Claude Code discovers skills from .claude/skills/ +skills_dir = Path(".claude/skills") + +# Install specific skills from the bundle. +# clean=True removes stale Musher-managed installs for this bundle from the +# target directory; it does NOT affect manually placed skills. +bundle.install_claude_skills( + skills_dir, + skills=["summarizing-changes", "writing-commit-messages"], + clean=True, +) + +print(f"Installed skills to {skills_dir}/") +for skill in bundle.skills(): + installed_path = skills_dir / skill.name / "SKILL.md" + status = "✓" if installed_path.exists() else "✗" + print(f" {status} {skill.name}") + +print() +print("Skills are now available in Claude Code.") +print("Try asking: 'Summarize the changes since the last release'") +print("Or: 'Write a commit message for my staged changes'") diff --git a/examples/python/03_use_with_claude.py b/examples/python/03_use_with_claude.py index 54d88ad..2e0d126 100644 --- a/examples/python/03_use_with_claude.py +++ b/examples/python/03_use_with_claude.py @@ -1,76 +1,82 @@ -"""Example: Use the demo-starter bundle's skills with Claude via the Python SDK. - -This example shows how to: -1. Pull the bundle and extract a skill's content -2. Use that skill as a system prompt with the Anthropic SDK -3. Run a code review using the code-reviewer agent spec - -Prerequisites: - pip install musher anthropic - export MUSHER_API_KEY="mush_..." - export ANTHROPIC_API_KEY="sk-ant-..." -""" - -import anthropic -import musher - -# Pull the demo-starter bundle -bundle = musher.pull("musher-dev/demo-starter:1.0.0") - -# ── Example 1: Use a skill as a system prompt ──────────────────────────────── - -skill = bundle.skill("write-commit-message") -assert skill is not None, "Skill 'write-commit-message' not found in bundle" - -client = anthropic.Anthropic() - -# The skill's SKILL.md content contains the full instructions for the model -skill_instructions = skill.text() - -response = client.messages.create( - model="claude-sonnet-4-5", - max_tokens=512, - system=skill_instructions, - messages=[ - { - "role": "user", - "content": ( - "I added input validation to the user registration endpoint. " - "It now rejects emails longer than 254 characters and passwords " - "shorter than 8 characters, returning a 422 with field-level errors." - ), - } - ], -) - -print("── Commit Message ───────────────────────────────") -print(response.content[0].text) -print() - -# ── Example 2: Use the code-reviewer agent spec ─────────────────────────────── - -agent = bundle.agent("code-reviewer") -assert agent is not None, "Agent 'code-reviewer' not found in bundle" - -agent_spec = agent.text() - -sample_code = ''' -def get_user(user_id): - query = f"SELECT * FROM users WHERE id = {user_id}" - return db.execute(query).fetchone() -''' - -response = client.messages.create( - model="claude-sonnet-4-5", - max_tokens=1024, - system=agent_spec, - messages=[ - { - "role": "user", - "content": f"Please review this Python function:\n\n```python{sample_code}```", - } - ], -) - -print("── Code Review ──────────────────────────────────") -print(response.content[0].text) +"""Example: Use the demo-starter bundle's skills with Claude via the Python SDK. + +This example shows how to: +1. Pull the bundle and extract a skill's content +2. Use that skill as a system prompt with the Anthropic SDK +3. Run a code review using the code-reviewer agent spec + +Prerequisites: + pip install musher-sdk anthropic + export MUSHER_API_KEY="mush_..." + export ANTHROPIC_API_KEY="sk-ant-..." +""" + +import anthropic +import musher + +# Pull the demo-starter bundle +bundle = musher.pull("musher-examples/demo-starter:1.0.0") + +# ── Example 1: Use a skill as a system prompt ──────────────────────────────── + +skill = bundle.skill("writing-commit-messages") + +client = anthropic.Anthropic() + +# The skill's SKILL.md content contains the full instructions for the model +skill_instructions = skill.skill_md().text() + +response = client.messages.create( + model="claude-sonnet-4-6", + max_tokens=512, + system=skill_instructions, + messages=[ + { + "role": "user", + "content": ( + "I added input validation to the user registration endpoint. " + "It now rejects emails longer than 254 characters and passwords " + "shorter than 8 characters, returning a 422 with field-level errors." + ), + } + ], +) + +print("── Commit Message ───────────────────────────────") +print(response.content[0].text) +print() + +# ── Example 2: Use the code-reviewer agent spec ─────────────────────────────── + +agent = bundle.agent_spec("code-reviewer") + +# The agent spec is JSON — parse it to build a system prompt from its fields +agent_config = agent.parse_json() +system_prompt = ( + f"You are a {agent_config['name']}. {agent_config['description']}\n\n" + "Analyze code for correctness, security vulnerabilities, and best practices. " + "For each finding, assign a severity (CRITICAL, MAJOR, MINOR, NIT) and explain " + "the issue with a suggested fix. End with an overall verdict: Approve, " + "Approve with suggestions, or Request changes." +) + +sample_code = ''' +def get_user(user_id): + query = f"SELECT * FROM users WHERE id = {user_id}" + return db.execute(query).fetchone() +''' + +response = client.messages.create( + model="claude-sonnet-4-6", + max_tokens=1024, + system=system_prompt, + messages=[ + { + "role": "user", + "content": f"Please review this Python function:\n\n```python{sample_code}```", + } + ], +) + +print("── Code Review ──────────────────────────────────") +print(response.content[0].text) diff --git a/examples/python/README.md b/examples/python/README.md index 59f197b..7737357 100644 --- a/examples/python/README.md +++ b/examples/python/README.md @@ -1,27 +1,39 @@ -# Python Examples - -These examples show how to use the Musher Python SDK with the `musher-dev/demo-starter` bundle. - -## Prerequisites - -```bash -pip install musher anthropic -export MUSHER_API_KEY="mush_..." # From https://hub.musher.dev -export ANTHROPIC_API_KEY="sk-ant-..." # Only needed for example 03 -``` - -## Examples - -| File | What it demonstrates | -|------|----------------------| -| `01_pull_bundle.py` | Pull a bundle and inspect its skills, agents, and files | -| `02_install_skills.py` | Install skills into a Claude Code project directory | -| `03_use_with_claude.py` | Use bundle skills as system prompts with the Anthropic API | - -## Running - -```bash -python 01_pull_bundle.py -python 02_install_skills.py -python 03_use_with_claude.py -``` +# Python Examples + +These examples show how to use the [Musher Python SDK](https://github.com/musher-dev/python-sdk) with the `musher-examples/demo-starter` bundle. + +## Prerequisites + +- Python 3.13+ + +```bash +pip install musher-sdk anthropic +``` + +### API Keys + +| Variable | Where to get it | Used by | +|----------|----------------|---------| +| `MUSHER_API_KEY` | Sign up at [hub.musher.dev](https://hub.musher.dev), then **Settings > API Keys** | All examples | +| `ANTHROPIC_API_KEY` | Create at [console.anthropic.com](https://console.anthropic.com/) under **API Keys** | Example 03 only | + +```bash +export MUSHER_API_KEY="mush_..." +export ANTHROPIC_API_KEY="sk-ant-..." # Only needed for example 03 +``` + +## Examples + +| File | What it demonstrates | +|------|----------------------| +| `01_pull_bundle.py` | Pull a bundle and inspect its skills, agents, and files | +| `02_install_skills.py` | Install skills into a Claude Code project directory | +| `03_use_with_claude.py` | Use bundle skills as system prompts with the Anthropic API | + +## Running + +```bash +python 01_pull_bundle.py +python 02_install_skills.py +python 03_use_with_claude.py +``` diff --git a/examples/python/requirements.txt b/examples/python/requirements.txt index 5c85622..0becbf5 100644 --- a/examples/python/requirements.txt +++ b/examples/python/requirements.txt @@ -1,2 +1,2 @@ -musher>=0.1.0 -anthropic>=0.25.0 +musher-sdk>=0.3.5 +anthropic>=0.25.0 diff --git a/examples/typescript/01-pull-bundle.ts b/examples/typescript/01-pull-bundle.ts index 5292295..f2ddbf7 100644 --- a/examples/typescript/01-pull-bundle.ts +++ b/examples/typescript/01-pull-bundle.ts @@ -1,50 +1,49 @@ -/** - * Pull the demo-starter bundle and inspect its contents. - * - * Prerequisites: - * export MUSHER_API_KEY="mush_..." - * - * Run: - * npx tsx 01-pull-bundle.ts - */ - -import { pull } from "@musher-dev/musher-sdk"; - -const bundle = await pull("musher-dev/demo-starter:1.0.0"); - -console.log(`Bundle: ${bundle.manifest.name} v${bundle.manifest.version}`); -console.log(`Description: ${bundle.manifest.description}`); -console.log(); - -// List all files in the bundle -const files = [...bundle.files()]; -console.log(`Files (${files.length}):`); -for (const file of files) { - console.log(` ${file.logicalPath} (${file.assetType}, ${file.sizeBytes} bytes)`); -} -console.log(); - -// List available skills -const skills = bundle.skills(); -console.log(`Skills (${skills.length}):`); -for (const skill of skills) { - console.log(` ${skill.name}`); -} -console.log(); - -// List available agents -const agents = bundle.agents(); -console.log(`Agents (${agents.length}):`); -for (const agent of agents) { - console.log(` ${agent.name}`); -} -console.log(); - -// Access a specific skill by name -const skill = bundle.skill("summarize-changes"); -if (skill) { - const preview = skill.content().slice(0, 300); - console.log(`Skill content preview (${skill.name}):`); - console.log(preview); - console.log("..."); -} +/** + * Pull the demo-starter bundle and inspect its contents. + * + * Prerequisites: + * export MUSHER_API_KEY="mush_..." + * + * Run: + * npx tsx 01-pull-bundle.ts + */ + +import { pull } from "@musher-dev/musher-sdk"; + +const bundle = await pull("musher-examples/demo-starter:1.0.0"); + +console.log(`Bundle: ${bundle.ref.toBaseRef()} v${bundle.version}`); +console.log(); + +// List all files in the bundle +const files = bundle.files(); +console.log(`Files (${files.length}):`); +for (const file of files) { + console.log(` ${file.logicalPath} (${file.assetType}, ${file.sizeBytes} bytes)`); +} +console.log(); + +// List available skills +const skills = bundle.skills(); +console.log(`Skills (${skills.length}):`); +for (const skill of skills) { + console.log(` ${skill.name}`); +} +console.log(); + +// List available agent specs +const agentSpecs = bundle.agentSpecs(); +console.log(`Agent Specs (${agentSpecs.length}):`); +for (const agentSpec of agentSpecs) { + console.log(` ${agentSpec.name}`); +} +console.log(); + +// Access a specific skill by name +const skill = bundle.skill("summarizing-changes"); +if (skill) { + const preview = skill.definition()!.text().slice(0, 300); + console.log(`Skill content preview (${skill.name}):`); + console.log(preview); + console.log("..."); +} diff --git a/examples/typescript/02-install-skills.ts b/examples/typescript/02-install-skills.ts new file mode 100644 index 0000000..5b3e4f9 --- /dev/null +++ b/examples/typescript/02-install-skills.ts @@ -0,0 +1,31 @@ +/** + * Install skills from the demo-starter bundle into a Claude Code project. + * + * This copies skill files into a .claude/skills/ directory so that + * Claude Code auto-discovers them when running in the project. + * + * Prerequisites: + * export MUSHER_API_KEY="mush_..." + * + * Run: + * npx tsx 02-install-skills.ts + */ + +import { pull } from "@musher-dev/musher-sdk"; + +const bundle = await pull("musher-examples/demo-starter:1.0.0"); + +// installClaudeSkills writes to .claude/skills/ under the given project root. +const projectRoot = "."; + +const written = await bundle.installClaudeSkills(projectRoot); + +console.log(`Installed ${written.length} skill(s) to .claude/skills/`); +for (const filePath of written) { + console.log(` ${filePath}`); +} + +console.log(); +console.log("Skills are now available in Claude Code."); +console.log("Try asking: 'Summarize the changes since the last release'"); +console.log("Or: 'Write a commit message for my staged changes'"); diff --git a/examples/typescript/02-use-with-claude.ts b/examples/typescript/03-use-with-claude.ts similarity index 79% rename from examples/typescript/02-use-with-claude.ts rename to examples/typescript/03-use-with-claude.ts index 95fdb73..0750f6e 100644 --- a/examples/typescript/02-use-with-claude.ts +++ b/examples/typescript/03-use-with-claude.ts @@ -1,76 +1,76 @@ -/** - * Use the demo-starter bundle's skills with Claude via the Anthropic SDK. - * - * This example shows how to: - * 1. Pull the bundle and extract a skill's content - * 2. Use that skill as a system prompt with the Anthropic SDK - * 3. Run a structured code review using the code-reviewer agent spec - * - * Prerequisites: - * export MUSHER_API_KEY="mush_..." - * export ANTHROPIC_API_KEY="sk-ant-..." - * - * Run: - * npx tsx 02-use-with-claude.ts - */ - -import Anthropic from "@anthropic-ai/sdk"; -import { pull } from "@musher-dev/musher-sdk"; - -const client = new Anthropic(); -const bundle = await pull("musher-dev/demo-starter:1.0.0"); - -// ── Example 1: Use a skill as a system prompt ──────────────────────────────── - -const commitSkill = bundle.skill("write-commit-message"); -if (!commitSkill) throw new Error("Skill 'write-commit-message' not found"); - -const commitResponse = await client.messages.create({ - model: "claude-sonnet-4-5", - max_tokens: 512, - system: commitSkill.content(), - messages: [ - { - role: "user", - content: - "I added input validation to the user registration endpoint. " + - "It now rejects emails longer than 254 characters and passwords " + - "shorter than 8 characters, returning a 422 with field-level errors.", - }, - ], -}); - -console.log("── Commit Message ───────────────────────────────"); -console.log( - commitResponse.content[0].type === "text" ? commitResponse.content[0].text : "", -); -console.log(); - -// ── Example 2: Use the code-reviewer agent spec ─────────────────────────────── - -const reviewerAgent = bundle.agent("code-reviewer"); -if (!reviewerAgent) throw new Error("Agent 'code-reviewer' not found"); - -const sampleCode = ` -function getUser(userId) { - const query = \`SELECT * FROM users WHERE id = \${userId}\`; - return db.execute(query).then(r => r.rows[0]); -} -`; - -const reviewResponse = await client.messages.create({ - model: "claude-sonnet-4-5", - max_tokens: 1024, - system: reviewerAgent.content(), - messages: [ - { - role: "user", - content: `Please review this JavaScript function:\n\n\`\`\`javascript${sampleCode}\`\`\``, - }, - ], -}); - -console.log("── Code Review ──────────────────────────────────"); -console.log( - reviewResponse.content[0].type === "text" ? reviewResponse.content[0].text : "", -); +/** + * Use the demo-starter bundle's skills with Claude via the Anthropic SDK. + * + * This example shows how to: + * 1. Pull the bundle and extract a skill's content + * 2. Use that skill as a system prompt with the Anthropic SDK + * 3. Run a structured code review using the code-reviewer agent spec + * + * Prerequisites: + * export MUSHER_API_KEY="mush_..." + * export ANTHROPIC_API_KEY="sk-ant-..." + * + * Run: + * npx tsx 03-use-with-claude.ts + */ + +import Anthropic from "@anthropic-ai/sdk"; +import { pull } from "@musher-dev/musher-sdk"; + +const client = new Anthropic(); +const bundle = await pull("musher-examples/demo-starter:1.0.0"); + +// ── Example 1: Use a skill as a system prompt ──────────────────────────────── + +const commitSkill = bundle.skill("writing-commit-messages"); +if (!commitSkill) throw new Error("Skill 'writing-commit-messages' not found"); + +const commitResponse = await client.messages.create({ + model: "claude-sonnet-4-6", + max_tokens: 512, + system: commitSkill.definition()!.text(), + messages: [ + { + role: "user", + content: + "I added input validation to the user registration endpoint. " + + "It now rejects emails longer than 254 characters and passwords " + + "shorter than 8 characters, returning a 422 with field-level errors.", + }, + ], +}); + +console.log("── Commit Message ───────────────────────────────"); +console.log( + commitResponse.content[0].type === "text" ? commitResponse.content[0].text : "", +); +console.log(); + +// ── Example 2: Use the code-reviewer agent spec ─────────────────────────────── + +const reviewerAgent = bundle.agentSpec("code-reviewer"); +if (!reviewerAgent) throw new Error("Agent spec 'code-reviewer' not found"); + +const sampleCode = ` +function getUser(userId) { + const query = \`SELECT * FROM users WHERE id = \${userId}\`; + return db.execute(query).then(r => r.rows[0]); +} +`; + +const reviewResponse = await client.messages.create({ + model: "claude-sonnet-4-6", + max_tokens: 1024, + system: reviewerAgent.content(), + messages: [ + { + role: "user", + content: `Please review this JavaScript function:\n\n\`\`\`javascript${sampleCode}\`\`\``, + }, + ], +}); + +console.log("── Code Review ──────────────────────────────────"); +console.log( + reviewResponse.content[0].type === "text" ? reviewResponse.content[0].text : "", +); diff --git a/examples/typescript/README.md b/examples/typescript/README.md index 2d4da0e..b2e4da5 100644 --- a/examples/typescript/README.md +++ b/examples/typescript/README.md @@ -1,25 +1,37 @@ -# TypeScript Examples - -These examples show how to use the Musher TypeScript SDK with the `musher-dev/demo-starter` bundle. - -## Prerequisites - -```bash -npm install -export MUSHER_API_KEY="mush_..." # From https://hub.musher.dev -export ANTHROPIC_API_KEY="sk-ant-..." # Only needed for example 02 -``` - -## Examples - -| File | What it demonstrates | -|------|----------------------| -| `01-pull-bundle.ts` | Pull a bundle and inspect its skills, agents, and files | -| `02-use-with-claude.ts` | Use bundle skills as system prompts with the Anthropic SDK | - -## Running - -```bash -npm run pull-bundle -npm run use-with-claude -``` +# TypeScript Examples + +These examples show how to use the [Musher TypeScript SDK](https://github.com/musher-dev/typescript-sdk) with the `musher-examples/demo-starter` bundle. + +## Prerequisites + +```bash +npm install +``` + +### API Keys + +| Variable | Where to get it | Used by | +|----------|----------------|---------| +| `MUSHER_API_KEY` | Sign up at [hub.musher.dev](https://hub.musher.dev), then **Settings > API Keys** | All examples | +| `ANTHROPIC_API_KEY` | Create at [console.anthropic.com](https://console.anthropic.com/) under **API Keys** | Example 03 only | + +```bash +export MUSHER_API_KEY="mush_..." +export ANTHROPIC_API_KEY="sk-ant-..." # Only needed for example 03 +``` + +## Examples + +| File | What it demonstrates | +|------|----------------------| +| `01-pull-bundle.ts` | Pull a bundle and inspect its skills, agent specs, and files | +| `02-install-skills.ts` | Install skills into a Claude Code project directory | +| `03-use-with-claude.ts` | Use bundle skills as system prompts with the Anthropic SDK | + +## Running + +```bash +npm run pull-bundle +npm run install-skills +npm run use-with-claude +``` diff --git a/examples/typescript/package-lock.json b/examples/typescript/package-lock.json new file mode 100644 index 0000000..39ab0c4 --- /dev/null +++ b/examples/typescript/package-lock.json @@ -0,0 +1,645 @@ +{ + "name": "musher-demo-examples", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "musher-demo-examples", + "version": "1.0.0", + "dependencies": { + "@anthropic-ai/sdk": "^0.82.0", + "@musher-dev/musher-sdk": "^0.4.0" + }, + "devDependencies": { + "tsx": "^4.0.0", + "typescript": "^5.0.0" + } + }, + "node_modules/@anthropic-ai/sdk": { + "version": "0.82.0", + "resolved": "https://registry.npmjs.org/@anthropic-ai/sdk/-/sdk-0.82.0.tgz", + "integrity": "sha512-xdHTjL1GlUlDugHq/I47qdOKp/ROPvuHl7ROJCgUQigbvPu7asf9KcAcU1EqdrP2LuVhEKaTs7Z+ShpZDRzHdQ==", + "license": "MIT", + "dependencies": { + "json-schema-to-ts": "^3.1.1" + }, + "bin": { + "anthropic-ai-sdk": "bin/cli" + }, + "peerDependencies": { + "zod": "^3.25.0 || ^4.0.0" + }, + "peerDependenciesMeta": { + "zod": { + "optional": true + } + } + }, + "node_modules/@babel/runtime": { + "version": "7.29.2", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.29.2.tgz", + "integrity": "sha512-JiDShH45zKHWyGe4ZNVRrCjBz8Nh9TMmZG1kh4QTK8hCBTWBi8Da+i7s1fJw7/lYpM4ccepSNfqzZ/QvABBi5g==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.7.tgz", + "integrity": "sha512-EKX3Qwmhz1eMdEJokhALr0YiD0lhQNwDqkPYyPhiSwKrh7/4KRjQc04sZ8db+5DVVnZ1LmbNDI1uAMPEUBnQPg==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.7.tgz", + "integrity": "sha512-jbPXvB4Yj2yBV7HUfE2KHe4GJX51QplCN1pGbYjvsyCZbQmies29EoJbkEc+vYuU5o45AfQn37vZlyXy4YJ8RQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.7.tgz", + "integrity": "sha512-62dPZHpIXzvChfvfLJow3q5dDtiNMkwiRzPylSCfriLvZeq0a1bWChrGx/BbUbPwOrsWKMn8idSllklzBy+dgQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.7.tgz", + "integrity": "sha512-x5VpMODneVDb70PYV2VQOmIUUiBtY3D3mPBG8NxVk5CogneYhkR7MmM3yR/uMdITLrC1ml/NV1rj4bMJuy9MCg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.7.tgz", + "integrity": "sha512-5lckdqeuBPlKUwvoCXIgI2D9/ABmPq3Rdp7IfL70393YgaASt7tbju3Ac+ePVi3KDH6N2RqePfHnXkaDtY9fkw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.7.tgz", + "integrity": "sha512-rYnXrKcXuT7Z+WL5K980jVFdvVKhCHhUwid+dDYQpH+qu+TefcomiMAJpIiC2EM3Rjtq0sO3StMV/+3w3MyyqQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.7.tgz", + "integrity": "sha512-B48PqeCsEgOtzME2GbNM2roU29AMTuOIN91dsMO30t+Ydis3z/3Ngoj5hhnsOSSwNzS+6JppqWsuhTp6E82l2w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.7.tgz", + "integrity": "sha512-jOBDK5XEjA4m5IJK3bpAQF9/Lelu/Z9ZcdhTRLf4cajlB+8VEhFFRjWgfy3M1O4rO2GQ/b2dLwCUGpiF/eATNQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.7.tgz", + "integrity": "sha512-RkT/YXYBTSULo3+af8Ib0ykH8u2MBh57o7q/DAs3lTJlyVQkgQvlrPTnjIzzRPQyavxtPtfg0EopvDyIt0j1rA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.7.tgz", + "integrity": "sha512-RZPHBoxXuNnPQO9rvjh5jdkRmVizktkT7TCDkDmQ0W2SwHInKCAV95GRuvdSvA7w4VMwfCjUiPwDi0ZO6Nfe9A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.7.tgz", + "integrity": "sha512-GA48aKNkyQDbd3KtkplYWT102C5sn/EZTY4XROkxONgruHPU72l+gW+FfF8tf2cFjeHaRbWpOYa/uRBz/Xq1Pg==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.7.tgz", + "integrity": "sha512-a4POruNM2oWsD4WKvBSEKGIiWQF8fZOAsycHOt6JBpZ+JN2n2JH9WAv56SOyu9X5IqAjqSIPTaJkqN8F7XOQ5Q==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.7.tgz", + "integrity": "sha512-KabT5I6StirGfIz0FMgl1I+R1H73Gp0ofL9A3nG3i/cYFJzKHhouBV5VWK1CSgKvVaG4q1RNpCTR2LuTVB3fIw==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.7.tgz", + "integrity": "sha512-gRsL4x6wsGHGRqhtI+ifpN/vpOFTQtnbsupUF5R5YTAg+y/lKelYR1hXbnBdzDjGbMYjVJLJTd2OFmMewAgwlQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.7.tgz", + "integrity": "sha512-hL25LbxO1QOngGzu2U5xeXtxXcW+/GvMN3ejANqXkxZ/opySAZMrc+9LY/WyjAan41unrR3YrmtTsUpwT66InQ==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.7.tgz", + "integrity": "sha512-2k8go8Ycu1Kb46vEelhu1vqEP+UeRVj2zY1pSuPdgvbd5ykAw82Lrro28vXUrRmzEsUV0NzCf54yARIK8r0fdw==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.7.tgz", + "integrity": "sha512-hzznmADPt+OmsYzw1EE33ccA+HPdIqiCRq7cQeL1Jlq2gb1+OyWBkMCrYGBJ+sxVzve2ZJEVeePbLM2iEIZSxA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.7.tgz", + "integrity": "sha512-b6pqtrQdigZBwZxAn1UpazEisvwaIDvdbMbmrly7cDTMFnw/+3lVxxCTGOrkPVnsYIosJJXAsILG9XcQS+Yu6w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.7.tgz", + "integrity": "sha512-OfatkLojr6U+WN5EDYuoQhtM+1xco+/6FSzJJnuWiUw5eVcicbyK3dq5EeV/QHT1uy6GoDhGbFpprUiHUYggrw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.7.tgz", + "integrity": "sha512-AFuojMQTxAz75Fo8idVcqoQWEHIXFRbOc1TrVcFSgCZtQfSdc1RXgB3tjOn/krRHENUB4j00bfGjyl2mJrU37A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.7.tgz", + "integrity": "sha512-+A1NJmfM8WNDv5CLVQYJ5PshuRm/4cI6WMZRg1by1GwPIQPCTs1GLEUHwiiQGT5zDdyLiRM/l1G0Pv54gvtKIg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.7.tgz", + "integrity": "sha512-+KrvYb/C8zA9CU/g0sR6w2RBw7IGc5J2BPnc3dYc5VJxHCSF1yNMxTV5LQ7GuKteQXZtspjFbiuW5/dOj7H4Yw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.7.tgz", + "integrity": "sha512-ikktIhFBzQNt/QDyOL580ti9+5mL/YZeUPKU2ivGtGjdTYoqz6jObj6nOMfhASpS4GU4Q/Clh1QtxWAvcYKamA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.7.tgz", + "integrity": "sha512-7yRhbHvPqSpRUV7Q20VuDwbjW5kIMwTHpptuUzV+AA46kiPze5Z7qgt6CLCK3pWFrHeNfDd1VKgyP4O+ng17CA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.7.tgz", + "integrity": "sha512-SmwKXe6VHIyZYbBLJrhOoCJRB/Z1tckzmgTLfFYOfpMAx63BJEaL9ExI8x7v0oAO3Zh6D/Oi1gVxEYr5oUCFhw==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.7.tgz", + "integrity": "sha512-56hiAJPhwQ1R4i+21FVF7V8kSD5zZTdHcVuRFMW0hn753vVfQN8xlx4uOPT4xoGH0Z/oVATuR82AiqSTDIpaHg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@musher-dev/musher-sdk": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/@musher-dev/musher-sdk/-/musher-sdk-0.4.0.tgz", + "integrity": "sha512-Q77dxOtJ+dPRYCubX3DSRUc31opQE4jfYAUBBKft5NDoN4T34wJP6iXFr8f+reLVSbgL3K3nyhc0qxMocSzUgA==", + "license": "MIT", + "dependencies": { + "zod": "^3.24.0" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/esbuild": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.7.tgz", + "integrity": "sha512-IxpibTjyVnmrIQo5aqNpCgoACA/dTKLTlhMHihVHhdkxKyPO1uBBthumT0rdHmcsk9uMonIWS0m4FljWzILh3w==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.27.7", + "@esbuild/android-arm": "0.27.7", + "@esbuild/android-arm64": "0.27.7", + "@esbuild/android-x64": "0.27.7", + "@esbuild/darwin-arm64": "0.27.7", + "@esbuild/darwin-x64": "0.27.7", + "@esbuild/freebsd-arm64": "0.27.7", + "@esbuild/freebsd-x64": "0.27.7", + "@esbuild/linux-arm": "0.27.7", + "@esbuild/linux-arm64": "0.27.7", + "@esbuild/linux-ia32": "0.27.7", + "@esbuild/linux-loong64": "0.27.7", + "@esbuild/linux-mips64el": "0.27.7", + "@esbuild/linux-ppc64": "0.27.7", + "@esbuild/linux-riscv64": "0.27.7", + "@esbuild/linux-s390x": "0.27.7", + "@esbuild/linux-x64": "0.27.7", + "@esbuild/netbsd-arm64": "0.27.7", + "@esbuild/netbsd-x64": "0.27.7", + "@esbuild/openbsd-arm64": "0.27.7", + "@esbuild/openbsd-x64": "0.27.7", + "@esbuild/openharmony-arm64": "0.27.7", + "@esbuild/sunos-x64": "0.27.7", + "@esbuild/win32-arm64": "0.27.7", + "@esbuild/win32-ia32": "0.27.7", + "@esbuild/win32-x64": "0.27.7" + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/get-tsconfig": { + "version": "4.13.7", + "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.13.7.tgz", + "integrity": "sha512-7tN6rFgBlMgpBML5j8typ92BKFi2sFQvIdpAqLA2beia5avZDrMs0FLZiM5etShWq5irVyGcGMEA1jcDaK7A/Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "resolve-pkg-maps": "^1.0.0" + }, + "funding": { + "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" + } + }, + "node_modules/json-schema-to-ts": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/json-schema-to-ts/-/json-schema-to-ts-3.1.1.tgz", + "integrity": "sha512-+DWg8jCJG2TEnpy7kOm/7/AxaYoaRbjVB4LFZLySZlWn8exGs3A4OLJR966cVvU26N7X9TWxl+Jsw7dzAqKT6g==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.18.3", + "ts-algebra": "^2.0.0" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/resolve-pkg-maps": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz", + "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" + } + }, + "node_modules/ts-algebra": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ts-algebra/-/ts-algebra-2.0.0.tgz", + "integrity": "sha512-FPAhNPFMrkwz76P7cdjdmiShwMynZYN6SgOujD1urY4oNm80Ou9oMdmbR45LotcKOXoy7wSmHkRFE6Mxbrhefw==", + "license": "MIT" + }, + "node_modules/tsx": { + "version": "4.21.0", + "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.21.0.tgz", + "integrity": "sha512-5C1sg4USs1lfG0GFb2RLXsdpXqBSEhAaA/0kPL01wxzpMqLILNxIxIOKiILz+cdg/pLnOUxFYOR5yhHU666wbw==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "~0.27.0", + "get-tsconfig": "^4.7.5" + }, + "bin": { + "tsx": "dist/cli.mjs" + }, + "engines": { + "node": ">=18.0.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + } + }, + "node_modules/typescript": { + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/zod": { + "version": "3.25.76", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", + "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + } + } +} diff --git a/examples/typescript/package.json b/examples/typescript/package.json index 8fa32fc..22cf3ff 100644 --- a/examples/typescript/package.json +++ b/examples/typescript/package.json @@ -1,18 +1,19 @@ -{ - "name": "musher-demo-examples", - "version": "1.0.0", - "private": true, - "type": "module", - "scripts": { - "pull-bundle": "tsx 01-pull-bundle.ts", - "use-with-claude": "tsx 02-use-with-claude.ts" - }, - "dependencies": { - "@anthropic-ai/sdk": "^0.30.0", - "@musher-dev/musher-sdk": "^0.1.0" - }, - "devDependencies": { - "tsx": "^4.0.0", - "typescript": "^5.0.0" - } -} +{ + "name": "musher-demo-examples", + "version": "1.0.0", + "private": true, + "type": "module", + "scripts": { + "pull-bundle": "tsx 01-pull-bundle.ts", + "install-skills": "tsx 02-install-skills.ts", + "use-with-claude": "tsx 03-use-with-claude.ts" + }, + "dependencies": { + "@anthropic-ai/sdk": "^0.82.0", + "@musher-dev/musher-sdk": "^0.4.0" + }, + "devDependencies": { + "tsx": "^4.0.0", + "typescript": "^5.0.0" + } +} diff --git a/examples/typescript/tsconfig.json b/examples/typescript/tsconfig.json index eb87c67..14b524b 100644 --- a/examples/typescript/tsconfig.json +++ b/examples/typescript/tsconfig.json @@ -1,11 +1,11 @@ -{ - "compilerOptions": { - "target": "ES2022", - "module": "Node16", - "moduleResolution": "Node16", - "strict": true, - "esModuleInterop": true, - "skipLibCheck": true - }, - "include": ["./*.ts"] -} +{ + "compilerOptions": { + "target": "ES2022", + "module": "Node16", + "moduleResolution": "Node16", + "strict": true, + "esModuleInterop": true, + "skipLibCheck": true + }, + "include": ["./*.ts"] +} diff --git a/lefthook.yml b/lefthook.yml new file mode 100644 index 0000000..f6f019e --- /dev/null +++ b/lefthook.yml @@ -0,0 +1,42 @@ +# EXAMPLE USAGE: +# +# Refer for explanation to following link: +# https://lefthook.dev/configuration/ +# +# pre-push: +# jobs: +# - name: packages audit +# tags: +# - frontend +# - security +# run: yarn audit +# +# - name: gems audit +# tags: +# - backend +# - security +# run: bundle audit +# +# pre-commit: +# parallel: true +# jobs: +# - run: yarn eslint {staged_files} +# glob: "*.{js,ts,jsx,tsx}" +# +# - name: rubocop +# glob: "*.rb" +# exclude: +# - config/application.rb +# - config/routes.rb +# run: bundle exec rubocop --force-exclusion -- {all_files} +# +# - name: govet +# files: git ls-files -m +# glob: "*.go" +# run: go vet -- {files} +# +# - script: "hello.js" +# runner: node +# +# - script: "hello.go" +# runner: go run