Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion docs/CATALOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ plugin manifests and kept in sync by CI — never hand-edit it; the category voc
- [`actionlint`](../plugins/actionlint) — Lint GitHub Actions workflow files on edit via actionlint, surfacing findings as advisory context.
- [`source-control`](../plugins/source-control) — Git and GitHub delivery workflow: /commit (Conventional Commits + Co-authored-by trailer via safe heredoc mechanics), /pull-request (prep, create, CI monitoring, review-comment triage, merge, CI-log fetch), /babysit-prs (self-pacing fleet loop — safe by default; opt-in worker/autopilot tiers add gate-checked merge and thread resolution behind a deterministic Python engine), /babysit-loop (the loop-lane merge lane: a standing or drain loop that invokes babysit-prs per cycle, configured through repo-scoped babysit_loop_* keys on the layered source-control.md seam, with merge authority human-only until the target repo's tracked config adopts the lane, a gate-proven C2-mechanical baseline once adopted, and standing merge-rung raises binding from the team-tracked layer only — with one named exception, where an invocation line explicitly typing both the autopilot tier keyword and the dedicated raise argument --merge c3-this-run widens that single invocation's merge authority up to C3 behind a fresh independent frontier-tier resolver, while C4-structural and C5-untrusted-provenance stay unconditionally human-merge), /worktree (create, status, cleanup, audit for parallel-session isolation), /setup (check the effective commit-subject / PR-title convention merged across its config layers and the babysit-prs config, or apply — interview the repo and write the convention config to a chosen layer), and /resolve-conflicts (intent-first merge/rebase conflict resolution with a semantic-conflict sweep — never --abort). The commit-subject / PR-title convention is configurable via a source-control.md config written by a re-runnable setup skill, layered across a ~/.claude user-global file, the tracked team file, and a gitignored .claude/source-control.local.md personal overlay merged per key; Conventional Commits is the default when no convention is declared.
- [`implementation`](../plugins/implementation) — Disciplined implementation stage: execute approved plans inline (`/implementation:implement`) or via orchestrated worker subagents (`/implementation:implement-dispatch`) with incremental validation, TDD-by-default cadence, green-checkpoint commits, scope-fence drift detection, and divergence detection that routes back to planning. Build/test/lint, testing, and outcome verification live in the companion `toolchain`, `testing`, and `verification` plugins, invoked when installed.
- [`toolchain`](../plugins/toolchain) — Repo-agnostic polyglot verification toolchain: build + test + lint for changed files across .NET, Python, TypeScript, Bash, PowerShell, Markdown, Go, YAML, and cross-cutting surfaces (`/toolchain:check`, `/toolchain:lint`), plus a re-runnable `/toolchain:setup` with check (report the configured ecosystems and their command surface) and apply (interview, infer, and write the tracked per-ecosystem command config those skills resolve first).
- [`toolchain`](../plugins/toolchain) — Repo-agnostic polyglot verification toolchain: build + test + lint for changed files across .NET, Python, TypeScript, Bash, PowerShell, Markdown, Go, YAML, and cross-cutting surfaces (`/toolchain:check`, `/toolchain:lint` with format-only `--fix` and gated `--code-fix`), plus a re-runnable `/toolchain:setup` with check (report the configured ecosystems and their command surface) and apply (interview, infer, and write the tracked per-ecosystem command config those skills resolve first).
- [`wizard`](../plugins/wizard) — Generate an interactive bash wizard that walks a human, step by step, through the manual procedures an agent cannot perform — provisioning infrastructure or credentials, setting CI secrets, clicking through third-party dashboards, one-off migrations and cutovers. One skill, generate (/wizard:generate): the agent scopes the stages from the repo (reading key NAMES only from a live .env, never values), authors them onto a fixed hardened library (TTY-only fail-closed prompts, https-only URL opening, hidden secret entry, single-quoted 0600 .env upserts with a gitignore check, repo-confirmed gh secret/variable writes over stdin, names-only summary), prints the full STAGES block for explicit human approval BEFORE the script is made executable, and never runs the wizard itself — the human does, in their own terminal. Ephemeral by default: built for one run, committed only when the setup path should live in the repo. The generated script requires bash (Windows: Git Bash or WSL); gh is optional — CI-secret stages degrade to a visible warning plus a closing-summary entry when it is absent.

## Testing
Expand Down
9 changes: 9 additions & 0 deletions docs/conventions/ecosystem-commands/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,14 @@
# Changelog — ecosystem-commands convention

## 1.3.0 — 2026-08-15

Additive schema change: new optional `code-fix-cmd` key for semantic/code-changing autofixes
(ruff `check --fix`, golangci-lint `--fix`, biome `check --write`, …). Clarifies that `fix-cmd`
is **format-only** (whitespace / import layout / style). `/toolchain:lint --fix` runs `fix-cmd`;
`--code-fix` runs `code-fix-cmd` behind that skill's confirmation / `--yes` gate. Null when
absent — tolerant readers ignore the unknown key until they upgrade. Closes
melodic-software/claude-code-plugins#2649.

## 1.2.3 — 2026-07-26

Docs-only, no schema shape change: the task-runner deferral's "Revisit triggers" label becomes
Expand Down
4 changes: 4 additions & 0 deletions docs/conventions/ecosystem-commands/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,10 @@ documented placeholders:
| `<project-dir>` | Each per-project root discovered via `project-discovery` |
| `$REPO_ROOT` | Absolute repo root (`git rev-parse --show-toplevel`) |

`fix-cmd` is **format-only** (whitespace / import layout / style). Semantic/code-changing
autofixes belong in optional `code-fix-cmd` and are invoked only by `/toolchain:lint --code-fix`
behind that skill's confirmation / `--yes` gate — never by bare `--fix`.

Consumers are tolerant readers: unknown keys are inert, missing optional keys fall back to defaults.
Consuming repos SHOULD validate their files against the schema in their own gates (a
`check-jsonschema` hook or CI lane); plugins SHOULD fail soft — a malformed file degrades to rung 2
Expand Down
6 changes: 5 additions & 1 deletion docs/conventions/ecosystem-commands/ecosystem.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,11 @@
},
"fix-cmd": {
"type": ["string", "null"],
"description": "Auto-fix command. Null when the toolchain has no fix mode."
"description": "Format-only auto-fix command (whitespace, import layout, style). Must not apply semantic/code-changing lint autofixes. Null when the toolchain has no format-fix mode. Code-changing autofixes belong in code-fix-cmd."
},
"code-fix-cmd": {
"type": ["string", "null"],
"description": "Code-changing auto-fix command (lint autofixes that rewrite logic, delete unused imports, apply safe/unsafe code edits). Null when absent. Invoked only by /toolchain:lint --code-fix behind that skill's confirmation / --yes gate — never by bare --fix."
},
"opt-in": {
"type": "string",
Expand Down
3 changes: 2 additions & 1 deletion docs/conventions/ecosystem-commands/examples/go.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ project-discovery: ["go.mod"]
build-cmd: "go build ./..."
test-cmd: "go test ./..."
check-cmd: "golangci-lint run ./..."
fix-cmd: "golangci-lint run --fix ./..."
fix-cmd: "gofmt -w <files>" # format-only; substitute only *.go from <files> (never go.mod/go.sum)
code-fix-cmd: "golangci-lint run --fix <files>" # *.go only; one package directory per invoke
opt-in: ".golangci.yml, .golangci.yaml, .golangci.toml, or .golangci.json present (walked from the changed file up to the repo root) — otherwise golangci-lint applies its own unconfigured \"standard\" linter preset unconditionally"
install-hint: "Install golangci-lint: https://golangci-lint.run/docs/welcome/install/ | Go toolchain: https://go.dev/dl/ | Buf CLI (needed by the proto-gen-freshness gate below): https://buf.build/docs/cli/installation/"
gates:
Expand Down
3 changes: 2 additions & 1 deletion docs/conventions/ecosystem-commands/examples/python.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ project-discovery: ["pyproject.toml"]
build-cmd: null # no separate build step; lint/test cover verification
test-cmd: "uv run pytest -x -q"
check-cmd: "uv run ruff check . --no-fix && uv run ruff format . --check"
fix-cmd: "uv run ruff check . --fix && uv run ruff format ."
fix-cmd: "uv run ruff format <files>" # format-only; code-changing autofixes live in code-fix-cmd
code-fix-cmd: "uv run ruff check <files> --fix --no-unsafe-fixes --unfixable F401"
opt-in: "ruff config (ruff.toml, .ruff.toml, or pyproject.toml [tool.ruff])"
install-hint: "pip install ruff uv | brew install uv ruff | winget install astral-sh.uv"
4 changes: 2 additions & 2 deletions plugins/toolchain/.claude-plugin/plugin.json
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
{
"$schema": "https://json.schemastore.org/claude-code-plugin-manifest.json",
"name": "toolchain",
"version": "0.12.1",
"description": "Repo-agnostic polyglot verification toolchain: build + test + lint for changed files across .NET, Python, TypeScript, Bash, PowerShell, Markdown, Go, YAML, and cross-cutting surfaces (`/toolchain:check`, `/toolchain:lint`), plus a re-runnable `/toolchain:setup` with check (report the configured ecosystems and their command surface) and apply (interview, infer, and write the tracked per-ecosystem command config those skills resolve first).",
"version": "0.13.0",
"description": "Repo-agnostic polyglot verification toolchain: build + test + lint for changed files across .NET, Python, TypeScript, Bash, PowerShell, Markdown, Go, YAML, and cross-cutting surfaces (`/toolchain:check`, `/toolchain:lint` with format-only `--fix` and gated `--code-fix`), plus a re-runnable `/toolchain:setup` with check (report the configured ecosystems and their command surface) and apply (interview, infer, and write the tracked per-ecosystem command config those skills resolve first).",
"author": {
"name": "Melodic Software",
"email": "info@melodicsoftware.com"
Expand Down
25 changes: 25 additions & 0 deletions plugins/toolchain/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,31 @@
All notable changes to the `toolchain` plugin are documented here. Format follows
[Keep a Changelog](https://keepachangelog.com/en/1.1.0/); this plugin uses semantic versioning.

## [0.13.0]

### Added

- **`/toolchain:lint --code-fix` — gated semantic lint autofixes.** New optional ecosystem key
`code-fix-cmd` (ecosystem-commands contract 1.3.0) holds code-changing autofixes. The skill emits
a plan, then requires interactive confirmation or `--yes` (non-interactive without `--yes` stops
after the plan). `--dry-run` always stops after the plan. A default **file-cap of 40** scoped files
stops over-broad applies unless `--all-files` is passed. After an apply, surface `git diff --stat`.

### Changed

- **`--fix` is format-only.** Bundled portable defaults split format from code-fix:
- python: `fix-cmd` → `ruff format <files>`; `code-fix-cmd` → `ruff check <files> --fix
--no-unsafe-fixes --unfixable F401` (matches the `ruff-format` hook's F401 guard)
- go: `fix-cmd` → `gofmt -w <files>`; `code-fix-cmd` → `golangci-lint run --fix <files>`
Go format/code-fix substitute **only `*.go` paths** (never `go.mod`/`go.sum`) and invoke
`golangci-lint run --fix` once per package directory so multi-package changes do not hit
`named files must all be in one directory`.
- typescript: `fix-cmd` → `biome format --write <files>`; `code-fix-cmd` →
`biome check --write <files>`
Ecosystems that were already format-only (dotnet, bash, markdown) are unchanged. Consumer
overrides that still put code-changing verbs in `fix-cmd` keep that behavior under `--fix`;
prefer migrating them to `code-fix-cmd`. Closes #2649.

## [0.12.1]

### Changed
Expand Down
2 changes: 1 addition & 1 deletion plugins/toolchain/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ defaults. Three skills, one concern: mechanical verification of changed code.
| Skill | What it does |
|---|---|
| `/toolchain:check` | Build + test + lint for changed files, auto-detecting affected ecosystems (.NET, Python, TypeScript, Bash, PowerShell, Markdown) from git status; resolves each ecosystem's commands through the shared four-rung ladder. Also the reference skill other plugins compose for ecosystem detection and command resolution. |
| `/toolchain:lint` | Lint + format checks only — faster than a build cycle, honors each tool's config-file opt-in, `--fix` mode where linters support it; also owns the `yaml` and `cross-cutting` lint surfaces. |
| `/toolchain:lint` | Lint + format checks only — faster than a build cycle, honors each tool's config-file opt-in; `--fix` is format-only, `--code-fix` runs semantic lint autofixes behind a confirmation / `--yes` gate; also owns the `yaml` and `cross-cutting` lint surfaces. |
| `/toolchain:setup` | Configure the plugin for a repo. `check` (read-only, default) reports which ecosystems are configured and each one's resolved command surface, validating the tracked files against the contract schema; `apply` interviews + infers + writes the tracked `.claude/ecosystems/<ecosystem>.yaml` files that `/toolchain:check` and `/toolchain:lint` resolve first. Re-runnable. |

## Works in any repo
Expand Down
5 changes: 3 additions & 2 deletions plugins/toolchain/reference/ecosystems/go.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -34,12 +34,13 @@ project-discovery: ["go.mod"]
build-cmd: "go build ./..."
test-cmd: "go test ./..."
check-cmd: "golangci-lint run ./..."
fix-cmd: "golangci-lint run --fix ./..."
fix-cmd: "gofmt -w <files>" # format-only; agent substitutes only *.go from <files> (never go.mod/go.sum)
code-fix-cmd: "golangci-lint run --fix <files>" # *.go only; invoke once per package directory (see lint SKILL.md)
opt-in: ".golangci.yml, .golangci.yaml, .golangci.toml, or .golangci.json present (walked from the changed file up to the repo root) — otherwise golangci-lint applies its own unconfigured \"standard\" linter preset unconditionally"
install-hint: "Install golangci-lint: https://golangci-lint.run/docs/welcome/install/ | Go toolchain: https://go.dev/dl/"
gates:
- name: go-mod-tidy-drift
cmd: "go mod tidy -diff"
trigger-globs: ["go.mod", "go.sum", "*.go"]
remediation: "Run go mod tidy and commit the updated go.mod/go.sum. (go mod tidy -diff requires Go 1.23+; an older toolchain rejects the flag, so the gate reports skip rather than drift.)"
notes: "govulncheck is intentionally not a rung-4 default (per the epic brief's \"optional\" framing) — add it as a consumer-local gate via .claude/ecosystems/go.local.yaml if desired."
notes: "gofmt / golangci-lint --fix receive only *.go paths from <files>, grouped by package directory for golangci-lint. govulncheck is intentionally not a rung-4 default (per the epic brief's \"optional\" framing) — add it as a consumer-local gate via .claude/ecosystems/go.local.yaml if desired."
9 changes: 8 additions & 1 deletion plugins/toolchain/reference/ecosystems/python.yaml
Original file line number Diff line number Diff line change
@@ -1,11 +1,18 @@
# Bundled portable default — python. Rung-4 fallback ONLY (consumer
# .claude/ecosystems/python.yaml overrides key-by-key). Contract + schema:
# https://raw.githubusercontent.com/melodic-software/claude-code-plugins/main/docs/conventions/ecosystem-commands/README.md
#
# fix-cmd is format-only (ruff format). Code-changing autofixes (ruff check
# --fix) live in code-fix-cmd and require /toolchain:lint --code-fix plus
# that skill's confirmation / --yes gate. --unfixable F401 matches the
# ruff-format hook: protects just-added imports during iterative editing;
# F401 still surfaces as a finding — only auto-deletion is suppressed.
globs: ["*.py", "pyproject.toml", "uv.lock", "pyrightconfig.json"]
project-discovery: ["pyproject.toml"]
build-cmd: null # no separate build step; lint/test cover verification
test-cmd: "uv run pytest -x -q" # plain pytest when the project doesn't use uv
check-cmd: "uv run ruff check . --no-fix && uv run ruff format . --check && uv run pyright"
fix-cmd: "uv run ruff check . --fix && uv run ruff format ." # pyright has no fix mode
fix-cmd: "uv run ruff format <files>" # format-only; pyright has no fix mode
code-fix-cmd: "uv run ruff check <files> --fix --no-unsafe-fixes --unfixable F401"
opt-in: "ruff config (ruff.toml, .ruff.toml, or pyproject.toml [tool.ruff])"
install-hint: "pip install ruff pyright uv | brew install uv ruff pyright | winget install astral-sh.uv"
6 changes: 5 additions & 1 deletion plugins/toolchain/reference/ecosystems/typescript.yaml
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
# Bundled portable default — typescript. Rung-4 fallback ONLY (consumer
# .claude/ecosystems/typescript.yaml overrides key-by-key). Contract + schema:
# https://raw.githubusercontent.com/melodic-software/claude-code-plugins/main/docs/conventions/ecosystem-commands/README.md
#
# fix-cmd is format-only (biome format). biome check --write applies lint
# autofixes and lives in code-fix-cmd behind /toolchain:lint --code-fix.
globs: ["*.ts", "*.tsx", "*.js", "*.jsx", "*.mjs", "package.json", "package-lock.json", "pnpm-lock.yaml", "yarn.lock", "tsconfig*.json"]
project-discovery: ["package.json"]
build-cmd: "npx tsc --noEmit" # when the project has a tsconfig; otherwise the package.json build script
test-cmd: "the package.json test script (npm test / npx vitest run / npx jest — use what the project configures)"
check-cmd: "npx biome check ." # biome.json present; use eslint when the project configures it instead
fix-cmd: "npx biome check --write ."
fix-cmd: "npx biome format --write <files>"
code-fix-cmd: "npx biome check --write <files>"
opt-in: "biome.json → Biome; eslint config → ESLint (use whichever the project configures)"
install-hint: "project-local devDependencies preferred; npm i -g typescript @biomejs/biome"
6 changes: 5 additions & 1 deletion plugins/toolchain/reference/resolution-ladder.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,11 @@ conforming to the contract's `ecosystem.schema.json`. Command keys are **opaque
- `build-cmd` — build/compile verification; `null` when the ecosystem has no build step
- `test-cmd` — test command; `null` when no test framework is wired
- `check-cmd` — lint/format check, no file modification; `null` when lint does not apply
- `fix-cmd` — auto-fix; `null` when the toolchain has no fix mode
- `fix-cmd` — **format-only** auto-fix (whitespace / import layout / style); `null` when absent.
Must not apply semantic/code-changing lint autofixes. `/toolchain:lint --fix` runs this key.
- `code-fix-cmd` — **code-changing** auto-fix (lint autofixes that rewrite logic, delete unused
imports, apply safe/unsafe code edits); `null` when absent. `/toolchain:lint --code-fix` runs
this key behind that skill's confirmation / `--yes` gate — never bare `--fix`.

Plus `globs` (required — classify changed files), and optional `enabled` (default `true`; a consumer
sets `false` to disable an ecosystem without deleting its file), `anchor`, `project-discovery`,
Expand Down
2 changes: 1 addition & 1 deletion plugins/toolchain/skills/check/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ If neither path yields changes and no `$ARGUMENTS`: report "No changes found (wo

### 1.5 Resolve each ecosystem's command surface

For each affected ecosystem, resolve its command surface (`globs`, `build-cmd`, `test-cmd`, `check-cmd`, `fix-cmd`, `anchor`, `project-discovery`, `install-hint`, `gates`, `notes`) through the four-rung ladder in [`${CLAUDE_PLUGIN_ROOT}/reference/resolution-ladder.md`](${CLAUDE_PLUGIN_ROOT}/reference/resolution-ladder.md):
For each affected ecosystem, resolve its command surface (`globs`, `build-cmd`, `test-cmd`, `check-cmd`, `fix-cmd`, `code-fix-cmd`, `anchor`, `project-discovery`, `install-hint`, `gates`, `notes`) through the four-rung ladder in [`${CLAUDE_PLUGIN_ROOT}/reference/resolution-ladder.md`](${CLAUDE_PLUGIN_ROOT}/reference/resolution-ladder.md):

1. Consumer `.claude/ecosystems/<ecosystem>.yaml` (+ `.local.yaml` overlay, `~/.claude/ecosystems/` user-global, additive per key) → authoritative.
2. Absent → infer from the repo's build files and offer to persist via `/toolchain:setup`.
Expand Down
11 changes: 9 additions & 2 deletions plugins/toolchain/skills/check/context/go.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,11 @@ own unconfigured "standard" linter preset on a repo that never configured any.
# Check (CI mode — fails on violations)
cd "$PROJECT_DIR" && golangci-lint run ./...

# Fix
cd "$PROJECT_DIR" && golangci-lint run --fix ./...
# Format-only (/toolchain:lint --fix)
cd "$PROJECT_DIR" && gofmt -w <files>

# Code-fix (semantic lint autofixes — /toolchain:lint --code-fix only)
cd "$PROJECT_DIR" && golangci-lint run --fix <files>
```

## Gotchas
Expand Down Expand Up @@ -58,3 +61,7 @@ Find all Go modules dynamically:
```bash
find "$REPO_ROOT" -name "go.mod" -not -path "*/vendor/*"
```

## Format / code-fix file scope

`gofmt` and `golangci-lint --fix` receive only `*.go` paths from `<files>` (never `go.mod`/`go.sum`). `golangci-lint --fix` is invoked once per package directory when changed files span multiple packages.
Loading