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,486 @@
1
+ /** wwa doc-review — generate docs/internal/ registry from doc-tokens.yaml and DOCS-MAP.md. */
2
+
3
+ import { readFileSync, existsSync, mkdirSync } from "node:fs";
4
+ import { resolve, join } from "node:path";
5
+ import { safeWriteFile } from "./helpers.js";
6
+ import { resolveTokensPath, resolveDocsMapPath, resolveOutputDir } from "../boundaries.js";
7
+
8
+ export function register(program) {
9
+ program
10
+ .command("doc-review [directory]")
11
+ .description(
12
+ "Generate docs/internal/ document registry (doc-registry.yaml + doc-registry.md)"
13
+ )
14
+ .option("--yaml-only", "Only generate doc-registry.yaml")
15
+ .option("--md-only", "Only generate doc-registry.md")
16
+ .option("--json", "Output registry data as JSON (no file write)")
17
+ .action(async (directory, opts) => {
18
+ directory = directory || ".";
19
+ const d = resolve(directory);
20
+
21
+ const data = await gatherRegistryData(d);
22
+
23
+ if (data.errors.length > 0) {
24
+ console.error("Cannot generate document registry:");
25
+ for (const e of data.errors) console.error(` - ${e}`);
26
+ process.exit(1);
27
+ }
28
+
29
+ if (opts.json) {
30
+ console.log(JSON.stringify(data, null, 2));
31
+ return;
32
+ }
33
+
34
+ const internalDir = await resolveOutputDir(d);
35
+ if (!existsSync(internalDir)) {
36
+ mkdirSync(internalDir, { recursive: true });
37
+ }
38
+
39
+ const results = [];
40
+
41
+ if (!opts.mdOnly) {
42
+ const yamlPath = join(internalDir, "doc-registry.yaml");
43
+ const wrote = await generateRegistryYaml(yamlPath, data);
44
+ if (wrote) results.push(yamlPath);
45
+ }
46
+
47
+ if (!opts.yamlOnly) {
48
+ const mdPath = join(internalDir, "doc-registry.md");
49
+ generateRegistryMd(mdPath, data);
50
+ results.push(mdPath);
51
+ }
52
+
53
+ if (results.length > 0) {
54
+ console.log("\n Document registry generated:\n");
55
+ for (const r of results) {
56
+ console.log(` - ${r}`);
57
+ }
58
+ console.log(
59
+ `\n Nodes: ${data.dependencyGraph.nodes.length} Edges: ${data.dependencyGraph.edges.length} Orphans: ${data.health.orphanNodes}\n`
60
+ );
61
+ }
62
+ });
63
+ }
64
+
65
+ // ---------------------------------------------------------------------------
66
+ // Data gathering
67
+ // ---------------------------------------------------------------------------
68
+
69
+ async function gatherRegistryData(dir) {
70
+ const data = {
71
+ generated: new Date().toISOString().slice(0, 10),
72
+ generator: "wwa doc-review",
73
+ dependencyGraph: { nodes: [], edges: [], orphans: [], readOrder: [] },
74
+ terminology: { modules: [], apis: [], entities: [], conventions: [] },
75
+ health: {
76
+ totalDocs: 0,
77
+ docsOnDisk: 0,
78
+ docsMissing: 0,
79
+ orphanNodes: 0,
80
+ staleDocs: 0,
81
+ dependencyCoverage: "0%",
82
+ },
83
+ docsInventory: [],
84
+ componentMappings: [],
85
+ errors: [],
86
+ warnings: [],
87
+ };
88
+
89
+ // --- Load doc_graph from doc-tokens.yaml ---
90
+ const tokensPath = await resolveTokensPath(dir);
91
+ if (!existsSync(tokensPath)) {
92
+ data.errors.push(
93
+ "No doc-tokens.yaml found — run wwa init or complete brownfield onboarding first"
94
+ );
95
+ return data;
96
+ }
97
+
98
+ let parsed;
99
+ try {
100
+ const yaml = (await import("js-yaml")).default;
101
+ const raw = readFileSync(tokensPath, "utf-8");
102
+ parsed = yaml.load(raw);
103
+ } catch (e) {
104
+ data.errors.push(`Failed to parse doc-tokens.yaml: ${e.message}`);
105
+ return data;
106
+ }
107
+
108
+ // Dependency graph
109
+ const docGraph = parsed?.doc_graph;
110
+ if (docGraph) {
111
+ const nodes = docGraph.nodes || [];
112
+ const edges = docGraph.edges || [];
113
+ data.dependencyGraph.nodes = nodes;
114
+ data.dependencyGraph.edges = edges;
115
+
116
+ // Compute orphans
117
+ const connected = new Set();
118
+ for (const e of edges) {
119
+ connected.add(e.from);
120
+ connected.add(e.to);
121
+ }
122
+ data.dependencyGraph.orphans = nodes
123
+ .filter((n) => !connected.has(n.path))
124
+ .map((n) => n.path);
125
+
126
+ // Topological read order (Kahn's algorithm on reads_from edges)
127
+ data.dependencyGraph.readOrder = computeReadOrder(nodes, edges);
128
+ } else {
129
+ data.warnings.push("No doc_graph section in doc-tokens.yaml — graph will be empty");
130
+ }
131
+
132
+ // Terminology
133
+ const projectNames = parsed?.project_names;
134
+ if (projectNames) {
135
+ data.terminology.modules = projectNames.modules || [];
136
+ data.terminology.apis = projectNames.apis || [];
137
+ data.terminology.entities = projectNames.entities || [];
138
+ data.terminology.conventions = projectNames.conventions || [];
139
+ }
140
+
141
+ // --- Load DOCS-MAP.md ---
142
+ const docsMapPath = await resolveDocsMapPath(dir);
143
+ if (existsSync(docsMapPath)) {
144
+ const content = readFileSync(docsMapPath, "utf-8");
145
+ const mapData = parseDocsMap(content);
146
+ data.docsInventory = mapData.inventory;
147
+ data.componentMappings = mapData.mappings;
148
+ }
149
+
150
+ // --- Health: check nodes against disk ---
151
+ const nodes = data.dependencyGraph.nodes;
152
+ let onDisk = 0;
153
+ let missing = 0;
154
+ const lastScan = docGraph?.last_scan || null;
155
+
156
+ for (const n of nodes) {
157
+ const fullPath = join(dir, n.path);
158
+ if (existsSync(fullPath)) {
159
+ onDisk++;
160
+ } else {
161
+ missing++;
162
+ }
163
+ }
164
+
165
+ const totalEdgeNodes = new Set();
166
+ for (const e of data.dependencyGraph.edges) {
167
+ totalEdgeNodes.add(e.from);
168
+ totalEdgeNodes.add(e.to);
169
+ }
170
+
171
+ data.health = {
172
+ totalDocs: nodes.length,
173
+ docsOnDisk: onDisk,
174
+ docsMissing: missing,
175
+ orphanNodes: data.dependencyGraph.orphans.length,
176
+ staleDocs: 0, // Could enhance with mtime comparison
177
+ dependencyCoverage:
178
+ nodes.length > 0
179
+ ? Math.round((totalEdgeNodes.size / nodes.length) * 100) + "%"
180
+ : "0%",
181
+ lastScan,
182
+ };
183
+
184
+ return data;
185
+ }
186
+
187
+ function computeReadOrder(nodes, edges) {
188
+ const readsFrom = edges.filter((e) => e.type === "reads_from");
189
+ if (readsFrom.length === 0) return nodes.map((n) => n.path).sort();
190
+
191
+ const pathSet = new Set(nodes.map((n) => n.path));
192
+ const inDegree = {};
193
+ const adj = {};
194
+ for (const p of pathSet) {
195
+ inDegree[p] = 0;
196
+ adj[p] = [];
197
+ }
198
+ for (const e of readsFrom) {
199
+ if (pathSet.has(e.from) && pathSet.has(e.to)) {
200
+ adj[e.to].push(e.from); // to must be read before from
201
+ inDegree[e.from] = (inDegree[e.from] || 0) + 1;
202
+ }
203
+ }
204
+
205
+ const queue = [];
206
+ for (const p of pathSet) {
207
+ if (inDegree[p] === 0) queue.push(p);
208
+ }
209
+ queue.sort();
210
+
211
+ const result = [];
212
+ while (queue.length > 0) {
213
+ const node = queue.shift();
214
+ result.push(node);
215
+ for (const neighbor of (adj[node] || []).sort()) {
216
+ inDegree[neighbor]--;
217
+ if (inDegree[neighbor] === 0) queue.push(neighbor);
218
+ }
219
+ }
220
+
221
+ // Add any remaining nodes (cycles) at the end
222
+ for (const p of pathSet) {
223
+ if (!result.includes(p)) result.push(p);
224
+ }
225
+ return result;
226
+ }
227
+
228
+ function parseDocsMap(content) {
229
+ const inventory = [];
230
+ const mappings = [];
231
+
232
+ const parseTable = (sectionName) => {
233
+ const re = new RegExp(
234
+ `## ${sectionName}[ \\t]*\\n\\n?\\|[^\\n]+\\n\\|[-| :]+\\n((?:\\|[^\\n]+\\n)*)`,
235
+ );
236
+ const m = content.match(re);
237
+ if (!m) return [];
238
+ return m[1]
239
+ .trim()
240
+ .split("\n")
241
+ .map((row) =>
242
+ row
243
+ .split("|")
244
+ .map((c) => c.trim())
245
+ .filter((c) => c && !c.startsWith("<!--"))
246
+ )
247
+ .filter((cols) => cols.length >= 2);
248
+ };
249
+
250
+ for (const cols of parseTable("Docs inventory")) {
251
+ inventory.push({
252
+ path: cols[0],
253
+ purpose: cols[1],
254
+ sources: cols[2] || "",
255
+ status: cols[3] || "",
256
+ });
257
+ }
258
+ for (const cols of parseTable("Component-to-docs mapping")) {
259
+ mappings.push({
260
+ component: cols[0],
261
+ documentedIn: cols[1],
262
+ trigger: cols[2] || "",
263
+ });
264
+ }
265
+
266
+ return { inventory, mappings };
267
+ }
268
+
269
+ // ---------------------------------------------------------------------------
270
+ // YAML output
271
+ // ---------------------------------------------------------------------------
272
+
273
+ async function generateRegistryYaml(yamlPath, data) {
274
+ try {
275
+ const yaml = (await import("js-yaml")).default;
276
+
277
+ const structured = {
278
+ generated: data.generated,
279
+ generator: data.generator,
280
+ dependency_graph: {
281
+ nodes: data.dependencyGraph.nodes,
282
+ edges: data.dependencyGraph.edges,
283
+ orphans: data.dependencyGraph.orphans,
284
+ read_order: data.dependencyGraph.readOrder,
285
+ },
286
+ terminology: data.terminology,
287
+ health: data.health,
288
+ docs_inventory: data.docsInventory,
289
+ component_mappings: data.componentMappings,
290
+ };
291
+
292
+ const yamlOutput = yaml.dump(structured, {
293
+ lineWidth: 120,
294
+ noRefs: true,
295
+ quotingType: '"',
296
+ });
297
+ const header = [
298
+ "# Document Registry — dependency graph, terminology, and health metrics",
299
+ `# Generated: ${data.generated} by wwa doc-review`,
300
+ "# Source: doc-tokens.yaml (doc_graph, project_names) + DOCS-MAP.md (resolved via boundaries)",
301
+ "",
302
+ ].join("\n");
303
+
304
+ safeWriteFile(yamlPath, header + yamlOutput, "utf-8");
305
+ return true;
306
+ } catch {
307
+ return false;
308
+ }
309
+ }
310
+
311
+ // ---------------------------------------------------------------------------
312
+ // Markdown output
313
+ // ---------------------------------------------------------------------------
314
+
315
+ function generateRegistryMd(mdPath, data) {
316
+ const lines = [];
317
+
318
+ lines.push(
319
+ "# Document Registry",
320
+ "",
321
+ `<!-- Generated: ${data.generated} by wwa doc-review -->`,
322
+ ""
323
+ );
324
+
325
+ // Dependency tree
326
+ lines.push("## Dependency tree", "");
327
+ if (data.dependencyGraph.readOrder.length > 0) {
328
+ lines.push("Read order (foundational files first):", "");
329
+ let i = 1;
330
+ for (const p of data.dependencyGraph.readOrder) {
331
+ const node = data.dependencyGraph.nodes.find((n) => n.path === p);
332
+ const role = node ? ` — ${node.role}` : "";
333
+ lines.push(`${i}. \`${p}\`${role}`);
334
+ i++;
335
+ }
336
+ } else {
337
+ lines.push("No dependency data available.");
338
+ }
339
+ lines.push("");
340
+
341
+ // Terminology glossary
342
+ lines.push("## Terminology glossary", "");
343
+
344
+ const term = data.terminology;
345
+ const hasTerminology =
346
+ term.modules.length > 0 ||
347
+ term.apis.length > 0 ||
348
+ term.entities.length > 0 ||
349
+ term.conventions.length > 0;
350
+
351
+ if (hasTerminology) {
352
+ if (term.modules.length > 0) {
353
+ lines.push(
354
+ "### Modules",
355
+ "",
356
+ "| Canonical | Aliases | Location |",
357
+ "|-----------|---------|----------|"
358
+ );
359
+ for (const m of term.modules) {
360
+ const aliases = Array.isArray(m.aliases) ? m.aliases.join(", ") : "";
361
+ lines.push(
362
+ `| ${m.canonical || m} | ${aliases} | ${m.location || ""} |`
363
+ );
364
+ }
365
+ lines.push("");
366
+ }
367
+ if (term.apis.length > 0) {
368
+ lines.push(
369
+ "### APIs",
370
+ "",
371
+ "| Endpoint | Methods | Documented in |",
372
+ "|----------|---------|---------------|"
373
+ );
374
+ for (const a of term.apis) {
375
+ const methods = Array.isArray(a.methods) ? a.methods.join(", ") : "";
376
+ lines.push(
377
+ `| ${a.canonical || a} | ${methods} | ${a.documented_in || ""} |`
378
+ );
379
+ }
380
+ lines.push("");
381
+ }
382
+ if (term.entities.length > 0) {
383
+ lines.push(
384
+ "### Entities",
385
+ "",
386
+ "| Entity | Schema location |",
387
+ "|--------|-----------------|"
388
+ );
389
+ for (const e of term.entities) {
390
+ lines.push(
391
+ `| ${e.canonical || e} | ${e.schema_location || ""} |`
392
+ );
393
+ }
394
+ lines.push("");
395
+ }
396
+ if (term.conventions.length > 0) {
397
+ lines.push("### Conventions", "");
398
+ for (const c of term.conventions) {
399
+ if (typeof c === "string") {
400
+ lines.push(`- ${c}`);
401
+ } else {
402
+ lines.push(`- **${c.name}**: ${c.pattern || ""}`);
403
+ }
404
+ }
405
+ lines.push("");
406
+ }
407
+ } else {
408
+ lines.push("No project terminology captured yet.", "");
409
+ }
410
+
411
+ // Health dashboard
412
+ const h = data.health;
413
+ lines.push(
414
+ "## Health dashboard",
415
+ "",
416
+ "| Metric | Value |",
417
+ "|--------|-------|",
418
+ `| Total nodes | ${h.totalDocs} |`,
419
+ `| Nodes on disk | ${h.docsOnDisk} |`,
420
+ `| Nodes missing | ${h.docsMissing} |`,
421
+ `| Orphan nodes | ${h.orphanNodes} |`,
422
+ `| Dependency coverage | ${h.dependencyCoverage} |`,
423
+ `| Last scan | ${h.lastScan || "unknown"} |`,
424
+ ""
425
+ );
426
+
427
+ if (data.dependencyGraph.orphans.length > 0) {
428
+ lines.push("### Orphan nodes (no edges)", "");
429
+ for (const o of data.dependencyGraph.orphans) {
430
+ lines.push(`- \`${o}\``);
431
+ }
432
+ lines.push("");
433
+ }
434
+
435
+ // Cross-reference index
436
+ lines.push("## Cross-reference index", "");
437
+ if (data.dependencyGraph.nodes.length > 0) {
438
+ for (const node of data.dependencyGraph.nodes) {
439
+ const upstream = data.dependencyGraph.edges
440
+ .filter((e) => e.to === node.path)
441
+ .map((e) => `\`${e.from}\` (${e.type})`);
442
+ const downstream = data.dependencyGraph.edges
443
+ .filter((e) => e.from === node.path)
444
+ .map((e) => `\`${e.to}\` (${e.type})`);
445
+
446
+ if (upstream.length > 0 || downstream.length > 0) {
447
+ lines.push(`### \`${node.path}\``, "");
448
+ if (upstream.length > 0) {
449
+ lines.push(`- **Upstream**: ${upstream.join(", ")}`);
450
+ }
451
+ if (downstream.length > 0) {
452
+ lines.push(`- **Downstream**: ${downstream.join(", ")}`);
453
+ }
454
+ lines.push("");
455
+ }
456
+ }
457
+ } else {
458
+ lines.push("No cross-references available.", "");
459
+ }
460
+
461
+ lines.push(
462
+ "---",
463
+ "",
464
+ "*Generated by `wwa doc-review` from doc-tokens.yaml and DOCS-MAP.md (resolved via boundaries).*",
465
+ ""
466
+ );
467
+
468
+ safeWriteFile(mdPath, lines.join("\n"), "utf-8");
469
+ }
470
+
471
+ // ---------------------------------------------------------------------------
472
+ // Public: load registry data for --internal-registry consumers
473
+ // ---------------------------------------------------------------------------
474
+
475
+ export async function loadInternalRegistry(dir) {
476
+ const outputDir = await resolveOutputDir(dir);
477
+ const registryPath = join(outputDir, "doc-registry.yaml");
478
+ if (!existsSync(registryPath)) return null;
479
+ try {
480
+ const yaml = (await import("js-yaml")).default;
481
+ const raw = readFileSync(registryPath, "utf-8");
482
+ return yaml.load(raw);
483
+ } catch {
484
+ return null;
485
+ }
486
+ }