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,505 @@
1
+ /**
2
+ * RegistryParser - Parses and validates template registry files
3
+ *
4
+ * Handles JSON schema validation, registry parsing, indexing, and search.
5
+ */
6
+
7
+ const fs = require('fs-extra');
8
+ const path = require('path');
9
+ const { ValidationError } = require('./template-error');
10
+
11
+ class RegistryParser {
12
+ constructor() {
13
+ this.registryCache = new Map();
14
+ }
15
+
16
+ /**
17
+ * Parses a template registry file
18
+ *
19
+ * @param {string} registryPath - Path to template-registry.json
20
+ * @returns {Promise<Object>} Parsed registry
21
+ */
22
+ async parseRegistry(registryPath) {
23
+ // Check cache
24
+ if (this.registryCache.has(registryPath)) {
25
+ return this.registryCache.get(registryPath);
26
+ }
27
+
28
+ // Read registry file
29
+ if (!await fs.pathExists(registryPath)) {
30
+ throw new ValidationError(
31
+ 'Registry file not found',
32
+ { path: registryPath }
33
+ );
34
+ }
35
+
36
+ let registry;
37
+ try {
38
+ registry = await fs.readJson(registryPath);
39
+ } catch (error) {
40
+ throw new ValidationError(
41
+ 'Failed to parse registry JSON',
42
+ {
43
+ path: registryPath,
44
+ error: error.message
45
+ }
46
+ );
47
+ }
48
+
49
+ // Validate schema
50
+ this.validateRegistrySchema(registry);
51
+
52
+ // Cache parsed registry
53
+ this.registryCache.set(registryPath, registry);
54
+
55
+ return registry;
56
+ }
57
+
58
+ /**
59
+ * Validates registry schema
60
+ *
61
+ * @param {Object} registry - Registry object
62
+ * @throws {ValidationError} If schema is invalid
63
+ */
64
+ validateRegistrySchema(registry) {
65
+ const errors = [];
66
+
67
+ // Check version field
68
+ if (!registry.version) {
69
+ errors.push('Missing required field: version');
70
+ }
71
+
72
+ // Check templates array
73
+ if (!registry.templates) {
74
+ errors.push('Missing required field: templates');
75
+ } else if (!Array.isArray(registry.templates)) {
76
+ errors.push('Field "templates" must be an array');
77
+ } else {
78
+ // Validate each template entry
79
+ registry.templates.forEach((template, index) => {
80
+ const templateErrors = this.validateTemplateEntry(template, index);
81
+ errors.push(...templateErrors);
82
+ });
83
+ }
84
+
85
+ if (errors.length > 0) {
86
+ throw new ValidationError(
87
+ 'Registry schema validation failed',
88
+ {
89
+ errors,
90
+ errorCount: errors.length
91
+ }
92
+ );
93
+ }
94
+ }
95
+
96
+ /**
97
+ * Validates a single template entry
98
+ *
99
+ * @param {Object} template - Template entry
100
+ * @param {number} index - Template index
101
+ * @returns {string[]} Array of error messages
102
+ */
103
+ validateTemplateEntry(template, index) {
104
+ const errors = [];
105
+ const prefix = `Template[${index}]`;
106
+
107
+ // Required fields
108
+ const requiredFields = [
109
+ 'id', 'name', 'category', 'description',
110
+ 'difficulty', 'tags', 'applicable_scenarios', 'files'
111
+ ];
112
+
113
+ for (const field of requiredFields) {
114
+ if (!template[field]) {
115
+ errors.push(`${prefix}: Missing required field "${field}"`);
116
+ }
117
+ }
118
+
119
+ // Validate field types
120
+ if (template.tags && !Array.isArray(template.tags)) {
121
+ errors.push(`${prefix}: Field "tags" must be an array`);
122
+ }
123
+
124
+ if (template.applicable_scenarios && !Array.isArray(template.applicable_scenarios)) {
125
+ errors.push(`${prefix}: Field "applicable_scenarios" must be an array`);
126
+ }
127
+
128
+ if (template.files && !Array.isArray(template.files)) {
129
+ errors.push(`${prefix}: Field "files" must be an array`);
130
+ }
131
+
132
+ // Validate difficulty
133
+ const validDifficulties = ['beginner', 'intermediate', 'advanced'];
134
+ if (template.difficulty && !validDifficulties.includes(template.difficulty)) {
135
+ errors.push(`${prefix}: Invalid difficulty "${template.difficulty}". Must be one of: ${validDifficulties.join(', ')}`);
136
+ }
137
+
138
+ // Validate files array
139
+ if (template.files && Array.isArray(template.files)) {
140
+ const requiredFiles = ['requirements.md', 'design.md', 'tasks.md'];
141
+ for (const file of requiredFiles) {
142
+ if (!template.files.includes(file)) {
143
+ errors.push(`${prefix}: Missing required file "${file}" in files array`);
144
+ }
145
+ }
146
+ }
147
+
148
+ return errors;
149
+ }
150
+
151
+ /**
152
+ * Builds searchable index from registry
153
+ *
154
+ * @param {Object} registry - Parsed registry
155
+ * @returns {Object} Indexed registry
156
+ */
157
+ buildIndex(registry) {
158
+ const index = {
159
+ byId: {},
160
+ byCategory: {},
161
+ byTag: {},
162
+ byDifficulty: {},
163
+ all: []
164
+ };
165
+
166
+ for (const template of registry.templates) {
167
+ // Index by ID
168
+ index.byId[template.id] = template;
169
+
170
+ // Index by category
171
+ if (!index.byCategory[template.category]) {
172
+ index.byCategory[template.category] = [];
173
+ }
174
+ index.byCategory[template.category].push(template);
175
+
176
+ // Index by tags
177
+ for (const tag of template.tags || []) {
178
+ if (!index.byTag[tag]) {
179
+ index.byTag[tag] = [];
180
+ }
181
+ index.byTag[tag].push(template);
182
+ }
183
+
184
+ // Index by difficulty
185
+ if (!index.byDifficulty[template.difficulty]) {
186
+ index.byDifficulty[template.difficulty] = [];
187
+ }
188
+ index.byDifficulty[template.difficulty].push(template);
189
+
190
+ // Add to all templates
191
+ index.all.push(template);
192
+ }
193
+
194
+ return index;
195
+ }
196
+
197
+ /**
198
+ * Merges multiple registries
199
+ *
200
+ * @param {Object[]} registries - Array of registry objects
201
+ * @returns {Object} Merged registry
202
+ */
203
+ mergeRegistries(registries) {
204
+ const merged = {
205
+ version: '1.0.0',
206
+ templates: []
207
+ };
208
+
209
+ const seenIds = new Set();
210
+
211
+ for (const registry of registries) {
212
+ for (const template of registry.templates || []) {
213
+ // Skip duplicates (first occurrence wins)
214
+ if (seenIds.has(template.id)) {
215
+ continue;
216
+ }
217
+
218
+ seenIds.add(template.id);
219
+ merged.templates.push(template);
220
+ }
221
+ }
222
+
223
+ return merged;
224
+ }
225
+
226
+ /**
227
+ * Clears registry cache
228
+ */
229
+ clearCache() {
230
+ this.registryCache.clear();
231
+ }
232
+
233
+ /**
234
+ * Gets template by ID
235
+ *
236
+ * @param {Object} index - Registry index
237
+ * @param {string} templateId - Template ID
238
+ * @returns {Object|null} Template or null
239
+ */
240
+ getTemplateById(index, templateId) {
241
+ return index.byId[templateId] || null;
242
+ }
243
+
244
+ /**
245
+ * Gets templates by category
246
+ *
247
+ * @param {Object} index - Registry index
248
+ * @param {string} category - Category name
249
+ * @returns {Object[]} Array of templates
250
+ */
251
+ getTemplatesByCategory(index, category) {
252
+ return index.byCategory[category] || [];
253
+ }
254
+
255
+ /**
256
+ * Gets templates by tag
257
+ *
258
+ * @param {Object} index - Registry index
259
+ * @param {string} tag - Tag name
260
+ * @returns {Object[]} Array of templates
261
+ */
262
+ getTemplatesByTag(index, tag) {
263
+ return index.byTag[tag] || [];
264
+ }
265
+
266
+ /**
267
+ * Gets templates by difficulty
268
+ *
269
+ * @param {Object} index - Registry index
270
+ * @param {string} difficulty - Difficulty level
271
+ * @returns {Object[]} Array of templates
272
+ */
273
+ getTemplatesByDifficulty(index, difficulty) {
274
+ return index.byDifficulty[difficulty] || [];
275
+ }
276
+
277
+ /**
278
+ * Gets all categories
279
+ *
280
+ * @param {Object} index - Registry index
281
+ * @returns {string[]} Array of category names
282
+ */
283
+ getCategories(index) {
284
+ return Object.keys(index.byCategory);
285
+ }
286
+
287
+ /**
288
+ * Gets all tags
289
+ *
290
+ * @param {Object} index - Registry index
291
+ * @returns {string[]} Array of tag names
292
+ */
293
+ getTags(index) {
294
+ return Object.keys(index.byTag);
295
+ }
296
+
297
+ /**
298
+ * Searches templates by keyword
299
+ *
300
+ * @param {Object} index - Registry index
301
+ * @param {string} keyword - Search keyword
302
+ * @param {Object} filters - Optional filters
303
+ * @param {string} filters.category - Filter by category
304
+ * @param {string} filters.difficulty - Filter by difficulty
305
+ * @param {string[]} filters.tags - Filter by tags (any match)
306
+ * @returns {Object[]} Array of matching templates
307
+ */
308
+ searchTemplates(index, keyword, filters = {}) {
309
+ const keywordLower = keyword.toLowerCase();
310
+ let results = [];
311
+
312
+ // Search in all templates
313
+ for (const template of index.all) {
314
+ // Check if keyword matches
315
+ const matchesKeyword = this._matchesKeyword(template, keywordLower);
316
+
317
+ if (!matchesKeyword) {
318
+ continue;
319
+ }
320
+
321
+ // Apply filters
322
+ if (filters.category && template.category !== filters.category) {
323
+ continue;
324
+ }
325
+
326
+ if (filters.difficulty && template.difficulty !== filters.difficulty) {
327
+ continue;
328
+ }
329
+
330
+ if (filters.tags && filters.tags.length > 0) {
331
+ const hasMatchingTag = filters.tags.some(tag =>
332
+ template.tags.includes(tag)
333
+ );
334
+ if (!hasMatchingTag) {
335
+ continue;
336
+ }
337
+ }
338
+
339
+ results.push(template);
340
+ }
341
+
342
+ return results;
343
+ }
344
+
345
+ /**
346
+ * Checks if template matches keyword
347
+ *
348
+ * @param {Object} template - Template object
349
+ * @param {string} keywordLower - Lowercase keyword
350
+ * @returns {boolean} True if matches
351
+ * @private
352
+ */
353
+ _matchesKeyword(template, keywordLower) {
354
+ // Search in name
355
+ if (template.name.toLowerCase().includes(keywordLower)) {
356
+ return true;
357
+ }
358
+
359
+ // Search in description
360
+ if (template.description.toLowerCase().includes(keywordLower)) {
361
+ return true;
362
+ }
363
+
364
+ // Search in tags
365
+ for (const tag of template.tags || []) {
366
+ if (tag.toLowerCase().includes(keywordLower)) {
367
+ return true;
368
+ }
369
+ }
370
+
371
+ // Search in applicable scenarios
372
+ for (const scenario of template.applicable_scenarios || []) {
373
+ if (scenario.toLowerCase().includes(keywordLower)) {
374
+ return true;
375
+ }
376
+ }
377
+
378
+ return false;
379
+ }
380
+
381
+ /**
382
+ * Filters templates
383
+ *
384
+ * @param {Object} index - Registry index
385
+ * @param {Object} filters - Filters
386
+ * @param {string} filters.category - Filter by category
387
+ * @param {string} filters.difficulty - Filter by difficulty
388
+ * @param {string[]} filters.tags - Filter by tags (any match)
389
+ * @returns {Object[]} Array of matching templates
390
+ */
391
+ filterTemplates(index, filters = {}) {
392
+ let results = [...index.all];
393
+
394
+ // Filter by category
395
+ if (filters.category) {
396
+ results = results.filter(t => t.category === filters.category);
397
+ }
398
+
399
+ // Filter by difficulty
400
+ if (filters.difficulty) {
401
+ results = results.filter(t => t.difficulty === filters.difficulty);
402
+ }
403
+
404
+ // Filter by tags (any match)
405
+ if (filters.tags && filters.tags.length > 0) {
406
+ results = results.filter(t =>
407
+ filters.tags.some(tag => t.tags.includes(tag))
408
+ );
409
+ }
410
+
411
+ return results;
412
+ }
413
+
414
+ /**
415
+ * Sorts templates
416
+ *
417
+ * @param {Object[]} templates - Array of templates
418
+ * @param {string} sortBy - Sort field ('name', 'difficulty', 'created_at', 'updated_at')
419
+ * @param {string} order - Sort order ('asc' or 'desc')
420
+ * @returns {Object[]} Sorted templates
421
+ */
422
+ sortTemplates(templates, sortBy = 'name', order = 'asc') {
423
+ const sorted = [...templates];
424
+
425
+ sorted.sort((a, b) => {
426
+ let aVal, bVal;
427
+
428
+ switch (sortBy) {
429
+ case 'name':
430
+ aVal = a.name.toLowerCase();
431
+ bVal = b.name.toLowerCase();
432
+ break;
433
+
434
+ case 'difficulty':
435
+ const difficultyOrder = { beginner: 1, intermediate: 2, advanced: 3 };
436
+ aVal = difficultyOrder[a.difficulty] || 0;
437
+ bVal = difficultyOrder[b.difficulty] || 0;
438
+ break;
439
+
440
+ case 'created_at':
441
+ aVal = new Date(a.created_at || 0);
442
+ bVal = new Date(b.created_at || 0);
443
+ break;
444
+
445
+ case 'updated_at':
446
+ aVal = new Date(a.updated_at || 0);
447
+ bVal = new Date(b.updated_at || 0);
448
+ break;
449
+
450
+ default:
451
+ aVal = a.name.toLowerCase();
452
+ bVal = b.name.toLowerCase();
453
+ }
454
+
455
+ if (aVal < bVal) return order === 'asc' ? -1 : 1;
456
+ if (aVal > bVal) return order === 'asc' ? 1 : -1;
457
+ return 0;
458
+ });
459
+
460
+ return sorted;
461
+ }
462
+
463
+ /**
464
+ * Groups templates by category
465
+ *
466
+ * @param {Object[]} templates - Array of templates
467
+ * @returns {Object} Templates grouped by category
468
+ */
469
+ groupByCategory(templates) {
470
+ const grouped = {};
471
+
472
+ for (const template of templates) {
473
+ if (!grouped[template.category]) {
474
+ grouped[template.category] = [];
475
+ }
476
+ grouped[template.category].push(template);
477
+ }
478
+
479
+ return grouped;
480
+ }
481
+
482
+ /**
483
+ * Gets template statistics
484
+ *
485
+ * @param {Object} index - Registry index
486
+ * @returns {Object} Statistics
487
+ */
488
+ getStatistics(index) {
489
+ return {
490
+ totalTemplates: index.all.length,
491
+ categories: Object.keys(index.byCategory).length,
492
+ tags: Object.keys(index.byTag).length,
493
+ byCategory: Object.keys(index.byCategory).reduce((acc, cat) => {
494
+ acc[cat] = index.byCategory[cat].length;
495
+ return acc;
496
+ }, {}),
497
+ byDifficulty: Object.keys(index.byDifficulty).reduce((acc, diff) => {
498
+ acc[diff] = index.byDifficulty[diff].length;
499
+ return acc;
500
+ }, {})
501
+ };
502
+ }
503
+ }
504
+
505
+ module.exports = RegistryParser;
@@ -0,0 +1,216 @@
1
+ /**
2
+ * SpecReader - Reads and validates Spec files from .kiro/specs/ directory
3
+ */
4
+
5
+ const fs = require('fs').promises;
6
+ const path = require('path');
7
+ const { execSync } = require('child_process');
8
+
9
+ class SpecReader {
10
+ constructor() {
11
+ this.specsDir = '.kiro/specs';
12
+ }
13
+
14
+ /**
15
+ * Finds a Spec by identifier (number or name)
16
+ * @param {string} identifier - Spec number (e.g., '23-00') or name
17
+ * @returns {Promise<Object>} Spec information
18
+ */
19
+ async findSpec(identifier) {
20
+ const normalizedId = identifier.toLowerCase().trim();
21
+
22
+ try {
23
+ const entries = await fs.readdir(this.specsDir, { withFileTypes: true });
24
+ const specDirs = entries.filter(entry => entry.isDirectory());
25
+
26
+ // Try exact match first
27
+ let matchedDir = specDirs.find(dir => {
28
+ const dirName = dir.name.toLowerCase();
29
+ return dirName === normalizedId ||
30
+ dirName.startsWith(`${normalizedId}-`) ||
31
+ dirName.includes(`-${normalizedId}`);
32
+ });
33
+
34
+ // If no exact match, try partial match
35
+ if (!matchedDir) {
36
+ matchedDir = specDirs.find(dir =>
37
+ dir.name.toLowerCase().includes(normalizedId)
38
+ );
39
+ }
40
+
41
+ if (!matchedDir) {
42
+ throw new Error(`Spec not found: ${identifier}`);
43
+ }
44
+
45
+ const specPath = path.join(this.specsDir, matchedDir.name);
46
+ return {
47
+ name: matchedDir.name,
48
+ path: specPath,
49
+ number: this.extractSpecNumber(matchedDir.name),
50
+ kebabName: this.extractKebabName(matchedDir.name)
51
+ };
52
+ } catch (error) {
53
+ if (error.code === 'ENOENT') {
54
+ throw new Error(`Specs directory not found: ${this.specsDir}`);
55
+ }
56
+ throw error;
57
+ }
58
+ }
59
+
60
+ /**
61
+ * Validates that all required files exist
62
+ * @param {string} specPath - Path to Spec directory
63
+ * @returns {Promise<Object>} Validation result
64
+ */
65
+ async validateSpecStructure(specPath) {
66
+ const requiredFiles = ['requirements.md', 'design.md', 'tasks.md'];
67
+ const missingFiles = [];
68
+ const existingFiles = [];
69
+
70
+ for (const file of requiredFiles) {
71
+ const filePath = path.join(specPath, file);
72
+ try {
73
+ await fs.access(filePath);
74
+ existingFiles.push(file);
75
+ } catch {
76
+ missingFiles.push(file);
77
+ }
78
+ }
79
+
80
+ return {
81
+ valid: missingFiles.length === 0,
82
+ missingFiles,
83
+ existingFiles
84
+ };
85
+ }
86
+
87
+ /**
88
+ * Reads all Spec files
89
+ * @param {string} specPath - Path to Spec directory
90
+ * @returns {Promise<Object>} File contents
91
+ */
92
+ async readSpecFiles(specPath) {
93
+ const files = ['requirements.md', 'design.md', 'tasks.md'];
94
+ const contents = {};
95
+
96
+ for (const file of files) {
97
+ const filePath = path.join(specPath, file);
98
+ try {
99
+ contents[file] = await fs.readFile(filePath, 'utf-8');
100
+ } catch (error) {
101
+ contents[file] = null;
102
+ }
103
+ }
104
+
105
+ return contents;
106
+ }
107
+
108
+ /**
109
+ * Extracts Spec metadata (name, number, dates)
110
+ * @param {string} specPath - Path to Spec directory
111
+ * @param {Object} fileContents - File contents
112
+ * @returns {Object} Extracted metadata
113
+ */
114
+ extractSpecMetadata(specPath, fileContents) {
115
+ const specName = path.basename(specPath);
116
+ const specNumber = this.extractSpecNumber(specName);
117
+ const kebabName = this.extractKebabName(specName);
118
+ const titleName = this.kebabToTitle(kebabName);
119
+
120
+ // Extract dates from file stats
121
+ const dates = this.extractDates(specPath);
122
+
123
+ // Extract author from git config or content
124
+ const author = this.extractAuthor(fileContents);
125
+
126
+ return {
127
+ specNumber,
128
+ specName: kebabName,
129
+ specNameTitle: titleName,
130
+ specPath,
131
+ fullDirName: specName,
132
+ author,
133
+ dates
134
+ };
135
+ }
136
+
137
+ /**
138
+ * Extracts Spec number from directory name
139
+ * @param {string} dirName - Directory name
140
+ * @returns {string} Spec number
141
+ */
142
+ extractSpecNumber(dirName) {
143
+ const match = dirName.match(/^(\d+-\d+)/);
144
+ return match ? match[1] : '';
145
+ }
146
+
147
+ /**
148
+ * Extracts kebab-case name from directory name
149
+ * @param {string} dirName - Directory name
150
+ * @returns {string} Kebab-case name
151
+ */
152
+ extractKebabName(dirName) {
153
+ // Remove spec number prefix
154
+ const withoutNumber = dirName.replace(/^\d+-\d+-/, '');
155
+ return withoutNumber;
156
+ }
157
+
158
+ /**
159
+ * Converts kebab-case to Title Case
160
+ * @param {string} kebabStr - Kebab-case string
161
+ * @returns {string} Title case string
162
+ */
163
+ kebabToTitle(kebabStr) {
164
+ return kebabStr
165
+ .split('-')
166
+ .map(word => word.charAt(0).toUpperCase() + word.slice(1))
167
+ .join(' ');
168
+ }
169
+
170
+ /**
171
+ * Extracts dates from file system
172
+ * @param {string} specPath - Path to Spec directory
173
+ * @returns {Object} Dates
174
+ */
175
+ extractDates(specPath) {
176
+ try {
177
+ const stats = require('fs').statSync(specPath);
178
+ return {
179
+ created: stats.birthtime.toISOString().split('T')[0],
180
+ modified: stats.mtime.toISOString().split('T')[0]
181
+ };
182
+ } catch {
183
+ const today = new Date().toISOString().split('T')[0];
184
+ return {
185
+ created: today,
186
+ modified: today
187
+ };
188
+ }
189
+ }
190
+
191
+ /**
192
+ * Extracts author from git config or content
193
+ * @param {Object} fileContents - File contents
194
+ * @returns {string} Author name
195
+ */
196
+ extractAuthor(fileContents) {
197
+ // Try git config first
198
+ try {
199
+ const gitUser = execSync('git config user.name', { encoding: 'utf-8' }).trim();
200
+ if (gitUser) return gitUser;
201
+ } catch {
202
+ // Git not available or not configured
203
+ }
204
+
205
+ // Try to find author in content
206
+ const allContent = Object.values(fileContents).join('\n');
207
+ const authorMatch = allContent.match(/(?:Author|Created by|Written by):\s*([A-Z][a-z]+(?:\s+[A-Z][a-z]+)*)/);
208
+ if (authorMatch) {
209
+ return authorMatch[1];
210
+ }
211
+
212
+ return 'Unknown';
213
+ }
214
+ }
215
+
216
+ module.exports = SpecReader;