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,605 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Template Sync System
|
|
3
|
+
*
|
|
4
|
+
* Automatically synchronizes template files with project files.
|
|
5
|
+
* Detects content differences and updates only changed files.
|
|
6
|
+
* Preserves user-specific files like CURRENT_CONTEXT.md.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
const path = require('path');
|
|
10
|
+
const crypto = require('crypto');
|
|
11
|
+
const fs = require('fs-extra');
|
|
12
|
+
const { pathExists } = require('../utils/fs-utils');
|
|
13
|
+
const { FileClassifier, FileCategory } = require('./file-classifier');
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Template Sync System
|
|
17
|
+
*
|
|
18
|
+
* Compares template files with project files and synchronizes
|
|
19
|
+
* only the files that have changed.
|
|
20
|
+
*/
|
|
21
|
+
class TemplateSync {
|
|
22
|
+
constructor() {
|
|
23
|
+
this.fileClassifier = new FileClassifier();
|
|
24
|
+
|
|
25
|
+
// Template files to sync (relative to .kiro/)
|
|
26
|
+
this.templateFiles = [
|
|
27
|
+
'steering/CORE_PRINCIPLES.md',
|
|
28
|
+
'steering/ENVIRONMENT.md',
|
|
29
|
+
'steering/RULES_GUIDE.md',
|
|
30
|
+
'tools/ultrawork_enhancer.py',
|
|
31
|
+
'README.md',
|
|
32
|
+
'ultrawork-application-guide.md',
|
|
33
|
+
'ultrawork-integration-summary.md',
|
|
34
|
+
'sisyphus-deep-dive.md'
|
|
35
|
+
];
|
|
36
|
+
|
|
37
|
+
// Files to always preserve (never sync)
|
|
38
|
+
this.preserveFiles = [
|
|
39
|
+
'steering/CURRENT_CONTEXT.md'
|
|
40
|
+
];
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Detect differences between template and project files
|
|
45
|
+
*
|
|
46
|
+
* @param {string} projectPath - Absolute path to project root
|
|
47
|
+
* @param {string} templatePath - Absolute path to template directory
|
|
48
|
+
* @returns {Promise<SyncReport>} Sync report with differences
|
|
49
|
+
*/
|
|
50
|
+
async detectTemplateDifferences(projectPath, templatePath) {
|
|
51
|
+
const differences = {
|
|
52
|
+
missing: [], // Files that don't exist in project
|
|
53
|
+
different: [], // Files with different content
|
|
54
|
+
identical: [], // Files with identical content
|
|
55
|
+
preserved: [], // Files that should be preserved
|
|
56
|
+
errors: [] // Files that couldn't be compared
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
const kiroPath = path.join(projectPath, '.kiro');
|
|
60
|
+
|
|
61
|
+
for (const templateFile of this.templateFiles) {
|
|
62
|
+
try {
|
|
63
|
+
// Check if file should be preserved
|
|
64
|
+
if (this.shouldPreserve(templateFile)) {
|
|
65
|
+
differences.preserved.push({
|
|
66
|
+
path: templateFile,
|
|
67
|
+
reason: 'User-specific file'
|
|
68
|
+
});
|
|
69
|
+
continue;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const projectFilePath = path.join(kiroPath, templateFile);
|
|
73
|
+
const templateFilePath = path.join(templatePath, templateFile);
|
|
74
|
+
|
|
75
|
+
// Check if template file exists
|
|
76
|
+
const templateExists = await pathExists(templateFilePath);
|
|
77
|
+
if (!templateExists) {
|
|
78
|
+
differences.errors.push({
|
|
79
|
+
path: templateFile,
|
|
80
|
+
error: 'Template file not found'
|
|
81
|
+
});
|
|
82
|
+
continue;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// Check if project file exists
|
|
86
|
+
const projectExists = await pathExists(projectFilePath);
|
|
87
|
+
if (!projectExists) {
|
|
88
|
+
differences.missing.push({
|
|
89
|
+
path: templateFile,
|
|
90
|
+
templatePath: templateFilePath,
|
|
91
|
+
projectPath: projectFilePath
|
|
92
|
+
});
|
|
93
|
+
continue;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// Compare file contents
|
|
97
|
+
const isDifferent = await this.compareFiles(projectFilePath, templateFilePath);
|
|
98
|
+
|
|
99
|
+
if (isDifferent) {
|
|
100
|
+
differences.different.push({
|
|
101
|
+
path: templateFile,
|
|
102
|
+
templatePath: templateFilePath,
|
|
103
|
+
projectPath: projectFilePath
|
|
104
|
+
});
|
|
105
|
+
} else {
|
|
106
|
+
differences.identical.push({
|
|
107
|
+
path: templateFile
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
} catch (error) {
|
|
111
|
+
differences.errors.push({
|
|
112
|
+
path: templateFile,
|
|
113
|
+
error: error.message
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
return {
|
|
119
|
+
differences,
|
|
120
|
+
summary: {
|
|
121
|
+
total: this.templateFiles.length,
|
|
122
|
+
missing: differences.missing.length,
|
|
123
|
+
different: differences.different.length,
|
|
124
|
+
identical: differences.identical.length,
|
|
125
|
+
preserved: differences.preserved.length,
|
|
126
|
+
errors: differences.errors.length,
|
|
127
|
+
needsSync: differences.missing.length + differences.different.length
|
|
128
|
+
}
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Compare two files for differences
|
|
134
|
+
*
|
|
135
|
+
* @param {string} file1Path - Path to first file
|
|
136
|
+
* @param {string} file2Path - Path to second file
|
|
137
|
+
* @returns {Promise<boolean>} True if files are different
|
|
138
|
+
*/
|
|
139
|
+
async compareFiles(file1Path, file2Path) {
|
|
140
|
+
try {
|
|
141
|
+
// Check if either file is binary
|
|
142
|
+
const isBinary1 = await this.isBinaryFile(file1Path);
|
|
143
|
+
const isBinary2 = await this.isBinaryFile(file2Path);
|
|
144
|
+
|
|
145
|
+
if (isBinary1 || isBinary2) {
|
|
146
|
+
// For binary files, compare file sizes and hashes
|
|
147
|
+
return await this.compareBinaryFiles(file1Path, file2Path);
|
|
148
|
+
} else {
|
|
149
|
+
// For text files, compare content hashes
|
|
150
|
+
return await this.compareTextFiles(file1Path, file2Path);
|
|
151
|
+
}
|
|
152
|
+
} catch (error) {
|
|
153
|
+
throw new Error(`Failed to compare files: ${error.message}`);
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Compare text files by content hash
|
|
159
|
+
*
|
|
160
|
+
* @param {string} file1Path - Path to first file
|
|
161
|
+
* @param {string} file2Path - Path to second file
|
|
162
|
+
* @returns {Promise<boolean>} True if files are different
|
|
163
|
+
*/
|
|
164
|
+
async compareTextFiles(file1Path, file2Path) {
|
|
165
|
+
try {
|
|
166
|
+
const content1 = await fs.readFile(file1Path, 'utf8');
|
|
167
|
+
const content2 = await fs.readFile(file2Path, 'utf8');
|
|
168
|
+
|
|
169
|
+
// Normalize line endings for comparison
|
|
170
|
+
const normalized1 = this.normalizeLineEndings(content1);
|
|
171
|
+
const normalized2 = this.normalizeLineEndings(content2);
|
|
172
|
+
|
|
173
|
+
// Compare hashes
|
|
174
|
+
const hash1 = this.calculateHash(normalized1);
|
|
175
|
+
const hash2 = this.calculateHash(normalized2);
|
|
176
|
+
|
|
177
|
+
return hash1 !== hash2;
|
|
178
|
+
} catch (error) {
|
|
179
|
+
throw new Error(`Failed to compare text files: ${error.message}`);
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* Compare binary files by size and hash
|
|
185
|
+
*
|
|
186
|
+
* @param {string} file1Path - Path to first file
|
|
187
|
+
* @param {string} file2Path - Path to second file
|
|
188
|
+
* @returns {Promise<boolean>} True if files are different
|
|
189
|
+
*/
|
|
190
|
+
async compareBinaryFiles(file1Path, file2Path) {
|
|
191
|
+
try {
|
|
192
|
+
// Compare file sizes first (quick check)
|
|
193
|
+
const stats1 = await fs.stat(file1Path);
|
|
194
|
+
const stats2 = await fs.stat(file2Path);
|
|
195
|
+
|
|
196
|
+
if (stats1.size !== stats2.size) {
|
|
197
|
+
return true; // Different sizes = different files
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
// Compare content hashes
|
|
201
|
+
const content1 = await fs.readFile(file1Path);
|
|
202
|
+
const content2 = await fs.readFile(file2Path);
|
|
203
|
+
|
|
204
|
+
const hash1 = this.calculateHash(content1);
|
|
205
|
+
const hash2 = this.calculateHash(content2);
|
|
206
|
+
|
|
207
|
+
return hash1 !== hash2;
|
|
208
|
+
} catch (error) {
|
|
209
|
+
throw new Error(`Failed to compare binary files: ${error.message}`);
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
/**
|
|
214
|
+
* Check if a file is binary
|
|
215
|
+
*
|
|
216
|
+
* @param {string} filePath - Path to file
|
|
217
|
+
* @returns {Promise<boolean>} True if file is binary
|
|
218
|
+
*/
|
|
219
|
+
async isBinaryFile(filePath) {
|
|
220
|
+
try {
|
|
221
|
+
// Check file extension first
|
|
222
|
+
const ext = path.extname(filePath).toLowerCase();
|
|
223
|
+
const textExtensions = ['.md', '.txt', '.json', '.js', '.py', '.yml', '.yaml', '.xml', '.html', '.css'];
|
|
224
|
+
|
|
225
|
+
if (textExtensions.includes(ext)) {
|
|
226
|
+
return false;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
// For unknown extensions, read first few bytes
|
|
230
|
+
const buffer = Buffer.alloc(512);
|
|
231
|
+
const fd = await fs.open(filePath, 'r');
|
|
232
|
+
|
|
233
|
+
try {
|
|
234
|
+
const { bytesRead } = await fs.read(fd, buffer, 0, 512, 0);
|
|
235
|
+
|
|
236
|
+
// Check for null bytes (common in binary files)
|
|
237
|
+
for (let i = 0; i < bytesRead; i++) {
|
|
238
|
+
if (buffer[i] === 0) {
|
|
239
|
+
return true;
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
return false;
|
|
244
|
+
} finally {
|
|
245
|
+
await fs.close(fd);
|
|
246
|
+
}
|
|
247
|
+
} catch (error) {
|
|
248
|
+
// If we can't determine, assume text
|
|
249
|
+
return false;
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
/**
|
|
254
|
+
* Normalize line endings to LF
|
|
255
|
+
*
|
|
256
|
+
* @param {string} content - File content
|
|
257
|
+
* @returns {string} Normalized content
|
|
258
|
+
*/
|
|
259
|
+
normalizeLineEndings(content) {
|
|
260
|
+
return content.replace(/\r\n/g, '\n').replace(/\r/g, '\n');
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
/**
|
|
264
|
+
* Calculate SHA-256 hash of content
|
|
265
|
+
*
|
|
266
|
+
* @param {string|Buffer} content - Content to hash
|
|
267
|
+
* @returns {string} Hex hash
|
|
268
|
+
*/
|
|
269
|
+
calculateHash(content) {
|
|
270
|
+
return crypto.createHash('sha256').update(content).digest('hex');
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
/**
|
|
274
|
+
* Check if a file should be preserved
|
|
275
|
+
*
|
|
276
|
+
* @param {string} filePath - File path relative to .kiro/
|
|
277
|
+
* @returns {boolean} True if file should be preserved
|
|
278
|
+
*/
|
|
279
|
+
shouldPreserve(filePath) {
|
|
280
|
+
const normalized = this.normalizeFilePath(filePath);
|
|
281
|
+
return this.preserveFiles.some(preserve =>
|
|
282
|
+
normalized === preserve || normalized.endsWith('/' + preserve)
|
|
283
|
+
);
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
/**
|
|
287
|
+
* Normalize file path
|
|
288
|
+
*
|
|
289
|
+
* @param {string} filePath - File path
|
|
290
|
+
* @returns {string} Normalized path
|
|
291
|
+
*/
|
|
292
|
+
normalizeFilePath(filePath) {
|
|
293
|
+
return filePath.replace(/\\/g, '/');
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
/**
|
|
297
|
+
* Synchronize templates to project
|
|
298
|
+
*
|
|
299
|
+
* @param {string} projectPath - Absolute path to project root
|
|
300
|
+
* @param {string} templatePath - Absolute path to template directory
|
|
301
|
+
* @param {Object} options - Sync options
|
|
302
|
+
* @param {boolean} options.dryRun - Preview without executing
|
|
303
|
+
* @param {Function} options.onProgress - Progress callback
|
|
304
|
+
* @returns {Promise<SyncResult>} Sync result
|
|
305
|
+
*/
|
|
306
|
+
async syncTemplates(projectPath, templatePath, options = {}) {
|
|
307
|
+
const { dryRun = false, onProgress = null } = options;
|
|
308
|
+
|
|
309
|
+
// Detect differences first
|
|
310
|
+
const report = await this.detectTemplateDifferences(projectPath, templatePath);
|
|
311
|
+
|
|
312
|
+
if (dryRun) {
|
|
313
|
+
return {
|
|
314
|
+
dryRun: true,
|
|
315
|
+
report,
|
|
316
|
+
synced: [],
|
|
317
|
+
errors: []
|
|
318
|
+
};
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
const synced = [];
|
|
322
|
+
const errors = [];
|
|
323
|
+
const kiroPath = path.join(projectPath, '.kiro');
|
|
324
|
+
|
|
325
|
+
// Sync missing files
|
|
326
|
+
for (const missing of report.differences.missing) {
|
|
327
|
+
try {
|
|
328
|
+
if (onProgress) {
|
|
329
|
+
onProgress({
|
|
330
|
+
type: 'create',
|
|
331
|
+
file: missing.path,
|
|
332
|
+
status: 'in-progress'
|
|
333
|
+
});
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
// Ensure directory exists
|
|
337
|
+
const targetDir = path.dirname(missing.projectPath);
|
|
338
|
+
await fs.ensureDir(targetDir);
|
|
339
|
+
|
|
340
|
+
// Copy file
|
|
341
|
+
await fs.copy(missing.templatePath, missing.projectPath);
|
|
342
|
+
|
|
343
|
+
synced.push({
|
|
344
|
+
path: missing.path,
|
|
345
|
+
action: 'created'
|
|
346
|
+
});
|
|
347
|
+
|
|
348
|
+
if (onProgress) {
|
|
349
|
+
onProgress({
|
|
350
|
+
type: 'create',
|
|
351
|
+
file: missing.path,
|
|
352
|
+
status: 'complete'
|
|
353
|
+
});
|
|
354
|
+
}
|
|
355
|
+
} catch (error) {
|
|
356
|
+
errors.push({
|
|
357
|
+
path: missing.path,
|
|
358
|
+
error: error.message
|
|
359
|
+
});
|
|
360
|
+
|
|
361
|
+
if (onProgress) {
|
|
362
|
+
onProgress({
|
|
363
|
+
type: 'create',
|
|
364
|
+
file: missing.path,
|
|
365
|
+
status: 'error',
|
|
366
|
+
error: error.message
|
|
367
|
+
});
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
// Sync different files
|
|
373
|
+
for (const different of report.differences.different) {
|
|
374
|
+
try {
|
|
375
|
+
if (onProgress) {
|
|
376
|
+
onProgress({
|
|
377
|
+
type: 'update',
|
|
378
|
+
file: different.path,
|
|
379
|
+
status: 'in-progress'
|
|
380
|
+
});
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
// Ensure directory exists
|
|
384
|
+
const targetDir = path.dirname(different.projectPath);
|
|
385
|
+
await fs.ensureDir(targetDir);
|
|
386
|
+
|
|
387
|
+
// Copy file (overwrite)
|
|
388
|
+
await fs.copy(different.templatePath, different.projectPath);
|
|
389
|
+
|
|
390
|
+
synced.push({
|
|
391
|
+
path: different.path,
|
|
392
|
+
action: 'updated'
|
|
393
|
+
});
|
|
394
|
+
|
|
395
|
+
if (onProgress) {
|
|
396
|
+
onProgress({
|
|
397
|
+
type: 'update',
|
|
398
|
+
file: different.path,
|
|
399
|
+
status: 'complete'
|
|
400
|
+
});
|
|
401
|
+
}
|
|
402
|
+
} catch (error) {
|
|
403
|
+
errors.push({
|
|
404
|
+
path: different.path,
|
|
405
|
+
error: error.message
|
|
406
|
+
});
|
|
407
|
+
|
|
408
|
+
if (onProgress) {
|
|
409
|
+
onProgress({
|
|
410
|
+
type: 'update',
|
|
411
|
+
file: different.path,
|
|
412
|
+
status: 'error',
|
|
413
|
+
error: error.message
|
|
414
|
+
});
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
return {
|
|
420
|
+
dryRun: false,
|
|
421
|
+
report,
|
|
422
|
+
synced,
|
|
423
|
+
errors,
|
|
424
|
+
summary: {
|
|
425
|
+
total: synced.length + errors.length,
|
|
426
|
+
synced: synced.length,
|
|
427
|
+
errors: errors.length,
|
|
428
|
+
created: synced.filter(s => s.action === 'created').length,
|
|
429
|
+
updated: synced.filter(s => s.action === 'updated').length
|
|
430
|
+
}
|
|
431
|
+
};
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
/**
|
|
435
|
+
* Get sync report summary as formatted string
|
|
436
|
+
*
|
|
437
|
+
* @param {SyncReport} report - Sync report
|
|
438
|
+
* @returns {string} Formatted summary
|
|
439
|
+
*/
|
|
440
|
+
formatSyncReport(report) {
|
|
441
|
+
const lines = [];
|
|
442
|
+
|
|
443
|
+
lines.push('Template Sync Report:');
|
|
444
|
+
lines.push(` Total templates: ${report.summary.total}`);
|
|
445
|
+
lines.push(` Needs sync: ${report.summary.needsSync}`);
|
|
446
|
+
lines.push(` Missing: ${report.summary.missing}`);
|
|
447
|
+
lines.push(` Different: ${report.summary.different}`);
|
|
448
|
+
lines.push(` Up-to-date: ${report.summary.identical}`);
|
|
449
|
+
lines.push(` Preserved: ${report.summary.preserved}`);
|
|
450
|
+
|
|
451
|
+
if (report.summary.errors > 0) {
|
|
452
|
+
lines.push(` Errors: ${report.summary.errors}`);
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
if (report.differences.missing.length > 0) {
|
|
456
|
+
lines.push('');
|
|
457
|
+
lines.push('Missing files:');
|
|
458
|
+
report.differences.missing.forEach(file => {
|
|
459
|
+
lines.push(` - ${file.path}`);
|
|
460
|
+
});
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
if (report.differences.different.length > 0) {
|
|
464
|
+
lines.push('');
|
|
465
|
+
lines.push('Different files:');
|
|
466
|
+
report.differences.different.forEach(file => {
|
|
467
|
+
lines.push(` - ${file.path}`);
|
|
468
|
+
});
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
if (report.differences.preserved.length > 0) {
|
|
472
|
+
lines.push('');
|
|
473
|
+
lines.push('Preserved files:');
|
|
474
|
+
report.differences.preserved.forEach(file => {
|
|
475
|
+
lines.push(` - ${file.path} (${file.reason})`);
|
|
476
|
+
});
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
if (report.differences.errors.length > 0) {
|
|
480
|
+
lines.push('');
|
|
481
|
+
lines.push('Errors:');
|
|
482
|
+
report.differences.errors.forEach(file => {
|
|
483
|
+
lines.push(` - ${file.path}: ${file.error}`);
|
|
484
|
+
});
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
return lines.join('\n');
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
/**
|
|
491
|
+
* Get sync result summary as formatted string
|
|
492
|
+
*
|
|
493
|
+
* @param {SyncResult} result - Sync result
|
|
494
|
+
* @returns {string} Formatted summary
|
|
495
|
+
*/
|
|
496
|
+
formatSyncResult(result) {
|
|
497
|
+
const lines = [];
|
|
498
|
+
|
|
499
|
+
if (result.dryRun) {
|
|
500
|
+
lines.push('Dry Run - No changes made');
|
|
501
|
+
lines.push('');
|
|
502
|
+
lines.push(this.formatSyncReport(result.report));
|
|
503
|
+
return lines.join('\n');
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
lines.push('Template Sync Complete:');
|
|
507
|
+
lines.push(` Total: ${result.summary.total}`);
|
|
508
|
+
lines.push(` Synced: ${result.summary.synced}`);
|
|
509
|
+
lines.push(` Created: ${result.summary.created}`);
|
|
510
|
+
lines.push(` Updated: ${result.summary.updated}`);
|
|
511
|
+
|
|
512
|
+
if (result.summary.errors > 0) {
|
|
513
|
+
lines.push(` Errors: ${result.summary.errors}`);
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
if (result.synced.length > 0) {
|
|
517
|
+
lines.push('');
|
|
518
|
+
lines.push('Synced files:');
|
|
519
|
+
result.synced.forEach(file => {
|
|
520
|
+
const icon = file.action === 'created' ? '✨' : '📝';
|
|
521
|
+
lines.push(` ${icon} ${file.path} (${file.action})`);
|
|
522
|
+
});
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
if (result.errors.length > 0) {
|
|
526
|
+
lines.push('');
|
|
527
|
+
lines.push('Errors:');
|
|
528
|
+
result.errors.forEach(file => {
|
|
529
|
+
lines.push(` ❌ ${file.path}: ${file.error}`);
|
|
530
|
+
});
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
return lines.join('\n');
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
/**
|
|
537
|
+
* Get list of template files
|
|
538
|
+
*
|
|
539
|
+
* @returns {string[]} Template file paths
|
|
540
|
+
*/
|
|
541
|
+
getTemplateFiles() {
|
|
542
|
+
return [...this.templateFiles];
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
/**
|
|
546
|
+
* Get list of preserved files
|
|
547
|
+
*
|
|
548
|
+
* @returns {string[]} Preserved file paths
|
|
549
|
+
*/
|
|
550
|
+
getPreservedFiles() {
|
|
551
|
+
return [...this.preserveFiles];
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
/**
|
|
555
|
+
* Add template file to sync list
|
|
556
|
+
*
|
|
557
|
+
* @param {string} filePath - File path relative to .kiro/
|
|
558
|
+
*/
|
|
559
|
+
addTemplateFile(filePath) {
|
|
560
|
+
const normalized = this.normalizeFilePath(filePath);
|
|
561
|
+
if (!this.templateFiles.includes(normalized)) {
|
|
562
|
+
this.templateFiles.push(normalized);
|
|
563
|
+
}
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
/**
|
|
567
|
+
* Remove template file from sync list
|
|
568
|
+
*
|
|
569
|
+
* @param {string} filePath - File path relative to .kiro/
|
|
570
|
+
*/
|
|
571
|
+
removeTemplateFile(filePath) {
|
|
572
|
+
const normalized = this.normalizeFilePath(filePath);
|
|
573
|
+
const index = this.templateFiles.indexOf(normalized);
|
|
574
|
+
if (index !== -1) {
|
|
575
|
+
this.templateFiles.splice(index, 1);
|
|
576
|
+
}
|
|
577
|
+
}
|
|
578
|
+
|
|
579
|
+
/**
|
|
580
|
+
* Add file to preserve list
|
|
581
|
+
*
|
|
582
|
+
* @param {string} filePath - File path relative to .kiro/
|
|
583
|
+
*/
|
|
584
|
+
addPreservedFile(filePath) {
|
|
585
|
+
const normalized = this.normalizeFilePath(filePath);
|
|
586
|
+
if (!this.preserveFiles.includes(normalized)) {
|
|
587
|
+
this.preserveFiles.push(normalized);
|
|
588
|
+
}
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
/**
|
|
592
|
+
* Remove file from preserve list
|
|
593
|
+
*
|
|
594
|
+
* @param {string} filePath - File path relative to .kiro/
|
|
595
|
+
*/
|
|
596
|
+
removePreservedFile(filePath) {
|
|
597
|
+
const normalized = this.normalizeFilePath(filePath);
|
|
598
|
+
const index = this.preserveFiles.indexOf(normalized);
|
|
599
|
+
if (index !== -1) {
|
|
600
|
+
this.preserveFiles.splice(index, 1);
|
|
601
|
+
}
|
|
602
|
+
}
|
|
603
|
+
}
|
|
604
|
+
|
|
605
|
+
module.exports = TemplateSync;
|