diff --git a/.claude/skills/docops/.docops-manifest b/.claude/commands/docops/.docops-manifest similarity index 73% rename from .claude/skills/docops/.docops-manifest rename to .claude/commands/docops/.docops-manifest index d1ccdda..a41aedc 100644 --- a/.claude/skills/docops/.docops-manifest +++ b/.claude/commands/docops/.docops-manifest @@ -1,10 +1,19 @@ # docops manifest — files in this directory owned by `docops upgrade`. # Do not hand-edit; rerun `docops upgrade` to regenerate. audit.md +close.md +do.md +get.md +graph.md init.md +list.md new-adr.md new-ctx.md new-task.md +next.md +plan.md +progress.md refresh.md +search.md state.md upgrade.md diff --git a/.claude/skills/docops/audit.md b/.claude/commands/docops/audit.md similarity index 100% rename from .claude/skills/docops/audit.md rename to .claude/commands/docops/audit.md diff --git a/.claude/commands/docops/close.md b/.claude/commands/docops/close.md new file mode 100644 index 0000000..c3ecea1 --- /dev/null +++ b/.claude/commands/docops/close.md @@ -0,0 +1,42 @@ +--- +name: close +description: Finish a task — flip status to done, refresh DocOps state, and stage a commit. Use when the user says "done with TP-X" / "mark TP-X complete". +--- + +# /docops:close + +Close out a finished task. + +Ask for the task ID if not provided. Then: + +1. Verify the task's acceptance criteria are actually met. Read the + task body, check for remaining TODOs, and (if code changed) + eyeball the diff. If something is incomplete, stop and tell the user. + +2. Flip the task's `status:` frontmatter to `done` via `Edit` on the + source file — do not hand-edit the index. + +3. Run: + +``` +docops refresh +``` + +4. Stage every file relevant to the task (source changes + task + frontmatter + `docs/.index.json` + `docs/STATE.md`), then propose a + commit message of the form: + + ``` + : + + + ``` + + Confirm with the user before committing. Never commit without their + explicit OK. + +5. After the commit, run `/docops:progress` to surface the next move. + +Do not mark a task `done` just because citing files changed — the +acceptance list is the contract. diff --git a/.claude/commands/docops/do.md b/.claude/commands/docops/do.md new file mode 100644 index 0000000..bcf5c56 --- /dev/null +++ b/.claude/commands/docops/do.md @@ -0,0 +1,32 @@ +--- +name: do +description: Route a freeform natural-language intent to the right /docops:* skill. Use when the user knows what they want but doesn't know which command runs it. +--- + +# /docops:do + +Interpret the user's request and dispatch to one matching skill. +Never do the work yourself — confirm the match, then hand off. + +Routing table (intent → skill): + +| Intent | Skill | +|---|---| +| "where are we", "what's the status", "summarise" | `/docops:progress` | +| "what's next", "pick a task" | `/docops:next` | +| "add a decision", "record that choice", "I need an ADR" | `/docops:new-adr` | +| "capture a PRD", "memo", "save constraints", "research note" | `/docops:new-ctx` | +| "add a task", "track this work", "create a TP" | `/docops:new-task` | +| "find X", "search for Y" | `/docops:search` | +| "look up ADR-0012" | `/docops:get` | +| "what depends on ADR-X", "blast radius" | `/docops:graph` | +| "list draft ADRs", "active tasks" | `/docops:list` | +| "what's broken", "coverage gaps" | `/docops:audit` | +| "finish TP-X", "mark done", "close this task" | `/docops:close` | +| "plan from CTX-X", "draft decision + tasks" | `/docops:plan` | +| "upgrade docops files", "refresh scaffolding" | `/docops:upgrade` | +| "validate", "reindex", "regenerate state" | `/docops:refresh` | + +Match on intent, not literal wording. If two skills could fit, ask one +short clarifying question. If nothing matches, say so — do not invent a +command. diff --git a/.claude/commands/docops/get.md b/.claude/commands/docops/get.md new file mode 100644 index 0000000..4dc7a7e --- /dev/null +++ b/.claude/commands/docops/get.md @@ -0,0 +1,21 @@ +--- +name: get +description: Look up a single DocOps doc by ID (ADR-nnnn, CTX-nnn, TP-nnn) and print its indexed record. Use when you need the frontmatter and edges for one doc without reading the full file. +--- + +# /docops:get + +Fetch one indexed doc by ID. + +``` +docops get ADR-0010 +docops get TP-029 --json +``` + +The plain output shows title, status, coverage/priority, dates, and both +forward and reverse edges (computed). `--json` returns the full +`IndexedDoc` — use it when scripting or when you need the exact edge +shape. + +When a user pastes an ID into chat, run `docops get ` first to +orient before reading the full file. diff --git a/.claude/commands/docops/graph.md b/.claude/commands/docops/graph.md new file mode 100644 index 0000000..0ae5cce --- /dev/null +++ b/.claude/commands/docops/graph.md @@ -0,0 +1,22 @@ +--- +name: graph +description: Walk the typed edge graph outward from one doc to its neighbours (requires, related, supersedes, reverse edges). Use to understand "what depends on what" before touching an ADR or task. +--- + +# /docops:graph + +Print the typed edge graph starting at one doc. + +``` +docops graph ADR-0010 +docops graph ADR-0010 --depth 2 +docops graph TP-029 --json +``` + +`--depth N` (default 1) controls traversal depth. Depth 1 shows direct +neighbours; higher depths show transitive closure. Edges include both +source fields (`requires`, `related`, `supersedes`, `depends_on`) and +computed reverse edges (`required_by`, etc. — see ADR-0006). + +Use this before superseding an ADR or removing a task: the graph shows +every doc that would break. diff --git a/.claude/skills/docops/init.md b/.claude/commands/docops/init.md similarity index 93% rename from .claude/skills/docops/init.md rename to .claude/commands/docops/init.md index dfa9518..0de7937 100644 --- a/.claude/skills/docops/init.md +++ b/.claude/commands/docops/init.md @@ -20,7 +20,7 @@ What it does: - Writes JSON Schemas to `docs/.docops/schema/` for in-editor validation. - Writes or refreshes the `` block inside `AGENTS.md` and `CLAUDE.md` (both files share the same docops block; Claude Code reads CLAUDE.md by default while other agents read AGENTS.md). - Installs a language-agnostic pre-commit hook that runs `docops validate`. -- Scaffolds `/docops:*` skills into `.claude/skills/docops/` and `.cursor/commands/docops/`. +- Scaffolds `/docops:*` slash commands into `.claude/commands/docops/` and `.cursor/commands/docops/`. Flags: diff --git a/.claude/commands/docops/list.md b/.claude/commands/docops/list.md new file mode 100644 index 0000000..46d59b1 --- /dev/null +++ b/.claude/commands/docops/list.md @@ -0,0 +1,26 @@ +--- +name: list +description: List DocOps docs with optional filters (kind, status, coverage, tag, stale, since). Use when looking for a set of docs, not one — e.g. "all draft ADRs", "all active tasks". +--- + +# /docops:list + +Enumerate docs with filters. Prefer this over reading `docs/.index.json` +directly. + +``` +docops list --kind ADR --status draft +docops list --kind TP --status active +docops list --kind ADR --coverage required +docops list --tag release +docops list --stale +docops list --since 2026-04-01 +docops list --json +``` + +Filters compose (AND). `--status` semantics are per-kind: +- ADR: `draft`, `accepted`, `superseded`, `rejected` +- TP: `backlog`, `active`, `done` +- CTX: has no status; `--status` with `--kind CTX` is invalid + +Default output is a table. Use `--json` for scripting. diff --git a/.claude/skills/docops/new-adr.md b/.claude/commands/docops/new-adr.md similarity index 100% rename from .claude/skills/docops/new-adr.md rename to .claude/commands/docops/new-adr.md diff --git a/.claude/skills/docops/new-ctx.md b/.claude/commands/docops/new-ctx.md similarity index 100% rename from .claude/skills/docops/new-ctx.md rename to .claude/commands/docops/new-ctx.md diff --git a/.claude/skills/docops/new-task.md b/.claude/commands/docops/new-task.md similarity index 100% rename from .claude/skills/docops/new-task.md rename to .claude/commands/docops/new-task.md diff --git a/.claude/commands/docops/next.md b/.claude/commands/docops/next.md new file mode 100644 index 0000000..a61353c --- /dev/null +++ b/.claude/commands/docops/next.md @@ -0,0 +1,27 @@ +--- +name: next +description: Ask DocOps which task to pick up next. Uses assignee, priority, status, and depends_on to recommend one task. Use at session start or after finishing a task. +--- + +# /docops:next + +Find the next task to work on. + +``` +docops next +``` + +Filter when the project has multiple contributors or priority bands: + +``` +docops next --assignee nachiket +docops next --priority p0 +docops next --json +``` + +The CLI picks one task by descending priority (p0 → p2), then ascending +ID among tasks with no unmet `depends_on`. If no task matches, exit code +is non-zero and stderr says `no task matches`. + +After selecting a task, read every doc in its `requires:` and +`depends_on:` before writing code. diff --git a/.claude/commands/docops/plan.md b/.claude/commands/docops/plan.md new file mode 100644 index 0000000..5a9f5b9 --- /dev/null +++ b/.claude/commands/docops/plan.md @@ -0,0 +1,51 @@ +--- +name: plan +description: Given a CTX (PRD, memo, research note), draft one ADR and one or more tasks that cite it. Human-confirmed before write. Use when turning stakeholder input into actionable work. +--- + +# /docops:plan + +Convert context into a decision plus tasks. + +Ask for the CTX ID if not provided. Read it, then draft: + +1. **One ADR** capturing the decision the CTX implies or demands. Propose + title, Context/Decision/Rationale/Consequences body. Confirm with the + user before writing. + +2. **One or more tasks** that cite the ADR. Each task should carry + priority, an acceptance checklist, and (if relevant) a depends-on + reference to other tasks. Confirm the full set before writing. + +Write via `--body -` heredocs so drafts land populated on creation (no +stub-then-rewrite round-trip): + +``` +docops new adr "Title" --related ADR-xxxx --body - <<'EOF' +## Context +... +## Decision +... +## Rationale +... +## Consequences +... +EOF + +docops new task "Title" --requires ADR-0026 --priority p1 --body - <<'EOF' +## Goal +... +## Acceptance +- ... +EOF +``` + +After all writes, run `docops refresh` to regenerate index and STATE.md. + +Rules: + +- Every task must cite ≥1 ADR or CTX in `requires:`. If the user cannot + name a citation, stop and draft the missing ADR/CTX first. +- Keep ADR `status: draft` unless the user explicitly accepts it. +- Don't write anything the user hasn't confirmed. Show proposed + frontmatter and body summary, then ask. diff --git a/.claude/commands/docops/progress.md b/.claude/commands/docops/progress.md new file mode 100644 index 0000000..511fb45 --- /dev/null +++ b/.claude/commands/docops/progress.md @@ -0,0 +1,28 @@ +--- +name: progress +description: Situational awareness — read STATE.md, run audit, and name the next action in one go. Use at session start or when the user asks "where are we?" / "what's next?". +--- + +# /docops:progress + +Summarise project state and recommend one next action. + +Run these, in order: + +``` +docops state +docops audit +docops next +``` + +Then produce a single short briefing: + +- **Counts:** doc totals and anything in needs-attention from STATE.md. +- **Active work:** tasks currently `status: active` (from `docops list --kind TP --status active --json`). +- **Audit gaps:** one line per finding; skip if audit is empty. +- **Next task:** whatever `docops next` picked, with its `requires:` IDs. +- **Recommendation:** one of `/docops:next`, `/docops:new-ctx`, `/docops:new-adr`, `/docops:new-task`, or `/docops:close ` depending on state. + +Keep the briefing under 10 lines. Do not regenerate STATE.md unless +something in the repo changed — running `docops state` is cheap and +idempotent, but a dirty diff shows up in git. diff --git a/.claude/skills/docops/refresh.md b/.claude/commands/docops/refresh.md similarity index 100% rename from .claude/skills/docops/refresh.md rename to .claude/commands/docops/refresh.md diff --git a/.claude/commands/docops/search.md b/.claude/commands/docops/search.md new file mode 100644 index 0000000..1364af7 --- /dev/null +++ b/.claude/commands/docops/search.md @@ -0,0 +1,28 @@ +--- +name: search +description: Substring or regex search across DocOps doc titles, tags, and bodies, with structured filters (kind, status, coverage, tag, priority, assignee, since). Use when looking for docs by content, not ID. +--- + +# /docops:search + +Query docs by text and/or frontmatter. + +``` +docops search "release process" +docops search "homebrew" --kind ADR +docops search --kind TP --status active --priority p1 +docops search "upgrade" --regex --case +docops search --tag release --since 2026-04-01 +docops search "migration" --json +``` + +The query is optional when at least one filter flag is present. Default +match is case-insensitive substring; add `--regex` for regular +expressions and `--case` for case-sensitive matching. + +Filters narrow before text match, so `--kind TP --status active "foo"` +is cheaper than searching and then filtering. + +Semantic/embedding search is out of scope (see ADR-0017) — use +substring + filters, or let the user point at specific IDs via +`/docops:get`. diff --git a/.claude/skills/docops/state.md b/.claude/commands/docops/state.md similarity index 100% rename from .claude/skills/docops/state.md rename to .claude/commands/docops/state.md diff --git a/.claude/skills/docops/upgrade.md b/.claude/commands/docops/upgrade.md similarity index 73% rename from .claude/skills/docops/upgrade.md rename to .claude/commands/docops/upgrade.md index e75cda9..8702546 100644 --- a/.claude/skills/docops/upgrade.md +++ b/.claude/commands/docops/upgrade.md @@ -15,7 +15,8 @@ docops upgrade Touches only DocOps-owned scaffolding: -- `.claude/skills/docops/*.md` and `.cursor/commands/docops/*.md` — synced to the shipped bundle (creates new files, refreshes changed ones, removes files that left the bundle). +- `.claude/commands/docops/*.md` and `.cursor/commands/docops/*.md` — synced to the shipped bundle (creates new files, refreshes changed ones, removes files that left the bundle). +- `.claude/skills/docops/*.md` — if this legacy folder exists from an older docops, its contents are removed (Claude Code reads slash commands from `.claude/commands/`, not `.claude/skills/`). - `docs/.docops/schema/*.schema.json` — regenerated from `docops.yaml`. - The `` block inside `AGENTS.md` and `CLAUDE.md` — refreshed in place; content outside the markers is preserved. Either file is created if absent (so v0.1.x users gain CLAUDE.md on first upgrade). @@ -47,5 +48,6 @@ docops upgrade --json ``` Exit codes: `0` on success or user abort; `2` when there is no -`docops.yaml` (run `docops init` first) or when the `.claude/skills/docops/` -directory contains user-added files docops did not write. +`docops.yaml` (run `docops init` first) or when the +`.claude/commands/docops/` directory contains user-added files docops did +not write. diff --git a/.cursor/commands/docops/.docops-manifest b/.cursor/commands/docops/.docops-manifest index d1ccdda..a41aedc 100644 --- a/.cursor/commands/docops/.docops-manifest +++ b/.cursor/commands/docops/.docops-manifest @@ -1,10 +1,19 @@ # docops manifest — files in this directory owned by `docops upgrade`. # Do not hand-edit; rerun `docops upgrade` to regenerate. audit.md +close.md +do.md +get.md +graph.md init.md +list.md new-adr.md new-ctx.md new-task.md +next.md +plan.md +progress.md refresh.md +search.md state.md upgrade.md diff --git a/.cursor/commands/docops/close.md b/.cursor/commands/docops/close.md new file mode 100644 index 0000000..c3ecea1 --- /dev/null +++ b/.cursor/commands/docops/close.md @@ -0,0 +1,42 @@ +--- +name: close +description: Finish a task — flip status to done, refresh DocOps state, and stage a commit. Use when the user says "done with TP-X" / "mark TP-X complete". +--- + +# /docops:close + +Close out a finished task. + +Ask for the task ID if not provided. Then: + +1. Verify the task's acceptance criteria are actually met. Read the + task body, check for remaining TODOs, and (if code changed) + eyeball the diff. If something is incomplete, stop and tell the user. + +2. Flip the task's `status:` frontmatter to `done` via `Edit` on the + source file — do not hand-edit the index. + +3. Run: + +``` +docops refresh +``` + +4. Stage every file relevant to the task (source changes + task + frontmatter + `docs/.index.json` + `docs/STATE.md`), then propose a + commit message of the form: + + ``` + : + + + ``` + + Confirm with the user before committing. Never commit without their + explicit OK. + +5. After the commit, run `/docops:progress` to surface the next move. + +Do not mark a task `done` just because citing files changed — the +acceptance list is the contract. diff --git a/.cursor/commands/docops/do.md b/.cursor/commands/docops/do.md new file mode 100644 index 0000000..bcf5c56 --- /dev/null +++ b/.cursor/commands/docops/do.md @@ -0,0 +1,32 @@ +--- +name: do +description: Route a freeform natural-language intent to the right /docops:* skill. Use when the user knows what they want but doesn't know which command runs it. +--- + +# /docops:do + +Interpret the user's request and dispatch to one matching skill. +Never do the work yourself — confirm the match, then hand off. + +Routing table (intent → skill): + +| Intent | Skill | +|---|---| +| "where are we", "what's the status", "summarise" | `/docops:progress` | +| "what's next", "pick a task" | `/docops:next` | +| "add a decision", "record that choice", "I need an ADR" | `/docops:new-adr` | +| "capture a PRD", "memo", "save constraints", "research note" | `/docops:new-ctx` | +| "add a task", "track this work", "create a TP" | `/docops:new-task` | +| "find X", "search for Y" | `/docops:search` | +| "look up ADR-0012" | `/docops:get` | +| "what depends on ADR-X", "blast radius" | `/docops:graph` | +| "list draft ADRs", "active tasks" | `/docops:list` | +| "what's broken", "coverage gaps" | `/docops:audit` | +| "finish TP-X", "mark done", "close this task" | `/docops:close` | +| "plan from CTX-X", "draft decision + tasks" | `/docops:plan` | +| "upgrade docops files", "refresh scaffolding" | `/docops:upgrade` | +| "validate", "reindex", "regenerate state" | `/docops:refresh` | + +Match on intent, not literal wording. If two skills could fit, ask one +short clarifying question. If nothing matches, say so — do not invent a +command. diff --git a/.cursor/commands/docops/get.md b/.cursor/commands/docops/get.md new file mode 100644 index 0000000..4dc7a7e --- /dev/null +++ b/.cursor/commands/docops/get.md @@ -0,0 +1,21 @@ +--- +name: get +description: Look up a single DocOps doc by ID (ADR-nnnn, CTX-nnn, TP-nnn) and print its indexed record. Use when you need the frontmatter and edges for one doc without reading the full file. +--- + +# /docops:get + +Fetch one indexed doc by ID. + +``` +docops get ADR-0010 +docops get TP-029 --json +``` + +The plain output shows title, status, coverage/priority, dates, and both +forward and reverse edges (computed). `--json` returns the full +`IndexedDoc` — use it when scripting or when you need the exact edge +shape. + +When a user pastes an ID into chat, run `docops get ` first to +orient before reading the full file. diff --git a/.cursor/commands/docops/graph.md b/.cursor/commands/docops/graph.md new file mode 100644 index 0000000..0ae5cce --- /dev/null +++ b/.cursor/commands/docops/graph.md @@ -0,0 +1,22 @@ +--- +name: graph +description: Walk the typed edge graph outward from one doc to its neighbours (requires, related, supersedes, reverse edges). Use to understand "what depends on what" before touching an ADR or task. +--- + +# /docops:graph + +Print the typed edge graph starting at one doc. + +``` +docops graph ADR-0010 +docops graph ADR-0010 --depth 2 +docops graph TP-029 --json +``` + +`--depth N` (default 1) controls traversal depth. Depth 1 shows direct +neighbours; higher depths show transitive closure. Edges include both +source fields (`requires`, `related`, `supersedes`, `depends_on`) and +computed reverse edges (`required_by`, etc. — see ADR-0006). + +Use this before superseding an ADR or removing a task: the graph shows +every doc that would break. diff --git a/.cursor/commands/docops/init.md b/.cursor/commands/docops/init.md index dfa9518..0de7937 100644 --- a/.cursor/commands/docops/init.md +++ b/.cursor/commands/docops/init.md @@ -20,7 +20,7 @@ What it does: - Writes JSON Schemas to `docs/.docops/schema/` for in-editor validation. - Writes or refreshes the `` block inside `AGENTS.md` and `CLAUDE.md` (both files share the same docops block; Claude Code reads CLAUDE.md by default while other agents read AGENTS.md). - Installs a language-agnostic pre-commit hook that runs `docops validate`. -- Scaffolds `/docops:*` skills into `.claude/skills/docops/` and `.cursor/commands/docops/`. +- Scaffolds `/docops:*` slash commands into `.claude/commands/docops/` and `.cursor/commands/docops/`. Flags: diff --git a/.cursor/commands/docops/list.md b/.cursor/commands/docops/list.md new file mode 100644 index 0000000..46d59b1 --- /dev/null +++ b/.cursor/commands/docops/list.md @@ -0,0 +1,26 @@ +--- +name: list +description: List DocOps docs with optional filters (kind, status, coverage, tag, stale, since). Use when looking for a set of docs, not one — e.g. "all draft ADRs", "all active tasks". +--- + +# /docops:list + +Enumerate docs with filters. Prefer this over reading `docs/.index.json` +directly. + +``` +docops list --kind ADR --status draft +docops list --kind TP --status active +docops list --kind ADR --coverage required +docops list --tag release +docops list --stale +docops list --since 2026-04-01 +docops list --json +``` + +Filters compose (AND). `--status` semantics are per-kind: +- ADR: `draft`, `accepted`, `superseded`, `rejected` +- TP: `backlog`, `active`, `done` +- CTX: has no status; `--status` with `--kind CTX` is invalid + +Default output is a table. Use `--json` for scripting. diff --git a/.cursor/commands/docops/next.md b/.cursor/commands/docops/next.md new file mode 100644 index 0000000..a61353c --- /dev/null +++ b/.cursor/commands/docops/next.md @@ -0,0 +1,27 @@ +--- +name: next +description: Ask DocOps which task to pick up next. Uses assignee, priority, status, and depends_on to recommend one task. Use at session start or after finishing a task. +--- + +# /docops:next + +Find the next task to work on. + +``` +docops next +``` + +Filter when the project has multiple contributors or priority bands: + +``` +docops next --assignee nachiket +docops next --priority p0 +docops next --json +``` + +The CLI picks one task by descending priority (p0 → p2), then ascending +ID among tasks with no unmet `depends_on`. If no task matches, exit code +is non-zero and stderr says `no task matches`. + +After selecting a task, read every doc in its `requires:` and +`depends_on:` before writing code. diff --git a/.cursor/commands/docops/plan.md b/.cursor/commands/docops/plan.md new file mode 100644 index 0000000..5a9f5b9 --- /dev/null +++ b/.cursor/commands/docops/plan.md @@ -0,0 +1,51 @@ +--- +name: plan +description: Given a CTX (PRD, memo, research note), draft one ADR and one or more tasks that cite it. Human-confirmed before write. Use when turning stakeholder input into actionable work. +--- + +# /docops:plan + +Convert context into a decision plus tasks. + +Ask for the CTX ID if not provided. Read it, then draft: + +1. **One ADR** capturing the decision the CTX implies or demands. Propose + title, Context/Decision/Rationale/Consequences body. Confirm with the + user before writing. + +2. **One or more tasks** that cite the ADR. Each task should carry + priority, an acceptance checklist, and (if relevant) a depends-on + reference to other tasks. Confirm the full set before writing. + +Write via `--body -` heredocs so drafts land populated on creation (no +stub-then-rewrite round-trip): + +``` +docops new adr "Title" --related ADR-xxxx --body - <<'EOF' +## Context +... +## Decision +... +## Rationale +... +## Consequences +... +EOF + +docops new task "Title" --requires ADR-0026 --priority p1 --body - <<'EOF' +## Goal +... +## Acceptance +- ... +EOF +``` + +After all writes, run `docops refresh` to regenerate index and STATE.md. + +Rules: + +- Every task must cite ≥1 ADR or CTX in `requires:`. If the user cannot + name a citation, stop and draft the missing ADR/CTX first. +- Keep ADR `status: draft` unless the user explicitly accepts it. +- Don't write anything the user hasn't confirmed. Show proposed + frontmatter and body summary, then ask. diff --git a/.cursor/commands/docops/progress.md b/.cursor/commands/docops/progress.md new file mode 100644 index 0000000..511fb45 --- /dev/null +++ b/.cursor/commands/docops/progress.md @@ -0,0 +1,28 @@ +--- +name: progress +description: Situational awareness — read STATE.md, run audit, and name the next action in one go. Use at session start or when the user asks "where are we?" / "what's next?". +--- + +# /docops:progress + +Summarise project state and recommend one next action. + +Run these, in order: + +``` +docops state +docops audit +docops next +``` + +Then produce a single short briefing: + +- **Counts:** doc totals and anything in needs-attention from STATE.md. +- **Active work:** tasks currently `status: active` (from `docops list --kind TP --status active --json`). +- **Audit gaps:** one line per finding; skip if audit is empty. +- **Next task:** whatever `docops next` picked, with its `requires:` IDs. +- **Recommendation:** one of `/docops:next`, `/docops:new-ctx`, `/docops:new-adr`, `/docops:new-task`, or `/docops:close ` depending on state. + +Keep the briefing under 10 lines. Do not regenerate STATE.md unless +something in the repo changed — running `docops state` is cheap and +idempotent, but a dirty diff shows up in git. diff --git a/.cursor/commands/docops/search.md b/.cursor/commands/docops/search.md new file mode 100644 index 0000000..1364af7 --- /dev/null +++ b/.cursor/commands/docops/search.md @@ -0,0 +1,28 @@ +--- +name: search +description: Substring or regex search across DocOps doc titles, tags, and bodies, with structured filters (kind, status, coverage, tag, priority, assignee, since). Use when looking for docs by content, not ID. +--- + +# /docops:search + +Query docs by text and/or frontmatter. + +``` +docops search "release process" +docops search "homebrew" --kind ADR +docops search --kind TP --status active --priority p1 +docops search "upgrade" --regex --case +docops search --tag release --since 2026-04-01 +docops search "migration" --json +``` + +The query is optional when at least one filter flag is present. Default +match is case-insensitive substring; add `--regex` for regular +expressions and `--case` for case-sensitive matching. + +Filters narrow before text match, so `--kind TP --status active "foo"` +is cheaper than searching and then filtering. + +Semantic/embedding search is out of scope (see ADR-0017) — use +substring + filters, or let the user point at specific IDs via +`/docops:get`. diff --git a/.cursor/commands/docops/upgrade.md b/.cursor/commands/docops/upgrade.md index e75cda9..8702546 100644 --- a/.cursor/commands/docops/upgrade.md +++ b/.cursor/commands/docops/upgrade.md @@ -15,7 +15,8 @@ docops upgrade Touches only DocOps-owned scaffolding: -- `.claude/skills/docops/*.md` and `.cursor/commands/docops/*.md` — synced to the shipped bundle (creates new files, refreshes changed ones, removes files that left the bundle). +- `.claude/commands/docops/*.md` and `.cursor/commands/docops/*.md` — synced to the shipped bundle (creates new files, refreshes changed ones, removes files that left the bundle). +- `.claude/skills/docops/*.md` — if this legacy folder exists from an older docops, its contents are removed (Claude Code reads slash commands from `.claude/commands/`, not `.claude/skills/`). - `docs/.docops/schema/*.schema.json` — regenerated from `docops.yaml`. - The `` block inside `AGENTS.md` and `CLAUDE.md` — refreshed in place; content outside the markers is preserved. Either file is created if absent (so v0.1.x users gain CLAUDE.md on first upgrade). @@ -47,5 +48,6 @@ docops upgrade --json ``` Exit codes: `0` on success or user abort; `2` when there is no -`docops.yaml` (run `docops init` first) or when the `.claude/skills/docops/` -directory contains user-added files docops did not write. +`docops.yaml` (run `docops init` first) or when the +`.claude/commands/docops/` directory contains user-added files docops did +not write. diff --git a/AGENTS.md b/AGENTS.md index 3433cb4..64921dd 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -85,79 +85,71 @@ bin/docops ← product: built binary (gitignored) ## Orientation -- **Why we're building this:** `docs/context/CTX-*` (look for documents of `type: brief` or `type: prd`). -- **What's been decided:** `docs/decisions/ADR-*.md`. Frontmatter is load-bearing — read it. -- **What to do next:** `docs/STATE.md` for counts, needs-attention, and active work. -- **Hard guardrails:** any CTX of `type: memo` or `type: notes` that describes constraints. +- **Why:** `docs/context/CTX-*` (types `brief`, `prd`). +- **Decided:** `docs/decisions/ADR-*.md`. Frontmatter is load-bearing. +- **Next:** `docs/STATE.md` — counts, needs-attention, active work. +- **Guardrails:** any CTX of type `memo` or `notes`. ## Folder layout | Folder | Contents | |---|---| -| `docs/context/` | Stakeholder inputs: PRDs, design docs, memos, research, notes. File prefix `CTX-`. | -| `docs/decisions/` | ADRs. File prefix `ADR-`. | -| `docs/tasks/` | Work units. File prefix `TP-`. Every task cites ≥1 ADR or CTX. | -| `docs/STATE.md` | Auto-generated state snapshot. Do not edit by hand. | -| `docs/.index.json` | Auto-generated graph with computed reverse edges. Do not cat it into your context — use `docops state` or read individual doc files instead. | +| `docs/context/` | PRDs, design, memos, research, notes. Prefix `CTX-`. | +| `docs/decisions/` | ADRs. Prefix `ADR-`. | +| `docs/tasks/` | Work units. Prefix `TP-`. Each cites ≥1 ADR or CTX. | +| `docs/STATE.md` | Auto-generated. Don't edit. | +| `docs/.index.json` | Auto-generated graph. Don't edit, don't load wholesale — query via `docops` instead. | -## Invariants you MUST respect +## Invariants -1. **Every task must cite at least one ADR or CTX** in its `requires:` frontmatter. The validator refuses tasks without it. -2. **References must resolve.** No citing non-existent docs; no citing superseded docs without good reason. -3. **Do not edit `docs/STATE.md` or `docs/.index.json`** — both are regenerated from source. -4. **Do not edit reverse-edge fields** in source frontmatter. They are computed in the index. -5. **Filename is the ID.** `ADR-0020-whatever.md` is `ADR-0020`. Don't add an `id:` field. +1. Every task cites ≥1 ADR or CTX in `requires:`. Validator enforces. +2. References must resolve. No non-existent or dangling-superseded refs. +3. Don't edit `docs/STATE.md` or `docs/.index.json` — regenerated from source. +4. Don't edit reverse-edge fields in frontmatter — computed in the index. +5. Filename is the ID. `ADR-0020-whatever.md` is `ADR-0020`. No `id:` field. -## CLI commands — this is the query API +## CLI — the query and mutation API -### Shipped (all support `--json` for structured output) +All commands support `--json`. ``` -docops init # scaffold DocOps in this repo -docops upgrade # refresh DocOps-owned scaffolding after a binary upgrade -docops update-check # check upstream for a newer docops release (cached probe) -docops validate # schema + graph invariants -docops index # rebuild docs/.index.json -docops state # regenerate docs/STATE.md -docops audit # structural coverage gaps -docops refresh # validate + index + state in one pass -docops new ctx "title" --type memo -docops new adr "title" [--related ADR-xxxx] -docops new task "title" --requires ADR-xxxx,CTX-xxx -docops schema # (re)write schema JSON from docops.yaml +docops init scaffold DocOps in this repo +docops upgrade refresh DocOps-owned scaffolding +docops update-check probe upstream for a newer release (cached) +docops validate schema + graph invariants +docops index rebuild docs/.index.json +docops state regenerate docs/STATE.md +docops audit structural coverage gaps +docops refresh validate + index + state in one pass +docops schema (re)write docs/.docops/schema/*.schema.json + +docops list [--type ctx|adr|task] [--status ...] [--tag ...] +docops get look up one doc by ID +docops graph typed edge graph from a starting doc +docops next recommend the next task +docops search substring/regex over title, tags, body + +docops new ctx "title" --type memo [--body -|TEXT | --body-file PATH] +docops new adr "title" [--related ADR-xxxx] [--body -|TEXT | --body-file PATH] +docops new task "title" --requires ADR-xxxx,CTX-xxx [--body -|TEXT | --body-file PATH] ``` -### Not yet built — do not call these +**Populate bodies at creation.** `docops new` accepts `--body TEXT`, `--body -` (stdin heredoc), or `--body-file PATH`. Use them — they avoid the stub-then-rewrite round-trip. If you must edit an existing doc, read it first, then edit in place. -`status`, `get`, `list`, `graph`, `next`, `search`, `review` do not exist -yet. If your workflow needs one, propose a task first rather than inventing -flags or behaviour. +For lookups, prefer `docops list|get|search|graph|next` over loading `docs/.index.json` into context. -**For now, instead of `list`/`get`/`search`:** read `docs/STATE.md` for -counts and needs-attention, or read individual `docs/{context,decisions,tasks}/` -files directly. Avoid loading `docs/.index.json` wholesale — it is for -bootstrap / CI consumers, not routine agent queries. See -`docs/decisions/ADR-0018-cli-as-query-layer.md` for the rationale. +## Workflow -The binary is language-agnostic — install via direct download, Homebrew, Scoop, or Docker. No Node/Bun/Python dependency. - -## Recommended agent workflow - -1. Read `docs/STATE.md`. -2. Run `docops audit` to see open gaps. -3. Read `docs/tasks/` to pick a task (check `depends_on` before starting). +1. `docops state` (or read `docs/STATE.md`). +2. `docops audit` for open gaps. +3. Pick a task from `docs/tasks/`; check `depends_on`. 4. Before coding: read every doc in the task's `requires:` and `depends_on:`. -5. Work in your native plan/execute mode. DocOps does not prescribe how you code. -6. After finishing, update the task's frontmatter `status:` field and run `docops refresh` to regenerate index and STATE.md. -7. If your work revealed a new decision, `docops new adr`. If a new gap, `docops new task` with citations. +5. Work in your native plan/execute mode. +6. After: update the task's `status:` and run `docops refresh`. +7. New decision → `docops new adr`. New gap → `docops new task` with citations. ## Editor integration -JSON Schemas for frontmatter validation live in `docs/.docops/schema/` (`context.schema.json`, `decision.schema.json`, `task.schema.json`). Wire them up in VS Code via the `redhat.vscode-yaml` extension and run `docops schema` to regenerate after editing `context_types:` in `docops.yaml`. - -## Pairs well with - -- **GStack** role skills — they apply perspective; DocOps provides the typed state they read. -- **Native plan mode** of your IDE — DocOps explicitly does not try to replace it. +JSON Schemas live in `docs/.docops/schema/`. In VS Code, use `redhat.vscode-yaml`. Regenerate after editing `context_types:` in `docops.yaml`: `docops schema`. diff --git a/CLAUDE.md b/CLAUDE.md index 68815a4..e058e00 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -14,87 +14,73 @@ Codex, Copilot, Windsurf, Zed); both files are auto-maintained by ## Orientation -- **Why we're building this:** `docs/context/CTX-*` (look for documents of `type: brief` or `type: prd`). -- **What's been decided:** `docs/decisions/ADR-*.md`. Frontmatter is load-bearing — read it. -- **What to do next:** `docs/STATE.md` for counts, needs-attention, and active work. -- **Hard guardrails:** any CTX of `type: memo` or `type: notes` that describes constraints. +- **Why:** `docs/context/CTX-*` (types `brief`, `prd`). +- **Decided:** `docs/decisions/ADR-*.md`. Frontmatter is load-bearing. +- **Next:** `docs/STATE.md` — counts, needs-attention, active work. +- **Guardrails:** any CTX of type `memo` or `notes`. ## Folder layout | Folder | Contents | |---|---| -| `docs/context/` | Stakeholder inputs: PRDs, design docs, memos, research, notes. File prefix `CTX-`. | -| `docs/decisions/` | ADRs. File prefix `ADR-`. | -| `docs/tasks/` | Work units. File prefix `TP-`. Every task cites ≥1 ADR or CTX. | -| `docs/STATE.md` | Auto-generated state snapshot. Do not edit by hand. | -| `docs/.index.json` | Auto-generated graph with computed reverse edges. Do not cat it into your context — use `docops state` or read individual doc files instead. | +| `docs/context/` | PRDs, design, memos, research, notes. Prefix `CTX-`. | +| `docs/decisions/` | ADRs. Prefix `ADR-`. | +| `docs/tasks/` | Work units. Prefix `TP-`. Each cites ≥1 ADR or CTX. | +| `docs/STATE.md` | Auto-generated. Don't edit. | +| `docs/.index.json` | Auto-generated graph. Don't edit, don't load wholesale — query via `docops` instead. | -## Invariants you MUST respect +## Invariants -1. **Every task must cite at least one ADR or CTX** in its `requires:` frontmatter. The validator refuses tasks without it. -2. **References must resolve.** No citing non-existent docs; no citing superseded docs without good reason. -3. **Do not edit `docs/STATE.md` or `docs/.index.json`** — both are regenerated from source. -4. **Do not edit reverse-edge fields** in source frontmatter. They are computed in the index. -5. **Filename is the ID.** `ADR-0020-whatever.md` is `ADR-0020`. Don't add an `id:` field. +1. Every task cites ≥1 ADR or CTX in `requires:`. Validator enforces. +2. References must resolve. No non-existent or dangling-superseded refs. +3. Don't edit `docs/STATE.md` or `docs/.index.json` — regenerated from source. +4. Don't edit reverse-edge fields in frontmatter — computed in the index. +5. Filename is the ID. `ADR-0020-whatever.md` is `ADR-0020`. No `id:` field. -## CLI commands — this is the query API +## CLI — the query and mutation API -### Shipped (all support `--json` for structured output) +All commands support `--json`. ``` -docops init # scaffold DocOps in this repo -docops upgrade # refresh DocOps-owned scaffolding after a binary upgrade -docops update-check # check upstream for a newer docops release (cached probe) -docops validate # schema + graph invariants -docops index # rebuild docs/.index.json -docops state # regenerate docs/STATE.md -docops audit # structural coverage gaps -docops refresh # validate + index + state in one pass -docops new ctx "title" --type memo -docops new adr "title" [--related ADR-xxxx] -docops new task "title" --requires ADR-xxxx,CTX-xxx -docops schema # (re)write schema JSON from docops.yaml +docops init scaffold DocOps in this repo +docops upgrade refresh DocOps-owned scaffolding +docops update-check probe upstream for a newer release (cached) +docops validate schema + graph invariants +docops index rebuild docs/.index.json +docops state regenerate docs/STATE.md +docops audit structural coverage gaps +docops refresh validate + index + state in one pass +docops schema (re)write docs/.docops/schema/*.schema.json + +docops list [--type ctx|adr|task] [--status ...] [--tag ...] +docops get look up one doc by ID +docops graph typed edge graph from a starting doc +docops next recommend the next task +docops search substring/regex over title, tags, body + +docops new ctx "title" --type memo [--body -|TEXT | --body-file PATH] +docops new adr "title" [--related ADR-xxxx] [--body -|TEXT | --body-file PATH] +docops new task "title" --requires ADR-xxxx,CTX-xxx [--body -|TEXT | --body-file PATH] ``` -### Not yet built — do not call these +**Populate bodies at creation.** `docops new` accepts `--body TEXT`, `--body -` (stdin heredoc), or `--body-file PATH`. Use them — they avoid the stub-then-rewrite round-trip. If you must edit an existing doc, read it first, then edit in place. -`status`, `get`, `list`, `graph`, `next`, `search`, `review` do not exist -yet. If your workflow needs one, propose a task first rather than inventing -flags or behaviour. +For lookups, prefer `docops list|get|search|graph|next` over loading `docs/.index.json` into context. -**For now, instead of `list`/`get`/`search`:** read `docs/STATE.md` for -counts and needs-attention, or read individual `docs/{context,decisions,tasks}/` -files directly. Avoid loading `docs/.index.json` wholesale — it is for -bootstrap / CI consumers, not routine agent queries. See -`docs/decisions/ADR-0018-cli-as-query-layer.md` for the rationale. +## Workflow -The binary is language-agnostic — install via direct download, Homebrew, Scoop, or Docker. No Node/Bun/Python dependency. - -## Recommended agent workflow - -1. Read `docs/STATE.md`. -2. Run `docops audit` to see open gaps. -3. Read `docs/tasks/` to pick a task (check `depends_on` before starting). +1. `docops state` (or read `docs/STATE.md`). +2. `docops audit` for open gaps. +3. Pick a task from `docs/tasks/`; check `depends_on`. 4. Before coding: read every doc in the task's `requires:` and `depends_on:`. -5. Work in your native plan/execute mode. DocOps does not prescribe how you code. -6. After finishing, update the task's frontmatter `status:` field and run `docops refresh` to regenerate index and STATE.md. -7. If your work revealed a new decision, `docops new adr`. If a new gap, `docops new task` with citations. +5. Work in your native plan/execute mode. +6. After: update the task's `status:` and run `docops refresh`. +7. New decision → `docops new adr`. New gap → `docops new task` with citations. ## Editor integration -JSON Schemas for frontmatter validation live in `docs/.docops/schema/` (`context.schema.json`, `decision.schema.json`, `task.schema.json`). Wire them up in VS Code via the `redhat.vscode-yaml` extension and run `docops schema` to regenerate after editing `context_types:` in `docops.yaml`. - -## Pairs well with - -- **GStack** role skills — they apply perspective; DocOps provides the typed state they read. -- **Native plan mode** of your IDE — DocOps explicitly does not try to replace it. +JSON Schemas live in `docs/.docops/schema/`. In VS Code, use `redhat.vscode-yaml`. Regenerate after editing `context_types:` in `docops.yaml`: `docops schema`. -## Notes for humans - -The block above is auto-maintained by `docops init` and `docops upgrade`. -Edit content outside the `` delimiters freely; DocOps will -preserve it. Edits inside the delimiters will be overwritten on the next -upgrade. The same docops block also appears in `AGENTS.md` (the multi-tool -sibling); both refresh from the same shipped template. +> Content inside the `` markers is regenerated by `docops upgrade`. Edit freely outside. diff --git a/cmd/docops/cmd_init.go b/cmd/docops/cmd_init.go index 599047c..d780db0 100644 --- a/cmd/docops/cmd_init.go +++ b/cmd/docops/cmd_init.go @@ -28,7 +28,7 @@ func cmdInit(args []string) int { fs.SetOutput(os.Stderr) dryRun := fs.Bool("dry-run", false, "print the planned changes without writing") force := fs.Bool("force", false, "overwrite files that have drifted from the shipped templates") - noSkills := fs.Bool("no-skills", false, "skip scaffolding .claude/skills/docops/ and .cursor/commands/docops/") + noSkills := fs.Bool("no-skills", false, "skip scaffolding .claude/commands/docops/ and .cursor/commands/docops/") yes := fs.Bool("yes", false, "skip the interactive confirm prompt") fs.BoolVar(yes, "y", false, "skip the interactive confirm prompt (short form)") fs.Usage = func() { diff --git a/cmd/docops/cmd_upgrade.go b/cmd/docops/cmd_upgrade.go index 0084c00..dc4f6ec 100644 --- a/cmd/docops/cmd_upgrade.go +++ b/cmd/docops/cmd_upgrade.go @@ -141,7 +141,8 @@ func reportUpgradeError(err error) int { func printUpgradeHeader(w io.Writer, cwd string, optConfig, optHook bool) { fmt.Fprintf(w, "docops upgrade will refresh DocOps-owned scaffolding in %s:\n", cwd) - fmt.Fprintln(w, " - .claude/skills/docops/* and .cursor/commands/docops/* (replaced/removed to match the shipped bundle)") + fmt.Fprintln(w, " - .claude/commands/docops/* and .cursor/commands/docops/* (replaced/removed to match the shipped bundle)") + fmt.Fprintln(w, " - .claude/skills/docops/* (legacy location — files removed; Claude Code reads slash commands from .claude/commands/)") fmt.Fprintln(w, " - docs/.docops/schema/*.schema.json (regenerated from docops.yaml)") fmt.Fprintln(w, " - The block in AGENTS.md and CLAUDE.md (refreshed in place; either file is created if absent)") if optConfig { diff --git a/cmd/docops/cmd_upgrade_test.go b/cmd/docops/cmd_upgrade_test.go index cbe3e8b..be364c0 100644 --- a/cmd/docops/cmd_upgrade_test.go +++ b/cmd/docops/cmd_upgrade_test.go @@ -31,7 +31,7 @@ func upgradeFixture(t *testing.T) string { if err != nil { t.Fatalf("LoadShippedSkills: %v", err) } - for _, dir := range []string{".claude/skills/docops", ".cursor/commands/docops"} { + for _, dir := range []string{".claude/commands/docops", ".cursor/commands/docops"} { if err := os.MkdirAll(filepath.Join(root, dir), 0o755); err != nil { t.Fatalf("mkdir: %v", err) } @@ -67,7 +67,7 @@ func TestCmdUpgrade_RefusesWithoutDocopsYAML(t *testing.T) { func TestCmdUpgrade_DryRunWritesNothing(t *testing.T) { root := upgradeFixture(t) - stale := filepath.Join(root, ".claude/skills/docops/next.md") + stale := filepath.Join(root, ".claude/commands/docops/old-command.md") if err := os.WriteFile(stale, []byte("stale\n"), 0o644); err != nil { t.Fatalf("seed stale: %v", err) } @@ -128,7 +128,7 @@ func TestCmdUpgrade_JSONShape(t *testing.T) { func TestCmdUpgrade_YesFlagSkipsPromptAndApplies(t *testing.T) { root := upgradeFixture(t) - stale := filepath.Join(root, ".claude/skills/docops/next.md") + stale := filepath.Join(root, ".claude/commands/docops/old-command.md") if err := os.WriteFile(stale, []byte("stale\n"), 0o644); err != nil { t.Fatalf("seed stale: %v", err) } diff --git a/docs/.docops/counters.json b/docs/.docops/counters.json index b720a26..319bb11 100644 --- a/docs/.docops/counters.json +++ b/docs/.docops/counters.json @@ -1,8 +1,8 @@ { "version": 1, "next": { - "ADR": 26, + "ADR": 27, "CTX": 5, - "TP": 29 + "TP": 30 } } diff --git a/docs/.index.json b/docs/.index.json index 61d7746..8d76f89 100644 --- a/docs/.index.json +++ b/docs/.index.json @@ -1,5 +1,5 @@ { - "generated_at": "2026-04-23T08:41:52Z", + "generated_at": "2026-04-23T17:40:34Z", "version": 1, "docs": [ { @@ -360,6 +360,10 @@ "id": "ADR-0010", "edge": "related" }, + { + "id": "ADR-0026", + "edge": "related" + }, { "id": "TP-005", "edge": "requires" @@ -438,6 +442,10 @@ "id": "ADR-0005", "edge": "related" }, + { + "id": "ADR-0026", + "edge": "related" + }, { "id": "TP-004", "edge": "requires" @@ -624,7 +632,7 @@ "edge": "requires" } ], - "implementation": "partial", + "implementation": "done", "stale": false }, { @@ -829,6 +837,10 @@ "id": "ADR-0025", "edge": "related" }, + { + "id": "ADR-0026", + "edge": "related" + }, { "id": "TP-012", "edge": "requires" @@ -923,7 +935,7 @@ "summary": "First-user testing of v0.1.0 on a real project (`lw-website-nextjs-web`) surfaced three distinct friction points that are not bugs but are design regressions relative to what a polished CLI would do:", "word_count": 1119, "last_touched": "2026-04-22T17:31:24Z", - "age_days": 0, + "age_days": 1, "referenced_by": [ { "id": "ADR-0021", @@ -1031,7 +1043,7 @@ "folder": "docs/decisions", "path": "docs/decisions/ADR-0023-update-check-cached-lazy-opt-out-version-probe.md", "title": "Update-check — cached, lazy, opt-out version probe", - "status": "draft", + "status": "accepted", "coverage": "required", "date": "2026-04-23", "related": [ @@ -1040,7 +1052,7 @@ ], "summary": "DocOps ships through Homebrew, Scoop, and direct binary downloads. Once a user runs `docops init`, their project files (skills, schemas, AGENTS.md block) drift from upstream every time we cut a releas…", "word_count": 909, - "last_touched": "2026-04-22T19:48:15Z", + "last_touched": "2026-04-23T17:24:44Z", "age_days": 0, "referenced_by": [ { @@ -1069,7 +1081,7 @@ "folder": "docs/decisions", "path": "docs/decisions/ADR-0024-ship-claude-md-alongside-agents-md-both-share-the.md", "title": "Ship CLAUDE.md alongside AGENTS.md — both share the docops block", - "status": "draft", + "status": "accepted", "coverage": "required", "date": "2026-04-23", "related": [ @@ -1078,7 +1090,7 @@ ], "summary": "DocOps writes its invariants (citation rules, no-edit-STATE.md, schema locations, etc.) into a delimited block in `AGENTS.md` so any coding agent — Claude Code, Cursor, Codex, Aider, Copilot, Windsurf…", "word_count": 555, - "last_touched": "2026-04-23T03:32:42Z", + "last_touched": "2026-04-23T17:24:44Z", "age_days": 0, "referenced_by": [ { @@ -1119,6 +1131,37 @@ "implementation": "not-started", "stale": false }, + { + "id": "ADR-0026", + "kind": "ADR", + "folder": "docs/decisions", + "path": "docs/decisions/ADR-0026-adr-implementation-delta-review-verify-shipped-cod.md", + "title": "ADR implementation delta review — verify shipped code matches decision", + "status": "draft", + "coverage": "required", + "date": "2026-04-23", + "related": [ + "ADR-0010", + "ADR-0008", + "ADR-0018" + ], + "summary": "DocOps derives ADR `implementation` state from citing tasks (per ADR-0010): once every citing task is `done`, implementation flips to `done`. But \"all tasks done\" is not the same as \"the ADR's Decisio…", + "word_count": 401, + "last_touched": "2026-04-23T17:31:41Z", + "age_days": 0, + "referenced_by": [ + { + "id": "TP-023", + "edge": "requires" + }, + { + "id": "TP-029", + "edge": "requires" + } + ], + "implementation": "partial", + "stale": false + }, { "id": "CTX-001", "kind": "CTX", @@ -1447,7 +1490,7 @@ "summary": "Command that turns a bare repository into a DocOps-enabled one. Idempotent — safe to run twice.", "word_count": 222, "last_touched": "2026-04-22T09:33:17Z", - "age_days": 0, + "age_days": 1, "referenced_by": [ { "id": "TP-010", @@ -1499,7 +1542,7 @@ "summary": "Commands that create new CTX, ADR, or Task documents with valid frontmatter, atomic ID allocation, and correct file naming.", "word_count": 180, "last_touched": "2026-04-22T09:42:49Z", - "age_days": 0, + "age_days": 1, "referenced_by": [ { "id": "TP-010", @@ -1534,7 +1577,7 @@ "summary": "Emit the frontmatter schemas as JSON Schema so editors (VS Code YAML plugin, JetBrains, Zed) can validate in-editor without running the CLI.", "word_count": 124, "last_touched": "2026-04-22T09:51:55Z", - "age_days": 0, + "age_days": 1, "referenced_by": [ { "id": "TP-018", @@ -1567,7 +1610,7 @@ "summary": "Produce the first version of the DocOps skill pack — markdown files that wrap CLI commands for Claude Code and Cursor so agents get discoverable slash-commands.", "word_count": 246, "last_touched": "2026-04-22T12:35:26Z", - "age_days": 0, + "age_days": 1, "stale": false }, { @@ -1636,7 +1679,7 @@ "summary": "ADR-0019 rescopes the skill-pack work: init-based distribution is the single path for v0.1.0; the `packages/` standalone bundles are deferred. This task closes the remaining gap items from TP-010 that…", "word_count": 301, "last_touched": "2026-04-22T12:35:26Z", - "age_days": 0, + "age_days": 1, "referenced_by": [ { "id": "TP-014", @@ -1673,7 +1716,7 @@ "summary": "Migrate the module path, add the license, tidy the release pipeline, and cut the first public tag. End state: `github.com/logicwind/DocOps` exists with `v0.1.0` tagged and goreleaser-produced archives…", "word_count": 324, "last_touched": "2026-04-22T12:39:07Z", - "age_days": 0, + "age_days": 1, "stale": false }, { @@ -1694,7 +1737,7 @@ "summary": "Fix the three ergonomic gaps in `docops init` surfaced by v0.1.0 first-user testing: no way to target a different folder, no explanation of what init will do, no confirmation before writing.", "word_count": 402, "last_touched": "2026-04-22T17:38:31Z", - "age_days": 0, + "age_days": 1, "stale": false }, { @@ -1877,17 +1920,18 @@ "kind": "TP", "folder": "docs/tasks", "path": "docs/tasks/TP-023-restore-docops-skills-for-next-search-get-list-gra.md", - "title": "Restore /docops:* skills for next, search, get, list, graph commands", - "task_status": "backlog", - "priority": "p2", - "assignee": "unassigned", + "title": "Restore /docops:* skills for read commands + add orchestration skills", + "task_status": "done", + "priority": "p1", + "assignee": "claude", "requires": [ "ADR-0013", - "ADR-0019" + "ADR-0019", + "ADR-0026" ], "summary": "Re-add shipped agent skills for the five CLI commands that landed in TP-011 (`search`) and TP-012 (`get`, `list`, `graph`, `next`) so the bundle satisfies TP-010/013's policy: a skill exists in `templ…", - "word_count": 346, - "last_touched": "2026-04-23T04:27:42Z", + "word_count": 715, + "last_touched": "2026-04-23T17:37:46Z", "age_days": 0, "stale": false }, @@ -1997,7 +2041,25 @@ ], "summary": "Make `make release VERSION=X.Y.Z DRY_RUN=1` actually be a dry run. Today it prints the \"would do\" lines and then runs the real `echo \u003e VERSION` / `git commit` / `git tag` / `git push` commands anyway.", "word_count": 425, - "last_touched": "2026-04-23T08:41:43Z", + "last_touched": "2026-04-23T08:42:57Z", + "age_days": 0, + "stale": false + }, + { + "id": "TP-029", + "kind": "TP", + "folder": "docs/tasks", + "path": "docs/tasks/TP-029-implement-docops-review-reviewed-at-adr-field-stal.md", + "title": "Implement docops review — reviewed_at ADR field + stale-review detection", + "task_status": "backlog", + "priority": "p2", + "assignee": "unassigned", + "requires": [ + "ADR-0026" + ], + "summary": "Ship the CLI + schema work defined in ADR-0026 so that ADRs whose implementation has flipped to `done` can be review-marked against the shipped code.", + "word_count": 295, + "last_touched": "2026-04-23T17:31:41Z", "age_days": 0, "stale": false } diff --git a/docs/STATE.md b/docs/STATE.md index 681a1d2..b3eecef 100644 --- a/docs/STATE.md +++ b/docs/STATE.md @@ -5,8 +5,8 @@ ## Counts - Context: 4 active · 0 superseded -- ADRs: 22 accepted · 3 draft · 0 superseded (24 `coverage: required`, 1 `coverage: not-needed`) -- Tasks: 6 backlog · 1 active · 0 blocked · 21 done +- ADRs: 24 accepted · 2 draft · 0 superseded (25 `coverage: required`, 1 `coverage: not-needed`) +- Tasks: 6 backlog · 1 active · 0 blocked · 22 done ## Needs attention @@ -18,7 +18,11 @@ ## Recent activity +- 2026-04-23 e59940b planning: ADR-0026 review-delta + TP-029, expand TP-023 scope +- 2026-04-23 d811d75 docs(adr): promote ADR-0023 and ADR-0024 to accepted +- 2026-04-23 d0cbd50 fix(release): pass HOMEBREW_TAP_GITHUB_TOKEN explicitly to tap/bucket publishers - 2026-04-23 c038a01 chore: mark TP-018/020/021/022 done; ignore /docops smoke binary +- 2026-04-23 afcd855 TP-023 phase 1: ship /docops:{next,get,list,graph,search} skills - 2026-04-23 aeb53b3 chore: enable tap/bucket auto-publish for v0.2.1 - 2026-04-23 a131d95 planning: ADR-0024 + TP-022 — ship CLAUDE.md alongside AGENTS.md - 2026-04-23 7656d08 planning: ADR-0023 + TP-020/TP-021 — split TP-018 into refactor + upgrader + update-check @@ -34,8 +38,4 @@ - 2026-04-22 dafd8eb TP-006: docops audit — structural gap punch list - 2026-04-22 d2ba125 TP-016: docops refresh — composite validate+index+state - 2026-04-22 ca20529 TP-015: docops init — [dir], announcement, TTY-interactive confirm -- 2026-04-22 8a175ae TP-014: close task, refresh committed artifacts -- 2026-04-22 84fb540 planning: ADR-0020 + TP-015/TP-016/TP-017 for v0.1.1 ergonomics -- 2026-04-22 73cd5db TP-010/TP-013: finalize skill-pack scope — --no-skills + skills lint -- 2026-04-22 6b41ceb TP-008: docops new — scaffold CTX/ADR/Task with atomic ID allocation diff --git a/docs/decisions/ADR-0023-update-check-cached-lazy-opt-out-version-probe.md b/docs/decisions/ADR-0023-update-check-cached-lazy-opt-out-version-probe.md index 6b6dd1f..65d3d56 100644 --- a/docs/decisions/ADR-0023-update-check-cached-lazy-opt-out-version-probe.md +++ b/docs/decisions/ADR-0023-update-check-cached-lazy-opt-out-version-probe.md @@ -1,6 +1,6 @@ --- title: Update-check — cached, lazy, opt-out version probe -status: draft +status: accepted coverage: required date: "2026-04-23" supersedes: [] diff --git a/docs/decisions/ADR-0024-ship-claude-md-alongside-agents-md-both-share-the.md b/docs/decisions/ADR-0024-ship-claude-md-alongside-agents-md-both-share-the.md index 19819d4..87cd58f 100644 --- a/docs/decisions/ADR-0024-ship-claude-md-alongside-agents-md-both-share-the.md +++ b/docs/decisions/ADR-0024-ship-claude-md-alongside-agents-md-both-share-the.md @@ -1,6 +1,6 @@ --- title: Ship CLAUDE.md alongside AGENTS.md — both share the docops block -status: draft +status: accepted coverage: required date: "2026-04-23" supersedes: [] diff --git a/docs/decisions/ADR-0026-adr-implementation-delta-review-verify-shipped-cod.md b/docs/decisions/ADR-0026-adr-implementation-delta-review-verify-shipped-cod.md new file mode 100644 index 0000000..b87312f --- /dev/null +++ b/docs/decisions/ADR-0026-adr-implementation-delta-review-verify-shipped-cod.md @@ -0,0 +1,71 @@ +--- +title: ADR implementation delta review — verify shipped code matches decision +status: draft +coverage: required +date: "2026-04-23" +supersedes: [] +related: [ADR-0010, ADR-0008, ADR-0018] +tags: [] +--- + +## Context + +DocOps derives ADR `implementation` state from citing tasks (per ADR-0010): +once every citing task is `done`, implementation flips to `done`. But "all +tasks done" is not the same as "the ADR's Decision and Consequences are +actually encoded in the shipped code." A task can ship something adjacent, +a Consequence can drift over subsequent refactors, or a Rationale can be +invalidated by downstream work. Without a review gate, ADRs accumulate as +aging contracts that nobody audits against reality. + +Today the only signal we have is `implementation=done` from ADR-0010. +There is no record of *who verified that the code matches the decision, +or when*. + +## Decision + +Add an optional source frontmatter field to ADRs: + +```yaml +reviewed_at: YYYY-MM-DD # absent = never reviewed +``` + +Ship a new CLI command `docops review`: + +- `docops review` (no args) — list ADRs with stale review: + `implementation == done` AND (`reviewed_at` missing OR `reviewed_at` < + newest commit touching any file referenced by the ADR's citing tasks). + Supports `--json`. +- `docops review ` — print the ADR body plus recent commits + touching files referenced by its citing tasks. The agent (or human) + reads this and decides whether code matches decision. +- `docops review --mark` — stamp today's date into `reviewed_at` + via atomic frontmatter rewrite (not hand-editing). + +Surface a `stale_review` count in `docs/STATE.md` next to other +needs-attention items, so the review backlog shows up in orientation. + +## Rationale + +- The computed `implementation` state (ADR-0010) is good enough to + *trigger* a review, but the review itself is human+agent judgment — + reading shipped code against a decision — so the sign-off belongs in + source, not index. Keeping it as a source field means we don't try to + infer "reviewed" from something the filesystem can't tell us. +- A dedicated command keeps this out of band: don't force review on + every touch; let the user (or an agent running `docops review`) close + the loop when they want to. +- Matches the CLI-as-query-API posture (ADR-0011, ADR-0018). No new data + model primitives, just a new field and a new verb. + +## Consequences + +- +1 optional frontmatter field (backwards compatible via schema + `required`/`additionalProperties`). +- +1 CLI surface; the stale-detection algorithm needs git-awareness + (already available elsewhere — `last_touched` is computed from git). +- Gives us a lightweight mechanism to catch ADR/code drift without + heavyweight review meetings. +- Opens a follow-on: integrate `docops review` into the agent + orchestration skills (see TP-023 phase 2) so agents can prompt for + review when implementation flips to done. diff --git a/docs/tasks/TP-023-restore-docops-skills-for-next-search-get-list-gra.md b/docs/tasks/TP-023-restore-docops-skills-for-next-search-get-list-gra.md index 0e05293..05aa844 100644 --- a/docs/tasks/TP-023-restore-docops-skills-for-next-search-get-list-gra.md +++ b/docs/tasks/TP-023-restore-docops-skills-for-next-search-get-list-gra.md @@ -1,13 +1,13 @@ --- -title: Restore /docops:* skills for next, search, get, list, graph commands -status: backlog -priority: p2 -assignee: unassigned -requires: [ADR-0013, ADR-0019] +title: Restore /docops:* skills for read commands + add orchestration skills +status: done +priority: p1 +assignee: claude +requires: [ADR-0013, ADR-0019, ADR-0026] depends_on: [] --- -# Restore /docops:* skills for next, search, get, list, graph commands +# Restore /docops:* skills for read commands + add orchestration skills ## Goal @@ -58,3 +58,53 @@ Patch release v0.2.1 once landed. - This task does not touch `AGENTS.md.tmpl` / `CLAUDE.md.tmpl` ("not yet built" list there) — those need a separate update once this lands. Consider folding that doc edit into the same PR. + (Partially addressed in commit 5d613ee — template verbosity trim + also removed the stale "not yet built" block. Still worth revisiting + once the five read-skills land.) + +## Phase 2 — orchestration skills (adds ADR-0026 citation) + +The read-command skills above are thin CLI wrappers. Separately ship a +small pack of *orchestration* skills that wrap multi-step DocOps +workflows — the pattern gstack/gsd use successfully (frontmatter points +at a workflow document; the skill is a slim shell that delegates). + +Orchestration skills to ship: + +- `/docops:progress` — read `docs/STATE.md` + `docops audit` + `docops + next`, summarize situation, recommend the next action. Equivalent of + `gsd-progress`. +- `/docops:do ` — NL intent router. Map "start a new decision" + → `/docops:new-adr`, "what's next" → `/docops:progress`, etc. + Equivalent of `gsd-do`. +- `/docops:plan` — given a CTX filename or ID, draft an ADR + one or + more tasks that cite it. Human-confirmed before write. Uses + `docops new adr --body -` and `docops new task --body -` under the + hood to avoid the stub-then-rewrite round-trip. +- `/docops:close` — given a task ID, update its status to `done`, run + `docops refresh`, and stage a commit with a templated message. Agent + fills in the "what changed" sentence. +- `/docops:review` — wraps `docops review` (see ADR-0026 / TP-029). For + each stale-review ADR: print ADR body + relevant commits, prompt the + agent to assess drift, offer `--mark` on confirmation. + +Acceptance for Phase 2: + +- Five new skill files under `templates/skills/docops/` with the same + frontmatter shape as existing skills (`name`, `description`, body). +- Skills are thin: bodies should read like "run `docops X`, interpret + output, prompt user on ambiguity." No embedded domain knowledge that + belongs in CLI help text. +- `templates/skills_lint_test.go` extended to cover the orchestration + skills' frontmatter. +- `docops upgrade` on a v0.2.x-scaffolded repo cleanly adds the five + new skill files (`+ new` lines only). +- `/docops:review` cannot ship until TP-029 lands. Track that edge as + an ordering constraint, not a blocker for the rest of Phase 2. + +## Phase ordering + +Phase 1 (read-command wrappers) unblocks agents immediately and is +self-contained. Phase 2 depends on TP-029 for `/docops:review` but the +other four orchestration skills can ship in parallel with or ahead of +TP-029. Split into two PRs if it helps keep the diff reviewable. diff --git a/docs/tasks/TP-029-implement-docops-review-reviewed-at-adr-field-stal.md b/docs/tasks/TP-029-implement-docops-review-reviewed-at-adr-field-stal.md new file mode 100644 index 0000000..609e9ee --- /dev/null +++ b/docs/tasks/TP-029-implement-docops-review-reviewed-at-adr-field-stal.md @@ -0,0 +1,53 @@ +--- +title: Implement docops review — reviewed_at ADR field + stale-review detection +status: backlog +priority: p2 +assignee: unassigned +requires: [ADR-0026] +depends_on: [] +--- + +## Goal + +Ship the CLI + schema work defined in ADR-0026 so that ADRs whose +implementation has flipped to `done` can be review-marked against the +shipped code. + +## Acceptance + +- `docs/.docops/schema/decision.schema.json` accepts an optional + `reviewed_at` field of format `date` (YYYY-MM-DD). Regenerated from + `docops.yaml` via `docops schema`. +- `docops review` (no args) lists ADRs where + `implementation == done` AND (`reviewed_at` missing OR + `reviewed_at < max(commit_date)` for commits touching files + referenced by the ADR's citing tasks). Supports `--json`. +- `docops review ` prints the ADR body plus the last N commits + (default 20, configurable via `--since` or `--limit`) touching files + referenced by its citing tasks. Commits must include subject + hash + + date; file list optional behind a flag to keep default output short. +- `docops review --mark` writes today's date into + `reviewed_at` via atomic frontmatter rewrite (reuse the same write + path as `docops new`). Idempotent: re-running same day is a no-op. +- `docs/STATE.md` surfaces a `stale_review` row in the + needs-attention table when the count is >0. Zero count stays silent. +- Tests: + - Schema acceptance of `reviewed_at` as optional date. + - Stale detection: ADR with `implementation=done`, `reviewed_at` + older than a citing-task commit → flagged. Newer `reviewed_at` → + not flagged. + - `--mark` idempotency on same-day runs. + - STATE.md row emitted iff count > 0. + +## Notes + +- Detecting "files referenced by citing tasks" needs a convention. The + simplest: scan task body for filesystem-looking strings + (`internal/foo.go`, `templates/...`) and use those as the git-log + path filter. Fall back to "any commit since the last citing-task + done-date" when no paths resolve. This heuristic can be tightened + later. +- Do not add `reviewed_at` to CTX or TP — review-delta is ADR-specific. +- Keep command output stable enough to diff in tests; no color codes + when stdout is not a TTY. + diff --git a/internal/initter/initter.go b/internal/initter/initter.go index 90083d2..0c37121 100644 --- a/internal/initter/initter.go +++ b/internal/initter/initter.go @@ -25,7 +25,7 @@ type Options struct { Root string DryRun bool Force bool - NoSkills bool // skip scaffolding .claude/skills/docops/ and .cursor/commands/docops/ + NoSkills bool // skip scaffolding .claude/commands/docops/ and .cursor/commands/docops/ Out io.Writer // human-readable progress; defaults to os.Stdout Verbose bool } @@ -151,7 +151,7 @@ func plan(opts Options) ([]Action, error) { } actions = append(actions, hookAction) - // 6. Agent skills — .claude/skills/docops/ and .cursor/commands/docops/. + // 6. Agent skills — .claude/commands/docops/ and .cursor/commands/docops/. // Skipped entirely when --no-skills is set; existing files are not touched. if !opts.NoSkills { skills, err := scaffold.LoadShippedSkills() @@ -163,7 +163,7 @@ func plan(opts Options) ([]Action, error) { skillNames = append(skillNames, name) } sort.Strings(skillNames) - for _, dir := range []string{".claude/skills/docops", ".cursor/commands/docops"} { + for _, dir := range []string{".claude/commands/docops", ".cursor/commands/docops"} { actions = append(actions, scaffold.DirAction(opts.Root, dir)) for _, name := range skillNames { rel := filepath.Join(dir, name) diff --git a/internal/initter/initter_test.go b/internal/initter/initter_test.go index 44385d3..c945ca8 100644 --- a/internal/initter/initter_test.go +++ b/internal/initter/initter_test.go @@ -41,9 +41,9 @@ func TestRun_BareRepo_CreatesAllArtifacts(t *testing.T) { "docops.yaml", "AGENTS.md", ".git/hooks/pre-commit", - ".claude/skills/docops/init.md", - ".claude/skills/docops/state.md", - ".claude/skills/docops/new-ctx.md", + ".claude/commands/docops/init.md", + ".claude/commands/docops/state.md", + ".claude/commands/docops/new-ctx.md", ".cursor/commands/docops/init.md", } { if _, err := os.Stat(filepath.Join(root, rel)); err != nil { @@ -301,7 +301,7 @@ func TestRun_UsesProjectContextTypes(t *testing.T) { } // TestRun_NoSkills_SkipsSkillDirs verifies that --no-skills prevents -// creation of .claude/skills/docops/ and .cursor/commands/docops/. +// creation of .claude/commands/docops/ and .cursor/commands/docops/. func TestRun_NoSkills_SkipsSkillDirs(t *testing.T) { root := t.TempDir() withGit(t, root) @@ -313,7 +313,7 @@ func TestRun_NoSkills_SkipsSkillDirs(t *testing.T) { // Neither skill directory nor any skill file should be created. for _, rel := range []string{ - ".claude/skills/docops", + ".claude/commands/docops", ".cursor/commands/docops", } { abs := filepath.Join(root, rel) diff --git a/internal/upgrader/upgrader.go b/internal/upgrader/upgrader.go index 42388ea..b435499 100644 --- a/internal/upgrader/upgrader.go +++ b/internal/upgrader/upgrader.go @@ -114,7 +114,15 @@ func Run(opts Options) (*Result, error) { // system might extend this; for now it is fixed to the two // agent-tool conventions docops init scaffolds. func docopsSkillDirs() []string { - return []string{".claude/skills/docops", ".cursor/commands/docops"} + return []string{".claude/commands/docops", ".cursor/commands/docops"} +} + +// legacyDocopsSkillDirs lists paths that earlier docops releases wrote +// skill files into and that upgrade should actively clean up. Claude +// Code reads slash commands from .claude/commands/, not .claude/skills/, +// so v0.2.x wrote to the wrong folder; v0.3.x moves them. +func legacyDocopsSkillDirs() []string { + return []string{".claude/skills/docops"} } // shippedSkillNames returns the basenames of skill files currently @@ -159,6 +167,34 @@ func plan(opts Options) ([]scaffold.Action, error) { var actions []scaffold.Action + // 0. Legacy cleanup — previous docops versions wrote Claude Code + // slash commands into .claude/skills/docops/, but Claude Code reads + // them from .claude/commands/. Remove every file still sitting in + // the old location so users do not end up with duplicate skill + // files after the bundle migrates to the new folder. + for _, legacyDir := range legacyDocopsSkillDirs() { + abs := filepath.Join(opts.Root, legacyDir) + entries, err := os.ReadDir(abs) + if err != nil { + if os.IsNotExist(err) { + continue + } + return nil, fmt.Errorf("read legacy skill dir %s: %w", legacyDir, err) + } + for _, entry := range entries { + if entry.IsDir() { + continue + } + rel := filepath.Join(legacyDir, entry.Name()) + actions = append(actions, scaffold.Action{ + Path: filepath.Join(opts.Root, rel), + Rel: rel, + Kind: scaffold.KindRemove, + Reason: "legacy location — moved to .claude/commands/docops/", + }) + } + } + // 1. JSON Schemas — always refreshed (docops-owned). schemas, err := schema.JSONSchemas(schema.Config{ContextTypes: cfg.ContextTypes}) if err != nil { diff --git a/internal/upgrader/upgrader_test.go b/internal/upgrader/upgrader_test.go index f669ed0..43fecce 100644 --- a/internal/upgrader/upgrader_test.go +++ b/internal/upgrader/upgrader_test.go @@ -37,7 +37,7 @@ func initted(t *testing.T) string { if err != nil { t.Fatalf("LoadShippedSkills: %v", err) } - for _, dir := range []string{".claude/skills/docops", ".cursor/commands/docops"} { + for _, dir := range []string{".claude/commands/docops", ".cursor/commands/docops"} { if err := os.MkdirAll(filepath.Join(root, dir), 0o755); err != nil { t.Fatalf("mkdir %s: %v", dir, err) } @@ -94,7 +94,7 @@ func TestRun_IdempotentOnFreshlyInittedProject(t *testing.T) { // don't seed them here, so a fresh run will emit creates for // the schema files — that's fine. Skill files are seeded // byte-identically and should be skips. - if strings.HasPrefix(a.Rel, ".claude/skills/docops/") && a.Kind != scaffold.KindMkdir && a.Kind != scaffold.KindSkip { + if strings.HasPrefix(a.Rel, ".claude/commands/docops/") && a.Kind != scaffold.KindMkdir && a.Kind != scaffold.KindSkip { t.Errorf("seeded skill should be skip; %s = %s", a.Rel, a.Kind) } } @@ -102,7 +102,7 @@ func TestRun_IdempotentOnFreshlyInittedProject(t *testing.T) { func TestRun_AddsNewSkillRemovesStaleRefreshesChanged(t *testing.T) { root := initted(t) - dir := filepath.Join(root, ".claude/skills/docops") + dir := filepath.Join(root, ".claude/commands/docops") // Simulate v0.1.0-era state in the dir: // - delete one shipped skill so the upgrader will (re)create it. @@ -116,7 +116,7 @@ func TestRun_AddsNewSkillRemovesStaleRefreshesChanged(t *testing.T) { if err := os.WriteFile(filepath.Join(dir, pickRefresh), []byte("stale local body\n"), 0o644); err != nil { t.Fatalf("mutate %s: %v", pickRefresh, err) } - if err := os.WriteFile(filepath.Join(dir, "next.md"), []byte("removed-upstream skill\n"), 0o644); err != nil { + if err := os.WriteFile(filepath.Join(dir, "old-command.md"), []byte("removed-upstream skill\n"), 0o644); err != nil { t.Fatalf("seed stale skill: %v", err) } @@ -125,26 +125,26 @@ func TestRun_AddsNewSkillRemovesStaleRefreshesChanged(t *testing.T) { t.Fatalf("Run: %v", err) } - addAction := findAction(res.Actions, ".claude/skills/docops/"+pickAdd) + addAction := findAction(res.Actions, ".claude/commands/docops/"+pickAdd) if addAction == nil || addAction.Kind != scaffold.KindWriteFile || addAction.Reason != "create" { t.Errorf("%s should be a create action; got %+v", pickAdd, addAction) } - refreshAction := findAction(res.Actions, ".claude/skills/docops/"+pickRefresh) + refreshAction := findAction(res.Actions, ".claude/commands/docops/"+pickRefresh) if refreshAction == nil || refreshAction.Kind != scaffold.KindWriteFile || refreshAction.Reason == "create" { t.Errorf("%s should be a refresh (overwrite) action; got %+v", pickRefresh, refreshAction) } - removeAction := findAction(res.Actions, ".claude/skills/docops/next.md") + removeAction := findAction(res.Actions, ".claude/commands/docops/old-command.md") if removeAction == nil || removeAction.Kind != scaffold.KindRemove { - t.Errorf("next.md should be a remove action; got %+v", removeAction) + t.Errorf("old-command.md should be a remove action; got %+v", removeAction) } } func TestRun_ApplyWritesFilesAndDeletesStale(t *testing.T) { root := initted(t) - dir := filepath.Join(root, ".claude/skills/docops") - staleName := "next.md" + dir := filepath.Join(root, ".claude/commands/docops") + staleName := "old-command.md" if err := os.WriteFile(filepath.Join(dir, staleName), []byte("stale\n"), 0o644); err != nil { t.Fatalf("seed stale: %v", err) } @@ -171,6 +171,36 @@ func TestRun_ApplyWritesFilesAndDeletesStale(t *testing.T) { } } +// TestRun_MigratesLegacyClaudeSkillsFolder verifies that an upgrade +// against a repo scaffolded by an older docops (which wrote slash +// commands into .claude/skills/docops/) removes those files so the +// agent doesn't see the same slash command from two locations. +func TestRun_MigratesLegacyClaudeSkillsFolder(t *testing.T) { + root := initted(t) + legacyDir := filepath.Join(root, ".claude/skills/docops") + if err := os.MkdirAll(legacyDir, 0o755); err != nil { + t.Fatalf("mkdir legacy: %v", err) + } + // Seed two arbitrary filenames — upgrade should delete anything + // sitting in the legacy dir, not just files matching the current + // bundle (the old bundle shape is irrelevant by design). + for _, name := range []string{"init.md", "historic-command.md"} { + if err := os.WriteFile(filepath.Join(legacyDir, name), []byte("legacy\n"), 0o644); err != nil { + t.Fatalf("seed %s: %v", name, err) + } + } + + if _, err := Run(Options{Root: root, DryRun: false, Out: io.Discard}); err != nil { + t.Fatalf("Run: %v", err) + } + + for _, name := range []string{"init.md", "historic-command.md"} { + if _, err := os.Stat(filepath.Join(legacyDir, name)); !os.IsNotExist(err) { + t.Errorf("%s in legacy folder should be deleted; stat err = %v", name, err) + } + } +} + func TestRun_DoesNotTouchDocopsYAMLByDefault(t *testing.T) { root := initted(t) yamlPath := filepath.Join(root, "docops.yaml") @@ -238,7 +268,7 @@ func TestRun_DoesNotTouchPreCommitHookByDefault(t *testing.T) { func TestRun_FirstUpgradeDeletesUserFileInsideDocopsDir(t *testing.T) { root := initted(t) - dir := filepath.Join(root, ".claude/skills/docops") + dir := filepath.Join(root, ".claude/commands/docops") custom := filepath.Join(dir, "custom.md") if err := os.WriteFile(custom, []byte("user-added\n"), 0o644); err != nil { t.Fatalf("seed custom skill: %v", err) @@ -255,7 +285,7 @@ func TestRun_FirstUpgradeDeletesUserFileInsideDocopsDir(t *testing.T) { func TestRun_DoesNotTouchUserFileOneLevelUp(t *testing.T) { root := initted(t) - sibling := filepath.Join(root, ".claude/skills/my-stuff.md") + sibling := filepath.Join(root, ".claude/commands/my-stuff.md") if err := os.WriteFile(sibling, []byte("user content\n"), 0o644); err != nil { t.Fatalf("seed sibling: %v", err) } @@ -281,7 +311,7 @@ func TestRun_SecondUpgradeRefusesUserAddedFileInDocopsDir(t *testing.T) { } // User now drops a custom skill inside the docops-owned dir. - custom := filepath.Join(root, ".claude/skills/docops/custom.md") + custom := filepath.Join(root, ".claude/commands/docops/custom.md") if err := os.WriteFile(custom, []byte("user-added post-init\n"), 0o644); err != nil { t.Fatalf("seed custom: %v", err) } @@ -381,8 +411,8 @@ func TestRun_PreservesUserContentInClaudeMdAcrossUpgrade(t *testing.T) { func TestRun_DryRunWritesNothing(t *testing.T) { root := initted(t) - dir := filepath.Join(root, ".claude/skills/docops") - stalePath := filepath.Join(dir, "next.md") + dir := filepath.Join(root, ".claude/commands/docops") + stalePath := filepath.Join(dir, "old-command.md") if err := os.WriteFile(stalePath, []byte("stale\n"), 0o644); err != nil { t.Fatalf("seed: %v", err) } diff --git a/templates/AGENTS.md.tmpl b/templates/AGENTS.md.tmpl index 687a932..479c9d2 100644 --- a/templates/AGENTS.md.tmpl +++ b/templates/AGENTS.md.tmpl @@ -1,95 +1,78 @@ # Agents working in this repo -This repository is managed with **DocOps** — a typed project-state substrate -([open-source, by Logicwind](https://github.com/logicwind/DocOps)). -If you are an AI coding agent (Claude Code, Cursor, Aider, Codex, Copilot, -Windsurf, Zed, etc.), read this file before doing any work. +This repo uses **DocOps** ([logicwind/DocOps](https://github.com/logicwind/DocOps)) — a typed project-state substrate. If you are an AI coding agent (Claude Code, Cursor, Aider, Codex, Copilot, Windsurf, Zed, etc.), read this before doing any work. ## Orientation -- **Why we're building this:** `docs/context/CTX-*` (look for documents of `type: brief` or `type: prd`). -- **What's been decided:** `docs/decisions/ADR-*.md`. Frontmatter is load-bearing — read it. -- **What to do next:** `docs/STATE.md` for counts, needs-attention, and active work. -- **Hard guardrails:** any CTX of `type: memo` or `type: notes` that describes constraints. +- **Why:** `docs/context/CTX-*` (types `brief`, `prd`). +- **Decided:** `docs/decisions/ADR-*.md`. Frontmatter is load-bearing. +- **Next:** `docs/STATE.md` — counts, needs-attention, active work. +- **Guardrails:** any CTX of type `memo` or `notes`. ## Folder layout | Folder | Contents | |---|---| -| `docs/context/` | Stakeholder inputs: PRDs, design docs, memos, research, notes. File prefix `CTX-`. | -| `docs/decisions/` | ADRs. File prefix `ADR-`. | -| `docs/tasks/` | Work units. File prefix `TP-`. Every task cites ≥1 ADR or CTX. | -| `docs/STATE.md` | Auto-generated state snapshot. Do not edit by hand. | -| `docs/.index.json` | Auto-generated graph with computed reverse edges. Do not cat it into your context — use `docops state` or read individual doc files instead. | +| `docs/context/` | PRDs, design, memos, research, notes. Prefix `CTX-`. | +| `docs/decisions/` | ADRs. Prefix `ADR-`. | +| `docs/tasks/` | Work units. Prefix `TP-`. Each cites ≥1 ADR or CTX. | +| `docs/STATE.md` | Auto-generated. Don't edit. | +| `docs/.index.json` | Auto-generated graph. Don't edit, don't load wholesale — query via `docops` instead. | -## Invariants you MUST respect +## Invariants -1. **Every task must cite at least one ADR or CTX** in its `requires:` frontmatter. The validator refuses tasks without it. -2. **References must resolve.** No citing non-existent docs; no citing superseded docs without good reason. -3. **Do not edit `docs/STATE.md` or `docs/.index.json`** — both are regenerated from source. -4. **Do not edit reverse-edge fields** in source frontmatter. They are computed in the index. -5. **Filename is the ID.** `ADR-0020-whatever.md` is `ADR-0020`. Don't add an `id:` field. +1. Every task cites ≥1 ADR or CTX in `requires:`. Validator enforces. +2. References must resolve. No non-existent or dangling-superseded refs. +3. Don't edit `docs/STATE.md` or `docs/.index.json` — regenerated from source. +4. Don't edit reverse-edge fields in frontmatter — computed in the index. +5. Filename is the ID. `ADR-0020-whatever.md` is `ADR-0020`. No `id:` field. -## CLI commands — this is the query API +## CLI — the query and mutation API -### Shipped (all support `--json` for structured output) +All commands support `--json`. ``` -docops init # scaffold DocOps in this repo -docops upgrade # refresh DocOps-owned scaffolding after a binary upgrade -docops update-check # check upstream for a newer docops release (cached probe) -docops validate # schema + graph invariants -docops index # rebuild docs/.index.json -docops state # regenerate docs/STATE.md -docops audit # structural coverage gaps -docops refresh # validate + index + state in one pass -docops new ctx "title" --type memo -docops new adr "title" [--related ADR-xxxx] -docops new task "title" --requires ADR-xxxx,CTX-xxx -docops schema # (re)write schema JSON from docops.yaml +docops init scaffold DocOps in this repo +docops upgrade refresh DocOps-owned scaffolding +docops update-check probe upstream for a newer release (cached) +docops validate schema + graph invariants +docops index rebuild docs/.index.json +docops state regenerate docs/STATE.md +docops audit structural coverage gaps +docops refresh validate + index + state in one pass +docops schema (re)write docs/.docops/schema/*.schema.json + +docops list [--type ctx|adr|task] [--status ...] [--tag ...] +docops get look up one doc by ID +docops graph typed edge graph from a starting doc +docops next recommend the next task +docops search substring/regex over title, tags, body + +docops new ctx "title" --type memo [--body -|TEXT | --body-file PATH] +docops new adr "title" [--related ADR-xxxx] [--body -|TEXT | --body-file PATH] +docops new task "title" --requires ADR-xxxx,CTX-xxx [--body -|TEXT | --body-file PATH] ``` -### Not yet built — do not call these +**Populate bodies at creation.** `docops new` accepts `--body TEXT`, `--body -` (stdin heredoc), or `--body-file PATH`. Use them — they avoid the stub-then-rewrite round-trip. If you must edit an existing doc, read it first, then edit in place. -`status`, `get`, `list`, `graph`, `next`, `search`, `review` do not exist -yet. If your workflow needs one, propose a task first rather than inventing -flags or behaviour. +For lookups, prefer `docops list|get|search|graph|next` over loading `docs/.index.json` into context. -**For now, instead of `list`/`get`/`search`:** read `docs/STATE.md` for -counts and needs-attention, or read individual `docs/{context,decisions,tasks}/` -files directly. Avoid loading `docs/.index.json` wholesale — it is for -bootstrap / CI consumers, not routine agent queries. See -`docs/decisions/ADR-0018-cli-as-query-layer.md` for the rationale. +## Workflow -The binary is language-agnostic — install via direct download, Homebrew, Scoop, or Docker. No Node/Bun/Python dependency. - -## Recommended agent workflow - -1. Read `docs/STATE.md`. -2. Run `docops audit` to see open gaps. -3. Read `docs/tasks/` to pick a task (check `depends_on` before starting). +1. `docops state` (or read `docs/STATE.md`). +2. `docops audit` for open gaps. +3. Pick a task from `docs/tasks/`; check `depends_on`. 4. Before coding: read every doc in the task's `requires:` and `depends_on:`. -5. Work in your native plan/execute mode. DocOps does not prescribe how you code. -6. After finishing, update the task's frontmatter `status:` field and run `docops refresh` to regenerate index and STATE.md. -7. If your work revealed a new decision, `docops new adr`. If a new gap, `docops new task` with citations. +5. Work in your native plan/execute mode. +6. After: update the task's `status:` and run `docops refresh`. +7. New decision → `docops new adr`. New gap → `docops new task` with citations. ## Editor integration -JSON Schemas for frontmatter validation live in `docs/.docops/schema/` (`context.schema.json`, `decision.schema.json`, `task.schema.json`). Wire them up in VS Code via the `redhat.vscode-yaml` extension and run `docops schema` to regenerate after editing `context_types:` in `docops.yaml`. - -## Pairs well with - -- **GStack** role skills — they apply perspective; DocOps provides the typed state they read. -- **Native plan mode** of your IDE — DocOps explicitly does not try to replace it. +JSON Schemas live in `docs/.docops/schema/`. In VS Code, use `redhat.vscode-yaml`. Regenerate after editing `context_types:` in `docops.yaml`: `docops schema`. -## Notes for humans - -The block above is auto-maintained by `docops init` and `docops upgrade`. -Edit content outside the `` delimiters freely; DocOps will -preserve it. Edits inside the delimiters will be overwritten on the next -upgrade. The same docops block also appears in `CLAUDE.md` (which Claude -Code reads by default); both files refresh from the same shipped template. +> Content inside the `` markers is regenerated by `docops upgrade`. Edit freely outside. diff --git a/templates/CLAUDE.md.tmpl b/templates/CLAUDE.md.tmpl index b58ed99..2e08e21 100644 --- a/templates/CLAUDE.md.tmpl +++ b/templates/CLAUDE.md.tmpl @@ -1,97 +1,78 @@ # Claude in this repo -This repository is managed with **DocOps** — a typed project-state substrate -([open-source, by Logicwind](https://github.com/logicwind/DocOps)). -You're reading this because Claude Code loads CLAUDE.md by default. The same -docops block is mirrored in `AGENTS.md` for non-Claude agents (Cursor, Aider, -Codex, Copilot, Windsurf, Zed); both files are auto-maintained by -`docops upgrade`. +This repo uses **DocOps** ([logicwind/DocOps](https://github.com/logicwind/DocOps)) — a typed project-state substrate. Claude Code loads this file by default; the block below is mirrored byte-for-byte in `AGENTS.md` for other agents. ## Orientation -- **Why we're building this:** `docs/context/CTX-*` (look for documents of `type: brief` or `type: prd`). -- **What's been decided:** `docs/decisions/ADR-*.md`. Frontmatter is load-bearing — read it. -- **What to do next:** `docs/STATE.md` for counts, needs-attention, and active work. -- **Hard guardrails:** any CTX of `type: memo` or `type: notes` that describes constraints. +- **Why:** `docs/context/CTX-*` (types `brief`, `prd`). +- **Decided:** `docs/decisions/ADR-*.md`. Frontmatter is load-bearing. +- **Next:** `docs/STATE.md` — counts, needs-attention, active work. +- **Guardrails:** any CTX of type `memo` or `notes`. ## Folder layout | Folder | Contents | |---|---| -| `docs/context/` | Stakeholder inputs: PRDs, design docs, memos, research, notes. File prefix `CTX-`. | -| `docs/decisions/` | ADRs. File prefix `ADR-`. | -| `docs/tasks/` | Work units. File prefix `TP-`. Every task cites ≥1 ADR or CTX. | -| `docs/STATE.md` | Auto-generated state snapshot. Do not edit by hand. | -| `docs/.index.json` | Auto-generated graph with computed reverse edges. Do not cat it into your context — use `docops state` or read individual doc files instead. | +| `docs/context/` | PRDs, design, memos, research, notes. Prefix `CTX-`. | +| `docs/decisions/` | ADRs. Prefix `ADR-`. | +| `docs/tasks/` | Work units. Prefix `TP-`. Each cites ≥1 ADR or CTX. | +| `docs/STATE.md` | Auto-generated. Don't edit. | +| `docs/.index.json` | Auto-generated graph. Don't edit, don't load wholesale — query via `docops` instead. | -## Invariants you MUST respect +## Invariants -1. **Every task must cite at least one ADR or CTX** in its `requires:` frontmatter. The validator refuses tasks without it. -2. **References must resolve.** No citing non-existent docs; no citing superseded docs without good reason. -3. **Do not edit `docs/STATE.md` or `docs/.index.json`** — both are regenerated from source. -4. **Do not edit reverse-edge fields** in source frontmatter. They are computed in the index. -5. **Filename is the ID.** `ADR-0020-whatever.md` is `ADR-0020`. Don't add an `id:` field. +1. Every task cites ≥1 ADR or CTX in `requires:`. Validator enforces. +2. References must resolve. No non-existent or dangling-superseded refs. +3. Don't edit `docs/STATE.md` or `docs/.index.json` — regenerated from source. +4. Don't edit reverse-edge fields in frontmatter — computed in the index. +5. Filename is the ID. `ADR-0020-whatever.md` is `ADR-0020`. No `id:` field. -## CLI commands — this is the query API +## CLI — the query and mutation API -### Shipped (all support `--json` for structured output) +All commands support `--json`. ``` -docops init # scaffold DocOps in this repo -docops upgrade # refresh DocOps-owned scaffolding after a binary upgrade -docops update-check # check upstream for a newer docops release (cached probe) -docops validate # schema + graph invariants -docops index # rebuild docs/.index.json -docops state # regenerate docs/STATE.md -docops audit # structural coverage gaps -docops refresh # validate + index + state in one pass -docops new ctx "title" --type memo -docops new adr "title" [--related ADR-xxxx] -docops new task "title" --requires ADR-xxxx,CTX-xxx -docops schema # (re)write schema JSON from docops.yaml +docops init scaffold DocOps in this repo +docops upgrade refresh DocOps-owned scaffolding +docops update-check probe upstream for a newer release (cached) +docops validate schema + graph invariants +docops index rebuild docs/.index.json +docops state regenerate docs/STATE.md +docops audit structural coverage gaps +docops refresh validate + index + state in one pass +docops schema (re)write docs/.docops/schema/*.schema.json + +docops list [--type ctx|adr|task] [--status ...] [--tag ...] +docops get look up one doc by ID +docops graph typed edge graph from a starting doc +docops next recommend the next task +docops search substring/regex over title, tags, body + +docops new ctx "title" --type memo [--body -|TEXT | --body-file PATH] +docops new adr "title" [--related ADR-xxxx] [--body -|TEXT | --body-file PATH] +docops new task "title" --requires ADR-xxxx,CTX-xxx [--body -|TEXT | --body-file PATH] ``` -### Not yet built — do not call these +**Populate bodies at creation.** `docops new` accepts `--body TEXT`, `--body -` (stdin heredoc), or `--body-file PATH`. Use them — they avoid the stub-then-rewrite round-trip. If you must edit an existing doc, read it first, then edit in place. -`status`, `get`, `list`, `graph`, `next`, `search`, `review` do not exist -yet. If your workflow needs one, propose a task first rather than inventing -flags or behaviour. +For lookups, prefer `docops list|get|search|graph|next` over loading `docs/.index.json` into context. -**For now, instead of `list`/`get`/`search`:** read `docs/STATE.md` for -counts and needs-attention, or read individual `docs/{context,decisions,tasks}/` -files directly. Avoid loading `docs/.index.json` wholesale — it is for -bootstrap / CI consumers, not routine agent queries. See -`docs/decisions/ADR-0018-cli-as-query-layer.md` for the rationale. +## Workflow -The binary is language-agnostic — install via direct download, Homebrew, Scoop, or Docker. No Node/Bun/Python dependency. - -## Recommended agent workflow - -1. Read `docs/STATE.md`. -2. Run `docops audit` to see open gaps. -3. Read `docs/tasks/` to pick a task (check `depends_on` before starting). +1. `docops state` (or read `docs/STATE.md`). +2. `docops audit` for open gaps. +3. Pick a task from `docs/tasks/`; check `depends_on`. 4. Before coding: read every doc in the task's `requires:` and `depends_on:`. -5. Work in your native plan/execute mode. DocOps does not prescribe how you code. -6. After finishing, update the task's frontmatter `status:` field and run `docops refresh` to regenerate index and STATE.md. -7. If your work revealed a new decision, `docops new adr`. If a new gap, `docops new task` with citations. +5. Work in your native plan/execute mode. +6. After: update the task's `status:` and run `docops refresh`. +7. New decision → `docops new adr`. New gap → `docops new task` with citations. ## Editor integration -JSON Schemas for frontmatter validation live in `docs/.docops/schema/` (`context.schema.json`, `decision.schema.json`, `task.schema.json`). Wire them up in VS Code via the `redhat.vscode-yaml` extension and run `docops schema` to regenerate after editing `context_types:` in `docops.yaml`. - -## Pairs well with - -- **GStack** role skills — they apply perspective; DocOps provides the typed state they read. -- **Native plan mode** of your IDE — DocOps explicitly does not try to replace it. +JSON Schemas live in `docs/.docops/schema/`. In VS Code, use `redhat.vscode-yaml`. Regenerate after editing `context_types:` in `docops.yaml`: `docops schema`. -## Notes for humans - -The block above is auto-maintained by `docops init` and `docops upgrade`. -Edit content outside the `` delimiters freely; DocOps will -preserve it. Edits inside the delimiters will be overwritten on the next -upgrade. The same docops block also appears in `AGENTS.md` (the multi-tool -sibling); both refresh from the same shipped template. +> Content inside the `` markers is regenerated by `docops upgrade`. Edit freely outside. diff --git a/templates/skills/docops/close.md b/templates/skills/docops/close.md new file mode 100644 index 0000000..c3ecea1 --- /dev/null +++ b/templates/skills/docops/close.md @@ -0,0 +1,42 @@ +--- +name: close +description: Finish a task — flip status to done, refresh DocOps state, and stage a commit. Use when the user says "done with TP-X" / "mark TP-X complete". +--- + +# /docops:close + +Close out a finished task. + +Ask for the task ID if not provided. Then: + +1. Verify the task's acceptance criteria are actually met. Read the + task body, check for remaining TODOs, and (if code changed) + eyeball the diff. If something is incomplete, stop and tell the user. + +2. Flip the task's `status:` frontmatter to `done` via `Edit` on the + source file — do not hand-edit the index. + +3. Run: + +``` +docops refresh +``` + +4. Stage every file relevant to the task (source changes + task + frontmatter + `docs/.index.json` + `docs/STATE.md`), then propose a + commit message of the form: + + ``` + : + + + ``` + + Confirm with the user before committing. Never commit without their + explicit OK. + +5. After the commit, run `/docops:progress` to surface the next move. + +Do not mark a task `done` just because citing files changed — the +acceptance list is the contract. diff --git a/templates/skills/docops/do.md b/templates/skills/docops/do.md new file mode 100644 index 0000000..bcf5c56 --- /dev/null +++ b/templates/skills/docops/do.md @@ -0,0 +1,32 @@ +--- +name: do +description: Route a freeform natural-language intent to the right /docops:* skill. Use when the user knows what they want but doesn't know which command runs it. +--- + +# /docops:do + +Interpret the user's request and dispatch to one matching skill. +Never do the work yourself — confirm the match, then hand off. + +Routing table (intent → skill): + +| Intent | Skill | +|---|---| +| "where are we", "what's the status", "summarise" | `/docops:progress` | +| "what's next", "pick a task" | `/docops:next` | +| "add a decision", "record that choice", "I need an ADR" | `/docops:new-adr` | +| "capture a PRD", "memo", "save constraints", "research note" | `/docops:new-ctx` | +| "add a task", "track this work", "create a TP" | `/docops:new-task` | +| "find X", "search for Y" | `/docops:search` | +| "look up ADR-0012" | `/docops:get` | +| "what depends on ADR-X", "blast radius" | `/docops:graph` | +| "list draft ADRs", "active tasks" | `/docops:list` | +| "what's broken", "coverage gaps" | `/docops:audit` | +| "finish TP-X", "mark done", "close this task" | `/docops:close` | +| "plan from CTX-X", "draft decision + tasks" | `/docops:plan` | +| "upgrade docops files", "refresh scaffolding" | `/docops:upgrade` | +| "validate", "reindex", "regenerate state" | `/docops:refresh` | + +Match on intent, not literal wording. If two skills could fit, ask one +short clarifying question. If nothing matches, say so — do not invent a +command. diff --git a/templates/skills/docops/get.md b/templates/skills/docops/get.md new file mode 100644 index 0000000..4dc7a7e --- /dev/null +++ b/templates/skills/docops/get.md @@ -0,0 +1,21 @@ +--- +name: get +description: Look up a single DocOps doc by ID (ADR-nnnn, CTX-nnn, TP-nnn) and print its indexed record. Use when you need the frontmatter and edges for one doc without reading the full file. +--- + +# /docops:get + +Fetch one indexed doc by ID. + +``` +docops get ADR-0010 +docops get TP-029 --json +``` + +The plain output shows title, status, coverage/priority, dates, and both +forward and reverse edges (computed). `--json` returns the full +`IndexedDoc` — use it when scripting or when you need the exact edge +shape. + +When a user pastes an ID into chat, run `docops get ` first to +orient before reading the full file. diff --git a/templates/skills/docops/graph.md b/templates/skills/docops/graph.md new file mode 100644 index 0000000..0ae5cce --- /dev/null +++ b/templates/skills/docops/graph.md @@ -0,0 +1,22 @@ +--- +name: graph +description: Walk the typed edge graph outward from one doc to its neighbours (requires, related, supersedes, reverse edges). Use to understand "what depends on what" before touching an ADR or task. +--- + +# /docops:graph + +Print the typed edge graph starting at one doc. + +``` +docops graph ADR-0010 +docops graph ADR-0010 --depth 2 +docops graph TP-029 --json +``` + +`--depth N` (default 1) controls traversal depth. Depth 1 shows direct +neighbours; higher depths show transitive closure. Edges include both +source fields (`requires`, `related`, `supersedes`, `depends_on`) and +computed reverse edges (`required_by`, etc. — see ADR-0006). + +Use this before superseding an ADR or removing a task: the graph shows +every doc that would break. diff --git a/templates/skills/docops/init.md b/templates/skills/docops/init.md index dfa9518..0de7937 100644 --- a/templates/skills/docops/init.md +++ b/templates/skills/docops/init.md @@ -20,7 +20,7 @@ What it does: - Writes JSON Schemas to `docs/.docops/schema/` for in-editor validation. - Writes or refreshes the `` block inside `AGENTS.md` and `CLAUDE.md` (both files share the same docops block; Claude Code reads CLAUDE.md by default while other agents read AGENTS.md). - Installs a language-agnostic pre-commit hook that runs `docops validate`. -- Scaffolds `/docops:*` skills into `.claude/skills/docops/` and `.cursor/commands/docops/`. +- Scaffolds `/docops:*` slash commands into `.claude/commands/docops/` and `.cursor/commands/docops/`. Flags: diff --git a/templates/skills/docops/list.md b/templates/skills/docops/list.md new file mode 100644 index 0000000..46d59b1 --- /dev/null +++ b/templates/skills/docops/list.md @@ -0,0 +1,26 @@ +--- +name: list +description: List DocOps docs with optional filters (kind, status, coverage, tag, stale, since). Use when looking for a set of docs, not one — e.g. "all draft ADRs", "all active tasks". +--- + +# /docops:list + +Enumerate docs with filters. Prefer this over reading `docs/.index.json` +directly. + +``` +docops list --kind ADR --status draft +docops list --kind TP --status active +docops list --kind ADR --coverage required +docops list --tag release +docops list --stale +docops list --since 2026-04-01 +docops list --json +``` + +Filters compose (AND). `--status` semantics are per-kind: +- ADR: `draft`, `accepted`, `superseded`, `rejected` +- TP: `backlog`, `active`, `done` +- CTX: has no status; `--status` with `--kind CTX` is invalid + +Default output is a table. Use `--json` for scripting. diff --git a/templates/skills/docops/next.md b/templates/skills/docops/next.md new file mode 100644 index 0000000..a61353c --- /dev/null +++ b/templates/skills/docops/next.md @@ -0,0 +1,27 @@ +--- +name: next +description: Ask DocOps which task to pick up next. Uses assignee, priority, status, and depends_on to recommend one task. Use at session start or after finishing a task. +--- + +# /docops:next + +Find the next task to work on. + +``` +docops next +``` + +Filter when the project has multiple contributors or priority bands: + +``` +docops next --assignee nachiket +docops next --priority p0 +docops next --json +``` + +The CLI picks one task by descending priority (p0 → p2), then ascending +ID among tasks with no unmet `depends_on`. If no task matches, exit code +is non-zero and stderr says `no task matches`. + +After selecting a task, read every doc in its `requires:` and +`depends_on:` before writing code. diff --git a/templates/skills/docops/plan.md b/templates/skills/docops/plan.md new file mode 100644 index 0000000..5a9f5b9 --- /dev/null +++ b/templates/skills/docops/plan.md @@ -0,0 +1,51 @@ +--- +name: plan +description: Given a CTX (PRD, memo, research note), draft one ADR and one or more tasks that cite it. Human-confirmed before write. Use when turning stakeholder input into actionable work. +--- + +# /docops:plan + +Convert context into a decision plus tasks. + +Ask for the CTX ID if not provided. Read it, then draft: + +1. **One ADR** capturing the decision the CTX implies or demands. Propose + title, Context/Decision/Rationale/Consequences body. Confirm with the + user before writing. + +2. **One or more tasks** that cite the ADR. Each task should carry + priority, an acceptance checklist, and (if relevant) a depends-on + reference to other tasks. Confirm the full set before writing. + +Write via `--body -` heredocs so drafts land populated on creation (no +stub-then-rewrite round-trip): + +``` +docops new adr "Title" --related ADR-xxxx --body - <<'EOF' +## Context +... +## Decision +... +## Rationale +... +## Consequences +... +EOF + +docops new task "Title" --requires ADR-0026 --priority p1 --body - <<'EOF' +## Goal +... +## Acceptance +- ... +EOF +``` + +After all writes, run `docops refresh` to regenerate index and STATE.md. + +Rules: + +- Every task must cite ≥1 ADR or CTX in `requires:`. If the user cannot + name a citation, stop and draft the missing ADR/CTX first. +- Keep ADR `status: draft` unless the user explicitly accepts it. +- Don't write anything the user hasn't confirmed. Show proposed + frontmatter and body summary, then ask. diff --git a/templates/skills/docops/progress.md b/templates/skills/docops/progress.md new file mode 100644 index 0000000..511fb45 --- /dev/null +++ b/templates/skills/docops/progress.md @@ -0,0 +1,28 @@ +--- +name: progress +description: Situational awareness — read STATE.md, run audit, and name the next action in one go. Use at session start or when the user asks "where are we?" / "what's next?". +--- + +# /docops:progress + +Summarise project state and recommend one next action. + +Run these, in order: + +``` +docops state +docops audit +docops next +``` + +Then produce a single short briefing: + +- **Counts:** doc totals and anything in needs-attention from STATE.md. +- **Active work:** tasks currently `status: active` (from `docops list --kind TP --status active --json`). +- **Audit gaps:** one line per finding; skip if audit is empty. +- **Next task:** whatever `docops next` picked, with its `requires:` IDs. +- **Recommendation:** one of `/docops:next`, `/docops:new-ctx`, `/docops:new-adr`, `/docops:new-task`, or `/docops:close ` depending on state. + +Keep the briefing under 10 lines. Do not regenerate STATE.md unless +something in the repo changed — running `docops state` is cheap and +idempotent, but a dirty diff shows up in git. diff --git a/templates/skills/docops/search.md b/templates/skills/docops/search.md new file mode 100644 index 0000000..1364af7 --- /dev/null +++ b/templates/skills/docops/search.md @@ -0,0 +1,28 @@ +--- +name: search +description: Substring or regex search across DocOps doc titles, tags, and bodies, with structured filters (kind, status, coverage, tag, priority, assignee, since). Use when looking for docs by content, not ID. +--- + +# /docops:search + +Query docs by text and/or frontmatter. + +``` +docops search "release process" +docops search "homebrew" --kind ADR +docops search --kind TP --status active --priority p1 +docops search "upgrade" --regex --case +docops search --tag release --since 2026-04-01 +docops search "migration" --json +``` + +The query is optional when at least one filter flag is present. Default +match is case-insensitive substring; add `--regex` for regular +expressions and `--case` for case-sensitive matching. + +Filters narrow before text match, so `--kind TP --status active "foo"` +is cheaper than searching and then filtering. + +Semantic/embedding search is out of scope (see ADR-0017) — use +substring + filters, or let the user point at specific IDs via +`/docops:get`. diff --git a/templates/skills/docops/upgrade.md b/templates/skills/docops/upgrade.md index e75cda9..8702546 100644 --- a/templates/skills/docops/upgrade.md +++ b/templates/skills/docops/upgrade.md @@ -15,7 +15,8 @@ docops upgrade Touches only DocOps-owned scaffolding: -- `.claude/skills/docops/*.md` and `.cursor/commands/docops/*.md` — synced to the shipped bundle (creates new files, refreshes changed ones, removes files that left the bundle). +- `.claude/commands/docops/*.md` and `.cursor/commands/docops/*.md` — synced to the shipped bundle (creates new files, refreshes changed ones, removes files that left the bundle). +- `.claude/skills/docops/*.md` — if this legacy folder exists from an older docops, its contents are removed (Claude Code reads slash commands from `.claude/commands/`, not `.claude/skills/`). - `docs/.docops/schema/*.schema.json` — regenerated from `docops.yaml`. - The `` block inside `AGENTS.md` and `CLAUDE.md` — refreshed in place; content outside the markers is preserved. Either file is created if absent (so v0.1.x users gain CLAUDE.md on first upgrade). @@ -47,5 +48,6 @@ docops upgrade --json ``` Exit codes: `0` on success or user abort; `2` when there is no -`docops.yaml` (run `docops init` first) or when the `.claude/skills/docops/` -directory contains user-added files docops did not write. +`docops.yaml` (run `docops init` first) or when the +`.claude/commands/docops/` directory contains user-added files docops did +not write. diff --git a/templates/skills_lint_test.go b/templates/skills_lint_test.go index b91cb18..8aa1656 100644 --- a/templates/skills_lint_test.go +++ b/templates/skills_lint_test.go @@ -18,7 +18,7 @@ import ( "testing" ) -// knownSubcmds is the shipped CLI surface for v0.1.1. +// knownSubcmds is the shipped CLI surface. var knownSubcmds = map[string]bool{ "init": true, "validate": true, @@ -30,6 +30,11 @@ var knownSubcmds = map[string]bool{ "refresh": true, "update-check": true, "upgrade": true, + "list": true, + "get": true, + "graph": true, + "next": true, + "search": true, } // knownNewKinds are valid arguments for `docops new`. @@ -79,6 +84,39 @@ var flagAllowlist = map[string]map[string]bool{ "--snooze": true, "--json": true, }, + "list": { + "--json": true, + "--kind": true, + "--status": true, + "--coverage": true, + "--tag": true, + "--stale": true, + "--since": true, + }, + "get": { + "--json": true, + }, + "graph": { + "--json": true, + "--depth": true, + }, + "next": { + "--json": true, + "--assignee": true, + "--priority": true, + }, + "search": { + "--json": true, + "--regex": true, + "--case": true, + "--kind": true, + "--status": true, + "--coverage": true, + "--tag": true, + "--priority": true, + "--assignee": true, + "--since": true, + }, // new ctx / new adr / new task share the "new" key; we resolve // per-kind flags under "new/" and fall back to "new". "new": { diff --git a/templates/templates.go b/templates/templates.go index dd8e10c..b952c15 100644 --- a/templates/templates.go +++ b/templates/templates.go @@ -40,8 +40,9 @@ func PreCommitHook() ([]byte, error) { } // Skills returns a map of skill leaf filename (e.g. "init.md") to file body. -// Callers write these into `.claude/skills/docops/` and -// `.cursor/commands/docops/`. +// Callers write these into `.claude/commands/docops/` and +// `.cursor/commands/docops/` — both are the slash-command directories +// for their respective agent tools. func Skills() (map[string][]byte, error) { out := make(map[string][]byte) entries, err := fs.ReadDir(tree, "skills/docops")