compound-workflow 1.4.3 → 1.4.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +55 -164
- package/package.json +4 -2
- package/scripts/check-workflow-contracts.mjs +43 -3
- package/scripts/install-cli.mjs +98 -89
- package/scripts/postinstall.mjs +62 -0
- package/src/.agents/commands/workflow/plan.md +7 -7
- package/src/.agents/commands/workflow/triage.md +3 -1
- package/src/.agents/commands/workflow/work.md +24 -16
- package/src/AGENTS.md +8 -5
package/README.md
CHANGED
|
@@ -1,193 +1,84 @@
|
|
|
1
|
-
# Compound Workflow
|
|
1
|
+
# Compound Workflow
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Compound Workflow is a portable, command-first system for shipping software with less ambiguity and stronger verification.
|
|
4
|
+
It follows a simple cycle: **clarify -> plan -> execute -> verify -> capture**.
|
|
5
|
+
Use it when you want repeatable delivery without ad-hoc process drift.
|
|
4
6
|
|
|
5
|
-
|
|
7
|
+
Inspired by [Compound Engineering](https://every.to/guides/compound-engineering) (Every).
|
|
6
8
|
|
|
7
|
-
|
|
9
|
+
Best fit when you need:
|
|
8
10
|
|
|
9
|
-
|
|
11
|
+
- Clear intent and acceptance criteria before coding
|
|
12
|
+
- Structured execution with explicit review gates
|
|
13
|
+
- A repeatable process that captures reusable learnings
|
|
10
14
|
|
|
11
|
-
|
|
15
|
+
## Workflow
|
|
12
16
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
## Get started
|
|
16
|
-
|
|
17
|
-
**1. Add the package and run Install** (in the project where you want the workflow):
|
|
18
|
-
|
|
19
|
-
```bash
|
|
20
|
-
npm install compound-workflow
|
|
21
|
-
npx compound-workflow install
|
|
22
|
-
npx compound-workflow install all
|
|
23
|
-
```
|
|
24
|
-
|
|
25
|
-
**2. Choose how you use it:**
|
|
26
|
-
|
|
27
|
-
- **Cursor:** If your project already has a `.cursor` directory, Install will create the correct structure: one symlink per skill under `.cursor/skills/`, plus `.cursor/agents`, `.cursor/commands`, and `.cursor/references` (each symlinked to the package). No plugin needed. Use the plugin from this repo if you need a different loading method.
|
|
28
|
-
- **OpenCode:** Install writes `opencode.json` and a symlink at `.agents/compound-workflow-skills`; OpenCode loads from the package. Run `/install` or `npx compound-workflow install` in the project.
|
|
29
|
-
- **Claude:** Add the compound-workflow plugin from this repo. In any repo, run `/install` or the CLI above.
|
|
30
|
-
|
|
31
|
-
**What Install does:** Merges `AGENTS.md` (preserves your Repo Config Block), creates standard dirs (`docs/`, `todos/`), writes `opencode.json` (for OpenCode), and—if the project has a `.cursor` directory—creates `.cursor/skills/<skill>`, `.cursor/agents`, `.cursor/commands`, and `.cursor/references` (symlinks into the package). All paths reference `node_modules/compound-workflow`; no file copying.
|
|
32
|
-
|
|
33
|
-
**CLI options:** `all` / `--all` (full install shortcut; same as `--cursor`), `--dry-run` (preview), `--root /path/to/project`, `--no-config` (skip Repo Config Block reminder), `--cursor` (create `.cursor` and wire Cursor symlinks even if `.cursor` does not already exist).
|
|
34
|
-
|
|
35
|
-
**Legacy (clone inside repo):** If you cloned this repo inside a host repo and need to copy files without npm, use `./scripts/sync-into-repo.sh` (copy only; does not update opencode.json). Prefer the npm + Install flow above.
|
|
36
|
-
|
|
37
|
-
To update to a new release, see [Updating compound-workflow](#updating-compound-workflow).
|
|
38
|
-
|
|
39
|
-
---
|
|
40
|
-
|
|
41
|
-
## Updating compound-workflow
|
|
42
|
-
|
|
43
|
-
- **Cursor (plugin):** If you load the plugin from this repo, pull latest and reload the plugin in Cursor.
|
|
44
|
-
- **Cursor (npm + .cursor):** Run `npm update compound-workflow`, then run **Install** again (`npx compound-workflow install`) to refresh the `.cursor/skills`, `.cursor/agents`, `.cursor/commands`, and `.cursor/references` symlinks and AGENTS.md.
|
|
45
|
-
- **OpenCode / npm:** Run `npm update compound-workflow` (or bump the version in `package.json` and `npm install`), then run **Install** again. This refreshes `opencode.json`, merges the latest `AGENTS.md` template, and ensures dirs exist; Repo Config Block is preserved.
|
|
46
|
-
- **Claude (plugin):** Update via the editor’s plugin; if from repo, pull latest and reload.
|
|
47
|
-
|
|
48
|
-
---
|
|
49
|
-
|
|
50
|
-
## Workflow at a glance
|
|
51
|
-
|
|
52
|
-
Clarify what to build -> plan how (fidelity + confidence) -> triage todos -> execute -> review -> capture learnings -> log and assess.
|
|
17
|
+
The workflow turns a request into validated output and reusable team knowledge.
|
|
53
18
|
|
|
54
19
|
```mermaid
|
|
55
20
|
flowchart LR
|
|
56
|
-
A["brainstorm"] --> B["plan"] --> C["triage"] --> D["
|
|
21
|
+
A["brainstorm"] --> B["plan"] --> C["work (includes triage)"] --> D["review"] --> E["capture"] --> F["metrics"]
|
|
57
22
|
```
|
|
58
23
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
If docs conflict: follow [docs/principles/workflow-baseline-principles.md](docs/principles/workflow-baseline-principles.md), then [src/AGENTS.md](src/AGENTS.md), then command docs under [src/.agents/commands/](src/.agents/commands/).
|
|
62
|
-
|
|
63
|
-
---
|
|
64
|
-
|
|
65
|
-
## Step-by-step: intent and commands
|
|
66
|
-
|
|
67
|
-
| Step | Intent | Command | Output / note |
|
|
68
|
-
|------|--------|---------|---------------|
|
|
69
|
-
| Clarify what to build | Dialogue only; no code | `/workflow:brainstorm [topic]` | `docs/brainstorms/` |
|
|
70
|
-
| Define how (fidelity + confidence) | Plan only; no code; include agentic access + validation contract | `/workflow:plan [description or brainstorm path]` | `docs/plans/` |
|
|
71
|
-
| Ready the queue | Priority/dependencies + executable agentic contract checks for pending todos | `/workflow:triage` | — |
|
|
72
|
-
| Execute | File-based todos; risk-tier testing; evidence-backed implementation; no auto-ship | `/workflow:work <plan-path>` | `todos/` |
|
|
73
|
-
| Validate quality | Independent, evidence-based review for code/config changes (docs-only exempt); no fixes by default | `/workflow:review [PR, branch, or current]` | pass / pass-with-notes / fail |
|
|
74
|
-
| Capture learnings | One solution doc for future use | `/workflow:compound [context]` | `docs/solutions/` |
|
|
75
|
-
| Log and improve | Session log + optional aggregate review | `/metrics` + `/assess weekly 7` (or monthly) | `docs/metrics/daily/`, weekly/monthly |
|
|
76
|
-
|
|
77
|
-
#### 1. Clarify (brainstorm)
|
|
78
|
-
|
|
79
|
-
**Intent:** Dialogue only; no code. **Command:** `/workflow:brainstorm [topic]`. **Output:** `docs/brainstorms/`.
|
|
80
|
-
|
|
81
|
-
#### 2. Define how (plan)
|
|
82
|
-
|
|
83
|
-
**Intent:** Plan only; no code; fidelity + confidence; include an agentic access + validation contract. **Command:** `/workflow:plan [description or brainstorm path]`. **Output:** `docs/plans/`.
|
|
84
|
-
|
|
85
|
-
#### 3. Ready the queue (triage)
|
|
86
|
-
|
|
87
|
-
**Intent:** Priority/dependencies for pending todos and readiness checks for agentic executability. **Command:** `/workflow:triage`. **Output:** —.
|
|
88
|
-
|
|
89
|
-
#### 4. Execute (work)
|
|
90
|
-
|
|
91
|
-
**Intent:** File-based todos; risk-tier testing; success-criteria evidence + quality gates before completion; no auto-ship. **Command:** `/workflow:work <plan-path>`. **Output:** `todos/`.
|
|
92
|
-
|
|
93
|
-
`/workflow:work` must not run until `/workflow:triage` has approved executable ready todos.
|
|
24
|
+
## Get Started
|
|
94
25
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
**Intent:** Independent, evidence-based review with explicit independence mode (`independent|degraded`) and evidence disclosure; no fixes by default. **Command:** `/workflow:review [PR|branch|current]`. **Output:** pass / pass-with-notes / fail.
|
|
100
|
-
|
|
101
|
-
#### 6. Capture learnings (compound)
|
|
102
|
-
|
|
103
|
-
**Intent:** One solution doc for future use. **Command:** `/workflow:compound [context]`. **Output:** `docs/solutions/`.
|
|
104
|
-
|
|
105
|
-
#### 7. Log and improve
|
|
106
|
-
|
|
107
|
-
**Intent:** Session log + optional aggregate review. **Command:** `/metrics` + `/assess weekly 7` (or monthly). **Output:** `docs/metrics/daily/`, weekly/monthly.
|
|
108
|
-
|
|
109
|
-
**Optional QA:** **`/test-browser [PR|branch|current]`** — Browser validation on affected pages via **agent-browser CLI only** (not MCP). Install: `npm install -g agent-browser` then `agent-browser install`. See [src/.agents/commands/test-browser.md](src/.agents/commands/test-browser.md).
|
|
110
|
-
|
|
111
|
-
---
|
|
112
|
-
|
|
113
|
-
## Command reference
|
|
114
|
-
|
|
115
|
-
**Onboarding:** `/install` — one action: merges AGENTS.md, creates dirs, preserves Repo Config Block; writes opencode.json (OpenCode) and, if present, symlinks into `.cursor/skills/` (Cursor). Run `npx compound-workflow install` in the project (requires `npm install compound-workflow`). Re-run after `npm update compound-workflow` to refresh config; see [Updating compound-workflow](#updating-compound-workflow).
|
|
116
|
-
|
|
117
|
-
**Core workflow:** See [Step-by-step](#step-by-step-intent-and-commands) above.
|
|
118
|
-
|
|
119
|
-
**QA:** `/test-browser [PR|branch|current]` — browser checks on affected routes (agent-browser CLI only).
|
|
120
|
-
|
|
121
|
-
**Improvement:** `/metrics [plan|todo|pr|solution|label]` — log session to `docs/metrics/daily/` and assess. `/assess [daily|weekly|monthly] [count]` — aggregate metrics and optional summary files.
|
|
122
|
-
|
|
123
|
-
**Experimental:** `/workflow:review-v2 [PR|branch|current]` — interactive snippet review; output-only (no GitHub publish).
|
|
124
|
-
|
|
125
|
-
Full detail: [src/AGENTS.md](src/AGENTS.md), [src/.agents/commands/](src/.agents/commands/).
|
|
126
|
-
|
|
127
|
-
---
|
|
128
|
-
|
|
129
|
-
## Artifacts
|
|
130
|
-
|
|
131
|
-
- **Brainstorms:** `docs/brainstorms/YYYY-MM-DD-<topic>-brainstorm.md`
|
|
132
|
-
- **Plans:** `docs/plans/YYYY-MM-DD-<type>-<slug>-plan.md`
|
|
133
|
-
- **Todos:** `todos/{id}-{status}-{priority}-{slug}.md`
|
|
134
|
-
- **Solutions:** `docs/solutions/<category>/YYYY-MM-DD-<module-slug>-<symptom-slug>.md`
|
|
135
|
-
- **Metrics:** `docs/metrics/daily/YYYY-MM-DD.md`, `docs/metrics/weekly/YYYY-WW.md`, `docs/metrics/monthly/YYYY-MM.md`
|
|
136
|
-
|
|
137
|
-
---
|
|
138
|
-
|
|
139
|
-
## How it works (internals)
|
|
140
|
-
|
|
141
|
-
Commands are the public API. Skills and agents are invoked by commands; you don’t call them directly.
|
|
142
|
-
|
|
143
|
-
- **Workflow skills:** `brainstorming`, `file-todos`, `compound-docs`, `document-review`, `technical-review`, `git-worktree`, `agent-browser`, `process-metrics`, `react-ddd-mvc-frontend`, `xstate-actor-orchestration`, `standards`.
|
|
144
|
-
- **State orchestration:** Use a state-orchestration skill when complexity exceeds simple local state (e.g. `xstate-actor-orchestration` per Skill Index)—UI container-as-orchestrator flows, backend/internal actor orchestration, receptionist/child-actor patterns, retries/timeouts/cancellation, or boolean-flag sprawl.
|
|
145
|
-
- **Skill-local metadata:** Some skills may include tool-specific metadata under `src/.agents/skills/<skill>/agents/` (for example `openai.yaml`) when required by skill validation/runtime.
|
|
146
|
-
- **Guardrail standards:** `data-foundations`, `pii-protection-prisma`, `financial-workflow-integrity`, `audit-traceability` — applied when work touches multi-tenant data, PII, money, or audit.
|
|
147
|
-
- **Agents:** Used by plan, review, and work for research, lint, and validation (e.g. `repo-research-analyst`, `learnings-researcher`, `git-history-analyzer`, `agent-native-reviewer`, `planning-technical-reviewer`).
|
|
148
|
-
|
|
149
|
-
Full “when to use what” and reference standards: [src/AGENTS.md](src/AGENTS.md).
|
|
150
|
-
|
|
151
|
-
---
|
|
152
|
-
|
|
153
|
-
## Guardrails
|
|
154
|
-
|
|
155
|
-
- **No auto-ship:** `/workflow:work` and `/workflow:review` do not commit, push, or create PRs by default.
|
|
156
|
-
|
|
157
|
-
- **Brainstorm and plan do not write code.** Output is documents only.
|
|
26
|
+
```bash
|
|
27
|
+
npm install compound-workflow
|
|
28
|
+
```
|
|
158
29
|
|
|
159
|
-
|
|
30
|
+
`npm install` adds the package and automatically configures your repo (`AGENTS.md`, required directories, and runtime wiring).
|
|
31
|
+
If your package manager skips lifecycle scripts, run `npx compound-workflow install` manually.
|
|
160
32
|
|
|
161
|
-
|
|
33
|
+
Install configures:
|
|
162
34
|
|
|
163
|
-
-
|
|
35
|
+
- Workflow template content in `AGENTS.md`
|
|
36
|
+
- Standard workspace directories for plans/todos/docs
|
|
37
|
+
- Runtime configuration used by supported tools
|
|
164
38
|
|
|
165
|
-
|
|
39
|
+
## Critical Path
|
|
166
40
|
|
|
167
|
-
|
|
41
|
+
After install, use this default sequence:
|
|
168
42
|
|
|
169
|
-
|
|
43
|
+
1. `/workflow:brainstorm` for requirements clarity
|
|
44
|
+
2. `/workflow:plan` for implementation design
|
|
45
|
+
3. `/workflow:work` to execute against the approved plan (includes automatic triage)
|
|
46
|
+
4. `/workflow:review` to validate quality before completion
|
|
47
|
+
5. `/workflow:compound` to capture reusable learnings
|
|
170
48
|
|
|
171
|
-
|
|
49
|
+
Optional:
|
|
172
50
|
|
|
173
|
-
|
|
51
|
+
- `/workflow:triage` for manual backlog curation before or during execution
|
|
52
|
+
- `/metrics` and `/assess` for process improvement
|
|
174
53
|
|
|
175
|
-
|
|
54
|
+
## Commands (Quick Map)
|
|
176
55
|
|
|
177
|
-
|
|
56
|
+
Core flow: `/workflow:brainstorm` -> `/workflow:plan` -> `/workflow:work` -> `/workflow:review` -> `/workflow:compound` -> `/metrics` (optional `/assess` for rollups).
|
|
178
57
|
|
|
179
|
-
|
|
58
|
+
| Command | Purpose | Related skills | Related agents |
|
|
59
|
+
|---|---|---|---|
|
|
60
|
+
| `/install` | Configure workflow files and runtime wiring in the repo | install CLI (no workflow skill routing) | none |
|
|
61
|
+
| `/workflow:brainstorm` | Clarify what to build through structured discussion | `brainstorming` (primary), `document-review` (optional refinement) | `repo-research-analyst` |
|
|
62
|
+
| `/workflow:plan` | Convert intent into an executable plan with fidelity/confidence | state-orchestration skill when needed (for example `xstate-actor-orchestration`) | `repo-research-analyst`, `learnings-researcher`, `best-practices-researcher`, `framework-docs-researcher`, `git-history-analyzer`, `spec-flow-analyzer`, `planning-technical-reviewer` |
|
|
63
|
+
| `/workflow:triage` | Manual queue curation for complex/multi-item backlogs (optional; `/workflow:work` runs triage automatically) | `file-todos` | none |
|
|
64
|
+
| `/workflow:work` | Execute plan/todos with quality gates and validation evidence | `git-worktree`, `file-todos`, `standards`, state-orchestration skill when needed | `repo-research-analyst`, `learnings-researcher`, `best-practices-researcher`, `framework-docs-researcher`, `git-history-analyzer` |
|
|
65
|
+
| `/workflow:review` | Perform independent quality review before completion | `git-worktree` (for non-current targets), `standards` | `learnings-researcher`, `lint`, `bug-reproduction-validator`, `git-history-analyzer`, `framework-docs-researcher`, `agent-native-reviewer` |
|
|
66
|
+
| `/workflow:compound` | Capture reusable implementation learnings in `docs/solutions/` | `compound-docs` (primary), `document-review` (optional) | `learnings-researcher`, `best-practices-researcher`, `framework-docs-researcher` |
|
|
67
|
+
| `/metrics` | Log session outcomes and improvement actions | `process-metrics`, `file-todos` (optional for follow-ups) | none |
|
|
68
|
+
| `/assess` | Aggregate metrics trends and propose process improvements | `file-todos` (for approved follow-up actions) | none |
|
|
69
|
+
| `/test-browser` | Validate affected routes with browser-level checks | `agent-browser`, `git-worktree` (optional branch isolation) | none |
|
|
180
70
|
|
|
181
|
-
|
|
71
|
+
Canonical command docs: [src/.agents/commands/](src/.agents/commands/)
|
|
182
72
|
|
|
183
|
-
##
|
|
73
|
+
## Learn More
|
|
184
74
|
|
|
185
|
-
|
|
75
|
+
- Workflow principles: [docs/principles/workflow-baseline-principles.md](docs/principles/workflow-baseline-principles.md)
|
|
76
|
+
- Project command and policy index: [src/AGENTS.md](src/AGENTS.md)
|
|
77
|
+
- Command definitions: [src/.agents/commands/](src/.agents/commands/)
|
|
186
78
|
|
|
187
|
-
|
|
79
|
+
If docs conflict: follow `docs/principles/workflow-baseline-principles.md`, then `src/AGENTS.md`, then command docs.
|
|
188
80
|
|
|
189
|
-
|
|
81
|
+
Guardrails:
|
|
190
82
|
|
|
191
|
-
-
|
|
192
|
-
-
|
|
193
|
-
- Principles and skill index: [src/AGENTS.md](src/AGENTS.md)
|
|
83
|
+
- Independent review policy: code/config changes require `/workflow:review` before workflow completion (docs-only changes are exempt).
|
|
84
|
+
- Standards baseline policy: code/config changes must pass the standards baseline gate in `/workflow:work` and `/workflow:review`.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "compound-workflow",
|
|
3
|
-
"version": "1.4.
|
|
3
|
+
"version": "1.4.5",
|
|
4
4
|
"description": "Clarify → plan → execute → verify → capture. One Install action for Cursor, Claude, and OpenCode.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -18,7 +18,9 @@
|
|
|
18
18
|
"skills"
|
|
19
19
|
],
|
|
20
20
|
"scripts": {
|
|
21
|
-
"
|
|
21
|
+
"postinstall": "node scripts/postinstall.mjs",
|
|
22
|
+
"check:pack-readme": "node scripts/check-pack-readme.mjs",
|
|
23
|
+
"test:install": "node --test tests/install-cli.test.mjs"
|
|
22
24
|
},
|
|
23
25
|
"engines": {
|
|
24
26
|
"node": ">=18"
|
|
@@ -11,6 +11,16 @@ const requiredChecks = [
|
|
|
11
11
|
pattern: "tie-breaker",
|
|
12
12
|
description: "baseline principles tie-breaker rule",
|
|
13
13
|
},
|
|
14
|
+
{
|
|
15
|
+
file: "docs/principles/workflow-baseline-principles.md",
|
|
16
|
+
pattern: "/workflow:work` - implement in isolation with evidence (includes required triage gate)",
|
|
17
|
+
description: "canonical flow routes triage through work by default",
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
file: "docs/principles/workflow-baseline-principles.md",
|
|
21
|
+
pattern: "Optional manual command:",
|
|
22
|
+
description: "principles preserve standalone manual triage command",
|
|
23
|
+
},
|
|
14
24
|
{
|
|
15
25
|
file: "src/AGENTS.md",
|
|
16
26
|
pattern: "## Contract Precedence",
|
|
@@ -33,12 +43,18 @@ const requiredChecks = [
|
|
|
33
43
|
},
|
|
34
44
|
{
|
|
35
45
|
file: "README.md",
|
|
36
|
-
|
|
46
|
+
anyOf: [
|
|
47
|
+
"code/config changes require `/workflow:review`",
|
|
48
|
+
"Independent review policy:",
|
|
49
|
+
],
|
|
37
50
|
description: "README review gate policy",
|
|
38
51
|
},
|
|
39
52
|
{
|
|
40
53
|
file: "README.md",
|
|
41
|
-
|
|
54
|
+
anyOf: [
|
|
55
|
+
"Standards baseline policy:",
|
|
56
|
+
"standards baseline gate",
|
|
57
|
+
],
|
|
42
58
|
description: "README standards baseline guardrail",
|
|
43
59
|
},
|
|
44
60
|
{
|
|
@@ -51,16 +67,36 @@ const requiredChecks = [
|
|
|
51
67
|
pattern: "Contract precedence:",
|
|
52
68
|
description: "triage command precedence note",
|
|
53
69
|
},
|
|
70
|
+
{
|
|
71
|
+
file: "src/.agents/commands/workflow/triage.md",
|
|
72
|
+
pattern: "independently runnable",
|
|
73
|
+
description: "triage command explicitly standalone while work auto-runs triage",
|
|
74
|
+
},
|
|
54
75
|
{
|
|
55
76
|
file: "src/.agents/commands/workflow/work.md",
|
|
56
77
|
pattern: "Contract precedence:",
|
|
57
78
|
description: "work command precedence note",
|
|
58
79
|
},
|
|
80
|
+
{
|
|
81
|
+
file: "src/.agents/commands/workflow/plan.md",
|
|
82
|
+
pattern: "Start `/workflow:work`",
|
|
83
|
+
description: "plan command default next-step routes to work",
|
|
84
|
+
},
|
|
59
85
|
{
|
|
60
86
|
file: "src/.agents/commands/workflow/work.md",
|
|
61
87
|
pattern: "HARD GATE - WORKTREE FIRST",
|
|
62
88
|
description: "worktree hard-gate wording in work command",
|
|
63
89
|
},
|
|
90
|
+
{
|
|
91
|
+
file: "src/.agents/commands/workflow/work.md",
|
|
92
|
+
pattern: "required prompt/create gate",
|
|
93
|
+
description: "mandatory worktree decision prompt/create gate in work command",
|
|
94
|
+
},
|
|
95
|
+
{
|
|
96
|
+
file: "src/.agents/commands/workflow/work.md",
|
|
97
|
+
pattern: "Do not infer or assume an answer when the user has not answered.",
|
|
98
|
+
description: "worktree decision cannot be silently assumed",
|
|
99
|
+
},
|
|
64
100
|
{
|
|
65
101
|
file: "src/.agents/commands/workflow/work.md",
|
|
66
102
|
pattern:
|
|
@@ -160,7 +196,11 @@ const readFile = (relativePath) => {
|
|
|
160
196
|
|
|
161
197
|
for (const check of requiredChecks) {
|
|
162
198
|
const contents = readFile(check.file);
|
|
163
|
-
|
|
199
|
+
const hasPattern = check.pattern ? contents.includes(check.pattern) : false;
|
|
200
|
+
const hasAnyOf = Array.isArray(check.anyOf)
|
|
201
|
+
? check.anyOf.some((pattern) => contents.includes(pattern))
|
|
202
|
+
: false;
|
|
203
|
+
if (!hasPattern && !hasAnyOf) {
|
|
164
204
|
failures.push(`Missing required contract text (${check.description}) in ${check.file}`);
|
|
165
205
|
}
|
|
166
206
|
}
|
package/scripts/install-cli.mjs
CHANGED
|
@@ -14,29 +14,32 @@ const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
|
14
14
|
function usage(exitCode = 0) {
|
|
15
15
|
const msg = `
|
|
16
16
|
Usage:
|
|
17
|
-
npx compound-workflow install [all|--all] [--root <projectDir>] [--dry-run] [--no-config]
|
|
17
|
+
npx compound-workflow install [all|--all] [--root <projectDir>] [--dry-run] [--no-config]
|
|
18
18
|
|
|
19
19
|
One action: writes opencode.json (loads from package), merges AGENTS.md, creates dirs,
|
|
20
20
|
and prompts for Repo Config Block (unless --no-config).
|
|
21
21
|
|
|
22
|
-
all, --all
|
|
22
|
+
all, --all Kept for compatibility
|
|
23
23
|
--root <dir> Project directory (default: cwd)
|
|
24
24
|
--dry-run Print planned changes only
|
|
25
25
|
--no-config Skip Repo Config Block prompt (only write opencode.json + AGENTS.md + dirs)
|
|
26
|
-
--cursor Force Cursor integration (create .cursor if missing, then wire links)
|
|
27
26
|
`;
|
|
28
27
|
(exitCode === 0 ? console.log : console.error)(msg.trimStart());
|
|
29
28
|
process.exit(exitCode);
|
|
30
29
|
}
|
|
31
30
|
|
|
32
31
|
function parseArgs(argv) {
|
|
33
|
-
const out = { root: process.cwd(), dryRun: false, noConfig: false
|
|
32
|
+
const out = { root: process.cwd(), dryRun: false, noConfig: false };
|
|
34
33
|
for (let i = 2; i < argv.length; i++) {
|
|
35
34
|
const a = argv[i];
|
|
36
35
|
if (a === "--dry-run") out.dryRun = true;
|
|
37
36
|
else if (a === "--no-config") out.noConfig = true;
|
|
38
|
-
else if (a === "--cursor")
|
|
39
|
-
|
|
37
|
+
else if (a === "--cursor") {
|
|
38
|
+
// Deprecated compatibility alias; install now auto-detects .cursor.
|
|
39
|
+
}
|
|
40
|
+
else if (a === "--all" || a === "all") {
|
|
41
|
+
// Deprecated compatibility alias; install now auto-detects .cursor.
|
|
42
|
+
}
|
|
40
43
|
else if (a === "--root") {
|
|
41
44
|
const v = argv[i + 1];
|
|
42
45
|
if (!v) usage(1);
|
|
@@ -58,7 +61,10 @@ function realpathSafe(p) {
|
|
|
58
61
|
|
|
59
62
|
const packageRoot = realpathSafe(path.join(__dirname, ".."));
|
|
60
63
|
const packageAgents = path.join(packageRoot, "src", ".agents");
|
|
61
|
-
const
|
|
64
|
+
const LOCAL_RUNTIME_ROOT = ".agents/compound-workflow";
|
|
65
|
+
const LOCAL_COMMANDS_ROOT = `${LOCAL_RUNTIME_ROOT}/commands`;
|
|
66
|
+
const LOCAL_AGENTS_ROOT = `${LOCAL_RUNTIME_ROOT}/agents`;
|
|
67
|
+
const LOCAL_REFERENCES_ROOT = `${LOCAL_RUNTIME_ROOT}/references`;
|
|
62
68
|
|
|
63
69
|
function walkFiles(dirAbs, predicate) {
|
|
64
70
|
const out = [];
|
|
@@ -101,14 +107,14 @@ function discoverCommands(agentsRoot) {
|
|
|
101
107
|
const files = walkFiles(commandsDir, (p) => p.endsWith(".md"));
|
|
102
108
|
const map = new Map();
|
|
103
109
|
for (const fileAbs of files) {
|
|
104
|
-
const
|
|
110
|
+
const relWithinCommands = path.relative(commandsDir, fileAbs).replaceAll(path.sep, "/");
|
|
105
111
|
const md = fs.readFileSync(fileAbs, "utf8");
|
|
106
112
|
const fm = parseFrontmatter(md);
|
|
107
113
|
const id = (fm.invocation || fm.name || path.basename(fileAbs, ".md")).trim();
|
|
108
114
|
const description = (fm.description || id).trim();
|
|
109
115
|
if (!id) continue;
|
|
110
|
-
const
|
|
111
|
-
map.set(id, { id, rel:
|
|
116
|
+
const localRel = `${LOCAL_COMMANDS_ROOT}/${relWithinCommands}`;
|
|
117
|
+
map.set(id, { id, rel: localRel, description });
|
|
112
118
|
}
|
|
113
119
|
return map;
|
|
114
120
|
}
|
|
@@ -118,18 +124,65 @@ function discoverAgents(agentsRoot) {
|
|
|
118
124
|
const files = walkFiles(agentsDir, (p) => p.endsWith(".md"));
|
|
119
125
|
const map = new Map();
|
|
120
126
|
for (const fileAbs of files) {
|
|
121
|
-
const
|
|
127
|
+
const relWithinAgents = path.relative(agentsDir, fileAbs).replaceAll(path.sep, "/");
|
|
122
128
|
const md = fs.readFileSync(fileAbs, "utf8");
|
|
123
129
|
const fm = parseFrontmatter(md);
|
|
124
130
|
const id = (fm.name || path.basename(fileAbs, ".md")).trim();
|
|
125
131
|
const description = (fm.description || id).trim();
|
|
126
132
|
if (!id) continue;
|
|
127
|
-
const
|
|
128
|
-
map.set(id, { id, rel:
|
|
133
|
+
const localRel = `${LOCAL_AGENTS_ROOT}/${relWithinAgents}`;
|
|
134
|
+
map.set(id, { id, rel: localRel, description });
|
|
129
135
|
}
|
|
130
136
|
return map;
|
|
131
137
|
}
|
|
132
138
|
|
|
139
|
+
function copyDirContents(sourceDir, targetDir) {
|
|
140
|
+
if (!fs.existsSync(sourceDir)) return;
|
|
141
|
+
fs.mkdirSync(targetDir, { recursive: true });
|
|
142
|
+
const entries = fs.readdirSync(sourceDir, { withFileTypes: true });
|
|
143
|
+
for (const entry of entries) {
|
|
144
|
+
const src = path.join(sourceDir, entry.name);
|
|
145
|
+
const dst = path.join(targetDir, entry.name);
|
|
146
|
+
if (entry.isDirectory()) {
|
|
147
|
+
copyDirContents(src, dst);
|
|
148
|
+
continue;
|
|
149
|
+
}
|
|
150
|
+
if (entry.isFile()) {
|
|
151
|
+
fs.mkdirSync(path.dirname(dst), { recursive: true });
|
|
152
|
+
fs.copyFileSync(src, dst);
|
|
153
|
+
}
|
|
154
|
+
if (entry.isSymbolicLink()) {
|
|
155
|
+
const real = realpathSafe(src);
|
|
156
|
+
const realStat = fs.statSync(real);
|
|
157
|
+
if (realStat.isDirectory()) {
|
|
158
|
+
copyDirContents(real, dst);
|
|
159
|
+
} else if (realStat.isFile()) {
|
|
160
|
+
fs.mkdirSync(path.dirname(dst), { recursive: true });
|
|
161
|
+
fs.copyFileSync(real, dst);
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
function syncRuntimeAssets(targetRoot, dryRun) {
|
|
168
|
+
const mappings = [
|
|
169
|
+
{ label: "commands", src: path.join(packageAgents, "commands"), dst: path.join(targetRoot, LOCAL_COMMANDS_ROOT) },
|
|
170
|
+
{ label: "agents", src: path.join(packageAgents, "agents"), dst: path.join(targetRoot, LOCAL_AGENTS_ROOT) },
|
|
171
|
+
{ label: "references", src: path.join(packageAgents, "references"), dst: path.join(targetRoot, LOCAL_REFERENCES_ROOT) },
|
|
172
|
+
];
|
|
173
|
+
|
|
174
|
+
for (const mapping of mappings) {
|
|
175
|
+
if (!fs.existsSync(mapping.src)) continue;
|
|
176
|
+
if (dryRun) {
|
|
177
|
+
console.log("[dry-run] Would sync", mapping.label, "to", path.relative(targetRoot, mapping.dst));
|
|
178
|
+
continue;
|
|
179
|
+
}
|
|
180
|
+
fs.rmSync(mapping.dst, { recursive: true, force: true });
|
|
181
|
+
copyDirContents(mapping.src, mapping.dst);
|
|
182
|
+
console.log("Synced", mapping.label + ":", path.relative(targetRoot, mapping.dst));
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
133
186
|
function ensureObject(v) {
|
|
134
187
|
return v && typeof v === "object" && !Array.isArray(v) ? v : {};
|
|
135
188
|
}
|
|
@@ -237,39 +290,29 @@ function ensureSkillsSymlink(targetRoot, dryRun) {
|
|
|
237
290
|
}
|
|
238
291
|
}
|
|
239
292
|
|
|
240
|
-
function
|
|
293
|
+
function ensureCursorDirSync(targetRoot, cursorSubdir, pkgSubdir, dryRun, label, cursorReady) {
|
|
241
294
|
const cursorDir = path.join(targetRoot, ".cursor");
|
|
242
295
|
if (!cursorReady) return { status: "skipped-missing-cursor" };
|
|
243
296
|
const pkgPath = path.join(packageRoot, "src", ".agents", pkgSubdir);
|
|
244
297
|
if (!fs.existsSync(pkgPath)) return { status: "skipped-missing-package-path" };
|
|
245
298
|
|
|
246
|
-
const
|
|
247
|
-
const targetRel = path.join("..", "node_modules", "compound-workflow", "src", ".agents", pkgSubdir);
|
|
248
|
-
const targetAbs = path.resolve(path.dirname(linkPath), targetRel);
|
|
249
|
-
|
|
299
|
+
const targetPath = path.join(cursorDir, cursorSubdir);
|
|
250
300
|
if (dryRun) {
|
|
251
|
-
console.log("[dry-run] Would
|
|
301
|
+
console.log("[dry-run] Would sync .cursor/" + cursorSubdir, "from", label || pkgSubdir, "(Cursor)");
|
|
252
302
|
return { status: "dry-run" };
|
|
253
303
|
}
|
|
254
304
|
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
else if (!stat.isSymbolicLink()) {
|
|
260
|
-
console.warn("Skipped", ".cursor/" + cursorSubdir, "because it exists and is not a symlink");
|
|
261
|
-
return { status: "blocked-nonsymlink", path: linkPath };
|
|
305
|
+
if (fs.existsSync(targetPath)) {
|
|
306
|
+
const stat = fs.lstatSync(targetPath);
|
|
307
|
+
if (!stat.isDirectory()) {
|
|
308
|
+
return { status: "blocked-nondirectory", path: targetPath };
|
|
262
309
|
}
|
|
263
|
-
} catch (_) {}
|
|
264
|
-
|
|
265
|
-
if (needCreate) {
|
|
266
|
-
removePathIfExists(linkPath);
|
|
267
|
-
const type = process.platform === "win32" ? "dir" : "dir";
|
|
268
|
-
fs.symlinkSync(targetRel, linkPath, type);
|
|
269
|
-
console.log("Created", ".cursor/" + cursorSubdir, "->", label || pkgSubdir, "(Cursor)");
|
|
270
|
-
return { status: "created" };
|
|
271
310
|
}
|
|
272
|
-
|
|
311
|
+
|
|
312
|
+
fs.rmSync(targetPath, { recursive: true, force: true });
|
|
313
|
+
copyDirContents(pkgPath, targetPath);
|
|
314
|
+
console.log("Synced", ".cursor/" + cursorSubdir, "from", label || pkgSubdir, "(Cursor)");
|
|
315
|
+
return { status: "synced" };
|
|
273
316
|
}
|
|
274
317
|
|
|
275
318
|
function ensureCursorSkills(targetRoot, dryRun, cursorReady) {
|
|
@@ -278,29 +321,13 @@ function ensureCursorSkills(targetRoot, dryRun, cursorReady) {
|
|
|
278
321
|
|
|
279
322
|
const packageSkillsDir = path.join(packageRoot, "src", ".agents", "skills");
|
|
280
323
|
if (!fs.existsSync(packageSkillsDir)) return { blocked: [] };
|
|
281
|
-
|
|
282
|
-
const skillNames = [];
|
|
283
|
-
try {
|
|
284
|
-
for (const name of fs.readdirSync(packageSkillsDir)) {
|
|
285
|
-
const skillPath = path.join(packageSkillsDir, name);
|
|
286
|
-
if (fs.statSync(skillPath).isDirectory() && fs.existsSync(path.join(skillPath, "SKILL.md"))) {
|
|
287
|
-
skillNames.push(name);
|
|
288
|
-
}
|
|
289
|
-
}
|
|
290
|
-
} catch (_) {
|
|
291
|
-
return;
|
|
292
|
-
}
|
|
293
|
-
|
|
294
324
|
const skillsDir = path.join(cursorDir, "skills");
|
|
295
|
-
const type = process.platform === "win32" ? "dir" : "dir";
|
|
296
|
-
|
|
297
325
|
if (dryRun) {
|
|
298
|
-
console.log("[dry-run] Would
|
|
326
|
+
console.log("[dry-run] Would sync .cursor/skills from package skills (Cursor)");
|
|
299
327
|
return { blocked: [] };
|
|
300
328
|
}
|
|
301
329
|
|
|
302
|
-
if (
|
|
303
|
-
else {
|
|
330
|
+
if (fs.existsSync(skillsDir)) {
|
|
304
331
|
const stat = fs.lstatSync(skillsDir);
|
|
305
332
|
if (!stat.isDirectory()) {
|
|
306
333
|
console.warn("Skipped .cursor/skills because it exists and is not a directory");
|
|
@@ -308,29 +335,10 @@ function ensureCursorSkills(targetRoot, dryRun, cursorReady) {
|
|
|
308
335
|
}
|
|
309
336
|
}
|
|
310
337
|
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
const targetAbs = path.resolve(path.dirname(linkPath), targetRel);
|
|
316
|
-
let needCreate = true;
|
|
317
|
-
try {
|
|
318
|
-
const stat = fs.lstatSync(linkPath);
|
|
319
|
-
if (stat.isSymbolicLink() && symlinkPointsTo(linkPath, targetAbs)) needCreate = false;
|
|
320
|
-
else if (!stat.isSymbolicLink()) {
|
|
321
|
-
console.warn("Skipped", ".cursor/skills/" + name, "because it exists and is not a symlink");
|
|
322
|
-
blocked.push(linkPath);
|
|
323
|
-
continue;
|
|
324
|
-
}
|
|
325
|
-
} catch (_) {}
|
|
326
|
-
|
|
327
|
-
if (needCreate) {
|
|
328
|
-
removePathIfExists(linkPath);
|
|
329
|
-
fs.symlinkSync(targetRel, linkPath, type);
|
|
330
|
-
console.log("Created", ".cursor/skills/" + name, "-> package skill (Cursor)");
|
|
331
|
-
}
|
|
332
|
-
}
|
|
333
|
-
return { blocked };
|
|
338
|
+
fs.rmSync(skillsDir, { recursive: true, force: true });
|
|
339
|
+
copyDirContents(packageSkillsDir, skillsDir);
|
|
340
|
+
console.log("Synced .cursor/skills from package skills (Cursor)");
|
|
341
|
+
return { blocked: [] };
|
|
334
342
|
}
|
|
335
343
|
|
|
336
344
|
function verifyCursorIntegration(targetRoot) {
|
|
@@ -345,13 +353,12 @@ function verifyCursorIntegration(targetRoot) {
|
|
|
345
353
|
const issues = [];
|
|
346
354
|
|
|
347
355
|
for (const check of checks) {
|
|
348
|
-
const
|
|
349
|
-
|
|
350
|
-
if (!fs.existsSync(linkPath)) issues.push(`${check.name} is missing`);
|
|
356
|
+
const dirPath = path.join(cursorDir, check.rel);
|
|
357
|
+
if (!fs.existsSync(dirPath)) issues.push(`${check.name} is missing`);
|
|
351
358
|
else {
|
|
352
|
-
const stat = fs.lstatSync(
|
|
353
|
-
if (stat.
|
|
354
|
-
issues.push(`${check.name}
|
|
359
|
+
const stat = fs.lstatSync(dirPath);
|
|
360
|
+
if (!stat.isDirectory()) {
|
|
361
|
+
issues.push(`${check.name} exists but is not a directory`);
|
|
355
362
|
}
|
|
356
363
|
}
|
|
357
364
|
}
|
|
@@ -413,9 +420,9 @@ function ensureCursorIntegration(targetRoot, dryRun, forceCursor) {
|
|
|
413
420
|
|
|
414
421
|
const skillReport = ensureCursorSkills(targetRoot, dryRun, cursorReady);
|
|
415
422
|
const dirReports = [
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
423
|
+
ensureCursorDirSync(targetRoot, "agents", "agents", dryRun, "package agents", cursorReady),
|
|
424
|
+
ensureCursorDirSync(targetRoot, "commands", "commands", dryRun, "package commands", cursorReady),
|
|
425
|
+
ensureCursorDirSync(targetRoot, "references", "references", dryRun, "package references", cursorReady),
|
|
419
426
|
];
|
|
420
427
|
|
|
421
428
|
const issues = [];
|
|
@@ -423,8 +430,8 @@ function ensureCursorIntegration(targetRoot, dryRun, forceCursor) {
|
|
|
423
430
|
for (const p of skillReport.blocked) issues.push(`${path.relative(targetRoot, p)} blocks symlink creation (not a symlink)`);
|
|
424
431
|
}
|
|
425
432
|
for (const report of dirReports) {
|
|
426
|
-
if (report?.status === "blocked-
|
|
427
|
-
issues.push(`${path.relative(targetRoot, report.path)} blocks
|
|
433
|
+
if (report?.status === "blocked-nondirectory") {
|
|
434
|
+
issues.push(`${path.relative(targetRoot, report.path)} blocks sync (not a directory)`);
|
|
428
435
|
}
|
|
429
436
|
}
|
|
430
437
|
|
|
@@ -579,12 +586,14 @@ function main() {
|
|
|
579
586
|
console.log("Package root:", packageRoot);
|
|
580
587
|
console.log("OpenCode CLI detected:", hasCommand("opencode") ? "yes" : "no");
|
|
581
588
|
|
|
589
|
+
syncRuntimeAssets(targetRoot, args.dryRun);
|
|
582
590
|
writeOpenCodeJson(targetRoot, args.dryRun);
|
|
583
591
|
ensureSkillsSymlink(targetRoot, args.dryRun);
|
|
584
592
|
reportOpenCodeIntegration(targetRoot, args.dryRun);
|
|
585
|
-
const
|
|
593
|
+
const cursorExists = fs.existsSync(path.join(targetRoot, ".cursor"));
|
|
594
|
+
const cursorReport = ensureCursorIntegration(targetRoot, args.dryRun, cursorExists);
|
|
586
595
|
if (cursorReport.status === "skipped-no-cursor") {
|
|
587
|
-
console.log("Cursor integration: skipped (.cursor
|
|
596
|
+
console.log("Cursor integration: skipped (.cursor not found).");
|
|
588
597
|
} else {
|
|
589
598
|
console.log("Cursor integration: verified skills, agents, commands, and references.");
|
|
590
599
|
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import fs from "node:fs";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { fileURLToPath } from "node:url";
|
|
5
|
+
import { spawnSync } from "node:child_process";
|
|
6
|
+
|
|
7
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
8
|
+
const packageRoot = path.resolve(__dirname, "..");
|
|
9
|
+
|
|
10
|
+
function realpathSafe(p) {
|
|
11
|
+
try {
|
|
12
|
+
return fs.realpathSync(p);
|
|
13
|
+
} catch {
|
|
14
|
+
return path.resolve(p);
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function isTruthy(v) {
|
|
19
|
+
const s = String(v || "").toLowerCase();
|
|
20
|
+
return s === "1" || s === "true" || s === "yes";
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function hasFile(dir, name) {
|
|
24
|
+
try {
|
|
25
|
+
return fs.existsSync(path.join(dir, name));
|
|
26
|
+
} catch {
|
|
27
|
+
return false;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function shouldSkip(targetRoot) {
|
|
32
|
+
if (!targetRoot) return "INIT_CWD not set";
|
|
33
|
+
if (isTruthy(process.env.npm_config_global)) return "global install";
|
|
34
|
+
if (isTruthy(process.env.COMPOUND_WORKFLOW_SKIP_POSTINSTALL)) return "disabled by env";
|
|
35
|
+
if (!hasFile(targetRoot, "package.json")) return "no package.json in target root";
|
|
36
|
+
if (realpathSafe(targetRoot) === realpathSafe(packageRoot)) return "package development install";
|
|
37
|
+
return null;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function run() {
|
|
41
|
+
const targetRoot = process.env.INIT_CWD ? path.resolve(process.env.INIT_CWD) : "";
|
|
42
|
+
const skipReason = shouldSkip(targetRoot);
|
|
43
|
+
if (skipReason) {
|
|
44
|
+
console.log(`[compound-workflow] postinstall skipped (${skipReason})`);
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const cliPath = path.join(packageRoot, "scripts", "install-cli.mjs");
|
|
49
|
+
const result = spawnSync(
|
|
50
|
+
process.execPath,
|
|
51
|
+
[cliPath, "install", "--root", targetRoot, "--no-config"],
|
|
52
|
+
{ stdio: "inherit", env: process.env }
|
|
53
|
+
);
|
|
54
|
+
|
|
55
|
+
if (result.status !== 0) {
|
|
56
|
+
console.warn(
|
|
57
|
+
"[compound-workflow] automatic setup failed; run `npx compound-workflow install` in your project."
|
|
58
|
+
);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
run();
|
|
@@ -863,17 +863,16 @@ Examples:
|
|
|
863
863
|
|
|
864
864
|
After writing the plan file, use **AskQuestion** to present these options:
|
|
865
865
|
|
|
866
|
-
Do not route directly from plan generation to `/workflow:work`.
|
|
867
|
-
|
|
868
866
|
**Question:** "Plan ready at `docs/plans/YYYY-MM-DD-<type>-<slug>-plan.md`. What would you like to do next?"
|
|
869
867
|
|
|
870
868
|
**Options:**
|
|
871
869
|
|
|
872
870
|
1. **Open plan in editor** - Open the plan file for review
|
|
873
871
|
2. **Review and refine** - Improve the document through structured self-review
|
|
874
|
-
3. **Start `/workflow:
|
|
875
|
-
4. **
|
|
876
|
-
5. **
|
|
872
|
+
3. **Start `/workflow:work`** - Execute this plan (includes default triage gate)
|
|
873
|
+
4. **Start `/workflow:triage`** - Manually curate/prioritize queue before execution
|
|
874
|
+
5. **Create Issue** - Create issue in project tracker (GitHub/Linear)
|
|
875
|
+
6. **Other** - Adjust the plan
|
|
877
876
|
|
|
878
877
|
Optional (only if those workflows exist in this repo):
|
|
879
878
|
|
|
@@ -884,6 +883,7 @@ Based on selection:
|
|
|
884
883
|
|
|
885
884
|
- **Open plan in editor** → Open the plan file in the editor (navigate to `docs/plans/<plan_filename>.md`)
|
|
886
885
|
- **Review and refine** → Load `document-review` skill.
|
|
886
|
+
- **Start `/workflow:work`** → Run `/workflow:work <plan_path>`; `/workflow:work` must run triage before implementation.
|
|
887
887
|
- **Start `/workflow:triage`** → Ensure plan todos exist (create via `file-todos` if needed), then run `/workflow:triage` to approve priority/dependencies and the executable ready queue.
|
|
888
888
|
- **Technical review** → Load `technical-review` skill; then if user agrees to changes, load `document-review` to update the plan.
|
|
889
889
|
- **Create Issue** → See "Issue Creation" section below
|
|
@@ -891,7 +891,7 @@ Based on selection:
|
|
|
891
891
|
|
|
892
892
|
**Note:** Only if `/deepen-plan` exists in this repo and the user has enabled it (e.g., ultrathink), you may run `/deepen-plan` after plan creation for extra depth; it is optional, not required.
|
|
893
893
|
|
|
894
|
-
Loop back to options after changes until user selects `/workflow:triage
|
|
894
|
+
Loop back to options after changes until user selects `/workflow:work`, `/workflow:triage`, or ends the session.
|
|
895
895
|
|
|
896
896
|
## Issue Creation
|
|
897
897
|
|
|
@@ -933,6 +933,6 @@ When user selects "Create Issue", detect their project tracker from repo guidanc
|
|
|
933
933
|
|
|
934
934
|
5. **After creation:**
|
|
935
935
|
- Display the issue URL
|
|
936
|
-
- Ask if they want to proceed to `/workflow:triage`
|
|
936
|
+
- Ask if they want to proceed to `/workflow:work` (default path) or `/workflow:triage` (manual queue curation)
|
|
937
937
|
|
|
938
938
|
NEVER CODE! Just research and write the plan.
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: triage
|
|
3
3
|
invocation: workflow:triage
|
|
4
|
-
description:
|
|
4
|
+
description: Manual triage command to prioritize todo files into an executable ready queue (priority, dependencies, recommended action)
|
|
5
5
|
argument-hint: "[optional: todo path, issue id, status filter ('pending'|'ready'|'active')]"
|
|
6
6
|
---
|
|
7
7
|
|
|
@@ -9,6 +9,8 @@ argument-hint: "[optional: todo path, issue id, status filter ('pending'|'ready'
|
|
|
9
9
|
|
|
10
10
|
Turn todo items into a prioritized executable queue.
|
|
11
11
|
|
|
12
|
+
This command is independently runnable. `/workflow:work` runs the same triage gate automatically before execution.
|
|
13
|
+
Use `/workflow:triage` when you want explicit manual queue curation before or during execution.
|
|
12
14
|
This command does not implement fixes. It approves and organizes work so `/workflow:work` can execute without ambiguity.
|
|
13
15
|
Output of this command is the only executable queue for `/workflow:work`.
|
|
14
16
|
|
|
@@ -100,7 +100,7 @@ The input must be a plan file path.
|
|
|
100
100
|
- Continue only when provided commands run successfully.
|
|
101
101
|
- If commands are not provided or fail, do not mark related todos complete.
|
|
102
102
|
|
|
103
|
-
Note: full execution preflight (triage + contract checks + isolation checks) runs after todo creation
|
|
103
|
+
Note: full execution preflight (auto-triage + contract checks + isolation checks) runs after todo creation in Step 3.5.
|
|
104
104
|
|
|
105
105
|
1.75. **Resolve Plan Scope Contract (REQUIRED)**
|
|
106
106
|
|
|
@@ -138,16 +138,23 @@ The input must be a plan file path.
|
|
|
138
138
|
- If you are already on a branch that clearly matches this plan, continue.
|
|
139
139
|
- Otherwise, continue anyway — the current active branch remains the reference/base for a new worktree unless the user explicitly requests a different base.
|
|
140
140
|
|
|
141
|
-
2)
|
|
141
|
+
2) Resolve the user decision (required prompt/create gate):
|
|
142
142
|
|
|
143
|
-
-
|
|
143
|
+
- If the user already gave an explicit instruction in this run:
|
|
144
|
+
- "create a worktree" / "yes use a worktree" => use worktree path
|
|
145
|
+
- "do not use a worktree" / "no worktree" => opt-out path
|
|
146
|
+
- Otherwise, you MUST ask this exact decision before proceeding:
|
|
147
|
+
- "Use a worktree for this work? (Yes/No; default recommendation: Yes)"
|
|
144
148
|
- Options:
|
|
145
149
|
- Yes (worktree)
|
|
146
150
|
- No (stay in current checkout; create/switch to a feature branch)
|
|
147
151
|
|
|
148
|
-
|
|
152
|
+
Mandatory behavior:
|
|
149
153
|
|
|
150
|
-
|
|
154
|
+
- Do not infer or assume an answer when the user has not answered.
|
|
155
|
+
- Do not run `skill: git-worktree` until the user has answered Yes (or already explicitly requested worktree creation).
|
|
156
|
+
- If Yes: ask for the new branch name when missing (e.g., `feat/<slug>`, `fix/<slug>`), then continue.
|
|
157
|
+
- If No: require explicit opt-out confirmation, then continue with the non-worktree path.
|
|
151
158
|
|
|
152
159
|
3) If worktree is chosen, run:
|
|
153
160
|
|
|
@@ -217,20 +224,21 @@ The input must be a plan file path.
|
|
|
217
224
|
- Each todo MUST include a link back to the plan file in `Resources` and reference the specific section(s) it implements.
|
|
218
225
|
- Default todo `status`:
|
|
219
226
|
- `ready` when the plan is approved and confidence is not low
|
|
220
|
-
- `pending` when plan confidence is low or requires
|
|
227
|
+
- `pending` when plan confidence is low or requires additional triage decisions
|
|
221
228
|
- Default todo `priority`: `p2` unless the plan indicates urgency/risk.
|
|
222
229
|
|
|
223
|
-
After creating todos:
|
|
230
|
+
After creating todos, run an in-command triage pass (same readiness/dependency rules as `/workflow:triage`) before any implementation work:
|
|
224
231
|
|
|
225
|
-
-
|
|
226
|
-
-
|
|
227
|
-
-
|
|
232
|
+
- approve/prioritize queue items for this plan
|
|
233
|
+
- make execution order explicit
|
|
234
|
+
- if no unblocked `ready` todos remain, stop and report pending/deferred/blocked items
|
|
235
|
+
- use standalone `/workflow:triage` only when the user explicitly requests manual queue curation
|
|
228
236
|
|
|
229
237
|
3.5. **Execution Preflight (HARD GATE before Phase 2)**
|
|
230
238
|
|
|
231
239
|
Contract checksum (MUST all be true before implementation):
|
|
232
240
|
|
|
233
|
-
- triage completed for this plan
|
|
241
|
+
- auto-triage completed for this plan (or standalone `/workflow:triage` completed)
|
|
234
242
|
- isolation gate recorded (`worktree_decision`, execution context, `gate_status: passed`)
|
|
235
243
|
- blocking spikes execute before dependent build todos
|
|
236
244
|
|
|
@@ -241,7 +249,7 @@ The input must be a plan file path.
|
|
|
241
249
|
- validation path is explicit (commands/routes/checks)
|
|
242
250
|
- evidence expectations are explicit
|
|
243
251
|
- quality gate commands are explicit or marked for ask-once fallback
|
|
244
|
-
- If a todo lacks this contract, move it to `pending`
|
|
252
|
+
- If a todo lacks this contract, move it to `pending` and require triage resolution before coding.
|
|
245
253
|
|
|
246
254
|
### Phase 2: Execute
|
|
247
255
|
|
|
@@ -268,7 +276,7 @@ The input must be a plan file path.
|
|
|
268
276
|
|
|
269
277
|
- If no unblocked `ready` todos remain:
|
|
270
278
|
- summarize remaining `pending`, `deferred` (parked for reference), and blocked items
|
|
271
|
-
- require re-running `/workflow:triage` for pending/blocked prioritization
|
|
279
|
+
- require re-running triage (auto-triage within `/workflow:work` or standalone `/workflow:triage`) for pending/blocked prioritization
|
|
272
280
|
- stop (do not invent work)
|
|
273
281
|
|
|
274
282
|
For each task in priority order:
|
|
@@ -319,7 +327,7 @@ The input must be a plan file path.
|
|
|
319
327
|
- If new, non-critical work is discovered, do NOT silently expand scope.
|
|
320
328
|
- Ask the user to choose one:
|
|
321
329
|
1) Do now (scope increase): only if small + tightly coupled
|
|
322
|
-
2) Create a triage item: create a new `pending` todo (default `p3` unless urgent) to be approved or deferred via
|
|
330
|
+
2) Create a triage item: create a new `pending` todo (default `p3` unless urgent) to be approved or deferred via triage
|
|
323
331
|
3) Park for reference: create a todo with Problem Statement + Findings + rationale, then mark it **deferred** (`*-deferred-*.md`, `status: deferred`) so it is kept for future reference but not in the executable queue
|
|
324
332
|
4) Compound candidate only: capture as a `/workflow:compound` documentation candidate (no todo by default)
|
|
325
333
|
- Always record the decision in the todo Work Log.
|
|
@@ -368,7 +376,7 @@ The input must be a plan file path.
|
|
|
368
376
|
- Convert the decision into explicit todos (implementation/investigation/deferral).
|
|
369
377
|
- If the chosen option is to run a timeboxed investigation or prototype, follow the **Spike Protocol** below.
|
|
370
378
|
- Record the decision + rationale in the todo Work Log.
|
|
371
|
-
- Re-approve the todo through
|
|
379
|
+
- Re-approve the todo through triage before returning it to `ready`.
|
|
372
380
|
|
|
373
381
|
**Spike Protocol (allocate a spike)**
|
|
374
382
|
|
|
@@ -376,7 +384,7 @@ The input must be a plan file path.
|
|
|
376
384
|
|
|
377
385
|
Steps:
|
|
378
386
|
|
|
379
|
-
1. **Spike todo:** Create a new `todos/*-pending-*.md` todo tagged `tags: [spike]` (or convert the current blocked todo to a spike todo). Fill Problem Statement, Proposed Solutions (options), and Acceptance Criteria (deliverable). Carry forward any plan metadata (initial priority, depends_on, unblocks, parallelizable).
|
|
387
|
+
1. **Spike todo:** Create a new `todos/*-pending-*.md` todo tagged `tags: [spike]` (or convert the current blocked todo to a spike todo). Fill Problem Statement, Proposed Solutions (options), and Acceptance Criteria (deliverable). Carry forward any plan metadata (initial priority, depends_on, unblocks, parallelizable). Ensure triage approves the spike and sets timebox + deliverable before treating it as `ready`.
|
|
380
388
|
2. **Isolated execution:** Recommend a dedicated spike worktree. Use `skill: git-worktree` with branch name `spike/<todo_id>-<slug>` (e.g. `spike/003-auth-approach`). Run worktree bootstrap per the git-worktree skill. Execute the spike in that worktree so build work is not mixed with exploration.
|
|
381
389
|
3. **Research subagents (per spike):** Run mandatory baseline research in parallel:
|
|
382
390
|
- Always (when agents exist): Task repo-research-analyst(context), Task learnings-researcher(context)
|
package/src/AGENTS.md
CHANGED
|
@@ -26,10 +26,13 @@ This `.agents` workspace is portable and command-first.
|
|
|
26
26
|
|
|
27
27
|
1. `/workflow:brainstorm` -> clarify what to build
|
|
28
28
|
2. `/workflow:plan` -> define how to build it
|
|
29
|
-
3. `/workflow:
|
|
30
|
-
4. `/workflow:
|
|
31
|
-
5. `/workflow:
|
|
32
|
-
|
|
29
|
+
3. `/workflow:work` -> implement (includes triage gate for todo readiness/prioritization)
|
|
30
|
+
4. `/workflow:review` -> validate quality
|
|
31
|
+
5. `/workflow:compound` -> capture durable learnings
|
|
32
|
+
|
|
33
|
+
Optional manual step:
|
|
34
|
+
|
|
35
|
+
- `/workflow:triage` -> explicitly curate/prioritize backlog items before execution when needed
|
|
33
36
|
|
|
34
37
|
Continuous improvement:
|
|
35
38
|
|
|
@@ -62,7 +65,7 @@ If workflow documents conflict, resolve them in this order:
|
|
|
62
65
|
- **Solution scope contract is mandatory in every plan.** Plans must declare `solution_scope` (`partial_fix|full_remediation|migration`) plus explicit completion expectation and non-goals so `/workflow:work` can enforce intent.
|
|
63
66
|
- **SpecFlow is a validation gate, not a rewrite engine.** High fidelity required; Medium recommended; Low optional. Output must translate into acceptance criteria/edge cases, not new scope.
|
|
64
67
|
- **Isolation preflight is a hard gate.** `/workflow:work` must complete and record worktree/isolation preflight before any implementation commands. `/workflow:review` must do the same for non-current PR/branch targets before analysis.
|
|
65
|
-
- **Triage before execution is mandatory.** `/workflow:work` must
|
|
68
|
+
- **Triage before execution is mandatory.** `/workflow:work` must run a triage pass before executing todos to prioritize the queue and validate dependencies/ready state for the current plan. `/workflow:triage` remains available as an explicit manual command.
|
|
66
69
|
- **Spike governance is explicit and ordered.** Risky plans must evaluate spike need, spike candidates must declare initial priority/dependencies/unblocks/timebox/deliverable, triage confirms those assumptions, and `/workflow:work` executes blocking spikes before dependent build todos.
|
|
67
70
|
- **Agentic access/testability is mandatory in planning.** Every plan must include an executable access + validation contract so work/review can run deterministically.
|
|
68
71
|
- **Independent review is required for code/config changes.** `/workflow:review` must emit `review_independence_mode: independent|degraded`, plus independence evidence and skipped-pass disclosure.
|