From 1ca99cd7bcf78ee6454125a3284db0f2b9373f6a Mon Sep 17 00:00:00 2001 From: Justin Merrell Date: Fri, 3 Apr 2026 15:26:20 +0000 Subject: [PATCH] refactor: replace demo-starter bundle with repo-standards-demo Replace the previous demo-starter bundle (code-reviewer agent, summarizing-changes and writing-commit-messages skills) with the repo-standards-demo bundle featuring repo documentation auditing, scaffolding, README writing, and security policy skills. - Remove unused compose services and observability/postgres configs - Simplify devcontainer.json to minimal configuration - Update musher.yaml with new bundle metadata and skill definitions - Replace Python and TypeScript SDK examples for new workflow - Update all documentation (README, CONFIGURATION, examples) - Add repo-docs-auditor agent and associated skills Co-Authored-By: Claude Opus 4.6 (1M context) --- .devcontainer/compose.yaml | 26 - .devcontainer/compose/azimutt.yaml | 16 - .devcontainer/compose/minio.yaml | 25 - .devcontainer/compose/observability.yaml | 155 ------ .devcontainer/compose/postgres.yaml | 24 - .devcontainer/compose/redis.yaml | 21 - .devcontainer/compose/registry.yaml | 20 - .../provisioning/dashboards/dashboards.yaml | 12 - .../provisioning/dashboards/json/.gitkeep | 0 .../provisioning/datasources/datasources.yaml | 23 - .../config/observability/loki-config.yaml | 38 -- .../observability/otel-collector-config.yaml | 41 -- .../config/observability/tempo-config.yaml | 36 -- .devcontainer/config/postgres/00-init.sql | 58 --- .../config/postgres/01-project.sql.example | 15 - .devcontainer/devcontainer.json | 328 ++++-------- .devcontainer/scripts/lib/base-setup.sh | 29 +- .github/workflows/validate.yaml | 70 ++- .gitignore | 4 + CONFIGURATION.md | 488 ++++-------------- README.md | 205 ++++---- bundle/README.md | 122 +++-- bundle/agents/code-reviewer.md | 94 ---- bundle/agents/repo-docs-auditor.md | 37 ++ bundle/musher.yaml | 63 ++- .../auditing-repo-documentation/SKILL.md | 104 ++++ .../references/scoring-rubric.md | 108 ++++ .../scaffolding-repo-documentation/SKILL.md | 104 ++++ .../references/docs-checklist.md | 38 ++ .../references/github-community-profile.md | 35 ++ bundle/skills/summarizing-changes/SKILL.md | 153 ------ .../skills/writing-commit-messages/SKILL.md | 173 ------- bundle/skills/writing-readme/SKILL.md | 68 +++ .../templates/README.template.md | 86 +++ .../skills/writing-security-policy/SKILL.md | 56 ++ .../templates/SECURITY.template.md | 60 +++ examples/README.md | 88 ++-- examples/python/01_pull_bundle.py | 50 -- examples/python/02_install_skills.py | 32 -- examples/python/03_use_with_claude.py | 82 --- examples/python/README.md | 94 ++-- .../.github/workflows/ci.yml | 18 + .../fixtures/under_documented_repo/LICENSE | 21 + .../fixtures/under_documented_repo/README.md | 3 + .../under_documented_repo/pyproject.toml | 15 + .../fixtures/under_documented_repo/src/app.py | 22 + examples/python/repo_docs_audit.py | 280 ++++++++++ examples/python/requirements.txt | 4 +- examples/typescript/01-pull-bundle.ts | 104 ++-- examples/typescript/02-install-skills.ts | 22 +- examples/typescript/03-use-with-claude.ts | 93 +--- examples/typescript/README.md | 79 +-- examples/typescript/package.json | 37 +- 53 files changed, 1757 insertions(+), 2222 deletions(-) delete mode 100644 .devcontainer/compose.yaml delete mode 100644 .devcontainer/compose/azimutt.yaml delete mode 100644 .devcontainer/compose/minio.yaml delete mode 100644 .devcontainer/compose/observability.yaml delete mode 100644 .devcontainer/compose/postgres.yaml delete mode 100644 .devcontainer/compose/redis.yaml delete mode 100644 .devcontainer/compose/registry.yaml delete mode 100644 .devcontainer/config/observability/grafana/provisioning/dashboards/dashboards.yaml delete mode 100644 .devcontainer/config/observability/grafana/provisioning/dashboards/json/.gitkeep delete mode 100644 .devcontainer/config/observability/grafana/provisioning/datasources/datasources.yaml delete mode 100644 .devcontainer/config/observability/loki-config.yaml delete mode 100644 .devcontainer/config/observability/otel-collector-config.yaml delete mode 100644 .devcontainer/config/observability/tempo-config.yaml delete mode 100644 .devcontainer/config/postgres/00-init.sql delete mode 100644 .devcontainer/config/postgres/01-project.sql.example delete mode 100644 bundle/agents/code-reviewer.md create mode 100644 bundle/agents/repo-docs-auditor.md create mode 100644 bundle/skills/auditing-repo-documentation/SKILL.md create mode 100644 bundle/skills/auditing-repo-documentation/references/scoring-rubric.md create mode 100644 bundle/skills/scaffolding-repo-documentation/SKILL.md create mode 100644 bundle/skills/scaffolding-repo-documentation/references/docs-checklist.md create mode 100644 bundle/skills/scaffolding-repo-documentation/references/github-community-profile.md delete mode 100644 bundle/skills/summarizing-changes/SKILL.md delete mode 100644 bundle/skills/writing-commit-messages/SKILL.md create mode 100644 bundle/skills/writing-readme/SKILL.md create mode 100644 bundle/skills/writing-readme/templates/README.template.md create mode 100644 bundle/skills/writing-security-policy/SKILL.md create mode 100644 bundle/skills/writing-security-policy/templates/SECURITY.template.md delete mode 100644 examples/python/01_pull_bundle.py delete mode 100644 examples/python/02_install_skills.py delete mode 100644 examples/python/03_use_with_claude.py create mode 100644 examples/python/fixtures/under_documented_repo/.github/workflows/ci.yml create mode 100644 examples/python/fixtures/under_documented_repo/LICENSE create mode 100644 examples/python/fixtures/under_documented_repo/README.md create mode 100644 examples/python/fixtures/under_documented_repo/pyproject.toml create mode 100644 examples/python/fixtures/under_documented_repo/src/app.py create mode 100644 examples/python/repo_docs_audit.py diff --git a/.devcontainer/compose.yaml b/.devcontainer/compose.yaml deleted file mode 100644 index d11a61c..0000000 --- a/.devcontainer/compose.yaml +++ /dev/null @@ -1,26 +0,0 @@ -# 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 deleted file mode 100644 index 839d4b4..0000000 --- a/.devcontainer/compose/azimutt.yaml +++ /dev/null @@ -1,16 +0,0 @@ -# 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 deleted file mode 100644 index 7db3df2..0000000 --- a/.devcontainer/compose/minio.yaml +++ /dev/null @@ -1,25 +0,0 @@ -# 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 deleted file mode 100644 index 1109a7b..0000000 --- a/.devcontainer/compose/observability.yaml +++ /dev/null @@ -1,155 +0,0 @@ -# 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 deleted file mode 100644 index 2fa3277..0000000 --- a/.devcontainer/compose/postgres.yaml +++ /dev/null @@ -1,24 +0,0 @@ -# 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 deleted file mode 100644 index ea13083..0000000 --- a/.devcontainer/compose/redis.yaml +++ /dev/null @@ -1,21 +0,0 @@ -# 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 deleted file mode 100644 index cbee88b..0000000 --- a/.devcontainer/compose/registry.yaml +++ /dev/null @@ -1,20 +0,0 @@ -# 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 deleted file mode 100644 index 849b493..0000000 --- a/.devcontainer/config/observability/grafana/provisioning/dashboards/dashboards.yaml +++ /dev/null @@ -1,12 +0,0 @@ -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/dashboards/json/.gitkeep b/.devcontainer/config/observability/grafana/provisioning/dashboards/json/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/.devcontainer/config/observability/grafana/provisioning/datasources/datasources.yaml b/.devcontainer/config/observability/grafana/provisioning/datasources/datasources.yaml deleted file mode 100644 index 4be8676..0000000 --- a/.devcontainer/config/observability/grafana/provisioning/datasources/datasources.yaml +++ /dev/null @@ -1,23 +0,0 @@ -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 deleted file mode 100644 index dea8b74..0000000 --- a/.devcontainer/config/observability/loki-config.yaml +++ /dev/null @@ -1,38 +0,0 @@ -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 deleted file mode 100644 index 125d588..0000000 --- a/.devcontainer/config/observability/otel-collector-config.yaml +++ /dev/null @@ -1,41 +0,0 @@ -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 deleted file mode 100644 index 7753b08..0000000 --- a/.devcontainer/config/observability/tempo-config.yaml +++ /dev/null @@ -1,36 +0,0 @@ -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 deleted file mode 100644 index eee71c8..0000000 --- a/.devcontainer/config/postgres/00-init.sql +++ /dev/null @@ -1,58 +0,0 @@ --- 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 deleted file mode 100644 index 08a9452..0000000 --- a/.devcontainer/config/postgres/01-project.sql.example +++ /dev/null @@ -1,15 +0,0 @@ --- 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/devcontainer.json b/.devcontainer/devcontainer.json index 1c3156d..774fe05 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -1,217 +1,111 @@ -// 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" - } - } - } - } -} +// Musher Demo Dev Container +{ + "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" + }, + + // --- Package managers --- + "ghcr.io/devcontainers-extra/features/uv:1": {} + }, + + "containerUser": "vscode", + "remoteUser": "vscode", + + "init": true, + "shutdownAction": "stopContainer", + + "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" + ], + + "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", + "NPM_CONFIG_CACHE": "/home/vscode/.cache/npm" + }, + + "remoteEnv": { + "PATH": "/home/vscode/.local/bin:${containerEnv:PATH}" + }, + + "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"], + + "customizations": { + "vscode": { + "extensions": [ + "redhat.vscode-yaml", + "tamasfe.even-better-toml", + "esbenp.prettier-vscode", + "ms-python.python", + "ms-python.vscode-pylance", + "charliermarsh.ruff", + "dbaeumer.vscode-eslint", + "eamodio.gitlens", + "bierner.markdown-mermaid" + ], + "settings": { + "editor.formatOnSave": true, + "editor.rulers": [80, 120], + "files.trimTrailingWhitespace": true, + "files.insertFinalNewline": true, + "terminal.integrated.defaultProfile.linux": "zsh", + "terminal.integrated.profiles.linux": { + "bash": { "path": "/bin/bash" }, + "zsh": { "path": "/bin/zsh" } + }, + "[python]": { + "editor.defaultFormatter": "charliermarsh.ruff", + "editor.codeActionsOnSave": { + "source.fixAll": "explicit", + "source.organizeImports": "explicit" + } + }, + "[javascript]": { + "editor.defaultFormatter": "esbenp.prettier-vscode" + }, + "[typescript]": { + "editor.defaultFormatter": "esbenp.prettier-vscode" + }, + "[json]": { + "editor.defaultFormatter": "esbenp.prettier-vscode" + } + } + } + } +} diff --git a/.devcontainer/scripts/lib/base-setup.sh b/.devcontainer/scripts/lib/base-setup.sh index 0255a83..9327258 100644 --- a/.devcontainer/scripts/lib/base-setup.sh +++ b/.devcontainer/scripts/lib/base-setup.sh @@ -33,8 +33,7 @@ source "${_LIB_DIR}/common.sh" base_setup_config_dirs() { setup_config_dirs \ "gh config:${_HOME}/.config/gh" \ - "claude:${_HOME}/.claude" \ - "codex:${_HOME}/.codex" + "claude:${_HOME}/.claude" } # --- Cache directories --- @@ -52,12 +51,7 @@ base_setup_cache_dirs() { "uv cache:${_HOME}/.cache/uv" \ "ruff cache:${_HOME}/.cache/ruff" \ "pip cache:${_HOME}/.cache/pip" \ - "mypy cache:${_HOME}/.cache/mypy" \ - "npm cache:${_HOME}/.cache/npm" \ - "deno cache:${_HOME}/.cache/deno" \ - "go mod cache:${_HOME}/.cache/go/mod" \ - "go build cache:${_HOME}/.cache/go/build" \ - "bun cache:${_HOME}/.cache/bun" + "npm cache:${_HOME}/.cache/npm" } # --- NVM --- @@ -84,22 +78,6 @@ base_install_claude() { retry 3 5 bash -c 'curl -fsSL https://claude.ai/install.sh | bash' } -# --- Codex CLI --- - -# Installs the Codex CLI if not already present. -# -# Outputs: -# Writes progress to stderr via log() -# Returns: -# 0 on success, non-zero on failure -base_install_codex() { - if has_cmd codex; then - log "Codex CLI already installed, skipping" - return 0 - fi - install_npm_cli "@openai/codex" -} - # --- Lefthook --- # Installs Lefthook if not already present. @@ -125,7 +103,7 @@ base_install_lefthook() { # Returns: # 0 if all tools found, 1 if any are missing base_verify_tools() { - verify_tools gh claude task codex lefthook + verify_tools gh claude lefthook } # --- Orchestrator --- @@ -140,7 +118,6 @@ base_setup() { base_setup_cache_dirs base_fix_nvm_permissions base_install_claude - base_install_codex base_install_lefthook base_verify_tools log "Base setup complete" diff --git a/.github/workflows/validate.yaml b/.github/workflows/validate.yaml index a75925e..9101c70 100644 --- a/.github/workflows/validate.yaml +++ b/.github/workflows/validate.yaml @@ -1,40 +1,30 @@ -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 + + 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 21ff3ec..ef31fcd 100644 --- a/.gitignore +++ b/.gitignore @@ -9,3 +9,7 @@ # Dependencies node_modules/ + +# Python example outputs +examples/python/out/ +examples/python/.claude/skills/ diff --git a/CONFIGURATION.md b/CONFIGURATION.md index 38365ab..c85b3af 100644 --- a/CONFIGURATION.md +++ b/CONFIGURATION.md @@ -1,376 +1,112 @@ -# 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. + +## 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) + +VS Code editor behavior or extension? + → devcontainer.json → customizations.vscode block + +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** | Language runtimes (Node, Python) | `devcontainer.json` → `features` | +| | Package managers (uv) | `devcontainer.json` → `features` | +| | CLI tools (Claude) | `scripts/lib/base-setup.sh` | +| **Editor** | VS Code settings | `devcontainer.json` → `customizations.vscode.settings` | +| | VS Code extensions | `devcontainer.json` → `customizations.vscode.extensions` | +| **Shell** | Shell aliases, functions | `.devcontainer/config/shell/` | +| | Git config | Host `.gitconfig` (auto-forwarded) | +| **Environment** | Runtime behavior vars | `devcontainer.json` → `containerEnv` | +| | PATH extensions | `devcontainer.json` → `remoteEnv` | +| | Secrets (API keys) | Host env forwarded via `remoteEnv` — never committed | +| **Lifecycle** | One-time container setup | `scripts/post-create.sh` → `lib/base-setup.sh` | +| | Every-start tasks | `scripts/startup.sh` | +| **AI Tools** | Claude CLI installation | `lib/base-setup.sh` | +| | Claude config persistence | `devcontainer.json` → `mounts` (named volume) | + +--- + +## Shell Customization + +| Pattern | Tracked | Purpose | +|---|---|---| +| `*.shared.sh` | Yes | Team defaults — aliases, functions | +| `*.local.sh` | No (gitignored) | Personal overrides | + +Shared files are sourced first, then local files. + +--- + +## Environment Variables + +| Scope | Location | When to Use | +|---|---|---| +| **Container-wide** | `devcontainer.json` → `containerEnv` | Runtime behavior (`PYTHONUNBUFFERED`, etc.) | +| **Remote/IDE** | `devcontainer.json` → `remoteEnv` | PATH extensions, forwarded host secrets | + +Never commit secrets. Forward them from your host: + +```jsonc +"remoteEnv": { + "MY_API_KEY": "${localEnv:MY_API_KEY}" +} +``` + +--- + +## Lifecycle + +| Hook | Runs | Use For | +|---|---|---| +| `postCreateCommand` | Once, on container creation | Tool installation, permissions | +| `postStartCommand` | Every container start | Service startup | + +### Script Layers + +``` +post-create.sh ← Entry point (repo-specific customization) + └── lib/base-setup.sh ← Reusable orchestrator (Claude CLI, config dirs) + └── lib/common.sh ← Shared utilities (log, retry, has_cmd) +``` + +--- + +## Directory Map + +``` +.devcontainer/ + devcontainer.json Runtimes, extensions, settings, mounts + .env.example Environment template (copy to .env) + .env Local overrides (gitignored) + config/ + shell/ + aliases.shared.sh Team-default shell aliases + scripts/ + post-create.sh One-time setup entry point + startup.sh Every-start launcher + lib/ + base-setup.sh Reusable tool installer + common.sh Shared utilities + motd.sh Message of the day +``` diff --git a/README.md b/README.md index 329276e..0a92a22 100644 --- a/README.md +++ b/README.md @@ -1,105 +1,100 @@ -# 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. +# Musher Demo + +A showcase repository for the [Musher](https://hub.musher.dev) platform. It contains a ready-to-publish bundle with multi-file skills and an agent spec, plus SDK examples that demonstrate the full lifecycle: **author → publish → install → run with Claude Agent SDK**. + +## Why This Example Exists + +Instead of hard-coding long system prompts or manually copying `.claude/skills/` folders between projects, we: + +1. **Author** a versioned Musher bundle with multi-file skills (rubrics, templates, checklists) +2. **Publish** it to the Musher Hub (or pack it locally) +3. **Install** the skills into the filesystem layout the Claude Agent SDK expects +4. **Run** the Agent SDK — Claude discovers and invokes the skills autonomously, constrained by hooks, permissions, and structured output + +The demo uses a **repo documentation auditing** use case: the agent scans a fixture repository, scores its documentation health, and scaffolds missing files. + +## What's Inside + +``` +demo/ +├── bundle/ Musher bundle source (skills + agents) +│ ├── musher.yaml Bundle manifest +│ ├── skills/ +│ │ ├── auditing-repo-documentation/ Score documentation health (SKILL.md + rubric) +│ │ ├── scaffolding-repo-documentation/ Create missing docs (SKILL.md + checklist + reference) +│ │ ├── writing-readme/ Generate README (SKILL.md + template) +│ │ └── writing-security-policy/ Generate SECURITY.md (SKILL.md + template) +│ └── agents/ +│ └── repo_docs_auditor.md Orchestrator agent composing all skills +└── examples/ + ├── python/ + │ ├── repo_docs_audit.py Pull, install, and run Agent SDK in one script + │ └── fixtures/under_documented_repo/ Intentionally under-documented fixture project + └── typescript/ TypeScript SDK (pull + install; Agent SDK demo pending) +``` + +## The Bundle + +The `repo-documentation-governance` bundle ships four multi-file skills and one agent for documentation governance. + +| Asset | Type | Files | What it does | +|-------|------|-------|--------------| +| `auditing-repo-documentation` | Skill | SKILL.md, scoring-rubric.md | Score docs health with weighted rubric | +| `scaffolding-repo-documentation` | Skill | SKILL.md, docs-checklist.md, github-community-profile.md | Create missing docs based on audit | +| `writing-readme` | Skill | SKILL.md, templates/README.template.md | Generate or improve README.md | +| `writing-security-policy` | Skill | SKILL.md, templates/SECURITY.template.md | Generate SECURITY.md with disclosure policy | +| `repo-docs-auditor` | Agent | repo_docs_auditor.md | Orchestrator that audits, scaffolds, and reports | + +## Quick Start + +### 1. Publish or pack the bundle + +```bash +# Install the Musher CLI +curl -fsSL https://get.musher.dev | sh + +cd bundle + +# Option A: Publish to your Musher Hub account +musher bundle publish + +# Option B: Pack locally (no account needed) +musher bundle pack +``` + +### 2. Run the demo (Python) + +```bash +cd examples/python +pip install -r requirements.txt +export MUSHER_API_KEY="mush_..." +export ANTHROPIC_API_KEY="sk-ant-..." + +# Point at your published bundle (or use the default) +export MUSHER_BUNDLE_REF="your-namespace/repo-documentation-governance:1.0.0" + +python repo_docs_audit.py +``` + +The script pulls the bundle, installs skills, and runs the Claude Agent SDK in one go. + +### What to Look For + +- Skills are loaded from `.claude/skills/` — including companion files (rubrics, templates) +- Claude invokes them autonomously through the `Skill` tool +- Hooks constrain where the agent can write (fixture repo only) +- The final result is JSON you could feed into CI, a dashboard, or another service + +## 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 dev container with Python, Node.js, and Claude CLI pre-installed. See [CONFIGURATION.md](CONFIGURATION.md) for details. diff --git a/bundle/README.md b/bundle/README.md index 51b86e5..6a0858f 100644 --- a/bundle/README.md +++ b/bundle/README.md @@ -1,63 +1,59 @@ -# 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 +# Repo Documentation Governance + +A [Musher](https://hub.musher.dev) bundle that audits and scaffolds repository documentation. Ships four multi-file skills and a specialist agent — designed to demonstrate how Musher distributes versioned agent behavior for the Claude Agent SDK. + +- **Domain:** repo-documentation +- **Purpose:** governance +- **Risk Level:** low +- **Technologies:** Claude Agent SDK +- **Aliases:** repo-docs, documentation-audit + +## Quick Start + +Publish to your own account or pack locally: + +```sh +cd bundle + +# Option A: Publish to Musher Hub +musher bundle publish + +# Option B: Local pack (no account needed) +musher bundle pack +``` + +Then install and use the skills: + +``` +Audit this repo's documentation health +``` + +``` +Scaffold missing community-health files +``` + +## What's Inside + +### Skills + +| Skill | Files | Description | +|-------|-------|-------------| +| `auditing-repo-documentation` | SKILL.md, scoring-rubric.md | Score documentation health with weighted rubric | +| `scaffolding-repo-documentation` | SKILL.md, docs-checklist.md, github-community-profile.md | Create missing docs based on audit results | +| `writing-readme` | SKILL.md, templates/README.template.md | Generate or improve README.md | +| `writing-security-policy` | SKILL.md, templates/SECURITY.template.md | Generate SECURITY.md with disclosure policy | + +### Agents + +| Agent | Description | +|-------|-------------| +| `repo_docs_auditor` | Orchestrator that audits, scaffolds, and reports in a single run | + +### Tools + +None + +## Why Multi-File Skills? + +Each skill ships companion files (rubrics, templates, checklists) alongside SKILL.md. This demonstrates Musher's distribution value: you are not just shipping a prompt, you are shipping **reusable expertise with supporting data** — versioned, pinned, and discoverable by the SDK. + diff --git a/bundle/agents/code-reviewer.md b/bundle/agents/code-reviewer.md deleted file mode 100644 index c5f09ac..0000000 --- a/bundle/agents/code-reviewer.md +++ /dev/null @@ -1,94 +0,0 @@ ---- -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/agents/repo-docs-auditor.md b/bundle/agents/repo-docs-auditor.md new file mode 100644 index 0000000..254211c --- /dev/null +++ b/bundle/agents/repo-docs-auditor.md @@ -0,0 +1,37 @@ +--- +name: repo-docs-auditor +description: Audit a repository for documentation gaps, then scaffold missing community-health and onboarding files. +tools: + - Read + - Glob + - Grep + - Write + - Skill +model: sonnet +permissionMode: default +--- + +# Repo Documentation Auditor + +You are a documentation auditor agent. Your job is to assess and improve a repository's documentation posture. + +## Workflow + +1. **Audit** — Use the `auditing-repo-documentation` skill to scan the target repository and produce a scored health report. + +2. **Plan** — Based on the audit results, identify which files should be created. Prioritize high-severity gaps first. + +3. **Scaffold** — Use the `scaffolding-repo-documentation` skill to create missing files. This skill will delegate to specialist skills (`writing-readme`, `writing-security-policy`) where appropriate. + +4. **Report** — Produce a final summary with: + - Overall documentation health score (before and after) + - List of files that were created + - Remaining gaps that require human attention (e.g. LICENSE selection, contact emails) + - Recommended next steps + +## Constraints + +- Never overwrite existing files. +- Never generate LICENSE files — license selection is a legal decision. +- Use TODO markers for content that requires human judgment. +- Stay within the target repository directory. diff --git a/bundle/musher.yaml b/bundle/musher.yaml index 31d5858..a630dec 100644 --- a/bundle/musher.yaml +++ b/bundle/musher.yaml @@ -1,28 +1,35 @@ -# 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 +namespace: musher-examples +slug: repo-documentation-governance +version: 1.0.0 +name: Repo Documentation Governance +description: A Musher demo bundle for repository documentation auditing and scaffolding with Claude Agent SDK. +visibility: public +readme: README.md +license: MIT +repository: https://github.com/musher-dev/demo +keywords: + - demo + - documentation + - governance + - github + - claude-agent-sdk +assets: + - id: auditing-repo-documentation + src: skills/auditing-repo-documentation/SKILL.md + kind: skill + + - id: scaffolding-repo-documentation + src: skills/scaffolding-repo-documentation/SKILL.md + kind: skill + + - id: writing-readme + src: skills/writing-readme/SKILL.md + kind: skill + + - id: writing-security-policy + src: skills/writing-security-policy/SKILL.md + kind: skill + + - id: repo-docs-auditor + src: agents/repo-docs-auditor.md + kind: agent diff --git a/bundle/skills/auditing-repo-documentation/SKILL.md b/bundle/skills/auditing-repo-documentation/SKILL.md new file mode 100644 index 0000000..d7ec220 --- /dev/null +++ b/bundle/skills/auditing-repo-documentation/SKILL.md @@ -0,0 +1,104 @@ +--- +name: auditing-repo-documentation +version: 1.0.0 +user-invocable: true +description: Scan a repository and score its documentation health against community-health and onboarding best practices. Use when assessing documentation quality, checking repo health, or identifying missing docs. Triggered by: audit docs, documentation health, repo score, docs review, community health check. +argument-hint: "[path/to/repo]" +allowed-tools: + - Bash + - Read + - Grep + - Glob +--- + +# Audit Repository Documentation + +Scan a repository and produce a weighted documentation-health score with actionable gap analysis. + +## Input + +**Arguments:** `$ARGUMENTS` + +- If a path is provided, audit that directory as the repository root. +- If empty, use the current working directory. + +## Phase 1 — Discover Repository Context + +Gather context that informs scoring and recommendations: + +1. Detect the primary language and framework from manifest files (`package.json`, `pyproject.toml`, `Cargo.toml`, `go.mod`, etc.). +2. List existing documentation files at the repo root and under `.github/`. +3. Note any `docs/` directory or existing documentation infrastructure. + +If the directory is not a git repository, skip git-specific checks and focus on file-presence auditing. + +## Phase 2 — Score Each Documentation File + +Use the scoring rubric in `references/scoring-rubric.md` (bundled alongside this skill). For each file listed in the rubric: + +1. **Check existence** — Does the file exist at the expected path (including common variants)? +2. **Check content quality** — If it exists, read it and assess against the rubric's quality criteria. +3. **Assign a score** — 0 (missing), 1 (poor/stub), 2 (adequate), 3 (good). +4. **Apply the weight** — Multiply the score by the rubric weight for that file. + +## Phase 3 — Calculate Overall Score + +``` +overall_score = sum(weighted_scores) / sum(max_possible_weighted_scores) * 100 +``` + +Round to the nearest integer. Map to a grade: + +| Score | Grade | Label | +|-------|-------|-------| +| 90–100 | A | Excellent | +| 75–89 | B | Good | +| 50–74 | C | Needs Improvement | +| 25–49 | D | Poor | +| 0–24 | F | Critical | + +## Phase 4 — Prioritize Gaps + +Sort missing or inadequate files by: + +1. **Severity** — High (weight ≥ 5), Medium (weight 3), Low (weight ≤ 2) +2. **Impact** — Files that block contributors rank above internal-only docs + +For each gap, provide: + +- File name and expected path +- Why it matters (one sentence) +- Suggested next step (e.g. "Use the `scaffolding-repo-documentation` skill to create this file") + +## Phase 5 — Output + +Present the assessment: + +``` +## Documentation Health Report + +**Repository:** +**Overall Score:** /100 () +**Primary Language:** + +### Per-File Scores + +| File | Status | Score | Weight | Weighted | +|------|--------|-------|--------|----------| +| README.md | ✅ Good | 3/3 | ×5 | 15/15 | +| CONTRIBUTING.md | ❌ Missing | 0/3 | ×3 | 0/9 | +| ... | ... | ... | ... | ... | + +### High Priority Gaps +1. **CONTRIBUTING.md** (weight: 3) — No contributor guide. + → Use the `scaffolding-repo-documentation` skill to create this file. + +### Summary +<2–3 sentence summary of the repo's documentation posture and the most impactful next steps.> +``` + +## Edge Cases + +- **Empty README** — A README with only a heading or fewer than 50 characters scores 1, not 2. +- **Non-GitHub hosting** — If `.github/` does not exist, skip GitHub-specific files but note them. +- **Monorepo** — Score only the root-level docs. Note that sub-packages may need their own READMEs. diff --git a/bundle/skills/auditing-repo-documentation/references/scoring-rubric.md b/bundle/skills/auditing-repo-documentation/references/scoring-rubric.md new file mode 100644 index 0000000..47cf033 --- /dev/null +++ b/bundle/skills/auditing-repo-documentation/references/scoring-rubric.md @@ -0,0 +1,108 @@ +# Documentation Scoring Rubric + +This rubric is referenced by the `auditing-repo-documentation` skill. Each file is scored 0–3 and multiplied by its weight to produce a weighted score. + +## Score Scale + +| Score | Meaning | +|-------|---------| +| 0 | Missing — file does not exist | +| 1 | Poor — file exists but is a stub, placeholder, or severely incomplete | +| 2 | Adequate — file covers the basics but lacks depth or polish | +| 3 | Good — file is thorough, well-structured, and actionable | + +## Scored Files + +### Tier 1: Critical (Weight 5) + +#### README.md +- **Path:** `README.md` (or `readme.md`, `README.rst`) +- **Quality criteria:** + - Contains project name and description (score ≥ 1) + - Includes installation/setup instructions (score ≥ 2) + - Includes usage examples, badges, contributing link, and license reference (score = 3) +- **Why:** The single most important file. First thing every visitor reads. + +#### LICENSE +- **Path:** `LICENSE`, `LICENSE.md`, `LICENSE.txt`, or `LICENCE` +- **Quality criteria:** + - Contains a recognized license text (score ≥ 2) + - Clearly identifies the license type (score = 3) +- **Why:** Legal requirement for open-source adoption. + +### Tier 2: Important (Weight 3) + +#### CONTRIBUTING.md +- **Path:** `CONTRIBUTING.md` +- **Quality criteria:** + - Describes how to submit changes (score ≥ 1) + - Includes code style, branch strategy, and PR process (score ≥ 2) + - Covers development setup, testing expectations, and communication channels (score = 3) +- **Why:** Removes friction for new contributors. + +#### SECURITY.md +- **Path:** `SECURITY.md` +- **Quality criteria:** + - Contains a reporting mechanism (score ≥ 1) + - Specifies supported versions and response timeline (score ≥ 2) + - Includes disclosure policy and security contacts (score = 3) +- **Why:** Gives vulnerability reporters a safe channel. + +#### CODE_OF_CONDUCT.md +- **Path:** `CODE_OF_CONDUCT.md` +- **Quality criteria:** + - Adopts or references a standard code of conduct (score ≥ 2) + - Includes enforcement contact and consequences (score = 3) +- **Why:** Sets behavioral expectations for contributors. + +#### CHANGELOG.md +- **Path:** `CHANGELOG.md`, `CHANGES.md`, or `HISTORY.md` +- **Quality criteria:** + - Contains at least one version entry (score ≥ 1) + - Follows a consistent format (score ≥ 2) + - Covers recent releases with categorized entries (score = 3) +- **Why:** Lets users understand release history without reading commits. + +### Tier 3: Recommended (Weight 2) + +#### CODEOWNERS +- **Path:** `CODEOWNERS` or `.github/CODEOWNERS` +- **Quality criteria:** + - Contains at least one ownership rule (score ≥ 2) + - Covers major directories with specific teams or individuals (score = 3) +- **Why:** Automates PR review assignment. + +#### .github/PULL_REQUEST_TEMPLATE.md +- **Path:** `.github/PULL_REQUEST_TEMPLATE.md` +- **Quality criteria:** + - Contains a structured template with sections (score ≥ 2) + - Includes checklist items for testing, docs, and breaking changes (score = 3) +- **Why:** Standardizes PR descriptions. + +### Tier 4: Nice to Have (Weight 1) + +#### .github/ISSUE_TEMPLATE/ +- **Path:** `.github/ISSUE_TEMPLATE/` directory or `.github/ISSUE_TEMPLATE.md` +- **Quality criteria:** + - At least one template exists (score ≥ 1) + - Separate templates for bugs and features (score ≥ 2) + - Includes `config.yml` for template chooser (score = 3) +- **Why:** Reduces incomplete bug reports. + +#### .github/FUNDING.yml +- **Path:** `.github/FUNDING.yml` +- **Quality criteria:** + - Contains at least one valid funding platform entry (score ≥ 2) +- **Why:** Enables GitHub's sponsor button. + +## Maximum Possible Score + +| Tier | Files | Max per File | Weight | Max Weighted | +|------|-------|-------------|--------|--------------| +| Critical | 2 | 3 | 5 | 30 | +| Important | 4 | 3 | 3 | 36 | +| Recommended | 2 | 3 | 2 | 12 | +| Nice to Have | 2 | 3 | 1 | 6 | +| **Total** | **10** | | | **84** | + +Overall score = (sum of weighted scores / 84) × 100, rounded to nearest integer. diff --git a/bundle/skills/scaffolding-repo-documentation/SKILL.md b/bundle/skills/scaffolding-repo-documentation/SKILL.md new file mode 100644 index 0000000..2c1af86 --- /dev/null +++ b/bundle/skills/scaffolding-repo-documentation/SKILL.md @@ -0,0 +1,104 @@ +--- +name: scaffolding-repo-documentation +version: 1.0.0 +user-invocable: true +description: Create missing documentation files based on audit results or a direct request. Uses repository context to generate tailored content. Never overwrites existing files. Use when scaffolding docs, creating community health files, or filling documentation gaps after an audit. Triggered by: scaffold docs, create docs, missing documentation, generate community files, docs gaps. +argument-hint: "[path/to/repo] [--files FILE1,FILE2,...] [--all]" +allowed-tools: + - Bash + - Read + - Write + - Glob + - Grep + - Skill +--- + +# Scaffold Repository Documentation + +Create missing community-health and onboarding documentation files, tailored to the repository's language, framework, and existing content. + +## Input + +**Arguments:** `$ARGUMENTS` + +- **Path** — Repository root to scaffold into. Defaults to the current working directory. +- **`--files`** — Comma-separated list of specific files to create. +- **`--all`** — Create all missing files from the checklist. +- If neither flag is provided, create all files flagged as High or Medium priority. + +## Phase 1 — Assess Current State + +Check which documentation files already exist. Use the checklist in `references/docs-checklist.md` (bundled alongside this skill) as the canonical list. + +If the `auditing-repo-documentation` skill was invoked earlier in this conversation, reuse its findings instead of re-scanning. + +## Phase 2 — Gather Repository Context + +Collect signals that inform generated content: + +1. **Language and framework** — Check `package.json`, `pyproject.toml`, `Cargo.toml`, `go.mod`, etc. +2. **Project name** — From the manifest, directory name, or first heading in an existing README. +3. **License** — From LICENSE file or package manifest. +4. **Existing README** — Extract useful context if present. +5. **GitHub expectations** — Reference `references/github-community-profile.md` (bundled alongside this skill). + +## Phase 3 — Generate Missing Files + +For each file to be created: + +1. **Confirm it does not already exist.** Never overwrite. +2. **Generate content** tailored to the repository context from Phase 2. +3. **Delegate to specialist skills when available:** + - For `README.md` → invoke the `writing-readme` skill + - For `SECURITY.md` → invoke the `writing-security-policy` skill +4. **For other files**, generate directly using the guidance below. + +### CONTRIBUTING.md +Sections: How to Contribute (fork/branch/PR workflow), Development Setup (language-appropriate commands), Code Style (reference linter config if present), Pull Request Process, Reporting Issues. + +### CODE_OF_CONDUCT.md +Adopt the Contributor Covenant v2.1. Use `[INSERT CONTACT METHOD]` as a TODO placeholder for the enforcement contact. + +### CHANGELOG.md +Skeleton following Keep a Changelog format with an `[Unreleased]` section. + +### CODEOWNERS +Generate based on directory structure with placeholder team names (`@org/maintainers`). + +### .github/PULL_REQUEST_TEMPLATE.md +Structured template: Summary, Type of Change (checkboxes), Testing, Checklist. + +### .github/ISSUE_TEMPLATE/bug_report.md +YAML frontmatter + sections: Description, Steps to Reproduce, Expected/Actual Behavior, Environment. + +### .github/ISSUE_TEMPLATE/feature_request.md +YAML frontmatter + sections: Problem Statement, Proposed Solution, Alternatives, Additional Context. + +## Phase 4 — Output + +Print a summary: + +``` +## Scaffolding Report + +**Repository:** + +### Files Created +- ✅ CONTRIBUTING.md +- ✅ SECURITY.md (via writing-security-policy skill) + +### Files Skipped (already exist) +- ⏭️ README.md +- ⏭️ LICENSE + +### TODO Items +- CODE_OF_CONDUCT.md: Replace `[INSERT CONTACT METHOD]` with an enforcement email +- CODEOWNERS: Replace `@org/maintainers` with actual GitHub team handles +``` + +## Safety Rules + +- **NEVER overwrite an existing file.** Skip and note in report. +- **NEVER generate LICENSE files.** License selection is a legal decision — flag it only. +- **ALWAYS use TODO markers** for content requiring human judgment. +- **Create parent directories** (e.g. `.github/ISSUE_TEMPLATE/`) as needed. diff --git a/bundle/skills/scaffolding-repo-documentation/references/docs-checklist.md b/bundle/skills/scaffolding-repo-documentation/references/docs-checklist.md new file mode 100644 index 0000000..6290f13 --- /dev/null +++ b/bundle/skills/scaffolding-repo-documentation/references/docs-checklist.md @@ -0,0 +1,38 @@ +# Documentation Checklist + +Canonical list of repository documentation files. Used by the `scaffolding-repo-documentation` skill to determine what to check and create. + +## High Priority + +| File | Path | Auto-Create | Notes | +|------|------|-------------|-------| +| README.md | `README.md` | Yes (via `writing-readme` skill) | Delegates to specialist skill | +| LICENSE | `LICENSE` | **No** | Legal decision; flag only | +| CONTRIBUTING.md | `CONTRIBUTING.md` | Yes | Tailored to detected language | +| SECURITY.md | `SECURITY.md` | Yes (via `writing-security-policy` skill) | Delegates to specialist skill | + +## Medium Priority + +| File | Path | Auto-Create | Notes | +|------|------|-------------|-------| +| CODE_OF_CONDUCT.md | `CODE_OF_CONDUCT.md` | Yes | Contributor Covenant v2.1 | +| CHANGELOG.md | `CHANGELOG.md` | Yes | Keep a Changelog skeleton | +| CODEOWNERS | `CODEOWNERS` or `.github/CODEOWNERS` | Yes | Placeholder teams | +| PR Template | `.github/PULL_REQUEST_TEMPLATE.md` | Yes | Structured checklist | + +## Low Priority + +| File | Path | Auto-Create | Notes | +|------|------|-------------|-------| +| Bug Report Template | `.github/ISSUE_TEMPLATE/bug_report.md` | Yes | With YAML frontmatter | +| Feature Request Template | `.github/ISSUE_TEMPLATE/feature_request.md` | Yes | With YAML frontmatter | +| Funding Config | `.github/FUNDING.yml` | **No** | Requires account-specific info | + +## Detection Rules + +When checking for existence, also check common variants: + +- `README.md`, `readme.md`, `README.rst`, `README.txt` +- `LICENSE`, `LICENSE.md`, `LICENSE.txt`, `LICENCE` +- `CHANGELOG.md`, `CHANGES.md`, `HISTORY.md` +- `CODEOWNERS`, `.github/CODEOWNERS`, `docs/CODEOWNERS` diff --git a/bundle/skills/scaffolding-repo-documentation/references/github-community-profile.md b/bundle/skills/scaffolding-repo-documentation/references/github-community-profile.md new file mode 100644 index 0000000..b3ec826 --- /dev/null +++ b/bundle/skills/scaffolding-repo-documentation/references/github-community-profile.md @@ -0,0 +1,35 @@ +# GitHub Community Health Profile + +Reference for GitHub's recommended community health files. Used by the `scaffolding-repo-documentation` skill to align with GitHub's standards. + +## What GitHub Checks + +GitHub's community profile (visible at `https://github.com///community`) evaluates: + +| File | Required for Profile | Notes | +|------|---------------------|-------| +| README.md | Yes | Must be non-empty | +| CODE_OF_CONDUCT.md | Yes | Can also be in `.github/` or `docs/` | +| CONTRIBUTING.md | Yes | Can also be in `.github/` or `docs/` | +| LICENSE | Yes | Detected by licensee gem | +| SECURITY.md | Yes | Can also be in `.github/` or `docs/` | +| .github/ISSUE_TEMPLATE/ | Yes | Or `.github/ISSUE_TEMPLATE.md` | +| .github/PULL_REQUEST_TEMPLATE.md | Yes | Or in `docs/` | + +## Organization Defaults + +Organizations can place default community health files in a `.github` repository. These apply to all repos that lack their own copy. When scaffolding, note this possibility so teams know they may not need per-repo copies. + +## File Location Precedence + +GitHub looks for community health files in this order: + +1. Repository root +2. `.github/` directory +3. `docs/` directory + +Prefer the repository root for discoverability, except: + +- Issue templates → `.github/ISSUE_TEMPLATE/` (required location) +- PR template → `.github/PULL_REQUEST_TEMPLATE.md` (conventional location) +- CODEOWNERS → `.github/CODEOWNERS` or repository root (both work) diff --git a/bundle/skills/summarizing-changes/SKILL.md b/bundle/skills/summarizing-changes/SKILL.md deleted file mode 100644 index 6ade0c5..0000000 --- a/bundle/skills/summarizing-changes/SKILL.md +++ /dev/null @@ -1,153 +0,0 @@ ---- -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/writing-commit-messages/SKILL.md b/bundle/skills/writing-commit-messages/SKILL.md deleted file mode 100644 index 54d386b..0000000 --- a/bundle/skills/writing-commit-messages/SKILL.md +++ /dev/null @@ -1,173 +0,0 @@ ---- -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/bundle/skills/writing-readme/SKILL.md b/bundle/skills/writing-readme/SKILL.md new file mode 100644 index 0000000..fda6a40 --- /dev/null +++ b/bundle/skills/writing-readme/SKILL.md @@ -0,0 +1,68 @@ +--- +name: writing-readme +version: 1.0.0 +user-invocable: true +description: Generate or improve a README.md tailored to the repository's language, framework, and purpose. Use when creating a new README, improving an existing one, or a repo lacks onboarding documentation. Triggered by: write readme, generate readme, improve readme, missing readme, onboarding docs. +argument-hint: "[path/to/repo] [--improve]" +allowed-tools: + - Bash + - Read + - Write + - Glob + - Grep +--- + +# Write README + +Generate a comprehensive README.md or improve an existing one using repository context. + +## Input + +**Arguments:** `$ARGUMENTS` + +- **Path** — Repository root. Defaults to current working directory. +- **`--improve`** — Improve an existing README rather than replacing it. +- Without `--improve`, if README.md already exists, warn before overwriting. + +## Phase 1 — Analyze the Repository + +Read the repository to gather information for each section: + +1. **Project identity** — Name, description, version from manifest files. +2. **Build and test tooling** — Makefile, CI workflows, Dockerfile, etc. +3. **Package manager** — Detect from lockfiles (package-lock.json, uv.lock, Cargo.lock, etc.). +4. **Configuration** — `.env.example`, linter configs, editor settings. + +If an existing README is present and `--improve` was passed, read it to identify which sections exist and which are thin or missing. + +## Phase 2 — Generate the README + +Use the template in `templates/README.template.md` (bundled alongside this skill) as a structural guide. Fill each section with real content from the repository analysis. + +### Section Guidance + +- **Title and Description** — Use the project name from the manifest. Write a 1–2 sentence description. +- **Badges** — Only include badges for tools actually present (CI, license, package version). +- **Installation** — Provide the exact install command for the detected package manager. +- **Usage** — Extract from scripts in the manifest or Makefile targets. Show at least one concrete example. +- **Configuration** — Include if `.env.example` or config files exist. +- **Development** — Setup steps, test command, lint command. +- **Contributing** — Link to `CONTRIBUTING.md` if it exists. +- **License** — Reference the LICENSE file and state the license type. + +## Phase 3 — Write or Improve + +**New README:** Write the complete file. + +**Improve mode (`--improve`):** +- Identify missing sections by comparing against the template. +- Append or insert missing sections in the correct order. +- Expand thin sections (< 2 sentences) with detected context. +- Do not remove or rewrite existing adequate sections. + +## Quality Standards + +- **Be concrete** — Every install command and path should reflect the actual repository. +- **Do not fabricate** — Use `` for anything you cannot determine. +- **Keep it scannable** — Use headings, code blocks, and lists. +- **No placeholders** — Use real project names when available. diff --git a/bundle/skills/writing-readme/templates/README.template.md b/bundle/skills/writing-readme/templates/README.template.md new file mode 100644 index 0000000..693ff8d --- /dev/null +++ b/bundle/skills/writing-readme/templates/README.template.md @@ -0,0 +1,86 @@ +# {project_name} + + + + + +{description} + +## Table of Contents + + + +- [Installation](#installation) +- [Usage](#usage) +- [Configuration](#configuration) +- [Development](#development) +- [Contributing](#contributing) +- [License](#license) + +## Installation + +### Prerequisites + +- {runtime} {version} or later +- {package_manager} + +### Install + +```bash +{install_command} +``` + +## Usage + +```bash +{usage_command} +``` + + + + +## Configuration + + + +```bash +cp .env.example .env +``` + +| Variable | Description | Default | +|----------|-------------|---------| +| `{VAR_NAME}` | {description} | `{default}` | + +## Development + +### Setup + +```bash +git clone {repo_url} +cd {project_name} +{install_command} +``` + +### Running Tests + +```bash +{test_command} +``` + +### Linting + +```bash +{lint_command} +``` + +## Contributing + +Contributions are welcome. Please see [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines. + +## License + +This project is licensed under the {license} License. See [LICENSE](LICENSE) for details. diff --git a/bundle/skills/writing-security-policy/SKILL.md b/bundle/skills/writing-security-policy/SKILL.md new file mode 100644 index 0000000..3f4adef --- /dev/null +++ b/bundle/skills/writing-security-policy/SKILL.md @@ -0,0 +1,56 @@ +--- +name: writing-security-policy +version: 1.0.0 +user-invocable: true +description: Generate a SECURITY.md with supported versions, vulnerability reporting instructions, and disclosure policy. Use when a repository needs a security policy, vulnerability reporting process, or disclosure guidelines. Triggered by: write security policy, generate security.md, vulnerability reporting, security disclosure, missing security policy. +argument-hint: "[path/to/repo]" +allowed-tools: + - Bash + - Read + - Write + - Glob + - Grep +--- + +# Write Security Policy + +Generate a comprehensive SECURITY.md tailored to the repository's release model and ecosystem. + +## Input + +**Arguments:** `$ARGUMENTS` + +- If a path is provided, write SECURITY.md into that directory. +- If empty, use the current working directory. +- If SECURITY.md already exists, warn and stop. Do not overwrite security policies. + +## Phase 1 — Gather Context + +1. **Existing security files** — Check for `SECURITY*`, `.github/SECURITY*`. +2. **Versioning strategy** — Inspect git tags (if available) and package manifest versions. +3. **Language ecosystem** — Determines which advisory databases to reference. +4. **Security tooling** — Note any CodeQL, Snyk, or Trivy configurations. + +If the directory is not a git repo, skip tag-based version detection and use the manifest version. + +## Phase 2 — Generate SECURITY.md + +Use the template in `templates/SECURITY.template.md` (bundled alongside this skill) as a structural guide. + +### Section Guidance + +- **Supported Versions** — Build a table from recent releases. Current major gets full support; previous major gets security fixes only. +- **Reporting a Vulnerability** — Private channel only (GitHub private reporting or email). Never suggest public issues. Include TODO placeholder for email. +- **Disclosure Policy** — Default to coordinated disclosure with 90-day timeline. +- **Security Contacts** — TODO placeholders for names and emails. + +## Phase 3 — Write the File + +Write `SECURITY.md` to the repository root. Do not write if the file already exists. + +## Safety Rules + +- **NEVER overwrite** an existing SECURITY.md. +- **NEVER include real email addresses** unless extracted from an existing file. Use TODO placeholders. +- **NEVER recommend public issue trackers** for vulnerability reporting. +- **ALWAYS include a response timeline.** diff --git a/bundle/skills/writing-security-policy/templates/SECURITY.template.md b/bundle/skills/writing-security-policy/templates/SECURITY.template.md new file mode 100644 index 0000000..ecfa9ee --- /dev/null +++ b/bundle/skills/writing-security-policy/templates/SECURITY.template.md @@ -0,0 +1,60 @@ +# Security Policy + +## Supported Versions + + + +| Version | Supported | +|---------|-----------| +| {current_major}.x | :white_check_mark: Active support | +| {previous_major}.x | :white_check_mark: Security fixes only | +| < {previous_major} | :x: End of life | + +## Reporting a Vulnerability + +**Please do not report security vulnerabilities through public GitHub issues.** + +Instead, please report them via one of these channels: + +1. **GitHub Private Vulnerability Reporting** (preferred): + Navigate to the **Security** tab of this repository and click **"Report a vulnerability"**. + +2. **Email:** + Send a detailed report to **[SECURITY-EMAIL]** + + +### What to Include + +- A description of the vulnerability and its potential impact +- Step-by-step instructions to reproduce the issue +- Affected version(s) +- Any potential mitigations you have identified +- Whether you would like to be credited in the advisory + +### What to Expect + +| Step | Timeline | +|------|----------| +| Acknowledgment of your report | Within **48 hours** | +| Initial assessment and severity rating | Within **1 week** | +| Fix development and testing | Within **30 days** for critical issues | +| Coordinated public disclosure | Within **90 days** of initial report | + +## Disclosure Policy + +We follow a **coordinated disclosure** model: + +1. Reporter submits vulnerability details through a private channel. +2. We acknowledge receipt and begin investigation. +3. We develop and test a fix on a private branch. +4. We release the fix and publish a security advisory simultaneously. +5. We credit the reporter (unless they request anonymity). + +## Security Contacts + + + +| Role | Contact | +|------|---------| +| Primary security contact | [SECURITY-CONTACT-NAME] <[SECURITY-EMAIL]> | +| Backup contact | [BACKUP-CONTACT-NAME] <[BACKUP-EMAIL]> | diff --git a/examples/README.md b/examples/README.md index 734632f..ffcd46b 100644 --- a/examples/README.md +++ b/examples/README.md @@ -1,43 +1,45 @@ -# 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. +# Examples + +SDK usage examples demonstrating the full Musher lifecycle: author → publish → consume with Claude Agent SDK. + +See [Python README](python/README.md) and [TypeScript README](typescript/README.md) for language-specific setup. + +## Structure + +``` +examples/ +├── python/ +│ ├── repo_docs_audit.py Pull, install, and run Agent SDK in one script +│ ├── fixtures/under_documented_repo/ Intentionally under-documented fixture project +│ └── requirements.txt +└── typescript/ + ├── 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 Agent SDK demo (stub — see Python) +``` + +## Quick Start + +**Python** +```bash +cd python +pip install -r requirements.txt +export MUSHER_API_KEY="mush_..." +export ANTHROPIC_API_KEY="sk-ant-..." +python repo_docs_audit.py +``` + +**TypeScript** +```bash +cd typescript +npm install +export MUSHER_API_KEY="mush_..." +npm run pull-bundle +npm run install-skills +``` + +## What These Examples Show + +1. **Pull a bundle** — Fetch the bundle from the registry and inspect its multi-file skills and agent specs +2. **Install skills** — Copy skills (SKILL.md + companion files) into `.claude/skills/` for SDK discovery +3. **Run with Agent SDK** — Claude discovers and invokes installed skills, constrained by hooks, producing structured JSON output diff --git a/examples/python/01_pull_bundle.py b/examples/python/01_pull_bundle.py deleted file mode 100644 index 60b8eea..0000000 --- a/examples/python/01_pull_bundle.py +++ /dev/null @@ -1,50 +0,0 @@ -"""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 deleted file mode 100644 index 74edc6e..0000000 --- a/examples/python/02_install_skills.py +++ /dev/null @@ -1,32 +0,0 @@ -"""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 deleted file mode 100644 index 2e0d126..0000000 --- a/examples/python/03_use_with_claude.py +++ /dev/null @@ -1,82 +0,0 @@ -"""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 7737357..05ec75a 100644 --- a/examples/python/README.md +++ b/examples/python/README.md @@ -1,39 +1,55 @@ -# 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 -``` +# Python Example + +This example shows how to use the [Musher Python SDK](https://github.com/musher-dev/python-sdk) and the Claude Agent SDK to consume a Musher bundle end-to-end. + +## Prerequisites + +- Python 3.13+ + +```bash +pip install -r requirements.txt +``` + +### 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** | Bundle pull & install | +| `ANTHROPIC_API_KEY` | Create at [console.anthropic.com](https://console.anthropic.com/) under **API Keys** | Agent SDK run | + +```bash +export MUSHER_API_KEY="mush_..." +export ANTHROPIC_API_KEY="sk-ant-..." +``` + +### Bundle Source + +By default the script pulls `musher-examples/repo-documentation-governance:1.0.0`. To use your own published bundle: + +```bash +export MUSHER_BUNDLE_REF="your-namespace/repo-documentation-governance:1.0.0" +``` + +## Running + +```bash +python repo_docs_audit.py +``` + +The script does everything in one run: + +1. **Pulls** the bundle from the Musher registry and prints its multi-file skills +2. **Installs** skills (SKILL.md + rubrics, templates, checklists) into `.claude/skills/` +3. **Runs** the Claude Agent SDK — it discovers the installed skills, audits a fixture repo, scaffolds missing docs, and returns a structured JSON report + +## Output + +- `out/repo_docs_report.json` — Structured report (score, gaps, files created, recommendations) +- `out/tool_calls.jsonl` — Log of every tool call the agent made + +## What to Look For + +- Skills are loaded from `.claude/skills/` — including companion files (rubrics, templates) +- Claude invokes them autonomously through the `Skill` tool +- Hooks constrain where the agent can write (fixture repo only) +- The final result is JSON you could feed into CI, a dashboard, or another service diff --git a/examples/python/fixtures/under_documented_repo/.github/workflows/ci.yml b/examples/python/fixtures/under_documented_repo/.github/workflows/ci.yml new file mode 100644 index 0000000..1cda7ed --- /dev/null +++ b/examples/python/fixtures/under_documented_repo/.github/workflows/ci.yml @@ -0,0 +1,18 @@ +name: CI + +on: + push: + branches: [main] + pull_request: + branches: [main] + +jobs: + test: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-python@v5 + with: + python-version: "3.12" + - run: pip install -e ".[test]" + - run: pytest diff --git a/examples/python/fixtures/under_documented_repo/LICENSE b/examples/python/fixtures/under_documented_repo/LICENSE new file mode 100644 index 0000000..4ad2946 --- /dev/null +++ b/examples/python/fixtures/under_documented_repo/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2025 Example Corp + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/examples/python/fixtures/under_documented_repo/README.md b/examples/python/fixtures/under_documented_repo/README.md new file mode 100644 index 0000000..476744e --- /dev/null +++ b/examples/python/fixtures/under_documented_repo/README.md @@ -0,0 +1,3 @@ +# weather-api + +A weather API service. diff --git a/examples/python/fixtures/under_documented_repo/pyproject.toml b/examples/python/fixtures/under_documented_repo/pyproject.toml new file mode 100644 index 0000000..c081808 --- /dev/null +++ b/examples/python/fixtures/under_documented_repo/pyproject.toml @@ -0,0 +1,15 @@ +[project] +name = "weather-api" +version = "0.2.1" +description = "A simple weather API service" +requires-python = ">=3.11" +license = "MIT" +dependencies = [ + "fastapi>=0.110", + "httpx>=0.27", + "uvicorn>=0.29", +] + +[build-system] +requires = ["hatchling"] +build-backend = "hatchling.build" diff --git a/examples/python/fixtures/under_documented_repo/src/app.py b/examples/python/fixtures/under_documented_repo/src/app.py new file mode 100644 index 0000000..8d0d59c --- /dev/null +++ b/examples/python/fixtures/under_documented_repo/src/app.py @@ -0,0 +1,22 @@ +from fastapi import FastAPI, HTTPException + +app = FastAPI() + +MOCK_DATA = { + "london": {"temp_c": 12, "condition": "Cloudy"}, + "tokyo": {"temp_c": 22, "condition": "Sunny"}, + "new-york": {"temp_c": 18, "condition": "Partly cloudy"}, +} + + +@app.get("/health") +def health(): + return {"status": "ok"} + + +@app.get("/weather/{city}") +def get_weather(city: str): + key = city.lower().strip() + if key not in MOCK_DATA: + raise HTTPException(status_code=404, detail=f"City '{city}' not found") + return {"city": city, **MOCK_DATA[key]} diff --git a/examples/python/repo_docs_audit.py b/examples/python/repo_docs_audit.py new file mode 100644 index 0000000..961ee2b --- /dev/null +++ b/examples/python/repo_docs_audit.py @@ -0,0 +1,280 @@ +"""Audit and scaffold repository documentation using a Musher bundle +and the Claude Agent SDK. + +This script demonstrates the full Musher consumption flow: + +1. Pull a versioned bundle from the Musher registry +2. Inspect its multi-file skills and agent specs +3. Install skills into .claude/skills/ for SDK discovery +4. Run the Claude Agent SDK — it discovers skills, applies hooks, + and returns structured JSON output + +Prerequisites: + pip install -r requirements.txt + export MUSHER_API_KEY="mush_..." + export ANTHROPIC_API_KEY="sk-ant-..." + +To use your own published bundle: + export MUSHER_BUNDLE_REF="your-namespace/repo-documentation-governance:1.0.0" +""" + +from __future__ import annotations + +import asyncio +import json +import os +from pathlib import Path +from typing import Any + +import musher +from claude_agent_sdk import ( + AssistantMessage, + ClaudeAgentOptions, + HookMatcher, + ResultMessage, + query, +) + +THIS_DIR = Path(__file__).resolve().parent +SKILLS_DIR = THIS_DIR / ".claude" / "skills" +FIXTURE_DIR = THIS_DIR / "fixtures" / "under_documented_repo" +OUTPUT_DIR = THIS_DIR / "out" +TOOL_LOG = OUTPUT_DIR / "tool_calls.jsonl" + +BUNDLE_REF = os.environ.get( + "MUSHER_BUNDLE_REF", "musher-examples/repo-documentation-governance:1.0.0" +) + +SKILL_NAMES = [ + "auditing-repo-documentation", + "scaffolding-repo-documentation", + "writing-readme", + "writing-security-policy", +] + +REPORT_SCHEMA: dict[str, Any] = { + "type": "object", + "properties": { + "overall_score": {"type": "number"}, + "missing_files_before": {"type": "array", "items": {"type": "string"}}, + "files_created": {"type": "array", "items": {"type": "string"}}, + "high_priority_gaps": {"type": "array", "items": {"type": "string"}}, + "follow_up_recommendations": { + "type": "array", + "items": {"type": "string"}, + }, + }, + "required": [ + "overall_score", + "missing_files_before", + "files_created", + "high_priority_gaps", + "follow_up_recommendations", + ], +} + + +# ── Hooks ──────────────────────────────────────────────────────────────────── + + +def _safe_resolve(path_str: str) -> Path: + try: + return Path(path_str).expanduser().resolve() + except Exception: + return Path(path_str) + + +async def guard_fixture_writes( + input_data: dict[str, Any], + tool_use_id: str, + context: Any, +) -> dict[str, Any]: + """Deny writes outside the fixture repo and protect .env files.""" + if input_data.get("hook_event_name") != "PreToolUse": + return {} + + tool_name = input_data.get("tool_name", "") + if tool_name not in {"Write", "Edit"}: + return {} + + tool_input = input_data.get("tool_input", {}) + file_path = tool_input.get("file_path", "") + target = _safe_resolve(file_path) + fixture_root = FIXTURE_DIR.resolve() + + if target.name == ".env": + return { + "hookSpecificOutput": { + "hookEventName": input_data["hook_event_name"], + "permissionDecision": "deny", + "permissionDecisionReason": "Editing .env files is blocked.", + } + } + + if not str(target).startswith(str(fixture_root)): + return { + "systemMessage": "Only modify files inside the fixture repository.", + "hookSpecificOutput": { + "hookEventName": input_data["hook_event_name"], + "permissionDecision": "deny", + "permissionDecisionReason": "Writes are only allowed inside the fixture repository.", + }, + } + + return {} + + +async def log_pretool( + input_data: dict[str, Any], + tool_use_id: str, + context: Any, +) -> dict[str, Any]: + """Append tool-call events to a local JSONL log.""" + OUTPUT_DIR.mkdir(parents=True, exist_ok=True) + record = { + "tool_use_id": tool_use_id, + "hook_event_name": input_data.get("hook_event_name"), + "tool_name": input_data.get("tool_name"), + "tool_input": input_data.get("tool_input"), + } + with TOOL_LOG.open("a", encoding="utf-8") as f: + f.write(json.dumps(record) + "\n") + return {} + + +# ── Step 1: Pull & Inspect ────────────────────────────────────────────────── + + +def pull_and_inspect() -> musher.Bundle: + """Pull the bundle from the registry and print its contents.""" + print(f"Pulling bundle: {BUNDLE_REF}") + bundle = musher.pull(BUNDLE_REF) + print(f" Version: {bundle.version}") + print(f" Files: {len(list(bundle.files()))}") + print() + + print("Skills:") + for skill in bundle.skills(): + files = skill.files() + print(f" {skill.name} ({len(files)} file(s))") + for fh in files: + print(f" {fh.logical_path}") + print() + + print("Agent specs:") + for agent in bundle.agent_specs(): + print(f" {agent.name}") + print() + + return bundle + + +# ── Step 2: Install Skills ────────────────────────────────────────────────── + + +def install_skills(bundle: musher.Bundle) -> None: + """Install multi-file skills into .claude/skills/ for SDK discovery.""" + bundle.install_claude_skills( + SKILLS_DIR, + skills=SKILL_NAMES, + clean=True, + ) + + print(f"Installed skills to {SKILLS_DIR}/") + for name in SKILL_NAMES: + skill_path = SKILLS_DIR / name + if skill_path.exists(): + installed = [f for f in skill_path.rglob("*") if f.is_file()] + print(f" ✓ {name} ({len(installed)} file(s))") + for f in installed: + print(f" {f.relative_to(skill_path)}") + else: + print(f" ✗ {name} (not found)") + print() + + +# ── Step 3: Run Agent SDK ─────────────────────────────────────────────────── + + +async def run_agent() -> None: + """Run the Claude Agent SDK against the fixture repo.""" + if not FIXTURE_DIR.exists(): + raise SystemExit(f"Fixture repo not found: {FIXTURE_DIR}") + + OUTPUT_DIR.mkdir(parents=True, exist_ok=True) + + prompt = f""" +Audit the repository at {FIXTURE_DIR} for missing community-health and onboarding +documentation. + +Use installed Skills when relevant. + +Constraints: +- Only inspect and modify files inside {FIXTURE_DIR} +- Do not touch secrets or .env files +- Focus on repository documentation and GitHub community-health files +- Create missing files when useful +- Return the final answer as structured JSON that matches the required schema +""" + + options = ClaudeAgentOptions( + cwd=str(THIS_DIR), + setting_sources=["project"], + allowed_tools=["Skill", "Read", "Write", "Edit", "Glob", "Grep"], + permission_mode="dontAsk", + hooks={ + "PreToolUse": [ + HookMatcher(matcher="Write|Edit", hooks=[guard_fixture_writes]), + HookMatcher(hooks=[log_pretool]), + ] + }, + output_format={"type": "json_schema", "schema": REPORT_SCHEMA}, + ) + + report_path = OUTPUT_DIR / "repo_docs_report.json" + + print("Running Claude Agent SDK...") + print(f" Skills source: {SKILLS_DIR}") + print(f" Target repo: {FIXTURE_DIR}") + print() + + async for message in query(prompt=prompt, options=options): + if isinstance(message, AssistantMessage): + for block in message.content: + if hasattr(block, "text") and block.text: + print(block.text) + elif hasattr(block, "name"): + print(f"[tool] {block.name}") + + elif isinstance(message, ResultMessage): + if message.subtype == "success" and message.result: + try: + report = json.loads(message.result) + except (json.JSONDecodeError, TypeError): + report = {"raw_result": message.result} + + report_path.write_text( + json.dumps(report, indent=2), + encoding="utf-8", + ) + print("\n── Structured Report ────────────────────────────") + print(json.dumps(report, indent=2)) + print(f"\nSaved to: {report_path}") + print(f"Tool log: {TOOL_LOG}") + else: + print(f"Run finished with subtype={message.subtype}") + if message.result: + print(message.result) + + +# ── Main ──────────────────────────────────────────────────────────────────── + + +async def main() -> None: + bundle = pull_and_inspect() + install_skills(bundle) + await run_agent() + + +if __name__ == "__main__": + asyncio.run(main()) diff --git a/examples/python/requirements.txt b/examples/python/requirements.txt index 0becbf5..5239ea5 100644 --- a/examples/python/requirements.txt +++ b/examples/python/requirements.txt @@ -1,2 +1,2 @@ -musher-sdk>=0.3.5 -anthropic>=0.25.0 +musher-sdk>=0.3.5 +claude-agent-sdk>=0.1.0 diff --git a/examples/typescript/01-pull-bundle.ts b/examples/typescript/01-pull-bundle.ts index f2ddbf7..7a9dd46 100644 --- a/examples/typescript/01-pull-bundle.ts +++ b/examples/typescript/01-pull-bundle.ts @@ -1,49 +1,55 @@ -/** - * 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("..."); -} +/** + * Pull the repo-documentation-governance bundle and inspect its contents. + * + * Prerequisites: + * export MUSHER_API_KEY="mush_..." + * + * To use your own published bundle, set: + * export MUSHER_BUNDLE_REF="your-namespace/repo-documentation-governance:1.0.0" + * + * Run: + * npx tsx 01-pull-bundle.ts + */ + +import { pull } from "@musher-dev/musher-sdk"; + +const bundleRef = + process.env.MUSHER_BUNDLE_REF ?? "musher-examples/repo-documentation-governance:1.0.0"; + +const bundle = await pull(bundleRef); + +console.log(`Bundle: ${bundle.ref.toBaseRef()} v${bundle.version}`); +console.log(); + +// List all files — notice multi-file skills ship more than just SKILL.md +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("auditing-repo-documentation"); +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 index 5b3e4f9..c31dee5 100644 --- a/examples/typescript/02-install-skills.ts +++ b/examples/typescript/02-install-skills.ts @@ -1,31 +1,37 @@ /** - * Install skills from the demo-starter bundle into a Claude Code project. + * Install skills from the repo-documentation-governance 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. + * This copies multi-file skills (SKILL.md + companion files like rubrics, + * templates, and checklists) into .claude/skills/ where the Claude Agent SDK + * discovers them. * * Prerequisites: * export MUSHER_API_KEY="mush_..." * + * To use your own published bundle, set: + * export MUSHER_BUNDLE_REF="your-namespace/repo-documentation-governance:1.0.0" + * * 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"); +const bundleRef = + process.env.MUSHER_BUNDLE_REF ?? "musher-examples/repo-documentation-governance:1.0.0"; + +const bundle = await pull(bundleRef); // 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/`); +console.log(`Installed ${written.length} file(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'"); +console.log("Skills are now available to the Claude Agent SDK."); +console.log("See examples/python/repo_docs_audit.py for the Agent SDK demo."); diff --git a/examples/typescript/03-use-with-claude.ts b/examples/typescript/03-use-with-claude.ts index 0750f6e..69e4abd 100644 --- a/examples/typescript/03-use-with-claude.ts +++ b/examples/typescript/03-use-with-claude.ts @@ -1,76 +1,17 @@ -/** - * 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 : "", -); +/** + * Agent SDK demo — coming soon. + * + * The Python example (../python/repo_docs_audit.py) demonstrates + * the full Claude Agent SDK integration including: + * - Skill discovery from .claude/skills/ + * - Hooks and permission guards + * - Structured JSON output + * - Tool-call logging + * + * A TypeScript equivalent will be added when the Claude Agent SDK ships + * a Node.js runtime. + */ + +console.log( + "See examples/python/repo_docs_audit.py for the Agent SDK demo.", +); diff --git a/examples/typescript/README.md b/examples/typescript/README.md index b2e4da5..6b36e85 100644 --- a/examples/typescript/README.md +++ b/examples/typescript/README.md @@ -1,37 +1,42 @@ -# 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 -``` +# TypeScript Examples + +These examples show how to use the [Musher TypeScript SDK](https://github.com/musher-dev/typescript-sdk) with the `repo-documentation-governance` 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 | + +```bash +export MUSHER_API_KEY="mush_..." +``` + +To use your own published bundle: + +```bash +export MUSHER_BUNDLE_REF="your-namespace/repo-documentation-governance:1.0.0" +``` + +## Examples + +| File | What it demonstrates | +|------|----------------------| +| `01-pull-bundle.ts` | Pull a bundle and inspect its multi-file skills, agents, and companion files | +| `02-install-skills.ts` | Install skills (SKILL.md + companion files) into `.claude/skills/` | +| `03-use-with-claude.ts` | Agent SDK demo (stub — see Python example for the full demo) | + +## Running + +```bash +npm run pull-bundle +npm run install-skills +``` + +The Agent SDK demo is available in the Python examples at `../python/repo_docs_audit.py`. diff --git a/examples/typescript/package.json b/examples/typescript/package.json index 22cf3ff..780e2d0 100644 --- a/examples/typescript/package.json +++ b/examples/typescript/package.json @@ -1,19 +1,18 @@ -{ - "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" - } -} +{ + "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", + "agent-sdk-demo": "tsx 03-use-with-claude.ts" + }, + "dependencies": { + "@musher-dev/musher-sdk": "^0.4.0" + }, + "devDependencies": { + "tsx": "^4.0.0", + "typescript": "^5.0.0" + } +}