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.
Files changed (336) hide show
  1. package/CHANGELOG.md +2513 -0
  2. package/LICENSE +21 -0
  3. package/README.md +765 -0
  4. package/README.zh.md +630 -0
  5. package/bin/kiro-spec-engine.js +796 -0
  6. package/bin/kse.js +3 -0
  7. package/bin/sce.js +3 -0
  8. package/bin/sco.js +3 -0
  9. package/docs/331-poc-adaptation-roadmap.md +156 -0
  10. package/docs/331-poc-dual-track-integration-guide.md +120 -0
  11. package/docs/331-poc-weekly-delivery-checklist.md +52 -0
  12. package/docs/OFFLINE_INSTALL.md +96 -0
  13. package/docs/README.md +279 -0
  14. package/docs/adopt-migration-guide.md +599 -0
  15. package/docs/adoption-guide.md +616 -0
  16. package/docs/agent-hooks-analysis.md +815 -0
  17. package/docs/architecture.md +733 -0
  18. package/docs/articles/ai-driven-development-philosophy-and-practice-review.md +208 -0
  19. package/docs/articles/ai-driven-development-philosophy-and-practice.en.md +459 -0
  20. package/docs/articles/ai-driven-development-philosophy-and-practice.md +492 -0
  21. package/docs/autonomous-control-guide.md +851 -0
  22. package/docs/command-reference.md +1368 -0
  23. package/docs/community.md +115 -0
  24. package/docs/cross-tool-guide.md +555 -0
  25. package/docs/developer-guide.md +619 -0
  26. package/docs/document-governance.md +865 -0
  27. package/docs/environment-management-guide.md +526 -0
  28. package/docs/examples/add-export-command/design.md +194 -0
  29. package/docs/examples/add-export-command/requirements.md +110 -0
  30. package/docs/examples/add-export-command/tasks.md +88 -0
  31. package/docs/examples/add-rest-api/design.md +855 -0
  32. package/docs/examples/add-rest-api/requirements.md +323 -0
  33. package/docs/examples/add-rest-api/tasks.md +355 -0
  34. package/docs/examples/add-user-dashboard/design.md +192 -0
  35. package/docs/examples/add-user-dashboard/requirements.md +143 -0
  36. package/docs/examples/add-user-dashboard/tasks.md +91 -0
  37. package/docs/faq.md +697 -0
  38. package/docs/handoffs/evidence/ontology/moqui-template-baseline-2026-02-17-232922.json +156 -0
  39. package/docs/handoffs/evidence/ontology/moqui-template-baseline-2026-02-17-232922.md +24 -0
  40. package/docs/images/wechat-qr.png +0 -0
  41. package/docs/integration-modes.md +529 -0
  42. package/docs/integration-philosophy.md +313 -0
  43. package/docs/knowledge-management-guide.md +263 -0
  44. package/docs/manual-workflows-guide.md +418 -0
  45. package/docs/moqui-capability-matrix.md +73 -0
  46. package/docs/moqui-template-core-library-playbook.md +109 -0
  47. package/docs/multi-agent-coordination-guide.md +553 -0
  48. package/docs/multi-repo-management-guide.md +1344 -0
  49. package/docs/quick-start-with-ai-tools.md +375 -0
  50. package/docs/quick-start.md +146 -0
  51. package/docs/release-checklist.md +121 -0
  52. package/docs/releases/README.md +13 -0
  53. package/docs/releases/v1.46.2-validation.md +45 -0
  54. package/docs/releases/v1.46.2.md +50 -0
  55. package/docs/scene-runtime-guide.md +347 -0
  56. package/docs/spec-collaboration-guide.md +369 -0
  57. package/docs/spec-locking-guide.md +225 -0
  58. package/docs/spec-numbering-guide.md +348 -0
  59. package/docs/spec-workflow.md +519 -0
  60. package/docs/steering-strategy-guide.md +196 -0
  61. package/docs/team-collaboration-guide.md +465 -0
  62. package/docs/testing-strategy.md +272 -0
  63. package/docs/tools/claude-guide.md +654 -0
  64. package/docs/tools/cursor-guide.md +706 -0
  65. package/docs/tools/generic-guide.md +446 -0
  66. package/docs/tools/kiro-guide.md +308 -0
  67. package/docs/tools/vscode-guide.md +445 -0
  68. package/docs/tools/windsurf-guide.md +391 -0
  69. package/docs/troubleshooting.md +1135 -0
  70. package/docs/upgrade-guide.md +639 -0
  71. package/docs/value-observability-guide.md +127 -0
  72. package/docs/zh/README.md +341 -0
  73. package/docs/zh/quick-start.md +764 -0
  74. package/docs/zh/release-checklist.md +121 -0
  75. package/docs/zh/releases/README.md +13 -0
  76. package/docs/zh/releases/v1.46.2-validation.md +45 -0
  77. package/docs/zh/releases/v1.46.2.md +50 -0
  78. package/docs/zh/spec-numbering-guide.md +348 -0
  79. package/docs/zh/tools/claude-guide.md +349 -0
  80. package/docs/zh/tools/cursor-guide.md +281 -0
  81. package/docs/zh/tools/generic-guide.md +499 -0
  82. package/docs/zh/tools/kiro-guide.md +342 -0
  83. package/docs/zh/tools/vscode-guide.md +449 -0
  84. package/docs/zh/tools/windsurf-guide.md +378 -0
  85. package/docs/zh/value-observability-guide.md +127 -0
  86. package/docs//344/272/244/344/273/230/346/270/205/345/215/225.md +75 -0
  87. package/lib/adoption/adoption-logger.js +487 -0
  88. package/lib/adoption/adoption-strategy.js +538 -0
  89. package/lib/adoption/backup-manager.js +420 -0
  90. package/lib/adoption/conflict-resolver.js +410 -0
  91. package/lib/adoption/detection-engine.js +275 -0
  92. package/lib/adoption/diff-viewer.js +226 -0
  93. package/lib/adoption/error-formatter.js +509 -0
  94. package/lib/adoption/file-classifier.js +385 -0
  95. package/lib/adoption/progress-reporter.js +534 -0
  96. package/lib/adoption/smart-orchestrator.js +470 -0
  97. package/lib/adoption/strategy-selector.js +218 -0
  98. package/lib/adoption/summary-generator.js +493 -0
  99. package/lib/adoption/template-sync.js +605 -0
  100. package/lib/auto/autonomous-engine.js +485 -0
  101. package/lib/auto/checkpoint-manager.js +300 -0
  102. package/lib/auto/close-loop-runner.js +2476 -0
  103. package/lib/auto/config-schema.js +176 -0
  104. package/lib/auto/decision-engine.js +344 -0
  105. package/lib/auto/error-recovery-manager.js +580 -0
  106. package/lib/auto/goal-decomposer.js +278 -0
  107. package/lib/auto/progress-tracker.js +502 -0
  108. package/lib/auto/safety-manager.js +186 -0
  109. package/lib/auto/semantic-decomposer.js +137 -0
  110. package/lib/auto/state-manager.js +126 -0
  111. package/lib/auto/task-queue-manager.js +340 -0
  112. package/lib/backup/backup-system.js +372 -0
  113. package/lib/backup/selective-backup.js +207 -0
  114. package/lib/collab/agent-registry.js +240 -0
  115. package/lib/collab/collab-manager.js +285 -0
  116. package/lib/collab/contract-manager.js +320 -0
  117. package/lib/collab/coordinator.js +370 -0
  118. package/lib/collab/dependency-manager.js +280 -0
  119. package/lib/collab/index.js +20 -0
  120. package/lib/collab/integration-manager.js +202 -0
  121. package/lib/collab/merge-coordinator.js +252 -0
  122. package/lib/collab/metadata-manager.js +233 -0
  123. package/lib/collab/multi-agent-config.js +120 -0
  124. package/lib/collab/spec-lifecycle-manager.js +304 -0
  125. package/lib/collab/sync-barrier.js +88 -0
  126. package/lib/collab/visualizer.js +208 -0
  127. package/lib/commands/adopt.js +749 -0
  128. package/lib/commands/auto.js +19559 -0
  129. package/lib/commands/collab.js +275 -0
  130. package/lib/commands/context.js +99 -0
  131. package/lib/commands/docs.js +808 -0
  132. package/lib/commands/doctor.js +273 -0
  133. package/lib/commands/env.js +420 -0
  134. package/lib/commands/knowledge.js +309 -0
  135. package/lib/commands/lock.js +235 -0
  136. package/lib/commands/ops.js +409 -0
  137. package/lib/commands/orchestrate.js +446 -0
  138. package/lib/commands/prompt.js +105 -0
  139. package/lib/commands/repo.js +118 -0
  140. package/lib/commands/rollback.js +219 -0
  141. package/lib/commands/scene.js +15549 -0
  142. package/lib/commands/spec-bootstrap.js +147 -0
  143. package/lib/commands/spec-gate.js +157 -0
  144. package/lib/commands/spec-pipeline.js +205 -0
  145. package/lib/commands/status.js +321 -0
  146. package/lib/commands/task.js +199 -0
  147. package/lib/commands/templates.js +654 -0
  148. package/lib/commands/upgrade.js +231 -0
  149. package/lib/commands/value.js +569 -0
  150. package/lib/commands/watch.js +684 -0
  151. package/lib/commands/workflows.js +240 -0
  152. package/lib/commands/workspace-multi.js +325 -0
  153. package/lib/commands/workspace.js +189 -0
  154. package/lib/context/context-exporter.js +378 -0
  155. package/lib/context/prompt-generator.js +482 -0
  156. package/lib/data/moqui-capability-lexicon.json +45 -0
  157. package/lib/environment/backup-system.js +189 -0
  158. package/lib/environment/environment-manager.js +379 -0
  159. package/lib/environment/environment-registry.js +168 -0
  160. package/lib/gitignore/gitignore-backup.js +229 -0
  161. package/lib/gitignore/gitignore-detector.js +239 -0
  162. package/lib/gitignore/gitignore-integration.js +267 -0
  163. package/lib/gitignore/gitignore-transformer.js +193 -0
  164. package/lib/gitignore/layered-rules-template.js +42 -0
  165. package/lib/governance/archive-tool.js +284 -0
  166. package/lib/governance/cleanup-tool.js +237 -0
  167. package/lib/governance/config-manager.js +186 -0
  168. package/lib/governance/diagnostic-engine.js +271 -0
  169. package/lib/governance/doc-reference-checker.js +200 -0
  170. package/lib/governance/execution-logger.js +243 -0
  171. package/lib/governance/file-scanner.js +285 -0
  172. package/lib/governance/hooks-manager.js +333 -0
  173. package/lib/governance/reporter.js +337 -0
  174. package/lib/governance/validation-engine.js +181 -0
  175. package/lib/i18n.js +79 -0
  176. package/lib/knowledge/entry-manager.js +208 -0
  177. package/lib/knowledge/index-manager.js +261 -0
  178. package/lib/knowledge/knowledge-manager.js +273 -0
  179. package/lib/knowledge/template-manager.js +191 -0
  180. package/lib/lock/index.js +21 -0
  181. package/lib/lock/lock-file.js +192 -0
  182. package/lib/lock/lock-manager.js +321 -0
  183. package/lib/lock/machine-identifier.js +135 -0
  184. package/lib/lock/steering-file-lock.js +207 -0
  185. package/lib/lock/task-lock-manager.js +345 -0
  186. package/lib/operations/audit-logger.js +293 -0
  187. package/lib/operations/feedback-manager.js +1147 -0
  188. package/lib/operations/index.js +23 -0
  189. package/lib/operations/models/index.js +170 -0
  190. package/lib/operations/operations-manager.js +151 -0
  191. package/lib/operations/operations-validator.js +280 -0
  192. package/lib/operations/permission-manager.js +354 -0
  193. package/lib/operations/template-loader.js +143 -0
  194. package/lib/orchestrator/agent-spawner.js +629 -0
  195. package/lib/orchestrator/bootstrap-prompt-builder.js +236 -0
  196. package/lib/orchestrator/index.js +19 -0
  197. package/lib/orchestrator/orchestration-engine.js +1270 -0
  198. package/lib/orchestrator/orchestrator-config.js +173 -0
  199. package/lib/orchestrator/status-monitor.js +591 -0
  200. package/lib/python-checker.js +209 -0
  201. package/lib/repo/config-manager.js +580 -0
  202. package/lib/repo/errors/config-error.js +13 -0
  203. package/lib/repo/errors/git-error.js +15 -0
  204. package/lib/repo/errors/repo-error.js +14 -0
  205. package/lib/repo/git-operations.js +181 -0
  206. package/lib/repo/handlers/.gitkeep +1 -0
  207. package/lib/repo/handlers/exec-handler.js +155 -0
  208. package/lib/repo/handlers/health-handler.js +169 -0
  209. package/lib/repo/handlers/init-handler.js +197 -0
  210. package/lib/repo/handlers/status-handler.js +176 -0
  211. package/lib/repo/output-formatter.js +184 -0
  212. package/lib/repo/path-resolver.js +178 -0
  213. package/lib/repo/repo-manager.js +514 -0
  214. package/lib/scene-runtime/audit-emitter.js +59 -0
  215. package/lib/scene-runtime/binding-plugin-loader.js +351 -0
  216. package/lib/scene-runtime/binding-registry.js +349 -0
  217. package/lib/scene-runtime/eval-bridge.js +44 -0
  218. package/lib/scene-runtime/index.js +19 -0
  219. package/lib/scene-runtime/moqui-adapter.js +620 -0
  220. package/lib/scene-runtime/moqui-client.js +606 -0
  221. package/lib/scene-runtime/moqui-extractor.js +2029 -0
  222. package/lib/scene-runtime/plan-compiler.js +208 -0
  223. package/lib/scene-runtime/policy-gate.js +58 -0
  224. package/lib/scene-runtime/runtime-executor.js +358 -0
  225. package/lib/scene-runtime/scene-loader.js +96 -0
  226. package/lib/scene-runtime/scene-ontology.js +959 -0
  227. package/lib/scene-runtime/scene-template-linter.js +852 -0
  228. package/lib/scene-runtime/templates/scene-template-erp-query-v0.1.yaml +28 -0
  229. package/lib/scene-runtime/templates/scene-template-hybrid-shadow-v0.1.yaml +34 -0
  230. package/lib/spec/bootstrap/context-collector.js +48 -0
  231. package/lib/spec/bootstrap/draft-generator.js +158 -0
  232. package/lib/spec/bootstrap/questionnaire-engine.js +70 -0
  233. package/lib/spec/bootstrap/trace-emitter.js +59 -0
  234. package/lib/spec/multi-spec-orchestrate.js +93 -0
  235. package/lib/spec/pipeline/constants.js +6 -0
  236. package/lib/spec/pipeline/stage-adapters.js +118 -0
  237. package/lib/spec/pipeline/stage-runner.js +146 -0
  238. package/lib/spec/pipeline/state-store.js +119 -0
  239. package/lib/spec-gate/engine/gate-engine.js +165 -0
  240. package/lib/spec-gate/policy/default-policy.js +22 -0
  241. package/lib/spec-gate/policy/policy-loader.js +103 -0
  242. package/lib/spec-gate/result-emitter.js +81 -0
  243. package/lib/spec-gate/rules/default-rules.js +156 -0
  244. package/lib/spec-gate/rules/rule-registry.js +51 -0
  245. package/lib/steering/adoption-config.js +164 -0
  246. package/lib/steering/compliance-auto-fixer.js +204 -0
  247. package/lib/steering/compliance-cache.js +99 -0
  248. package/lib/steering/compliance-error-reporter.js +70 -0
  249. package/lib/steering/context-sync-manager.js +273 -0
  250. package/lib/steering/index.js +92 -0
  251. package/lib/steering/spec-steering.js +230 -0
  252. package/lib/steering/steering-compliance-checker.js +73 -0
  253. package/lib/steering/steering-loader.js +144 -0
  254. package/lib/steering/steering-manager.js +289 -0
  255. package/lib/task/index.js +12 -0
  256. package/lib/task/task-claimer.js +489 -0
  257. package/lib/task/task-status-store.js +418 -0
  258. package/lib/templates/cache-manager.js +440 -0
  259. package/lib/templates/content-generalizer.js +247 -0
  260. package/lib/templates/frontmatter-generator.js +128 -0
  261. package/lib/templates/git-handler.js +471 -0
  262. package/lib/templates/metadata-collector.js +328 -0
  263. package/lib/templates/path-utils.js +144 -0
  264. package/lib/templates/registry-parser.js +505 -0
  265. package/lib/templates/spec-reader.js +216 -0
  266. package/lib/templates/template-applicator.js +249 -0
  267. package/lib/templates/template-creator.js +256 -0
  268. package/lib/templates/template-error.js +143 -0
  269. package/lib/templates/template-exporter.js +502 -0
  270. package/lib/templates/template-manager.js +782 -0
  271. package/lib/templates/template-validator.js +361 -0
  272. package/lib/upgrade/migration-engine.js +382 -0
  273. package/lib/upgrade/migrations/.gitkeep +52 -0
  274. package/lib/upgrade/migrations/1.0.0-to-1.1.0.js +78 -0
  275. package/lib/utils/file-diff.js +177 -0
  276. package/lib/utils/fs-utils.js +274 -0
  277. package/lib/utils/tool-detector.js +383 -0
  278. package/lib/utils/validation.js +324 -0
  279. package/lib/value/gate-summary-emitter.js +99 -0
  280. package/lib/value/metric-contract-loader.js +210 -0
  281. package/lib/value/risk-evaluator.js +117 -0
  282. package/lib/value/weekly-snapshot-builder.js +61 -0
  283. package/lib/version/version-checker.js +156 -0
  284. package/lib/version/version-manager.js +327 -0
  285. package/lib/watch/action-executor.js +458 -0
  286. package/lib/watch/event-debouncer.js +323 -0
  287. package/lib/watch/execution-logger.js +550 -0
  288. package/lib/watch/file-watcher.js +499 -0
  289. package/lib/watch/presets.js +266 -0
  290. package/lib/watch/watch-manager.js +533 -0
  291. package/lib/workspace/multi/global-config.js +150 -0
  292. package/lib/workspace/multi/index.js +22 -0
  293. package/lib/workspace/multi/path-utils.js +173 -0
  294. package/lib/workspace/multi/workspace-context-resolver.js +244 -0
  295. package/lib/workspace/multi/workspace-registry.js +196 -0
  296. package/lib/workspace/multi/workspace-state-manager.js +537 -0
  297. package/lib/workspace/multi/workspace.js +90 -0
  298. package/lib/workspace/workspace-manager.js +370 -0
  299. package/lib/workspace/workspace-sync.js +356 -0
  300. package/locales/en.json +114 -0
  301. package/locales/zh.json +114 -0
  302. package/package.json +102 -0
  303. package/template/.kiro/README.md +247 -0
  304. package/template/.kiro/hooks/check-spec-on-create.kiro.hook +17 -0
  305. package/template/.kiro/hooks/run-tests-on-save.kiro.hook +13 -0
  306. package/template/.kiro/hooks/sync-tasks-on-edit.kiro.hook +16 -0
  307. package/template/.kiro/specs/SPEC_WORKFLOW_GUIDE.md +134 -0
  308. package/template/.kiro/steering/CORE_PRINCIPLES.md +133 -0
  309. package/template/.kiro/steering/CURRENT_CONTEXT.md +30 -0
  310. package/template/.kiro/steering/ENVIRONMENT.md +35 -0
  311. package/template/.kiro/steering/RULES_GUIDE.md +46 -0
  312. package/template/.kiro/templates/operations/default/change-impact.md +112 -0
  313. package/template/.kiro/templates/operations/default/deployment.md +91 -0
  314. package/template/.kiro/templates/operations/default/feedback-response.md +269 -0
  315. package/template/.kiro/templates/operations/default/migration-plan.md +172 -0
  316. package/template/.kiro/templates/operations/default/monitoring.md +135 -0
  317. package/template/.kiro/templates/operations/default/operations.md +135 -0
  318. package/template/.kiro/templates/operations/default/rollback.md +143 -0
  319. package/template/.kiro/templates/operations/default/tools.yaml +364 -0
  320. package/template/.kiro/templates/operations/default/troubleshooting.md +123 -0
  321. package/template/.kiro/tools/backup_manager.py +295 -0
  322. package/template/.kiro/tools/configuration_manager.py +218 -0
  323. package/template/.kiro/tools/document_evaluator.py +550 -0
  324. package/template/.kiro/tools/enhancement_logger.py +168 -0
  325. package/template/.kiro/tools/error_handler.py +335 -0
  326. package/template/.kiro/tools/improvement_identifier.py +444 -0
  327. package/template/.kiro/tools/modification_applicator.py +737 -0
  328. package/template/.kiro/tools/quality_gate_enforcer.py +207 -0
  329. package/template/.kiro/tools/quality_scorer.py +305 -0
  330. package/template/.kiro/tools/report_generator.py +154 -0
  331. package/template/.kiro/tools/ultrawork_enhancer.py +676 -0
  332. package/template/.kiro/tools/ultrawork_enhancer_refactored.py +0 -0
  333. package/template/.kiro/tools/ultrawork_enhancer_v2.py +463 -0
  334. package/template/.kiro/tools/ultrawork_enhancer_v3.py +606 -0
  335. package/template/.kiro/tools/workflow_quality_gate.py +100 -0
  336. 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;