scene-capability-engine 3.0.0

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 (336) hide show
  1. package/CHANGELOG.md +2513 -0
  2. package/LICENSE +21 -0
  3. package/README.md +765 -0
  4. package/README.zh.md +630 -0
  5. package/bin/kiro-spec-engine.js +796 -0
  6. package/bin/kse.js +3 -0
  7. package/bin/sce.js +3 -0
  8. package/bin/sco.js +3 -0
  9. package/docs/331-poc-adaptation-roadmap.md +156 -0
  10. package/docs/331-poc-dual-track-integration-guide.md +120 -0
  11. package/docs/331-poc-weekly-delivery-checklist.md +52 -0
  12. package/docs/OFFLINE_INSTALL.md +96 -0
  13. package/docs/README.md +279 -0
  14. package/docs/adopt-migration-guide.md +599 -0
  15. package/docs/adoption-guide.md +616 -0
  16. package/docs/agent-hooks-analysis.md +815 -0
  17. package/docs/architecture.md +733 -0
  18. package/docs/articles/ai-driven-development-philosophy-and-practice-review.md +208 -0
  19. package/docs/articles/ai-driven-development-philosophy-and-practice.en.md +459 -0
  20. package/docs/articles/ai-driven-development-philosophy-and-practice.md +492 -0
  21. package/docs/autonomous-control-guide.md +851 -0
  22. package/docs/command-reference.md +1368 -0
  23. package/docs/community.md +115 -0
  24. package/docs/cross-tool-guide.md +555 -0
  25. package/docs/developer-guide.md +619 -0
  26. package/docs/document-governance.md +865 -0
  27. package/docs/environment-management-guide.md +526 -0
  28. package/docs/examples/add-export-command/design.md +194 -0
  29. package/docs/examples/add-export-command/requirements.md +110 -0
  30. package/docs/examples/add-export-command/tasks.md +88 -0
  31. package/docs/examples/add-rest-api/design.md +855 -0
  32. package/docs/examples/add-rest-api/requirements.md +323 -0
  33. package/docs/examples/add-rest-api/tasks.md +355 -0
  34. package/docs/examples/add-user-dashboard/design.md +192 -0
  35. package/docs/examples/add-user-dashboard/requirements.md +143 -0
  36. package/docs/examples/add-user-dashboard/tasks.md +91 -0
  37. package/docs/faq.md +697 -0
  38. package/docs/handoffs/evidence/ontology/moqui-template-baseline-2026-02-17-232922.json +156 -0
  39. package/docs/handoffs/evidence/ontology/moqui-template-baseline-2026-02-17-232922.md +24 -0
  40. package/docs/images/wechat-qr.png +0 -0
  41. package/docs/integration-modes.md +529 -0
  42. package/docs/integration-philosophy.md +313 -0
  43. package/docs/knowledge-management-guide.md +263 -0
  44. package/docs/manual-workflows-guide.md +418 -0
  45. package/docs/moqui-capability-matrix.md +73 -0
  46. package/docs/moqui-template-core-library-playbook.md +109 -0
  47. package/docs/multi-agent-coordination-guide.md +553 -0
  48. package/docs/multi-repo-management-guide.md +1344 -0
  49. package/docs/quick-start-with-ai-tools.md +375 -0
  50. package/docs/quick-start.md +146 -0
  51. package/docs/release-checklist.md +121 -0
  52. package/docs/releases/README.md +13 -0
  53. package/docs/releases/v1.46.2-validation.md +45 -0
  54. package/docs/releases/v1.46.2.md +50 -0
  55. package/docs/scene-runtime-guide.md +347 -0
  56. package/docs/spec-collaboration-guide.md +369 -0
  57. package/docs/spec-locking-guide.md +225 -0
  58. package/docs/spec-numbering-guide.md +348 -0
  59. package/docs/spec-workflow.md +519 -0
  60. package/docs/steering-strategy-guide.md +196 -0
  61. package/docs/team-collaboration-guide.md +465 -0
  62. package/docs/testing-strategy.md +272 -0
  63. package/docs/tools/claude-guide.md +654 -0
  64. package/docs/tools/cursor-guide.md +706 -0
  65. package/docs/tools/generic-guide.md +446 -0
  66. package/docs/tools/kiro-guide.md +308 -0
  67. package/docs/tools/vscode-guide.md +445 -0
  68. package/docs/tools/windsurf-guide.md +391 -0
  69. package/docs/troubleshooting.md +1135 -0
  70. package/docs/upgrade-guide.md +639 -0
  71. package/docs/value-observability-guide.md +127 -0
  72. package/docs/zh/README.md +341 -0
  73. package/docs/zh/quick-start.md +764 -0
  74. package/docs/zh/release-checklist.md +121 -0
  75. package/docs/zh/releases/README.md +13 -0
  76. package/docs/zh/releases/v1.46.2-validation.md +45 -0
  77. package/docs/zh/releases/v1.46.2.md +50 -0
  78. package/docs/zh/spec-numbering-guide.md +348 -0
  79. package/docs/zh/tools/claude-guide.md +349 -0
  80. package/docs/zh/tools/cursor-guide.md +281 -0
  81. package/docs/zh/tools/generic-guide.md +499 -0
  82. package/docs/zh/tools/kiro-guide.md +342 -0
  83. package/docs/zh/tools/vscode-guide.md +449 -0
  84. package/docs/zh/tools/windsurf-guide.md +378 -0
  85. package/docs/zh/value-observability-guide.md +127 -0
  86. package/docs//344/272/244/344/273/230/346/270/205/345/215/225.md +75 -0
  87. package/lib/adoption/adoption-logger.js +487 -0
  88. package/lib/adoption/adoption-strategy.js +538 -0
  89. package/lib/adoption/backup-manager.js +420 -0
  90. package/lib/adoption/conflict-resolver.js +410 -0
  91. package/lib/adoption/detection-engine.js +275 -0
  92. package/lib/adoption/diff-viewer.js +226 -0
  93. package/lib/adoption/error-formatter.js +509 -0
  94. package/lib/adoption/file-classifier.js +385 -0
  95. package/lib/adoption/progress-reporter.js +534 -0
  96. package/lib/adoption/smart-orchestrator.js +470 -0
  97. package/lib/adoption/strategy-selector.js +218 -0
  98. package/lib/adoption/summary-generator.js +493 -0
  99. package/lib/adoption/template-sync.js +605 -0
  100. package/lib/auto/autonomous-engine.js +485 -0
  101. package/lib/auto/checkpoint-manager.js +300 -0
  102. package/lib/auto/close-loop-runner.js +2476 -0
  103. package/lib/auto/config-schema.js +176 -0
  104. package/lib/auto/decision-engine.js +344 -0
  105. package/lib/auto/error-recovery-manager.js +580 -0
  106. package/lib/auto/goal-decomposer.js +278 -0
  107. package/lib/auto/progress-tracker.js +502 -0
  108. package/lib/auto/safety-manager.js +186 -0
  109. package/lib/auto/semantic-decomposer.js +137 -0
  110. package/lib/auto/state-manager.js +126 -0
  111. package/lib/auto/task-queue-manager.js +340 -0
  112. package/lib/backup/backup-system.js +372 -0
  113. package/lib/backup/selective-backup.js +207 -0
  114. package/lib/collab/agent-registry.js +240 -0
  115. package/lib/collab/collab-manager.js +285 -0
  116. package/lib/collab/contract-manager.js +320 -0
  117. package/lib/collab/coordinator.js +370 -0
  118. package/lib/collab/dependency-manager.js +280 -0
  119. package/lib/collab/index.js +20 -0
  120. package/lib/collab/integration-manager.js +202 -0
  121. package/lib/collab/merge-coordinator.js +252 -0
  122. package/lib/collab/metadata-manager.js +233 -0
  123. package/lib/collab/multi-agent-config.js +120 -0
  124. package/lib/collab/spec-lifecycle-manager.js +304 -0
  125. package/lib/collab/sync-barrier.js +88 -0
  126. package/lib/collab/visualizer.js +208 -0
  127. package/lib/commands/adopt.js +749 -0
  128. package/lib/commands/auto.js +19559 -0
  129. package/lib/commands/collab.js +275 -0
  130. package/lib/commands/context.js +99 -0
  131. package/lib/commands/docs.js +808 -0
  132. package/lib/commands/doctor.js +273 -0
  133. package/lib/commands/env.js +420 -0
  134. package/lib/commands/knowledge.js +309 -0
  135. package/lib/commands/lock.js +235 -0
  136. package/lib/commands/ops.js +409 -0
  137. package/lib/commands/orchestrate.js +446 -0
  138. package/lib/commands/prompt.js +105 -0
  139. package/lib/commands/repo.js +118 -0
  140. package/lib/commands/rollback.js +219 -0
  141. package/lib/commands/scene.js +15549 -0
  142. package/lib/commands/spec-bootstrap.js +147 -0
  143. package/lib/commands/spec-gate.js +157 -0
  144. package/lib/commands/spec-pipeline.js +205 -0
  145. package/lib/commands/status.js +321 -0
  146. package/lib/commands/task.js +199 -0
  147. package/lib/commands/templates.js +654 -0
  148. package/lib/commands/upgrade.js +231 -0
  149. package/lib/commands/value.js +569 -0
  150. package/lib/commands/watch.js +684 -0
  151. package/lib/commands/workflows.js +240 -0
  152. package/lib/commands/workspace-multi.js +325 -0
  153. package/lib/commands/workspace.js +189 -0
  154. package/lib/context/context-exporter.js +378 -0
  155. package/lib/context/prompt-generator.js +482 -0
  156. package/lib/data/moqui-capability-lexicon.json +45 -0
  157. package/lib/environment/backup-system.js +189 -0
  158. package/lib/environment/environment-manager.js +379 -0
  159. package/lib/environment/environment-registry.js +168 -0
  160. package/lib/gitignore/gitignore-backup.js +229 -0
  161. package/lib/gitignore/gitignore-detector.js +239 -0
  162. package/lib/gitignore/gitignore-integration.js +267 -0
  163. package/lib/gitignore/gitignore-transformer.js +193 -0
  164. package/lib/gitignore/layered-rules-template.js +42 -0
  165. package/lib/governance/archive-tool.js +284 -0
  166. package/lib/governance/cleanup-tool.js +237 -0
  167. package/lib/governance/config-manager.js +186 -0
  168. package/lib/governance/diagnostic-engine.js +271 -0
  169. package/lib/governance/doc-reference-checker.js +200 -0
  170. package/lib/governance/execution-logger.js +243 -0
  171. package/lib/governance/file-scanner.js +285 -0
  172. package/lib/governance/hooks-manager.js +333 -0
  173. package/lib/governance/reporter.js +337 -0
  174. package/lib/governance/validation-engine.js +181 -0
  175. package/lib/i18n.js +79 -0
  176. package/lib/knowledge/entry-manager.js +208 -0
  177. package/lib/knowledge/index-manager.js +261 -0
  178. package/lib/knowledge/knowledge-manager.js +273 -0
  179. package/lib/knowledge/template-manager.js +191 -0
  180. package/lib/lock/index.js +21 -0
  181. package/lib/lock/lock-file.js +192 -0
  182. package/lib/lock/lock-manager.js +321 -0
  183. package/lib/lock/machine-identifier.js +135 -0
  184. package/lib/lock/steering-file-lock.js +207 -0
  185. package/lib/lock/task-lock-manager.js +345 -0
  186. package/lib/operations/audit-logger.js +293 -0
  187. package/lib/operations/feedback-manager.js +1147 -0
  188. package/lib/operations/index.js +23 -0
  189. package/lib/operations/models/index.js +170 -0
  190. package/lib/operations/operations-manager.js +151 -0
  191. package/lib/operations/operations-validator.js +280 -0
  192. package/lib/operations/permission-manager.js +354 -0
  193. package/lib/operations/template-loader.js +143 -0
  194. package/lib/orchestrator/agent-spawner.js +629 -0
  195. package/lib/orchestrator/bootstrap-prompt-builder.js +236 -0
  196. package/lib/orchestrator/index.js +19 -0
  197. package/lib/orchestrator/orchestration-engine.js +1270 -0
  198. package/lib/orchestrator/orchestrator-config.js +173 -0
  199. package/lib/orchestrator/status-monitor.js +591 -0
  200. package/lib/python-checker.js +209 -0
  201. package/lib/repo/config-manager.js +580 -0
  202. package/lib/repo/errors/config-error.js +13 -0
  203. package/lib/repo/errors/git-error.js +15 -0
  204. package/lib/repo/errors/repo-error.js +14 -0
  205. package/lib/repo/git-operations.js +181 -0
  206. package/lib/repo/handlers/.gitkeep +1 -0
  207. package/lib/repo/handlers/exec-handler.js +155 -0
  208. package/lib/repo/handlers/health-handler.js +169 -0
  209. package/lib/repo/handlers/init-handler.js +197 -0
  210. package/lib/repo/handlers/status-handler.js +176 -0
  211. package/lib/repo/output-formatter.js +184 -0
  212. package/lib/repo/path-resolver.js +178 -0
  213. package/lib/repo/repo-manager.js +514 -0
  214. package/lib/scene-runtime/audit-emitter.js +59 -0
  215. package/lib/scene-runtime/binding-plugin-loader.js +351 -0
  216. package/lib/scene-runtime/binding-registry.js +349 -0
  217. package/lib/scene-runtime/eval-bridge.js +44 -0
  218. package/lib/scene-runtime/index.js +19 -0
  219. package/lib/scene-runtime/moqui-adapter.js +620 -0
  220. package/lib/scene-runtime/moqui-client.js +606 -0
  221. package/lib/scene-runtime/moqui-extractor.js +2029 -0
  222. package/lib/scene-runtime/plan-compiler.js +208 -0
  223. package/lib/scene-runtime/policy-gate.js +58 -0
  224. package/lib/scene-runtime/runtime-executor.js +358 -0
  225. package/lib/scene-runtime/scene-loader.js +96 -0
  226. package/lib/scene-runtime/scene-ontology.js +959 -0
  227. package/lib/scene-runtime/scene-template-linter.js +852 -0
  228. package/lib/scene-runtime/templates/scene-template-erp-query-v0.1.yaml +28 -0
  229. package/lib/scene-runtime/templates/scene-template-hybrid-shadow-v0.1.yaml +34 -0
  230. package/lib/spec/bootstrap/context-collector.js +48 -0
  231. package/lib/spec/bootstrap/draft-generator.js +158 -0
  232. package/lib/spec/bootstrap/questionnaire-engine.js +70 -0
  233. package/lib/spec/bootstrap/trace-emitter.js +59 -0
  234. package/lib/spec/multi-spec-orchestrate.js +93 -0
  235. package/lib/spec/pipeline/constants.js +6 -0
  236. package/lib/spec/pipeline/stage-adapters.js +118 -0
  237. package/lib/spec/pipeline/stage-runner.js +146 -0
  238. package/lib/spec/pipeline/state-store.js +119 -0
  239. package/lib/spec-gate/engine/gate-engine.js +165 -0
  240. package/lib/spec-gate/policy/default-policy.js +22 -0
  241. package/lib/spec-gate/policy/policy-loader.js +103 -0
  242. package/lib/spec-gate/result-emitter.js +81 -0
  243. package/lib/spec-gate/rules/default-rules.js +156 -0
  244. package/lib/spec-gate/rules/rule-registry.js +51 -0
  245. package/lib/steering/adoption-config.js +164 -0
  246. package/lib/steering/compliance-auto-fixer.js +204 -0
  247. package/lib/steering/compliance-cache.js +99 -0
  248. package/lib/steering/compliance-error-reporter.js +70 -0
  249. package/lib/steering/context-sync-manager.js +273 -0
  250. package/lib/steering/index.js +92 -0
  251. package/lib/steering/spec-steering.js +230 -0
  252. package/lib/steering/steering-compliance-checker.js +73 -0
  253. package/lib/steering/steering-loader.js +144 -0
  254. package/lib/steering/steering-manager.js +289 -0
  255. package/lib/task/index.js +12 -0
  256. package/lib/task/task-claimer.js +489 -0
  257. package/lib/task/task-status-store.js +418 -0
  258. package/lib/templates/cache-manager.js +440 -0
  259. package/lib/templates/content-generalizer.js +247 -0
  260. package/lib/templates/frontmatter-generator.js +128 -0
  261. package/lib/templates/git-handler.js +471 -0
  262. package/lib/templates/metadata-collector.js +328 -0
  263. package/lib/templates/path-utils.js +144 -0
  264. package/lib/templates/registry-parser.js +505 -0
  265. package/lib/templates/spec-reader.js +216 -0
  266. package/lib/templates/template-applicator.js +249 -0
  267. package/lib/templates/template-creator.js +256 -0
  268. package/lib/templates/template-error.js +143 -0
  269. package/lib/templates/template-exporter.js +502 -0
  270. package/lib/templates/template-manager.js +782 -0
  271. package/lib/templates/template-validator.js +361 -0
  272. package/lib/upgrade/migration-engine.js +382 -0
  273. package/lib/upgrade/migrations/.gitkeep +52 -0
  274. package/lib/upgrade/migrations/1.0.0-to-1.1.0.js +78 -0
  275. package/lib/utils/file-diff.js +177 -0
  276. package/lib/utils/fs-utils.js +274 -0
  277. package/lib/utils/tool-detector.js +383 -0
  278. package/lib/utils/validation.js +324 -0
  279. package/lib/value/gate-summary-emitter.js +99 -0
  280. package/lib/value/metric-contract-loader.js +210 -0
  281. package/lib/value/risk-evaluator.js +117 -0
  282. package/lib/value/weekly-snapshot-builder.js +61 -0
  283. package/lib/version/version-checker.js +156 -0
  284. package/lib/version/version-manager.js +327 -0
  285. package/lib/watch/action-executor.js +458 -0
  286. package/lib/watch/event-debouncer.js +323 -0
  287. package/lib/watch/execution-logger.js +550 -0
  288. package/lib/watch/file-watcher.js +499 -0
  289. package/lib/watch/presets.js +266 -0
  290. package/lib/watch/watch-manager.js +533 -0
  291. package/lib/workspace/multi/global-config.js +150 -0
  292. package/lib/workspace/multi/index.js +22 -0
  293. package/lib/workspace/multi/path-utils.js +173 -0
  294. package/lib/workspace/multi/workspace-context-resolver.js +244 -0
  295. package/lib/workspace/multi/workspace-registry.js +196 -0
  296. package/lib/workspace/multi/workspace-state-manager.js +537 -0
  297. package/lib/workspace/multi/workspace.js +90 -0
  298. package/lib/workspace/workspace-manager.js +370 -0
  299. package/lib/workspace/workspace-sync.js +356 -0
  300. package/locales/en.json +114 -0
  301. package/locales/zh.json +114 -0
  302. package/package.json +102 -0
  303. package/template/.kiro/README.md +247 -0
  304. package/template/.kiro/hooks/check-spec-on-create.kiro.hook +17 -0
  305. package/template/.kiro/hooks/run-tests-on-save.kiro.hook +13 -0
  306. package/template/.kiro/hooks/sync-tasks-on-edit.kiro.hook +16 -0
  307. package/template/.kiro/specs/SPEC_WORKFLOW_GUIDE.md +134 -0
  308. package/template/.kiro/steering/CORE_PRINCIPLES.md +133 -0
  309. package/template/.kiro/steering/CURRENT_CONTEXT.md +30 -0
  310. package/template/.kiro/steering/ENVIRONMENT.md +35 -0
  311. package/template/.kiro/steering/RULES_GUIDE.md +46 -0
  312. package/template/.kiro/templates/operations/default/change-impact.md +112 -0
  313. package/template/.kiro/templates/operations/default/deployment.md +91 -0
  314. package/template/.kiro/templates/operations/default/feedback-response.md +269 -0
  315. package/template/.kiro/templates/operations/default/migration-plan.md +172 -0
  316. package/template/.kiro/templates/operations/default/monitoring.md +135 -0
  317. package/template/.kiro/templates/operations/default/operations.md +135 -0
  318. package/template/.kiro/templates/operations/default/rollback.md +143 -0
  319. package/template/.kiro/templates/operations/default/tools.yaml +364 -0
  320. package/template/.kiro/templates/operations/default/troubleshooting.md +123 -0
  321. package/template/.kiro/tools/backup_manager.py +295 -0
  322. package/template/.kiro/tools/configuration_manager.py +218 -0
  323. package/template/.kiro/tools/document_evaluator.py +550 -0
  324. package/template/.kiro/tools/enhancement_logger.py +168 -0
  325. package/template/.kiro/tools/error_handler.py +335 -0
  326. package/template/.kiro/tools/improvement_identifier.py +444 -0
  327. package/template/.kiro/tools/modification_applicator.py +737 -0
  328. package/template/.kiro/tools/quality_gate_enforcer.py +207 -0
  329. package/template/.kiro/tools/quality_scorer.py +305 -0
  330. package/template/.kiro/tools/report_generator.py +154 -0
  331. package/template/.kiro/tools/ultrawork_enhancer.py +676 -0
  332. package/template/.kiro/tools/ultrawork_enhancer_refactored.py +0 -0
  333. package/template/.kiro/tools/ultrawork_enhancer_v2.py +463 -0
  334. package/template/.kiro/tools/ultrawork_enhancer_v3.py +606 -0
  335. package/template/.kiro/tools/workflow_quality_gate.py +100 -0
  336. package/template/README.md +111 -0
@@ -0,0 +1,304 @@
1
+ /**
2
+ * SpecLifecycleManager - Manages Spec lifecycle state machine
3
+ *
4
+ * Tracks Spec status through: planned → assigned → in-progress → completed → released.
5
+ * Persists state to `.kiro/specs/{specName}/lifecycle.json`.
6
+ * On completion, triggers ContextSyncManager update and AgentRegistry notification.
7
+ * In single-Agent mode, all operations are no-ops.
8
+ *
9
+ * Requirements: 4.1, 4.2, 4.3, 4.4, 4.5, 4.6, 6.3
10
+ */
11
+
12
+ const path = require('path');
13
+ const fs = require('fs-extra');
14
+ const fsUtils = require('../utils/fs-utils');
15
+ const { MultiAgentConfig } = require('./multi-agent-config');
16
+
17
+ const SPECS_DIR = '.kiro/specs';
18
+
19
+ const VALID_TRANSITIONS = {
20
+ planned: ['assigned'],
21
+ assigned: ['in-progress', 'planned'],
22
+ 'in-progress': ['completed', 'assigned'],
23
+ completed: ['released'],
24
+ released: [],
25
+ };
26
+
27
+ const DEFAULT_STATUS = 'planned';
28
+
29
+ class SpecLifecycleManager {
30
+ /**
31
+ * @param {string} workspaceRoot - Absolute path to the project root
32
+ * @param {import('../steering/context-sync-manager').ContextSyncManager} contextSyncManager
33
+ * @param {import('./agent-registry').AgentRegistry} agentRegistry
34
+ */
35
+ constructor(workspaceRoot, contextSyncManager, agentRegistry) {
36
+ this._workspaceRoot = workspaceRoot;
37
+ this._contextSyncManager = contextSyncManager;
38
+ this._agentRegistry = agentRegistry;
39
+ this._multiAgentConfig = new MultiAgentConfig(workspaceRoot);
40
+ }
41
+
42
+ /**
43
+ * Get the lifecycle.json path for a Spec.
44
+ * @param {string} specName
45
+ * @returns {string}
46
+ * @private
47
+ */
48
+ _lifecyclePath(specName) {
49
+ return path.join(this._workspaceRoot, SPECS_DIR, specName, 'lifecycle.json');
50
+ }
51
+
52
+ /**
53
+ * Get the current lifecycle status of a Spec.
54
+ * Single-Agent mode: returns 'planned' (default).
55
+ *
56
+ * @param {string} specName
57
+ * @returns {Promise<string>}
58
+ */
59
+ async getStatus(specName) {
60
+ const enabled = await this._multiAgentConfig.isEnabled();
61
+ if (!enabled) {
62
+ return DEFAULT_STATUS;
63
+ }
64
+
65
+ const lifecycle = await this.readLifecycle(specName);
66
+ return lifecycle.status;
67
+ }
68
+
69
+ /**
70
+ * Transition a Spec to a new status.
71
+ * Validates the transition against VALID_TRANSITIONS.
72
+ * Single-Agent mode: returns success without persisting.
73
+ *
74
+ * @param {string} specName
75
+ * @param {string} newStatus
76
+ * @returns {Promise<{success: boolean, oldStatus: string, newStatus: string, error?: string}>}
77
+ */
78
+ async transition(specName, newStatus) {
79
+ const enabled = await this._multiAgentConfig.isEnabled();
80
+ if (!enabled) {
81
+ return { success: true, oldStatus: DEFAULT_STATUS, newStatus };
82
+ }
83
+
84
+ const lifecycle = await this.readLifecycle(specName);
85
+ const oldStatus = lifecycle.status;
86
+
87
+ // Validate transition
88
+ const allowed = VALID_TRANSITIONS[oldStatus];
89
+ if (!allowed || !allowed.includes(newStatus)) {
90
+ return {
91
+ success: false,
92
+ oldStatus,
93
+ newStatus,
94
+ error: `Invalid transition: ${oldStatus} → ${newStatus}. Allowed: [${(allowed || []).join(', ')}]`,
95
+ };
96
+ }
97
+
98
+ // Record transition
99
+ lifecycle.status = newStatus;
100
+ lifecycle.transitions.push({
101
+ from: oldStatus,
102
+ to: newStatus,
103
+ timestamp: new Date().toISOString(),
104
+ agentId: null, // Will be set by caller if needed
105
+ });
106
+
107
+ await this.writeLifecycle(specName, lifecycle);
108
+
109
+ // On completion: trigger ContextSyncManager update and AgentRegistry notification
110
+ if (newStatus === 'completed') {
111
+ await this._onCompleted(specName);
112
+ }
113
+
114
+ return { success: true, oldStatus, newStatus };
115
+ }
116
+
117
+ /**
118
+ * Check if all tasks in a Spec are completed and auto-transition to 'completed'.
119
+ * Only transitions if current status is 'in-progress'.
120
+ * Single-Agent mode: returns {completed: false, transitioned: false}.
121
+ *
122
+ * @param {string} specName
123
+ * @returns {Promise<{completed: boolean, transitioned: boolean}>}
124
+ */
125
+ async checkCompletion(specName) {
126
+ const enabled = await this._multiAgentConfig.isEnabled();
127
+ if (!enabled) {
128
+ return { completed: false, transitioned: false };
129
+ }
130
+
131
+ const tasksPath = path.join(this._workspaceRoot, SPECS_DIR, specName, 'tasks.md');
132
+ const exists = await fs.pathExists(tasksPath);
133
+ if (!exists) {
134
+ return { completed: false, transitioned: false };
135
+ }
136
+
137
+ let content;
138
+ try {
139
+ content = await fs.readFile(tasksPath, 'utf8');
140
+ } catch (err) {
141
+ console.warn(`[SpecLifecycleManager] Failed to read ${tasksPath}: ${err.message}`);
142
+ return { completed: false, transitioned: false };
143
+ }
144
+
145
+ const allCompleted = this._areAllLeafTasksCompleted(content);
146
+ if (!allCompleted) {
147
+ return { completed: false, transitioned: false };
148
+ }
149
+
150
+ // Only transition if currently in-progress
151
+ const lifecycle = await this.readLifecycle(specName);
152
+ if (lifecycle.status !== 'in-progress') {
153
+ return { completed: true, transitioned: false };
154
+ }
155
+
156
+ const result = await this.transition(specName, 'completed');
157
+ return { completed: true, transitioned: result.success };
158
+ }
159
+
160
+ /**
161
+ * Read lifecycle.json for a Spec.
162
+ * Returns default lifecycle if file doesn't exist or is corrupted.
163
+ *
164
+ * @param {string} specName
165
+ * @returns {Promise<{specName: string, status: string, transitions: Array}>}
166
+ */
167
+ async readLifecycle(specName) {
168
+ const filePath = this._lifecyclePath(specName);
169
+ const exists = await fs.pathExists(filePath);
170
+
171
+ if (!exists) {
172
+ return { specName, status: DEFAULT_STATUS, transitions: [] };
173
+ }
174
+
175
+ try {
176
+ const data = await fsUtils.readJSON(filePath);
177
+ if (!data || typeof data.status !== 'string' || !Array.isArray(data.transitions)) {
178
+ console.warn(`[SpecLifecycleManager] Corrupted lifecycle.json for ${specName}, rebuilding`);
179
+ return { specName, status: DEFAULT_STATUS, transitions: [] };
180
+ }
181
+ return {
182
+ specName: data.specName || specName,
183
+ status: data.status,
184
+ transitions: data.transitions,
185
+ };
186
+ } catch (err) {
187
+ console.warn(`[SpecLifecycleManager] Failed to read lifecycle.json for ${specName}: ${err.message}`);
188
+ return { specName, status: DEFAULT_STATUS, transitions: [] };
189
+ }
190
+ }
191
+
192
+ /**
193
+ * Write lifecycle.json for a Spec (atomic write).
194
+ *
195
+ * @param {string} specName
196
+ * @param {{specName?: string, status: string, transitions: Array}} lifecycle
197
+ * @returns {Promise<void>}
198
+ */
199
+ async writeLifecycle(specName, lifecycle) {
200
+ const filePath = this._lifecyclePath(specName);
201
+ const dir = path.dirname(filePath);
202
+ await fsUtils.ensureDirectory(dir);
203
+
204
+ const data = {
205
+ specName: lifecycle.specName || specName,
206
+ status: lifecycle.status,
207
+ transitions: lifecycle.transitions || [],
208
+ };
209
+
210
+ await fsUtils.writeJSON(filePath, data);
211
+ }
212
+
213
+ // ── Private helpers ──────────────────────────────────────────────
214
+
215
+ /**
216
+ * Handle completion side-effects: update ContextSyncManager and notify via AgentRegistry.
217
+ *
218
+ * @param {string} specName
219
+ * @returns {Promise<void>}
220
+ * @private
221
+ */
222
+ async _onCompleted(specName) {
223
+ // Update ContextSyncManager with completed status
224
+ if (this._contextSyncManager) {
225
+ try {
226
+ await this._contextSyncManager.updateSpecProgress(specName, {
227
+ status: 'completed',
228
+ progress: 100,
229
+ summary: `Spec ${specName} completed`,
230
+ });
231
+ } catch (err) {
232
+ console.warn(`[SpecLifecycleManager] Failed to update context for ${specName}: ${err.message}`);
233
+ }
234
+ }
235
+
236
+ // Notify active agents via AgentRegistry (log for now)
237
+ if (this._agentRegistry) {
238
+ try {
239
+ const activeAgents = await this._agentRegistry.getActiveAgents();
240
+ if (activeAgents.length > 0) {
241
+ console.log(
242
+ `[SpecLifecycleManager] Spec ${specName} completed. Notifying ${activeAgents.length} active agent(s): ${activeAgents.map(a => a.agentId).join(', ')}`
243
+ );
244
+ }
245
+ } catch (err) {
246
+ console.warn(`[SpecLifecycleManager] Failed to notify agents for ${specName}: ${err.message}`);
247
+ }
248
+ }
249
+ }
250
+
251
+ /**
252
+ * Check if all leaf tasks in tasks.md content are completed.
253
+ * Leaf tasks are checkbox lines with no deeper-indented sub-tasks following them.
254
+ * A task is completed when its checkbox is [x].
255
+ *
256
+ * @param {string} content
257
+ * @returns {boolean}
258
+ * @private
259
+ */
260
+ _areAllLeafTasksCompleted(content) {
261
+ const lines = content.split('\n');
262
+ const taskPattern = /^(\s*)- \[([ x\-~])\]\*?\s/;
263
+
264
+ // First pass: identify all task lines with their indentation levels
265
+ const tasks = [];
266
+ for (let i = 0; i < lines.length; i++) {
267
+ const match = lines[i].match(taskPattern);
268
+ if (match) {
269
+ tasks.push({
270
+ index: i,
271
+ indent: match[1].length,
272
+ status: match[2],
273
+ });
274
+ }
275
+ }
276
+
277
+ if (tasks.length === 0) {
278
+ return false;
279
+ }
280
+
281
+ // Second pass: identify leaf tasks and check completion
282
+ let total = 0;
283
+ let completed = 0;
284
+
285
+ for (let i = 0; i < tasks.length; i++) {
286
+ const current = tasks[i];
287
+ const next = tasks[i + 1];
288
+
289
+ // A task is a leaf if the next task is not more indented
290
+ const isLeaf = !next || next.indent <= current.indent;
291
+
292
+ if (isLeaf) {
293
+ total++;
294
+ if (current.status === 'x') {
295
+ completed++;
296
+ }
297
+ }
298
+ }
299
+
300
+ return total > 0 && completed === total;
301
+ }
302
+ }
303
+
304
+ module.exports = { SpecLifecycleManager, VALID_TRANSITIONS, DEFAULT_STATUS };
@@ -0,0 +1,88 @@
1
+ /**
2
+ * SyncBarrier - Agent 切换 Spec 时的同步屏障
3
+ *
4
+ * 确保 Agent 切换 Spec 时基于一致的代码库和 Steering 工作。
5
+ * - 检查工作区是否有未提交的更改(git status --porcelain)
6
+ * - 重新加载所有层级的 Steering 约束
7
+ * - 单 Agent 模式下为 no-op(返回 {ready: true})
8
+ *
9
+ * Requirements: 5.1, 5.2, 5.3, 5.4
10
+ */
11
+
12
+ const { execSync } = require('child_process');
13
+ const { MultiAgentConfig } = require('./multi-agent-config');
14
+
15
+ class SyncBarrier {
16
+ /**
17
+ * @param {string} workspaceRoot - Absolute path to the project root
18
+ * @param {import('../steering/steering-loader').SteeringLoader} steeringLoader
19
+ */
20
+ constructor(workspaceRoot, steeringLoader) {
21
+ this._workspaceRoot = workspaceRoot;
22
+ this._steeringLoader = steeringLoader;
23
+ this._multiAgentConfig = new MultiAgentConfig(workspaceRoot);
24
+ }
25
+
26
+ /**
27
+ * 执行 Spec 切换前的同步检查。
28
+ * 单 Agent 模式下为 no-op(返回 {ready: true})。
29
+ *
30
+ * @param {string} specName - 目标 Spec
31
+ * @returns {Promise<{ready: boolean, error?: string, steering?: object}>}
32
+ */
33
+ async prepareSwitch(specName) {
34
+ const enabled = await this._multiAgentConfig.isEnabled();
35
+ if (!enabled) {
36
+ return { ready: true };
37
+ }
38
+
39
+ // Check for uncommitted changes
40
+ try {
41
+ const hasChanges = await this.hasUncommittedChanges();
42
+ if (hasChanges) {
43
+ return {
44
+ ready: false,
45
+ error: 'Uncommitted changes detected. Please commit or stash your changes before switching Spec.',
46
+ };
47
+ }
48
+ } catch (err) {
49
+ return {
50
+ ready: false,
51
+ error: `Failed to check git status: ${err.message}`,
52
+ };
53
+ }
54
+
55
+ // Reload steering for the target Spec
56
+ try {
57
+ const steering = await this._steeringLoader.loadMerged(specName);
58
+ return { ready: true, steering };
59
+ } catch (err) {
60
+ return {
61
+ ready: false,
62
+ error: `Failed to reload steering for ${specName}: ${err.message}`,
63
+ };
64
+ }
65
+ }
66
+
67
+ /**
68
+ * 检查工作区是否有未提交的更改。
69
+ * Git 命令失败时返回 false(优雅降级)。
70
+ *
71
+ * @returns {Promise<boolean>}
72
+ */
73
+ async hasUncommittedChanges() {
74
+ try {
75
+ const output = execSync('git status --porcelain', {
76
+ encoding: 'utf8',
77
+ cwd: this._workspaceRoot,
78
+ stdio: ['pipe', 'pipe', 'pipe'],
79
+ });
80
+ return output.trim().length > 0;
81
+ } catch (_err) {
82
+ // Git command failed — graceful degradation
83
+ return false;
84
+ }
85
+ }
86
+ }
87
+
88
+ module.exports = { SyncBarrier };
@@ -0,0 +1,208 @@
1
+ /**
2
+ * Visualizer generates dependency graph visualizations
3
+ */
4
+ class Visualizer {
5
+ /**
6
+ * Generate text-based dependency graph
7
+ * @param {Object} graph - Graph with nodes and edges
8
+ * @param {Object} options - Visualization options
9
+ * @returns {string} Text-based graph
10
+ */
11
+ generateTextGraph(graph, options = {}) {
12
+ const lines = [];
13
+ const { showCriticalPath = false } = options;
14
+
15
+ // Get critical path if requested
16
+ let criticalPath = [];
17
+ if (showCriticalPath) {
18
+ criticalPath = this._getCriticalPath(graph);
19
+ }
20
+
21
+ // Group by master specs
22
+ const masterSpecs = graph.nodes.filter(n => n.type === 'master');
23
+ const subSpecs = graph.nodes.filter(n => n.type === 'sub');
24
+
25
+ if (masterSpecs.length > 0) {
26
+ for (const master of masterSpecs) {
27
+ lines.push(this._formatNode(master, criticalPath));
28
+
29
+ // Find sub-specs of this master
30
+ const children = this._getChildren(master.id, graph);
31
+ for (const child of children) {
32
+ lines.push(` ${this._formatNode(child, criticalPath)}`);
33
+
34
+ // Show dependencies
35
+ const deps = graph.edges.filter(e => e.from === child.id);
36
+ for (const dep of deps) {
37
+ lines.push(` └─ requires: ${dep.to} (${dep.type})`);
38
+ }
39
+ }
40
+ lines.push('');
41
+ }
42
+ }
43
+
44
+ // Show standalone specs
45
+ const standalone = subSpecs.filter(s => {
46
+ const hasParent = graph.edges.some(e => e.to === s.id && graph.nodes.find(n => n.id === e.from && n.type === 'master'));
47
+ return !hasParent;
48
+ });
49
+
50
+ if (standalone.length > 0) {
51
+ lines.push('Standalone Specs:');
52
+ for (const spec of standalone) {
53
+ lines.push(this._formatNode(spec, criticalPath));
54
+
55
+ const deps = graph.edges.filter(e => e.from === spec.id);
56
+ for (const dep of deps) {
57
+ lines.push(` └─ requires: ${dep.to} (${dep.type})`);
58
+ }
59
+ }
60
+ }
61
+
62
+ if (showCriticalPath && criticalPath.length > 0) {
63
+ lines.push('');
64
+ lines.push(`Critical Path: ${criticalPath.join(' → ')}`);
65
+ }
66
+
67
+ return lines.join('\n');
68
+ }
69
+
70
+ /**
71
+ * Generate Mermaid format graph
72
+ * @param {Object} graph - Graph with nodes and edges
73
+ * @returns {string} Mermaid graph definition
74
+ */
75
+ generateMermaidGraph(graph) {
76
+ const lines = ['graph TD'];
77
+
78
+ // Add nodes
79
+ for (const node of graph.nodes) {
80
+ const symbol = this._getStatusSymbol(node.status);
81
+ const label = `${node.id} ${symbol}`;
82
+ const nodeId = this._sanitizeId(node.id);
83
+
84
+ // Use different shapes for different types
85
+ if (node.type === 'master') {
86
+ lines.push(` ${nodeId}[["${label}"]]`);
87
+ } else {
88
+ lines.push(` ${nodeId}["${label}"]`);
89
+ }
90
+ }
91
+
92
+ // Add edges
93
+ for (const edge of edges) {
94
+ const fromId = this._sanitizeId(edge.from);
95
+ const toId = this._sanitizeId(edge.to);
96
+ const label = edge.type === 'requires-completion' ? 'requires' : edge.type;
97
+ lines.push(` ${fromId} -->|${label}| ${toId}`);
98
+ }
99
+
100
+ return lines.join('\n');
101
+ }
102
+
103
+ /**
104
+ * Highlight critical path in graph
105
+ * @param {Object} graph - Graph with nodes and edges
106
+ * @returns {Array<string>} Critical path node IDs
107
+ */
108
+ highlightCriticalPath(graph) {
109
+ return this._getCriticalPath(graph);
110
+ }
111
+
112
+ /**
113
+ * Format a spec node for display
114
+ * @param {Object} node - Node to format
115
+ * @param {Array<string>} criticalPath - Critical path nodes
116
+ * @returns {string} Formatted node string
117
+ */
118
+ _formatNode(node, criticalPath = []) {
119
+ const symbol = this._getStatusSymbol(node.status);
120
+ const assignment = node.kiroInstance ? ` (${node.kiroInstance})` : ' (unassigned)';
121
+ const critical = criticalPath.includes(node.id) ? ' [CRITICAL]' : '';
122
+
123
+ return `${symbol} ${node.id}${assignment}${critical}`;
124
+ }
125
+
126
+ /**
127
+ * Get status symbol for a node
128
+ * @param {string} status - Node status
129
+ * @returns {string} Symbol
130
+ */
131
+ _getStatusSymbol(status) {
132
+ const symbols = {
133
+ 'completed': '✓',
134
+ 'in-progress': '⧗',
135
+ 'not-started': '○',
136
+ 'blocked': '✗'
137
+ };
138
+ return symbols[status] || '?';
139
+ }
140
+
141
+ /**
142
+ * Get children of a master spec
143
+ * @param {string} masterId - Master spec ID
144
+ * @param {Object} graph - Graph
145
+ * @returns {Array<Object>} Child nodes
146
+ */
147
+ _getChildren(masterId, graph) {
148
+ // In our model, children reference parent via metadata
149
+ // For visualization, we can infer from naming or metadata
150
+ // For now, return empty array (would need metadata access)
151
+ return [];
152
+ }
153
+
154
+ /**
155
+ * Get critical path (longest dependency chain)
156
+ * @param {Object} graph - Graph
157
+ * @returns {Array<string>} Node IDs in critical path
158
+ */
159
+ _getCriticalPath(graph) {
160
+ const memo = new Map();
161
+
162
+ const getLongestPath = (nodeId) => {
163
+ if (memo.has(nodeId)) {
164
+ return memo.get(nodeId);
165
+ }
166
+
167
+ const incoming = graph.edges.filter(e => e.to === nodeId);
168
+
169
+ if (incoming.length === 0) {
170
+ memo.set(nodeId, [nodeId]);
171
+ return [nodeId];
172
+ }
173
+
174
+ let longestPath = [];
175
+ for (const edge of incoming) {
176
+ const path = getLongestPath(edge.from);
177
+ if (path.length > longestPath.length) {
178
+ longestPath = path;
179
+ }
180
+ }
181
+
182
+ const result = [...longestPath, nodeId];
183
+ memo.set(nodeId, result);
184
+ return result;
185
+ };
186
+
187
+ let criticalPath = [];
188
+ for (const node of graph.nodes) {
189
+ const path = getLongestPath(node.id);
190
+ if (path.length > criticalPath.length) {
191
+ criticalPath = path;
192
+ }
193
+ }
194
+
195
+ return criticalPath;
196
+ }
197
+
198
+ /**
199
+ * Sanitize ID for Mermaid
200
+ * @param {string} id - Node ID
201
+ * @returns {string} Sanitized ID
202
+ */
203
+ _sanitizeId(id) {
204
+ return id.replace(/[^a-zA-Z0-9]/g, '_');
205
+ }
206
+ }
207
+
208
+ module.exports = Visualizer;