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,482 @@
|
|
|
1
|
+
const fs = require('fs-extra');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* PromptGenerator - 提示生成器
|
|
6
|
+
*
|
|
7
|
+
* 为特定任务生成 AI 编码助手的提示文件
|
|
8
|
+
*/
|
|
9
|
+
class PromptGenerator {
|
|
10
|
+
constructor() {
|
|
11
|
+
this.promptsDir = 'prompts';
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* 生成任务提示
|
|
16
|
+
*
|
|
17
|
+
* @param {string} projectPath - 项目根目录路径
|
|
18
|
+
* @param {string} specName - Spec 名称
|
|
19
|
+
* @param {string} taskId - 任务 ID
|
|
20
|
+
* @param {Object} options - 生成选项
|
|
21
|
+
* @returns {Promise<Object>} 生成结果
|
|
22
|
+
*/
|
|
23
|
+
async generatePrompt(projectPath, specName, taskId, options = {}) {
|
|
24
|
+
const {
|
|
25
|
+
targetTool = 'generic',
|
|
26
|
+
includeCodeContext = false,
|
|
27
|
+
maxContextLength = 10000
|
|
28
|
+
} = options;
|
|
29
|
+
|
|
30
|
+
try {
|
|
31
|
+
const specPath = path.join(projectPath, '.kiro/specs', specName);
|
|
32
|
+
|
|
33
|
+
// 检查 Spec 是否存在
|
|
34
|
+
const specExists = await fs.pathExists(specPath);
|
|
35
|
+
if (!specExists) {
|
|
36
|
+
return {
|
|
37
|
+
success: false,
|
|
38
|
+
error: `Spec not found: ${specName}`
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// 读取 Spec 文件
|
|
43
|
+
const requirements = await this.readSpecFile(specPath, 'requirements.md');
|
|
44
|
+
const design = await this.readSpecFile(specPath, 'design.md');
|
|
45
|
+
const tasks = await this.readSpecFile(specPath, 'tasks.md');
|
|
46
|
+
|
|
47
|
+
if (!tasks) {
|
|
48
|
+
return {
|
|
49
|
+
success: false,
|
|
50
|
+
error: 'tasks.md not found'
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// 提取任务信息
|
|
55
|
+
const taskInfo = this.extractTaskInfo(tasks, taskId);
|
|
56
|
+
|
|
57
|
+
if (!taskInfo) {
|
|
58
|
+
return {
|
|
59
|
+
success: false,
|
|
60
|
+
error: `Task not found: ${taskId}`
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// 提取相关 Requirements
|
|
65
|
+
const relevantRequirements = this.extractRelevantRequirements(
|
|
66
|
+
requirements,
|
|
67
|
+
taskInfo
|
|
68
|
+
);
|
|
69
|
+
|
|
70
|
+
// 提取相关 Design
|
|
71
|
+
const relevantDesign = this.extractRelevantDesignSections(
|
|
72
|
+
design,
|
|
73
|
+
taskInfo
|
|
74
|
+
);
|
|
75
|
+
|
|
76
|
+
// 格式化提示
|
|
77
|
+
const prompt = this.formatPrompt({
|
|
78
|
+
specName,
|
|
79
|
+
taskId,
|
|
80
|
+
taskInfo,
|
|
81
|
+
relevantRequirements,
|
|
82
|
+
relevantDesign,
|
|
83
|
+
targetTool,
|
|
84
|
+
maxContextLength
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
// 保存提示文件
|
|
88
|
+
const promptPath = await this.savePrompt(specPath, taskId, prompt);
|
|
89
|
+
|
|
90
|
+
return {
|
|
91
|
+
success: true,
|
|
92
|
+
promptPath,
|
|
93
|
+
specName,
|
|
94
|
+
taskId,
|
|
95
|
+
size: Buffer.byteLength(prompt, 'utf8')
|
|
96
|
+
};
|
|
97
|
+
} catch (error) {
|
|
98
|
+
return {
|
|
99
|
+
success: false,
|
|
100
|
+
error: error.message
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* 读取 Spec 文件
|
|
107
|
+
*
|
|
108
|
+
* @param {string} specPath - Spec 目录路径
|
|
109
|
+
* @param {string} fileName - 文件名
|
|
110
|
+
* @returns {Promise<string|null>} 文件内容或 null
|
|
111
|
+
*/
|
|
112
|
+
async readSpecFile(specPath, fileName) {
|
|
113
|
+
const filePath = path.join(specPath, fileName);
|
|
114
|
+
|
|
115
|
+
try {
|
|
116
|
+
const exists = await fs.pathExists(filePath);
|
|
117
|
+
if (!exists) {
|
|
118
|
+
return null;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
return await fs.readFile(filePath, 'utf8');
|
|
122
|
+
} catch (error) {
|
|
123
|
+
return null;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* 提取任务信息
|
|
129
|
+
*
|
|
130
|
+
* @param {string} tasksContent - tasks.md 内容
|
|
131
|
+
* @param {string} taskId - 任务 ID
|
|
132
|
+
* @returns {Object|null} 任务信息
|
|
133
|
+
*/
|
|
134
|
+
extractTaskInfo(tasksContent, taskId) {
|
|
135
|
+
const lines = tasksContent.split('\n');
|
|
136
|
+
const taskPattern = new RegExp(`^-\\s*\\[[\\s\\-x~]\\]\\*?\\s+${taskId}\\s+(.+)$`);
|
|
137
|
+
|
|
138
|
+
let taskInfo = null;
|
|
139
|
+
let inTaskDetails = false;
|
|
140
|
+
const details = [];
|
|
141
|
+
|
|
142
|
+
for (let i = 0; i < lines.length; i++) {
|
|
143
|
+
const line = lines[i];
|
|
144
|
+
const match = line.match(taskPattern);
|
|
145
|
+
|
|
146
|
+
if (match) {
|
|
147
|
+
taskInfo = {
|
|
148
|
+
id: taskId,
|
|
149
|
+
title: match[1].replace(/\[@.+\]$/, '').trim(),
|
|
150
|
+
fullLine: line,
|
|
151
|
+
details: []
|
|
152
|
+
};
|
|
153
|
+
inTaskDetails = true;
|
|
154
|
+
continue;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
if (inTaskDetails) {
|
|
158
|
+
// 收集任务详细信息(缩进的行)
|
|
159
|
+
if (line.trim() === '') {
|
|
160
|
+
continue;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
if (line.startsWith(' ') || line.startsWith(' - ')) {
|
|
164
|
+
details.push(line.trim());
|
|
165
|
+
} else if (line.match(/^-\s*\[/)) {
|
|
166
|
+
// 遇到下一个任务,停止收集
|
|
167
|
+
break;
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
if (taskInfo) {
|
|
173
|
+
taskInfo.details = details;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
return taskInfo;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* 提取相关 Requirements
|
|
181
|
+
*
|
|
182
|
+
* @param {string} requirements - Requirements 内容
|
|
183
|
+
* @param {Object} taskInfo - 任务信息
|
|
184
|
+
* @returns {string} 相关 Requirements
|
|
185
|
+
*/
|
|
186
|
+
extractRelevantRequirements(requirements, taskInfo) {
|
|
187
|
+
if (!requirements) {
|
|
188
|
+
return 'No requirements document found.';
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
// 从任务详情中提取 Requirements 引用
|
|
192
|
+
const reqReferences = [];
|
|
193
|
+
for (const detail of taskInfo.details) {
|
|
194
|
+
const match = detail.match(/_Requirements?:\s*([\d\.,\s]+)_/);
|
|
195
|
+
if (match) {
|
|
196
|
+
const refs = match[1].split(',').map(r => r.trim());
|
|
197
|
+
reqReferences.push(...refs);
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
if (reqReferences.length === 0) {
|
|
202
|
+
// 如果没有明确引用,返回所有 Requirements
|
|
203
|
+
return requirements;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
// 提取引用的 Requirements 章节
|
|
207
|
+
const sections = this.extractSectionsByReferences(
|
|
208
|
+
requirements,
|
|
209
|
+
reqReferences,
|
|
210
|
+
'Requirement'
|
|
211
|
+
);
|
|
212
|
+
|
|
213
|
+
return sections.length > 0 ? sections.join('\n\n') : requirements;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
/**
|
|
217
|
+
* 提取相关 Design 章节
|
|
218
|
+
*
|
|
219
|
+
* @param {string} design - Design 内容
|
|
220
|
+
* @param {Object} taskInfo - 任务信息
|
|
221
|
+
* @returns {string} 相关 Design 章节
|
|
222
|
+
*/
|
|
223
|
+
extractRelevantDesignSections(design, taskInfo) {
|
|
224
|
+
if (!design) {
|
|
225
|
+
return 'No design document found.';
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
// 从任务标题中提取关键词
|
|
229
|
+
const keywords = this.extractKeywords(taskInfo.title);
|
|
230
|
+
|
|
231
|
+
// 查找包含关键词的 Design 章节
|
|
232
|
+
const sections = this.extractSectionsByKeywords(design, keywords);
|
|
233
|
+
|
|
234
|
+
return sections.length > 0 ? sections.join('\n\n') : design;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
/**
|
|
238
|
+
* 根据引用提取章节
|
|
239
|
+
*
|
|
240
|
+
* @param {string} content - 文档内容
|
|
241
|
+
* @param {Array<string>} references - 引用列表
|
|
242
|
+
* @param {string} prefix - 章节前缀
|
|
243
|
+
* @returns {Array<string>} 提取的章节
|
|
244
|
+
*/
|
|
245
|
+
extractSectionsByReferences(content, references, prefix) {
|
|
246
|
+
const sections = [];
|
|
247
|
+
const lines = content.split('\n');
|
|
248
|
+
|
|
249
|
+
for (const ref of references) {
|
|
250
|
+
const pattern = new RegExp(`^###?\\s+${prefix}\\s+${ref}[:\\s]`, 'i');
|
|
251
|
+
let inSection = false;
|
|
252
|
+
let section = [];
|
|
253
|
+
|
|
254
|
+
for (const line of lines) {
|
|
255
|
+
if (pattern.test(line)) {
|
|
256
|
+
inSection = true;
|
|
257
|
+
section = [line];
|
|
258
|
+
} else if (inSection) {
|
|
259
|
+
if (line.match(/^###?\s+/)) {
|
|
260
|
+
// 遇到下一个章节,停止收集
|
|
261
|
+
sections.push(section.join('\n'));
|
|
262
|
+
break;
|
|
263
|
+
}
|
|
264
|
+
section.push(line);
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
if (section.length > 0) {
|
|
269
|
+
sections.push(section.join('\n'));
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
return sections;
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
/**
|
|
277
|
+
* 提取关键词
|
|
278
|
+
*
|
|
279
|
+
* @param {string} text - 文本
|
|
280
|
+
* @returns {Array<string>} 关键词列表
|
|
281
|
+
*/
|
|
282
|
+
extractKeywords(text) {
|
|
283
|
+
// 移除常见词汇,提取有意义的关键词
|
|
284
|
+
const stopWords = ['implement', 'create', 'add', 'update', 'write', 'test', 'the', 'a', 'an', 'and', 'or', 'for', 'to', 'with'];
|
|
285
|
+
const words = text.toLowerCase()
|
|
286
|
+
.replace(/[^\w\s]/g, ' ')
|
|
287
|
+
.split(/\s+/)
|
|
288
|
+
.filter(w => w.length > 3 && !stopWords.includes(w));
|
|
289
|
+
|
|
290
|
+
return [...new Set(words)];
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
/**
|
|
294
|
+
* 根据关键词提取章节
|
|
295
|
+
*
|
|
296
|
+
* @param {string} content - 文档内容
|
|
297
|
+
* @param {Array<string>} keywords - 关键词列表
|
|
298
|
+
* @returns {Array<string>} 提取的章节
|
|
299
|
+
*/
|
|
300
|
+
extractSectionsByKeywords(content, keywords) {
|
|
301
|
+
if (keywords.length === 0) {
|
|
302
|
+
return [];
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
const sections = [];
|
|
306
|
+
const lines = content.split('\n');
|
|
307
|
+
let currentSection = [];
|
|
308
|
+
let inRelevantSection = false;
|
|
309
|
+
|
|
310
|
+
for (const line of lines) {
|
|
311
|
+
if (line.match(/^###?\s+/)) {
|
|
312
|
+
// 检查是否是新章节
|
|
313
|
+
if (currentSection.length > 0 && inRelevantSection) {
|
|
314
|
+
sections.push(currentSection.join('\n'));
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
// 检查章节标题是否包含关键词
|
|
318
|
+
const lowerLine = line.toLowerCase();
|
|
319
|
+
inRelevantSection = keywords.some(kw => lowerLine.includes(kw));
|
|
320
|
+
currentSection = [line];
|
|
321
|
+
} else if (inRelevantSection) {
|
|
322
|
+
currentSection.push(line);
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
if (currentSection.length > 0 && inRelevantSection) {
|
|
327
|
+
sections.push(currentSection.join('\n'));
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
return sections;
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
/**
|
|
334
|
+
* 格式化提示
|
|
335
|
+
*
|
|
336
|
+
* @param {Object} params - 提示参数
|
|
337
|
+
* @returns {string} 格式化的提示
|
|
338
|
+
*/
|
|
339
|
+
formatPrompt(params) {
|
|
340
|
+
const {
|
|
341
|
+
specName,
|
|
342
|
+
taskId,
|
|
343
|
+
taskInfo,
|
|
344
|
+
relevantRequirements,
|
|
345
|
+
relevantDesign,
|
|
346
|
+
targetTool,
|
|
347
|
+
maxContextLength
|
|
348
|
+
} = params;
|
|
349
|
+
|
|
350
|
+
const sections = [];
|
|
351
|
+
|
|
352
|
+
// 头部
|
|
353
|
+
sections.push(`# Task Prompt: ${specName} - Task ${taskId}`);
|
|
354
|
+
sections.push('');
|
|
355
|
+
sections.push(`**Generated**: ${new Date().toISOString()}`);
|
|
356
|
+
sections.push(`**Target Tool**: ${targetTool}`);
|
|
357
|
+
sections.push('');
|
|
358
|
+
sections.push('---');
|
|
359
|
+
sections.push('');
|
|
360
|
+
|
|
361
|
+
// 任务描述
|
|
362
|
+
sections.push('## 📋 Task Description');
|
|
363
|
+
sections.push('');
|
|
364
|
+
sections.push(`**Task ID**: ${taskId}`);
|
|
365
|
+
sections.push(`**Title**: ${taskInfo.title}`);
|
|
366
|
+
sections.push('');
|
|
367
|
+
|
|
368
|
+
if (taskInfo.details.length > 0) {
|
|
369
|
+
sections.push('**Details**:');
|
|
370
|
+
for (const detail of taskInfo.details) {
|
|
371
|
+
sections.push(`- ${detail}`);
|
|
372
|
+
}
|
|
373
|
+
sections.push('');
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
sections.push('---');
|
|
377
|
+
sections.push('');
|
|
378
|
+
|
|
379
|
+
// 相关 Requirements
|
|
380
|
+
sections.push('## 📖 Relevant Requirements');
|
|
381
|
+
sections.push('');
|
|
382
|
+
sections.push(this.truncateContent(relevantRequirements, maxContextLength * 0.4));
|
|
383
|
+
sections.push('');
|
|
384
|
+
sections.push('---');
|
|
385
|
+
sections.push('');
|
|
386
|
+
|
|
387
|
+
// 相关 Design
|
|
388
|
+
sections.push('## 🏗️ Relevant Design');
|
|
389
|
+
sections.push('');
|
|
390
|
+
sections.push(this.truncateContent(relevantDesign, maxContextLength * 0.4));
|
|
391
|
+
sections.push('');
|
|
392
|
+
sections.push('---');
|
|
393
|
+
sections.push('');
|
|
394
|
+
|
|
395
|
+
// 实现指南
|
|
396
|
+
sections.push('## 💡 Implementation Guidelines');
|
|
397
|
+
sections.push('');
|
|
398
|
+
sections.push('1. **Read the requirements** carefully to understand what needs to be implemented');
|
|
399
|
+
sections.push('2. **Review the design** to understand the architecture and interfaces');
|
|
400
|
+
sections.push('3. **Implement the functionality** following the task details');
|
|
401
|
+
sections.push('4. **Write tests** to verify the implementation');
|
|
402
|
+
sections.push('5. **Update task status** after completion');
|
|
403
|
+
sections.push('');
|
|
404
|
+
sections.push('---');
|
|
405
|
+
sections.push('');
|
|
406
|
+
|
|
407
|
+
// 任务状态更新说明
|
|
408
|
+
sections.push('## ✅ Task Status Update');
|
|
409
|
+
sections.push('');
|
|
410
|
+
sections.push('After completing this task, update the task status in `tasks.md`:');
|
|
411
|
+
sections.push('');
|
|
412
|
+
sections.push('```markdown');
|
|
413
|
+
sections.push(`- [x] ${taskId} ${taskInfo.title}`);
|
|
414
|
+
sections.push('```');
|
|
415
|
+
sections.push('');
|
|
416
|
+
sections.push('---');
|
|
417
|
+
sections.push('');
|
|
418
|
+
|
|
419
|
+
// 工具特定说明
|
|
420
|
+
sections.push('## 🔧 Tool-Specific Notes');
|
|
421
|
+
sections.push('');
|
|
422
|
+
sections.push(this.getToolSpecificNotes(targetTool));
|
|
423
|
+
sections.push('');
|
|
424
|
+
|
|
425
|
+
return sections.join('\n');
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
/**
|
|
429
|
+
* 截断内容
|
|
430
|
+
*
|
|
431
|
+
* @param {string} content - 内容
|
|
432
|
+
* @param {number} maxLength - 最大长度
|
|
433
|
+
* @returns {string} 截断后的内容
|
|
434
|
+
*/
|
|
435
|
+
truncateContent(content, maxLength) {
|
|
436
|
+
if (content.length <= maxLength) {
|
|
437
|
+
return content;
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
return content.substring(0, maxLength) + '\n\n... (content truncated)';
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
/**
|
|
444
|
+
* 获取工具特定说明
|
|
445
|
+
*
|
|
446
|
+
* @param {string} targetTool - 目标工具
|
|
447
|
+
* @returns {string} 工具特定说明
|
|
448
|
+
*/
|
|
449
|
+
getToolSpecificNotes(targetTool) {
|
|
450
|
+
const notes = {
|
|
451
|
+
'generic': 'This prompt is compatible with any AI coding assistant.',
|
|
452
|
+
'claude-code': 'Use this prompt with Claude Code by copying it into the chat.',
|
|
453
|
+
'cursor': 'Use this prompt with Cursor by pasting it into the composer.',
|
|
454
|
+
'codex': 'Use this prompt with GitHub Copilot by including it in your code comments.',
|
|
455
|
+
'kiro': 'This prompt is optimized for Kiro IDE with automatic steering loading.'
|
|
456
|
+
};
|
|
457
|
+
|
|
458
|
+
return notes[targetTool] || notes['generic'];
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
/**
|
|
462
|
+
* 保存提示文件
|
|
463
|
+
*
|
|
464
|
+
* @param {string} specPath - Spec 目录路径
|
|
465
|
+
* @param {string} taskId - 任务 ID
|
|
466
|
+
* @param {string} prompt - 提示内容
|
|
467
|
+
* @returns {Promise<string>} 提示文件路径
|
|
468
|
+
*/
|
|
469
|
+
async savePrompt(specPath, taskId, prompt) {
|
|
470
|
+
const promptsPath = path.join(specPath, this.promptsDir);
|
|
471
|
+
await fs.ensureDir(promptsPath);
|
|
472
|
+
|
|
473
|
+
const fileName = `task-${taskId.replace(/\./g, '-')}.md`;
|
|
474
|
+
const promptPath = path.join(promptsPath, fileName);
|
|
475
|
+
|
|
476
|
+
await fs.writeFile(promptPath, prompt, 'utf8');
|
|
477
|
+
|
|
478
|
+
return promptPath;
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
module.exports = PromptGenerator;
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": "2026.02.17",
|
|
3
|
+
"source": "kse-moqui-core",
|
|
4
|
+
"capabilities": [
|
|
5
|
+
{
|
|
6
|
+
"canonical": "erp-order-query-read",
|
|
7
|
+
"aliases": [
|
|
8
|
+
"order-query-read",
|
|
9
|
+
"order-read",
|
|
10
|
+
"moqui-order-query-read",
|
|
11
|
+
"moqui-order-read"
|
|
12
|
+
],
|
|
13
|
+
"deprecated_aliases": [
|
|
14
|
+
"order-query",
|
|
15
|
+
"order-reader"
|
|
16
|
+
]
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
"canonical": "erp-order-fulfillment-workflow",
|
|
20
|
+
"aliases": [
|
|
21
|
+
"order-fulfillment-workflow",
|
|
22
|
+
"order-fulfillment",
|
|
23
|
+
"fulfillment-workflow",
|
|
24
|
+
"moqui-order-fulfillment"
|
|
25
|
+
],
|
|
26
|
+
"deprecated_aliases": [
|
|
27
|
+
"order-fulfilment-workflow",
|
|
28
|
+
"order-fulfilment"
|
|
29
|
+
]
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
"canonical": "erp-inventory-reserve-adjust",
|
|
33
|
+
"aliases": [
|
|
34
|
+
"inventory-reserve-adjust",
|
|
35
|
+
"inventory-adjust",
|
|
36
|
+
"inventory-reserve",
|
|
37
|
+
"moqui-inventory-reserve-adjust"
|
|
38
|
+
],
|
|
39
|
+
"deprecated_aliases": [
|
|
40
|
+
"inventory-adjustment",
|
|
41
|
+
"inventory-reservation-adjustment"
|
|
42
|
+
]
|
|
43
|
+
}
|
|
44
|
+
]
|
|
45
|
+
}
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
const fs = require('fs-extra');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Backup System
|
|
6
|
+
*
|
|
7
|
+
* Manages automatic backups before environment switches and supports rollback.
|
|
8
|
+
* Maintains backup history with a maximum of 10 backups per target file.
|
|
9
|
+
*/
|
|
10
|
+
class BackupSystem {
|
|
11
|
+
/**
|
|
12
|
+
* Create a new Backup System
|
|
13
|
+
* @param {string} projectRoot - Project root directory
|
|
14
|
+
*/
|
|
15
|
+
constructor(projectRoot) {
|
|
16
|
+
this.projectRoot = projectRoot;
|
|
17
|
+
this.backupDir = path.join(projectRoot, '.kiro', 'env-backups');
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Create backup of target files
|
|
22
|
+
* @param {Array} targetFiles - Array of file paths to backup
|
|
23
|
+
* @param {string} environmentName - Name of environment being switched to
|
|
24
|
+
* @returns {Object} Backup metadata (timestamp, files, location)
|
|
25
|
+
* @throws {Error} If backup creation fails
|
|
26
|
+
*/
|
|
27
|
+
async createBackup(targetFiles, environmentName) {
|
|
28
|
+
const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
|
|
29
|
+
const backupSubDir = path.join(this.backupDir, `${timestamp}_${environmentName}`);
|
|
30
|
+
|
|
31
|
+
await fs.ensureDir(backupSubDir);
|
|
32
|
+
|
|
33
|
+
const backedUpFiles = [];
|
|
34
|
+
|
|
35
|
+
for (const targetFile of targetFiles) {
|
|
36
|
+
const targetPath = path.join(this.projectRoot, targetFile);
|
|
37
|
+
|
|
38
|
+
// Only backup if file exists
|
|
39
|
+
if (await fs.pathExists(targetPath)) {
|
|
40
|
+
const backupPath = path.join(backupSubDir, targetFile);
|
|
41
|
+
const backupFileDir = path.dirname(backupPath);
|
|
42
|
+
|
|
43
|
+
await fs.ensureDir(backupFileDir);
|
|
44
|
+
await fs.copy(targetPath, backupPath);
|
|
45
|
+
|
|
46
|
+
backedUpFiles.push({
|
|
47
|
+
original_path: targetFile,
|
|
48
|
+
backup_path: path.relative(this.projectRoot, backupPath)
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// Save backup metadata
|
|
54
|
+
const metadata = {
|
|
55
|
+
timestamp,
|
|
56
|
+
environment_name: environmentName,
|
|
57
|
+
backup_directory: path.relative(this.projectRoot, backupSubDir),
|
|
58
|
+
files: backedUpFiles
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
await fs.writeFile(
|
|
62
|
+
path.join(backupSubDir, 'metadata.json'),
|
|
63
|
+
JSON.stringify(metadata, null, 2),
|
|
64
|
+
'utf8'
|
|
65
|
+
);
|
|
66
|
+
|
|
67
|
+
// Clean up old backups
|
|
68
|
+
await this.cleanupOldBackups();
|
|
69
|
+
|
|
70
|
+
return metadata;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Restore most recent backup
|
|
75
|
+
* @param {string} environmentName - Optional environment name to restore
|
|
76
|
+
* @returns {Object} Restore result with restored files
|
|
77
|
+
* @throws {Error} If restore fails or no backups exist
|
|
78
|
+
*/
|
|
79
|
+
async restoreBackup(environmentName = null) {
|
|
80
|
+
const backups = await this.listBackups();
|
|
81
|
+
|
|
82
|
+
if (backups.length === 0) {
|
|
83
|
+
throw new Error('No backups available to restore');
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// Find most recent backup (optionally filtered by environment name)
|
|
87
|
+
let targetBackup = null;
|
|
88
|
+
if (environmentName) {
|
|
89
|
+
const filtered = backups.filter(b => b.environment_name === environmentName);
|
|
90
|
+
if (filtered.length === 0) {
|
|
91
|
+
throw new Error(`No backups found for environment "${environmentName}"`);
|
|
92
|
+
}
|
|
93
|
+
targetBackup = filtered[0]; // Most recent
|
|
94
|
+
} else {
|
|
95
|
+
targetBackup = backups[0]; // Most recent overall
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
const restoredFiles = [];
|
|
99
|
+
|
|
100
|
+
// Restore each file
|
|
101
|
+
for (const fileInfo of targetBackup.files) {
|
|
102
|
+
const backupPath = path.join(this.projectRoot, fileInfo.backup_path);
|
|
103
|
+
const targetPath = path.join(this.projectRoot, fileInfo.original_path);
|
|
104
|
+
|
|
105
|
+
if (await fs.pathExists(backupPath)) {
|
|
106
|
+
const targetDir = path.dirname(targetPath);
|
|
107
|
+
await fs.ensureDir(targetDir);
|
|
108
|
+
await fs.copy(backupPath, targetPath, { overwrite: true });
|
|
109
|
+
restoredFiles.push(fileInfo.original_path);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
return {
|
|
114
|
+
success: true,
|
|
115
|
+
backup_timestamp: targetBackup.timestamp,
|
|
116
|
+
environment_name: targetBackup.environment_name,
|
|
117
|
+
files_restored: restoredFiles.length,
|
|
118
|
+
restored_files: restoredFiles
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* List available backups
|
|
124
|
+
* @returns {Array} Array of backup metadata objects (sorted by timestamp, newest first)
|
|
125
|
+
*/
|
|
126
|
+
async listBackups() {
|
|
127
|
+
if (!await fs.pathExists(this.backupDir)) {
|
|
128
|
+
return [];
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
const entries = await fs.readdir(this.backupDir);
|
|
132
|
+
const backups = [];
|
|
133
|
+
|
|
134
|
+
for (const entry of entries) {
|
|
135
|
+
const metadataPath = path.join(this.backupDir, entry, 'metadata.json');
|
|
136
|
+
if (await fs.pathExists(metadataPath)) {
|
|
137
|
+
try {
|
|
138
|
+
const content = await fs.readFile(metadataPath, 'utf8');
|
|
139
|
+
const metadata = JSON.parse(content);
|
|
140
|
+
backups.push(metadata);
|
|
141
|
+
} catch (error) {
|
|
142
|
+
// Skip corrupted metadata files
|
|
143
|
+
continue;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// Sort by timestamp (newest first)
|
|
149
|
+
backups.sort((a, b) => b.timestamp.localeCompare(a.timestamp));
|
|
150
|
+
|
|
151
|
+
return backups;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* Clean up old backups (keep last 10 per file)
|
|
156
|
+
* @returns {Object} Cleanup result with removed backup count
|
|
157
|
+
*/
|
|
158
|
+
async cleanupOldBackups() {
|
|
159
|
+
const backups = await this.listBackups();
|
|
160
|
+
|
|
161
|
+
if (backups.length <= 10) {
|
|
162
|
+
return { removed: 0 };
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
// Remove oldest backups (keep only last 10)
|
|
166
|
+
const backupsToRemove = backups.slice(10);
|
|
167
|
+
|
|
168
|
+
let removed = 0;
|
|
169
|
+
for (const backup of backupsToRemove) {
|
|
170
|
+
const fullPath = path.join(this.projectRoot, backup.backup_directory);
|
|
171
|
+
if (await fs.pathExists(fullPath)) {
|
|
172
|
+
await fs.remove(fullPath);
|
|
173
|
+
removed++;
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
return { removed };
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/**
|
|
181
|
+
* Get backup directory path
|
|
182
|
+
* @returns {string} Path to backup directory
|
|
183
|
+
*/
|
|
184
|
+
getBackupDirectory() {
|
|
185
|
+
return this.backupDir;
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
module.exports = BackupSystem;
|