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.
- package/CHANGELOG.md +2513 -0
- package/LICENSE +21 -0
- package/README.md +765 -0
- package/README.zh.md +630 -0
- package/bin/kiro-spec-engine.js +796 -0
- package/bin/kse.js +3 -0
- package/bin/sce.js +3 -0
- package/bin/sco.js +3 -0
- package/docs/331-poc-adaptation-roadmap.md +156 -0
- package/docs/331-poc-dual-track-integration-guide.md +120 -0
- package/docs/331-poc-weekly-delivery-checklist.md +52 -0
- package/docs/OFFLINE_INSTALL.md +96 -0
- package/docs/README.md +279 -0
- package/docs/adopt-migration-guide.md +599 -0
- package/docs/adoption-guide.md +616 -0
- package/docs/agent-hooks-analysis.md +815 -0
- package/docs/architecture.md +733 -0
- package/docs/articles/ai-driven-development-philosophy-and-practice-review.md +208 -0
- package/docs/articles/ai-driven-development-philosophy-and-practice.en.md +459 -0
- package/docs/articles/ai-driven-development-philosophy-and-practice.md +492 -0
- package/docs/autonomous-control-guide.md +851 -0
- package/docs/command-reference.md +1368 -0
- package/docs/community.md +115 -0
- package/docs/cross-tool-guide.md +555 -0
- package/docs/developer-guide.md +619 -0
- package/docs/document-governance.md +865 -0
- package/docs/environment-management-guide.md +526 -0
- package/docs/examples/add-export-command/design.md +194 -0
- package/docs/examples/add-export-command/requirements.md +110 -0
- package/docs/examples/add-export-command/tasks.md +88 -0
- package/docs/examples/add-rest-api/design.md +855 -0
- package/docs/examples/add-rest-api/requirements.md +323 -0
- package/docs/examples/add-rest-api/tasks.md +355 -0
- package/docs/examples/add-user-dashboard/design.md +192 -0
- package/docs/examples/add-user-dashboard/requirements.md +143 -0
- package/docs/examples/add-user-dashboard/tasks.md +91 -0
- package/docs/faq.md +697 -0
- package/docs/handoffs/evidence/ontology/moqui-template-baseline-2026-02-17-232922.json +156 -0
- package/docs/handoffs/evidence/ontology/moqui-template-baseline-2026-02-17-232922.md +24 -0
- package/docs/images/wechat-qr.png +0 -0
- package/docs/integration-modes.md +529 -0
- package/docs/integration-philosophy.md +313 -0
- package/docs/knowledge-management-guide.md +263 -0
- package/docs/manual-workflows-guide.md +418 -0
- package/docs/moqui-capability-matrix.md +73 -0
- package/docs/moqui-template-core-library-playbook.md +109 -0
- package/docs/multi-agent-coordination-guide.md +553 -0
- package/docs/multi-repo-management-guide.md +1344 -0
- package/docs/quick-start-with-ai-tools.md +375 -0
- package/docs/quick-start.md +146 -0
- package/docs/release-checklist.md +121 -0
- package/docs/releases/README.md +13 -0
- package/docs/releases/v1.46.2-validation.md +45 -0
- package/docs/releases/v1.46.2.md +50 -0
- package/docs/scene-runtime-guide.md +347 -0
- package/docs/spec-collaboration-guide.md +369 -0
- package/docs/spec-locking-guide.md +225 -0
- package/docs/spec-numbering-guide.md +348 -0
- package/docs/spec-workflow.md +519 -0
- package/docs/steering-strategy-guide.md +196 -0
- package/docs/team-collaboration-guide.md +465 -0
- package/docs/testing-strategy.md +272 -0
- package/docs/tools/claude-guide.md +654 -0
- package/docs/tools/cursor-guide.md +706 -0
- package/docs/tools/generic-guide.md +446 -0
- package/docs/tools/kiro-guide.md +308 -0
- package/docs/tools/vscode-guide.md +445 -0
- package/docs/tools/windsurf-guide.md +391 -0
- package/docs/troubleshooting.md +1135 -0
- package/docs/upgrade-guide.md +639 -0
- package/docs/value-observability-guide.md +127 -0
- package/docs/zh/README.md +341 -0
- package/docs/zh/quick-start.md +764 -0
- package/docs/zh/release-checklist.md +121 -0
- package/docs/zh/releases/README.md +13 -0
- package/docs/zh/releases/v1.46.2-validation.md +45 -0
- package/docs/zh/releases/v1.46.2.md +50 -0
- package/docs/zh/spec-numbering-guide.md +348 -0
- package/docs/zh/tools/claude-guide.md +349 -0
- package/docs/zh/tools/cursor-guide.md +281 -0
- package/docs/zh/tools/generic-guide.md +499 -0
- package/docs/zh/tools/kiro-guide.md +342 -0
- package/docs/zh/tools/vscode-guide.md +449 -0
- package/docs/zh/tools/windsurf-guide.md +378 -0
- package/docs/zh/value-observability-guide.md +127 -0
- package/docs//344/272/244/344/273/230/346/270/205/345/215/225.md +75 -0
- package/lib/adoption/adoption-logger.js +487 -0
- package/lib/adoption/adoption-strategy.js +538 -0
- package/lib/adoption/backup-manager.js +420 -0
- package/lib/adoption/conflict-resolver.js +410 -0
- package/lib/adoption/detection-engine.js +275 -0
- package/lib/adoption/diff-viewer.js +226 -0
- package/lib/adoption/error-formatter.js +509 -0
- package/lib/adoption/file-classifier.js +385 -0
- package/lib/adoption/progress-reporter.js +534 -0
- package/lib/adoption/smart-orchestrator.js +470 -0
- package/lib/adoption/strategy-selector.js +218 -0
- package/lib/adoption/summary-generator.js +493 -0
- package/lib/adoption/template-sync.js +605 -0
- package/lib/auto/autonomous-engine.js +485 -0
- package/lib/auto/checkpoint-manager.js +300 -0
- package/lib/auto/close-loop-runner.js +2476 -0
- package/lib/auto/config-schema.js +176 -0
- package/lib/auto/decision-engine.js +344 -0
- package/lib/auto/error-recovery-manager.js +580 -0
- package/lib/auto/goal-decomposer.js +278 -0
- package/lib/auto/progress-tracker.js +502 -0
- package/lib/auto/safety-manager.js +186 -0
- package/lib/auto/semantic-decomposer.js +137 -0
- package/lib/auto/state-manager.js +126 -0
- package/lib/auto/task-queue-manager.js +340 -0
- package/lib/backup/backup-system.js +372 -0
- package/lib/backup/selective-backup.js +207 -0
- package/lib/collab/agent-registry.js +240 -0
- package/lib/collab/collab-manager.js +285 -0
- package/lib/collab/contract-manager.js +320 -0
- package/lib/collab/coordinator.js +370 -0
- package/lib/collab/dependency-manager.js +280 -0
- package/lib/collab/index.js +20 -0
- package/lib/collab/integration-manager.js +202 -0
- package/lib/collab/merge-coordinator.js +252 -0
- package/lib/collab/metadata-manager.js +233 -0
- package/lib/collab/multi-agent-config.js +120 -0
- package/lib/collab/spec-lifecycle-manager.js +304 -0
- package/lib/collab/sync-barrier.js +88 -0
- package/lib/collab/visualizer.js +208 -0
- package/lib/commands/adopt.js +749 -0
- package/lib/commands/auto.js +19559 -0
- package/lib/commands/collab.js +275 -0
- package/lib/commands/context.js +99 -0
- package/lib/commands/docs.js +808 -0
- package/lib/commands/doctor.js +273 -0
- package/lib/commands/env.js +420 -0
- package/lib/commands/knowledge.js +309 -0
- package/lib/commands/lock.js +235 -0
- package/lib/commands/ops.js +409 -0
- package/lib/commands/orchestrate.js +446 -0
- package/lib/commands/prompt.js +105 -0
- package/lib/commands/repo.js +118 -0
- package/lib/commands/rollback.js +219 -0
- package/lib/commands/scene.js +15549 -0
- package/lib/commands/spec-bootstrap.js +147 -0
- package/lib/commands/spec-gate.js +157 -0
- package/lib/commands/spec-pipeline.js +205 -0
- package/lib/commands/status.js +321 -0
- package/lib/commands/task.js +199 -0
- package/lib/commands/templates.js +654 -0
- package/lib/commands/upgrade.js +231 -0
- package/lib/commands/value.js +569 -0
- package/lib/commands/watch.js +684 -0
- package/lib/commands/workflows.js +240 -0
- package/lib/commands/workspace-multi.js +325 -0
- package/lib/commands/workspace.js +189 -0
- package/lib/context/context-exporter.js +378 -0
- package/lib/context/prompt-generator.js +482 -0
- package/lib/data/moqui-capability-lexicon.json +45 -0
- package/lib/environment/backup-system.js +189 -0
- package/lib/environment/environment-manager.js +379 -0
- package/lib/environment/environment-registry.js +168 -0
- package/lib/gitignore/gitignore-backup.js +229 -0
- package/lib/gitignore/gitignore-detector.js +239 -0
- package/lib/gitignore/gitignore-integration.js +267 -0
- package/lib/gitignore/gitignore-transformer.js +193 -0
- package/lib/gitignore/layered-rules-template.js +42 -0
- package/lib/governance/archive-tool.js +284 -0
- package/lib/governance/cleanup-tool.js +237 -0
- package/lib/governance/config-manager.js +186 -0
- package/lib/governance/diagnostic-engine.js +271 -0
- package/lib/governance/doc-reference-checker.js +200 -0
- package/lib/governance/execution-logger.js +243 -0
- package/lib/governance/file-scanner.js +285 -0
- package/lib/governance/hooks-manager.js +333 -0
- package/lib/governance/reporter.js +337 -0
- package/lib/governance/validation-engine.js +181 -0
- package/lib/i18n.js +79 -0
- package/lib/knowledge/entry-manager.js +208 -0
- package/lib/knowledge/index-manager.js +261 -0
- package/lib/knowledge/knowledge-manager.js +273 -0
- package/lib/knowledge/template-manager.js +191 -0
- package/lib/lock/index.js +21 -0
- package/lib/lock/lock-file.js +192 -0
- package/lib/lock/lock-manager.js +321 -0
- package/lib/lock/machine-identifier.js +135 -0
- package/lib/lock/steering-file-lock.js +207 -0
- package/lib/lock/task-lock-manager.js +345 -0
- package/lib/operations/audit-logger.js +293 -0
- package/lib/operations/feedback-manager.js +1147 -0
- package/lib/operations/index.js +23 -0
- package/lib/operations/models/index.js +170 -0
- package/lib/operations/operations-manager.js +151 -0
- package/lib/operations/operations-validator.js +280 -0
- package/lib/operations/permission-manager.js +354 -0
- package/lib/operations/template-loader.js +143 -0
- package/lib/orchestrator/agent-spawner.js +629 -0
- package/lib/orchestrator/bootstrap-prompt-builder.js +236 -0
- package/lib/orchestrator/index.js +19 -0
- package/lib/orchestrator/orchestration-engine.js +1270 -0
- package/lib/orchestrator/orchestrator-config.js +173 -0
- package/lib/orchestrator/status-monitor.js +591 -0
- package/lib/python-checker.js +209 -0
- package/lib/repo/config-manager.js +580 -0
- package/lib/repo/errors/config-error.js +13 -0
- package/lib/repo/errors/git-error.js +15 -0
- package/lib/repo/errors/repo-error.js +14 -0
- package/lib/repo/git-operations.js +181 -0
- package/lib/repo/handlers/.gitkeep +1 -0
- package/lib/repo/handlers/exec-handler.js +155 -0
- package/lib/repo/handlers/health-handler.js +169 -0
- package/lib/repo/handlers/init-handler.js +197 -0
- package/lib/repo/handlers/status-handler.js +176 -0
- package/lib/repo/output-formatter.js +184 -0
- package/lib/repo/path-resolver.js +178 -0
- package/lib/repo/repo-manager.js +514 -0
- package/lib/scene-runtime/audit-emitter.js +59 -0
- package/lib/scene-runtime/binding-plugin-loader.js +351 -0
- package/lib/scene-runtime/binding-registry.js +349 -0
- package/lib/scene-runtime/eval-bridge.js +44 -0
- package/lib/scene-runtime/index.js +19 -0
- package/lib/scene-runtime/moqui-adapter.js +620 -0
- package/lib/scene-runtime/moqui-client.js +606 -0
- package/lib/scene-runtime/moqui-extractor.js +2029 -0
- package/lib/scene-runtime/plan-compiler.js +208 -0
- package/lib/scene-runtime/policy-gate.js +58 -0
- package/lib/scene-runtime/runtime-executor.js +358 -0
- package/lib/scene-runtime/scene-loader.js +96 -0
- package/lib/scene-runtime/scene-ontology.js +959 -0
- package/lib/scene-runtime/scene-template-linter.js +852 -0
- package/lib/scene-runtime/templates/scene-template-erp-query-v0.1.yaml +28 -0
- package/lib/scene-runtime/templates/scene-template-hybrid-shadow-v0.1.yaml +34 -0
- package/lib/spec/bootstrap/context-collector.js +48 -0
- package/lib/spec/bootstrap/draft-generator.js +158 -0
- package/lib/spec/bootstrap/questionnaire-engine.js +70 -0
- package/lib/spec/bootstrap/trace-emitter.js +59 -0
- package/lib/spec/multi-spec-orchestrate.js +93 -0
- package/lib/spec/pipeline/constants.js +6 -0
- package/lib/spec/pipeline/stage-adapters.js +118 -0
- package/lib/spec/pipeline/stage-runner.js +146 -0
- package/lib/spec/pipeline/state-store.js +119 -0
- package/lib/spec-gate/engine/gate-engine.js +165 -0
- package/lib/spec-gate/policy/default-policy.js +22 -0
- package/lib/spec-gate/policy/policy-loader.js +103 -0
- package/lib/spec-gate/result-emitter.js +81 -0
- package/lib/spec-gate/rules/default-rules.js +156 -0
- package/lib/spec-gate/rules/rule-registry.js +51 -0
- package/lib/steering/adoption-config.js +164 -0
- package/lib/steering/compliance-auto-fixer.js +204 -0
- package/lib/steering/compliance-cache.js +99 -0
- package/lib/steering/compliance-error-reporter.js +70 -0
- package/lib/steering/context-sync-manager.js +273 -0
- package/lib/steering/index.js +92 -0
- package/lib/steering/spec-steering.js +230 -0
- package/lib/steering/steering-compliance-checker.js +73 -0
- package/lib/steering/steering-loader.js +144 -0
- package/lib/steering/steering-manager.js +289 -0
- package/lib/task/index.js +12 -0
- package/lib/task/task-claimer.js +489 -0
- package/lib/task/task-status-store.js +418 -0
- package/lib/templates/cache-manager.js +440 -0
- package/lib/templates/content-generalizer.js +247 -0
- package/lib/templates/frontmatter-generator.js +128 -0
- package/lib/templates/git-handler.js +471 -0
- package/lib/templates/metadata-collector.js +328 -0
- package/lib/templates/path-utils.js +144 -0
- package/lib/templates/registry-parser.js +505 -0
- package/lib/templates/spec-reader.js +216 -0
- package/lib/templates/template-applicator.js +249 -0
- package/lib/templates/template-creator.js +256 -0
- package/lib/templates/template-error.js +143 -0
- package/lib/templates/template-exporter.js +502 -0
- package/lib/templates/template-manager.js +782 -0
- package/lib/templates/template-validator.js +361 -0
- package/lib/upgrade/migration-engine.js +382 -0
- package/lib/upgrade/migrations/.gitkeep +52 -0
- package/lib/upgrade/migrations/1.0.0-to-1.1.0.js +78 -0
- package/lib/utils/file-diff.js +177 -0
- package/lib/utils/fs-utils.js +274 -0
- package/lib/utils/tool-detector.js +383 -0
- package/lib/utils/validation.js +324 -0
- package/lib/value/gate-summary-emitter.js +99 -0
- package/lib/value/metric-contract-loader.js +210 -0
- package/lib/value/risk-evaluator.js +117 -0
- package/lib/value/weekly-snapshot-builder.js +61 -0
- package/lib/version/version-checker.js +156 -0
- package/lib/version/version-manager.js +327 -0
- package/lib/watch/action-executor.js +458 -0
- package/lib/watch/event-debouncer.js +323 -0
- package/lib/watch/execution-logger.js +550 -0
- package/lib/watch/file-watcher.js +499 -0
- package/lib/watch/presets.js +266 -0
- package/lib/watch/watch-manager.js +533 -0
- package/lib/workspace/multi/global-config.js +150 -0
- package/lib/workspace/multi/index.js +22 -0
- package/lib/workspace/multi/path-utils.js +173 -0
- package/lib/workspace/multi/workspace-context-resolver.js +244 -0
- package/lib/workspace/multi/workspace-registry.js +196 -0
- package/lib/workspace/multi/workspace-state-manager.js +537 -0
- package/lib/workspace/multi/workspace.js +90 -0
- package/lib/workspace/workspace-manager.js +370 -0
- package/lib/workspace/workspace-sync.js +356 -0
- package/locales/en.json +114 -0
- package/locales/zh.json +114 -0
- package/package.json +102 -0
- package/template/.kiro/README.md +247 -0
- package/template/.kiro/hooks/check-spec-on-create.kiro.hook +17 -0
- package/template/.kiro/hooks/run-tests-on-save.kiro.hook +13 -0
- package/template/.kiro/hooks/sync-tasks-on-edit.kiro.hook +16 -0
- package/template/.kiro/specs/SPEC_WORKFLOW_GUIDE.md +134 -0
- package/template/.kiro/steering/CORE_PRINCIPLES.md +133 -0
- package/template/.kiro/steering/CURRENT_CONTEXT.md +30 -0
- package/template/.kiro/steering/ENVIRONMENT.md +35 -0
- package/template/.kiro/steering/RULES_GUIDE.md +46 -0
- package/template/.kiro/templates/operations/default/change-impact.md +112 -0
- package/template/.kiro/templates/operations/default/deployment.md +91 -0
- package/template/.kiro/templates/operations/default/feedback-response.md +269 -0
- package/template/.kiro/templates/operations/default/migration-plan.md +172 -0
- package/template/.kiro/templates/operations/default/monitoring.md +135 -0
- package/template/.kiro/templates/operations/default/operations.md +135 -0
- package/template/.kiro/templates/operations/default/rollback.md +143 -0
- package/template/.kiro/templates/operations/default/tools.yaml +364 -0
- package/template/.kiro/templates/operations/default/troubleshooting.md +123 -0
- package/template/.kiro/tools/backup_manager.py +295 -0
- package/template/.kiro/tools/configuration_manager.py +218 -0
- package/template/.kiro/tools/document_evaluator.py +550 -0
- package/template/.kiro/tools/enhancement_logger.py +168 -0
- package/template/.kiro/tools/error_handler.py +335 -0
- package/template/.kiro/tools/improvement_identifier.py +444 -0
- package/template/.kiro/tools/modification_applicator.py +737 -0
- package/template/.kiro/tools/quality_gate_enforcer.py +207 -0
- package/template/.kiro/tools/quality_scorer.py +305 -0
- package/template/.kiro/tools/report_generator.py +154 -0
- package/template/.kiro/tools/ultrawork_enhancer.py +676 -0
- package/template/.kiro/tools/ultrawork_enhancer_refactored.py +0 -0
- package/template/.kiro/tools/ultrawork_enhancer_v2.py +463 -0
- package/template/.kiro/tools/ultrawork_enhancer_v3.py +606 -0
- package/template/.kiro/tools/workflow_quality_gate.py +100 -0
- 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;
|