compound-workflow 1.6.3 → 1.6.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.
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "compound-workflow",
3
- "version": "1.6.3",
3
+ "version": "1.6.5",
4
4
  "description": "Clarify -> plan -> execute -> verify -> capture workflow: commands, skills, and agents for Claude Code",
5
5
  "author": {
6
6
  "name": "Compound Workflow"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "compound-workflow",
3
- "version": "1.6.3",
3
+ "version": "1.6.5",
4
4
  "description": "Clarify -> plan -> execute -> verify -> capture workflow for Cursor",
5
5
  "author": {
6
6
  "name": "Compound Workflow"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "compound-workflow",
3
- "version": "1.6.3",
3
+ "version": "1.6.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,6 +18,7 @@
18
18
  ],
19
19
  "scripts": {
20
20
  "postinstall": "node scripts/postinstall.mjs",
21
+ "prepublishOnly": "npm run generate:artifacts",
21
22
  "generate:artifacts": "node scripts/generate-platform-artifacts.mjs",
22
23
  "check:artifacts": "node scripts/generate-platform-artifacts.mjs --check",
23
24
  "check:version-parity": "node scripts/check-version-parity.mjs",
@@ -7,6 +7,14 @@ const __dirname = path.dirname(fileURLToPath(import.meta.url));
7
7
  const repoRoot = path.resolve(__dirname, "..");
8
8
  const agentsRoot = path.join(repoRoot, "src", ".agents");
9
9
 
10
+ function loadRegistry() {
11
+ const registryPath = path.join(agentsRoot, "registry.json");
12
+ if (!fs.existsSync(registryPath)) {
13
+ throw new Error(`Registry not found at ${registryPath}`);
14
+ }
15
+ return JSON.parse(fs.readFileSync(registryPath, "utf8"));
16
+ }
17
+
10
18
  function walkFiles(dirAbs, predicate) {
11
19
  const out = [];
12
20
  const stack = [dirAbs];
@@ -42,44 +50,64 @@ function parseFrontmatter(md) {
42
50
  return out;
43
51
  }
44
52
 
45
- function discoverCommands() {
46
- const commandsDir = path.join(agentsRoot, "commands");
47
- const files = walkFiles(commandsDir, (p) => p.endsWith(".md"));
48
- const commands = [];
49
-
50
- for (const fileAbs of files) {
51
- const relWithin = path.relative(commandsDir, fileAbs).replaceAll(path.sep, "/");
52
- const frontmatter = parseFrontmatter(fs.readFileSync(fileAbs, "utf8"));
53
- const id = (frontmatter.invocation || frontmatter.name || path.basename(fileAbs, ".md")).trim();
54
- if (!id) continue;
55
- commands.push({
56
- id,
57
- description: (frontmatter.description || id).trim(),
58
- rel: relWithin,
59
- });
53
+ /**
54
+ * Resolve id from frontmatter and config. idFrom = list of frontmatter keys; idFallback = "basename" | "dirname".
55
+ */
56
+ function resolveId(frontmatter, fileAbs, dirAbs, config) {
57
+ for (const key of config.idFrom || []) {
58
+ if (key === "dirname") {
59
+ const relDir = path.relative(dirAbs, path.dirname(fileAbs));
60
+ return (relDir ? relDir.replaceAll(path.sep, "/") : path.basename(path.dirname(fileAbs))).trim();
61
+ }
62
+ const v = frontmatter[key];
63
+ if (typeof v === "string" && v.trim()) return v.trim();
60
64
  }
65
+ if (config.idFallback === "basename") return path.basename(fileAbs, ".md").trim();
66
+ if (config.idFallback === "dirname") return path.basename(path.dirname(fileAbs)).trim();
67
+ return path.basename(fileAbs, ".md").trim();
68
+ }
61
69
 
62
- return commands.sort((a, b) => a.id.localeCompare(b.id));
70
+ function resolveDescription(frontmatter, config, id) {
71
+ const key = config.descriptionFrom;
72
+ if (key && frontmatter[key] != null) return String(frontmatter[key]).trim();
73
+ if (config.descriptionFallback === "id") return id || "";
74
+ return id || "";
63
75
  }
64
76
 
65
- function discoverAgents() {
66
- const agentDir = path.join(agentsRoot, "agents");
67
- const files = walkFiles(agentDir, (p) => p.endsWith(".md"));
68
- const agents = [];
77
+ /**
78
+ * Discover assets of one type from registry config. Returns array of { id, description, rel }.
79
+ */
80
+ function discoverByType(agentsRootAbs, typeKey, config) {
81
+ const dirAbs = path.join(agentsRootAbs, config.dir);
82
+ if (!fs.existsSync(dirAbs)) return [];
83
+
84
+ let files = [];
85
+ if (config.glob === "**/*.md") {
86
+ files = walkFiles(dirAbs, (p) => p.endsWith(".md"));
87
+ } else if (config.glob === "*/SKILL.md") {
88
+ const entries = fs.readdirSync(dirAbs, { withFileTypes: true });
89
+ for (const e of entries) {
90
+ if (e.isDirectory()) {
91
+ const skillMd = path.join(dirAbs, e.name, "SKILL.md");
92
+ if (fs.existsSync(skillMd)) files.push(skillMd);
93
+ }
94
+ }
95
+ files.sort();
96
+ } else {
97
+ files = walkFiles(dirAbs, (p) => p.endsWith(".md"));
98
+ }
69
99
 
100
+ const out = [];
70
101
  for (const fileAbs of files) {
71
- const relWithin = path.relative(agentDir, fileAbs).replaceAll(path.sep, "/");
72
- const frontmatter = parseFrontmatter(fs.readFileSync(fileAbs, "utf8"));
73
- const id = (frontmatter.name || path.basename(fileAbs, ".md")).trim();
102
+ const rel = path.relative(dirAbs, fileAbs).replaceAll(path.sep, "/");
103
+ const raw = fs.readFileSync(fileAbs, "utf8");
104
+ const frontmatter = parseFrontmatter(raw);
105
+ const id = resolveId(frontmatter, fileAbs, dirAbs, config);
74
106
  if (!id) continue;
75
- agents.push({
76
- id,
77
- description: (frontmatter.description || id).trim(),
78
- rel: relWithin,
79
- });
107
+ const description = resolveDescription(frontmatter, config, id);
108
+ out.push({ id, description, rel });
80
109
  }
81
-
82
- return agents.sort((a, b) => a.id.localeCompare(b.id));
110
+ return out.sort((a, b) => a.id.localeCompare(b.id));
83
111
  }
84
112
 
85
113
  function normalizeRepoUrl(repoValue) {
@@ -105,13 +133,25 @@ function writeJson(absPath, value, checkOnly, changed) {
105
133
 
106
134
  function main() {
107
135
  const checkOnly = process.argv.includes("--check");
136
+ const registry = loadRegistry();
137
+ const roots = registry.roots?.consumer || {};
138
+ const assetTypes = registry.assetTypes || {};
139
+
140
+ const commands =
141
+ assetTypes.command != null
142
+ ? discoverByType(agentsRoot, "command", assetTypes.command)
143
+ : [];
144
+ const agents =
145
+ assetTypes.agent != null ? discoverByType(agentsRoot, "agent", assetTypes.agent) : [];
108
146
 
109
147
  const pkg = JSON.parse(fs.readFileSync(path.join(repoRoot, "package.json"), "utf8"));
110
148
  const repositoryUrl = normalizeRepoUrl(pkg.repository);
111
- const commands = discoverCommands();
112
- const agents = discoverAgents();
113
149
  const changed = [];
114
150
 
151
+ const commandRoot = roots.commands || "node_modules/compound-workflow/src/.agents/commands";
152
+ const agentRoot = roots.agents || "node_modules/compound-workflow/src/.agents/agents";
153
+ const skillsPath = roots.skills || "node_modules/compound-workflow/src/.agents/skills";
154
+
115
155
  const claudePlugin = {
116
156
  name: pkg.name,
117
157
  version: pkg.version,
@@ -140,9 +180,9 @@ function main() {
140
180
 
141
181
  const openCodeManaged = {
142
182
  $schema: "https://opencode.ai/config.json",
143
- skillsPath: "node_modules/compound-workflow/src/.agents/skills",
144
- commandRoot: "node_modules/compound-workflow/src/.agents/commands",
145
- agentRoot: "node_modules/compound-workflow/src/.agents/agents",
183
+ skillsPath,
184
+ commandRoot,
185
+ agentRoot,
146
186
  commands,
147
187
  agents,
148
188
  };
@@ -18,6 +18,8 @@ it.
18
18
  discussion-first facilitation (one-question-then-prompts), approach
19
19
  exploration patterns, and YAGNI principles.
20
20
 
21
+ It is critical that you follow this workflow in order; do not skip or shortcut steps.
22
+
21
23
  ## Guardrails
22
24
 
23
25
  - Do not write or modify application code.
@@ -27,6 +27,8 @@ Do not create drafts, notes, or intermediate files.
27
27
  After the primary solution doc is written, optional post-capture actions (by explicit user choice) may update other references (e.g. patterns indexes).
28
28
  </critical_requirement>
29
29
 
30
+ It is critical that you follow this workflow in order; do not skip or shortcut steps.
31
+
30
32
  ## Usage
31
33
 
32
34
  ```bash
@@ -49,14 +51,16 @@ After the primary solution doc is written, optional post-capture actions (by exp
49
51
  - **Implementation insight:** For feature work, confirm implementation is complete and there is a reusable learning or pattern to capture (no "problem solved" requirement for this path).
50
52
  - If critical context is missing, ask targeted questions and wait. Then **invoke the `compound-docs` skill** (it defines required fields, validation gates, and template).
51
53
 
52
- ### 2. Optional enrichment (before write)
54
+ ### 2. Required research (before write)
55
+
56
+ Research is required and must run in subagents. Do not perform research in-context. Run all research via Task (subagent). If the environment cannot run the Task, state that and perform a minimal in-context fallback, then proceed to capture.
53
57
 
54
- Run only agents that exist in this `.agents` workspace. **Enrichment is read-only**—it informs the doc content but must not write files.
58
+ Run only agents that exist in this `.agents` workspace. **Research is read-only**—it informs the doc content but must not write files.
55
59
 
56
- - Related docs: `Task learnings-researcher(<symptom/module/root cause keywords>)`
57
- - Optional: `Task best-practices-researcher(<topic>)`, `Task framework-docs-researcher(<topic>)`
60
+ - **Required:** `Task learnings-researcher(<symptom/module/root cause keywords>)` (related docs).
61
+ - **Conditional** (when the topic warrants, e.g. framework usage or cross-cutting patterns): `Task best-practices-researcher(<topic>)`, `Task framework-docs-researcher(<topic>)`. When run, these must also run as Tasks.
58
62
 
59
- Complete enrichment before assembly. Report which ran vs skipped.
63
+ Complete research before assembly. Report which enrichments ran (required: learnings-researcher; conditional: best-practices, framework-docs) and which were skipped.
60
64
 
61
65
  ### 3. Capture
62
66
 
@@ -105,14 +109,14 @@ User may optionally run `document-review` on the created doc. If the repo has do
105
109
 
106
110
  ## Success output (shape)
107
111
 
108
- Report which optional enrichments ran vs skipped. Then output the path of the created file. Present the **`compound-docs` decision menu** (see skill) and wait for user choice—do not substitute a shorter custom menu.
112
+ Report which enrichments ran (required: learnings-researcher; conditional: best-practices, framework-docs) and which were skipped. Then output the path of the created file. Present the **`compound-docs` decision menu** (see skill) and wait for user choice—do not substitute a shorter custom menu.
109
113
 
110
114
  Example summary (generic, portable):
111
115
 
112
116
  ```
113
117
  ✓ Documentation complete
114
118
 
115
- Optional enrichments: related-docs ran|skipped, best-practices ran|skipped, framework-docs ran|skipped
119
+ Enrichments: learnings-researcher (required) ran|skipped; best-practices ran|skipped; framework-docs ran|skipped
116
120
 
117
121
  File created: docs/solutions/<category>/YYYY-MM-DD-<module-slug>-<symptom-slug>.md
118
122
 
@@ -15,6 +15,8 @@ Transform feature descriptions, bug reports, or improvement ideas into well-stru
15
15
 
16
16
  Contract precedence: if this command conflicts with other workflow docs, follow `docs/principles/workflow-baseline-principles.md`, then `src/AGENTS.md`, then this command.
17
17
 
18
+ It is critical that you follow this workflow in order; do not skip or shortcut steps.
19
+
18
20
  This workflow MUST choose a planning fidelity and confidence level before final plan construction:
19
21
 
20
22
  - Fidelity: `Low | Medium | High`
@@ -833,7 +835,7 @@ Write the complete plan file to `docs/plans/YYYY-MM-DD-<type>-<slug>-plan.md`. T
833
835
 
834
836
  Confirm: "Plan written to docs/plans/[filename]"
835
837
 
836
- **Auto technical review (when Fidelity is Medium or High):** After writing the plan file, if the declared **Fidelity is Medium or High**, automatically run technical review: run **Task planning-technical-reviewer(plan_path)** when the environment can run the Task, or load the `technical-review` skill so the subagent is used when available. Use the plan path just written. If Fidelity is Low, skip this step; the user can still choose "Technical review" from Post-Generation Options or call `/workflow:tech-review` later.
838
+ **Technical review (required when Fidelity is Medium or High):** After writing the plan file, if the declared **Fidelity is Medium or High**, you **must** run technical review—it is not optional. Run **Task planning-technical-reviewer(plan_path)** using the plan path just written. Do not perform technical review in-context unless the environment cannot run the Task; if you fall back, state "planning-technical-reviewer unavailable; running direct technical review (degraded bias resistance)" and then load the `technical-review` skill and run it in-context. If Fidelity is Low, skip this step; the user can still choose "Technical review" from Post-Generation Options or call `/workflow:tech-review` later.
837
839
 
838
840
  **Non-interactive mode:** When the invocation is non-interactive (e.g., `workflow:plan` run by automation, CI, or with an explicit non-interactive flag/convention), skip AskQuestion calls and do not present Post-Generation Options. For determinism, the repo should define the flag or convention (e.g., in `AGENTS.md` Repo Config Block or a documented env var). Still **declare** Fidelity, Confidence, Solution scope, Spike evaluation, Spikes needed, Research mode, and Open questions in the required announcement format before writing the plan. Use these defaults when user input is unavailable: fidelity = Medium, confidence = Medium, solution_scope = full_remediation, spike evaluation = not-required unless risky-work triggers are present, spikes needed = n/a when spike evaluation is not required, research mode = local + external for Medium/High risk topics else local only. Proceed directly to writing the plan file and then exit or return the plan path as output.
839
841
 
@@ -863,6 +865,8 @@ Examples:
863
865
 
864
866
  ## Post-Generation Options
865
867
 
868
+ Technical review (above) is required for Medium/High fidelity and must be run via subagent when available.
869
+
866
870
  After writing the plan file, use **AskQuestion** to present these options:
867
871
 
868
872
  **Question:** "Plan ready at `docs/plans/YYYY-MM-DD-<type>-<slug>-plan.md`. What would you like to do next?"
@@ -13,6 +13,8 @@ Contract precedence: if this command conflicts with other workflow docs, follow
13
13
 
14
14
  **If the user provides a document path** (e.g. a plan or spec): redirect to the `technical-review` skill for technical correctness (no edits), and/or the `document-review` skill to refine the document. This command does not review documents.
15
15
 
16
+ It is critical that you follow this workflow in order; do not skip or shortcut steps.
17
+
16
18
  Guardrails (unless explicitly requested):
17
19
 
18
20
  - Do not modify code or documents
@@ -11,6 +11,8 @@ Run technical review on a feature approach or plan document. Checks technical al
11
11
 
12
12
  Contract precedence: if this command conflicts with other workflow docs, follow `docs/principles/workflow-baseline-principles.md`, then `src/AGENTS.md`, then this command.
13
13
 
14
+ It is critical that you follow this workflow in order; do not skip or shortcut steps.
15
+
14
16
  ## Inputs
15
17
 
16
18
  - **Plan path (optional):** `$ARGUMENTS` — path to the plan file (e.g. `docs/plans/YYYY-MM-DD-<type>-<slug>-plan.md`).
@@ -16,6 +16,8 @@ Output of this command is the only executable queue for `/workflow:work`.
16
16
 
17
17
  Contract precedence: if this command conflicts with other workflow docs, follow `docs/principles/workflow-baseline-principles.md`, then `src/AGENTS.md`, then this command.
18
18
 
19
+ It is critical that you follow this workflow in order; do not skip or shortcut steps.
20
+
19
21
  ## Inputs
20
22
 
21
23
  - Default: triage all active todos under `todos/` (`pending` + `ready`)
@@ -15,6 +15,8 @@ This command takes a plan file and executes it systematically. The focus is on c
15
15
 
16
16
  Contract precedence: if this command conflicts with other workflow docs, follow `docs/principles/workflow-baseline-principles.md`, then `src/AGENTS.md`, then this command.
17
17
 
18
+ It is critical that you follow this workflow in order; do not skip or shortcut steps.
19
+
18
20
  Non-goals (unless explicitly requested):
19
21
 
20
22
  - Creating commits
@@ -0,0 +1,48 @@
1
+ {
2
+ "$schema": "https://compound-workflow.dev/registry.schema.json",
3
+ "roots": {
4
+ "package": {
5
+ "commands": "src/.agents/commands",
6
+ "agents": "src/.agents/agents",
7
+ "skills": "src/.agents/skills",
8
+ "references": "src/.agents/references"
9
+ },
10
+ "consumer": {
11
+ "commands": "node_modules/compound-workflow/src/.agents/commands",
12
+ "agents": "node_modules/compound-workflow/src/.agents/agents",
13
+ "skills": "node_modules/compound-workflow/src/.agents/skills",
14
+ "references": "node_modules/compound-workflow/src/.agents/references"
15
+ }
16
+ },
17
+ "assetTypes": {
18
+ "command": {
19
+ "dir": "commands",
20
+ "glob": "**/*.md",
21
+ "idFrom": ["invocation", "name"],
22
+ "idFallback": "basename",
23
+ "descriptionFrom": "description",
24
+ "descriptionFallback": "id",
25
+ "output": ["opencode.command", "plugin.commands"]
26
+ },
27
+ "agent": {
28
+ "dir": "agents",
29
+ "glob": "**/*.md",
30
+ "idFrom": ["name"],
31
+ "idFallback": "basename",
32
+ "descriptionFrom": "description",
33
+ "descriptionFallback": "id",
34
+ "output": ["opencode.agent", "plugin.agents"]
35
+ },
36
+ "skill": {
37
+ "dir": "skills",
38
+ "glob": "*/SKILL.md",
39
+ "idFrom": ["dirname"],
40
+ "output": ["opencode.skillsPath", "plugin.skills"]
41
+ },
42
+ "reference": {
43
+ "dir": "references",
44
+ "glob": "**/*.md",
45
+ "output": ["path"]
46
+ }
47
+ }
48
+ }