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,345 @@
1
+ /**
2
+ * TaskLockManager - Task-level fine-grained lock manager
3
+ *
4
+ * Provides per-task locking instead of per-Spec locking, allowing multiple
5
+ * Agents to work on different tasks within the same Spec concurrently.
6
+ *
7
+ * Lock file path: `.kiro/specs/{specName}/locks/{taskId}.lock`
8
+ * (dots in taskId replaced with dashes for filesystem safety)
9
+ *
10
+ * In single-Agent mode, delegates to the existing LockManager (Spec-level lock).
11
+ *
12
+ * Requirements: 2.1, 2.2, 2.3, 2.4, 2.5, 2.6, 2.7
13
+ */
14
+
15
+ const fs = require('fs').promises;
16
+ const path = require('path');
17
+ const fsUtils = require('../utils/fs-utils');
18
+ const { MultiAgentConfig } = require('../collab/multi-agent-config');
19
+ const { LockManager } = require('./lock-manager');
20
+ const TaskClaimer = require('../task/task-claimer');
21
+
22
+ class TaskLockManager {
23
+ /**
24
+ * @param {string} workspaceRoot - Absolute path to the project root
25
+ * @param {import('./machine-identifier').MachineIdentifier} machineIdentifier
26
+ */
27
+ constructor(workspaceRoot, machineIdentifier) {
28
+ this._workspaceRoot = workspaceRoot;
29
+ this._machineIdentifier = machineIdentifier;
30
+ this._specsDir = path.join(workspaceRoot, '.kiro', 'specs');
31
+ this._multiAgentConfig = new MultiAgentConfig(workspaceRoot);
32
+ this._lockManager = new LockManager(workspaceRoot, machineIdentifier);
33
+ this._taskClaimer = new TaskClaimer();
34
+ }
35
+
36
+ /**
37
+ * Convert a taskId to a filesystem-safe filename.
38
+ * Replaces dots with dashes (e.g. "2.1" → "2-1").
39
+ * @param {string} taskId
40
+ * @returns {string}
41
+ * @private
42
+ */
43
+ _safeTaskId(taskId) {
44
+ return taskId.replace(/\./g, '-');
45
+ }
46
+
47
+ /**
48
+ * Get the locks directory for a spec.
49
+ * @param {string} specName
50
+ * @returns {string}
51
+ * @private
52
+ */
53
+ _locksDir(specName) {
54
+ return path.join(this._specsDir, specName, 'locks');
55
+ }
56
+
57
+ /**
58
+ * Get the lock file path for a specific task.
59
+ * @param {string} specName
60
+ * @param {string} taskId
61
+ * @returns {string}
62
+ * @private
63
+ */
64
+ _lockFilePath(specName, taskId) {
65
+ return path.join(this._locksDir(specName), `${this._safeTaskId(taskId)}.lock`);
66
+ }
67
+
68
+ /**
69
+ * Check whether multi-Agent mode is enabled.
70
+ * @returns {Promise<boolean>}
71
+ */
72
+ async isMultiAgentMode() {
73
+ return this._multiAgentConfig.isEnabled();
74
+ }
75
+
76
+ /**
77
+ * Acquire a task-level lock.
78
+ *
79
+ * In multi-Agent mode: creates `.kiro/specs/{specName}/locks/{taskId}.lock`
80
+ * In single-Agent mode: delegates to LockManager.acquireLock(specName)
81
+ *
82
+ * @param {string} specName
83
+ * @param {string} taskId
84
+ * @param {string} agentId
85
+ * @param {object} [options={}]
86
+ * @param {string} [options.reason] - Reason for acquiring the lock
87
+ * @returns {Promise<{success: boolean, lock?: object, holder?: object, error?: string}>}
88
+ */
89
+ async acquireTaskLock(specName, taskId, agentId, options = {}) {
90
+ const multiAgent = await this.isMultiAgentMode();
91
+
92
+ if (!multiAgent) {
93
+ // Single-Agent mode: delegate to Spec-level lock
94
+ return this._lockManager.acquireLock(specName, { reason: options.reason });
95
+ }
96
+
97
+ const lockPath = this._lockFilePath(specName, taskId);
98
+
99
+ // Check for existing lock
100
+ const existing = await this._readLockFile(lockPath);
101
+ if (existing) {
102
+ if (existing.agentId === agentId) {
103
+ // Already held by this agent – refresh
104
+ const lock = await this._buildLockRecord(specName, taskId, agentId, options.reason);
105
+ await this._atomicWriteLock(lockPath, lock);
106
+ return { success: true, lock };
107
+ }
108
+ // Held by another agent (Req 2.2)
109
+ return { success: false, error: 'Task is already locked', holder: existing };
110
+ }
111
+
112
+ // Acquire new lock (Req 2.1)
113
+ const lock = await this._buildLockRecord(specName, taskId, agentId, options.reason);
114
+ await fsUtils.ensureDirectory(this._locksDir(specName));
115
+ await this._atomicWriteLock(lockPath, lock);
116
+
117
+ // Verify we actually got the lock (race-condition guard)
118
+ const verify = await this._readLockFile(lockPath);
119
+ if (verify && verify.agentId === agentId) {
120
+ // Integrate with TaskClaimer: claim the task atomically (Req 2.5)
121
+ const claimResult = await this._taskClaimer.claimTask(
122
+ this._workspaceRoot, specName, taskId, agentId, true
123
+ );
124
+ if (!claimResult.success) {
125
+ // Rollback: release the lock file since claim failed
126
+ try {
127
+ await fs.unlink(lockPath);
128
+ } catch (unlinkErr) {
129
+ if (unlinkErr.code !== 'ENOENT') throw unlinkErr;
130
+ }
131
+ return { success: false, error: `Lock acquired but claim failed: ${claimResult.error}` };
132
+ }
133
+ return { success: true, lock };
134
+ }
135
+
136
+ return { success: false, error: 'Task is already locked', holder: verify };
137
+ }
138
+
139
+ /**
140
+ * Release a task-level lock.
141
+ *
142
+ * In multi-Agent mode: deletes the lock file (Req 2.3)
143
+ * In single-Agent mode: delegates to LockManager.releaseLock(specName)
144
+ *
145
+ * @param {string} specName
146
+ * @param {string} taskId
147
+ * @param {string} agentId
148
+ * @returns {Promise<{success: boolean, error?: string}>}
149
+ */
150
+ async releaseTaskLock(specName, taskId, agentId) {
151
+ const multiAgent = await this.isMultiAgentMode();
152
+
153
+ if (!multiAgent) {
154
+ return this._lockManager.releaseLock(specName);
155
+ }
156
+
157
+ const lockPath = this._lockFilePath(specName, taskId);
158
+ const existing = await this._readLockFile(lockPath);
159
+
160
+ if (!existing) {
161
+ return { success: true }; // Nothing to release
162
+ }
163
+
164
+ if (existing.agentId !== agentId) {
165
+ return { success: false, error: 'Lock owned by different agent', holder: existing };
166
+ }
167
+
168
+ // Integrate with TaskClaimer: unclaim the task (best effort) (Req 2.5)
169
+ try {
170
+ await this._taskClaimer.unclaimTask(this._workspaceRoot, specName, taskId, agentId);
171
+ } catch (_unclaimErr) {
172
+ // Best effort – proceed with lock file deletion even if unclaim fails
173
+ }
174
+
175
+ try {
176
+ await fs.unlink(lockPath);
177
+ } catch (err) {
178
+ if (err.code !== 'ENOENT') throw err;
179
+ }
180
+
181
+ return { success: true };
182
+ }
183
+
184
+ /**
185
+ * Release all task locks held by a specific agent.
186
+ * Scans all spec directories for `locks/` subdirectories.
187
+ *
188
+ * @param {string} agentId
189
+ * @returns {Promise<{released: Array<{specName: string, taskId: string}>}>}
190
+ */
191
+ async releaseAllLocks(agentId) {
192
+ const released = [];
193
+
194
+ let specEntries;
195
+ try {
196
+ specEntries = await fs.readdir(this._specsDir, { withFileTypes: true });
197
+ } catch (err) {
198
+ if (err.code === 'ENOENT') return { released };
199
+ throw err;
200
+ }
201
+
202
+ for (const entry of specEntries) {
203
+ if (!entry.isDirectory()) continue;
204
+
205
+ const specName = entry.name;
206
+ const locksDir = this._locksDir(specName);
207
+
208
+ let lockFiles;
209
+ try {
210
+ lockFiles = await fs.readdir(locksDir);
211
+ } catch (err) {
212
+ if (err.code === 'ENOENT') continue;
213
+ throw err;
214
+ }
215
+
216
+ for (const file of lockFiles) {
217
+ if (!file.endsWith('.lock')) continue;
218
+
219
+ const lockPath = path.join(locksDir, file);
220
+ const lockData = await this._readLockFile(lockPath);
221
+
222
+ if (lockData && lockData.agentId === agentId) {
223
+ try {
224
+ await fs.unlink(lockPath);
225
+ released.push({ specName, taskId: lockData.taskId });
226
+ } catch (err) {
227
+ if (err.code !== 'ENOENT') throw err;
228
+ }
229
+ }
230
+ }
231
+ }
232
+
233
+ return { released };
234
+ }
235
+
236
+ /**
237
+ * Get the lock status for a specific task.
238
+ *
239
+ * @param {string} specName
240
+ * @param {string} taskId
241
+ * @returns {Promise<{locked: boolean, lock?: object}>}
242
+ */
243
+ async getTaskLockStatus(specName, taskId) {
244
+ const lockPath = this._lockFilePath(specName, taskId);
245
+ const lockData = await this._readLockFile(lockPath);
246
+
247
+ if (!lockData) {
248
+ return { locked: false, specName, taskId };
249
+ }
250
+
251
+ return { locked: true, specName, taskId, lock: lockData };
252
+ }
253
+
254
+ /**
255
+ * List all locked tasks within a spec.
256
+ *
257
+ * @param {string} specName
258
+ * @returns {Promise<Array<{locked: boolean, specName: string, taskId: string, lock?: object}>>}
259
+ */
260
+ async listLockedTasks(specName) {
261
+ const locksDir = this._locksDir(specName);
262
+ const results = [];
263
+
264
+ let files;
265
+ try {
266
+ files = await fs.readdir(locksDir);
267
+ } catch (err) {
268
+ if (err.code === 'ENOENT') return results;
269
+ throw err;
270
+ }
271
+
272
+ for (const file of files) {
273
+ if (!file.endsWith('.lock')) continue;
274
+
275
+ const lockPath = path.join(locksDir, file);
276
+ const lockData = await this._readLockFile(lockPath);
277
+
278
+ if (lockData) {
279
+ results.push({
280
+ locked: true,
281
+ specName,
282
+ taskId: lockData.taskId,
283
+ lock: lockData,
284
+ });
285
+ }
286
+ }
287
+
288
+ return results;
289
+ }
290
+
291
+ // ── Private helpers ──────────────────────────────────────────────────
292
+
293
+ /**
294
+ * Build a lock record object.
295
+ * @private
296
+ */
297
+ async _buildLockRecord(specName, taskId, agentId, reason) {
298
+ const machineInfo = await this._machineIdentifier.getMachineId();
299
+ return {
300
+ agentId,
301
+ machineId: machineInfo.id,
302
+ hostname: machineInfo.hostname,
303
+ taskId,
304
+ specName,
305
+ acquiredAt: new Date().toISOString(),
306
+ reason: reason || null,
307
+ };
308
+ }
309
+
310
+ /**
311
+ * Read and parse a lock file. Returns null if missing or corrupted.
312
+ * @param {string} lockPath
313
+ * @returns {Promise<object|null>}
314
+ * @private
315
+ */
316
+ async _readLockFile(lockPath) {
317
+ try {
318
+ const raw = await fs.readFile(lockPath, 'utf8');
319
+ const data = JSON.parse(raw);
320
+ if (data && typeof data.agentId === 'string' && typeof data.taskId === 'string') {
321
+ return data;
322
+ }
323
+ return null; // Invalid structure
324
+ } catch (err) {
325
+ if (err.code === 'ENOENT' || err instanceof SyntaxError) {
326
+ return null;
327
+ }
328
+ throw err;
329
+ }
330
+ }
331
+
332
+ /**
333
+ * Atomically write a lock file using temp + rename (Req 2.7).
334
+ * @param {string} lockPath
335
+ * @param {object} data
336
+ * @returns {Promise<void>}
337
+ * @private
338
+ */
339
+ async _atomicWriteLock(lockPath, data) {
340
+ const content = JSON.stringify(data, null, 2);
341
+ await fsUtils.atomicWrite(lockPath, content);
342
+ }
343
+ }
344
+
345
+ module.exports = { TaskLockManager };
@@ -0,0 +1,293 @@
1
+ /**
2
+ * Audit Logger
3
+ *
4
+ * Logs all AI operations with tamper-evident storage
5
+ */
6
+
7
+ const fs = require('fs-extra');
8
+ const path = require('path');
9
+ const crypto = require('crypto');
10
+
11
+ class AuditLogger {
12
+ constructor(projectRoot = process.cwd()) {
13
+ this.projectRoot = projectRoot;
14
+ this.auditPath = path.join(projectRoot, '.kiro/audit');
15
+ }
16
+
17
+ /**
18
+ * Log an operation
19
+ *
20
+ * @param {Object} entry - Audit entry
21
+ * @returns {Promise<string>} Entry ID
22
+ */
23
+ async logOperation(entry) {
24
+ await fs.ensureDir(this.auditPath);
25
+
26
+ // Generate entry ID
27
+ const id = `audit-${Date.now()}-${Math.random().toString(36).substring(2, 11)}`;
28
+
29
+ // Create full entry
30
+ const fullEntry = {
31
+ id,
32
+ timestamp: new Date().toISOString(),
33
+ ...entry
34
+ };
35
+
36
+ // Calculate checksum for tamper-evidence
37
+ const checksum = this.calculateChecksum(fullEntry);
38
+ fullEntry.checksum = checksum;
39
+
40
+ // Write to log file (append)
41
+ const logFile = path.join(this.auditPath, 'operations.jsonl');
42
+ await fs.appendFile(
43
+ logFile,
44
+ JSON.stringify(fullEntry) + '\n',
45
+ 'utf8'
46
+ );
47
+
48
+ return id;
49
+ }
50
+
51
+ /**
52
+ * Query audit logs
53
+ *
54
+ * @param {Object} query - Query parameters
55
+ * @returns {Promise<Array>} Matching entries
56
+ */
57
+ async queryLogs(query = {}) {
58
+ const logFile = path.join(this.auditPath, 'operations.jsonl');
59
+
60
+ if (!await fs.pathExists(logFile)) {
61
+ return [];
62
+ }
63
+
64
+ // Read all entries
65
+ const content = await fs.readFile(logFile, 'utf8');
66
+ const lines = content.trim().split('\n').filter(line => line);
67
+ const entries = lines.map(line => JSON.parse(line));
68
+
69
+ // Filter by query
70
+ return entries.filter(entry => {
71
+ if (query.projectName && entry.project !== query.projectName) return false;
72
+ if (query.operationType && entry.operationType !== query.operationType) return false;
73
+ if (query.outcome && entry.outcome !== query.outcome) return false;
74
+ if (query.environment && entry.securityEnvironment !== query.environment) return false;
75
+
76
+ if (query.fromDate) {
77
+ const entryDate = new Date(entry.timestamp);
78
+ const fromDate = new Date(query.fromDate);
79
+ if (entryDate < fromDate) return false;
80
+ }
81
+
82
+ if (query.toDate) {
83
+ const entryDate = new Date(entry.timestamp);
84
+ const toDate = new Date(query.toDate);
85
+ if (entryDate > toDate) return false;
86
+ }
87
+
88
+ return true;
89
+ });
90
+ }
91
+
92
+ /**
93
+ * Generate audit summary
94
+ *
95
+ * @param {string} project - Project name
96
+ * @param {Object} timeRange - Time range {from, to}
97
+ * @returns {Promise<Object>} Audit summary
98
+ */
99
+ async generateSummary(project, timeRange = {}) {
100
+ const entries = await this.queryLogs({
101
+ projectName: project,
102
+ fromDate: timeRange.from,
103
+ toDate: timeRange.to
104
+ });
105
+
106
+ const summary = {
107
+ project,
108
+ timeRange,
109
+ totalOperations: entries.length,
110
+ successCount: entries.filter(e => e.outcome === 'success').length,
111
+ failureCount: entries.filter(e => e.outcome === 'failure').length,
112
+ operationsByType: {},
113
+ operationsByLevel: {}
114
+ };
115
+
116
+ summary.successRate = summary.totalOperations > 0
117
+ ? (summary.successCount / summary.totalOperations * 100).toFixed(2) + '%'
118
+ : '0%';
119
+
120
+ // Count by type
121
+ entries.forEach(entry => {
122
+ const type = entry.operationType || 'unknown';
123
+ summary.operationsByType[type] = (summary.operationsByType[type] || 0) + 1;
124
+
125
+ const level = entry.takeoverLevel || 'unknown';
126
+ summary.operationsByLevel[level] = (summary.operationsByLevel[level] || 0) + 1;
127
+ });
128
+
129
+ return summary;
130
+ }
131
+
132
+ /**
133
+ * Export audit logs
134
+ *
135
+ * @param {Object} query - Query parameters
136
+ * @param {string} format - Export format (json, csv)
137
+ * @returns {Promise<string>} Exported data
138
+ */
139
+ async exportLogs(query = {}, format = 'json') {
140
+ const entries = await this.queryLogs(query);
141
+
142
+ if (format === 'json') {
143
+ return JSON.stringify(entries, null, 2);
144
+ }
145
+
146
+ if (format === 'csv') {
147
+ if (entries.length === 0) return '';
148
+
149
+ // CSV header
150
+ const headers = Object.keys(entries[0]);
151
+ let csv = headers.join(',') + '\n';
152
+
153
+ // CSV rows
154
+ entries.forEach(entry => {
155
+ const row = headers.map(h => {
156
+ const value = entry[h];
157
+ return typeof value === 'string' ? `"${value}"` : value;
158
+ });
159
+ csv += row.join(',') + '\n';
160
+ });
161
+
162
+ return csv;
163
+ }
164
+
165
+ throw new Error(`Unsupported export format: ${format}`);
166
+ }
167
+
168
+ /**
169
+ * Calculate checksum for tamper-evidence
170
+ *
171
+ * @param {Object} entry - Audit entry
172
+ * @returns {string} SHA-256 checksum
173
+ */
174
+ calculateChecksum(entry) {
175
+ const data = JSON.stringify(entry);
176
+ return crypto.createHash('sha256').update(data).digest('hex');
177
+ }
178
+
179
+ /**
180
+ * Verify entry integrity
181
+ *
182
+ * @param {Object} entry - Audit entry with checksum
183
+ * @returns {boolean} Whether entry is valid
184
+ */
185
+ verifyEntry(entry) {
186
+ const { checksum, ...entryWithoutChecksum } = entry;
187
+ const calculatedChecksum = this.calculateChecksum(entryWithoutChecksum);
188
+ return checksum === calculatedChecksum;
189
+ }
190
+
191
+ /**
192
+ * Flag anomalies in operations
193
+ *
194
+ * @param {string} project - Project name
195
+ * @param {Object} threshold - Anomaly thresholds
196
+ * @returns {Promise<Array>} Detected anomalies
197
+ */
198
+ async flagAnomalies(project, threshold = {}) {
199
+ const entries = await this.queryLogs({ projectName: project });
200
+
201
+ if (entries.length === 0) {
202
+ return [];
203
+ }
204
+
205
+ const anomalies = [];
206
+
207
+ // Default thresholds
208
+ const thresholds = {
209
+ errorRatePercent: threshold.errorRatePercent || 10,
210
+ operationCountPerHour: threshold.operationCountPerHour || 100,
211
+ unusualOperationType: threshold.unusualOperationType || true,
212
+ ...threshold
213
+ };
214
+
215
+ // Calculate baseline metrics
216
+ const totalOps = entries.length;
217
+ const failedOps = entries.filter(e => e.outcome === 'failure').length;
218
+ const errorRate = (failedOps / totalOps) * 100;
219
+
220
+ // Anomaly 1: High error rate
221
+ if (errorRate > thresholds.errorRatePercent) {
222
+ anomalies.push({
223
+ type: 'high_error_rate',
224
+ severity: 'high',
225
+ message: `Error rate ${errorRate.toFixed(2)}% exceeds threshold ${thresholds.errorRatePercent}%`,
226
+ metric: errorRate,
227
+ threshold: thresholds.errorRatePercent,
228
+ affectedOperations: failedOps
229
+ });
230
+ }
231
+
232
+ // Anomaly 2: Unusual operation frequency
233
+ const now = new Date();
234
+ const oneHourAgo = new Date(now.getTime() - 60 * 60 * 1000);
235
+ const recentOps = entries.filter(e => new Date(e.timestamp) > oneHourAgo);
236
+
237
+ if (recentOps.length > thresholds.operationCountPerHour) {
238
+ anomalies.push({
239
+ type: 'high_operation_frequency',
240
+ severity: 'medium',
241
+ message: `${recentOps.length} operations in last hour exceeds threshold ${thresholds.operationCountPerHour}`,
242
+ metric: recentOps.length,
243
+ threshold: thresholds.operationCountPerHour,
244
+ timeWindow: '1 hour'
245
+ });
246
+ }
247
+
248
+ // Anomaly 3: Unusual operation types
249
+ if (thresholds.unusualOperationType) {
250
+ const operationTypes = {};
251
+ entries.forEach(e => {
252
+ const type = e.operationType || 'unknown';
253
+ operationTypes[type] = (operationTypes[type] || 0) + 1;
254
+ });
255
+
256
+ // Flag operation types that appear only once or twice (potentially unusual)
257
+ Object.entries(operationTypes).forEach(([type, count]) => {
258
+ if (count <= 2 && totalOps > 10) {
259
+ anomalies.push({
260
+ type: 'unusual_operation_type',
261
+ severity: 'low',
262
+ message: `Operation type '${type}' appears only ${count} time(s)`,
263
+ operationType: type,
264
+ occurrences: count
265
+ });
266
+ }
267
+ });
268
+ }
269
+
270
+ // Anomaly 4: Repeated failures of same operation
271
+ const failuresByType = {};
272
+ entries.filter(e => e.outcome === 'failure').forEach(e => {
273
+ const type = e.operationType || 'unknown';
274
+ failuresByType[type] = (failuresByType[type] || 0) + 1;
275
+ });
276
+
277
+ Object.entries(failuresByType).forEach(([type, count]) => {
278
+ if (count >= 3) {
279
+ anomalies.push({
280
+ type: 'repeated_failures',
281
+ severity: 'high',
282
+ message: `Operation type '${type}' failed ${count} times`,
283
+ operationType: type,
284
+ failureCount: count
285
+ });
286
+ }
287
+ });
288
+
289
+ return anomalies;
290
+ }
291
+ }
292
+
293
+ module.exports = AuditLogger;