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,173 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PathUtils - Cross-platform path handling utilities
|
|
3
|
+
*
|
|
4
|
+
* Provides utilities for normalizing and converting paths across platforms.
|
|
5
|
+
* Implements the data atomicity principle by ensuring paths are stored
|
|
6
|
+
* in a consistent format and converted at runtime.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
const path = require('path');
|
|
10
|
+
const os = require('os');
|
|
11
|
+
|
|
12
|
+
class PathUtils {
|
|
13
|
+
/**
|
|
14
|
+
* Normalize path to use forward slashes for storage
|
|
15
|
+
*
|
|
16
|
+
* All paths are stored with forward slashes for cross-platform compatibility.
|
|
17
|
+
* This ensures the configuration files are portable across Windows, Linux, and Mac.
|
|
18
|
+
*
|
|
19
|
+
* @param {string} inputPath - Path to normalize
|
|
20
|
+
* @returns {string} Normalized path with forward slashes
|
|
21
|
+
*/
|
|
22
|
+
static normalize(inputPath) {
|
|
23
|
+
if (!inputPath || typeof inputPath !== 'string' || inputPath.trim().length === 0) {
|
|
24
|
+
throw new Error('Path must be a non-empty string');
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// Expand home directory first
|
|
28
|
+
const expandedPath = this.expandHome(inputPath);
|
|
29
|
+
|
|
30
|
+
// Convert to absolute path if not already
|
|
31
|
+
const absolutePath = path.isAbsolute(expandedPath)
|
|
32
|
+
? expandedPath
|
|
33
|
+
: path.resolve(expandedPath);
|
|
34
|
+
|
|
35
|
+
// Replace backslashes with forward slashes for storage
|
|
36
|
+
return absolutePath.replace(/\\/g, '/');
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Convert stored path to platform-specific format for runtime use
|
|
41
|
+
*
|
|
42
|
+
* @param {string} storedPath - Path with forward slashes (from storage)
|
|
43
|
+
* @returns {string} Platform-specific path
|
|
44
|
+
*/
|
|
45
|
+
static toPlatform(storedPath) {
|
|
46
|
+
if (storedPath === null) {
|
|
47
|
+
return null;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
if (!storedPath) {
|
|
51
|
+
return '';
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// Convert forward slashes to platform-specific separators
|
|
55
|
+
return storedPath.split('/').join(path.sep);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Expand home directory (~) to absolute path
|
|
60
|
+
*
|
|
61
|
+
* @param {string} inputPath - Path that may contain ~
|
|
62
|
+
* @returns {string} Expanded absolute path
|
|
63
|
+
*/
|
|
64
|
+
static expandHome(inputPath) {
|
|
65
|
+
if (inputPath === null) {
|
|
66
|
+
return null;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
if (inputPath === undefined) {
|
|
70
|
+
return undefined;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
if (!inputPath) {
|
|
74
|
+
return '';
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
if (inputPath.startsWith('~/') || inputPath.startsWith('~\\') || inputPath === '~') {
|
|
78
|
+
return path.join(os.homedir(), inputPath.slice(2));
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
return inputPath;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Get the default kse configuration directory
|
|
86
|
+
*
|
|
87
|
+
* @returns {string} Path to ~/.kse directory
|
|
88
|
+
*/
|
|
89
|
+
static getConfigDir() {
|
|
90
|
+
return path.join(os.homedir(), '.kse');
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Get the default workspace state file path
|
|
95
|
+
*
|
|
96
|
+
* @returns {string} Path to ~/.kse/workspace-state.json
|
|
97
|
+
*/
|
|
98
|
+
static getWorkspaceStatePath() {
|
|
99
|
+
return path.join(this.getConfigDir(), 'workspace-state.json');
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Check if a path is within another path
|
|
104
|
+
*
|
|
105
|
+
* @param {string} childPath - Path to check
|
|
106
|
+
* @param {string} parentPath - Parent path
|
|
107
|
+
* @returns {boolean} True if childPath is within parentPath
|
|
108
|
+
*/
|
|
109
|
+
static isWithin(childPath, parentPath) {
|
|
110
|
+
const normalizedChild = this.normalize(childPath);
|
|
111
|
+
const normalizedParent = this.normalize(parentPath);
|
|
112
|
+
|
|
113
|
+
return normalizedChild === normalizedParent ||
|
|
114
|
+
normalizedChild.startsWith(normalizedParent + '/');
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Get relative path from parent to child
|
|
119
|
+
*
|
|
120
|
+
* @param {string} from - Parent path
|
|
121
|
+
* @param {string} to - Child path
|
|
122
|
+
* @returns {string} Relative path with forward slashes
|
|
123
|
+
*/
|
|
124
|
+
static relative(from, to) {
|
|
125
|
+
const relativePath = path.relative(from, to);
|
|
126
|
+
return relativePath.replace(/\\/g, '/');
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Join path segments
|
|
131
|
+
*
|
|
132
|
+
* @param {...string} segments - Path segments to join
|
|
133
|
+
* @returns {string} Joined path with forward slashes
|
|
134
|
+
*/
|
|
135
|
+
static join(...segments) {
|
|
136
|
+
const joinedPath = path.join(...segments);
|
|
137
|
+
return joinedPath.replace(/\\/g, '/');
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Get directory name from path
|
|
142
|
+
*
|
|
143
|
+
* @param {string} filePath - File path
|
|
144
|
+
* @returns {string} Directory name with forward slashes
|
|
145
|
+
*/
|
|
146
|
+
static dirname(filePath) {
|
|
147
|
+
const dirPath = path.dirname(filePath);
|
|
148
|
+
return dirPath.replace(/\\/g, '/');
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Get base name from path
|
|
153
|
+
*
|
|
154
|
+
* @param {string} filePath - File path
|
|
155
|
+
* @param {string} ext - Optional extension to remove
|
|
156
|
+
* @returns {string} Base name
|
|
157
|
+
*/
|
|
158
|
+
static basename(filePath, ext) {
|
|
159
|
+
return path.basename(filePath, ext);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Get file extension from path
|
|
164
|
+
*
|
|
165
|
+
* @param {string} filePath - File path
|
|
166
|
+
* @returns {string} File extension (including the dot)
|
|
167
|
+
*/
|
|
168
|
+
static extname(filePath) {
|
|
169
|
+
return path.extname(filePath);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
module.exports = PathUtils;
|
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
const fs = require('fs-extra');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* WorkspaceContextResolver - Determines the active workspace for command execution
|
|
6
|
+
*
|
|
7
|
+
* Implements workspace resolution priority logic:
|
|
8
|
+
* 1. Explicit --workspace parameter (highest priority)
|
|
9
|
+
* 2. Current directory matches a registered workspace path
|
|
10
|
+
* 3. Active workspace from global config
|
|
11
|
+
* 4. Error: No workspace context available (lowest priority)
|
|
12
|
+
*
|
|
13
|
+
* This class is the central component for workspace context detection and resolution.
|
|
14
|
+
*/
|
|
15
|
+
class WorkspaceContextResolver {
|
|
16
|
+
/**
|
|
17
|
+
* Create a new WorkspaceContextResolver instance
|
|
18
|
+
*
|
|
19
|
+
* @param {WorkspaceRegistry} registry - Workspace registry instance
|
|
20
|
+
* @param {GlobalConfig} config - Global configuration instance
|
|
21
|
+
*/
|
|
22
|
+
constructor(registry, config) {
|
|
23
|
+
this.registry = registry;
|
|
24
|
+
this.config = config;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Resolve workspace using priority rules
|
|
29
|
+
*
|
|
30
|
+
* Priority order:
|
|
31
|
+
* 1. Explicit workspace parameter
|
|
32
|
+
* 2. Current directory match
|
|
33
|
+
* 3. Active workspace from config
|
|
34
|
+
* 4. Error if no context available
|
|
35
|
+
*
|
|
36
|
+
* @param {string|null} explicitWorkspace - Explicit --workspace parameter value
|
|
37
|
+
* @param {string|null} currentDir - Current directory (defaults to process.cwd())
|
|
38
|
+
* @returns {Promise<Workspace|null>} Resolved workspace or null
|
|
39
|
+
* @throws {Error} If explicit workspace doesn't exist or no context available
|
|
40
|
+
*/
|
|
41
|
+
async resolveWorkspace(explicitWorkspace = null, currentDir = null) {
|
|
42
|
+
// Priority 1: Explicit --workspace parameter
|
|
43
|
+
if (explicitWorkspace) {
|
|
44
|
+
const workspace = await this.registry.getWorkspace(explicitWorkspace);
|
|
45
|
+
if (!workspace) {
|
|
46
|
+
const available = await this.getAvailableWorkspaceNames();
|
|
47
|
+
throw new Error(
|
|
48
|
+
`Workspace "${explicitWorkspace}" does not exist.\n` +
|
|
49
|
+
`Available workspaces: ${available.join(', ') || 'none'}`
|
|
50
|
+
);
|
|
51
|
+
}
|
|
52
|
+
return workspace;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// Priority 2: Current directory matches a registered workspace
|
|
56
|
+
const targetDir = currentDir || process.cwd();
|
|
57
|
+
const workspaceFromPath = await this.detectWorkspaceFromPath(targetDir);
|
|
58
|
+
if (workspaceFromPath) {
|
|
59
|
+
return workspaceFromPath;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// Priority 3: Active workspace from config
|
|
63
|
+
const activeWorkspaceName = await this.config.getActiveWorkspace();
|
|
64
|
+
if (activeWorkspaceName) {
|
|
65
|
+
const activeWorkspace = await this.registry.getWorkspace(activeWorkspaceName);
|
|
66
|
+
if (activeWorkspace) {
|
|
67
|
+
return activeWorkspace;
|
|
68
|
+
}
|
|
69
|
+
// Active workspace no longer exists, clear it
|
|
70
|
+
await this.config.clearActiveWorkspace();
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// Priority 4: No workspace context available
|
|
74
|
+
return null;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Detect workspace from a given path
|
|
79
|
+
*
|
|
80
|
+
* Searches the registry for a workspace that contains the given path.
|
|
81
|
+
*
|
|
82
|
+
* @param {string} targetPath - Path to search for
|
|
83
|
+
* @returns {Promise<Workspace|null>} Workspace containing the path, or null
|
|
84
|
+
*/
|
|
85
|
+
async detectWorkspaceFromPath(targetPath) {
|
|
86
|
+
return await this.registry.findWorkspaceByPath(targetPath);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Check if a directory is a valid kse project directory
|
|
91
|
+
*
|
|
92
|
+
* @param {string} dirPath - Directory path to check
|
|
93
|
+
* @returns {Promise<boolean>} True if directory contains .kiro/ structure
|
|
94
|
+
*/
|
|
95
|
+
async isValidKseDirectory(dirPath) {
|
|
96
|
+
try {
|
|
97
|
+
const kiroPath = path.join(dirPath, '.kiro');
|
|
98
|
+
const exists = await fs.pathExists(kiroPath);
|
|
99
|
+
|
|
100
|
+
if (!exists) {
|
|
101
|
+
return false;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
const stats = await fs.stat(kiroPath);
|
|
105
|
+
return stats.isDirectory();
|
|
106
|
+
} catch (error) {
|
|
107
|
+
return false;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Get the currently active workspace from config
|
|
113
|
+
*
|
|
114
|
+
* @returns {Promise<Workspace|null>} Active workspace or null
|
|
115
|
+
*/
|
|
116
|
+
async getActiveWorkspace() {
|
|
117
|
+
const activeWorkspaceName = await this.config.getActiveWorkspace();
|
|
118
|
+
if (!activeWorkspaceName) {
|
|
119
|
+
return null;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
const workspace = await this.registry.getWorkspace(activeWorkspaceName);
|
|
123
|
+
if (!workspace) {
|
|
124
|
+
// Active workspace no longer exists, clear it
|
|
125
|
+
await this.config.clearActiveWorkspace();
|
|
126
|
+
return null;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
return workspace;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Set the active workspace in config
|
|
134
|
+
*
|
|
135
|
+
* @param {string} name - Workspace name
|
|
136
|
+
* @returns {Promise<void>}
|
|
137
|
+
* @throws {Error} If workspace doesn't exist
|
|
138
|
+
*/
|
|
139
|
+
async setActiveWorkspace(name) {
|
|
140
|
+
const workspace = await this.registry.getWorkspace(name);
|
|
141
|
+
if (!workspace) {
|
|
142
|
+
const available = await this.getAvailableWorkspaceNames();
|
|
143
|
+
throw new Error(
|
|
144
|
+
`Workspace "${name}" does not exist.\n` +
|
|
145
|
+
`Available workspaces: ${available.join(', ') || 'none'}`
|
|
146
|
+
);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
await this.config.setActiveWorkspace(name);
|
|
150
|
+
await this.registry.updateLastAccessed(name);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* Clear the active workspace
|
|
155
|
+
*
|
|
156
|
+
* @returns {Promise<void>}
|
|
157
|
+
*/
|
|
158
|
+
async clearActiveWorkspace() {
|
|
159
|
+
await this.config.clearActiveWorkspace();
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Get list of available workspace names
|
|
164
|
+
*
|
|
165
|
+
* @private
|
|
166
|
+
* @returns {Promise<Array<string>>} Array of workspace names
|
|
167
|
+
*/
|
|
168
|
+
async getAvailableWorkspaceNames() {
|
|
169
|
+
const workspaces = await this.registry.listWorkspaces();
|
|
170
|
+
return workspaces.map(ws => ws.name);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* Check if current directory should prompt for workspace registration
|
|
175
|
+
*
|
|
176
|
+
* Returns true if:
|
|
177
|
+
* - Current directory is a valid kse project
|
|
178
|
+
* - Current directory is not within any registered workspace
|
|
179
|
+
*
|
|
180
|
+
* @param {string|null} currentDir - Current directory (defaults to process.cwd())
|
|
181
|
+
* @returns {Promise<boolean>} True if should prompt for registration
|
|
182
|
+
*/
|
|
183
|
+
async shouldPromptForRegistration(currentDir = null) {
|
|
184
|
+
const targetDir = currentDir || process.cwd();
|
|
185
|
+
|
|
186
|
+
// Check if it's a valid kse directory
|
|
187
|
+
const isValid = await this.isValidKseDirectory(targetDir);
|
|
188
|
+
if (!isValid) {
|
|
189
|
+
return false;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
// Check if it's already registered
|
|
193
|
+
const workspace = await this.detectWorkspaceFromPath(targetDir);
|
|
194
|
+
return workspace === null;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* Resolve workspace with error handling for commands that require a workspace
|
|
199
|
+
*
|
|
200
|
+
* This is a convenience method that throws a helpful error if no workspace
|
|
201
|
+
* context can be resolved.
|
|
202
|
+
*
|
|
203
|
+
* @param {string|null} explicitWorkspace - Explicit --workspace parameter value
|
|
204
|
+
* @param {string|null} currentDir - Current directory (defaults to process.cwd())
|
|
205
|
+
* @returns {Promise<Workspace>} Resolved workspace
|
|
206
|
+
* @throws {Error} If no workspace context available
|
|
207
|
+
*/
|
|
208
|
+
async resolveWorkspaceOrError(explicitWorkspace = null, currentDir = null) {
|
|
209
|
+
const workspace = await this.resolveWorkspace(explicitWorkspace, currentDir);
|
|
210
|
+
|
|
211
|
+
if (!workspace) {
|
|
212
|
+
const targetDir = currentDir || process.cwd();
|
|
213
|
+
const isValid = await this.isValidKseDirectory(targetDir);
|
|
214
|
+
|
|
215
|
+
if (isValid) {
|
|
216
|
+
throw new Error(
|
|
217
|
+
'No workspace context available.\n' +
|
|
218
|
+
'The current directory is a valid kse project but not registered as a workspace.\n' +
|
|
219
|
+
'Action Required: Run "kse workspace create <name>" to register this directory.'
|
|
220
|
+
);
|
|
221
|
+
} else {
|
|
222
|
+
const available = await this.getAvailableWorkspaceNames();
|
|
223
|
+
if (available.length > 0) {
|
|
224
|
+
throw new Error(
|
|
225
|
+
'No workspace context available.\n' +
|
|
226
|
+
'The current directory is not a kse project and no active workspace is set.\n' +
|
|
227
|
+
`Action Required: Run "kse workspace switch <name>" or use --workspace parameter.\n` +
|
|
228
|
+
`Available workspaces: ${available.join(', ')}`
|
|
229
|
+
);
|
|
230
|
+
} else {
|
|
231
|
+
throw new Error(
|
|
232
|
+
'No workspace context available.\n' +
|
|
233
|
+
'No workspaces are registered and the current directory is not a kse project.\n' +
|
|
234
|
+
'Action Required: Run "kse workspace create <name>" to register a workspace.'
|
|
235
|
+
);
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
return workspace;
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
module.exports = WorkspaceContextResolver;
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
const WorkspaceStateManager = require('./workspace-state-manager');
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* WorkspaceRegistry - Facade for WorkspaceStateManager
|
|
5
|
+
*
|
|
6
|
+
* Provides backward-compatible API for workspace registry operations.
|
|
7
|
+
* All operations are delegated to WorkspaceStateManager which implements
|
|
8
|
+
* the Data Atomicity Principle (single source of truth).
|
|
9
|
+
*
|
|
10
|
+
* @deprecated This class is a compatibility layer. New code should use
|
|
11
|
+
* WorkspaceStateManager directly.
|
|
12
|
+
*/
|
|
13
|
+
class WorkspaceRegistry {
|
|
14
|
+
/**
|
|
15
|
+
* Create a new WorkspaceRegistry instance
|
|
16
|
+
*
|
|
17
|
+
* @param {string} configPath - Path to workspace-state.json (optional)
|
|
18
|
+
*/
|
|
19
|
+
constructor(configPath = null) {
|
|
20
|
+
// Delegate to WorkspaceStateManager
|
|
21
|
+
this.stateManager = new WorkspaceStateManager(configPath);
|
|
22
|
+
// Expose configPath for backward compatibility
|
|
23
|
+
this.configPath = this.stateManager.statePath;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Get the default configuration file path
|
|
28
|
+
*
|
|
29
|
+
* @returns {string} Path to ~/.kse/workspace-state.json
|
|
30
|
+
* @deprecated Use WorkspaceStateManager.getDefaultStatePath() instead
|
|
31
|
+
*/
|
|
32
|
+
getDefaultConfigPath() {
|
|
33
|
+
return this.stateManager.getDefaultStatePath();
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Load workspace registry from disk
|
|
38
|
+
*
|
|
39
|
+
* @returns {Promise<boolean>} True if loaded successfully
|
|
40
|
+
*/
|
|
41
|
+
async load() {
|
|
42
|
+
return await this.stateManager.load();
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Save workspace registry to disk
|
|
47
|
+
*
|
|
48
|
+
* @returns {Promise<boolean>} True if saved successfully
|
|
49
|
+
*/
|
|
50
|
+
async save() {
|
|
51
|
+
return await this.stateManager.save();
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Ensure registry is loaded before operations
|
|
56
|
+
*
|
|
57
|
+
* @private
|
|
58
|
+
*/
|
|
59
|
+
async ensureLoaded() {
|
|
60
|
+
await this.stateManager.ensureLoaded();
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Validate that a path is a valid kse project directory
|
|
65
|
+
*
|
|
66
|
+
* @param {string} workspacePath - Path to validate
|
|
67
|
+
* @returns {Promise<boolean>} True if valid kse project
|
|
68
|
+
*/
|
|
69
|
+
async validateWorkspacePath(workspacePath) {
|
|
70
|
+
const fs = require('fs-extra');
|
|
71
|
+
const path = require('path');
|
|
72
|
+
|
|
73
|
+
try {
|
|
74
|
+
// Check if path exists
|
|
75
|
+
const exists = await fs.pathExists(workspacePath);
|
|
76
|
+
if (!exists) {
|
|
77
|
+
return false;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// Check if it's a directory
|
|
81
|
+
const stats = await fs.stat(workspacePath);
|
|
82
|
+
if (!stats.isDirectory()) {
|
|
83
|
+
return false;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// Check if .kiro directory exists
|
|
87
|
+
const kiroPath = path.join(workspacePath, '.kiro');
|
|
88
|
+
const kiroExists = await fs.pathExists(kiroPath);
|
|
89
|
+
|
|
90
|
+
return kiroExists;
|
|
91
|
+
} catch (error) {
|
|
92
|
+
return false;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Create a new workspace entry
|
|
98
|
+
*
|
|
99
|
+
* @param {string} name - Unique workspace name
|
|
100
|
+
* @param {string} workspacePath - Path to workspace directory
|
|
101
|
+
* @returns {Promise<Workspace>} Created workspace
|
|
102
|
+
* @throws {Error} If name already exists or path is invalid
|
|
103
|
+
*/
|
|
104
|
+
async createWorkspace(name, workspacePath) {
|
|
105
|
+
return await this.stateManager.createWorkspace(name, workspacePath);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Get a workspace by name
|
|
110
|
+
*
|
|
111
|
+
* @param {string} name - Workspace name
|
|
112
|
+
* @returns {Promise<Workspace|null>} Workspace or null if not found
|
|
113
|
+
*/
|
|
114
|
+
async getWorkspace(name) {
|
|
115
|
+
return await this.stateManager.getWorkspace(name);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* List all registered workspaces
|
|
120
|
+
*
|
|
121
|
+
* @returns {Promise<Array<Workspace>>} Array of workspaces
|
|
122
|
+
*/
|
|
123
|
+
async listWorkspaces() {
|
|
124
|
+
return await this.stateManager.listWorkspaces();
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Remove a workspace from the registry
|
|
129
|
+
*
|
|
130
|
+
* @param {string} name - Workspace name
|
|
131
|
+
* @returns {Promise<boolean>} True if removed, false if not found
|
|
132
|
+
*/
|
|
133
|
+
async removeWorkspace(name) {
|
|
134
|
+
return await this.stateManager.removeWorkspace(name);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Update the last accessed timestamp for a workspace
|
|
139
|
+
*
|
|
140
|
+
* @param {string} name - Workspace name
|
|
141
|
+
* @returns {Promise<boolean>} True if updated, false if not found
|
|
142
|
+
*/
|
|
143
|
+
async updateLastAccessed(name) {
|
|
144
|
+
await this.ensureLoaded();
|
|
145
|
+
|
|
146
|
+
const workspace = await this.stateManager.getWorkspace(name);
|
|
147
|
+
if (!workspace) {
|
|
148
|
+
return false;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
workspace.updateLastAccessed();
|
|
152
|
+
await this.stateManager.save();
|
|
153
|
+
|
|
154
|
+
return true;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Find workspace that contains the given path
|
|
159
|
+
*
|
|
160
|
+
* @param {string} targetPath - Path to search for
|
|
161
|
+
* @returns {Promise<Workspace|null>} Workspace containing the path, or null
|
|
162
|
+
*/
|
|
163
|
+
async findWorkspaceByPath(targetPath) {
|
|
164
|
+
return await this.stateManager.findWorkspaceByPath(targetPath);
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* Check if a workspace name exists
|
|
169
|
+
*
|
|
170
|
+
* @param {string} name - Workspace name
|
|
171
|
+
* @returns {Promise<boolean>} True if exists
|
|
172
|
+
*/
|
|
173
|
+
async hasWorkspace(name) {
|
|
174
|
+
return await this.stateManager.hasWorkspace(name);
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* Get count of registered workspaces
|
|
179
|
+
*
|
|
180
|
+
* @returns {Promise<number>} Number of workspaces
|
|
181
|
+
*/
|
|
182
|
+
async count() {
|
|
183
|
+
return await this.stateManager.count();
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
* Clear all workspaces (for testing purposes)
|
|
188
|
+
*
|
|
189
|
+
* @returns {Promise<void>}
|
|
190
|
+
*/
|
|
191
|
+
async clear() {
|
|
192
|
+
await this.stateManager.clear();
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
module.exports = WorkspaceRegistry;
|