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,372 @@
1
+ /**
2
+ * Backup System
3
+ *
4
+ * Creates, manages, and restores backups of the .kiro/ directory.
5
+ * Provides rollback capability for safe adoption and upgrade operations.
6
+ */
7
+
8
+ const path = require('path');
9
+ const fs = require('fs-extra');
10
+ const {
11
+ pathExists,
12
+ copyDirectory,
13
+ ensureDirectory,
14
+ listFiles,
15
+ listFilesRecursive,
16
+ getDirectorySize,
17
+ readJSON,
18
+ writeJSON,
19
+ remove
20
+ } = require('../utils/fs-utils');
21
+
22
+ class BackupSystem {
23
+ constructor() {
24
+ this.backupDirName = 'backups';
25
+ }
26
+
27
+ /**
28
+ * Gets the path to the backups directory
29
+ *
30
+ * @param {string} projectPath - Absolute path to project root
31
+ * @returns {string} - Absolute path to backups directory
32
+ */
33
+ getBackupDir(projectPath) {
34
+ return path.join(projectPath, '.kiro', this.backupDirName);
35
+ }
36
+
37
+ /**
38
+ * Gets the path to the .kiro directory
39
+ *
40
+ * @param {string} projectPath - Absolute path to project root
41
+ * @returns {string} - Absolute path to .kiro directory
42
+ */
43
+ getKiroDir(projectPath) {
44
+ return path.join(projectPath, '.kiro');
45
+ }
46
+
47
+ /**
48
+ * Generates a backup ID based on type and timestamp
49
+ *
50
+ * @param {string} type - Backup type (adopt, upgrade, pre-rollback)
51
+ * @returns {string} - Backup ID (e.g., "adopt-2026-01-23-100000")
52
+ */
53
+ generateBackupId(type) {
54
+ const now = new Date();
55
+ const year = now.getFullYear();
56
+ const month = String(now.getMonth() + 1).padStart(2, '0');
57
+ const day = String(now.getDate()).padStart(2, '0');
58
+ const hours = String(now.getHours()).padStart(2, '0');
59
+ const minutes = String(now.getMinutes()).padStart(2, '0');
60
+ const seconds = String(now.getSeconds()).padStart(2, '0');
61
+
62
+ return `${type}-${year}-${month}-${day}-${hours}${minutes}${seconds}`;
63
+ }
64
+
65
+ /**
66
+ * Creates backup of .kiro/ directory
67
+ *
68
+ * @param {string} projectPath - Absolute path to project root
69
+ * @param {Object} options - Backup options
70
+ * @param {string} options.type - Backup type (adopt, upgrade, pre-rollback)
71
+ * @returns {Promise<BackupInfo>}
72
+ */
73
+ async createBackup(projectPath, options = {}) {
74
+ const { type = 'manual' } = options;
75
+
76
+ try {
77
+ const kiroDir = this.getKiroDir(projectPath);
78
+
79
+ // Check if .kiro/ exists
80
+ const kiroExists = await pathExists(kiroDir);
81
+ if (!kiroExists) {
82
+ throw new Error('.kiro/ directory does not exist');
83
+ }
84
+
85
+ // Create backups directory if it doesn't exist
86
+ const backupDir = this.getBackupDir(projectPath);
87
+ await ensureDirectory(backupDir);
88
+
89
+ // Generate backup ID
90
+ const backupId = this.generateBackupId(type);
91
+ const backupPath = path.join(backupDir, backupId);
92
+
93
+ // Check if backup already exists (shouldn't happen with timestamp)
94
+ const backupExists = await pathExists(backupPath);
95
+ if (backupExists) {
96
+ throw new Error(`Backup already exists: ${backupId}`);
97
+ }
98
+
99
+ // Create backup directory
100
+ await ensureDirectory(backupPath);
101
+
102
+ // Copy .kiro/ contents to backup (excluding backups/ itself)
103
+ const items = await listFiles(kiroDir);
104
+
105
+ for (const item of items) {
106
+ // Skip the backups directory itself
107
+ if (item === this.backupDirName) {
108
+ continue;
109
+ }
110
+
111
+ const sourcePath = path.join(kiroDir, item);
112
+ const destPath = path.join(backupPath, item);
113
+
114
+ await copyDirectory(sourcePath, destPath, { overwrite: false });
115
+ }
116
+
117
+ // Get backup metadata
118
+ const files = await listFilesRecursive(backupPath);
119
+ const size = await getDirectorySize(backupPath);
120
+
121
+ // Read version from backup if it exists
122
+ const versionPath = path.join(backupPath, 'version.json');
123
+ let version = 'unknown';
124
+ try {
125
+ const versionExists = await pathExists(versionPath);
126
+ if (versionExists) {
127
+ const versionInfo = await readJSON(versionPath);
128
+ version = versionInfo['kse-version'] || 'unknown';
129
+ }
130
+ } catch (error) {
131
+ // Ignore version read errors
132
+ }
133
+
134
+ // Create metadata file
135
+ const metadata = {
136
+ id: backupId,
137
+ type,
138
+ created: new Date().toISOString(),
139
+ version,
140
+ size,
141
+ files: files.length
142
+ };
143
+
144
+ const metadataPath = path.join(backupPath, 'metadata.json');
145
+ await writeJSON(metadataPath, metadata, { spaces: 2 });
146
+
147
+ // Return backup info
148
+ return {
149
+ id: backupId,
150
+ type,
151
+ created: metadata.created,
152
+ version,
153
+ size,
154
+ files: files.length,
155
+ path: backupPath
156
+ };
157
+ } catch (error) {
158
+ throw new Error(`Failed to create backup: ${error.message}`);
159
+ }
160
+ }
161
+
162
+ /**
163
+ * Lists available backups
164
+ *
165
+ * @param {string} projectPath - Absolute path to project root
166
+ * @returns {Promise<BackupInfo[]>} - Array of backup info, sorted by date (newest first)
167
+ */
168
+ async listBackups(projectPath) {
169
+ try {
170
+ const backupDir = this.getBackupDir(projectPath);
171
+
172
+ // Check if backups directory exists
173
+ const backupDirExists = await pathExists(backupDir);
174
+ if (!backupDirExists) {
175
+ return [];
176
+ }
177
+
178
+ // List backup directories
179
+ const items = await listFiles(backupDir);
180
+ const backups = [];
181
+
182
+ for (const item of items) {
183
+ const backupPath = path.join(backupDir, item);
184
+ const metadataPath = path.join(backupPath, 'metadata.json');
185
+
186
+ try {
187
+ // Check if metadata exists
188
+ const metadataExists = await pathExists(metadataPath);
189
+ if (!metadataExists) {
190
+ continue;
191
+ }
192
+
193
+ // Read metadata
194
+ const metadata = await readJSON(metadataPath);
195
+
196
+ backups.push({
197
+ id: metadata.id,
198
+ type: metadata.type,
199
+ created: metadata.created,
200
+ version: metadata.version,
201
+ size: metadata.size,
202
+ files: metadata.files,
203
+ path: backupPath
204
+ });
205
+ } catch (error) {
206
+ // Skip backups with invalid metadata
207
+ continue;
208
+ }
209
+ }
210
+
211
+ // Sort by created date (newest first)
212
+ backups.sort((a, b) => {
213
+ return new Date(b.created) - new Date(a.created);
214
+ });
215
+
216
+ return backups;
217
+ } catch (error) {
218
+ throw new Error(`Failed to list backups: ${error.message}`);
219
+ }
220
+ }
221
+
222
+ /**
223
+ * Restores from backup
224
+ *
225
+ * @param {string} projectPath - Absolute path to project root
226
+ * @param {string} backupId - Backup ID to restore from
227
+ * @returns {Promise<RestoreResult>}
228
+ */
229
+ async restore(projectPath, backupId) {
230
+ try {
231
+ const backupDir = this.getBackupDir(projectPath);
232
+ const backupPath = path.join(backupDir, backupId);
233
+
234
+ // Check if backup exists
235
+ const backupExists = await pathExists(backupPath);
236
+ if (!backupExists) {
237
+ throw new Error(`Backup not found: ${backupId}`);
238
+ }
239
+
240
+ // Validate backup before restoring
241
+ const isValid = await this.validateBackup(backupPath);
242
+ if (!isValid) {
243
+ throw new Error(`Backup validation failed: ${backupId}`);
244
+ }
245
+
246
+ const kiroDir = this.getKiroDir(projectPath);
247
+
248
+ // Get list of items to restore (excluding metadata.json)
249
+ const items = await listFiles(backupPath);
250
+ const itemsToRestore = items.filter(item => item !== 'metadata.json');
251
+
252
+ // Remove existing .kiro/ contents (except backups/)
253
+ const existingItems = await listFiles(kiroDir);
254
+ for (const item of existingItems) {
255
+ if (item === this.backupDirName) {
256
+ continue;
257
+ }
258
+
259
+ const itemPath = path.join(kiroDir, item);
260
+ await remove(itemPath);
261
+ }
262
+
263
+ // Restore items from backup
264
+ const restoredFiles = [];
265
+ for (const item of itemsToRestore) {
266
+ const sourcePath = path.join(backupPath, item);
267
+ const destPath = path.join(kiroDir, item);
268
+
269
+ await copyDirectory(sourcePath, destPath, { overwrite: true });
270
+ restoredFiles.push(item);
271
+ }
272
+
273
+ return {
274
+ success: true,
275
+ backupId,
276
+ filesRestored: restoredFiles.length,
277
+ files: restoredFiles
278
+ };
279
+ } catch (error) {
280
+ throw new Error(`Failed to restore backup: ${error.message}`);
281
+ }
282
+ }
283
+
284
+ /**
285
+ * Validates backup integrity
286
+ *
287
+ * @param {string} backupPath - Absolute path to backup directory
288
+ * @returns {Promise<boolean>}
289
+ */
290
+ async validateBackup(backupPath) {
291
+ try {
292
+ // Check if backup directory exists
293
+ const backupExists = await pathExists(backupPath);
294
+ if (!backupExists) {
295
+ return false;
296
+ }
297
+
298
+ // Check if metadata exists
299
+ const metadataPath = path.join(backupPath, 'metadata.json');
300
+ const metadataExists = await pathExists(metadataPath);
301
+ if (!metadataExists) {
302
+ return false;
303
+ }
304
+
305
+ // Read and validate metadata
306
+ const metadata = await readJSON(metadataPath);
307
+ if (!metadata.id || !metadata.type || !metadata.created) {
308
+ return false;
309
+ }
310
+
311
+ // Count files in backup
312
+ const files = await listFilesRecursive(backupPath);
313
+ // Subtract 1 for metadata.json itself
314
+ const fileCount = files.length - 1;
315
+
316
+ // Verify file count matches metadata (allow some tolerance)
317
+ // Files might be slightly different due to metadata.json
318
+ if (Math.abs(fileCount - metadata.files) > 1) {
319
+ return false;
320
+ }
321
+
322
+ // Verify version.json exists and is valid (if present)
323
+ const versionPath = path.join(backupPath, 'version.json');
324
+ const versionExists = await pathExists(versionPath);
325
+ if (versionExists) {
326
+ try {
327
+ const versionInfo = await readJSON(versionPath);
328
+ // Basic validation
329
+ if (!versionInfo['kse-version']) {
330
+ return false;
331
+ }
332
+ } catch (error) {
333
+ return false;
334
+ }
335
+ }
336
+
337
+ return true;
338
+ } catch (error) {
339
+ return false;
340
+ }
341
+ }
342
+
343
+ /**
344
+ * Cleans old backups (keeps last N backups)
345
+ *
346
+ * @param {string} projectPath - Absolute path to project root
347
+ * @param {number} keepCount - Number of backups to keep (default: 5)
348
+ * @returns {Promise<void>}
349
+ */
350
+ async cleanOldBackups(projectPath, keepCount = 5) {
351
+ try {
352
+ // Get all backups sorted by date (newest first)
353
+ const backups = await this.listBackups(projectPath);
354
+
355
+ // If we have fewer backups than keepCount, nothing to do
356
+ if (backups.length <= keepCount) {
357
+ return;
358
+ }
359
+
360
+ // Remove old backups
361
+ const backupsToRemove = backups.slice(keepCount);
362
+
363
+ for (const backup of backupsToRemove) {
364
+ await remove(backup.path);
365
+ }
366
+ } catch (error) {
367
+ throw new Error(`Failed to clean old backups: ${error.message}`);
368
+ }
369
+ }
370
+ }
371
+
372
+ module.exports = BackupSystem;
@@ -0,0 +1,207 @@
1
+ /**
2
+ * Selective Backup System
3
+ *
4
+ * Creates targeted backups of specific files rather than entire directories.
5
+ * Used for conflict resolution during adoption to backup only files being overwritten.
6
+ */
7
+
8
+ const path = require('path');
9
+ const fs = require('fs').promises;
10
+ const {
11
+ pathExists,
12
+ ensureDirectory,
13
+ safeCopy,
14
+ readJSON,
15
+ writeJSON
16
+ } = require('../utils/fs-utils');
17
+
18
+ /**
19
+ * SelectiveBackup class for creating targeted file backups
20
+ */
21
+ class SelectiveBackup {
22
+ constructor() {
23
+ this.backupDir = '.kiro/backups';
24
+ }
25
+
26
+ /**
27
+ * Creates a backup of specific files before overwriting
28
+ *
29
+ * @param {string} projectPath - Project root path
30
+ * @param {string[]} filePaths - Relative paths of files to backup (from .kiro/)
31
+ * @param {Object} options - Backup options
32
+ * @param {string} options.type - Backup type (default: 'conflict')
33
+ * @returns {Promise<SelectiveBackupInfo>}
34
+ */
35
+ async createSelectiveBackup(projectPath, filePaths, options = {}) {
36
+ const { type = 'conflict' } = options;
37
+
38
+ // Generate backup ID with timestamp
39
+ const timestamp = new Date().toISOString().replace(/[:.]/g, '-').split('T');
40
+ const dateStr = timestamp[0];
41
+ const timeStr = timestamp[1].split('-').slice(0, 3).join('');
42
+ const backupId = `${type}-${dateStr}-${timeStr}`;
43
+
44
+ const backupPath = path.join(projectPath, this.backupDir, backupId);
45
+ const filesBackupPath = path.join(backupPath, 'files');
46
+
47
+ // Create backup directory structure
48
+ await ensureDirectory(backupPath);
49
+ await ensureDirectory(filesBackupPath);
50
+
51
+ const backedUpFiles = [];
52
+ let totalSize = 0;
53
+
54
+ // Backup each file
55
+ for (const filePath of filePaths) {
56
+ const sourcePath = path.join(projectPath, '.kiro', filePath);
57
+ const destPath = path.join(filesBackupPath, filePath);
58
+
59
+ // Check if source file exists
60
+ const sourceExists = await pathExists(sourcePath);
61
+ if (!sourceExists) {
62
+ continue; // Skip non-existent files
63
+ }
64
+
65
+ // Ensure destination directory exists
66
+ const destDir = path.dirname(destPath);
67
+ await ensureDirectory(destDir);
68
+
69
+ // Copy file
70
+ await safeCopy(sourcePath, destPath, { overwrite: true });
71
+
72
+ // Get file size
73
+ const stats = await fs.stat(sourcePath);
74
+ totalSize += stats.size;
75
+
76
+ backedUpFiles.push(filePath);
77
+ }
78
+
79
+ // Create metadata
80
+ const metadata = {
81
+ id: backupId,
82
+ type,
83
+ created: new Date().toISOString(),
84
+ files: backedUpFiles,
85
+ fileCount: backedUpFiles.length,
86
+ totalSize
87
+ };
88
+
89
+ // Write metadata
90
+ await writeJSON(path.join(backupPath, 'metadata.json'), metadata);
91
+
92
+ // Write files list
93
+ await writeJSON(path.join(backupPath, 'files.json'), backedUpFiles);
94
+
95
+ return {
96
+ id: backupId,
97
+ type,
98
+ created: metadata.created,
99
+ files: backedUpFiles,
100
+ fileCount: backedUpFiles.length,
101
+ totalSize,
102
+ path: backupPath
103
+ };
104
+ }
105
+
106
+ /**
107
+ * Restores specific files from a selective backup
108
+ *
109
+ * @param {string} projectPath - Project root path
110
+ * @param {string} backupId - Backup ID to restore from
111
+ * @param {string[]} filePaths - Optional: specific files to restore (if not provided, restores all)
112
+ * @returns {Promise<RestoreResult>}
113
+ */
114
+ async restoreSelective(projectPath, backupId, filePaths = null) {
115
+ const backupPath = path.join(projectPath, this.backupDir, backupId);
116
+
117
+ // Check if backup exists
118
+ const backupExists = await pathExists(backupPath);
119
+ if (!backupExists) {
120
+ throw new Error(`Backup not found: ${backupId}`);
121
+ }
122
+
123
+ // Read metadata
124
+ const metadataPath = path.join(backupPath, 'metadata.json');
125
+ const metadata = await readJSON(metadataPath);
126
+
127
+ if (!metadata) {
128
+ throw new Error(`Invalid backup: metadata.json not found in ${backupId}`);
129
+ }
130
+
131
+ // Determine which files to restore
132
+ const filesToRestore = filePaths || metadata.files;
133
+
134
+ const restoredFiles = [];
135
+ const errors = [];
136
+
137
+ // Restore each file
138
+ for (const filePath of filesToRestore) {
139
+ try {
140
+ const sourcePath = path.join(backupPath, 'files', filePath);
141
+ const destPath = path.join(projectPath, '.kiro', filePath);
142
+
143
+ // Check if backup file exists
144
+ const sourceExists = await pathExists(sourcePath);
145
+ if (!sourceExists) {
146
+ errors.push(`File not found in backup: ${filePath}`);
147
+ continue;
148
+ }
149
+
150
+ // Ensure destination directory exists
151
+ const destDir = path.dirname(destPath);
152
+ await ensureDirectory(destDir);
153
+
154
+ // Restore file
155
+ await safeCopy(sourcePath, destPath, { overwrite: true });
156
+ restoredFiles.push(filePath);
157
+ } catch (error) {
158
+ errors.push(`Failed to restore ${filePath}: ${error.message}`);
159
+ }
160
+ }
161
+
162
+ return {
163
+ success: errors.length === 0,
164
+ backupId,
165
+ restoredFiles,
166
+ errors
167
+ };
168
+ }
169
+
170
+ /**
171
+ * Lists files in a selective backup
172
+ *
173
+ * @param {string} projectPath - Project root path
174
+ * @param {string} backupId - Backup ID
175
+ * @returns {Promise<string[]>} - Array of file paths in backup
176
+ */
177
+ async listBackupFiles(projectPath, backupId) {
178
+ const backupPath = path.join(projectPath, this.backupDir, backupId);
179
+
180
+ // Check if backup exists
181
+ const backupExists = await pathExists(backupPath);
182
+ if (!backupExists) {
183
+ throw new Error(`Backup not found: ${backupId}`);
184
+ }
185
+
186
+ // Read files list
187
+ const filesPath = path.join(backupPath, 'files.json');
188
+ const filesExists = await pathExists(filesPath);
189
+
190
+ if (filesExists) {
191
+ const files = await readJSON(filesPath);
192
+ return files || [];
193
+ }
194
+
195
+ // Fallback: read from metadata
196
+ const metadataPath = path.join(backupPath, 'metadata.json');
197
+ const metadata = await readJSON(metadataPath);
198
+
199
+ if (metadata && metadata.files) {
200
+ return metadata.files;
201
+ }
202
+
203
+ return [];
204
+ }
205
+ }
206
+
207
+ module.exports = SelectiveBackup;