agent-method 1.5.12

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.
Files changed (108) hide show
  1. package/README.md +343 -0
  2. package/bin/wwa.js +115 -0
  3. package/docs/internal/cli-commands.yaml +259 -0
  4. package/docs/internal/doc-tokens.yaml +1103 -0
  5. package/docs/internal/feature-registry.yaml +1643 -0
  6. package/lib/boundaries.js +247 -0
  7. package/lib/cli/add.js +170 -0
  8. package/lib/cli/casestudy.js +1000 -0
  9. package/lib/cli/check.js +323 -0
  10. package/lib/cli/close.js +838 -0
  11. package/lib/cli/completion.js +735 -0
  12. package/lib/cli/deps.js +234 -0
  13. package/lib/cli/digest.js +73 -0
  14. package/lib/cli/doc-review.js +486 -0
  15. package/lib/cli/docs.js +315 -0
  16. package/lib/cli/helpers.js +198 -0
  17. package/lib/cli/implement.js +169 -0
  18. package/lib/cli/init.js +280 -0
  19. package/lib/cli/pipeline.js +206 -0
  20. package/lib/cli/plan.js +140 -0
  21. package/lib/cli/record.js +98 -0
  22. package/lib/cli/refine.js +202 -0
  23. package/lib/cli/report-helpers.js +113 -0
  24. package/lib/cli/review.js +76 -0
  25. package/lib/cli/routable.js +109 -0
  26. package/lib/cli/route.js +101 -0
  27. package/lib/cli/scan.js +133 -0
  28. package/lib/cli/serve.js +23 -0
  29. package/lib/cli/status.js +65 -0
  30. package/lib/cli/update-docs.js +574 -0
  31. package/lib/cli/upgrade.js +222 -0
  32. package/lib/cli/watch.js +32 -0
  33. package/lib/dependencies.js +196 -0
  34. package/lib/init.js +692 -0
  35. package/lib/mcp-server.js +612 -0
  36. package/lib/pipeline.js +907 -0
  37. package/lib/registry.js +132 -0
  38. package/lib/watcher.js +165 -0
  39. package/package.json +54 -0
  40. package/templates/README.md +363 -0
  41. package/templates/entry-points/.cursorrules +90 -0
  42. package/templates/entry-points/AGENT.md +90 -0
  43. package/templates/entry-points/CLAUDE.md +88 -0
  44. package/templates/extensions/MANIFEST.md +110 -0
  45. package/templates/extensions/analytical-system.md +96 -0
  46. package/templates/extensions/code-project.md +77 -0
  47. package/templates/extensions/data-exploration.md +117 -0
  48. package/templates/full/.context/BASE.md +101 -0
  49. package/templates/full/.context/COMPOSITION.md +47 -0
  50. package/templates/full/.context/INDEX.yaml +56 -0
  51. package/templates/full/.context/METHODOLOGY.md +246 -0
  52. package/templates/full/.context/PROTOCOL.yaml +169 -0
  53. package/templates/full/.context/REGISTRY.md +75 -0
  54. package/templates/full/.cursorrules +90 -0
  55. package/templates/full/AGENT.md +90 -0
  56. package/templates/full/CLAUDE.md +90 -0
  57. package/templates/full/Management/DIGEST.md +23 -0
  58. package/templates/full/Management/STATUS.md +46 -0
  59. package/templates/full/PLAN.md +67 -0
  60. package/templates/full/PROJECT-PROFILE.md +61 -0
  61. package/templates/full/PROJECT.md +80 -0
  62. package/templates/full/REQUIREMENTS.md +30 -0
  63. package/templates/full/ROADMAP.md +39 -0
  64. package/templates/full/Reviews/INDEX.md +41 -0
  65. package/templates/full/Reviews/backlog.md +52 -0
  66. package/templates/full/Reviews/plan.md +43 -0
  67. package/templates/full/Reviews/project.md +41 -0
  68. package/templates/full/Reviews/requirements.md +42 -0
  69. package/templates/full/Reviews/roadmap.md +41 -0
  70. package/templates/full/Reviews/state.md +56 -0
  71. package/templates/full/SESSION-LOG.md +102 -0
  72. package/templates/full/STATE.md +42 -0
  73. package/templates/full/SUMMARY.md +27 -0
  74. package/templates/full/agentWorkflows/INDEX.md +42 -0
  75. package/templates/full/agentWorkflows/observations.md +65 -0
  76. package/templates/full/agentWorkflows/patterns.md +68 -0
  77. package/templates/full/agentWorkflows/sessions.md +92 -0
  78. package/templates/full/intro/README.md +39 -0
  79. package/templates/full/registry/feature-registry.yaml +25 -0
  80. package/templates/full/registry/features/catalog.yaml +743 -0
  81. package/templates/full/registry/features/protocol.yaml +121 -0
  82. package/templates/full/registry/features/routing.yaml +358 -0
  83. package/templates/full/registry/features/workflows.yaml +404 -0
  84. package/templates/full/todos/backlog.md +19 -0
  85. package/templates/starter/.context/BASE.md +66 -0
  86. package/templates/starter/.context/INDEX.yaml +51 -0
  87. package/templates/starter/.context/METHODOLOGY.md +228 -0
  88. package/templates/starter/.context/PROTOCOL.yaml +165 -0
  89. package/templates/starter/.cursorrules +90 -0
  90. package/templates/starter/AGENT.md +90 -0
  91. package/templates/starter/CLAUDE.md +90 -0
  92. package/templates/starter/Management/DIGEST.md +23 -0
  93. package/templates/starter/Management/STATUS.md +46 -0
  94. package/templates/starter/PLAN.md +67 -0
  95. package/templates/starter/PROJECT-PROFILE.md +44 -0
  96. package/templates/starter/PROJECT.md +80 -0
  97. package/templates/starter/ROADMAP.md +39 -0
  98. package/templates/starter/Reviews/INDEX.md +75 -0
  99. package/templates/starter/SESSION-LOG.md +102 -0
  100. package/templates/starter/STATE.md +42 -0
  101. package/templates/starter/SUMMARY.md +27 -0
  102. package/templates/starter/agentWorkflows/INDEX.md +61 -0
  103. package/templates/starter/intro/README.md +37 -0
  104. package/templates/starter/registry/feature-registry.yaml +25 -0
  105. package/templates/starter/registry/features/catalog.yaml +743 -0
  106. package/templates/starter/registry/features/protocol.yaml +121 -0
  107. package/templates/starter/registry/features/routing.yaml +358 -0
  108. package/templates/starter/registry/features/workflows.yaml +404 -0
@@ -0,0 +1,98 @@
1
+ /**
2
+ * wwa record — append the last CLI run summary to SESSION-LOG.md.
3
+ */
4
+
5
+ import { readFileSync, existsSync } from "node:fs";
6
+ import { resolve } from "node:path";
7
+ import { findSessionLog, safeWriteFile } from "./helpers.js";
8
+ import {
9
+ readLastRunSummary,
10
+ clearLastRunSummary,
11
+ parseLastRunContent,
12
+ } from "./report-helpers.js";
13
+
14
+ export function register(program) {
15
+ program
16
+ .command("record [directory]")
17
+ .description("Record the last CLI run summary to SESSION-LOG.md")
18
+ .option("-d, --directory <path>", "Project directory", ".")
19
+ .option("--dry-run", "Show what would be appended without writing")
20
+ .option("--json", "Output as JSON")
21
+ .action(async (directory, cmd) => {
22
+ const opts = typeof cmd?.opts === "function" ? cmd.opts() : (cmd || {});
23
+ const dir = resolve(opts.directory || directory || ".");
24
+ const content = readLastRunSummary(dir);
25
+
26
+ if (!content) {
27
+ const msg =
28
+ "No recent CLI output to record. Run a wwa command (e.g. wwa route '…', wwa scan) first.";
29
+ if (opts.json) {
30
+ console.log(JSON.stringify({ recorded: false, error: msg }, null, 2));
31
+ } else {
32
+ console.log(`\n ${msg}\n`);
33
+ }
34
+ return;
35
+ }
36
+
37
+ const parsed = parseLastRunContent(content);
38
+ if (!parsed) {
39
+ const msg = "Last run file was invalid; nothing to record.";
40
+ if (opts.json) {
41
+ console.log(JSON.stringify({ recorded: false, error: msg }, null, 2));
42
+ } else {
43
+ console.log(`\n ${msg}\n`);
44
+ }
45
+ return;
46
+ }
47
+
48
+ const date = parsed.timestamp.slice(0, 10);
49
+ const block = [
50
+ "",
51
+ `### CLI run — ${date} — ${parsed.command}`,
52
+ "",
53
+ `Command: wwa ${parsed.command}`,
54
+ `Input: ${parsed.inputRef}`,
55
+ `Summary: ${parsed.summary}`,
56
+ "",
57
+ ].join("\n");
58
+
59
+ const sessionLog = findSessionLog(dir);
60
+ if (!sessionLog) {
61
+ const msg = "No SESSION-LOG.md found. Create it or run from project root.";
62
+ if (opts.json) {
63
+ console.log(JSON.stringify({ recorded: false, error: msg }, null, 2));
64
+ } else {
65
+ console.log(`\n ${msg}\n`);
66
+ }
67
+ return;
68
+ }
69
+
70
+ if (opts.dryRun) {
71
+ if (opts.json) {
72
+ console.log(
73
+ JSON.stringify(
74
+ { dryRun: true, target: sessionLog, block: block.trim() },
75
+ null,
76
+ 2
77
+ )
78
+ );
79
+ } else {
80
+ console.log("\n Dry run — would append to", sessionLog);
81
+ console.log(block);
82
+ }
83
+ return;
84
+ }
85
+
86
+ const existing = readFileSync(sessionLog, "utf-8");
87
+ safeWriteFile(sessionLog, existing + block, "utf-8");
88
+ clearLastRunSummary(dir);
89
+
90
+ if (opts.json) {
91
+ console.log(
92
+ JSON.stringify({ recorded: true, target: sessionLog }, null, 2)
93
+ );
94
+ } else {
95
+ console.log("\n Recorded last CLI run to SESSION-LOG.md.\n");
96
+ }
97
+ });
98
+ }
@@ -0,0 +1,202 @@
1
+ /** wwa refine — extract refinement report from session history. */
2
+
3
+ import { readFileSync, existsSync } from "node:fs";
4
+ import { findSessionLog, safeWriteFile } from "./helpers.js";
5
+
6
+ export function register(program) {
7
+ program
8
+ .command("refine [session-log]")
9
+ .description("Extract a refinement report from session history")
10
+ .option("-o, --output <path>", "Output file (default: stdout)")
11
+ .option("--json", "Output as JSON")
12
+ .action(async (sessionLog, opts) => {
13
+ if (!sessionLog) {
14
+ sessionLog = findSessionLog(".");
15
+ if (!sessionLog) {
16
+ console.error(
17
+ "No SESSION-LOG.md found in current directory.\n" +
18
+ "Specify a path: wwa refine path/to/SESSION-LOG.md"
19
+ );
20
+ process.exit(1);
21
+ }
22
+ }
23
+
24
+ if (!existsSync(sessionLog)) {
25
+ console.error(`File not found: ${sessionLog}`);
26
+ process.exit(1);
27
+ }
28
+
29
+ const content = readFileSync(sessionLog, "utf-8");
30
+ const parsed = parseSessionLog(content);
31
+
32
+ let result;
33
+ if (opts.json) {
34
+ result = JSON.stringify(parsed, null, 2);
35
+ } else {
36
+ result = generateRefinementReport(parsed);
37
+ }
38
+
39
+ if (opts.output) {
40
+ safeWriteFile(opts.output, result, "utf-8");
41
+ console.log(
42
+ `Refinement report written to ${opts.output} (${parsed.entries.length} sessions)`
43
+ );
44
+ } else {
45
+ console.log(result);
46
+ }
47
+ });
48
+ }
49
+
50
+ // ---------------------------------------------------------------------------
51
+ // Session log parsing
52
+ // ---------------------------------------------------------------------------
53
+
54
+ function parseSessionLog(content) {
55
+ const context = {};
56
+ const ctxMatch = content.match(
57
+ /## Project context\s*\n\s*\|[^\n]+\n\s*\|[-| ]+\n((?:\|[^\n]+\n)*)/
58
+ );
59
+ if (ctxMatch) {
60
+ for (const row of ctxMatch[1].trim().split("\n")) {
61
+ const cols = row
62
+ .split("|")
63
+ .map((c) => c.trim())
64
+ .filter((c) => c);
65
+ if (cols.length >= 2) {
66
+ context[cols[0].toLowerCase()] = cols[1];
67
+ }
68
+ }
69
+ }
70
+
71
+ const entries = [];
72
+ const entryPattern =
73
+ /###\s+S(\d+)\s*(?:\u2014|--)\s*(\S+)\s*(?:\u2014|--)\s*(.+?)$(.*?)(?=###\s+S\d+|$)/gms;
74
+ let m;
75
+ while ((m = entryPattern.exec(content)) !== null) {
76
+ const [, num, date, title, body] = m;
77
+ const entry = { session: parseInt(num, 10), date, title: title.trim() };
78
+
79
+ for (const line of body.split("\n")) {
80
+ const trimmed = line.trim();
81
+ if (!trimmed || trimmed.startsWith("#")) continue;
82
+ const segments = trimmed.split("|").map((s) => s.trim());
83
+ for (const seg of segments) {
84
+ if (seg.includes(":")) {
85
+ const idx = seg.indexOf(":");
86
+ const key = seg.slice(0, idx).trim().toLowerCase();
87
+ const val = seg.slice(idx + 1).trim();
88
+ if (
89
+ [
90
+ "model", "profile", "workflow", "queries",
91
+ "features", "cascades", "decisions", "friction", "finding",
92
+ ].includes(key)
93
+ ) {
94
+ entry[key] = val;
95
+ }
96
+ }
97
+ }
98
+ }
99
+ entries.push(entry);
100
+ }
101
+
102
+ return { context, entries };
103
+ }
104
+
105
+ function generateRefinementReport(parsed) {
106
+ const ctx = parsed.context;
107
+ const entries = parsed.entries;
108
+
109
+ const project = ctx["project name"] || "Unknown";
110
+ const ptype = ctx["project type"] || "unknown";
111
+ const profile = ctx["integration profile"] || "unknown";
112
+ const extensions = ctx["extension(s)"] || "none";
113
+
114
+ const workflows = new Set();
115
+ let totalFriction = 0;
116
+ let totalFindings = 0;
117
+ const findingsList = [];
118
+ const frictionList = [];
119
+
120
+ for (const e of entries) {
121
+ if (e.workflow) workflows.add(e.workflow);
122
+ const friction = e.friction || "none";
123
+ if (friction.toLowerCase() !== "none" && friction) {
124
+ totalFriction++;
125
+ frictionList.push(`S${e.session} (${e.date}): ${friction}`);
126
+ }
127
+ const finding = e.finding || "none";
128
+ if (finding.toLowerCase() !== "none" && finding) {
129
+ totalFindings++;
130
+ findingsList.push(`S${e.session} (${e.date}): ${finding}`);
131
+ }
132
+ }
133
+
134
+ const wfSorted = [...workflows].sort().join(", ") || "none";
135
+ const lines = [
136
+ `# Refinement Report: ${project}`,
137
+ "",
138
+ "Auto-generated from SESSION-LOG.md by `wwa refine`.",
139
+ "",
140
+ "## Source",
141
+ "",
142
+ "| Field | Value |",
143
+ "|-------|-------|",
144
+ `| Project type | ${ptype} |`,
145
+ `| Extension(s) | ${extensions} |`,
146
+ `| Workflow(s) exercised | ${wfSorted} |`,
147
+ `| Integration profile | ${profile} |`,
148
+ `| Sessions | ${entries.length} |`,
149
+ "",
150
+ "## Session summary",
151
+ "",
152
+ "| # | Date | Title | Workflow | Friction | Finding |",
153
+ "|---|------|-------|----------|----------|---------|",
154
+ ];
155
+
156
+ for (const e of entries) {
157
+ const friction = e.friction || "none";
158
+ const finding = e.finding || "none";
159
+ const wf = e.workflow || "\u2014";
160
+ lines.push(
161
+ `| S${e.session} | ${e.date} | ${e.title} | ${wf} | ${friction} | ${finding} |`
162
+ );
163
+ }
164
+
165
+ lines.push(
166
+ "", "## Statistics", "",
167
+ `- **Total sessions**: ${entries.length}`,
168
+ `- **Workflows used**: ${wfSorted}`,
169
+ `- **Sessions with friction**: ${totalFriction}`,
170
+ `- **Sessions with findings**: ${totalFindings}`,
171
+ ""
172
+ );
173
+
174
+ if (frictionList.length > 0) {
175
+ lines.push("## Friction points", "");
176
+ for (const f of frictionList) lines.push(`- ${f}`);
177
+ lines.push("");
178
+ }
179
+
180
+ if (findingsList.length > 0) {
181
+ lines.push("## Findings", "");
182
+ for (const f of findingsList) lines.push(`- ${f}`);
183
+ lines.push("");
184
+ }
185
+
186
+ lines.push(
187
+ "## Dimension scores", "",
188
+ "Fill in after reviewing the session data above:", "",
189
+ "| Dimension | Score | One-line summary |",
190
+ "|-----------|:-----:|-----------------|",
191
+ "| Context quality | \u2014 | |",
192
+ "| Decision preservation | \u2014 | |",
193
+ "| Scope discipline | \u2014 | |",
194
+ "| Cascade coverage | \u2014 | |",
195
+ "| Audit completeness | \u2014 | |",
196
+ "| Bootstrap speed | \u2014 | |",
197
+ "| Lifecycle fit | \u2014 | |",
198
+ "| Model adequacy | \u2014 | |"
199
+ );
200
+
201
+ return lines.join("\n") + "\n";
202
+ }
@@ -0,0 +1,113 @@
1
+ /**
2
+ * CLI report and last-run summary helpers.
3
+ * Used to print human-readable input + summary and to persist the last run for `wwa record`.
4
+ */
5
+
6
+ import { readFileSync, existsSync, mkdirSync, unlinkSync } from "node:fs";
7
+ import { resolve, join } from "node:path";
8
+ import { safeWriteFile } from "./helpers.js";
9
+
10
+ const LAST_RUN_FILENAME = "last-cli-run.md";
11
+ const LAST_RUN_DIR = ".context";
12
+
13
+ /**
14
+ * Print the standard CLI report block (input reference + summary).
15
+ * Call before printing command-specific detail when not --json.
16
+ * @param {{ command: string, inputRef: string, summary: string, skipDetail?: boolean }} opts
17
+ */
18
+ export function printCliReport(opts) {
19
+ const { command, inputRef, summary, skipDetail } = opts;
20
+ console.log("\n Input");
21
+ console.log(` Command: wwa ${command}`);
22
+ console.log(` ${inputRef}`);
23
+ console.log("\n Summary");
24
+ for (const line of summary.split("\n").filter(Boolean)) {
25
+ console.log(` ${line.trim()}`);
26
+ }
27
+ if (!skipDetail) {
28
+ console.log("");
29
+ }
30
+ }
31
+
32
+ /**
33
+ * Path to the last-run file under the given project root.
34
+ * @param {string} dir - Project root (resolved)
35
+ */
36
+ function lastRunPath(dir) {
37
+ return join(resolve(dir), LAST_RUN_DIR, LAST_RUN_FILENAME);
38
+ }
39
+
40
+ /**
41
+ * Write the last-run summary so `wwa record` can append it to SESSION-LOG.
42
+ * Only call when not --json. Creates .context/ if needed.
43
+ * @param {string} dir - Project root
44
+ * @param {{ command: string, inputRef: string, summary: string }} data
45
+ */
46
+ export function writeLastRunSummary(dir, data) {
47
+ const d = resolve(dir);
48
+ const dirPath = join(d, LAST_RUN_DIR);
49
+ if (!existsSync(dirPath)) {
50
+ mkdirSync(dirPath, { recursive: true });
51
+ }
52
+ const ts = new Date().toISOString();
53
+ const content = [
54
+ `command: ${data.command}`,
55
+ `inputRef: ${data.inputRef.replace(/\n/g, " ")}`,
56
+ `summary: ${data.summary.replace(/\n/g, " ").trim()}`,
57
+ `timestamp: ${ts}`,
58
+ ].join("\n");
59
+ const path = lastRunPath(d);
60
+ safeWriteFile(path, content, "utf-8");
61
+ }
62
+
63
+ /**
64
+ * Read the last-run summary content, or null if missing.
65
+ * @param {string} dir - Project root
66
+ * @returns {string | null}
67
+ */
68
+ export function readLastRunSummary(dir) {
69
+ const path = lastRunPath(resolve(dir));
70
+ if (!existsSync(path)) return null;
71
+ try {
72
+ return readFileSync(path, "utf-8").trim();
73
+ } catch {
74
+ return null;
75
+ }
76
+ }
77
+
78
+ /**
79
+ * Remove the last-run file after a successful record.
80
+ * @param {string} dir - Project root
81
+ */
82
+ export function clearLastRunSummary(dir) {
83
+ const path = lastRunPath(resolve(dir));
84
+ if (existsSync(path)) {
85
+ try {
86
+ unlinkSync(path);
87
+ } catch {
88
+ // ignore
89
+ }
90
+ }
91
+ }
92
+
93
+ /**
94
+ * Parse last-run file content into { command, inputRef, summary, timestamp }.
95
+ * @param {string} content
96
+ * @returns {{ command: string, inputRef: string, summary: string, timestamp: string } | null}
97
+ */
98
+ export function parseLastRunContent(content) {
99
+ if (!content || !content.trim()) return null;
100
+ const out = { command: "", inputRef: "", summary: "", timestamp: "" };
101
+ for (const line of content.split("\n")) {
102
+ const colon = line.indexOf(":");
103
+ if (colon <= 0) continue;
104
+ const key = line.slice(0, colon).trim();
105
+ const val = line.slice(colon + 1).trim();
106
+ if (key === "command") out.command = val;
107
+ else if (key === "inputRef") out.inputRef = val;
108
+ else if (key === "summary") out.summary = val;
109
+ else if (key === "timestamp") out.timestamp = val;
110
+ }
111
+ if (!out.command) return null;
112
+ return out;
113
+ }
@@ -0,0 +1,76 @@
1
+ /** wwa review — display project review dashboard. */
2
+
3
+ import { readFileSync, existsSync } from "node:fs";
4
+ import { resolve, join } from "node:path";
5
+ import { printCliReport, writeLastRunSummary } from "./report-helpers.js";
6
+
7
+ export function register(program) {
8
+ program
9
+ .command("review [directory]")
10
+ .description("Display project review dashboard from Reviews/INDEX.md")
11
+ .option("--json", "Output as JSON")
12
+ .action(async (directory, opts) => {
13
+ directory = directory || ".";
14
+ const d = resolve(directory);
15
+
16
+ const reviewIndex = join(d, "Reviews", "INDEX.md");
17
+ const hasReviews = existsSync(reviewIndex);
18
+
19
+ // Fallback: synthesize from intelligence layer files
20
+ const sources = {
21
+ roadmap: join(d, "ROADMAP.md"),
22
+ plan: join(d, "PLAN.md"),
23
+ state: join(d, "STATE.md"),
24
+ requirements: join(d, "REQUIREMENTS.md"),
25
+ backlog: join(d, "todos", "backlog.md"),
26
+ };
27
+
28
+ const available = {};
29
+ for (const [key, path] of Object.entries(sources)) {
30
+ available[key] = existsSync(path);
31
+ }
32
+
33
+ if (opts.json) {
34
+ const data = { hasReviewsDir: hasReviews, available };
35
+ if (hasReviews) {
36
+ data.indexContent = readFileSync(reviewIndex, "utf-8");
37
+ }
38
+ console.log(JSON.stringify(data, null, 2));
39
+ return;
40
+ }
41
+
42
+ const inputRef = `directory=${directory}`;
43
+ if (hasReviews) {
44
+ const content = readFileSync(reviewIndex, "utf-8");
45
+ const summary = "Reviews/INDEX.md found. Dashboard content below.";
46
+ printCliReport({ command: "review", inputRef, summary });
47
+ console.log(" Detail\n");
48
+ console.log(content);
49
+ writeLastRunSummary(d, { command: "review", inputRef, summary });
50
+ return;
51
+ }
52
+
53
+ const count = Object.values(available).filter(Boolean).length;
54
+ const summary = `No Reviews/ directory. ${count} source(s) available (roadmap, plan, state, requirements, backlog). Use full template for full reviews.`;
55
+ printCliReport({ command: "review", inputRef, summary });
56
+ console.log(" Detail — Project Review Dashboard\n");
57
+ console.log(" No Reviews/ directory found. Showing available sources:\n");
58
+
59
+ for (const [key, path] of Object.entries(sources)) {
60
+ if (available[key]) {
61
+ const content = readFileSync(path, "utf-8");
62
+ const lines = content.split("\n").length;
63
+ const title = content.match(/^#\s+(.+)$/m)?.[1] || key;
64
+ console.log(` [+] ${key.padEnd(14)} ${title} (${lines} lines)`);
65
+ } else {
66
+ console.log(` [ ] ${key.padEnd(14)} not found`);
67
+ }
68
+ }
69
+
70
+ console.log(
71
+ "\n To get full reviews, use the full template tier:" +
72
+ "\n wwa init <type> --tier full\n"
73
+ );
74
+ writeLastRunSummary(d, { command: "review", inputRef, summary });
75
+ });
76
+ }
@@ -0,0 +1,109 @@
1
+ /**
2
+ * Phase 7p routable workflow commands.
3
+ * Each command invokes the pipeline with a fixed query_type (no natural-language classification).
4
+ */
5
+
6
+ import {
7
+ resolveProjectType,
8
+ getPipeline,
9
+ loadRegistryData,
10
+ } from "./helpers.js";
11
+ import { orderByDependencies } from "../dependencies.js";
12
+ import { loadDocGraph } from "../boundaries.js";
13
+ import { printCliReport, writeLastRunSummary } from "./report-helpers.js";
14
+
15
+ const ROUTABLE_COMMANDS = [
16
+ // Phase 1 set (Phase 7p)
17
+ { name: "project-discovery", queryType: "project_discovery", description: "Run project discovery workflow" },
18
+ { name: "context-refresh", queryType: "context_refresh", description: "Run context refresh workflow" },
19
+ { name: "phase-complete", queryType: "phase_completion", description: "Run phase completion workflow" },
20
+ { name: "backlog", queryType: "backlog", description: "Open/edit backlog (show routing for backlog)" },
21
+ // Phase 2 set
22
+ { name: "plan-create", queryType: "planning", description: "Run planning workflow (create/update plan)" },
23
+ { name: "dependency-analysis", queryType: "dependency_analysis", description: "Run dependency analysis workflow" },
24
+ { name: "debt-assessment", queryType: "debt_assessment", description: "Run debt assessment workflow" },
25
+ // Phase 3 set
26
+ { name: "docs-update", queryType: "docs_update", description: "Run docs update workflow" },
27
+ { name: "cross-reference", queryType: "cross_reference", description: "Run cross-reference workflow" },
28
+ ];
29
+
30
+ function outputRouteResult(result, label, asJson) {
31
+ if (asJson) {
32
+ console.log(JSON.stringify(result, null, 2));
33
+ return;
34
+ }
35
+ console.log(` Detail — Routing: ${label} (type: ${result.project_type})`);
36
+ for (const section of ["S1_classify", "S2_select", "S3_resolve", "S4_compute"]) {
37
+ console.log(`\n ${section}:`);
38
+ for (const [k, v] of Object.entries(result[section] || {})) {
39
+ if (Array.isArray(v)) {
40
+ console.log(` ${k}: [${v.join(", ")}]`);
41
+ } else {
42
+ console.log(` ${k}: ${v}`);
43
+ }
44
+ }
45
+ }
46
+ if (result.S4b_order) {
47
+ console.log(`\n S4b_order (dependency-ordered reads):`);
48
+ for (let i = 0; i < result.S4b_order.ordered_reads.length; i++) {
49
+ console.log(` ${i + 1}. ${result.S4b_order.ordered_reads[i]}`);
50
+ }
51
+ }
52
+ }
53
+
54
+ export function register(program) {
55
+ for (const { name, queryType, description } of ROUTABLE_COMMANDS) {
56
+ program
57
+ .command(name)
58
+ .description(description)
59
+ .option("-p, --project-type <type>", "Project type: code, context, data, mix, general (auto-detected if omitted)")
60
+ .option("-s, --stage <stage>", "Workflow stage", "scope")
61
+ .option("--first-session", "Force WF-04 bootstrap")
62
+ .option("--registry <path>", "Path to feature-registry.yaml")
63
+ .option("--json", "Output as JSON")
64
+ .action(async function () {
65
+ const opts = this.opts();
66
+ const { routeByQueryType, detectProjectType } = await getPipeline();
67
+ const reg = await loadRegistryData(opts.registry);
68
+
69
+ let projectType;
70
+ if (opts.projectType) {
71
+ projectType = resolveProjectType(opts.projectType);
72
+ } else {
73
+ const detected = detectProjectType(".");
74
+ projectType = detected.project_type || "general";
75
+ }
76
+
77
+ const result = routeByQueryType(
78
+ queryType,
79
+ projectType,
80
+ opts.stage,
81
+ reg,
82
+ opts.firstSession || false
83
+ );
84
+
85
+ const docGraph = await loadDocGraph(".");
86
+ if (docGraph && result.S4_compute?.read_set) {
87
+ const orderedReads = orderByDependencies(result.S4_compute.read_set, docGraph);
88
+ result.S4b_order = { ordered_reads: orderedReads, ordering_source: "doc_graph" };
89
+ }
90
+
91
+ if (opts.json) {
92
+ outputRouteResult(result, `[${queryType}]`, true);
93
+ } else {
94
+ const readSet = result.S4b_order?.ordered_reads || result.S4_compute?.read_set || [];
95
+ const writeSet = result.S4_compute?.write_set || [];
96
+ const workflowId = result.S2_select?.workflow_id || result.S2_select?.workflow || "—";
97
+ const summary = [
98
+ `Workflow ${workflowId}.`,
99
+ readSet.length ? `Read: ${readSet.slice(0, 5).join(", ")}${readSet.length > 5 ? "…" : ""}.` : "No read set.",
100
+ writeSet.length ? `Write: ${writeSet.slice(0, 5).join(", ")}${writeSet.length > 5 ? "…" : ""}.` : "No write set.",
101
+ ].join(" ");
102
+ const inputRef = `workflow=${queryType}, project type=${projectType}`;
103
+ printCliReport({ command: name, inputRef, summary });
104
+ outputRouteResult(result, `[${queryType}]`, false);
105
+ writeLastRunSummary(".", { command: name, inputRef, summary });
106
+ }
107
+ });
108
+ }
109
+ }
@@ -0,0 +1,101 @@
1
+ /** wwa route — show how a query routes through the pipeline. */
2
+
3
+ import {
4
+ resolveProjectType,
5
+ getPipeline,
6
+ loadRegistryData,
7
+ } from "./helpers.js";
8
+ import { orderByDependencies } from "../dependencies.js";
9
+ import { loadDocGraph } from "../boundaries.js";
10
+ import { printCliReport, writeLastRunSummary } from "./report-helpers.js";
11
+
12
+ export function register(program) {
13
+ program
14
+ .command("route <query>")
15
+ .description("Show how a query routes through the pipeline")
16
+ .option(
17
+ "-p, --project-type <type>",
18
+ "Project type: code, context, data, mix, general (auto-detected if omitted)"
19
+ )
20
+ .option("-s, --stage <stage>", "Workflow stage", "scope")
21
+ .option("--first-session", "Force WF-04 bootstrap")
22
+ .option("--registry <path>", "Path to feature-registry.yaml")
23
+ .option("--json", "Output as JSON")
24
+ .action(async (query, opts) => {
25
+ const { route, detectProjectType } = await getPipeline();
26
+ const reg = await loadRegistryData(opts.registry);
27
+
28
+ let projectType;
29
+ if (opts.projectType) {
30
+ projectType = resolveProjectType(opts.projectType);
31
+ } else {
32
+ const detected = detectProjectType(".");
33
+ projectType = detected.project_type || "general";
34
+ }
35
+
36
+ const result = route(
37
+ query,
38
+ projectType,
39
+ opts.stage,
40
+ reg,
41
+ opts.firstSession || false
42
+ );
43
+
44
+ // S4b: dependency-ordered reads (optional — requires doc_graph)
45
+ const docGraph = await loadDocGraph(".");
46
+ if (docGraph && result.S4_compute?.read_set) {
47
+ const orderedReads = orderByDependencies(result.S4_compute.read_set, docGraph);
48
+ result.S4b_order = {
49
+ ordered_reads: orderedReads,
50
+ ordering_source: "doc_graph",
51
+ };
52
+ }
53
+
54
+ if (opts.json) {
55
+ console.log(JSON.stringify(result, null, 2));
56
+ } else {
57
+ const readSet = result.S4b_order?.ordered_reads || result.S4_compute?.read_set || [];
58
+ const writeSet = result.S4_compute?.write_set || [];
59
+ const workflowId = result.S2_select?.workflow_id || result.S2_select?.workflow || "—";
60
+ const summary = [
61
+ `Query routes to workflow ${workflowId}.`,
62
+ readSet.length
63
+ ? `Read these files in order: ${readSet.slice(0, 5).join(", ")}${readSet.length > 5 ? "…" : ""}.`
64
+ : "No read set.",
65
+ writeSet.length
66
+ ? `Write set: ${writeSet.slice(0, 5).join(", ")}${writeSet.length > 5 ? "…" : ""}.`
67
+ : "No write set.",
68
+ ].join(" ");
69
+ const inputRef = `query="${query}", project type=${projectType}`;
70
+ printCliReport({
71
+ command: "route",
72
+ inputRef,
73
+ summary,
74
+ });
75
+ console.log(` Detail — Routing: "${query}" (type: ${projectType})`);
76
+ for (const section of [
77
+ "S1_classify",
78
+ "S2_select",
79
+ "S3_resolve",
80
+ "S4_compute",
81
+ ]) {
82
+ console.log(`\n ${section}:`);
83
+ for (const [k, v] of Object.entries(result[section])) {
84
+ if (Array.isArray(v)) {
85
+ console.log(` ${k}: [${v.join(", ")}]`);
86
+ } else {
87
+ console.log(` ${k}: ${v}`);
88
+ }
89
+ }
90
+ }
91
+ if (result.S4b_order) {
92
+ console.log(`\n S4b_order (dependency-ordered reads):`);
93
+ for (let i = 0; i < result.S4b_order.ordered_reads.length; i++) {
94
+ console.log(` ${i + 1}. ${result.S4b_order.ordered_reads[i]}`);
95
+ }
96
+ }
97
+ writeLastRunSummary(".", { command: "route", inputRef, summary });
98
+ }
99
+ });
100
+ }
101
+