@planu/cli 5.3.67 → 5.3.69

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 (389) hide show
  1. package/CHANGELOG.md +92 -0
  2. package/dist/.planu-build.json +1 -1
  3. package/dist/cli/commands/install.d.ts +2 -15
  4. package/dist/config/release-policy.json +2 -2
  5. package/dist/core/shared/fs.d.ts +11 -0
  6. package/dist/core/shared/fs.js +90 -0
  7. package/dist/core/shared/strings.d.ts +3 -0
  8. package/dist/core/shared/strings.js +13 -0
  9. package/dist/engine/actuals/git-analyzer.js +1 -1
  10. package/dist/engine/actuals/progress-parser.js +1 -3
  11. package/dist/engine/agent-handoff-store.js +1 -0
  12. package/dist/engine/agent-squad/dispatcher.js +1 -0
  13. package/dist/engine/agent-telemetry.js +1 -0
  14. package/dist/engine/auth/config.js +1 -0
  15. package/dist/engine/auto-updater/atomic-upgrade.js +1 -0
  16. package/dist/engine/auto-updater/index.js +1 -0
  17. package/dist/engine/autopilot/action-registry.js +20 -0
  18. package/dist/engine/autopilot/audit-logger.js +1 -0
  19. package/dist/engine/autopilot/bootstrap.js +9 -2
  20. package/dist/engine/autopilot/complete-loop.js +4 -0
  21. package/dist/engine/autopilot/crash-shield-rate-limiter.js +1 -0
  22. package/dist/engine/autopilot/event-bus.js +1 -0
  23. package/dist/engine/autopilot/state-updater.js +6 -0
  24. package/dist/engine/browser-validator.js +1 -3
  25. package/dist/engine/build-tool-detector.js +1 -8
  26. package/dist/engine/bundle-installer.js +2 -10
  27. package/dist/engine/cascade-hooks/core/append-releases.js +1 -0
  28. package/dist/engine/cascade-hooks/core/write-session-json.js +2 -2
  29. package/dist/engine/cascade-hooks/hooks/dispatch-hook-event.hook.d.ts +3 -1
  30. package/dist/engine/cascade-hooks/hooks/dispatch-hook-event.hook.js +12 -3
  31. package/dist/engine/cascade-hooks/hooks/estimate-calibration.hook.js +1 -0
  32. package/dist/engine/cascade-hooks/hooks/housekeeping-on-done.hook.js +2 -0
  33. package/dist/engine/cascade-hooks/hooks/implementing-kickoff.hook.js +5 -1
  34. package/dist/engine/cascade-hooks/hooks/lesson-drift.hook.js +1 -0
  35. package/dist/engine/cascade-hooks/hooks/orchestration-plan.hook.js +1 -0
  36. package/dist/engine/cascade-hooks/hooks/session-context.hook.js +4 -2
  37. package/dist/engine/cascade-hooks/hooks/slack-notify.hook.js +1 -0
  38. package/dist/engine/cascade-hooks/hooks/status-json.hook.js +2 -0
  39. package/dist/engine/cascade-hooks/hooks/velocity-profile.hook.js +2 -0
  40. package/dist/engine/cascade-hooks/runner.js +2 -0
  41. package/dist/engine/cascade-hooks/state-drift-detector.js +2 -0
  42. package/dist/engine/checkpoint/checkpoint-manager.js +2 -0
  43. package/dist/engine/checkpoint/policy-engine.js +1 -0
  44. package/dist/engine/ci-generator/stack-detector.js +2 -10
  45. package/dist/engine/clarification-gate/token-store.js +1 -0
  46. package/dist/engine/claude-config-auditor/claudemd-auditor.js +2 -10
  47. package/dist/engine/claude-config-auditor/settings-auditor.js +2 -10
  48. package/dist/engine/client-detection.js +2 -10
  49. package/dist/engine/compliance/auto-remediator.js +1 -0
  50. package/dist/engine/compliance/evidence-builder.d.ts +2 -2
  51. package/dist/engine/compliance/framework-catalog.d.ts +6 -6
  52. package/dist/engine/compliance/gap-analyzer.d.ts +2 -2
  53. package/dist/engine/config-health/build-tool-checker.js +1 -9
  54. package/dist/engine/config-health/ci-commands-collector.d.ts +2 -1
  55. package/dist/engine/config-health/ci-commands-collector.js +2 -9
  56. package/dist/engine/config-health/go-checker.js +1 -9
  57. package/dist/engine/config-health/mcp-config-checker.js +2 -10
  58. package/dist/engine/config-health/python-checker.js +1 -9
  59. package/dist/engine/config-health/rust-checker.js +1 -9
  60. package/dist/engine/config-health/ts-checker-scripts.js +1 -9
  61. package/dist/engine/config-health/ts-checker.js +1 -9
  62. package/dist/engine/context-artifacts/store.js +1 -0
  63. package/dist/engine/context-md-generator.js +1 -0
  64. package/dist/engine/context-orchestrator/index.js +2 -0
  65. package/dist/engine/convention-scanner/convention-parser.js +2 -9
  66. package/dist/engine/cost-guardrails.js +1 -0
  67. package/dist/engine/cost-tracking/ledger.js +1 -0
  68. package/dist/engine/criterion-identity.js +1 -1
  69. package/dist/engine/dashboard/data-loader.js +1 -0
  70. package/dist/engine/dashboard/drift-score.js +2 -0
  71. package/dist/engine/dashboard/spec-updater.d.ts +3 -1
  72. package/dist/engine/dashboard/spec-updater.js +9 -2
  73. package/dist/engine/dashboard/templates-layout.d.ts +2 -2
  74. package/dist/engine/dashboard/templates-layout.js +2 -9
  75. package/dist/engine/data-projects-gc/gc-runner.js +2 -4
  76. package/dist/engine/deleter/audit-logger.js +1 -0
  77. package/dist/engine/deleter/core.js +6 -0
  78. package/dist/engine/deleter/trash-manager.js +1 -0
  79. package/dist/engine/desktop-detector.js +1 -9
  80. package/dist/engine/detection-utils.d.ts +2 -1
  81. package/dist/engine/detection-utils.js +3 -10
  82. package/dist/engine/detectors/advanced-framework-detector/helpers.d.ts +3 -2
  83. package/dist/engine/detectors/advanced-framework-detector/helpers.js +3 -17
  84. package/dist/engine/detectors/cicd-detector.js +2 -10
  85. package/dist/engine/detectors/css-framework-detector.js +2 -10
  86. package/dist/engine/detectors/deployment-detector.js +3 -12
  87. package/dist/engine/detectors/frontend-perf-detector.js +2 -10
  88. package/dist/engine/detectors/gitops-detector.js +3 -11
  89. package/dist/engine/detectors/idp-detector.js +1 -3
  90. package/dist/engine/detectors/k3s-detector.js +3 -11
  91. package/dist/engine/detectors/llm-detector/file-readers.d.ts +2 -1
  92. package/dist/engine/detectors/llm-detector/file-readers.js +2 -9
  93. package/dist/engine/detectors/mfe-detector.js +5 -13
  94. package/dist/engine/detectors/monitoring-detector.js +2 -11
  95. package/dist/engine/detectors/monorepo-detector.d.ts +1 -0
  96. package/dist/engine/detectors/monorepo-detector.js +1 -10
  97. package/dist/engine/detectors/nomad-detector.js +3 -11
  98. package/dist/engine/detectors/otel-ecosystem-detectors.d.ts +2 -1
  99. package/dist/engine/detectors/otel-ecosystem-detectors.js +2 -10
  100. package/dist/engine/doc-generator/executive-summary/html-template.js +1 -7
  101. package/dist/engine/doc-generator/executive-summary/section-builders.js +1 -7
  102. package/dist/engine/doc-generator/per-spec-report/mermaid-renderer.js +1 -7
  103. package/dist/engine/doc-generator/per-spec-report/spec-section-builders.js +1 -7
  104. package/dist/engine/doc-generator/per-spec-report/technical-report.js +1 -7
  105. package/dist/engine/doc-generator/portal/analytics-page.js +1 -7
  106. package/dist/engine/doc-generator/portal/architecture-page.js +1 -7
  107. package/dist/engine/doc-generator/portal/changelog-page.js +1 -7
  108. package/dist/engine/doc-generator/portal/decision-page.js +1 -7
  109. package/dist/engine/doc-generator/portal/decision-timeline-builder.js +1 -7
  110. package/dist/engine/doc-generator/portal/kanban-view-builder.js +1 -7
  111. package/dist/engine/doc-generator/portal/portal-breadcrumbs.js +1 -7
  112. package/dist/engine/doc-generator/portal/portal-landing-cards.js +1 -7
  113. package/dist/engine/doc-generator/portal/portal-navbar.js +1 -7
  114. package/dist/engine/doc-generator/portal/portal-page-detector.d.ts +3 -0
  115. package/dist/engine/doc-generator/portal/portal-page-detector.js +1 -10
  116. package/dist/engine/doc-generator/portal/portal-regenerator.js +2 -0
  117. package/dist/engine/doc-generator/portal/risk-matrix-svg.js +1 -7
  118. package/dist/engine/doc-generator/portal/risk-page.js +1 -7
  119. package/dist/engine/doc-generator/portal/roadmap-page.js +1 -7
  120. package/dist/engine/doc-generator/proposal/proposal-gantt-builder.js +1 -7
  121. package/dist/engine/doc-generator/proposal/proposal-kpi-builder.js +1 -7
  122. package/dist/engine/doc-generator/proposal/proposal-section-builders-advanced.js +2 -8
  123. package/dist/engine/doc-generator/proposal/proposal-section-builders.js +1 -7
  124. package/dist/engine/docs-site-generator/html-templates.js +1 -7
  125. package/dist/engine/docs-site-generator/markdown-renderer.js +1 -7
  126. package/dist/engine/drift/violation-resolver.js +2 -0
  127. package/dist/engine/dynamic-knowledge/knowledge-cache.js +1 -0
  128. package/dist/engine/ecosystem-absorber/knowledge-cache.js +1 -0
  129. package/dist/engine/edge-db-detector.js +1 -10
  130. package/dist/engine/escalator/retry-counter.js +1 -0
  131. package/dist/engine/evidence-gates/artifact-reader.js +1 -0
  132. package/dist/engine/evidence-gates/evidence-autofill.js +1 -0
  133. package/dist/engine/evidence-gates/evidence-skeletons.js +1 -0
  134. package/dist/engine/evidence-gates/reconciliation-freshness.js +1 -0
  135. package/dist/engine/execution/durable-job-heartbeat-runtime.js +1 -0
  136. package/dist/engine/execution/job-runtime.js +2 -0
  137. package/dist/engine/execution/validate-job-executor.d.ts +1 -1
  138. package/dist/engine/execution/validate-job-executor.js +3 -0
  139. package/dist/engine/execution/validate-job-payload.js +1 -0
  140. package/dist/engine/execution/validate-worker-thread.js +1 -0
  141. package/dist/engine/facilitate/context-loader.js +1 -0
  142. package/dist/engine/figma/change-detector.d.ts +1 -8
  143. package/dist/engine/figma/code-connect-fetcher.d.ts +1 -7
  144. package/dist/engine/figma/design-enricher.d.ts +1 -17
  145. package/dist/engine/figma/design-enricher.js +0 -1
  146. package/dist/engine/figma/e2e-generator.d.ts +1 -23
  147. package/dist/engine/figma/flow-analyzer.d.ts +1 -11
  148. package/dist/engine/figma/frame-extractor.d.ts +1 -11
  149. package/dist/engine/figma/frame-extractor.js +3 -0
  150. package/dist/engine/figma/multi-file-manager.d.ts +1 -6
  151. package/dist/engine/figma/spec-generator.js +2 -0
  152. package/dist/engine/figma/thumbnail-fetcher.d.ts +1 -1
  153. package/dist/engine/figma/token-extractor.d.ts +1 -6
  154. package/dist/engine/figma/token-extractor.js +0 -1
  155. package/dist/engine/figma/version-tracker.d.ts +1 -7
  156. package/dist/engine/figma/version-tracker.js +0 -1
  157. package/dist/engine/figma/visual-qa.d.ts +5 -19
  158. package/dist/engine/figma/visual-qa.js +1 -2
  159. package/dist/engine/figma/webhook-manager.d.ts +1 -7
  160. package/dist/engine/frontmatter-sha/reseal.js +1 -0
  161. package/dist/engine/frontmatter-sha/verify-terminal.js +1 -0
  162. package/dist/engine/git-hook-injector.js +2 -10
  163. package/dist/engine/github/quality-dimensions.js +1 -3
  164. package/dist/engine/global-rules-manager.js +2 -3
  165. package/dist/engine/handoff-artifacts/implementation-review-reader.js +1 -0
  166. package/dist/engine/handoff-artifacts/io.js +2 -0
  167. package/dist/engine/handoff-packager.js +2 -1
  168. package/dist/engine/health/auto-fixer.js +1 -0
  169. package/dist/engine/hooks/context-injector.js +1 -0
  170. package/dist/engine/hooks-reconciler.js +1 -8
  171. package/dist/engine/impact-detector/index.js +2 -9
  172. package/dist/engine/implementation-contract/common.js +1 -3
  173. package/dist/engine/implementation-contract/evaluator.js +1 -3
  174. package/dist/engine/keyword-matcher/boundary.d.ts +2 -7
  175. package/dist/engine/keyword-matcher/boundary.js +2 -14
  176. package/dist/engine/lifecycle-reconciliation-io.js +1 -0
  177. package/dist/engine/lifecycle-reconciliation.js +3 -0
  178. package/dist/engine/local-first/detector.js +1 -9
  179. package/dist/engine/marketplace-fetcher/cache.js +2 -0
  180. package/dist/engine/marketplace-fetcher/index.js +1 -0
  181. package/dist/engine/mcp-config/mcp-config-writer.js +2 -10
  182. package/dist/engine/migrations/remove-commercial-state.js +2 -0
  183. package/dist/engine/mobile-detector-shared.d.ts +1 -1
  184. package/dist/engine/mobile-detector-shared.js +2 -9
  185. package/dist/engine/multi-channel-notifier.js +1 -0
  186. package/dist/engine/multi-teammate-review/disagreement-counter.js +1 -0
  187. package/dist/engine/multi-teammate-review/panel-orchestrator.js +1 -0
  188. package/dist/engine/onboarding-engine.js +1 -9
  189. package/dist/engine/permissions-merger/stack-detectors.js +2 -10
  190. package/dist/engine/pii-detector/core.js +1 -3
  191. package/dist/engine/platform-engineering/dx-analyzer.js +1 -10
  192. package/dist/engine/platform-engineering/idp-detector.js +2 -10
  193. package/dist/engine/project-dna/ai-tool-detector.js +2 -10
  194. package/dist/engine/project-dna/stack-detector.js +2 -10
  195. package/dist/engine/project-graph/builder.js +11 -2
  196. package/dist/engine/project-graph/cache.d.ts +5 -2
  197. package/dist/engine/project-graph/cache.js +174 -34
  198. package/dist/engine/project-graph/extractors/decision-store-extractor.js +1 -0
  199. package/dist/engine/project-graph/freshness-projection.js +3 -0
  200. package/dist/engine/project-graph/query.js +14 -11
  201. package/dist/engine/project-health-checker.js +2 -6
  202. package/dist/engine/provider-adapters/shared/spec-loader.js +1 -0
  203. package/dist/engine/quality-gates/gate-evaluator.js +1 -3
  204. package/dist/engine/react-detector.js +6 -14
  205. package/dist/engine/reconcile/apply-changes.js +1 -3
  206. package/dist/engine/registry/core.js +1 -0
  207. package/dist/engine/registry/loader.js +2 -0
  208. package/dist/engine/release/postmortem-generator.js +1 -0
  209. package/dist/engine/release/rollback-runner.d.ts +2 -2
  210. package/dist/engine/release/stuck-spec-detector.js +7 -0
  211. package/dist/engine/release-pipeline/release-spec-closer.js +3 -0
  212. package/dist/engine/rules-reconciler.d.ts +1 -1
  213. package/dist/engine/rules-reconciler.js +2 -9
  214. package/dist/engine/safety/atomic-writer.js +1 -0
  215. package/dist/engine/safety/cross-process-lock.js +2 -0
  216. package/dist/engine/safety/file-mutex.js +1 -0
  217. package/dist/engine/safety/lock-lease-store.js +1 -0
  218. package/dist/engine/safety/lock-scavenger.js +2 -0
  219. package/dist/engine/saga/transition-cascade.js +3 -0
  220. package/dist/engine/scan-project/index.js +1 -0
  221. package/dist/engine/scan-project/module-discoverer.js +1 -9
  222. package/dist/engine/schema-parity-auditor/handler-checker.js +1 -3
  223. package/dist/engine/schema-parity-auditor/index.js +1 -3
  224. package/dist/engine/schema-parity-auditor/type-checker.js +1 -3
  225. package/dist/engine/self-healing/strategies/coverage-fix.js +2 -10
  226. package/dist/engine/session-context-generator.d.ts +8 -0
  227. package/dist/engine/session-context-generator.js +33 -23
  228. package/dist/engine/session-state/session-artifact-refresher.d.ts +7 -0
  229. package/dist/engine/session-state/session-artifact-refresher.js +140 -0
  230. package/dist/engine/session-state/writer.d.ts +3 -0
  231. package/dist/engine/session-state/writer.js +35 -17
  232. package/dist/engine/shared-agent-memory.js +1 -0
  233. package/dist/engine/skill-adapter/index.js +3 -0
  234. package/dist/engine/skill-bootstrap/registry-fetcher.js +1 -0
  235. package/dist/engine/skill-bootstrap/skill-writer.js +2 -9
  236. package/dist/engine/skill-bootstrap/stack-matcher.js +1 -9
  237. package/dist/engine/skill-discovery/multi-agent-writer.d.ts +1 -0
  238. package/dist/engine/skill-discovery/multi-agent-writer.js +1 -9
  239. package/dist/engine/skill-generator/workflow-skill-generator.js +1 -0
  240. package/dist/engine/skill-registry/ttl-refresh.js +2 -0
  241. package/dist/engine/spec-audit/index.js +2 -0
  242. package/dist/engine/spec-audit/report-writer.js +1 -0
  243. package/dist/engine/spec-coverage/core.js +1 -0
  244. package/dist/engine/spec-decomposer/analyzer.js +1 -11
  245. package/dist/engine/spec-dependency-graph/dot-exporter.d.ts +1 -1
  246. package/dist/engine/spec-dependency-graph/graph-builder.d.ts +1 -20
  247. package/dist/engine/spec-dependency-graph/superseded-set.d.ts +1 -1
  248. package/dist/engine/spec-diff-engine.js +1 -7
  249. package/dist/engine/spec-format/read-technical-section.js +1 -3
  250. package/dist/engine/spec-format/replace-section.js +1 -3
  251. package/dist/engine/spec-format/spec-id.d.ts +9 -1
  252. package/dist/engine/spec-format/spec-id.js +24 -3
  253. package/dist/engine/spec-format/unified-spec-builder.js +1 -3
  254. package/dist/engine/spec-migrator/canonical-deduplicator.js +2 -0
  255. package/dist/engine/spec-observability/tracker.js +1 -0
  256. package/dist/engine/spec-registry/adapter.js +2 -10
  257. package/dist/engine/spec-registry/validator.d.ts +1 -0
  258. package/dist/engine/spec-registry/validator.js +1 -9
  259. package/dist/engine/spec-state-machine/transition-spec.js +1 -0
  260. package/dist/engine/spec-templates/custom-loader.js +1 -0
  261. package/dist/engine/stack-auditor/build-tool-auditor.js +1 -9
  262. package/dist/engine/staleness/stale-implementing.js +5 -14
  263. package/dist/engine/status-reconciler/index.js +4 -1
  264. package/dist/engine/structural-memory/helpers.js +1 -0
  265. package/dist/engine/supply-chain/slsa-checker.js +1 -9
  266. package/dist/engine/tdd-enforcer/stub-artifact-path.js +1 -0
  267. package/dist/engine/telemetry/health-checker.js +1 -0
  268. package/dist/engine/telemetry/telemetry-store.js +1 -0
  269. package/dist/engine/token-optimizer/response-cache.js +1 -3
  270. package/dist/engine/tool-groups/group-manager.js +2 -0
  271. package/dist/engine/update-notifier.js +1 -0
  272. package/dist/engine/validation/approved-spec-portability.js +1 -0
  273. package/dist/engine/validation/durable-validation.js +1 -0
  274. package/dist/engine/validation/validation-freshness.js +2 -0
  275. package/dist/engine/validation/validation-receipt.js +1 -0
  276. package/dist/engine/validation/validation-submission.d.ts +1 -1
  277. package/dist/engine/validation-evidence-ledger.js +1 -0
  278. package/dist/engine/validator/deep-code-checker.js +1 -3
  279. package/dist/engine/validator/executable-evidence.d.ts +1 -14
  280. package/dist/engine/validator/scope-resolver.d.ts +2 -2
  281. package/dist/engine/validator/spec-compliance-runner.d.ts +1 -31
  282. package/dist/engine/validator/validation-report-writer.js +1 -1
  283. package/dist/engine/validator.d.ts +1 -1
  284. package/dist/engine/validator.js +1 -0
  285. package/dist/engine/velocity/measure-velocity.js +2 -0
  286. package/dist/engine/version-resolver.js +1 -0
  287. package/dist/engine/workers/handlers/auto-drift.js +3 -0
  288. package/dist/engine/workspace-manager.js +4 -0
  289. package/dist/hosts/gemini/config-scaffold.js +2 -10
  290. package/dist/storage/approval-operation-lock.js +1 -0
  291. package/dist/storage/auto-promoter-config-store.js +1 -0
  292. package/dist/storage/autopilot-log-store.js +1 -0
  293. package/dist/storage/base-store.js +1 -0
  294. package/dist/storage/checkpoint-store.js +1 -0
  295. package/dist/storage/feedback-remote.js +1 -0
  296. package/dist/storage/gaps-log.js +1 -0
  297. package/dist/storage/global-projects-store.d.ts +17 -0
  298. package/dist/storage/global-projects-store.js +47 -0
  299. package/dist/storage/global-store.js +1 -0
  300. package/dist/storage/knowledge-store/sessions.js +2 -0
  301. package/dist/storage/migrations/canonical-storage.js +42 -18
  302. package/dist/storage/project-drift-store.js +1 -0
  303. package/dist/storage/project-identity.d.ts +27 -0
  304. package/dist/storage/project-identity.js +132 -2
  305. package/dist/storage/project-resolver.js +1 -17
  306. package/dist/storage/runtime-db.d.ts +2 -20
  307. package/dist/storage/runtime-db.js +2 -9
  308. package/dist/storage/semantic-index-store.js +1 -0
  309. package/dist/storage/sentry-store.js +1 -0
  310. package/dist/storage/session-state-store.js +1 -0
  311. package/dist/storage/spec-store.d.ts +31 -3
  312. package/dist/storage/spec-store.js +146 -5
  313. package/dist/storage/status-store/atomic-status-write.js +1 -0
  314. package/dist/storage/status-store/file-lock.js +1 -0
  315. package/dist/storage/status-store/self-healing.js +1 -0
  316. package/dist/storage/storage-bundle.d.ts +12 -2
  317. package/dist/storage/supabase-store.js +1 -0
  318. package/dist/storage/technology-selection-store.js +1 -0
  319. package/dist/storage/trash-store.d.ts +2 -4
  320. package/dist/storage/trash-store.js +2 -15
  321. package/dist/storage/vector-store/json-fallback.js +1 -0
  322. package/dist/storage/vector-store/migrator.js +1 -0
  323. package/dist/storage/vector-store/sqlite-adapter.js +1 -0
  324. package/dist/tools/audit-claude-config.js +1 -10
  325. package/dist/tools/challenge-spec-helpers.d.ts +1 -1
  326. package/dist/tools/challenge-spec-helpers.js +7 -2
  327. package/dist/tools/challenge-spec.js +11 -6
  328. package/dist/tools/check-readiness.js +121 -24
  329. package/dist/tools/clarify-requirements/multiple-choice.d.ts +3 -3
  330. package/dist/tools/configure-oauth-handler.js +1 -0
  331. package/dist/tools/create-spec-helpers.d.ts +1 -3
  332. package/dist/tools/create-spec-helpers.js +1 -8
  333. package/dist/tools/create-spec.js +1 -13
  334. package/dist/tools/dashboard.js +5 -0
  335. package/dist/tools/generate-execution-plan.js +10 -6
  336. package/dist/tools/github-pr-handler.js +1 -0
  337. package/dist/tools/github-release-handler.d.ts +1 -0
  338. package/dist/tools/github-release-handler.js +4 -3
  339. package/dist/tools/graph-specs.d.ts +1 -1
  340. package/dist/tools/heal-spec-docs.js +1 -3
  341. package/dist/tools/init-project/git-setup.d.ts +2 -2
  342. package/dist/tools/init-project/host-assets-writer.js +2 -10
  343. package/dist/tools/init-project/lifecycle-helpers.d.ts +2 -2
  344. package/dist/tools/init-project/lint-scaffolder.js +2 -10
  345. package/dist/tools/init-project/stack-detector.d.ts +2 -2
  346. package/dist/tools/reconcile-interactive-question-hooks.d.ts +2 -2
  347. package/dist/tools/register-sdd-tools.js +1 -0
  348. package/dist/tools/safe-handler.js +1 -0
  349. package/dist/tools/schemas/spec-id.d.ts +1 -6
  350. package/dist/tools/schemas/spec-id.js +1 -16
  351. package/dist/tools/tool-entry.d.ts +2 -2
  352. package/dist/tools/tool-entry.js +1 -1
  353. package/dist/tools/tool-registry/core-tools.d.ts +2 -2
  354. package/dist/tools/tool-registry/deprecated-stubs.d.ts +3 -3
  355. package/dist/tools/tool-registry/deprecated-stubs.js +1 -1
  356. package/dist/tools/triage-request.d.ts +2 -2
  357. package/dist/tools/update-status/approval-gates.d.ts +2 -2
  358. package/dist/tools/update-status/approval-gates.js +1 -0
  359. package/dist/tools/update-status/dod-gates.d.ts +1 -1
  360. package/dist/tools/update-status/done-receipt-verifier.d.ts +1 -1
  361. package/dist/tools/update-status/evidence-gate.js +5 -5
  362. package/dist/tools/update-status/file-sync.js +11 -39
  363. package/dist/tools/update-status/index.js +1 -1
  364. package/dist/tools/update-status/side-effects.js +5 -0
  365. package/dist/tools/update-status/transition-guard.d.ts +1 -1
  366. package/dist/tools/update-status/transition-guard.js +40 -8
  367. package/dist/tools/update-status-actions.js +28 -40
  368. package/dist/tools/validate-graph-coverage.js +14 -2
  369. package/dist/tools/validate.js +2 -0
  370. package/dist/transports/middleware/types.d.ts +3 -3
  371. package/dist/types/atomic-upgrade.d.ts +1 -1
  372. package/dist/types/cascade-hooks.d.ts +3 -0
  373. package/dist/types/cross-repo-search.d.ts +2 -0
  374. package/dist/types/dashboard.d.ts +4 -1
  375. package/dist/types/durable-job.d.ts +20 -0
  376. package/dist/types/durable-job.js +9 -1
  377. package/dist/types/index.d.ts +2 -0
  378. package/dist/types/index.js +2 -0
  379. package/dist/types/installation.d.ts +16 -0
  380. package/dist/types/installation.js +2 -0
  381. package/dist/types/project-knowledge-graph.d.ts +11 -0
  382. package/dist/types/session-checkpoint.d.ts +16 -0
  383. package/dist/types/spec-dependency-graph.d.ts +21 -0
  384. package/dist/types/spec-dependency-graph.js +2 -0
  385. package/dist/types/validation-evidence.d.ts +45 -0
  386. package/package.json +2 -2
  387. package/planu-plugin.json +1 -1
  388. package/dist/types/storage-bundle.d.ts +0 -13
  389. package/dist/types/storage-bundle.js +0 -2
@@ -5,6 +5,7 @@
5
5
  // to the prior status.
6
6
  import { runSaga } from './cascade-saga.js';
7
7
  import { transitionSpec } from '../spec-state-machine/transition-spec.js';
8
+ // eslint-disable-next-line no-restricted-imports -- grandfathered layer violation, remediation SPEC-1661 SPEC-1662 SPEC-1663
8
9
  import { appendTransitionEvent } from '../../storage/transition-log.js';
9
10
  // ---------------------------------------------------------------------------
10
11
  // Step definitions
@@ -30,6 +31,7 @@ function buildCascadeSteps(_ctx) {
30
31
  if (!c.projectPath) {
31
32
  return null; // No project path — Slack config unavailable
32
33
  }
34
+ // eslint-disable-next-line no-restricted-syntax -- grandfathered layer violation, remediation SPEC-1661 SPEC-1662 SPEC-1663
33
35
  const { getSlackConfig } = await import('../../storage/slack-store.js');
34
36
  const slackConfig = await getSlackConfig(c.projectPath).catch(() => null);
35
37
  if (!slackConfig?.webhookUrl) {
@@ -100,6 +102,7 @@ function buildCascadeSteps(_ctx) {
100
102
  // Errors are NOT caught here — the saga catches them and records a warning
101
103
  // because critical:false. The try/catch is intentionally absent.
102
104
  run: async (c) => {
105
+ // eslint-disable-next-line no-restricted-syntax -- grandfathered layer violation, remediation SPEC-1661 SPEC-1662 SPEC-1663
103
106
  const { dispatchFeedbackEvent } = await import('../../tools/learn.js');
104
107
  await dispatchFeedbackEvent(c.projectId, {
105
108
  type: 'validate',
@@ -9,6 +9,7 @@ import { calculateProjectHealth } from './health-scorer.js';
9
9
  import { generateProjectOverview } from './overview-generator.js';
10
10
  import { generateTeamPlan } from './team-planner.js';
11
11
  import { runDeepAnalysis } from '../reverse-engineer/index.js';
12
+ // eslint-disable-next-line no-restricted-imports -- grandfathered layer violation, remediation SPEC-1661 SPEC-1662 SPEC-1663
12
13
  import { createSpec, listSpecs } from '../../storage/spec-store.js';
13
14
  // Re-export submodules for barrel access
14
15
  export { discoverModules } from './module-discoverer.js';
@@ -1,4 +1,5 @@
1
1
  import { technologyValue } from '../technology-registry.js';
2
+ import { fileExists } from '../../core/shared/fs.js';
2
3
  // engine/scan-project/module-discoverer.ts — Detects module boundaries in a project (SPEC-134)
3
4
  // Language-agnostic: supports monorepo, package-based, framework-based, and directory-based detection.
4
5
  import { readFile, readdir, stat } from 'node:fs/promises';
@@ -108,15 +109,6 @@ async function getSubdirs(dirPath) {
108
109
  return subdirs;
109
110
  }
110
111
  /** Checks if a file exists at the given path */
111
- async function fileExists(filePath) {
112
- try {
113
- await stat(filePath);
114
- return true;
115
- }
116
- catch {
117
- return false;
118
- }
119
- }
120
112
  /** Reads a file safely, returns empty string on error */
121
113
  async function safeRead(filePath) {
122
114
  try {
@@ -1,6 +1,7 @@
1
1
  // engine/schema-parity-auditor/handler-checker.ts — Checks handler files for safe access patterns
2
2
  import { readFile } from 'node:fs/promises';
3
3
  import { glob } from 'glob';
4
+ import { escapeRegex } from '../../core/shared/strings.js';
4
5
  /**
5
6
  * Finds the handler file for a given tool registration file.
6
7
  * Convention: register-foo-tools.ts → foo-handler.ts (or similar).
@@ -50,7 +51,4 @@ export function checkHandlerContent(fieldName, handlerFile, content) {
50
51
  hasIfCheck,
51
52
  };
52
53
  }
53
- function escapeRegex(str) {
54
- return str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
55
- }
56
54
  //# sourceMappingURL=handler-checker.js.map
@@ -4,6 +4,7 @@ import { glob } from 'glob';
4
4
  import { scanZodOptionalFields } from './zod-scanner.js';
5
5
  import { checkTypeOptional } from './type-checker.js';
6
6
  import { findHandlerFile, checkHandlerAccess } from './handler-checker.js';
7
+ import { escapeRegex } from '../../core/shared/strings.js';
7
8
  /**
8
9
  * Default type values to use when auto-fixing handlers.
9
10
  * Maps Zod base type → default value string.
@@ -112,7 +113,4 @@ async function fixTypeFile(fieldName, typeFile, dryRun) {
112
113
  preview: dryRun ? `- ${fieldName}: → ${fieldName}?:` : undefined,
113
114
  };
114
115
  }
115
- function escapeRegex(str) {
116
- return str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
117
- }
118
116
  //# sourceMappingURL=index.js.map
@@ -1,6 +1,7 @@
1
1
  // engine/schema-parity-auditor/type-checker.ts — Checks TypeScript types for optional mark
2
2
  import { readFile } from 'node:fs/promises';
3
3
  import { glob } from 'glob';
4
+ import { escapeRegex } from '../../core/shared/strings.js';
4
5
  /**
5
6
  * Checks whether a given fieldName has an optional mark ("?") in any TypeScript type
6
7
  * file under projectPath/src/types/.
@@ -40,7 +41,4 @@ export function checkFieldInContent(fieldName, filePath, content) {
40
41
  }
41
42
  return { found: false, hasOptionalMark: false, typeFile: '' };
42
43
  }
43
- function escapeRegex(str) {
44
- return str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
45
- }
46
44
  //# sourceMappingURL=type-checker.js.map
@@ -1,6 +1,7 @@
1
1
  // engine/self-healing/strategies/coverage-fix.ts — SPEC-474: Coverage fix strategy
2
- import { readFile, writeFile, mkdir, access } from 'node:fs/promises';
2
+ import { readFile, writeFile, mkdir } from 'node:fs/promises';
3
3
  import { join, dirname, basename, relative } from 'node:path';
4
+ import { fileExists } from '../../../core/shared/fs.js';
4
5
  /**
5
6
  * Reads coverage/coverage-final.json and returns files with 0% statement coverage.
6
7
  */
@@ -74,15 +75,6 @@ describe('${moduleName}', () => {
74
75
  /**
75
76
  * Checks whether a file already exists on the filesystem.
76
77
  */
77
- async function fileExists(path) {
78
- try {
79
- await access(path);
80
- return true;
81
- }
82
- catch {
83
- return false;
84
- }
85
- }
86
78
  /**
87
79
  * Scaffolds a single test file for an uncovered source file.
88
80
  * Returns the relative test path on success, or null if skipped/failed.
@@ -1,3 +1,11 @@
1
+ import type { Spec } from '../types/index.js';
2
+ /**
3
+ * SPEC-1320: Renders session-context.md content from a supplied active-spec snapshot.
4
+ * Pure aside from reading project version and recent lessons — no file write.
5
+ */
6
+ export declare function buildSessionContextContent(projectPath: string, allSpecs: Spec[], activeSpecs: Spec[]): Promise<string>;
7
+ /** Absolute path to a project's generated session-context.md. */
8
+ export declare function sessionContextPath(projectPath: string): string;
1
9
  /**
2
10
  * Auto-generates `planu/session-context.md` for a project.
3
11
  * Entirely best-effort: never throws, never blocks the caller.
@@ -2,8 +2,10 @@
2
2
  // Writes planu/session-context.md so any LLM (Cursor, Windsurf, Aider…) can pick up project state.
3
3
  import { readFile } from 'node:fs/promises';
4
4
  import { join } from 'node:path';
5
+ // eslint-disable-next-line no-restricted-imports -- grandfathered layer violation, remediation SPEC-1661 SPEC-1662 SPEC-1663
5
6
  import { specStore, lessonsStore } from '../storage/index.js';
6
7
  import { stableWriteGeneratedFile, stripSessionMarkdownVolatileFields, } from './generated-artifact-writer.js';
8
+ import { deriveActiveSpecSnapshot } from './session-state/writer.js';
7
9
  /** Reads the `version` field from the project's package.json, or returns 'unknown'. */
8
10
  async function readProjectVersion(projectPath) {
9
11
  try {
@@ -46,10 +48,7 @@ function getPendingApproved(specs) {
46
48
  }
47
49
  /** Returns active in-progress specs ordered by most recently updated first. */
48
50
  function getActiveSpecs(specs) {
49
- return specs
50
- .filter((s) => s.status === 'implementing' || s.status === 'review')
51
- .sort((a, b) => b.updatedAt.localeCompare(a.updatedAt))
52
- .slice(0, 5);
51
+ return deriveActiveSpecSnapshot(specs);
53
52
  }
54
53
  /** Fetches the last 3 lessons for the project via lessonsStore (best-effort). */
55
54
  async function fetchRecentLessons(projectPath) {
@@ -126,32 +125,43 @@ function buildMarkdown(params) {
126
125
  '*Generated by [Planu](https://planu.dev) — The complete SDD stack for AI agents*',
127
126
  ].join('\n');
128
127
  }
128
+ /**
129
+ * SPEC-1320: Renders session-context.md content from a supplied active-spec snapshot.
130
+ * Pure aside from reading project version and recent lessons — no file write.
131
+ */
132
+ export async function buildSessionContextContent(projectPath, allSpecs, activeSpecs) {
133
+ const [version, lessons] = await Promise.all([
134
+ readProjectVersion(projectPath),
135
+ fetchRecentLessons(projectPath),
136
+ ]);
137
+ const done = getRecentlyDone(allSpecs);
138
+ const approved = getPendingApproved(allSpecs);
139
+ return buildMarkdown({
140
+ version,
141
+ activeCount: activeSpecs.length,
142
+ doneCount: allSpecs.filter((s) => s.status === 'done').length,
143
+ approvedCount: approved.length,
144
+ activeRows: buildActiveRows(activeSpecs),
145
+ doneRows: buildDoneRows(done),
146
+ approvedRows: buildApprovedRows(approved),
147
+ lessonsContent: buildLessonsSection(lessons),
148
+ generatedAt: new Date().toISOString(),
149
+ });
150
+ }
151
+ /** Absolute path to a project's generated session-context.md. */
152
+ export function sessionContextPath(projectPath) {
153
+ return join(projectPath, 'planu', 'session-context.md');
154
+ }
129
155
  /**
130
156
  * Auto-generates `planu/session-context.md` for a project.
131
157
  * Entirely best-effort: never throws, never blocks the caller.
132
158
  */
133
159
  export async function generateSessionContext(projectPath, projectId) {
134
160
  try {
135
- const [version, allSpecs, lessons] = await Promise.all([
136
- readProjectVersion(projectPath),
137
- specStore.listSpecs(projectId),
138
- fetchRecentLessons(projectPath),
139
- ]);
161
+ const allSpecs = await specStore.listSpecs(projectId);
140
162
  const active = getActiveSpecs(allSpecs);
141
- const done = getRecentlyDone(allSpecs);
142
- const approved = getPendingApproved(allSpecs);
143
- const content = buildMarkdown({
144
- version,
145
- activeCount: active.length,
146
- doneCount: allSpecs.filter((s) => s.status === 'done').length,
147
- approvedCount: approved.length,
148
- activeRows: buildActiveRows(active),
149
- doneRows: buildDoneRows(done),
150
- approvedRows: buildApprovedRows(approved),
151
- lessonsContent: buildLessonsSection(lessons),
152
- generatedAt: new Date().toISOString(),
153
- });
154
- const contextPath = join(projectPath, 'planu', 'session-context.md');
163
+ const content = await buildSessionContextContent(projectPath, allSpecs, active);
164
+ const contextPath = sessionContextPath(projectPath);
155
165
  const writeResult = await stableWriteGeneratedFile(contextPath, content, {
156
166
  equivalence: stripSessionMarkdownVolatileFields,
157
167
  });
@@ -0,0 +1,7 @@
1
+ import type { RefreshSessionArtifactsResult, SessionArtifactRefreshFailureCode } from '../../types/index.js';
2
+ export declare class SessionArtifactRefreshError extends Error {
3
+ readonly code: SessionArtifactRefreshFailureCode;
4
+ constructor(code: SessionArtifactRefreshFailureCode);
5
+ }
6
+ export declare function refreshSessionArtifacts(projectPath: string, projectId: string): Promise<RefreshSessionArtifactsResult>;
7
+ //# sourceMappingURL=session-artifact-refresher.d.ts.map
@@ -0,0 +1,140 @@
1
+ import { readFile, writeFile, rename, unlink, mkdir } from 'node:fs/promises';
2
+ import { dirname } from 'node:path';
3
+ import { randomBytes } from 'node:crypto';
4
+ // eslint-disable-next-line no-restricted-imports -- grandfathered layer violation, remediation SPEC-1661 SPEC-1662 SPEC-1663
5
+ import { specStore } from '../../storage/index.js';
6
+ // eslint-disable-next-line no-restricted-imports -- grandfathered layer violation, remediation SPEC-1661 SPEC-1662 SPEC-1663
7
+ import { withFileLock } from '../../storage/file-mutex.js';
8
+ import { stableJsonStringify, stripRootUpdatedAt, stripSessionMarkdownVolatileFields, withFinalNewline, } from '../generated-artifact-writer.js';
9
+ import { buildSessionContextContent, sessionContextPath } from '../session-context-generator.js';
10
+ import { buildSessionIndexPatch, deriveActiveSpecSnapshot, rootFile } from './writer.js';
11
+ export class SessionArtifactRefreshError extends Error {
12
+ code;
13
+ constructor(code) {
14
+ super(`session-artifact-refresh-failed:${code}`);
15
+ this.name = 'SessionArtifactRefreshError';
16
+ this.code = code;
17
+ this.stack = undefined;
18
+ }
19
+ }
20
+ function isEnoent(err) {
21
+ return err instanceof Error && err.code === 'ENOENT';
22
+ }
23
+ async function prepareArtifact(finalPath, content, equivalence) {
24
+ const next = withFinalNewline(content);
25
+ let previous;
26
+ try {
27
+ previous = await readFile(finalPath, 'utf-8');
28
+ }
29
+ catch (err) {
30
+ if (!isEnoent(err)) {
31
+ throw new SessionArtifactRefreshError('STAGE_WRITE_FAILED');
32
+ }
33
+ previous = null;
34
+ }
35
+ let changed = true;
36
+ if (previous !== null) {
37
+ const stableNewline = previous.endsWith('\n') && !previous.endsWith('\n\n');
38
+ if (previous === next || (stableNewline && equivalence(previous) === equivalence(next))) {
39
+ changed = false;
40
+ }
41
+ }
42
+ return {
43
+ finalPath,
44
+ content: next,
45
+ tmpPath: `${finalPath}.tmp.${process.pid.toString()}.${randomBytes(4).toString('hex')}`,
46
+ changed,
47
+ previous,
48
+ };
49
+ }
50
+ async function cleanupTmp(artifacts) {
51
+ await Promise.all(artifacts.map((artifact) => unlink(artifact.tmpPath).catch(() => {
52
+ /* best-effort cleanup */
53
+ })));
54
+ }
55
+ async function restorePrevious(artifact) {
56
+ try {
57
+ if (artifact.previous === null) {
58
+ await unlink(artifact.finalPath);
59
+ }
60
+ else {
61
+ await writeFile(artifact.finalPath, artifact.previous, 'utf-8');
62
+ }
63
+ }
64
+ catch {
65
+ /* best-effort rollback */
66
+ }
67
+ }
68
+ async function commitPair(sessionJson, sessionContext) {
69
+ const staged = [sessionJson, sessionContext].filter((artifact) => artifact.changed);
70
+ if (staged.length === 0) {
71
+ return;
72
+ }
73
+ try {
74
+ await mkdir(dirname(sessionJson.finalPath), { recursive: true });
75
+ }
76
+ catch {
77
+ throw new SessionArtifactRefreshError('STAGE_WRITE_FAILED');
78
+ }
79
+ const writeOutcomes = await Promise.allSettled(staged.map((artifact) => writeFile(artifact.tmpPath, artifact.content, 'utf-8')));
80
+ const anyWriteFailed = writeOutcomes.some((outcome) => outcome.status === 'rejected');
81
+ if (anyWriteFailed) {
82
+ await cleanupTmp(staged);
83
+ throw new SessionArtifactRefreshError('STAGE_WRITE_FAILED');
84
+ }
85
+ const committed = [];
86
+ try {
87
+ for (const artifact of staged) {
88
+ await rename(artifact.tmpPath, artifact.finalPath);
89
+ committed.push(artifact);
90
+ }
91
+ }
92
+ catch {
93
+ await Promise.all(committed.map((artifact) => restorePrevious(artifact)));
94
+ await cleanupTmp(staged.filter((artifact) => !committed.includes(artifact)));
95
+ throw new SessionArtifactRefreshError('COMMIT_FAILED');
96
+ }
97
+ }
98
+ async function refreshLocked(projectPath, projectId) {
99
+ const allSpecs = await specStore.listSpecs(projectId);
100
+ const activeSpecs = deriveActiveSpecSnapshot(allSpecs);
101
+ const sessionJsonPath = rootFile(projectPath);
102
+ const contextMdPath = sessionContextPath(projectPath);
103
+ let existingIndex = { activeSpecs: [], updatedAt: '' };
104
+ try {
105
+ existingIndex = JSON.parse(await readFile(sessionJsonPath, 'utf-8'));
106
+ }
107
+ catch {
108
+ /* start fresh */
109
+ }
110
+ const nextIndex = buildSessionIndexPatch(existingIndex, allSpecs);
111
+ const sessionContextContent = await buildSessionContextContent(projectPath, allSpecs, activeSpecs);
112
+ const sessionJsonArtifact = await prepareArtifact(sessionJsonPath, stableJsonStringify(nextIndex), stripRootUpdatedAt);
113
+ const sessionContextArtifact = await prepareArtifact(contextMdPath, sessionContextContent, stripSessionMarkdownVolatileFields);
114
+ try {
115
+ await commitPair(sessionJsonArtifact, sessionContextArtifact);
116
+ }
117
+ catch (err) {
118
+ if (err instanceof SessionArtifactRefreshError) {
119
+ throw err;
120
+ }
121
+ throw new SessionArtifactRefreshError('UNEXPECTED_FAILURE');
122
+ }
123
+ return {
124
+ activeSpecIds: nextIndex.activeSpecs,
125
+ sessionJsonWritten: sessionJsonArtifact.changed,
126
+ sessionContextWritten: sessionContextArtifact.changed,
127
+ };
128
+ }
129
+ export async function refreshSessionArtifacts(projectPath, projectId) {
130
+ try {
131
+ return await withFileLock(`session-artifacts:${projectPath}`, () => refreshLocked(projectPath, projectId));
132
+ }
133
+ catch (err) {
134
+ if (err instanceof SessionArtifactRefreshError) {
135
+ throw err;
136
+ }
137
+ throw new SessionArtifactRefreshError('UNEXPECTED_FAILURE');
138
+ }
139
+ }
140
+ //# sourceMappingURL=session-artifact-refresher.js.map
@@ -1,4 +1,5 @@
1
1
  import type { SpecSessionState, SessionIndex, Spec } from '../../types/index.js';
2
+ export declare function rootFile(projectPath: string): string;
2
3
  /**
3
4
  * SPEC-633: Write per-spec session state and update root session.json index.
4
5
  * Best-effort — never throws.
@@ -6,6 +7,8 @@ import type { SpecSessionState, SessionIndex, Spec } from '../../types/index.js'
6
7
  export declare function writeSpecSessionState(projectPath: string, specId: string, specTitle: string, lastAction: string, testsStatus?: SpecSessionState['testsStatus'], contextUsage?: number): Promise<void>;
7
8
  /** SPEC-656: Compute epicProgress map from all project specs. Tags shared by >=3 specs are epics. */
8
9
  export declare function computeEpicProgress(specs: Spec[]): Record<string, string>;
10
+ export declare function deriveActiveSpecSnapshot(specs: Spec[]): Spec[];
11
+ export declare function buildSessionIndexPatch(existingIndex: SessionIndex, specs: Spec[]): SessionIndex;
9
12
  /**
10
13
  * SPEC-656: Update epicProgress in root session.json after any status change.
11
14
  * Best-effort — never throws.
@@ -12,7 +12,7 @@ function stateDir(projectPath) {
12
12
  function specFile(projectPath, specId) {
13
13
  return join(stateDir(projectPath), `session-${specId}.json`);
14
14
  }
15
- function rootFile(projectPath) {
15
+ export function rootFile(projectPath) {
16
16
  return join(projectPath, 'planu', ROOT_SESSION);
17
17
  }
18
18
  function getModifiedFiles(projectPath) {
@@ -123,13 +123,39 @@ export function computeEpicProgress(specs) {
123
123
  }
124
124
  return progress;
125
125
  }
126
- function reconcileActiveSpecIds(existing, specs) {
127
- const activeFromSpecs = specs
126
+ const ACTIVE_SPEC_LIMIT = 5;
127
+ export function deriveActiveSpecSnapshot(specs) {
128
+ const sorted = specs
128
129
  .filter((spec) => spec.status === 'implementing' || spec.status === 'review')
129
- .sort((a, b) => b.updatedAt.localeCompare(a.updatedAt))
130
- .map((spec) => spec.id);
131
- const knownActive = new Set(activeFromSpecs);
132
- return [...new Set(existing.filter((specId) => knownActive.has(specId)).concat(activeFromSpecs))];
130
+ .sort((a, b) => b.updatedAt.localeCompare(a.updatedAt) || b.id.localeCompare(a.id));
131
+ const seenIds = new Set();
132
+ const deduped = [];
133
+ for (const spec of sorted) {
134
+ if (seenIds.has(spec.id)) {
135
+ continue;
136
+ }
137
+ seenIds.add(spec.id);
138
+ deduped.push(spec);
139
+ }
140
+ return deduped.slice(0, ACTIVE_SPEC_LIMIT);
141
+ }
142
+ function reconcileActiveSpecIds(specs) {
143
+ return deriveActiveSpecSnapshot(specs).map((spec) => spec.id);
144
+ }
145
+ export function buildSessionIndexPatch(existingIndex, specs) {
146
+ const epicProgress = computeEpicProgress(specs);
147
+ const next = {
148
+ ...existingIndex,
149
+ activeSpecs: reconcileActiveSpecIds(specs),
150
+ updatedAt: new Date().toISOString(),
151
+ };
152
+ if (Object.keys(epicProgress).length === 0) {
153
+ delete next.epicProgress;
154
+ }
155
+ else {
156
+ next.epicProgress = epicProgress;
157
+ }
158
+ return next;
133
159
  }
134
160
  /**
135
161
  * SPEC-656: Update epicProgress in root session.json after any status change.
@@ -137,7 +163,6 @@ function reconcileActiveSpecIds(existing, specs) {
137
163
  */
138
164
  export async function updateEpicProgressInSession(projectPath, specs) {
139
165
  try {
140
- const epicProgress = computeEpicProgress(specs);
141
166
  const filePath = rootFile(projectPath);
142
167
  let existingContent = null;
143
168
  let index = { activeSpecs: [], updatedAt: '' };
@@ -148,15 +173,8 @@ export async function updateEpicProgressInSession(projectPath, specs) {
148
173
  catch {
149
174
  /* start fresh */
150
175
  }
151
- index.activeSpecs = reconcileActiveSpecIds(index.activeSpecs, specs);
152
- if (Object.keys(epicProgress).length === 0) {
153
- delete index.epicProgress;
154
- }
155
- else {
156
- index.epicProgress = epicProgress;
157
- }
158
- index.updatedAt = new Date().toISOString();
159
- await stableWriteGeneratedFile(filePath, stableJsonStringify(index), {
176
+ const next = buildSessionIndexPatch(index, specs);
177
+ await stableWriteGeneratedFile(filePath, stableJsonStringify(next), {
160
178
  equivalence: stripRootUpdatedAt,
161
179
  existingContent,
162
180
  });
@@ -1,6 +1,7 @@
1
1
  // engine/shared-agent-memory.ts — SPEC-689
2
2
  import { readFile, mkdir, rename } from 'node:fs/promises';
3
3
  import { join } from 'node:path';
4
+ // eslint-disable-next-line no-restricted-imports -- grandfathered layer violation, remediation SPEC-1661 SPEC-1662 SPEC-1663
4
5
  import { hashProjectPath, projectDataDir } from '../storage/base-store.js';
5
6
  import { acquireLock, releaseLock } from '../engine/safety/file-mutex.js';
6
7
  import { atomicWriteFile } from '../engine/safety/atomic-write-file.js';
@@ -2,10 +2,13 @@
2
2
  // Replaces placeholders and rewrites commands based on ProjectKnowledge.
3
3
  import { mkdir, readFile, writeFile } from 'node:fs/promises';
4
4
  import { join } from 'node:path';
5
+ // eslint-disable-next-line no-restricted-imports -- grandfathered layer violation, remediation SPEC-1661 SPEC-1662 SPEC-1663
5
6
  import { readSkillRegistry, writeSkillRegistryEntry, } from '../../storage/skill-registry-storage.js';
6
7
  import { PLACEHOLDER_MAP } from './placeholder-map.js';
7
8
  import { rewritePackageManagerCommands, rewriteTestRunnerCommands } from './command-rewriter.js';
9
+ // eslint-disable-next-line no-restricted-imports -- grandfathered layer violation, remediation SPEC-1661 SPEC-1662 SPEC-1663
8
10
  import { hashProjectPath } from '../../storage/base-store.js';
11
+ // eslint-disable-next-line no-restricted-imports -- grandfathered layer violation, remediation SPEC-1661 SPEC-1662 SPEC-1663
9
12
  import { resolveStorageLayout } from '../../storage/storage-layout.js';
10
13
  export { PLACEHOLDER_MAP } from './placeholder-map.js';
11
14
  export { rewritePackageManagerCommands, rewriteTestRunnerCommands } from './command-rewriter.js';
@@ -1,3 +1,4 @@
1
+ // eslint-disable-next-line no-restricted-imports -- grandfathered layer violation, remediation SPEC-1661 SPEC-1662 SPEC-1663
1
2
  import { getSkillBootstrapConfig } from '../../storage/skill-bootstrap-store.js';
2
3
  import { detectProjectStack, matchTechsToAvailable } from './stack-matcher.js';
3
4
  const FETCH_TIMEOUT_MS = 5000;
@@ -1,7 +1,8 @@
1
1
  // engine/skill-bootstrap/skill-writer.ts — SPEC-439: Write skills to target AI tool files
2
- import { readFile, writeFile, mkdir } from 'node:fs/promises';
2
+ import { writeFile, mkdir } from 'node:fs/promises';
3
3
  import { join, dirname } from 'node:path';
4
4
  import { assertEnglishOnlyArtifactText } from '../spec-language/english-only.js';
5
+ import { readFileOrNull as readFileSafe } from '../../core/shared/fs.js';
5
6
  const AUTO_SECTION_HEADER = '## Auto-installed Skills (Planu)';
6
7
  function targetToRelativePath(target) {
7
8
  switch (target) {
@@ -23,14 +24,6 @@ function targetToRelativePath(target) {
23
24
  }
24
25
  }
25
26
  }
26
- async function readFileSafe(filePath) {
27
- try {
28
- return await readFile(filePath, 'utf-8');
29
- }
30
- catch {
31
- return null;
32
- }
33
- }
34
27
  export function formatSkillsForTarget(skills, target) {
35
28
  if (target === 'cursorrules' || target === 'windsurfrules' || target === 'clinerules') {
36
29
  return skills.map((s) => s.content).join('\n\n');
@@ -1,7 +1,7 @@
1
1
  import { technologyValue } from '../technology-registry.js';
2
2
  // engine/skill-bootstrap/stack-matcher.ts — SPEC-439: Detect project stack and match to skills
3
- import { readFile } from 'node:fs/promises';
4
3
  import { join } from 'node:path';
4
+ import { readFileOrNull as readFileSafe } from '../../core/shared/fs.js';
5
5
  const DEP_TO_TECH = {
6
6
  // Frontend frameworks
7
7
  next: technologyValue('technology-nextjs-c93379'),
@@ -114,14 +114,6 @@ const DEP_TO_TECH = {
114
114
  sqlalchemy: technologyValue('technology-sqlalchemy-37f268'),
115
115
  pydantic: technologyValue('technology-pydantic-636623'),
116
116
  };
117
- async function readFileSafe(filePath) {
118
- try {
119
- return await readFile(filePath, 'utf-8');
120
- }
121
- catch {
122
- return null;
123
- }
124
- }
125
117
  async function detectFromPackageJson(projectPath) {
126
118
  const content = await readFileSafe(join(projectPath, 'package.json'));
127
119
  if (!content) {
@@ -1,4 +1,5 @@
1
1
  import type { SkillWriteTarget, WrittenSkillFile } from '../../types/skill-generation.js';
2
+ /** Check if a file exists on disk. */
2
3
  /**
3
4
  * Write skill content to the appropriate locations for each requested agent.
4
5
  *
@@ -2,6 +2,7 @@
2
2
  // Maps skill content to the file location expected by each supported AI agent/IDE.
3
3
  import * as fs from 'node:fs/promises';
4
4
  import * as path from 'node:path';
5
+ import { fileExists } from '../../core/shared/fs.js';
5
6
  // ---------------------------------------------------------------------------
6
7
  // Agent path resolvers
7
8
  // ---------------------------------------------------------------------------
@@ -74,15 +75,6 @@ function replaceSection(fileContent, skillName, newSection) {
74
75
  return (fileContent.slice(0, start) + newSection.trimStart() + fileContent.slice(end + endMarker.length));
75
76
  }
76
77
  /** Check if a file exists on disk. */
77
- async function fileExists(filePath) {
78
- try {
79
- await fs.access(filePath);
80
- return true;
81
- }
82
- catch {
83
- return false;
84
- }
85
- }
86
78
  // ---------------------------------------------------------------------------
87
79
  // Public API
88
80
  // ---------------------------------------------------------------------------
@@ -6,6 +6,7 @@ import { technologyValue } from '../technology-registry.js';
6
6
  import { mkdir, readFile, writeFile } from 'node:fs/promises';
7
7
  import { join } from 'node:path';
8
8
  import { adaptSkillContent } from '../skill-adapter/index.js';
9
+ // eslint-disable-next-line no-restricted-imports -- grandfathered layer violation, remediation SPEC-1661 SPEC-1662 SPEC-1663
9
10
  import { writeSkillRegistryEntry } from '../../storage/skill-registry-storage.js';
10
11
  import { WORKFLOW_SKILL_DEFINITIONS } from './skill-definitions.js';
11
12
  import { hashConventions } from './conventions-hasher.js';
@@ -3,7 +3,9 @@
3
3
  import { readFile, writeFile, mkdir, appendFile } from 'node:fs/promises';
4
4
  import { join } from 'node:path';
5
5
  import { fetchAnthropicSkillContent } from './anthropic-adapter.js';
6
+ // eslint-disable-next-line no-restricted-imports -- grandfathered layer violation, remediation SPEC-1661 SPEC-1662 SPEC-1663
6
7
  import { hashProjectPath } from '../../storage/base-store.js';
8
+ // eslint-disable-next-line no-restricted-imports -- grandfathered layer violation, remediation SPEC-1661 SPEC-1662 SPEC-1663
7
9
  import { resolveStorageLayout } from '../../storage/storage-layout.js';
8
10
  const TTL_MS = 30 * 24 * 60 * 60 * 1000;
9
11
  const SKILLS_DIR = '.claude/skills';
@@ -1,7 +1,9 @@
1
1
  // engine/spec-audit/index.ts — SPEC-744
2
2
  // Orchestrates the two-tier spec drift audit.
3
3
  import { join } from 'node:path';
4
+ // eslint-disable-next-line no-restricted-imports -- grandfathered layer violation, remediation SPEC-1661 SPEC-1662 SPEC-1663
4
5
  import { listSpecs } from '../../storage/spec-store.js';
6
+ // eslint-disable-next-line no-restricted-imports -- grandfathered layer violation, remediation SPEC-1661 SPEC-1662 SPEC-1663
5
7
  import { hashProjectPath, projectDataDir } from '../../storage/base-store.js';
6
8
  import { buildSpecGraph } from '../spec-dependency-graph/graph-builder.js';
7
9
  import { getSupersededSet, getSupersededMap } from '../spec-dependency-graph/superseded-set.js';
@@ -4,6 +4,7 @@ import { mkdir, readFile, writeFile } from 'node:fs/promises';
4
4
  import { join, dirname } from 'node:path';
5
5
  import { existsSync } from 'node:fs';
6
6
  import { atomicWriteFile } from '../safety/atomic-write-file.js';
7
+ // eslint-disable-next-line no-restricted-imports -- grandfathered layer violation, remediation SPEC-1661 SPEC-1662 SPEC-1663
7
8
  import { hashProjectPath, projectDataDir } from '../../storage/base-store.js';
8
9
  // ---------------------------------------------------------------------------
9
10
  // Markdown report
@@ -2,6 +2,7 @@
2
2
  // Coordinates test-finder, criteria-mapper, scoring, and persistence.
3
3
  import { readFile } from 'node:fs/promises';
4
4
  import { join } from 'node:path';
5
+ // eslint-disable-next-line no-restricted-imports -- grandfathered layer violation, remediation SPEC-1661 SPEC-1662 SPEC-1663
5
6
  import { readJson, writeJson, projectDataDir } from '../../storage/base-store.js';
6
7
  import { findTestFiles, detectTestRunners, readTestFileContent } from './test-finder.js';
7
8
  import { extractCriteria, mapTestsToCriterion } from './criteria-mapper.js';