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,782 @@
1
+ /**
2
+ * TemplateManager - Main template management class
3
+ *
4
+ * Coordinates all template operations: sources, discovery, download, caching, and application.
5
+ */
6
+
7
+ const fs = require('fs-extra');
8
+ const path = require('path');
9
+ const os = require('os');
10
+ const GitHandler = require('./git-handler');
11
+ const CacheManager = require('./cache-manager');
12
+ const RegistryParser = require('./registry-parser');
13
+ const TemplateValidator = require('./template-validator');
14
+ const TemplateApplicator = require('./template-applicator');
15
+ const { ValidationError, NetworkError } = require('./template-error');
16
+
17
+ class TemplateManager {
18
+ constructor(options = {}) {
19
+ this.cacheDir = options.cacheDir || path.join(os.homedir(), '.kse', 'templates');
20
+
21
+ // Initialize components
22
+ this.gitHandler = new GitHandler();
23
+ this.cacheManager = new CacheManager(this.cacheDir);
24
+ this.registryParser = new RegistryParser();
25
+ this.validator = new TemplateValidator();
26
+ this.applicator = new TemplateApplicator();
27
+
28
+ // Sources configuration file
29
+ this.sourcesFile = path.join(this.cacheDir, '.sources.json');
30
+
31
+ // Default official source
32
+ this.officialSource = {
33
+ name: 'official',
34
+ type: 'official',
35
+ url: 'https://github.com/heguangyong/scene-capability-engine-templates.git',
36
+ branch: 'main',
37
+ enabled: true
38
+ };
39
+ }
40
+
41
+ /**
42
+ * Gets all configured sources
43
+ *
44
+ * @returns {Promise<Object[]>} Array of source configurations
45
+ */
46
+ async getSources() {
47
+ if (!await fs.pathExists(this.sourcesFile)) {
48
+ // Return default official source
49
+ return [this.officialSource];
50
+ }
51
+
52
+ try {
53
+ const sources = await fs.readJson(this.sourcesFile);
54
+ return sources.sources || [this.officialSource];
55
+ } catch (error) {
56
+ return [this.officialSource];
57
+ }
58
+ }
59
+
60
+ /**
61
+ * Saves sources configuration
62
+ *
63
+ * @param {Object[]} sources - Array of source configurations
64
+ * @returns {Promise<void>}
65
+ */
66
+ async saveSources(sources) {
67
+ await this.cacheManager.ensureCacheDir();
68
+ await fs.writeJson(this.sourcesFile, { sources }, { spaces: 2 });
69
+ }
70
+
71
+ /**
72
+ * Adds a custom template source
73
+ *
74
+ * @param {string} name - Source name
75
+ * @param {string} gitUrl - Git repository URL
76
+ * @param {Object} options - Source options
77
+ * @returns {Promise<Object>} Result
78
+ */
79
+ async addSource(name, gitUrl, options = {}) {
80
+ const { branch = 'main' } = options;
81
+
82
+ // Validate name
83
+ if (!name || !/^[a-z0-9-]+$/.test(name)) {
84
+ throw new ValidationError(
85
+ 'Invalid source name. Use lowercase letters, numbers, and hyphens only.',
86
+ { name }
87
+ );
88
+ }
89
+
90
+ // Check if source already exists
91
+ const sources = await this.getSources();
92
+ if (sources.some(s => s.name === name)) {
93
+ throw new ValidationError(
94
+ 'Source already exists',
95
+ { name }
96
+ );
97
+ }
98
+
99
+ // Add new source
100
+ const newSource = {
101
+ name,
102
+ type: 'custom',
103
+ url: gitUrl,
104
+ branch,
105
+ enabled: true
106
+ };
107
+
108
+ sources.push(newSource);
109
+ await this.saveSources(sources);
110
+
111
+ return {
112
+ success: true,
113
+ source: newSource,
114
+ message: `Source "${name}" added successfully`
115
+ };
116
+ }
117
+
118
+ /**
119
+ * Removes a template source
120
+ *
121
+ * @param {string} name - Source name
122
+ * @param {Object} options - Options
123
+ * @param {boolean} options.clearCache - Clear cached templates
124
+ * @returns {Promise<Object>} Result
125
+ */
126
+ async removeSource(name, options = {}) {
127
+ const { clearCache = false } = options;
128
+
129
+ // Cannot remove official source
130
+ if (name === 'official') {
131
+ throw new ValidationError(
132
+ 'Cannot remove official source',
133
+ { name }
134
+ );
135
+ }
136
+
137
+ const sources = await this.getSources();
138
+ const index = sources.findIndex(s => s.name === name);
139
+
140
+ if (index === -1) {
141
+ throw new ValidationError(
142
+ 'Source not found',
143
+ { name }
144
+ );
145
+ }
146
+
147
+ // Remove source
148
+ sources.splice(index, 1);
149
+ await this.saveSources(sources);
150
+
151
+ // Clear cache if requested
152
+ if (clearCache) {
153
+ await this.cacheManager.clearCache(name);
154
+ }
155
+
156
+ return {
157
+ success: true,
158
+ message: `Source "${name}" removed successfully`
159
+ };
160
+ }
161
+
162
+ /**
163
+ * Lists all template sources
164
+ *
165
+ * @returns {Promise<Object[]>} Array of sources with status
166
+ */
167
+ async listSources() {
168
+ const sources = await this.getSources();
169
+ const result = [];
170
+
171
+ for (const source of sources) {
172
+ const cached = await this.cacheManager.cacheExists(source.name);
173
+ const metadata = await this.cacheManager.getSourceMetadata(source.name);
174
+
175
+ result.push({
176
+ ...source,
177
+ cached,
178
+ lastUpdated: metadata ? metadata.last_updated : null,
179
+ templateCount: metadata ? metadata.template_count : null
180
+ });
181
+ }
182
+
183
+ return result;
184
+ }
185
+
186
+ /**
187
+ * Enables or disables a source
188
+ *
189
+ * @param {string} name - Source name
190
+ * @param {boolean} enabled - Enable or disable
191
+ * @returns {Promise<void>}
192
+ */
193
+ async setSourceEnabled(name, enabled) {
194
+ const sources = await this.getSources();
195
+ const source = sources.find(s => s.name === name);
196
+
197
+ if (!source) {
198
+ throw new ValidationError(
199
+ 'Source not found',
200
+ { name }
201
+ );
202
+ }
203
+
204
+ source.enabled = enabled;
205
+ await this.saveSources(sources);
206
+ }
207
+
208
+ /**
209
+ * Gets a specific source by name
210
+ *
211
+ * @param {string} name - Source name
212
+ * @returns {Promise<Object|null>} Source or null
213
+ */
214
+ async getSource(name) {
215
+ const sources = await this.getSources();
216
+ return sources.find(s => s.name === name) || null;
217
+ }
218
+
219
+ /**
220
+ * Lists all templates from all enabled sources
221
+ *
222
+ * @param {Object} options - List options
223
+ * @param {string} options.category - Filter by category
224
+ * @param {string} options.source - Filter by source
225
+ * @returns {Promise<Object[]>} Array of templates
226
+ */
227
+ async listTemplates(options = {}) {
228
+ const { category = null, source = null } = options;
229
+
230
+ const sources = await this.getSources();
231
+ const enabledSources = sources.filter(s => s.enabled);
232
+
233
+ // Filter by source if specified
234
+ const targetSources = source
235
+ ? enabledSources.filter(s => s.name === source)
236
+ : enabledSources;
237
+
238
+ const allTemplates = [];
239
+
240
+ for (const src of targetSources) {
241
+ // Check if source is cached
242
+ if (!await this.cacheManager.cacheExists(src.name)) {
243
+ continue;
244
+ }
245
+
246
+ // Parse registry
247
+ const registryPath = path.join(
248
+ this.cacheManager.getSourceCachePath(src.name),
249
+ 'template-registry.json'
250
+ );
251
+
252
+ if (!await fs.pathExists(registryPath)) {
253
+ continue;
254
+ }
255
+
256
+ try {
257
+ const registry = await this.registryParser.parseRegistry(registryPath);
258
+ const index = this.registryParser.buildIndex(registry);
259
+
260
+ // Get templates
261
+ let templates = category
262
+ ? this.registryParser.getTemplatesByCategory(index, category)
263
+ : index.all;
264
+
265
+ // Add source information
266
+ templates = templates.map(t => ({
267
+ ...t,
268
+ source: src.name
269
+ }));
270
+
271
+ allTemplates.push(...templates);
272
+ } catch (error) {
273
+ // Skip sources with invalid registries
274
+ continue;
275
+ }
276
+ }
277
+
278
+ return allTemplates;
279
+ }
280
+
281
+ /**
282
+ * Searches templates across all enabled sources
283
+ *
284
+ * @param {string} keyword - Search keyword
285
+ * @param {Object} filters - Search filters
286
+ * @returns {Promise<Object[]>} Array of matching templates
287
+ */
288
+ async searchTemplates(keyword, filters = {}) {
289
+ const sources = await this.getSources();
290
+ const enabledSources = sources.filter(s => s.enabled);
291
+
292
+ const allResults = [];
293
+
294
+ for (const src of enabledSources) {
295
+ if (!await this.cacheManager.cacheExists(src.name)) {
296
+ continue;
297
+ }
298
+
299
+ const registryPath = path.join(
300
+ this.cacheManager.getSourceCachePath(src.name),
301
+ 'template-registry.json'
302
+ );
303
+
304
+ if (!await fs.pathExists(registryPath)) {
305
+ continue;
306
+ }
307
+
308
+ try {
309
+ const registry = await this.registryParser.parseRegistry(registryPath);
310
+ const index = this.registryParser.buildIndex(registry);
311
+
312
+ const results = this.registryParser.searchTemplates(index, keyword, filters);
313
+
314
+ // Add source information
315
+ const resultsWithSource = results.map(t => ({
316
+ ...t,
317
+ source: src.name
318
+ }));
319
+
320
+ allResults.push(...resultsWithSource);
321
+ } catch (error) {
322
+ continue;
323
+ }
324
+ }
325
+
326
+ return allResults;
327
+ }
328
+
329
+ /**
330
+ * Shows detailed information about a template
331
+ *
332
+ * @param {string} templatePath - Template path (e.g., 'web-features/rest-api' or 'official:web-features/rest-api')
333
+ * @returns {Promise<Object>} Template details
334
+ */
335
+ async showTemplate(templatePath) {
336
+ // Parse template path
337
+ const { sourceName, templateId } = this._parseTemplatePath(templatePath);
338
+
339
+ // Get source
340
+ const source = await this.getSource(sourceName);
341
+ if (!source) {
342
+ throw new ValidationError(
343
+ 'Source not found',
344
+ { source: sourceName }
345
+ );
346
+ }
347
+
348
+ // Check cache
349
+ if (!await this.cacheManager.cacheExists(sourceName)) {
350
+ throw new ValidationError(
351
+ 'Source not cached. Run update first.',
352
+ { source: sourceName }
353
+ );
354
+ }
355
+
356
+ // Parse registry
357
+ const registryPath = path.join(
358
+ this.cacheManager.getSourceCachePath(sourceName),
359
+ 'template-registry.json'
360
+ );
361
+
362
+ const registry = await this.registryParser.parseRegistry(registryPath);
363
+ const index = this.registryParser.buildIndex(registry);
364
+
365
+ // Find template
366
+ const template = this.registryParser.getTemplateById(index, templateId);
367
+
368
+ if (!template) {
369
+ throw new ValidationError(
370
+ 'Template not found',
371
+ { templateId, source: sourceName }
372
+ );
373
+ }
374
+
375
+ return {
376
+ ...template,
377
+ source: sourceName
378
+ };
379
+ }
380
+
381
+ /**
382
+ * Gets all categories from all enabled sources
383
+ *
384
+ * @returns {Promise<string[]>} Array of unique categories
385
+ */
386
+ async getCategories() {
387
+ const sources = await this.getSources();
388
+ const enabledSources = sources.filter(s => s.enabled);
389
+
390
+ const allCategories = new Set();
391
+
392
+ for (const src of enabledSources) {
393
+ if (!await this.cacheManager.cacheExists(src.name)) {
394
+ continue;
395
+ }
396
+
397
+ const registryPath = path.join(
398
+ this.cacheManager.getSourceCachePath(src.name),
399
+ 'template-registry.json'
400
+ );
401
+
402
+ if (!await fs.pathExists(registryPath)) {
403
+ continue;
404
+ }
405
+
406
+ try {
407
+ const registry = await this.registryParser.parseRegistry(registryPath);
408
+ const index = this.registryParser.buildIndex(registry);
409
+ const categories = this.registryParser.getCategories(index);
410
+
411
+ categories.forEach(cat => allCategories.add(cat));
412
+ } catch (error) {
413
+ continue;
414
+ }
415
+ }
416
+
417
+ return Array.from(allCategories).sort();
418
+ }
419
+
420
+ /**
421
+ * Parses template path to extract source and template ID
422
+ *
423
+ * @param {string} templatePath - Template path
424
+ * @returns {Object} Parsed path
425
+ * @private
426
+ */
427
+ _parseTemplatePath(templatePath) {
428
+ // Format: 'source:category/template' or 'category/template'
429
+ if (templatePath.includes(':')) {
430
+ const [sourceName, templateId] = templatePath.split(':', 2);
431
+ return { sourceName, templateId };
432
+ } else {
433
+ return { sourceName: 'official', templateId: templatePath };
434
+ }
435
+ }
436
+
437
+ /**
438
+ * Downloads templates from a source
439
+ *
440
+ * @param {string} sourceName - Source name (default: 'official')
441
+ * @returns {Promise<Object>} Download result
442
+ */
443
+ async downloadTemplates(sourceName = 'official') {
444
+ const source = await this.getSource(sourceName);
445
+
446
+ if (!source) {
447
+ throw new ValidationError(
448
+ 'Source not found',
449
+ { source: sourceName }
450
+ );
451
+ }
452
+
453
+ const targetPath = this.cacheManager.getSourceCachePath(sourceName);
454
+
455
+ // Check if already cached
456
+ if (await this.cacheManager.cacheExists(sourceName)) {
457
+ throw new ValidationError(
458
+ 'Templates already cached. Use update to refresh.',
459
+ { source: sourceName }
460
+ );
461
+ }
462
+
463
+ // Clone repository
464
+ try {
465
+ await this.gitHandler.cloneRepository(source.url, targetPath, {
466
+ shallow: true,
467
+ branch: source.branch
468
+ });
469
+ } catch (error) {
470
+ throw new NetworkError(
471
+ 'Failed to download templates',
472
+ {
473
+ source: sourceName,
474
+ url: source.url,
475
+ error: error.message
476
+ }
477
+ );
478
+ }
479
+
480
+ // Validate repository structure
481
+ const validation = await this.gitHandler.validateRepository(targetPath);
482
+ if (!validation.valid) {
483
+ // Clean up invalid download
484
+ await fs.remove(targetPath);
485
+
486
+ throw new ValidationError(
487
+ 'Downloaded repository is invalid',
488
+ {
489
+ source: sourceName,
490
+ errors: validation.errors
491
+ }
492
+ );
493
+ }
494
+
495
+ // Count templates
496
+ const registryPath = path.join(targetPath, 'template-registry.json');
497
+ const registry = await this.registryParser.parseRegistry(registryPath);
498
+ const templateCount = registry.templates.length;
499
+
500
+ // Update cache metadata
501
+ await this.cacheManager.updateMetadata(sourceName, {
502
+ version: await this.gitHandler.getCurrentCommit(targetPath),
503
+ template_count: templateCount,
504
+ size_bytes: await this.cacheManager.getCacheSize(sourceName)
505
+ });
506
+
507
+ return {
508
+ success: true,
509
+ source: sourceName,
510
+ templateCount,
511
+ message: `Downloaded ${templateCount} templates from ${sourceName}`
512
+ };
513
+ }
514
+
515
+ /**
516
+ * Updates templates from a source
517
+ *
518
+ * @param {string} sourceName - Source name (null = all sources)
519
+ * @param {string} version - Specific version to checkout (optional)
520
+ * @returns {Promise<Object>} Update result
521
+ */
522
+ async updateTemplates(sourceName = null, version = null) {
523
+ if (sourceName) {
524
+ return await this._updateSingleSource(sourceName, version);
525
+ } else {
526
+ return await this._updateAllSources();
527
+ }
528
+ }
529
+
530
+ /**
531
+ * Updates a single source
532
+ *
533
+ * @param {string} sourceName - Source name
534
+ * @param {string} version - Specific version (optional)
535
+ * @returns {Promise<Object>} Update result
536
+ * @private
537
+ */
538
+ async _updateSingleSource(sourceName, version = null) {
539
+ const source = await this.getSource(sourceName);
540
+
541
+ if (!source) {
542
+ throw new ValidationError(
543
+ 'Source not found',
544
+ { source: sourceName }
545
+ );
546
+ }
547
+
548
+ const targetPath = this.cacheManager.getSourceCachePath(sourceName);
549
+
550
+ // If not cached, download instead
551
+ if (!await this.cacheManager.cacheExists(sourceName)) {
552
+ return await this.downloadTemplates(sourceName);
553
+ }
554
+
555
+ // Get old template list
556
+ const oldRegistryPath = path.join(targetPath, 'template-registry.json');
557
+ const oldRegistry = await this.registryParser.parseRegistry(oldRegistryPath);
558
+ const oldTemplates = new Map(
559
+ oldRegistry.templates.map(t => [t.id, t])
560
+ );
561
+
562
+ // Pull updates
563
+ try {
564
+ await this.gitHandler.pullUpdates(targetPath);
565
+
566
+ // Checkout specific version if requested
567
+ if (version) {
568
+ await this.gitHandler.checkoutVersion(targetPath, version);
569
+ }
570
+ } catch (error) {
571
+ throw new NetworkError(
572
+ 'Failed to update templates',
573
+ {
574
+ source: sourceName,
575
+ error: error.message
576
+ }
577
+ );
578
+ }
579
+
580
+ // Get new template list
581
+ const newRegistry = await this.registryParser.parseRegistry(oldRegistryPath);
582
+ const newTemplates = new Map(
583
+ newRegistry.templates.map(t => [t.id, t])
584
+ );
585
+
586
+ // Detect changes
587
+ const changes = {
588
+ added: 0,
589
+ modified: 0,
590
+ deleted: 0,
591
+ details: {
592
+ added: [],
593
+ modified: [],
594
+ deleted: []
595
+ }
596
+ };
597
+
598
+ // Find added and modified templates
599
+ for (const [id, newTemplate] of newTemplates) {
600
+ if (!oldTemplates.has(id)) {
601
+ changes.added++;
602
+ changes.details.added.push(id);
603
+ } else {
604
+ const oldTemplate = oldTemplates.get(id);
605
+ // Check if template was modified (compare updated_at or version)
606
+ if (oldTemplate.updated_at !== newTemplate.updated_at ||
607
+ oldTemplate.version !== newTemplate.version) {
608
+ changes.modified++;
609
+ changes.details.modified.push(id);
610
+ }
611
+ }
612
+ }
613
+
614
+ // Find deleted templates
615
+ for (const [id] of oldTemplates) {
616
+ if (!newTemplates.has(id)) {
617
+ changes.deleted++;
618
+ changes.details.deleted.push(id);
619
+ }
620
+ }
621
+
622
+ // Update cache metadata
623
+ await this.cacheManager.updateMetadata(sourceName, {
624
+ version: await this.gitHandler.getCurrentCommit(targetPath),
625
+ template_count: newTemplates.size,
626
+ size_bytes: await this.cacheManager.getCacheSize(sourceName)
627
+ });
628
+
629
+ return {
630
+ success: true,
631
+ source: sourceName,
632
+ changes,
633
+ message: `Updated ${sourceName}: ${changes.added} added, ${changes.modified} modified, ${changes.deleted} deleted`
634
+ };
635
+ }
636
+
637
+ /**
638
+ * Updates all enabled sources
639
+ *
640
+ * @returns {Promise<Object>} Update result
641
+ * @private
642
+ */
643
+ async _updateAllSources() {
644
+ const sources = await this.getSources();
645
+ const enabledSources = sources.filter(s => s.enabled);
646
+
647
+ const results = [];
648
+ const errors = [];
649
+
650
+ for (const source of enabledSources) {
651
+ try {
652
+ const result = await this._updateSingleSource(source.name);
653
+ results.push(result);
654
+ } catch (error) {
655
+ errors.push({
656
+ source: source.name,
657
+ error: error.message
658
+ });
659
+ }
660
+ }
661
+
662
+ return {
663
+ success: errors.length === 0,
664
+ results,
665
+ errors,
666
+ message: `Updated ${results.length} source(s), ${errors.length} error(s)`
667
+ };
668
+ }
669
+
670
+ /**
671
+ * Ensures templates are cached (downloads if needed)
672
+ *
673
+ * @param {string} sourceName - Source name
674
+ * @returns {Promise<void>}
675
+ */
676
+ async ensureCached(sourceName) {
677
+ if (!await this.cacheManager.cacheExists(sourceName)) {
678
+ await this.downloadTemplates(sourceName);
679
+ }
680
+ }
681
+
682
+ /**
683
+ * Applies a template to create a new Spec
684
+ *
685
+ * @param {string} specName - Spec name
686
+ * @param {string} templatePath - Template path
687
+ * @param {Object} options - Application options
688
+ * @returns {Promise<Object>} Application result
689
+ */
690
+ async applyTemplate(specName, templatePath, options = {}) {
691
+ const { force = false, variables = {} } = options;
692
+
693
+ // Parse template path
694
+ const { sourceName, templateId } = this._parseTemplatePath(templatePath);
695
+
696
+ // Ensure source is cached
697
+ await this.ensureCached(sourceName);
698
+
699
+ // Get template details
700
+ const template = await this.showTemplate(templatePath);
701
+
702
+ // Get template directory path
703
+ const sourcePath = this.cacheManager.getSourceCachePath(sourceName);
704
+ const templateDir = path.join(sourcePath, templateId);
705
+
706
+ // Validate template
707
+ const validation = await this.validator.validateTemplate(templateDir);
708
+ if (!validation.valid) {
709
+ throw new ValidationError(
710
+ 'Template validation failed',
711
+ {
712
+ template: templateId,
713
+ errors: validation.errors
714
+ }
715
+ );
716
+ }
717
+
718
+ // Determine target directory
719
+ const targetDir = path.join(process.cwd(), '.kiro', 'specs', specName);
720
+
721
+ // Apply template
722
+ const result = await this.applicator.applyTemplate(
723
+ specName,
724
+ templateDir,
725
+ targetDir,
726
+ { force, variables }
727
+ );
728
+
729
+ return {
730
+ ...result,
731
+ template: template.name,
732
+ source: sourceName,
733
+ specPath: targetDir
734
+ };
735
+ }
736
+
737
+ /**
738
+ * Gets cache status
739
+ *
740
+ * @returns {Promise<Object>} Cache status
741
+ */
742
+ async getCacheStatus() {
743
+ const stats = await this.cacheManager.getStatistics();
744
+ const sources = await this.listSources();
745
+
746
+ return {
747
+ ...stats,
748
+ sources: sources.map(s => ({
749
+ name: s.name,
750
+ type: s.type,
751
+ enabled: s.enabled,
752
+ cached: s.cached,
753
+ lastUpdated: s.lastUpdated,
754
+ templateCount: s.templateCount
755
+ }))
756
+ };
757
+ }
758
+
759
+ /**
760
+ * Clears cache
761
+ *
762
+ * @param {string} sourceName - Source name (null = all)
763
+ * @returns {Promise<Object>} Result
764
+ */
765
+ async clearCache(sourceName = null) {
766
+ if (sourceName) {
767
+ await this.cacheManager.clearCache(sourceName);
768
+ return {
769
+ success: true,
770
+ message: `Cache cleared for ${sourceName}`
771
+ };
772
+ } else {
773
+ await this.cacheManager.clearAllCache();
774
+ return {
775
+ success: true,
776
+ message: 'All cache cleared'
777
+ };
778
+ }
779
+ }
780
+ }
781
+
782
+ module.exports = TemplateManager;