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,284 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Archive Tool
|
|
3
|
+
*
|
|
4
|
+
* Organizes Spec artifacts into proper subdirectories
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
const fs = require('fs-extra');
|
|
8
|
+
const path = require('path');
|
|
9
|
+
const FileScanner = require('./file-scanner');
|
|
10
|
+
|
|
11
|
+
class ArchiveTool {
|
|
12
|
+
constructor(projectPath, config) {
|
|
13
|
+
this.projectPath = projectPath;
|
|
14
|
+
this.config = config;
|
|
15
|
+
this.scanner = new FileScanner(projectPath);
|
|
16
|
+
this.movedFiles = [];
|
|
17
|
+
this.errors = [];
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Archive artifacts in a Spec directory
|
|
22
|
+
*
|
|
23
|
+
* @param {string} specName - Spec name
|
|
24
|
+
* @param {Object} options - Archive options
|
|
25
|
+
* @param {boolean} options.dryRun - Preview without moving
|
|
26
|
+
* @returns {Promise<ArchiveReport>}
|
|
27
|
+
*/
|
|
28
|
+
async archive(specName, options = {}) {
|
|
29
|
+
const specPath = this.scanner.getSpecDirectory(specName);
|
|
30
|
+
|
|
31
|
+
// Check if Spec directory exists
|
|
32
|
+
if (!await this.scanner.exists(specPath)) {
|
|
33
|
+
this.errors.push({
|
|
34
|
+
path: specPath,
|
|
35
|
+
error: `Spec directory does not exist: ${specName}`
|
|
36
|
+
});
|
|
37
|
+
return this.generateReport();
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const artifacts = await this.identifyArtifacts(specPath);
|
|
41
|
+
|
|
42
|
+
if (options.dryRun) {
|
|
43
|
+
return this.generateDryRunReport(artifacts);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
for (const artifact of artifacts) {
|
|
47
|
+
await this.moveArtifact(artifact);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
return this.generateReport();
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Identify artifacts to archive
|
|
55
|
+
*
|
|
56
|
+
* @param {string} specPath - Path to Spec directory
|
|
57
|
+
* @returns {Promise<Artifact[]>}
|
|
58
|
+
*/
|
|
59
|
+
async identifyArtifacts(specPath) {
|
|
60
|
+
const artifacts = [];
|
|
61
|
+
const files = await this.scanner.getFiles(specPath);
|
|
62
|
+
const requiredFiles = ['requirements.md', 'design.md', 'tasks.md'];
|
|
63
|
+
|
|
64
|
+
for (const filePath of files) {
|
|
65
|
+
const basename = path.basename(filePath);
|
|
66
|
+
|
|
67
|
+
// Skip required files
|
|
68
|
+
if (requiredFiles.includes(basename)) {
|
|
69
|
+
continue;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const targetSubdir = this.determineTargetSubdir(basename);
|
|
73
|
+
artifacts.push({
|
|
74
|
+
sourcePath: filePath,
|
|
75
|
+
targetSubdir: targetSubdir,
|
|
76
|
+
filename: basename
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
return artifacts;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Determine target subdirectory for a file
|
|
85
|
+
*
|
|
86
|
+
* @param {string} filename - File name
|
|
87
|
+
* @returns {string}
|
|
88
|
+
*/
|
|
89
|
+
determineTargetSubdir(filename) {
|
|
90
|
+
const lower = filename.toLowerCase();
|
|
91
|
+
|
|
92
|
+
// Results - check for result keywords FIRST (before test patterns)
|
|
93
|
+
// This ensures "test-results.json" goes to results, not tests
|
|
94
|
+
if (lower.includes('result') || lower.includes('output')) {
|
|
95
|
+
return this.resolveConfiguredSubdir('results');
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// Tests - check for test patterns (test files, not result files)
|
|
99
|
+
if (lower.endsWith('.test.js') || lower.endsWith('.spec.js') ||
|
|
100
|
+
lower.includes('.test.') || lower.includes('.spec.') ||
|
|
101
|
+
(lower.includes('test') && (lower.endsWith('.js') || lower.endsWith('.py')))) {
|
|
102
|
+
return this.resolveConfiguredSubdir('tests');
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// Scripts - check for script extensions and keywords
|
|
106
|
+
if (lower.endsWith('.js') || lower.endsWith('.py') ||
|
|
107
|
+
lower.endsWith('.sh') || lower.endsWith('.bat') ||
|
|
108
|
+
lower.includes('script')) {
|
|
109
|
+
return this.resolveConfiguredSubdir('scripts');
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// Reports - check for report keywords
|
|
113
|
+
if (lower.includes('report') || lower.includes('analysis') ||
|
|
114
|
+
lower.includes('summary') || lower.includes('diagnostic')) {
|
|
115
|
+
return this.resolveConfiguredSubdir('reports');
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// Data files
|
|
119
|
+
if (lower.includes('data')) {
|
|
120
|
+
return this.resolveConfiguredSubdir('results');
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// Default to docs for markdown and other documentation files
|
|
124
|
+
return this.resolveConfiguredSubdir('docs');
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Resolve preferred subdirectory against project governance config
|
|
129
|
+
*
|
|
130
|
+
* @param {string} preferredSubdir - Preferred subdirectory name
|
|
131
|
+
* @returns {string}
|
|
132
|
+
*/
|
|
133
|
+
resolveConfiguredSubdir(preferredSubdir) {
|
|
134
|
+
const allowedSubdirs = this.getAllowedSpecSubdirs();
|
|
135
|
+
|
|
136
|
+
// Backward compatibility when no config is available
|
|
137
|
+
if (allowedSubdirs.length === 0) {
|
|
138
|
+
return preferredSubdir;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
if (allowedSubdirs.includes(preferredSubdir)) {
|
|
142
|
+
return preferredSubdir;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// Generic safe fallback for governance-customized projects
|
|
146
|
+
if (allowedSubdirs.includes('custom')) {
|
|
147
|
+
return 'custom';
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
// Semantic fallback when custom bucket is unavailable
|
|
151
|
+
const fallbackMap = {
|
|
152
|
+
docs: 'reports',
|
|
153
|
+
tests: 'scripts',
|
|
154
|
+
results: 'reports'
|
|
155
|
+
};
|
|
156
|
+
|
|
157
|
+
const mappedSubdir = fallbackMap[preferredSubdir];
|
|
158
|
+
if (mappedSubdir && allowedSubdirs.includes(mappedSubdir)) {
|
|
159
|
+
return mappedSubdir;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
return allowedSubdirs[0];
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* Get normalized allowed Spec subdirectories from config
|
|
167
|
+
*
|
|
168
|
+
* @returns {string[]}
|
|
169
|
+
*/
|
|
170
|
+
getAllowedSpecSubdirs() {
|
|
171
|
+
const configured = this.config && Array.isArray(this.config.specSubdirs)
|
|
172
|
+
? this.config.specSubdirs
|
|
173
|
+
: [];
|
|
174
|
+
|
|
175
|
+
return configured
|
|
176
|
+
.filter(subdir => typeof subdir === 'string' && subdir.trim().length > 0)
|
|
177
|
+
.map(subdir => subdir.trim().toLowerCase());
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/**
|
|
181
|
+
* Move an artifact to its target subdirectory
|
|
182
|
+
*
|
|
183
|
+
* @param {Artifact} artifact - Artifact to move
|
|
184
|
+
* @returns {Promise<void>}
|
|
185
|
+
*/
|
|
186
|
+
async moveArtifact(artifact) {
|
|
187
|
+
try {
|
|
188
|
+
const targetDir = path.join(
|
|
189
|
+
path.dirname(artifact.sourcePath),
|
|
190
|
+
artifact.targetSubdir
|
|
191
|
+
);
|
|
192
|
+
|
|
193
|
+
// Ensure target directory exists
|
|
194
|
+
await fs.ensureDir(targetDir);
|
|
195
|
+
|
|
196
|
+
const targetPath = path.join(targetDir, artifact.filename);
|
|
197
|
+
|
|
198
|
+
// Check if target file already exists
|
|
199
|
+
if (await fs.pathExists(targetPath)) {
|
|
200
|
+
this.errors.push({
|
|
201
|
+
path: artifact.sourcePath,
|
|
202
|
+
error: `Target file already exists: ${targetPath}`
|
|
203
|
+
});
|
|
204
|
+
return;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
await fs.move(artifact.sourcePath, targetPath);
|
|
208
|
+
|
|
209
|
+
this.movedFiles.push({
|
|
210
|
+
from: artifact.sourcePath,
|
|
211
|
+
to: targetPath
|
|
212
|
+
});
|
|
213
|
+
} catch (error) {
|
|
214
|
+
this.errors.push({
|
|
215
|
+
path: artifact.sourcePath,
|
|
216
|
+
error: error.message
|
|
217
|
+
});
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
/**
|
|
222
|
+
* Generate dry run report
|
|
223
|
+
*
|
|
224
|
+
* @param {Artifact[]} artifacts - Artifacts that would be moved
|
|
225
|
+
* @returns {ArchiveReport}
|
|
226
|
+
*/
|
|
227
|
+
generateDryRunReport(artifacts) {
|
|
228
|
+
const movedFiles = artifacts.map(artifact => ({
|
|
229
|
+
from: artifact.sourcePath,
|
|
230
|
+
to: path.join(
|
|
231
|
+
path.dirname(artifact.sourcePath),
|
|
232
|
+
artifact.targetSubdir,
|
|
233
|
+
artifact.filename
|
|
234
|
+
)
|
|
235
|
+
}));
|
|
236
|
+
|
|
237
|
+
return {
|
|
238
|
+
success: true,
|
|
239
|
+
movedFiles: movedFiles,
|
|
240
|
+
errors: [],
|
|
241
|
+
summary: {
|
|
242
|
+
totalMoved: movedFiles.length,
|
|
243
|
+
totalErrors: 0
|
|
244
|
+
},
|
|
245
|
+
dryRun: true
|
|
246
|
+
};
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
/**
|
|
250
|
+
* Generate archive report
|
|
251
|
+
*
|
|
252
|
+
* @returns {ArchiveReport}
|
|
253
|
+
*/
|
|
254
|
+
generateReport() {
|
|
255
|
+
return {
|
|
256
|
+
success: this.errors.length === 0,
|
|
257
|
+
movedFiles: this.movedFiles,
|
|
258
|
+
errors: this.errors,
|
|
259
|
+
summary: {
|
|
260
|
+
totalMoved: this.movedFiles.length,
|
|
261
|
+
totalErrors: this.errors.length
|
|
262
|
+
},
|
|
263
|
+
dryRun: false
|
|
264
|
+
};
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
/**
|
|
269
|
+
* @typedef {Object} Artifact
|
|
270
|
+
* @property {string} sourcePath - Current file path
|
|
271
|
+
* @property {string} targetSubdir - Target subdirectory name
|
|
272
|
+
* @property {string} filename - File name
|
|
273
|
+
*/
|
|
274
|
+
|
|
275
|
+
/**
|
|
276
|
+
* @typedef {Object} ArchiveReport
|
|
277
|
+
* @property {boolean} success - Whether archiving succeeded
|
|
278
|
+
* @property {Object[]} movedFiles - Files that were moved
|
|
279
|
+
* @property {Object[]} errors - Errors encountered
|
|
280
|
+
* @property {Object} summary - Summary statistics
|
|
281
|
+
* @property {boolean} dryRun - Whether this was a dry run
|
|
282
|
+
*/
|
|
283
|
+
|
|
284
|
+
module.exports = ArchiveTool;
|
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cleanup Tool
|
|
3
|
+
*
|
|
4
|
+
* Removes temporary and non-compliant documents
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
const fs = require('fs-extra');
|
|
8
|
+
const path = require('path');
|
|
9
|
+
const inquirer = require('inquirer');
|
|
10
|
+
const FileScanner = require('./file-scanner');
|
|
11
|
+
|
|
12
|
+
class CleanupTool {
|
|
13
|
+
constructor(projectPath, config) {
|
|
14
|
+
this.projectPath = projectPath;
|
|
15
|
+
this.config = config;
|
|
16
|
+
this.scanner = new FileScanner(projectPath);
|
|
17
|
+
this.deletedFiles = [];
|
|
18
|
+
this.errors = [];
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Execute cleanup operation
|
|
23
|
+
*
|
|
24
|
+
* @param {Object} options - Cleanup options
|
|
25
|
+
* @param {boolean} options.dryRun - Preview without deleting
|
|
26
|
+
* @param {boolean} options.interactive - Prompt for each file
|
|
27
|
+
* @param {string} options.spec - Specific Spec to clean
|
|
28
|
+
* @returns {Promise<CleanupReport>}
|
|
29
|
+
*/
|
|
30
|
+
async cleanup(options = {}) {
|
|
31
|
+
const filesToDelete = await this.identifyFilesToDelete(options.spec);
|
|
32
|
+
|
|
33
|
+
if (options.dryRun) {
|
|
34
|
+
return this.generateDryRunReport(filesToDelete);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
for (const file of filesToDelete) {
|
|
38
|
+
if (options.interactive) {
|
|
39
|
+
const shouldDelete = await this.promptForConfirmation(file);
|
|
40
|
+
if (!shouldDelete) continue;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
await this.deleteFile(file);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
return this.generateReport();
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Identify files to delete
|
|
51
|
+
*
|
|
52
|
+
* @param {string} specName - Optional specific Spec
|
|
53
|
+
* @returns {Promise<string[]>}
|
|
54
|
+
*/
|
|
55
|
+
async identifyFilesToDelete(specName = null) {
|
|
56
|
+
const files = [];
|
|
57
|
+
|
|
58
|
+
// Scan root directory
|
|
59
|
+
files.push(...await this.scanRootForTemporary());
|
|
60
|
+
|
|
61
|
+
// Scan Spec directories
|
|
62
|
+
if (specName) {
|
|
63
|
+
files.push(...await this.scanSpecForTemporary(specName));
|
|
64
|
+
} else {
|
|
65
|
+
files.push(...await this.scanAllSpecsForTemporary());
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
return files;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Scan root directory for temporary files
|
|
73
|
+
*
|
|
74
|
+
* @returns {Promise<string[]>}
|
|
75
|
+
*/
|
|
76
|
+
async scanRootForTemporary() {
|
|
77
|
+
const temporaryFiles = [];
|
|
78
|
+
const mdFiles = await this.scanner.findMarkdownFiles(this.projectPath);
|
|
79
|
+
const allowedFiles = this.config.rootAllowedFiles || [];
|
|
80
|
+
const temporaryPatterns = this.config.temporaryPatterns || [];
|
|
81
|
+
|
|
82
|
+
for (const filePath of mdFiles) {
|
|
83
|
+
const basename = path.basename(filePath);
|
|
84
|
+
|
|
85
|
+
// If not in allowed list, check if it matches temporary patterns
|
|
86
|
+
if (!allowedFiles.includes(basename)) {
|
|
87
|
+
// Check if it matches any temporary pattern
|
|
88
|
+
if (this.scanner.matchesPattern(filePath, temporaryPatterns)) {
|
|
89
|
+
temporaryFiles.push(filePath);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
return temporaryFiles;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Scan a specific Spec directory for temporary files
|
|
99
|
+
*
|
|
100
|
+
* @param {string} specName - Spec name
|
|
101
|
+
* @returns {Promise<string[]>}
|
|
102
|
+
*/
|
|
103
|
+
async scanSpecForTemporary(specName) {
|
|
104
|
+
const temporaryFiles = [];
|
|
105
|
+
const specPath = this.scanner.getSpecDirectory(specName);
|
|
106
|
+
|
|
107
|
+
// Check if Spec directory exists
|
|
108
|
+
if (!await this.scanner.exists(specPath)) {
|
|
109
|
+
return temporaryFiles;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
const mdFiles = await this.scanner.findMarkdownFiles(specPath);
|
|
113
|
+
const temporaryPatterns = this.config.temporaryPatterns || [];
|
|
114
|
+
const requiredFiles = ['requirements.md', 'design.md', 'tasks.md'];
|
|
115
|
+
|
|
116
|
+
for (const filePath of mdFiles) {
|
|
117
|
+
const basename = path.basename(filePath);
|
|
118
|
+
|
|
119
|
+
// Don't delete required files even if they match patterns
|
|
120
|
+
if (requiredFiles.includes(basename)) {
|
|
121
|
+
continue;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// Check if it matches any temporary pattern
|
|
125
|
+
if (this.scanner.matchesPattern(filePath, temporaryPatterns)) {
|
|
126
|
+
temporaryFiles.push(filePath);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
return temporaryFiles;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Scan all Spec directories for temporary files
|
|
135
|
+
*
|
|
136
|
+
* @returns {Promise<string[]>}
|
|
137
|
+
*/
|
|
138
|
+
async scanAllSpecsForTemporary() {
|
|
139
|
+
const temporaryFiles = [];
|
|
140
|
+
const specDirs = await this.scanner.findSpecDirectories();
|
|
141
|
+
|
|
142
|
+
for (const specDir of specDirs) {
|
|
143
|
+
const specName = path.basename(specDir);
|
|
144
|
+
const specTemporaryFiles = await this.scanSpecForTemporary(specName);
|
|
145
|
+
temporaryFiles.push(...specTemporaryFiles);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
return temporaryFiles;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Prompt user for confirmation to delete a file
|
|
153
|
+
*
|
|
154
|
+
* @param {string} filePath - Path to file
|
|
155
|
+
* @returns {Promise<boolean>}
|
|
156
|
+
*/
|
|
157
|
+
async promptForConfirmation(filePath) {
|
|
158
|
+
const relativePath = this.scanner.getRelativePath(filePath);
|
|
159
|
+
|
|
160
|
+
const answer = await inquirer.prompt([
|
|
161
|
+
{
|
|
162
|
+
type: 'confirm',
|
|
163
|
+
name: 'shouldDelete',
|
|
164
|
+
message: `Delete ${relativePath}?`,
|
|
165
|
+
default: false
|
|
166
|
+
}
|
|
167
|
+
]);
|
|
168
|
+
|
|
169
|
+
return answer.shouldDelete;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* Delete a file safely
|
|
174
|
+
*
|
|
175
|
+
* @param {string} filePath - Path to file
|
|
176
|
+
* @returns {Promise<void>}
|
|
177
|
+
*/
|
|
178
|
+
async deleteFile(filePath) {
|
|
179
|
+
try {
|
|
180
|
+
await fs.remove(filePath);
|
|
181
|
+
this.deletedFiles.push(filePath);
|
|
182
|
+
} catch (error) {
|
|
183
|
+
this.errors.push({
|
|
184
|
+
path: filePath,
|
|
185
|
+
error: error.message
|
|
186
|
+
});
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
* Generate dry run report
|
|
192
|
+
*
|
|
193
|
+
* @param {string[]} filesToDelete - Files that would be deleted
|
|
194
|
+
* @returns {CleanupReport}
|
|
195
|
+
*/
|
|
196
|
+
generateDryRunReport(filesToDelete) {
|
|
197
|
+
return {
|
|
198
|
+
success: true,
|
|
199
|
+
deletedFiles: filesToDelete,
|
|
200
|
+
errors: [],
|
|
201
|
+
summary: {
|
|
202
|
+
totalDeleted: filesToDelete.length,
|
|
203
|
+
totalErrors: 0
|
|
204
|
+
},
|
|
205
|
+
dryRun: true
|
|
206
|
+
};
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
* Generate cleanup report
|
|
211
|
+
*
|
|
212
|
+
* @returns {CleanupReport}
|
|
213
|
+
*/
|
|
214
|
+
generateReport() {
|
|
215
|
+
return {
|
|
216
|
+
success: this.errors.length === 0,
|
|
217
|
+
deletedFiles: this.deletedFiles,
|
|
218
|
+
errors: this.errors,
|
|
219
|
+
summary: {
|
|
220
|
+
totalDeleted: this.deletedFiles.length,
|
|
221
|
+
totalErrors: this.errors.length
|
|
222
|
+
},
|
|
223
|
+
dryRun: false
|
|
224
|
+
};
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
/**
|
|
229
|
+
* @typedef {Object} CleanupReport
|
|
230
|
+
* @property {boolean} success - Whether cleanup succeeded
|
|
231
|
+
* @property {string[]} deletedFiles - Files that were deleted
|
|
232
|
+
* @property {Object[]} errors - Errors encountered
|
|
233
|
+
* @property {Object} summary - Summary statistics
|
|
234
|
+
* @property {boolean} dryRun - Whether this was a dry run
|
|
235
|
+
*/
|
|
236
|
+
|
|
237
|
+
module.exports = CleanupTool;
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Configuration Manager
|
|
3
|
+
*
|
|
4
|
+
* Manages document governance configuration
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
const fs = require('fs-extra');
|
|
8
|
+
const path = require('path');
|
|
9
|
+
|
|
10
|
+
class ConfigManager {
|
|
11
|
+
constructor(projectPath) {
|
|
12
|
+
this.projectPath = projectPath;
|
|
13
|
+
this.configPath = path.join(projectPath, '.kiro/config/docs.json');
|
|
14
|
+
this.config = null;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Load configuration
|
|
19
|
+
*
|
|
20
|
+
* @returns {Promise<Object>}
|
|
21
|
+
*/
|
|
22
|
+
async load() {
|
|
23
|
+
if (await fs.pathExists(this.configPath)) {
|
|
24
|
+
try {
|
|
25
|
+
this.config = await fs.readJson(this.configPath);
|
|
26
|
+
|
|
27
|
+
// Validate and merge with defaults to ensure all required fields exist
|
|
28
|
+
const defaults = this.getDefaults();
|
|
29
|
+
this.config = { ...defaults, ...this.config };
|
|
30
|
+
|
|
31
|
+
} catch (error) {
|
|
32
|
+
console.warn('Config file corrupted, using defaults');
|
|
33
|
+
this.config = this.getDefaults();
|
|
34
|
+
}
|
|
35
|
+
} else {
|
|
36
|
+
this.config = this.getDefaults();
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
return this.config;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Get default configuration
|
|
44
|
+
*
|
|
45
|
+
* @returns {Object}
|
|
46
|
+
*/
|
|
47
|
+
getDefaults() {
|
|
48
|
+
return {
|
|
49
|
+
rootAllowedFiles: [
|
|
50
|
+
'README.md',
|
|
51
|
+
'README.zh.md',
|
|
52
|
+
'CHANGELOG.md',
|
|
53
|
+
'CONTRIBUTING.md'
|
|
54
|
+
],
|
|
55
|
+
specSubdirs: [
|
|
56
|
+
'reports',
|
|
57
|
+
'scripts',
|
|
58
|
+
'tests',
|
|
59
|
+
'results',
|
|
60
|
+
'docs'
|
|
61
|
+
],
|
|
62
|
+
temporaryPatterns: [
|
|
63
|
+
'*-SUMMARY.md',
|
|
64
|
+
'SESSION-*.md',
|
|
65
|
+
'*-COMPLETE.md',
|
|
66
|
+
'TEMP-*.md',
|
|
67
|
+
'WIP-*.md',
|
|
68
|
+
'MVP-*.md'
|
|
69
|
+
]
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Save configuration
|
|
75
|
+
*
|
|
76
|
+
* @param {Object} config - Configuration to save
|
|
77
|
+
* @returns {Promise<void>}
|
|
78
|
+
*/
|
|
79
|
+
async save(config) {
|
|
80
|
+
await fs.ensureDir(path.dirname(this.configPath));
|
|
81
|
+
await fs.writeJson(this.configPath, config, { spaces: 2 });
|
|
82
|
+
this.config = config;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Update a configuration value
|
|
87
|
+
*
|
|
88
|
+
* @param {string} key - Configuration key
|
|
89
|
+
* @param {any} value - New value
|
|
90
|
+
* @returns {Promise<void>}
|
|
91
|
+
*/
|
|
92
|
+
async set(key, value) {
|
|
93
|
+
if (!this.config) {
|
|
94
|
+
await this.load();
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
this.config[key] = value;
|
|
98
|
+
await this.save(this.config);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Get a configuration value
|
|
103
|
+
*
|
|
104
|
+
* @param {string} key - Configuration key
|
|
105
|
+
* @returns {any} - Configuration value
|
|
106
|
+
*/
|
|
107
|
+
get(key) {
|
|
108
|
+
if (!this.config) {
|
|
109
|
+
throw new Error('Configuration not loaded. Call load() first.');
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
return this.config[key];
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Get all configuration
|
|
117
|
+
*
|
|
118
|
+
* @returns {Object} - Complete configuration object
|
|
119
|
+
*/
|
|
120
|
+
getAll() {
|
|
121
|
+
if (!this.config) {
|
|
122
|
+
throw new Error('Configuration not loaded. Call load() first.');
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
return { ...this.config };
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Reset to defaults
|
|
130
|
+
*
|
|
131
|
+
* @returns {Promise<void>}
|
|
132
|
+
*/
|
|
133
|
+
async reset() {
|
|
134
|
+
this.config = this.getDefaults();
|
|
135
|
+
await this.save(this.config);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* Validate configuration structure
|
|
140
|
+
*
|
|
141
|
+
* @param {Object} config - Configuration to validate
|
|
142
|
+
* @returns {Object} - Validation result { valid: boolean, errors: string[] }
|
|
143
|
+
*/
|
|
144
|
+
validate(config) {
|
|
145
|
+
const errors = [];
|
|
146
|
+
|
|
147
|
+
// Check required fields
|
|
148
|
+
if (!config.rootAllowedFiles || !Array.isArray(config.rootAllowedFiles)) {
|
|
149
|
+
errors.push('rootAllowedFiles must be an array');
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
if (!config.specSubdirs || !Array.isArray(config.specSubdirs)) {
|
|
153
|
+
errors.push('specSubdirs must be an array');
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
if (!config.temporaryPatterns || !Array.isArray(config.temporaryPatterns)) {
|
|
157
|
+
errors.push('temporaryPatterns must be an array');
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// Check array contents
|
|
161
|
+
if (config.rootAllowedFiles && Array.isArray(config.rootAllowedFiles)) {
|
|
162
|
+
if (config.rootAllowedFiles.some(f => typeof f !== 'string')) {
|
|
163
|
+
errors.push('rootAllowedFiles must contain only strings');
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
if (config.specSubdirs && Array.isArray(config.specSubdirs)) {
|
|
168
|
+
if (config.specSubdirs.some(d => typeof d !== 'string')) {
|
|
169
|
+
errors.push('specSubdirs must contain only strings');
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
if (config.temporaryPatterns && Array.isArray(config.temporaryPatterns)) {
|
|
174
|
+
if (config.temporaryPatterns.some(p => typeof p !== 'string')) {
|
|
175
|
+
errors.push('temporaryPatterns must contain only strings');
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
return {
|
|
180
|
+
valid: errors.length === 0,
|
|
181
|
+
errors
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
module.exports = ConfigManager;
|