ai-spector 0.8.40 → 0.8.42

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 (177) hide show
  1. package/dist/cli.js +415 -7
  2. package/dist/cli.js.map +1 -1
  3. package/dist/core/graph/InMemoryGraph.d.ts.map +1 -1
  4. package/dist/core/graph/detail-sections.d.ts.map +1 -1
  5. package/dist/core/graph/doc-extract.d.ts.map +1 -1
  6. package/dist/core/graph/expand-path-nodes.d.ts.map +1 -1
  7. package/dist/core/graph/health.d.ts.map +1 -1
  8. package/dist/core/graph/impact.d.ts.map +1 -1
  9. package/dist/core/graph/index.d.ts.map +1 -1
  10. package/dist/core/graph/knowledge.d.ts.map +1 -1
  11. package/dist/core/graph/load.d.ts.map +1 -1
  12. package/dist/core/graph/merge.d.ts.map +1 -1
  13. package/dist/core/graph/patch.d.ts.map +1 -1
  14. package/dist/core/graph/path-target-edges.d.ts.map +1 -1
  15. package/dist/core/graph/project.d.ts.map +1 -1
  16. package/dist/core/graph/query.d.ts.map +1 -1
  17. package/dist/core/graph/resolve.d.ts.map +1 -1
  18. package/dist/core/graph/session.d.ts.map +1 -1
  19. package/dist/core/graph/stats.d.ts.map +1 -1
  20. package/dist/core/graph/translation.d.ts +5 -3
  21. package/dist/core/graph/translation.d.ts.map +1 -1
  22. package/dist/core/graph/translation.js +35 -9
  23. package/dist/core/graph/translation.js.map +1 -1
  24. package/dist/core/index/docs-build.d.ts.map +1 -1
  25. package/dist/core/operations/bootstrap.d.ts.map +1 -1
  26. package/dist/core/operations/check.d.ts +30 -0
  27. package/dist/core/operations/check.d.ts.map +1 -0
  28. package/dist/core/operations/check.js +279 -0
  29. package/dist/core/operations/check.js.map +1 -0
  30. package/dist/core/operations/context.d.ts +84 -0
  31. package/dist/core/operations/context.d.ts.map +1 -0
  32. package/dist/core/operations/context.js +164 -0
  33. package/dist/core/operations/context.js.map +1 -0
  34. package/dist/core/operations/extracted.d.ts +80 -0
  35. package/dist/core/operations/extracted.d.ts.map +1 -0
  36. package/dist/core/operations/extracted.js +135 -0
  37. package/dist/core/operations/extracted.js.map +1 -0
  38. package/dist/core/operations/graph-merge.d.ts.map +1 -1
  39. package/dist/core/operations/graph-query.d.ts +1 -0
  40. package/dist/core/operations/graph-query.d.ts.map +1 -1
  41. package/dist/core/operations/graph-query.js +17 -1
  42. package/dist/core/operations/graph-query.js.map +1 -1
  43. package/dist/core/operations/hooks.d.ts.map +1 -1
  44. package/dist/core/operations/hooks.js +13 -0
  45. package/dist/core/operations/hooks.js.map +1 -1
  46. package/dist/core/operations/index.d.ts.map +1 -1
  47. package/dist/core/operations/index.js +39 -2
  48. package/dist/core/operations/index.js.map +1 -1
  49. package/dist/core/operations/lang.d.ts.map +1 -1
  50. package/dist/core/operations/lang.js +5 -5
  51. package/dist/core/operations/lang.js.map +1 -1
  52. package/dist/core/operations/resolve-task.d.ts +9 -0
  53. package/dist/core/operations/resolve-task.d.ts.map +1 -1
  54. package/dist/core/operations/resolve-task.js +10 -1
  55. package/dist/core/operations/resolve-task.js.map +1 -1
  56. package/dist/core/operations/task-templates.d.ts +17 -0
  57. package/dist/core/operations/task-templates.d.ts.map +1 -0
  58. package/dist/core/operations/task-templates.js +45 -0
  59. package/dist/core/operations/task-templates.js.map +1 -0
  60. package/dist/core/operations/task.d.ts +262 -0
  61. package/dist/core/operations/task.d.ts.map +1 -0
  62. package/dist/core/operations/task.js +639 -0
  63. package/dist/core/operations/task.js.map +1 -0
  64. package/dist/core/operations/template.d.ts.map +1 -1
  65. package/dist/core/operations/template.js +5 -0
  66. package/dist/core/operations/template.js.map +1 -1
  67. package/dist/core/operations/validate.d.ts.map +1 -1
  68. package/dist/core/paths/localized-output.d.ts +15 -0
  69. package/dist/core/paths/localized-output.d.ts.map +1 -0
  70. package/dist/core/paths/localized-output.js +65 -0
  71. package/dist/core/paths/localized-output.js.map +1 -0
  72. package/dist/core/registry/build.d.ts.map +1 -1
  73. package/dist/core/registry/structure-patch.d.ts.map +1 -1
  74. package/dist/core/util/fs.d.ts +2 -0
  75. package/dist/core/util/fs.d.ts.map +1 -1
  76. package/dist/core/util/fs.js +8 -1
  77. package/dist/core/util/fs.js.map +1 -1
  78. package/dist/core/visualize/html.d.ts.map +1 -1
  79. package/dist/core/visualize/stats.d.ts.map +1 -1
  80. package/dist/interfaces/cli/format/check.d.ts +3 -0
  81. package/dist/interfaces/cli/format/check.d.ts.map +1 -0
  82. package/dist/interfaces/cli/format/check.js +29 -0
  83. package/dist/interfaces/cli/format/check.js.map +1 -0
  84. package/dist/interfaces/cli/format/cocoindex.d.ts.map +1 -1
  85. package/dist/interfaces/cli/format/comments.d.ts.map +1 -1
  86. package/dist/interfaces/cli/format/comments.js.map +1 -1
  87. package/dist/interfaces/cli/format/context.d.ts +5 -0
  88. package/dist/interfaces/cli/format/context.d.ts.map +1 -0
  89. package/dist/interfaces/cli/format/context.js +46 -0
  90. package/dist/interfaces/cli/format/context.js.map +1 -0
  91. package/dist/interfaces/cli/format/extracted.d.ts +6 -0
  92. package/dist/interfaces/cli/format/extracted.d.ts.map +1 -0
  93. package/dist/interfaces/cli/format/extracted.js +59 -0
  94. package/dist/interfaces/cli/format/extracted.js.map +1 -0
  95. package/dist/interfaces/cli/format/graph.d.ts.map +1 -1
  96. package/dist/interfaces/cli/format/graph.js.map +1 -1
  97. package/dist/interfaces/cli/format/index-cmd.d.ts.map +1 -1
  98. package/dist/interfaces/cli/format/misc.d.ts.map +1 -1
  99. package/dist/interfaces/cli/format/misc.js.map +1 -1
  100. package/dist/interfaces/cli/format/reviews.d.ts.map +1 -1
  101. package/dist/interfaces/cli/format/task.d.ts +9 -0
  102. package/dist/interfaces/cli/format/task.d.ts.map +1 -0
  103. package/dist/interfaces/cli/format/task.js +66 -0
  104. package/dist/interfaces/cli/format/task.js.map +1 -0
  105. package/dist/interfaces/mcp/schemas.d.ts +732 -16
  106. package/dist/interfaces/mcp/schemas.d.ts.map +1 -1
  107. package/dist/interfaces/mcp/schemas.js +225 -3
  108. package/dist/interfaces/mcp/schemas.js.map +1 -1
  109. package/dist/interfaces/mcp/server.js +136 -1
  110. package/dist/interfaces/mcp/server.js.map +1 -1
  111. package/dist/interfaces/mcp/tools/analyze.js.map +1 -1
  112. package/dist/interfaces/mcp/tools/check.d.ts +4 -0
  113. package/dist/interfaces/mcp/tools/check.d.ts.map +1 -0
  114. package/dist/interfaces/mcp/tools/check.js +5 -0
  115. package/dist/interfaces/mcp/tools/check.js.map +1 -0
  116. package/dist/interfaces/mcp/tools/cocoindex.d.ts.map +1 -1
  117. package/dist/interfaces/mcp/tools/cocoindex.js.map +1 -1
  118. package/dist/interfaces/mcp/tools/comments.d.ts.map +1 -1
  119. package/dist/interfaces/mcp/tools/comments.js.map +1 -1
  120. package/dist/interfaces/mcp/tools/context.d.ts +6 -0
  121. package/dist/interfaces/mcp/tools/context.d.ts.map +1 -0
  122. package/dist/interfaces/mcp/tools/context.js +26 -0
  123. package/dist/interfaces/mcp/tools/context.js.map +1 -0
  124. package/dist/interfaces/mcp/tools/extracted.d.ts +7 -0
  125. package/dist/interfaces/mcp/tools/extracted.d.ts.map +1 -0
  126. package/dist/interfaces/mcp/tools/extracted.js +35 -0
  127. package/dist/interfaces/mcp/tools/extracted.js.map +1 -0
  128. package/dist/interfaces/mcp/tools/graph.d.ts.map +1 -1
  129. package/dist/interfaces/mcp/tools/graph.js +13 -1
  130. package/dist/interfaces/mcp/tools/graph.js.map +1 -1
  131. package/dist/interfaces/mcp/tools/index.d.ts.map +1 -1
  132. package/dist/interfaces/mcp/tools/index.js.map +1 -1
  133. package/dist/interfaces/mcp/tools/lang.js.map +1 -1
  134. package/dist/interfaces/mcp/tools/resolve-task.d.ts.map +1 -1
  135. package/dist/interfaces/mcp/tools/resolve-task.js +43 -12
  136. package/dist/interfaces/mcp/tools/resolve-task.js.map +1 -1
  137. package/dist/interfaces/mcp/tools/reviews.d.ts.map +1 -1
  138. package/dist/interfaces/mcp/tools/reviews.js.map +1 -1
  139. package/dist/interfaces/mcp/tools/task.d.ts +13 -0
  140. package/dist/interfaces/mcp/tools/task.d.ts.map +1 -0
  141. package/dist/interfaces/mcp/tools/task.js +68 -0
  142. package/dist/interfaces/mcp/tools/task.js.map +1 -0
  143. package/dist/interfaces/mcp/tools/template.d.ts.map +1 -1
  144. package/dist/interfaces/mcp/tools/template.js.map +1 -1
  145. package/dist/interfaces/sdk/index.d.ts +8 -0
  146. package/dist/interfaces/sdk/index.d.ts.map +1 -1
  147. package/dist/interfaces/sdk/index.js +8 -0
  148. package/dist/interfaces/sdk/index.js.map +1 -1
  149. package/dist/interfaces/types/index.d.ts.map +1 -1
  150. package/package.json +3 -2
  151. package/scaffold/.ai-spector/.docflow/config/workspace.rules.json +15 -0
  152. package/scaffold/.ai-spector/.docflow/tasks/index.json +5 -0
  153. package/scaffold/claude/.claude/skills/ai-spector-generate-basic-design/skill.md +19 -5
  154. package/scaffold/claude/.claude/skills/ai-spector-generate-srs/skill.md +27 -5
  155. package/scaffold/claude/.claude/skills/ai-spector-resolve-task/skill.md +4 -4
  156. package/scaffold/claude/.claude/skills/ai-spector-task/skill.md +28 -0
  157. package/scaffold/claude/CLAUDE.md +29 -3
  158. package/scaffold/cursor/WORKFLOW.md +18 -5
  159. package/scaffold/cursor/commands/_workflow.md +2 -1
  160. package/scaffold/cursor/skills/README.md +6 -1
  161. package/scaffold/cursor/skills/_skill-router.md +14 -2
  162. package/scaffold/cursor/skills/ai-spector/references/clarify.md +50 -0
  163. package/scaffold/cursor/skills/ai-spector/references/context-store.md +45 -0
  164. package/scaffold/cursor/skills/ai-spector/references/extract-specs.md +39 -0
  165. package/scaffold/cursor/skills/ai-spector/references/generate-graph.md +1 -1
  166. package/scaffold/cursor/skills/ai-spector/references/generate-workflow.md +54 -9
  167. package/scaffold/cursor/skills/ai-spector/references/plan-and-briefing.md +49 -0
  168. package/scaffold/cursor/skills/ai-spector/references/workspace-check.md +50 -0
  169. package/scaffold/cursor/skills/ai-spector-check/SKILL.md +30 -0
  170. package/scaffold/cursor/skills/ai-spector-generate-basic-design/SKILL.md +7 -2
  171. package/scaffold/cursor/skills/ai-spector-generate-srs/SKILL.md +7 -2
  172. package/scaffold/cursor/skills/ai-spector-resolve-task/SKILL.md +4 -4
  173. package/scaffold/cursor/skills/ai-spector-resolve-task/references/runbook.md +23 -9
  174. package/scaffold/cursor/skills/ai-spector-task/SKILL.md +36 -0
  175. package/scaffold/cursor/skills/ai-spector-task/references/runbook.md +76 -0
  176. package/schemas/context-store.schema.json +32 -0
  177. package/schemas/task-state.schema.json +67 -0
package/dist/cli.js CHANGED
@@ -6,6 +6,8 @@ import { writeJson, readJson } from "./core/util/fs.js";
6
6
  import { resolveProjectPaths } from "./core/util/paths.js";
7
7
  import { buildSectionRegistry } from "./core/registry/build.js";
8
8
  import { bootstrapFromRegistry } from "./core/operations/bootstrap.js";
9
+ import { applyPrimaryLanguageOutputs } from "./core/graph/translation.js";
10
+ import { loadDocflowConfig } from "./core/config/load.js";
9
11
  import { validateGraph, formatIssues } from "./core/operations/validate.js";
10
12
  import { runInit } from "./core/operations/init.js";
11
13
  import { runLangAdd, runLangSetClient } from "./core/operations/lang.js";
@@ -13,6 +15,14 @@ import { runLangQueueFailed, runLangQueueFail, runLangQueuePending, runLangQueue
13
15
  import { formatPendingTable, formatResolvedTable, formatFailedTable } from "./core/lang/queue.js";
14
16
  import { runHooksInstall, runHooksPreCommit, formatPreCommitReport } from "./core/operations/hooks.js";
15
17
  import { runSetup, runSetupCheck } from "./core/operations/setup.js";
18
+ import { runCheck } from "./core/operations/check.js";
19
+ import { formatCheck } from "./interfaces/cli/format/check.js";
20
+ import { runContextList, runContextRecord, runContextResolve, } from "./core/operations/context.js";
21
+ import { formatContextList, formatContextRecord, formatContextResolve, } from "./interfaces/cli/format/context.js";
22
+ import { runSpecList, runSpecApprove, runSpecReject, } from "./core/operations/extracted.js";
23
+ import { formatSpecList, formatSpecApprove, formatSpecReject, } from "./interfaces/cli/format/extracted.js";
24
+ import { runTaskAbandon, runTaskApprovePlan, runTaskComplete, runTaskCreate, runTaskGet, runTaskList, runTaskPause, runTaskResume, runTaskUpdate, recordGenerateWaveProgress, } from "./core/operations/task.js";
25
+ import { formatTaskCreate, formatTaskGet, formatTaskList, formatTaskSimple, formatTaskUpdate, } from "./interfaces/cli/format/task.js";
16
26
  import { runSyncCursor } from "./core/operations/sync-cursor.js";
17
27
  import { runGraphQuery } from "./core/operations/graph-query.js";
18
28
  import { runGraphImpact } from "./core/operations/graph-impact.js";
@@ -113,6 +123,361 @@ program
113
123
  console.log(formatSetupAudit(audit, true));
114
124
  }
115
125
  });
126
+ program
127
+ .command("check")
128
+ .description("Validate workspace structure & config (warns on drift; non-zero exit on errors)")
129
+ .option("-C, --cwd <path>", "Project root", process.cwd())
130
+ .option("--fix", "Auto-create missing directories where possible")
131
+ .option("-p, --path <paths...>", "Validate specific doc output path(s) after writing (repeatable)")
132
+ .option("--json", "JSON output")
133
+ .action(async (opts) => {
134
+ const root = resolve(opts.cwd ?? process.cwd());
135
+ const paths = opts.path;
136
+ const result = await runCheck({ root, fix: opts.fix, paths });
137
+ if (opts.json)
138
+ console.log(JSON.stringify(result, null, 2));
139
+ else
140
+ console.log(formatCheck(result));
141
+ if (!result.ok)
142
+ process.exitCode = 1;
143
+ });
144
+ const context = program
145
+ .command("context")
146
+ .description("Clarification context store (questions answered before generation)");
147
+ context
148
+ .command("list")
149
+ .description("List context entries, optionally filtered by doc type / status")
150
+ .option("-C, --cwd <path>", "Project root", process.cwd())
151
+ .option("-t, --doc-type <type>", "Doc type store (e.g. srs)")
152
+ .option("-s, --status <status>", "Filter: open | answered | stale")
153
+ .option("--json", "JSON output")
154
+ .action(async (opts) => {
155
+ const result = await runContextList({
156
+ root: resolve(opts.cwd ?? process.cwd()),
157
+ docType: opts.docType,
158
+ status: opts.status,
159
+ });
160
+ if (opts.json)
161
+ console.log(JSON.stringify(result, null, 2));
162
+ else
163
+ console.log(formatContextList(result));
164
+ });
165
+ context
166
+ .command("record <docType> <question>")
167
+ .description("Record a clarifying question (pass --answer to record it answered)")
168
+ .option("-C, --cwd <path>", "Project root", process.cwd())
169
+ .option("-a, --answer <answer>", "Answer (records entry as answered)")
170
+ .option("--scope <scope>", "DAG node / section this informs (e.g. srs.use-cases)")
171
+ .option("--source <source>", "user | inferred | data-source", "user")
172
+ .option("--refs <paths>", "Comma-separated source files that make this stale on change")
173
+ .option("--by <name>", "Who answered")
174
+ .option("--json", "JSON output")
175
+ .action(async (docType, question, opts) => {
176
+ const result = await runContextRecord({
177
+ root: resolve(opts.cwd ?? process.cwd()),
178
+ docType,
179
+ question,
180
+ answer: opts.answer,
181
+ scope: opts.scope,
182
+ source: opts.source,
183
+ sourceRefs: opts.refs
184
+ ? opts.refs.split(",").map((s) => s.trim()).filter(Boolean)
185
+ : undefined,
186
+ answeredBy: opts.by,
187
+ });
188
+ if (opts.json)
189
+ console.log(JSON.stringify(result, null, 2));
190
+ else
191
+ console.log(formatContextRecord(result));
192
+ });
193
+ context
194
+ .command("resolve <docType> <id> <answer>")
195
+ .description("Answer an open/stale entry by id (e.g. Q-001)")
196
+ .option("-C, --cwd <path>", "Project root", process.cwd())
197
+ .option("--by <name>", "Who answered")
198
+ .option("--json", "JSON output")
199
+ .action(async (docType, id, answer, opts) => {
200
+ const result = await runContextResolve({
201
+ root: resolve(opts.cwd ?? process.cwd()),
202
+ docType,
203
+ id,
204
+ answer,
205
+ answeredBy: opts.by,
206
+ });
207
+ if (opts.json)
208
+ console.log(JSON.stringify(result, null, 2));
209
+ else
210
+ console.log(formatContextResolve(result));
211
+ });
212
+ const spec = program
213
+ .command("spec")
214
+ .description("Extracted-spec review queue (specs pulled from generated docs, approved before graph merge)");
215
+ spec
216
+ .command("list")
217
+ .description("List extracted specs, optionally filtered by doc type / status")
218
+ .option("-C, --cwd <path>", "Project root", process.cwd())
219
+ .option("-t, --doc-type <type>", "Doc type queue (e.g. srs)")
220
+ .option("-s, --status <status>", "Filter: pending | approved | rejected")
221
+ .option("--json", "JSON output")
222
+ .action(async (opts) => {
223
+ const result = await runSpecList({
224
+ root: resolve(opts.cwd ?? process.cwd()),
225
+ docType: opts.docType,
226
+ status: opts.status,
227
+ });
228
+ if (opts.json)
229
+ console.log(JSON.stringify(result, null, 2));
230
+ else
231
+ console.log(formatSpecList(result));
232
+ });
233
+ spec
234
+ .command("approve <docType> <id>")
235
+ .description("Approve a pending spec; merges its graph patch (if any) into the graph")
236
+ .option("-C, --cwd <path>", "Project root", process.cwd())
237
+ .option("--by <name>", "Reviewer name")
238
+ .option("--note <note>", "Review note")
239
+ .option("--skip-merge", "Approve without merging the graph patch")
240
+ .option("--json", "JSON output")
241
+ .action(async (docType, id, opts) => {
242
+ const result = await runSpecApprove({
243
+ root: resolve(opts.cwd ?? process.cwd()),
244
+ docType,
245
+ id,
246
+ by: opts.by,
247
+ note: opts.note,
248
+ skipMerge: opts.skipMerge,
249
+ });
250
+ if (opts.json)
251
+ console.log(JSON.stringify(result, null, 2));
252
+ else
253
+ console.log(formatSpecApprove(result));
254
+ });
255
+ spec
256
+ .command("reject <docType> <id>")
257
+ .description("Reject a pending spec (kept for audit, never merged)")
258
+ .option("-C, --cwd <path>", "Project root", process.cwd())
259
+ .option("--by <name>", "Reviewer name")
260
+ .option("--note <note>", "Why the spec was rejected")
261
+ .option("--json", "JSON output")
262
+ .action(async (docType, id, opts) => {
263
+ const result = await runSpecReject({
264
+ root: resolve(opts.cwd ?? process.cwd()),
265
+ docType,
266
+ id,
267
+ by: opts.by,
268
+ note: opts.note,
269
+ });
270
+ if (opts.json)
271
+ console.log(JSON.stringify(result, null, 2));
272
+ else
273
+ console.log(formatSpecReject(result));
274
+ });
275
+ const task = program
276
+ .command("task")
277
+ .description("Workflow task state (persist plan/progress across sessions)");
278
+ task
279
+ .command("create")
280
+ .description("Create a new workflow task")
281
+ .requiredOption("-k, --kind <kind>", "generate | resolve")
282
+ .requiredOption("-w, --workflow <workflow>", "generate-srs | generate-basic-design | resolve")
283
+ .requiredOption("-t, --trigger <text>", "User intent that started this task")
284
+ .option("-C, --cwd <path>", "Project root", process.cwd())
285
+ .option("--doc-type <type>", "Doc type for generate workflows (e.g. srs)")
286
+ .option("--force", "Replace existing active task in the same slot")
287
+ .option("--json", "JSON output")
288
+ .action(async (opts) => {
289
+ const result = await runTaskCreate({
290
+ root: resolve(opts.cwd ?? process.cwd()),
291
+ kind: opts.kind,
292
+ workflow: opts.workflow,
293
+ trigger: opts.trigger,
294
+ docType: opts.docType,
295
+ force: opts.force,
296
+ });
297
+ if (opts.json)
298
+ console.log(JSON.stringify(result, null, 2));
299
+ else
300
+ console.log(formatTaskCreate(result));
301
+ });
302
+ task
303
+ .command("list")
304
+ .description("List tasks")
305
+ .option("-C, --cwd <path>", "Project root", process.cwd())
306
+ .option("-s, --status <status>", "Filter by status (comma-separated)")
307
+ .option("-k, --kind <kind>", "generate | resolve")
308
+ .option("-w, --workflow <workflow>", "Workflow id filter")
309
+ .option("--recent", "Only tasks in index.recent")
310
+ .option("--json", "JSON output")
311
+ .action(async (opts) => {
312
+ const status = opts.status
313
+ ? opts.status.split(",").map((s) => s.trim())
314
+ : undefined;
315
+ const result = await runTaskList({
316
+ root: resolve(opts.cwd ?? process.cwd()),
317
+ status: status?.length === 1 ? status[0] : status,
318
+ kind: opts.kind,
319
+ workflow: opts.workflow,
320
+ recentOnly: opts.recent,
321
+ });
322
+ if (opts.json)
323
+ console.log(JSON.stringify(result, null, 2));
324
+ else
325
+ console.log(formatTaskList(result));
326
+ });
327
+ task
328
+ .command("get <taskId>")
329
+ .description("Get full task state")
330
+ .option("-C, --cwd <path>", "Project root", process.cwd())
331
+ .option("--json", "JSON output")
332
+ .action(async (taskId, opts) => {
333
+ const result = await runTaskGet({
334
+ root: resolve(opts.cwd ?? process.cwd()),
335
+ taskId,
336
+ });
337
+ if (opts.json)
338
+ console.log(JSON.stringify(result, null, 2));
339
+ else
340
+ console.log(formatTaskGet(result));
341
+ });
342
+ task
343
+ .command("update <taskId>")
344
+ .description("Patch task state (pass --patch as JSON)")
345
+ .option("-C, --cwd <path>", "Project root", process.cwd())
346
+ .option("--patch <json>", "JSON patch object (TaskUpdatePatch)")
347
+ .option("--json", "JSON output")
348
+ .action(async (taskId, opts) => {
349
+ if (!opts.patch) {
350
+ throw new Error("--patch is required (JSON object)");
351
+ }
352
+ const patch = JSON.parse(opts.patch);
353
+ const result = await runTaskUpdate({
354
+ root: resolve(opts.cwd ?? process.cwd()),
355
+ taskId,
356
+ patch,
357
+ });
358
+ if (opts.json)
359
+ console.log(JSON.stringify(result, null, 2));
360
+ else
361
+ console.log(formatTaskUpdate(result));
362
+ });
363
+ task
364
+ .command("approve <taskId>")
365
+ .description("Approve the task plan and advance to the next step")
366
+ .option("-C, --cwd <path>", "Project root", process.cwd())
367
+ .option("--plan <json>", "Plan JSON (StoredPlan) if not already set on task")
368
+ .option("--json", "JSON output")
369
+ .action(async (taskId, opts) => {
370
+ const result = await runTaskApprovePlan({
371
+ root: resolve(opts.cwd ?? process.cwd()),
372
+ taskId,
373
+ plan: opts.plan ? JSON.parse(opts.plan) : undefined,
374
+ });
375
+ if (opts.json)
376
+ console.log(JSON.stringify(result, null, 2));
377
+ else
378
+ console.log(formatTaskSimple("Approved plan for", result));
379
+ });
380
+ task
381
+ .command("pause <taskId>")
382
+ .description("Pause a task")
383
+ .option("-C, --cwd <path>", "Project root", process.cwd())
384
+ .option("--json", "JSON output")
385
+ .action(async (taskId, opts) => {
386
+ const result = await runTaskPause({
387
+ root: resolve(opts.cwd ?? process.cwd()),
388
+ taskId,
389
+ });
390
+ if (opts.json)
391
+ console.log(JSON.stringify(result, null, 2));
392
+ else
393
+ console.log(formatTaskSimple("Paused", result));
394
+ });
395
+ task
396
+ .command("resume <taskId>")
397
+ .description("Resume a paused task (validates workspace and drift)")
398
+ .option("-C, --cwd <path>", "Project root", process.cwd())
399
+ .option("--json", "JSON output")
400
+ .action(async (taskId, opts) => {
401
+ const result = await runTaskResume({
402
+ root: resolve(opts.cwd ?? process.cwd()),
403
+ taskId,
404
+ });
405
+ if (opts.json)
406
+ console.log(JSON.stringify(result, null, 2));
407
+ else {
408
+ const lines = [
409
+ `Resume ${result.task.id} — canContinue: ${result.canContinue}`,
410
+ ` workspace: ${result.workspaceOk ? "ok" : "errors"}`,
411
+ ` drift: ${result.drift.length} file(s)`,
412
+ ` stale: ${result.staleContextIds.join(", ") || "none"}`,
413
+ ` next: ${result.suggestedNext}`,
414
+ ];
415
+ if (result.drift.length > 0) {
416
+ for (const d of result.drift) {
417
+ lines.push(` ${d.kind}: ${d.path}`);
418
+ }
419
+ }
420
+ console.log(lines.join("\n"));
421
+ }
422
+ });
423
+ task
424
+ .command("complete <taskId>")
425
+ .description("Mark a task complete")
426
+ .option("-C, --cwd <path>", "Project root", process.cwd())
427
+ .option("--summary <text>", "Completion summary")
428
+ .option("--json", "JSON output")
429
+ .action(async (taskId, opts) => {
430
+ const result = await runTaskComplete({
431
+ root: resolve(opts.cwd ?? process.cwd()),
432
+ taskId,
433
+ summary: opts.summary,
434
+ });
435
+ if (opts.json)
436
+ console.log(JSON.stringify(result, null, 2));
437
+ else
438
+ console.log(formatTaskSimple("Completed", result));
439
+ });
440
+ task
441
+ .command("record-wave <taskId> <waveId>")
442
+ .description("Record generate wave progress (e.g. wave-1 done with artifacts)")
443
+ .option("-C, --cwd <path>", "Project root", process.cwd())
444
+ .option("-s, --status <status>", "done | in-progress | blocked", "done")
445
+ .option("--artifacts <paths>", "Comma-separated doc paths written this wave")
446
+ .option("--blocker <text>", "Blocker message when status is blocked")
447
+ .option("--json", "JSON output")
448
+ .action(async (taskId, waveId, opts) => {
449
+ const result = await recordGenerateWaveProgress({
450
+ root: resolve(opts.cwd ?? process.cwd()),
451
+ taskId,
452
+ waveId,
453
+ status: opts.status,
454
+ artifacts: opts.artifacts
455
+ ? opts.artifacts.split(",").map((p) => p.trim()).filter(Boolean)
456
+ : undefined,
457
+ blocker: opts.blocker ?? null,
458
+ });
459
+ if (opts.json)
460
+ console.log(JSON.stringify(result, null, 2));
461
+ else
462
+ console.log(formatTaskUpdate(result));
463
+ });
464
+ task
465
+ .command("abandon <taskId>")
466
+ .description("Abandon a task")
467
+ .option("-C, --cwd <path>", "Project root", process.cwd())
468
+ .option("--reason <text>", "Why the task was abandoned")
469
+ .option("--json", "JSON output")
470
+ .action(async (taskId, opts) => {
471
+ const result = await runTaskAbandon({
472
+ root: resolve(opts.cwd ?? process.cwd()),
473
+ taskId,
474
+ reason: opts.reason,
475
+ });
476
+ if (opts.json)
477
+ console.log(JSON.stringify(result, null, 2));
478
+ else
479
+ console.log(formatTaskSimple("Abandoned", result));
480
+ });
116
481
  const lang = program.command("lang").description("Manage project languages");
117
482
  lang
118
483
  .command("add <code>")
@@ -287,6 +652,16 @@ graph
287
652
  const graphPath = opts.output ?? paths.graph;
288
653
  const registry = await readJson(registryPath);
289
654
  const graph = bootstrapFromRegistry(registry);
655
+ try {
656
+ const { config } = await loadDocflowConfig(paths.root);
657
+ const primary = config.languages[0];
658
+ if (primary) {
659
+ applyPrimaryLanguageOutputs(graph, primary.code, config.languages.map((l) => l.code));
660
+ }
661
+ }
662
+ catch {
663
+ // uninitialized project — keep manifest paths as-is
664
+ }
290
665
  await writeJson(graphPath, graph.toTraceabilityGraph());
291
666
  console.log(`Wrote ${graphPath} (${graph.nodesById.size} nodes, ${graph.toTraceabilityGraph().edges.length} edges)`);
292
667
  });
@@ -301,6 +676,7 @@ graph
301
676
  const paths = await getPaths(cmd);
302
677
  const result = await runGraphQuery({
303
678
  graphPath: paths.graph,
679
+ projectRoot: paths.root,
304
680
  seedId: id,
305
681
  direction: opts.direction,
306
682
  depth: Number(opts.depth),
@@ -876,9 +1252,10 @@ cocoindex
876
1252
  });
877
1253
  // ── resolve-task ──────────────────────────────────────────────────────────────
878
1254
  program
879
- .command("resolve-task <planJson>")
880
- .description("Execute a resolve-task plan from a JSON file. The file must contain { intent, goalSpec, plan }.")
1255
+ .command("resolve-task [planJson]")
1256
+ .description("Execute a resolve-task plan from a JSON file or --task-id (approved task in .ai-spector/.docflow/tasks/).")
881
1257
  .option("--root <path>", "Project root (default: cwd)")
1258
+ .option("--task-id <id>", "Load approved plan from task file")
882
1259
  .option("--dry-run", "Validate plan without writing any changes")
883
1260
  .option("--json", "JSON output")
884
1261
  .action(async (planJson, opts) => {
@@ -889,8 +1266,28 @@ program
889
1266
  const { runGraphReport } = await import("./core/operations/graph-report.js");
890
1267
  const { runGraphImpact } = await import("./core/operations/graph-impact.js");
891
1268
  const { resolveProjectPaths } = await import("./core/util/paths.js");
892
- const raw = await readJson(resolve(planJson));
1269
+ const { loadResolveExecutionContext, recordResolveStepProgress, } = await import("./core/operations/task.js");
893
1270
  const paths = await resolveProjectPaths(opts.root);
1271
+ let intent;
1272
+ let goalSpec;
1273
+ let plan;
1274
+ const taskId = opts.taskId;
1275
+ if (taskId) {
1276
+ const ctx = await loadResolveExecutionContext({ root: paths.root, taskId });
1277
+ intent = ctx.intent;
1278
+ goalSpec = ctx.goalSpec;
1279
+ plan = ctx.plan;
1280
+ }
1281
+ else {
1282
+ if (!planJson) {
1283
+ throw new Error("Provide planJson path or --task-id");
1284
+ }
1285
+ const raw = await readJson(resolve(planJson));
1286
+ goalSpec = createGoalSpec(raw.goalSpec.trigger, raw.goalSpec.domain, raw.goalSpec.scope, raw.goalSpec.criteria, raw.goalSpec.notes);
1287
+ plan = createPlan(goalSpec, raw.plan.steps, goalSpec.scope.map((nodeId) => ({ nodeId, directCallers: 0, riskLevel: "low" })));
1288
+ plan.approvedAt = new Date().toISOString();
1289
+ intent = raw.intent;
1290
+ }
894
1291
  const executors = {
895
1292
  index: async (_args, root) => {
896
1293
  await runIndex({ root, graphOnly: false, docsOnly: false });
@@ -916,11 +1313,21 @@ program
916
1313
  return { artifacts: [] };
917
1314
  },
918
1315
  };
919
- const goalSpec = createGoalSpec(raw.goalSpec.trigger, raw.goalSpec.domain, raw.goalSpec.scope, raw.goalSpec.criteria, raw.goalSpec.notes);
920
- const plan = createPlan(goalSpec, raw.plan.steps, goalSpec.scope.map((nodeId) => ({ nodeId, directCallers: 0, riskLevel: "low" })));
921
- plan.approvedAt = new Date().toISOString();
1316
+ const onStepComplete = taskId
1317
+ ? async (event) => {
1318
+ await recordResolveStepProgress({
1319
+ root: paths.root,
1320
+ taskId,
1321
+ plan: event.plan,
1322
+ stepId: event.stepId,
1323
+ stepStatus: event.status,
1324
+ artifacts: event.artifacts,
1325
+ blocker: event.issue ?? null,
1326
+ });
1327
+ }
1328
+ : undefined;
922
1329
  const result = await runResolveTask({
923
- intent: raw.intent,
1330
+ intent,
924
1331
  goalSpec,
925
1332
  plan,
926
1333
  projectRoot: paths.root,
@@ -928,6 +1335,7 @@ program
928
1335
  rulesPath: paths.rulesImpact,
929
1336
  executors,
930
1337
  dryRun: opts.dryRun,
1338
+ onStepComplete,
931
1339
  });
932
1340
  if (opts.json)
933
1341
  console.log(JSON.stringify(result, null, 2));