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,285 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* File Scanner
|
|
3
|
+
*
|
|
4
|
+
* Utility for scanning directories and detecting files based on patterns
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
const fs = require('fs-extra');
|
|
8
|
+
const path = require('path');
|
|
9
|
+
const { minimatch } = require('minimatch');
|
|
10
|
+
|
|
11
|
+
class FileScanner {
|
|
12
|
+
constructor(projectPath) {
|
|
13
|
+
this.projectPath = projectPath;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Find all markdown files in a directory (non-recursive)
|
|
18
|
+
*
|
|
19
|
+
* @param {string} dirPath - Directory path to scan
|
|
20
|
+
* @returns {Promise<string[]>} - Array of absolute file paths
|
|
21
|
+
*/
|
|
22
|
+
async findMarkdownFiles(dirPath) {
|
|
23
|
+
try {
|
|
24
|
+
const entries = await fs.readdir(dirPath, { withFileTypes: true });
|
|
25
|
+
const mdFiles = [];
|
|
26
|
+
|
|
27
|
+
for (const entry of entries) {
|
|
28
|
+
if (entry.isFile() && entry.name.endsWith('.md')) {
|
|
29
|
+
mdFiles.push(path.join(dirPath, entry.name));
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
return mdFiles;
|
|
34
|
+
} catch (error) {
|
|
35
|
+
// If directory doesn't exist or can't be read, return empty array
|
|
36
|
+
return [];
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Find all markdown files in a directory recursively
|
|
42
|
+
*
|
|
43
|
+
* @param {string} dirPath - Directory path to scan
|
|
44
|
+
* @param {Object} options - Scan options
|
|
45
|
+
* @param {string[]} options.excludeDirs - Directory names to exclude (e.g., ['node_modules', '.git'])
|
|
46
|
+
* @returns {Promise<string[]>} - Array of absolute file paths
|
|
47
|
+
*/
|
|
48
|
+
async findMarkdownFilesRecursive(dirPath, options = {}) {
|
|
49
|
+
const excludeDirs = options.excludeDirs || ['node_modules', '.git'];
|
|
50
|
+
const mdFiles = [];
|
|
51
|
+
|
|
52
|
+
try {
|
|
53
|
+
await this._scanRecursive(dirPath, mdFiles, excludeDirs);
|
|
54
|
+
return mdFiles;
|
|
55
|
+
} catch (error) {
|
|
56
|
+
// If directory doesn't exist or can't be read, return empty array
|
|
57
|
+
return [];
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Internal recursive scanning helper
|
|
63
|
+
*
|
|
64
|
+
* @private
|
|
65
|
+
*/
|
|
66
|
+
async _scanRecursive(dirPath, results, excludeDirs) {
|
|
67
|
+
try {
|
|
68
|
+
const entries = await fs.readdir(dirPath, { withFileTypes: true });
|
|
69
|
+
|
|
70
|
+
for (const entry of entries) {
|
|
71
|
+
const fullPath = path.join(dirPath, entry.name);
|
|
72
|
+
|
|
73
|
+
if (entry.isDirectory()) {
|
|
74
|
+
// Skip excluded directories
|
|
75
|
+
if (!excludeDirs.includes(entry.name) && !entry.name.startsWith('.')) {
|
|
76
|
+
await this._scanRecursive(fullPath, results, excludeDirs);
|
|
77
|
+
}
|
|
78
|
+
} else if (entry.isFile() && entry.name.endsWith('.md')) {
|
|
79
|
+
results.push(fullPath);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
} catch (error) {
|
|
83
|
+
// Skip directories that can't be read
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Match files against glob patterns
|
|
90
|
+
*
|
|
91
|
+
* @param {string[]} filePaths - Array of file paths to check
|
|
92
|
+
* @param {string[]} patterns - Array of glob patterns (e.g., ['*-SUMMARY.md', 'TEMP-*.md'])
|
|
93
|
+
* @returns {string[]} - Array of matching file paths
|
|
94
|
+
*/
|
|
95
|
+
matchPatterns(filePaths, patterns) {
|
|
96
|
+
const matches = [];
|
|
97
|
+
|
|
98
|
+
for (const filePath of filePaths) {
|
|
99
|
+
const basename = path.basename(filePath);
|
|
100
|
+
|
|
101
|
+
for (const pattern of patterns) {
|
|
102
|
+
if (minimatch(basename, pattern, { nocase: false })) {
|
|
103
|
+
matches.push(filePath);
|
|
104
|
+
break; // Don't add the same file multiple times
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
return matches;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Check if a file matches any of the given patterns
|
|
114
|
+
*
|
|
115
|
+
* @param {string} filePath - File path to check
|
|
116
|
+
* @param {string[]} patterns - Array of glob patterns
|
|
117
|
+
* @returns {boolean} - True if file matches any pattern
|
|
118
|
+
*/
|
|
119
|
+
matchesPattern(filePath, patterns) {
|
|
120
|
+
const basename = path.basename(filePath);
|
|
121
|
+
|
|
122
|
+
for (const pattern of patterns) {
|
|
123
|
+
if (minimatch(basename, pattern, { nocase: false })) {
|
|
124
|
+
return true;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
return false;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Find all Spec directories in the project
|
|
133
|
+
*
|
|
134
|
+
* @returns {Promise<string[]>} - Array of Spec directory paths
|
|
135
|
+
*/
|
|
136
|
+
async findSpecDirectories() {
|
|
137
|
+
const specsPath = path.join(this.projectPath, '.kiro/specs');
|
|
138
|
+
|
|
139
|
+
try {
|
|
140
|
+
const entries = await fs.readdir(specsPath, { withFileTypes: true });
|
|
141
|
+
const specDirs = [];
|
|
142
|
+
|
|
143
|
+
for (const entry of entries) {
|
|
144
|
+
if (entry.isDirectory() && !entry.name.startsWith('.')) {
|
|
145
|
+
specDirs.push(path.join(specsPath, entry.name));
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
return specDirs;
|
|
150
|
+
} catch (error) {
|
|
151
|
+
// If .kiro/specs doesn't exist, return empty array
|
|
152
|
+
return [];
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* Get Spec directory by name
|
|
158
|
+
*
|
|
159
|
+
* @param {string} specName - Spec name
|
|
160
|
+
* @returns {string} - Spec directory path
|
|
161
|
+
*/
|
|
162
|
+
getSpecDirectory(specName) {
|
|
163
|
+
return path.join(this.projectPath, '.kiro/specs', specName);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* Check if a path exists
|
|
168
|
+
*
|
|
169
|
+
* @param {string} filePath - Path to check
|
|
170
|
+
* @returns {Promise<boolean>} - True if path exists
|
|
171
|
+
*/
|
|
172
|
+
async exists(filePath) {
|
|
173
|
+
return await fs.pathExists(filePath);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* Check if a path is a directory
|
|
178
|
+
*
|
|
179
|
+
* @param {string} dirPath - Path to check
|
|
180
|
+
* @returns {Promise<boolean>} - True if path is a directory
|
|
181
|
+
*/
|
|
182
|
+
async isDirectory(dirPath) {
|
|
183
|
+
try {
|
|
184
|
+
const stat = await fs.stat(dirPath);
|
|
185
|
+
return stat.isDirectory();
|
|
186
|
+
} catch (error) {
|
|
187
|
+
return false;
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
/**
|
|
192
|
+
* Check if a path is a file
|
|
193
|
+
*
|
|
194
|
+
* @param {string} filePath - Path to check
|
|
195
|
+
* @returns {Promise<boolean>} - True if path is a file
|
|
196
|
+
*/
|
|
197
|
+
async isFile(filePath) {
|
|
198
|
+
try {
|
|
199
|
+
const stat = await fs.stat(filePath);
|
|
200
|
+
return stat.isFile();
|
|
201
|
+
} catch (error) {
|
|
202
|
+
return false;
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
/**
|
|
207
|
+
* Get all files in a directory (non-recursive)
|
|
208
|
+
*
|
|
209
|
+
* @param {string} dirPath - Directory path
|
|
210
|
+
* @returns {Promise<string[]>} - Array of file paths
|
|
211
|
+
*/
|
|
212
|
+
async getFiles(dirPath) {
|
|
213
|
+
try {
|
|
214
|
+
const entries = await fs.readdir(dirPath, { withFileTypes: true });
|
|
215
|
+
const files = [];
|
|
216
|
+
|
|
217
|
+
for (const entry of entries) {
|
|
218
|
+
if (entry.isFile()) {
|
|
219
|
+
files.push(path.join(dirPath, entry.name));
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
return files;
|
|
224
|
+
} catch (error) {
|
|
225
|
+
return [];
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
/**
|
|
230
|
+
* Get all subdirectories in a directory (non-recursive)
|
|
231
|
+
*
|
|
232
|
+
* @param {string} dirPath - Directory path
|
|
233
|
+
* @returns {Promise<string[]>} - Array of subdirectory paths
|
|
234
|
+
*/
|
|
235
|
+
async getSubdirectories(dirPath) {
|
|
236
|
+
try {
|
|
237
|
+
const entries = await fs.readdir(dirPath, { withFileTypes: true });
|
|
238
|
+
const dirs = [];
|
|
239
|
+
|
|
240
|
+
for (const entry of entries) {
|
|
241
|
+
if (entry.isDirectory()) {
|
|
242
|
+
dirs.push(path.join(dirPath, entry.name));
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
return dirs;
|
|
247
|
+
} catch (error) {
|
|
248
|
+
return [];
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
/**
|
|
253
|
+
* Normalize path for cross-platform compatibility
|
|
254
|
+
*
|
|
255
|
+
* @param {string} filePath - Path to normalize
|
|
256
|
+
* @returns {string} - Normalized path
|
|
257
|
+
*/
|
|
258
|
+
normalizePath(filePath) {
|
|
259
|
+
// First replace all backslashes with forward slashes for consistency
|
|
260
|
+
// Then use path.normalize to get platform-specific separators
|
|
261
|
+
return path.normalize(filePath.replace(/\\/g, '/'));
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
/**
|
|
265
|
+
* Get relative path from project root
|
|
266
|
+
*
|
|
267
|
+
* @param {string} filePath - Absolute file path
|
|
268
|
+
* @returns {string} - Relative path from project root
|
|
269
|
+
*/
|
|
270
|
+
getRelativePath(filePath) {
|
|
271
|
+
return path.relative(this.projectPath, filePath);
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
/**
|
|
275
|
+
* Get absolute path from relative path
|
|
276
|
+
*
|
|
277
|
+
* @param {string} relativePath - Relative path from project root
|
|
278
|
+
* @returns {string} - Absolute path
|
|
279
|
+
*/
|
|
280
|
+
getAbsolutePath(relativePath) {
|
|
281
|
+
return path.join(this.projectPath, relativePath);
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
module.exports = FileScanner;
|
|
@@ -0,0 +1,333 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Hooks Manager
|
|
3
|
+
*
|
|
4
|
+
* Manages Git hooks for document governance
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
const fs = require('fs-extra');
|
|
8
|
+
const path = require('path');
|
|
9
|
+
|
|
10
|
+
class HooksManager {
|
|
11
|
+
constructor(projectPath) {
|
|
12
|
+
this.projectPath = projectPath;
|
|
13
|
+
this.gitDir = path.join(projectPath, '.git');
|
|
14
|
+
this.hooksDir = path.join(this.gitDir, 'hooks');
|
|
15
|
+
this.preCommitPath = path.join(this.hooksDir, 'pre-commit');
|
|
16
|
+
this.backupPath = path.join(this.hooksDir, 'pre-commit.backup');
|
|
17
|
+
|
|
18
|
+
// Marker to identify our hook content
|
|
19
|
+
this.hookMarkerStart = '# BEGIN kiro-spec-engine document governance';
|
|
20
|
+
this.hookMarkerEnd = '# END kiro-spec-engine document governance';
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Check if Git hooks are installed
|
|
25
|
+
*
|
|
26
|
+
* @returns {Promise<Object>}
|
|
27
|
+
*/
|
|
28
|
+
async checkHooksInstalled() {
|
|
29
|
+
try {
|
|
30
|
+
// Check if .git directory exists
|
|
31
|
+
if (!await fs.pathExists(this.gitDir)) {
|
|
32
|
+
return {
|
|
33
|
+
installed: false,
|
|
34
|
+
reason: 'not_git_repo',
|
|
35
|
+
message: 'Not a Git repository'
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// Check if hooks directory exists
|
|
40
|
+
if (!await fs.pathExists(this.hooksDir)) {
|
|
41
|
+
return {
|
|
42
|
+
installed: false,
|
|
43
|
+
reason: 'no_hooks_dir',
|
|
44
|
+
message: 'Git hooks directory does not exist'
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// Check if pre-commit hook exists
|
|
49
|
+
if (!await fs.pathExists(this.preCommitPath)) {
|
|
50
|
+
return {
|
|
51
|
+
installed: false,
|
|
52
|
+
reason: 'no_hook',
|
|
53
|
+
message: 'Pre-commit hook not installed'
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// Check if our hook content is present
|
|
58
|
+
const content = await fs.readFile(this.preCommitPath, 'utf8');
|
|
59
|
+
const hasOurHook = content.includes(this.hookMarkerStart);
|
|
60
|
+
|
|
61
|
+
return {
|
|
62
|
+
installed: hasOurHook,
|
|
63
|
+
reason: hasOurHook ? 'installed' : 'other_hook',
|
|
64
|
+
message: hasOurHook
|
|
65
|
+
? 'Document governance hook is installed'
|
|
66
|
+
: 'Pre-commit hook exists but is not ours',
|
|
67
|
+
hasExistingHook: !hasOurHook
|
|
68
|
+
};
|
|
69
|
+
} catch (error) {
|
|
70
|
+
return {
|
|
71
|
+
installed: false,
|
|
72
|
+
reason: 'error',
|
|
73
|
+
message: `Error checking hooks: ${error.message}`,
|
|
74
|
+
error: error.message
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Install pre-commit hook
|
|
81
|
+
*
|
|
82
|
+
* @returns {Promise<Object>}
|
|
83
|
+
*/
|
|
84
|
+
async installHooks() {
|
|
85
|
+
try {
|
|
86
|
+
// Check if .git directory exists
|
|
87
|
+
if (!await fs.pathExists(this.gitDir)) {
|
|
88
|
+
return {
|
|
89
|
+
success: false,
|
|
90
|
+
reason: 'not_git_repo',
|
|
91
|
+
message: 'Not a Git repository. Initialize Git first with: git init'
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// Create hooks directory if it doesn't exist
|
|
96
|
+
if (!await fs.pathExists(this.hooksDir)) {
|
|
97
|
+
await fs.ensureDir(this.hooksDir);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// Check if pre-commit hook already exists
|
|
101
|
+
let existingContent = '';
|
|
102
|
+
let hasExistingHook = false;
|
|
103
|
+
|
|
104
|
+
if (await fs.pathExists(this.preCommitPath)) {
|
|
105
|
+
existingContent = await fs.readFile(this.preCommitPath, 'utf8');
|
|
106
|
+
|
|
107
|
+
// Check if our hook is already installed
|
|
108
|
+
if (existingContent.includes(this.hookMarkerStart)) {
|
|
109
|
+
return {
|
|
110
|
+
success: true,
|
|
111
|
+
reason: 'already_installed',
|
|
112
|
+
message: 'Document governance hook is already installed'
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
hasExistingHook = true;
|
|
117
|
+
|
|
118
|
+
// Backup existing hook
|
|
119
|
+
await fs.writeFile(this.backupPath, existingContent);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// Generate our hook content
|
|
123
|
+
const ourHookContent = this.generateHookContent();
|
|
124
|
+
|
|
125
|
+
// Combine with existing hook if present
|
|
126
|
+
let finalContent;
|
|
127
|
+
if (hasExistingHook) {
|
|
128
|
+
// Preserve existing hook and add ours
|
|
129
|
+
finalContent = this.combineHooks(existingContent, ourHookContent);
|
|
130
|
+
} else {
|
|
131
|
+
// Just use our hook with shebang
|
|
132
|
+
finalContent = `#!/bin/sh\n\n${ourHookContent}`;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// Write the hook file
|
|
136
|
+
await fs.writeFile(this.preCommitPath, finalContent);
|
|
137
|
+
|
|
138
|
+
// Make it executable (Unix-like systems)
|
|
139
|
+
if (process.platform !== 'win32') {
|
|
140
|
+
await fs.chmod(this.preCommitPath, 0o755);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
return {
|
|
144
|
+
success: true,
|
|
145
|
+
reason: hasExistingHook ? 'installed_with_preservation' : 'installed',
|
|
146
|
+
message: hasExistingHook
|
|
147
|
+
? 'Document governance hook installed. Existing hook preserved and backed up.'
|
|
148
|
+
: 'Document governance hook installed successfully.',
|
|
149
|
+
backupCreated: hasExistingHook
|
|
150
|
+
};
|
|
151
|
+
} catch (error) {
|
|
152
|
+
return {
|
|
153
|
+
success: false,
|
|
154
|
+
reason: 'error',
|
|
155
|
+
message: `Failed to install hooks: ${error.message}`,
|
|
156
|
+
error: error.message
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* Uninstall pre-commit hook
|
|
163
|
+
*
|
|
164
|
+
* @returns {Promise<Object>}
|
|
165
|
+
*/
|
|
166
|
+
async uninstallHooks() {
|
|
167
|
+
try {
|
|
168
|
+
// Check if hook exists
|
|
169
|
+
if (!await fs.pathExists(this.preCommitPath)) {
|
|
170
|
+
return {
|
|
171
|
+
success: true,
|
|
172
|
+
reason: 'not_installed',
|
|
173
|
+
message: 'Document governance hook is not installed'
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
// Read current hook content
|
|
178
|
+
const content = await fs.readFile(this.preCommitPath, 'utf8');
|
|
179
|
+
|
|
180
|
+
// Check if our hook is present
|
|
181
|
+
if (!content.includes(this.hookMarkerStart)) {
|
|
182
|
+
return {
|
|
183
|
+
success: false,
|
|
184
|
+
reason: 'not_our_hook',
|
|
185
|
+
message: 'Pre-commit hook exists but is not ours. Manual removal required.'
|
|
186
|
+
};
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
// Remove our hook content
|
|
190
|
+
const newContent = this.removeOurHook(content);
|
|
191
|
+
|
|
192
|
+
// If nothing left (or just shebang), remove the file
|
|
193
|
+
const trimmedContent = newContent.trim();
|
|
194
|
+
if (trimmedContent === '' || trimmedContent === '#!/bin/sh') {
|
|
195
|
+
await fs.remove(this.preCommitPath);
|
|
196
|
+
|
|
197
|
+
// Restore backup if it exists
|
|
198
|
+
if (await fs.pathExists(this.backupPath)) {
|
|
199
|
+
await fs.remove(this.backupPath);
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
return {
|
|
203
|
+
success: true,
|
|
204
|
+
reason: 'removed',
|
|
205
|
+
message: 'Document governance hook removed successfully.'
|
|
206
|
+
};
|
|
207
|
+
} else {
|
|
208
|
+
// Write back the remaining content
|
|
209
|
+
await fs.writeFile(this.preCommitPath, newContent);
|
|
210
|
+
|
|
211
|
+
return {
|
|
212
|
+
success: true,
|
|
213
|
+
reason: 'removed_preserved',
|
|
214
|
+
message: 'Document governance hook removed. Other hooks preserved.'
|
|
215
|
+
};
|
|
216
|
+
}
|
|
217
|
+
} catch (error) {
|
|
218
|
+
return {
|
|
219
|
+
success: false,
|
|
220
|
+
reason: 'error',
|
|
221
|
+
message: `Failed to uninstall hooks: ${error.message}`,
|
|
222
|
+
error: error.message
|
|
223
|
+
};
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
/**
|
|
228
|
+
* Generate hook content
|
|
229
|
+
*
|
|
230
|
+
* @returns {string}
|
|
231
|
+
*/
|
|
232
|
+
generateHookContent() {
|
|
233
|
+
// Use Node.js to run validation
|
|
234
|
+
// This works on both Windows and Unix-like systems
|
|
235
|
+
return `${this.hookMarkerStart}
|
|
236
|
+
|
|
237
|
+
# Run document governance validation
|
|
238
|
+
node -e "
|
|
239
|
+
const path = require('path');
|
|
240
|
+
const ConfigManager = require('./lib/governance/config-manager');
|
|
241
|
+
const ValidationEngine = require('./lib/governance/validation-engine');
|
|
242
|
+
|
|
243
|
+
(async () => {
|
|
244
|
+
try {
|
|
245
|
+
const projectPath = process.cwd();
|
|
246
|
+
const configManager = new ConfigManager(projectPath);
|
|
247
|
+
const config = await configManager.load();
|
|
248
|
+
const validator = new ValidationEngine(projectPath, config);
|
|
249
|
+
|
|
250
|
+
// Validate root directory and all specs
|
|
251
|
+
const report = await validator.validate({ all: true });
|
|
252
|
+
|
|
253
|
+
if (!report.valid) {
|
|
254
|
+
console.error('\\n❌ Document governance validation failed!\\n');
|
|
255
|
+
console.error('Found ' + report.errors.length + ' error(s):\\n');
|
|
256
|
+
|
|
257
|
+
report.errors.forEach(err => {
|
|
258
|
+
console.error(' • ' + err.path);
|
|
259
|
+
console.error(' ' + err.message);
|
|
260
|
+
console.error(' → ' + err.recommendation + '\\n');
|
|
261
|
+
});
|
|
262
|
+
|
|
263
|
+
console.error('Fix these issues before committing:');
|
|
264
|
+
console.error(' kse doctor --docs # Diagnose issues');
|
|
265
|
+
console.error(' kse cleanup # Remove temporary files');
|
|
266
|
+
console.error(' kse validate --all # Validate structure\\n');
|
|
267
|
+
|
|
268
|
+
process.exit(1);
|
|
269
|
+
}
|
|
270
|
+
} catch (error) {
|
|
271
|
+
console.error('Error running document validation:', error.message);
|
|
272
|
+
// Don't block commit on validation errors
|
|
273
|
+
process.exit(0);
|
|
274
|
+
}
|
|
275
|
+
})();
|
|
276
|
+
"
|
|
277
|
+
|
|
278
|
+
${this.hookMarkerEnd}
|
|
279
|
+
`;
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
/**
|
|
283
|
+
* Combine existing hook with our hook
|
|
284
|
+
*
|
|
285
|
+
* @param {string} existingContent - Existing hook content
|
|
286
|
+
* @param {string} ourContent - Our hook content
|
|
287
|
+
* @returns {string}
|
|
288
|
+
*/
|
|
289
|
+
combineHooks(existingContent, ourContent) {
|
|
290
|
+
// Ensure shebang is present
|
|
291
|
+
let combined = existingContent;
|
|
292
|
+
|
|
293
|
+
if (!combined.startsWith('#!/')) {
|
|
294
|
+
combined = '#!/bin/sh\n\n' + combined;
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
// Add our hook at the end
|
|
298
|
+
combined += '\n\n' + ourContent;
|
|
299
|
+
|
|
300
|
+
return combined;
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
/**
|
|
304
|
+
* Remove our hook from combined content
|
|
305
|
+
*
|
|
306
|
+
* @param {string} content - Hook content
|
|
307
|
+
* @returns {string}
|
|
308
|
+
*/
|
|
309
|
+
removeOurHook(content) {
|
|
310
|
+
const startIndex = content.indexOf(this.hookMarkerStart);
|
|
311
|
+
const endIndex = content.indexOf(this.hookMarkerEnd);
|
|
312
|
+
|
|
313
|
+
if (startIndex === -1 || endIndex === -1) {
|
|
314
|
+
return content;
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
// Remove our hook section (including markers and newlines)
|
|
318
|
+
const before = content.substring(0, startIndex).trimEnd();
|
|
319
|
+
const after = content.substring(endIndex + this.hookMarkerEnd.length).trimStart();
|
|
320
|
+
|
|
321
|
+
if (before && after) {
|
|
322
|
+
return before + '\n\n' + after;
|
|
323
|
+
} else if (before) {
|
|
324
|
+
return before;
|
|
325
|
+
} else if (after) {
|
|
326
|
+
return after;
|
|
327
|
+
} else {
|
|
328
|
+
return '';
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
module.exports = HooksManager;
|