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,514 @@
1
+ const fs = require('fs').promises;
2
+ const path = require('path');
3
+ const GitOperations = require('./git-operations');
4
+ const PathResolver = require('./path-resolver');
5
+ const RepoError = require('./errors/repo-error');
6
+
7
+ /**
8
+ * RepoManager - Orchestrates operations across multiple repositories
9
+ *
10
+ * Provides high-level operations for repository discovery, status checking,
11
+ * command execution, and health diagnostics across multiple Git repositories.
12
+ */
13
+ class RepoManager {
14
+ /**
15
+ * Create a new RepoManager
16
+ * @param {string} projectRoot - The project root directory
17
+ */
18
+ constructor(projectRoot) {
19
+ if (!projectRoot) {
20
+ throw new Error('Project root is required');
21
+ }
22
+ this.projectRoot = projectRoot;
23
+ this.gitOps = new GitOperations();
24
+ this.pathResolver = new PathResolver();
25
+ }
26
+
27
+ /**
28
+ * Scan directory for Git repositories
29
+ * @param {string} rootPath - Root path to scan
30
+ * @param {Object} options - Scan options
31
+ * @param {number} options.maxDepth - Maximum depth to scan (default: 3)
32
+ * @param {string[]} options.exclude - Directories to exclude (default: ['.kiro'])
33
+ * @param {boolean} options.nested - Enable nested repository scanning (default: true)
34
+ * @returns {Promise<Array<{path: string, name: string, remote: string|null, branch: string, hasRemote: boolean, parent: string|null}>>}
35
+ */
36
+ async discoverRepositories(rootPath, options = {}) {
37
+ const maxDepth = options.maxDepth || 3;
38
+ const exclude = options.exclude || ['.kiro'];
39
+ const nested = options.nested !== false; // Default to true
40
+
41
+ const discovered = [];
42
+ const visitedPaths = new Set();
43
+
44
+ try {
45
+ await this._scanDirectory(rootPath, rootPath, 0, maxDepth, exclude, discovered, nested, null, visitedPaths);
46
+ } catch (error) {
47
+ throw new RepoError(
48
+ `Failed to scan directory: ${error.message}`,
49
+ null,
50
+ { rootPath, error: error.message }
51
+ );
52
+ }
53
+
54
+ return discovered;
55
+ }
56
+
57
+ /**
58
+ * Check if directory should be skipped during scanning
59
+ * @private
60
+ * @param {string} dirName - Directory name
61
+ * @param {string[]} exclude - User-specified exclusions
62
+ * @returns {boolean} True if directory should be skipped
63
+ */
64
+ _shouldSkipDirectory(dirName, exclude) {
65
+ // Common non-repository directories to skip
66
+ const commonExclusions = [
67
+ 'node_modules',
68
+ '.git',
69
+ 'build',
70
+ 'dist',
71
+ 'target',
72
+ 'out',
73
+ '.next',
74
+ '.nuxt',
75
+ 'vendor',
76
+ '.idea',
77
+ '.vscode',
78
+ '__pycache__',
79
+ '.pytest_cache',
80
+ 'coverage'
81
+ ];
82
+
83
+ // Skip if in common exclusions
84
+ if (commonExclusions.includes(dirName)) {
85
+ return true;
86
+ }
87
+
88
+ // Skip if in user-specified exclusions
89
+ if (exclude.includes(dirName)) {
90
+ return true;
91
+ }
92
+
93
+ return false;
94
+ }
95
+
96
+ /**
97
+ * Recursively scan directory for Git repositories
98
+ * @private
99
+ * @param {string} currentPath - Current directory path
100
+ * @param {string} rootPath - Root path for relative path calculation
101
+ * @param {number} depth - Current depth
102
+ * @param {number} maxDepth - Maximum depth
103
+ * @param {string[]} exclude - Directories to exclude
104
+ * @param {Array} discovered - Array to store discovered repositories
105
+ * @param {boolean} nested - Enable nested repository scanning
106
+ * @param {string|null} parentPath - Parent repository path (for nested repos)
107
+ * @param {Set<string>} visitedPaths - Set of visited paths (for symlink detection)
108
+ */
109
+ async _scanDirectory(currentPath, rootPath, depth, maxDepth, exclude, discovered, nested, parentPath, visitedPaths) {
110
+ // Check depth limit
111
+ if (depth > maxDepth) {
112
+ return;
113
+ }
114
+
115
+ // Resolve symlinks and check for circular references
116
+ try {
117
+ const realPath = await fs.realpath(currentPath);
118
+ if (visitedPaths.has(realPath)) {
119
+ // Skip circular symlink
120
+ return;
121
+ }
122
+ visitedPaths.add(realPath);
123
+ } catch (error) {
124
+ // If realpath fails (e.g., in tests with mock fs), use currentPath
125
+ if (visitedPaths.has(currentPath)) {
126
+ return;
127
+ }
128
+ visitedPaths.add(currentPath);
129
+ }
130
+
131
+ // Check if current directory should be excluded
132
+ const dirName = path.basename(currentPath);
133
+ if (this._shouldSkipDirectory(dirName, exclude)) {
134
+ return;
135
+ }
136
+
137
+ // Check if current directory is a Git repository
138
+ const isRepo = await this.gitOps.isGitRepo(currentPath);
139
+
140
+ if (isRepo) {
141
+ // Extract repository information
142
+ try {
143
+ const branch = await this.gitOps.getCurrentBranch(currentPath);
144
+ const remotes = await this.gitOps.getRemotes(currentPath);
145
+
146
+ // Select remote: prefer 'origin', otherwise use first available
147
+ let remoteUrl = null;
148
+ let hasRemote = false;
149
+
150
+ if (remotes.length > 0) {
151
+ hasRemote = true;
152
+ const originRemote = remotes.find(r => r.name === 'origin');
153
+ if (originRemote) {
154
+ remoteUrl = originRemote.refs.fetch;
155
+ } else {
156
+ remoteUrl = remotes[0].refs.fetch;
157
+ }
158
+ }
159
+
160
+ // Generate repository name from path
161
+ const relativePath = this.pathResolver.toRelative(currentPath, rootPath);
162
+ // Handle empty path (root directory) or '.' (current directory)
163
+ const normalizedPath = (!relativePath || relativePath === '.') ? '.' : relativePath;
164
+ const repoName = normalizedPath === '.' ? path.basename(rootPath) : normalizedPath.replace(/\//g, '-');
165
+
166
+ discovered.push({
167
+ path: normalizedPath,
168
+ name: repoName,
169
+ remote: remoteUrl,
170
+ branch: branch,
171
+ hasRemote: hasRemote,
172
+ parent: parentPath // Track parent repository
173
+ });
174
+
175
+ // If nested scanning is disabled, don't scan subdirectories
176
+ if (!nested) {
177
+ return;
178
+ }
179
+
180
+ // If nested scanning is enabled, continue scanning subdirectories
181
+ // Update parentPath to current repository for nested repos
182
+ parentPath = relativePath;
183
+ } catch (error) {
184
+ // If we can't get repo info, skip this directory
185
+ return;
186
+ }
187
+ }
188
+
189
+ // Scan subdirectories
190
+ try {
191
+ const entries = await fs.readdir(currentPath, { withFileTypes: true });
192
+
193
+ for (const entry of entries) {
194
+ if (entry.isDirectory()) {
195
+ const subPath = path.join(currentPath, entry.name);
196
+ await this._scanDirectory(subPath, rootPath, depth + 1, maxDepth, exclude, discovered, nested, parentPath, visitedPaths);
197
+ }
198
+ }
199
+ } catch (error) {
200
+ // If we can't read the directory, skip it
201
+ return;
202
+ }
203
+ }
204
+
205
+ /**
206
+ * Get status for a single repository
207
+ * @param {Object} repo - Repository configuration
208
+ * @param {string} repo.name - Repository name
209
+ * @param {string} repo.path - Repository path
210
+ * @returns {Promise<Object>} Repository status
211
+ */
212
+ async getRepoStatus(repo) {
213
+ const repoPath = this.pathResolver.resolvePath(repo.path, this.projectRoot);
214
+
215
+ try {
216
+ // Check if path exists
217
+ try {
218
+ await fs.access(repoPath);
219
+ } catch (error) {
220
+ return {
221
+ name: repo.name,
222
+ path: repo.path,
223
+ branch: null,
224
+ isClean: false,
225
+ modified: 0,
226
+ added: 0,
227
+ deleted: 0,
228
+ ahead: 0,
229
+ behind: 0,
230
+ error: 'Path not found or inaccessible'
231
+ };
232
+ }
233
+
234
+ // Check if it's a Git repository
235
+ const isRepo = await this.gitOps.isGitRepo(repoPath);
236
+ if (!isRepo) {
237
+ return {
238
+ name: repo.name,
239
+ path: repo.path,
240
+ branch: null,
241
+ isClean: false,
242
+ modified: 0,
243
+ added: 0,
244
+ deleted: 0,
245
+ ahead: 0,
246
+ behind: 0,
247
+ error: 'Not a Git repository'
248
+ };
249
+ }
250
+
251
+ // Get status
252
+ const status = await this.gitOps.getStatus(repoPath);
253
+
254
+ return {
255
+ name: repo.name,
256
+ path: repo.path,
257
+ branch: status.current,
258
+ isClean: status.isClean(),
259
+ modified: status.modified.length,
260
+ added: status.created.length,
261
+ deleted: status.deleted.length,
262
+ ahead: status.ahead,
263
+ behind: status.behind,
264
+ error: null
265
+ };
266
+ } catch (error) {
267
+ return {
268
+ name: repo.name,
269
+ path: repo.path,
270
+ branch: null,
271
+ isClean: false,
272
+ modified: 0,
273
+ added: 0,
274
+ deleted: 0,
275
+ ahead: 0,
276
+ behind: 0,
277
+ error: error.message
278
+ };
279
+ }
280
+ }
281
+
282
+ /**
283
+ * Get status for all repositories
284
+ * @param {Array<Object>} repos - Array of repository configurations
285
+ * @returns {Promise<Array<Object>>} Array of repository statuses
286
+ */
287
+ async getAllRepoStatuses(repos) {
288
+ const statuses = [];
289
+
290
+ for (const repo of repos) {
291
+ const status = await this.getRepoStatus(repo);
292
+ statuses.push(status);
293
+ }
294
+
295
+ return statuses;
296
+ }
297
+
298
+ /**
299
+ * Execute command in a repository
300
+ * @param {Object} repo - Repository configuration
301
+ * @param {string} command - Git command to execute (without 'git' prefix)
302
+ * @returns {Promise<Object>} Execution result
303
+ */
304
+ async execInRepo(repo, command) {
305
+ const repoPath = this.pathResolver.resolvePath(repo.path, this.projectRoot);
306
+
307
+ try {
308
+ // Check if path exists and is a Git repository
309
+ try {
310
+ await fs.access(repoPath);
311
+ } catch (error) {
312
+ return {
313
+ name: repo.name,
314
+ path: repo.path,
315
+ command: command,
316
+ success: false,
317
+ output: '',
318
+ error: 'Path not found or inaccessible',
319
+ exitCode: 1
320
+ };
321
+ }
322
+
323
+ const isRepo = await this.gitOps.isGitRepo(repoPath);
324
+ if (!isRepo) {
325
+ return {
326
+ name: repo.name,
327
+ path: repo.path,
328
+ command: command,
329
+ success: false,
330
+ output: '',
331
+ error: 'Not a Git repository',
332
+ exitCode: 1
333
+ };
334
+ }
335
+
336
+ // Parse command into arguments
337
+ // Check if command already starts with "git" to avoid duplication
338
+ const trimmedCommand = command.trim();
339
+ const args = trimmedCommand.startsWith('git ')
340
+ ? trimmedCommand.substring(4).trim().split(/\s+/)
341
+ : trimmedCommand.split(/\s+/);
342
+
343
+ // Execute command
344
+ const output = await this.gitOps.execRaw(repoPath, args);
345
+
346
+ return {
347
+ name: repo.name,
348
+ path: repo.path,
349
+ command: command,
350
+ success: true,
351
+ output: output.trim(),
352
+ error: null,
353
+ exitCode: 0
354
+ };
355
+ } catch (error) {
356
+ return {
357
+ name: repo.name,
358
+ path: repo.path,
359
+ command: command,
360
+ success: false,
361
+ output: '',
362
+ error: error.message,
363
+ exitCode: error.exitCode || 1
364
+ };
365
+ }
366
+ }
367
+
368
+ /**
369
+ * Execute command in all repositories
370
+ * @param {Array<Object>} repos - Array of repository configurations
371
+ * @param {string} command - Git command to execute
372
+ * @returns {Promise<Array<Object>>} Array of execution results
373
+ */
374
+ async execInAllRepos(repos, command) {
375
+ const results = [];
376
+
377
+ for (const repo of repos) {
378
+ const result = await this.execInRepo(repo, command);
379
+ results.push(result);
380
+ }
381
+
382
+ return results;
383
+ }
384
+
385
+ /**
386
+ * Perform health check on a repository
387
+ * @param {Object} repo - Repository configuration
388
+ * @param {string} repo.name - Repository name
389
+ * @param {string} repo.path - Repository path
390
+ * @param {string} [repo.remote] - Remote URL
391
+ * @param {string} [repo.defaultBranch] - Default branch name
392
+ * @returns {Promise<Object>} Health check result
393
+ */
394
+ async checkRepoHealth(repo) {
395
+ const repoPath = this.pathResolver.resolvePath(repo.path, this.projectRoot);
396
+ const checks = {
397
+ pathExists: false,
398
+ isGitRepo: false,
399
+ remoteReachable: null,
400
+ branchExists: null
401
+ };
402
+ const errors = [];
403
+ const warnings = [];
404
+
405
+ // Check 1: Path exists
406
+ try {
407
+ await fs.access(repoPath);
408
+ checks.pathExists = true;
409
+ } catch (error) {
410
+ errors.push(`Path does not exist or is not accessible: ${repo.path}`);
411
+ return {
412
+ name: repo.name,
413
+ path: repo.path,
414
+ checks,
415
+ errors,
416
+ warnings,
417
+ healthy: false
418
+ };
419
+ }
420
+
421
+ // Check 2: Is Git repository
422
+ try {
423
+ checks.isGitRepo = await this.gitOps.isGitRepo(repoPath);
424
+ if (!checks.isGitRepo) {
425
+ errors.push(`Path is not a valid Git repository: ${repo.path}`);
426
+ return {
427
+ name: repo.name,
428
+ path: repo.path,
429
+ checks,
430
+ errors,
431
+ warnings,
432
+ healthy: false
433
+ };
434
+ }
435
+ } catch (error) {
436
+ errors.push(`Failed to verify Git repository: ${error.message}`);
437
+ return {
438
+ name: repo.name,
439
+ path: repo.path,
440
+ checks,
441
+ errors,
442
+ warnings,
443
+ healthy: false
444
+ };
445
+ }
446
+
447
+ // Check 3: Remote reachable (if remote is configured)
448
+ if (repo.remote) {
449
+ try {
450
+ const remotes = await this.gitOps.getRemotes(repoPath);
451
+ const hasOrigin = remotes.some(r => r.name === 'origin');
452
+
453
+ if (hasOrigin) {
454
+ checks.remoteReachable = await this.gitOps.isRemoteReachable(repoPath, 'origin');
455
+ if (!checks.remoteReachable) {
456
+ warnings.push('Remote "origin" is not reachable (network issue or invalid URL)');
457
+ }
458
+ } else {
459
+ checks.remoteReachable = false;
460
+ warnings.push('Remote "origin" is not configured');
461
+ }
462
+ } catch (error) {
463
+ checks.remoteReachable = false;
464
+ warnings.push(`Failed to check remote: ${error.message}`);
465
+ }
466
+ }
467
+
468
+ // Check 4: Default branch exists (if configured)
469
+ if (repo.defaultBranch) {
470
+ try {
471
+ const currentBranch = await this.gitOps.getCurrentBranch(repoPath);
472
+ checks.branchExists = currentBranch === repo.defaultBranch;
473
+
474
+ if (!checks.branchExists) {
475
+ warnings.push(
476
+ `Current branch "${currentBranch}" differs from configured default branch "${repo.defaultBranch}"`
477
+ );
478
+ }
479
+ } catch (error) {
480
+ checks.branchExists = false;
481
+ warnings.push(`Failed to check branch: ${error.message}`);
482
+ }
483
+ }
484
+
485
+ const healthy = errors.length === 0;
486
+
487
+ return {
488
+ name: repo.name,
489
+ path: repo.path,
490
+ checks,
491
+ errors,
492
+ warnings,
493
+ healthy
494
+ };
495
+ }
496
+
497
+ /**
498
+ * Perform health check on all repositories
499
+ * @param {Array<Object>} repos - Array of repository configurations
500
+ * @returns {Promise<Array<Object>>} Array of health check results
501
+ */
502
+ async checkAllReposHealth(repos) {
503
+ const results = [];
504
+
505
+ for (const repo of repos) {
506
+ const result = await this.checkRepoHealth(repo);
507
+ results.push(result);
508
+ }
509
+
510
+ return results;
511
+ }
512
+ }
513
+
514
+ module.exports = RepoManager;
@@ -0,0 +1,59 @@
1
+ const fs = require('fs-extra');
2
+ const path = require('path');
3
+ const crypto = require('crypto');
4
+
5
+ function createId(prefix) {
6
+ return `${prefix}-${Date.now()}-${Math.random().toString(36).slice(2, 10)}`;
7
+ }
8
+
9
+ class AuditEmitter {
10
+ constructor(projectRoot = process.cwd(), options = {}) {
11
+ this.projectRoot = projectRoot;
12
+ this.auditFile = options.auditFile || path.join(projectRoot, '.kiro', 'audit', 'scene-runtime-events.jsonl');
13
+ }
14
+
15
+ async emit(eventType, payload = {}) {
16
+ const event = {
17
+ event_id: createId('srevt'),
18
+ event_type: eventType,
19
+ timestamp: new Date().toISOString(),
20
+ trace_id: payload.trace_id || payload.traceId || createId('trace'),
21
+ scene_ref: payload.scene_ref || payload.sceneRef || null,
22
+ scene_version: payload.scene_version || payload.sceneVersion || null,
23
+ run_mode: payload.run_mode || payload.runMode || null,
24
+ actor: payload.actor || 'kse.scene-runtime',
25
+ payload: payload.payload || payload
26
+ };
27
+
28
+ event.checksum = this.calculateChecksum(event);
29
+
30
+ await fs.ensureDir(path.dirname(this.auditFile));
31
+ await fs.appendFile(this.auditFile, `${JSON.stringify(event)}\n`, 'utf8');
32
+
33
+ return event;
34
+ }
35
+
36
+ calculateChecksum(event) {
37
+ const { checksum, ...rest } = event;
38
+ return crypto.createHash('sha256').update(JSON.stringify(rest)).digest('hex');
39
+ }
40
+
41
+ verifyEvent(event) {
42
+ return this.calculateChecksum(event) === event.checksum;
43
+ }
44
+
45
+ async readAll() {
46
+ if (!await fs.pathExists(this.auditFile)) {
47
+ return [];
48
+ }
49
+
50
+ const content = await fs.readFile(this.auditFile, 'utf8');
51
+ return content
52
+ .split('\n')
53
+ .map((line) => line.trim())
54
+ .filter(Boolean)
55
+ .map((line) => JSON.parse(line));
56
+ }
57
+ }
58
+
59
+ module.exports = AuditEmitter;