@planu/cli 4.14.1 → 5.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 (424) hide show
  1. package/CHANGELOG.md +35 -1
  2. package/LICENSE +55 -12
  3. package/README.md +10 -2
  4. package/dist/cli/commands/install.d.ts +1 -12
  5. package/dist/cli/commands/install.js +9 -65
  6. package/dist/cli/commands/job.js +4 -3
  7. package/dist/cli/commands/spec.js +2 -2
  8. package/dist/cli/commands/validate.js +2 -2
  9. package/dist/cli/router.js +1 -3
  10. package/dist/config/environment-schema.json +1 -3
  11. package/dist/config/official-sdd-tools.d.ts +4 -0
  12. package/dist/config/official-sdd-tools.js +35 -0
  13. package/dist/config/product-public.d.ts +139 -3
  14. package/dist/config/product-public.js +8 -1
  15. package/dist/config/product-public.json +7 -36
  16. package/dist/config/registries/hosts/codex.json +20 -363
  17. package/dist/config/release-policy.json +84 -2
  18. package/dist/config/runtime-policy.json +12 -1
  19. package/dist/config/skill-templates/planu-resume-work.md +2 -2
  20. package/dist/core/index.d.ts +1 -1
  21. package/dist/core/spec-validator.d.ts +4 -4
  22. package/dist/core/spec-validator.js +1 -1
  23. package/dist/engine/abortable-process-runner.d.ts +20 -2
  24. package/dist/engine/abortable-process-runner.js +243 -74
  25. package/dist/engine/ambiguity-scorer.js +4 -26
  26. package/dist/engine/auth/scope-mapper.js +1 -7
  27. package/dist/engine/autopilot/state-updater.js +4 -4
  28. package/dist/engine/cascade-hooks/hooks/review-enricher.hook.js +9 -6
  29. package/dist/engine/cascade-hooks/state-drift-detector.d.ts +3 -3
  30. package/dist/engine/cascade-hooks/state-drift-detector.js +1 -1
  31. package/dist/engine/ci-generator/planu-config.js +1 -7
  32. package/dist/engine/clarification-gate/gate.d.ts +3 -3
  33. package/dist/engine/code-scanner/layer-scanner.js +6 -22
  34. package/dist/engine/core-bridge.d.ts +3 -3
  35. package/dist/engine/cost-guardrails.d.ts +1 -1
  36. package/dist/engine/cost-guardrails.js +1 -1
  37. package/dist/engine/criterion-identity.d.ts +1 -0
  38. package/dist/engine/criterion-identity.js +7 -0
  39. package/dist/engine/dashboard/data-loader.js +2 -7
  40. package/dist/engine/dashboard/templates-layout.js +0 -3
  41. package/dist/engine/dashboard/templates-project.js +1 -13
  42. package/dist/engine/docs-site-generator/data-collector.d.ts +1 -1
  43. package/dist/engine/docs-site-generator/data-collector.js +7 -28
  44. package/dist/engine/docs-site-generator/tools-renderer.js +3 -10
  45. package/dist/engine/evidence-gates/artifact-reader.d.ts +10 -1
  46. package/dist/engine/evidence-gates/artifact-reader.js +115 -2
  47. package/dist/engine/evidence-gates/lifecycle-gate.js +4 -3
  48. package/dist/engine/evidence-gates/reconciliation-freshness.d.ts +11 -0
  49. package/dist/engine/evidence-gates/reconciliation-freshness.js +72 -0
  50. package/dist/engine/evidence-index/done-drift.d.ts +1 -1
  51. package/dist/engine/evidence-index/done-drift.js +40 -18
  52. package/dist/engine/execution/context.js +3 -0
  53. package/dist/engine/execution/durable-job-heartbeat-runtime.d.ts +4 -0
  54. package/dist/engine/execution/durable-job-heartbeat-runtime.js +400 -0
  55. package/dist/engine/execution/job-runtime.d.ts +30 -0
  56. package/dist/engine/execution/job-runtime.js +114 -6
  57. package/dist/engine/execution/public-job-sanitizer.d.ts +4 -0
  58. package/dist/engine/execution/public-job-sanitizer.js +31 -0
  59. package/dist/engine/execution/validate-job-executor.d.ts +12 -1
  60. package/dist/engine/execution/validate-job-executor.js +549 -86
  61. package/dist/engine/execution/validate-job-payload.js +107 -50
  62. package/dist/engine/execution/validate-job-recovery.js +1 -1
  63. package/dist/engine/execution/validate-worker-runtime.d.ts +5 -0
  64. package/dist/engine/execution/validate-worker-runtime.js +90 -2
  65. package/dist/engine/execution/validate-worker-thread.js +17 -24
  66. package/dist/engine/handoff-artifacts/schemas.d.ts +2 -1
  67. package/dist/engine/handoff-artifacts/schemas.js +33 -0
  68. package/dist/engine/handoff-packager.d.ts +19 -0
  69. package/dist/engine/handoff-packager.js +415 -13
  70. package/dist/engine/hooks/effort-configurator.js +1 -1
  71. package/dist/engine/impact-detector/test-break-predictor.js +3 -3
  72. package/dist/engine/impact-detector/tool-registration.js +1 -1
  73. package/dist/engine/implementation-contract/renderer.js +23 -8
  74. package/dist/engine/lifecycle-reconciliation-io.d.ts +11 -0
  75. package/dist/engine/lifecycle-reconciliation-io.js +180 -0
  76. package/dist/engine/lifecycle-reconciliation.d.ts +16 -0
  77. package/dist/engine/lifecycle-reconciliation.js +406 -0
  78. package/dist/engine/local-first/tool-classification.js +2 -0
  79. package/dist/engine/macos-keychain-native-port.d.ts +5 -0
  80. package/dist/engine/macos-keychain-native-port.js +19 -7
  81. package/dist/engine/migrations/remove-commercial-state.d.ts +3 -0
  82. package/dist/engine/migrations/remove-commercial-state.js +343 -0
  83. package/dist/engine/network-policy.js +1 -0
  84. package/dist/engine/planu-core.darwin-arm64.node.manifest.json +7 -7
  85. package/dist/engine/planu-core.darwin-arm64.node.sbom.json +10 -10
  86. package/dist/engine/planu-core.darwin-x64.node.manifest.json +7 -7
  87. package/dist/engine/planu-core.darwin-x64.node.sbom.json +10 -10
  88. package/dist/engine/planu-core.linux-arm64-gnu.node.manifest.json +7 -7
  89. package/dist/engine/planu-core.linux-arm64-gnu.node.sbom.json +10 -10
  90. package/dist/engine/planu-core.linux-arm64-musl.node.manifest.json +7 -7
  91. package/dist/engine/planu-core.linux-arm64-musl.node.sbom.json +10 -10
  92. package/dist/engine/planu-core.linux-x64-gnu.node.manifest.json +7 -7
  93. package/dist/engine/planu-core.linux-x64-gnu.node.sbom.json +10 -10
  94. package/dist/engine/planu-core.linux-x64-musl.node.manifest.json +7 -7
  95. package/dist/engine/planu-core.linux-x64-musl.node.sbom.json +10 -10
  96. package/dist/engine/planu-core.win32-arm64-msvc.node.manifest.json +7 -7
  97. package/dist/engine/planu-core.win32-arm64-msvc.node.sbom.json +10 -10
  98. package/dist/engine/planu-core.win32-x64-msvc.node.manifest.json +7 -7
  99. package/dist/engine/planu-core.win32-x64-msvc.node.sbom.json +10 -10
  100. package/dist/engine/readiness-checker.d.ts +1 -16
  101. package/dist/engine/readiness-checker.js +27 -88
  102. package/dist/engine/runtime-policy.d.ts +2 -0
  103. package/dist/engine/runtime-policy.js +42 -5
  104. package/dist/engine/runtime-security/rate-limiter.js +0 -1
  105. package/dist/engine/safety/contained-project-file.d.ts +3 -0
  106. package/dist/engine/safety/contained-project-file.js +23 -0
  107. package/dist/engine/scope-boundaries/contradiction-checker.js +63 -1
  108. package/dist/engine/scope-boundaries/scope-validator.js +45 -7
  109. package/dist/engine/session/checkpoint-writer.js +0 -9
  110. package/dist/engine/spec-format/acceptance-criteria.d.ts +6 -2
  111. package/dist/engine/spec-format/acceptance-criteria.js +77 -18
  112. package/dist/engine/spec-format/markdown-sections.d.ts +14 -0
  113. package/dist/engine/spec-format/markdown-sections.js +105 -0
  114. package/dist/engine/spec-format/read-technical-section.js +33 -5
  115. package/dist/engine/spec-format/technical-md-populator.js +3 -2
  116. package/dist/engine/spec-format/unified-spec-builder.d.ts +7 -0
  117. package/dist/engine/spec-format/unified-spec-builder.js +202 -0
  118. package/dist/engine/spec-migrator/canonical-deduplicator.d.ts +18 -0
  119. package/dist/engine/spec-migrator/canonical-deduplicator.js +479 -0
  120. package/dist/engine/spec-migrator/filesystem-import.d.ts +4 -8
  121. package/dist/engine/spec-migrator/filesystem-import.js +121 -14
  122. package/dist/engine/spec-migrator/frontmatter-parser.js +32 -15
  123. package/dist/engine/spec-migrator/index.d.ts +1 -0
  124. package/dist/engine/spec-migrator/index.js +1 -0
  125. package/dist/engine/spec-state-machine/transition-spec.d.ts +6 -1
  126. package/dist/engine/spec-state-machine/transition-spec.js +10 -1
  127. package/dist/engine/technical-enricher/index.js +16 -8
  128. package/dist/engine/technical-enricher/render-enriched.d.ts +1 -0
  129. package/dist/engine/technical-enricher/render-enriched.js +42 -14
  130. package/dist/engine/telemetry/health-checker.js +53 -17
  131. package/dist/engine/telemetry/telemetry-client.js +26 -8
  132. package/dist/engine/text-signal-boundaries.d.ts +2 -0
  133. package/dist/engine/text-signal-boundaries.js +25 -2
  134. package/dist/engine/time/relative-date.d.ts +3 -0
  135. package/dist/engine/time/relative-date.js +16 -0
  136. package/dist/engine/timing/budget.d.ts +11 -0
  137. package/dist/engine/timing/budget.js +27 -9
  138. package/dist/engine/token-optimizer/analytics.d.ts +1 -1
  139. package/dist/engine/token-optimizer/analytics.js +5 -6
  140. package/dist/engine/usage-tracker/core.d.ts +2 -2
  141. package/dist/engine/usage-tracker/core.js +1 -2
  142. package/dist/engine/usage-tracker/index.d.ts +0 -3
  143. package/dist/engine/usage-tracker/index.js +0 -3
  144. package/dist/engine/usage-tracker/stats.d.ts +2 -2
  145. package/dist/engine/usage-tracker/stats.js +1 -28
  146. package/dist/engine/validation/durable-validation.d.ts +1 -1
  147. package/dist/engine/validation/durable-validation.js +3 -82
  148. package/dist/engine/validation/validation-freshness.d.ts +11 -1
  149. package/dist/engine/validation/validation-freshness.js +231 -91
  150. package/dist/engine/validation/validation-receipt.d.ts +9 -6
  151. package/dist/engine/validation/validation-receipt.js +84 -24
  152. package/dist/engine/validation/validation-source-digest.d.ts +2 -0
  153. package/dist/engine/validation/validation-source-digest.js +86 -0
  154. package/dist/engine/validation/validation-submission.d.ts +5 -4
  155. package/dist/engine/validation/validation-submission.js +25 -11
  156. package/dist/engine/validation/validation-worktree.d.ts +21 -0
  157. package/dist/engine/validation/validation-worktree.js +260 -0
  158. package/dist/engine/validation-impact-planner.d.ts +14 -2
  159. package/dist/engine/validation-impact-planner.js +0 -3
  160. package/dist/engine/validator/dor-dod.d.ts +1 -0
  161. package/dist/engine/validator/dor-dod.js +5 -3
  162. package/dist/engine/validator/extractors.d.ts +1 -5
  163. package/dist/engine/validator/extractors.js +24 -69
  164. package/dist/engine/validator/spec-compliance-runner.d.ts +12 -24
  165. package/dist/engine/validator/spec-compliance-runner.js +429 -211
  166. package/dist/engine/validator.js +35 -22
  167. package/dist/index.js +4 -16
  168. package/dist/resources/usage-stats-resource.js +2 -18
  169. package/dist/security/adapters/linux-secret-service.js +7 -3
  170. package/dist/security/adapters/windows-credential-manager.js +6 -4
  171. package/dist/server/routes/specs.js +10 -0
  172. package/dist/storage/base-store.d.ts +1 -1
  173. package/dist/storage/base-store.js +1 -1
  174. package/dist/storage/feedback-remote.d.ts +2 -3
  175. package/dist/storage/feedback-remote.js +45 -31
  176. package/dist/storage/global-store.d.ts +1 -17
  177. package/dist/storage/global-store.js +1 -42
  178. package/dist/storage/index.d.ts +0 -2
  179. package/dist/storage/index.js +0 -2
  180. package/dist/storage/migrations/canonical-storage.js +124 -11
  181. package/dist/storage/runtime-db.d.ts +9 -0
  182. package/dist/storage/runtime-db.js +64 -8
  183. package/dist/storage/spec-store.d.ts +14 -2
  184. package/dist/storage/spec-store.js +104 -14
  185. package/dist/storage/status-store/atomic-status-write.d.ts +5 -0
  186. package/dist/storage/status-store/atomic-status-write.js +57 -0
  187. package/dist/storage/status-store/file-lock.js +30 -11
  188. package/dist/storage/status-store/self-healing.js +3 -3
  189. package/dist/storage/status-store/version-sync.d.ts +2 -1
  190. package/dist/storage/status-store/version-sync.js +35 -40
  191. package/dist/storage/usage-store.d.ts +1 -5
  192. package/dist/storage/usage-store.js +0 -35
  193. package/dist/tools/auto-update-handler.d.ts +3 -3
  194. package/dist/tools/auto-update-handler.js +3 -3
  195. package/dist/tools/challenge-spec/resilience-challenge-scenarios.d.ts +2 -1
  196. package/dist/tools/challenge-spec/resilience-challenge-scenarios.js +10 -7
  197. package/dist/tools/challenge-spec/scenario-collector.js +1 -1
  198. package/dist/tools/challenge-spec/scenarios-data.js +1 -1
  199. package/dist/tools/challenge-spec/scenarios-utils.d.ts +1 -1
  200. package/dist/tools/challenge-spec/scenarios-utils.js +35 -11
  201. package/dist/tools/challenge-spec.js +5 -5
  202. package/dist/tools/check-readiness.js +1 -1
  203. package/dist/tools/configure-telemetry.js +30 -2
  204. package/dist/tools/create-spec/spec-builder.js +0 -11
  205. package/dist/tools/create-spec.d.ts +1 -0
  206. package/dist/tools/create-spec.js +444 -239
  207. package/dist/tools/git/branch-ops.d.ts +1 -1
  208. package/dist/tools/git/branch-ops.js +3 -2
  209. package/dist/tools/init-project/handler.js +10 -12
  210. package/dist/tools/init-project/host-assets-writer.js +26 -1
  211. package/dist/tools/init-project/lint-scaffolder.d.ts +3 -3
  212. package/dist/tools/init-project/migration-runner.d.ts +2 -4
  213. package/dist/tools/init-project/migration-runner.js +3 -2
  214. package/dist/tools/init-project/portable-index-reconciler.d.ts +4 -0
  215. package/dist/tools/init-project/portable-index-reconciler.js +14 -0
  216. package/dist/tools/init-project/result-builder.js +1 -3
  217. package/dist/tools/jobs/handlers.js +38 -6
  218. package/dist/tools/list-specs.js +5 -26
  219. package/dist/tools/merge-risk-handler.js +2 -2
  220. package/dist/tools/package-handoff.js +2 -0
  221. package/dist/tools/reconcile-spec.js +0 -27
  222. package/dist/tools/register-agent-squad-tools.js +2 -2
  223. package/dist/tools/register-platform-tools/design-stack-tools.js +11 -11
  224. package/dist/tools/register-platform-tools/lifecycle-infra-tools.js +7 -7
  225. package/dist/tools/register-sdd-tools.d.ts +1 -1
  226. package/dist/tools/register-sdd-tools.js +2 -29
  227. package/dist/tools/register-spec-tools/analysis-tools.js +8 -8
  228. package/dist/tools/register-spec-tools/core-spec-tools.js +17 -8
  229. package/dist/tools/safe-handler.d.ts +5 -13
  230. package/dist/tools/safe-handler.js +7 -23
  231. package/dist/tools/schemas/index.d.ts +1 -1
  232. package/dist/tools/schemas/index.js +1 -1
  233. package/dist/tools/schemas/output-schemas.d.ts +0 -23
  234. package/dist/tools/schemas/output-schemas.js +0 -28
  235. package/dist/tools/schemas/spec.d.ts +4 -0
  236. package/dist/tools/schemas/spec.js +3 -0
  237. package/dist/tools/schemas/validate-output-schema.d.ts +11 -0
  238. package/dist/tools/schemas/validate-output-schema.js +7 -0
  239. package/dist/tools/spec-prompt-handler.js +3 -3
  240. package/dist/tools/sync-spec-state-handler.d.ts +1 -1
  241. package/dist/tools/sync-spec-state-handler.js +1 -1
  242. package/dist/tools/tool-entry.d.ts +4 -4
  243. package/dist/tools/tool-entry.js +3 -3
  244. package/dist/tools/tool-registry/core-tools.js +31 -15
  245. package/dist/tools/tool-registry/group-infra.js +27 -110
  246. package/dist/tools/tool-registry/group-quality-compliance.js +26 -26
  247. package/dist/tools/tool-registry/inventory.d.ts +2 -0
  248. package/dist/tools/tool-registry/inventory.js +2 -0
  249. package/dist/tools/tool-registry-helpers.d.ts +3 -3
  250. package/dist/tools/tool-registry-helpers.js +3 -3
  251. package/dist/tools/tool-usage-report/registered-tools.d.ts +1 -1
  252. package/dist/tools/tool-usage-report/registered-tools.js +3 -15
  253. package/dist/tools/update-status/approval-gates.js +3 -0
  254. package/dist/tools/update-status/done-receipt-verifier.js +40 -27
  255. package/dist/tools/update-status/evidence-gate.js +36 -1
  256. package/dist/tools/update-status/file-sync.d.ts +2 -1
  257. package/dist/tools/update-status/file-sync.js +57 -6
  258. package/dist/tools/update-status/index.d.ts +2 -0
  259. package/dist/tools/update-status/index.js +95 -58
  260. package/dist/tools/update-status/response-builder.js +3 -3
  261. package/dist/tools/update-status/side-effects.d.ts +2 -0
  262. package/dist/tools/update-status/side-effects.js +2 -2
  263. package/dist/tools/update-status/transition-guard.js +32 -15
  264. package/dist/tools/update-status-actions.d.ts +4 -2
  265. package/dist/tools/update-status-actions.js +16 -14
  266. package/dist/tools/usage-report.js +1 -16
  267. package/dist/tools/usage-stats.js +1 -18
  268. package/dist/tools/usage-tracking.d.ts +4 -0
  269. package/dist/tools/usage-tracking.js +20 -0
  270. package/dist/tools/validate-lint.d.ts +1 -0
  271. package/dist/tools/validate-lint.js +1 -1
  272. package/dist/tools/validate.d.ts +2 -1
  273. package/dist/tools/validate.js +114 -34
  274. package/dist/transports/http-transport.js +6 -8
  275. package/dist/transports/mcp-types.d.ts +0 -2
  276. package/dist/transports/session-context.js +1 -1
  277. package/dist/types/cascade-hooks.d.ts +1 -1
  278. package/dist/types/ci.d.ts +0 -4
  279. package/dist/types/clarification-token.d.ts +1 -1
  280. package/dist/types/claude-code-runtime.d.ts +0 -27
  281. package/dist/types/commercial-migration.d.ts +17 -0
  282. package/dist/types/commercial-migration.js +2 -0
  283. package/dist/types/common/primitives.d.ts +0 -2
  284. package/dist/types/core-bridge.d.ts +4 -10
  285. package/dist/types/dashboard.d.ts +3 -7
  286. package/dist/types/docs.d.ts +0 -1
  287. package/dist/types/durable-job.d.ts +4 -4
  288. package/dist/types/durable-validation.d.ts +100 -2
  289. package/dist/types/durable-validation.js +165 -1
  290. package/dist/types/errors/structured-error.d.ts +3 -3
  291. package/dist/types/estimation.d.ts +2 -0
  292. package/dist/types/evidence-gates.d.ts +9 -1
  293. package/dist/types/execution.d.ts +2 -0
  294. package/dist/types/gemini-integration.d.ts +0 -11
  295. package/dist/types/handoff-artifacts.d.ts +41 -0
  296. package/dist/types/hooks-advanced.d.ts +0 -13
  297. package/dist/types/impact-detection.d.ts +2 -2
  298. package/dist/types/index.d.ts +2 -6
  299. package/dist/types/index.js +1 -5
  300. package/dist/types/kiro-integration.d.ts +0 -6
  301. package/dist/types/network-policy.d.ts +1 -1
  302. package/dist/types/project/lint-scaffolder.d.ts +1 -1
  303. package/dist/types/runtime-policy.d.ts +11 -0
  304. package/dist/types/session-safeguard.d.ts +0 -31
  305. package/dist/types/spec/canonical-repair.d.ts +81 -0
  306. package/dist/types/spec/canonical-repair.js +2 -0
  307. package/dist/types/spec/core.d.ts +14 -0
  308. package/dist/types/spec/index.d.ts +1 -0
  309. package/dist/types/spec/index.js +1 -0
  310. package/dist/types/spec/inputs.d.ts +29 -0
  311. package/dist/types/spec-format.d.ts +24 -0
  312. package/dist/types/telemetry.d.ts +4 -1
  313. package/dist/types/transport.d.ts +0 -2
  314. package/dist/types/usage.d.ts +1 -36
  315. package/dist/types/usage.js +1 -1
  316. package/dist/types/validate-worker.d.ts +6 -0
  317. package/dist/types/validation-receipt.d.ts +1 -1
  318. package/dist/types/validation.d.ts +2 -2
  319. package/package.json +16 -12
  320. package/planu-native.json +1 -1
  321. package/planu-plugin.json +23 -40
  322. package/scripts/lib/pending-release-file.mjs +93 -0
  323. package/src/i18n/messages/en.json +0 -41
  324. package/src/i18n/messages/es.json +0 -41
  325. package/src/i18n/messages/pt.json +0 -41
  326. package/dist/cli/commands/activate.d.ts +0 -14
  327. package/dist/cli/commands/activate.js +0 -174
  328. package/dist/config/license-plans.json +0 -251
  329. package/dist/config/product-public.ts +0 -148
  330. package/dist/config/server-instructions.ts +0 -121
  331. package/dist/config/version-defaults.ts +0 -76
  332. package/dist/config/version.ts +0 -21
  333. package/dist/engine/ai-integration/gemini/settings-generator.d.ts +0 -4
  334. package/dist/engine/ai-integration/gemini/settings-generator.js +0 -195
  335. package/dist/engine/ai-integration/kiro/hooks-generator.d.ts +0 -6
  336. package/dist/engine/ai-integration/kiro/hooks-generator.js +0 -75
  337. package/dist/engine/hooks/full-spectrum-generator.d.ts +0 -32
  338. package/dist/engine/hooks/full-spectrum-generator.js +0 -117
  339. package/dist/engine/license-validator/config-loader.d.ts +0 -5
  340. package/dist/engine/license-validator/config-loader.js +0 -16
  341. package/dist/engine/license-validator/core.d.ts +0 -15
  342. package/dist/engine/license-validator/core.js +0 -151
  343. package/dist/engine/license-validator/fingerprint.d.ts +0 -3
  344. package/dist/engine/license-validator/fingerprint.js +0 -17
  345. package/dist/engine/license-validator/lemon-squeezy.d.ts +0 -5
  346. package/dist/engine/license-validator/lemon-squeezy.js +0 -55
  347. package/dist/engine/license-validator.d.ts +0 -5
  348. package/dist/engine/license-validator.js +0 -6
  349. package/dist/engine/product-intelligence/index.d.ts +0 -7
  350. package/dist/engine/product-intelligence/index.js +0 -211
  351. package/dist/engine/session/auto-save.d.ts +0 -18
  352. package/dist/engine/session/auto-save.js +0 -98
  353. package/dist/engine/session/context-snapshot.d.ts +0 -8
  354. package/dist/engine/session/context-snapshot.js +0 -49
  355. package/dist/engine/session/memory-sync.d.ts +0 -12
  356. package/dist/engine/session/memory-sync.js +0 -34
  357. package/dist/engine/session/session-diff.d.ts +0 -11
  358. package/dist/engine/session/session-diff.js +0 -42
  359. package/dist/engine/session/session-merge.d.ts +0 -12
  360. package/dist/engine/session/session-merge.js +0 -55
  361. package/dist/engine/session-safeguard/checkpoint-runner.d.ts +0 -4
  362. package/dist/engine/session-safeguard/checkpoint-runner.js +0 -70
  363. package/dist/engine/session-safeguard/learnings-buffer.d.ts +0 -15
  364. package/dist/engine/session-safeguard/learnings-buffer.js +0 -91
  365. package/dist/engine/session-safeguard/precompaction-drain.d.ts +0 -10
  366. package/dist/engine/session-safeguard/precompaction-drain.js +0 -82
  367. package/dist/engine/session-safeguard/unpushed-detector.d.ts +0 -7
  368. package/dist/engine/session-safeguard/unpushed-detector.js +0 -59
  369. package/dist/engine/spec-changelog/core.d.ts +0 -16
  370. package/dist/engine/spec-changelog/core.js +0 -176
  371. package/dist/engine/spec-changelog/diff.d.ts +0 -18
  372. package/dist/engine/spec-changelog/diff.js +0 -121
  373. package/dist/engine/spec-changelog/index.d.ts +0 -3
  374. package/dist/engine/spec-changelog/index.js +0 -4
  375. package/dist/engine/trial-engine.d.ts +0 -5
  376. package/dist/engine/trial-engine.js +0 -14
  377. package/dist/engine/usage-tracker/rate-check.d.ts +0 -7
  378. package/dist/engine/usage-tracker/rate-check.js +0 -18
  379. package/dist/engine/usage-tracker/rate-limiter.d.ts +0 -14
  380. package/dist/engine/usage-tracker/rate-limiter.js +0 -73
  381. package/dist/engine/usage-tracker/trial.d.ts +0 -27
  382. package/dist/engine/usage-tracker/trial.js +0 -49
  383. package/dist/engine/webhook/event-handlers.d.ts +0 -25
  384. package/dist/engine/webhook/event-handlers.js +0 -89
  385. package/dist/engine/webhook/index.d.ts +0 -4
  386. package/dist/engine/webhook/index.js +0 -5
  387. package/dist/engine/webhook/server.d.ts +0 -12
  388. package/dist/engine/webhook/server.js +0 -249
  389. package/dist/engine/webhook/signature.d.ts +0 -16
  390. package/dist/engine/webhook/signature.js +0 -49
  391. package/dist/storage/license-store.d.ts +0 -6
  392. package/dist/storage/license-store.js +0 -63
  393. package/dist/storage/session-store.d.ts +0 -20
  394. package/dist/storage/session-store.js +0 -136
  395. package/dist/tools/activate-license.d.ts +0 -6
  396. package/dist/tools/activate-license.js +0 -132
  397. package/dist/tools/checkpoint-handler.d.ts +0 -3
  398. package/dist/tools/checkpoint-handler.js +0 -130
  399. package/dist/tools/export-session.d.ts +0 -3
  400. package/dist/tools/export-session.js +0 -90
  401. package/dist/tools/license-gate.d.ts +0 -18
  402. package/dist/tools/license-gate.js +0 -222
  403. package/dist/tools/license-status.d.ts +0 -3
  404. package/dist/tools/license-status.js +0 -93
  405. package/dist/tools/list-sessions.d.ts +0 -3
  406. package/dist/tools/list-sessions.js +0 -46
  407. package/dist/tools/planu-session-checkpoint.d.ts +0 -6
  408. package/dist/tools/planu-session-checkpoint.js +0 -42
  409. package/dist/tools/restore-session.d.ts +0 -3
  410. package/dist/tools/restore-session.js +0 -71
  411. package/dist/tools/spec-history.d.ts +0 -3
  412. package/dist/tools/spec-history.js +0 -59
  413. package/dist/tools/webhook.d.ts +0 -6
  414. package/dist/tools/webhook.js +0 -150
  415. package/dist/types/changelog.d.ts +0 -49
  416. package/dist/types/changelog.js +0 -3
  417. package/dist/types/licensing.d.ts +0 -109
  418. package/dist/types/licensing.js +0 -4
  419. package/dist/types/product-intelligence.d.ts +0 -60
  420. package/dist/types/product-intelligence.js +0 -3
  421. package/dist/types/session.d.ts +0 -84
  422. package/dist/types/session.js +0 -3
  423. package/dist/types/webhook.d.ts +0 -62
  424. package/dist/types/webhook.js +0 -4
@@ -3,34 +3,51 @@
3
3
  // --- Lightweight frontmatter parser ---
4
4
  const FM_RE = /^---\n([\s\S]*?)\n---\n/;
5
5
  const FM_KV_RE = /^(\w+):\s*(.+)$/;
6
+ const FM_OBJECT_RE = /^(\w+):\s*$/;
7
+ function parseScalar(val) {
8
+ if (val.startsWith('[') && val.endsWith(']')) {
9
+ return val
10
+ .slice(1, -1)
11
+ .split(',')
12
+ .map((item) => item.trim().replace(/^["']|["']$/g, ''));
13
+ }
14
+ if (val === 'null') {
15
+ return null;
16
+ }
17
+ if (/^\d+$/.test(val)) {
18
+ return parseInt(val, 10);
19
+ }
20
+ return val.replace(/^["']|["']$/g, '');
21
+ }
6
22
  export function parseFrontmatterLocal(content) {
7
23
  const match = FM_RE.exec(content);
8
24
  if (!match?.[1]) {
9
25
  return { metadata: {} };
10
26
  }
11
27
  const metadata = {};
28
+ let activeObject = null;
12
29
  for (const line of match[1].split('\n')) {
13
- const kv = FM_KV_RE.exec(line.trim());
30
+ const indent = line.length - line.trimStart().length;
31
+ const trimmed = line.trim();
32
+ const object = FM_OBJECT_RE.exec(trimmed);
33
+ if (object?.[1]) {
34
+ const value = {};
35
+ metadata[object[1]] = value;
36
+ activeObject = { indent, value };
37
+ continue;
38
+ }
39
+ const kv = FM_KV_RE.exec(trimmed);
14
40
  if (!kv?.[1] || kv[2] === undefined) {
15
41
  continue;
16
42
  }
17
43
  const key = kv[1];
18
44
  const val = kv[2].trim();
19
- if (val.startsWith('[') && val.endsWith(']')) {
20
- metadata[key] = val
21
- .slice(1, -1)
22
- .split(',')
23
- .map((s) => s.trim().replace(/^["']|["']$/g, ''));
24
- }
25
- else if (val === 'null') {
26
- metadata[key] = null;
27
- }
28
- else if (/^\d+$/.test(val)) {
29
- metadata[key] = parseInt(val, 10);
30
- }
31
- else {
32
- metadata[key] = val.replace(/^["']|["']$/g, '');
45
+ if (activeObject !== null && indent > activeObject.indent) {
46
+ activeObject.value[key] = parseScalar(val);
47
+ continue;
33
48
  }
49
+ activeObject = null;
50
+ metadata[key] = parseScalar(val);
34
51
  }
35
52
  return { metadata };
36
53
  }
@@ -3,6 +3,7 @@ export { filterUnprefixedSpecs, migrateSpecFolderNames } from './prefix-migratio
3
3
  export { discoverAndFlattenSpecs } from './flatten-specs.js';
4
4
  export { reconcileSpecPaths } from './reconcile-paths.js';
5
5
  export { importFilesystemSpecs } from './filesystem-import.js';
6
+ export { buildCanonicalRepairPlan, repairDuplicateSpecIds, rollbackCanonicalRepair, } from './canonical-deduplicator.js';
6
7
  export { rewritePath, flattenSpecPath } from './path-utils.js';
7
8
  export { hasLegacyConfig } from './version-detection.js';
8
9
  export { validateMigrationResult, mergeMigrationResults, emptyMigrationResult, } from './migration-validator.js';
@@ -9,6 +9,7 @@ export { discoverAndFlattenSpecs } from './flatten-specs.js';
9
9
  export { reconcileSpecPaths } from './reconcile-paths.js';
10
10
  // Import filesystem specs into the store
11
11
  export { importFilesystemSpecs } from './filesystem-import.js';
12
+ export { buildCanonicalRepairPlan, repairDuplicateSpecIds, rollbackCanonicalRepair, } from './canonical-deduplicator.js';
12
13
  // Path utilities (re-exported for consumers that may depend on them)
13
14
  export { rewritePath, flattenSpecPath } from './path-utils.js';
14
15
  // Legacy config detection
@@ -2,7 +2,7 @@ import type { Spec, SpecStatus } from '../../types/index.js';
2
2
  /**
3
3
  * Why this transition is happening — surfaces in audit-trail and transition_log.
4
4
  */
5
- export type TransitionTrigger = 'user' | 'auto-advance' | 'git-merge' | 'fs-hook' | 'sync-spec-state' | 'multi-teammate-review' | 'http' | 'dashboard' | 'reopen';
5
+ export type TransitionTrigger = 'user' | 'auto-advance' | 'git-merge' | 'fs-hook' | 'sync-spec-state' | 'multi-teammate-review' | 'http' | 'dashboard' | 'reopen' | 'reconciliation';
6
6
  export interface TransitionContext {
7
7
  /** Why this transition is happening. */
8
8
  trigger: TransitionTrigger;
@@ -16,6 +16,11 @@ export interface TransitionContext {
16
16
  updates?: Omit<Partial<Spec>, 'status'>;
17
17
  /** Optional work scheduled after the durable transition boundary. */
18
18
  pendingBackgroundActions?: readonly string[];
19
+ /** Opaque binding to the selected root for post-commit recovery. */
20
+ transitionRootBinding?: string;
21
+ /** Dedicated recovery transaction binding persisted with its status entry. */
22
+ reconciliationRequestId?: string;
23
+ reconciliationBindingDigest?: string;
19
24
  /** Snapshot used by lifecycle gates; the store rejects intervening mutations. */
20
25
  expectedSpec?: Spec;
21
26
  }
@@ -20,7 +20,16 @@ export async function transitionSpec(projectId, specId, newStatus, ctx) {
20
20
  throw new Error(`Spec ${specId} not found in project ${projectId}`);
21
21
  }
22
22
  // Use the internal-only write path that bypasses the status guard
23
- const after = await specStore.__internalSetStatus(projectId, specId, newStatus, ctx.updates, ctx.pendingBackgroundActions, { status: before.status, updatedAt: before.updatedAt });
23
+ const reconciliation = ctx.reconciliationRequestId === undefined
24
+ ? undefined
25
+ : {
26
+ reconciliationRequestId: ctx.reconciliationRequestId,
27
+ reconciliationBindingDigest: ctx.reconciliationBindingDigest,
28
+ trigger: ctx.trigger,
29
+ };
30
+ const after = reconciliation
31
+ ? await specStore.__internalSetStatus(projectId, specId, newStatus, ctx.updates, ctx.pendingBackgroundActions, { status: before.status, updatedAt: before.updatedAt }, ctx.transitionRootBinding, reconciliation)
32
+ : await specStore.__internalSetStatus(projectId, specId, newStatus, ctx.updates, ctx.pendingBackgroundActions, { status: before.status, updatedAt: before.updatedAt }, ctx.transitionRootBinding);
24
33
  const receipt = after.statusHistory?.at(-1);
25
34
  if (!receipt?.transitionId) {
26
35
  throw new Error(`Transition receipt was not persisted for ${specId}`);
@@ -1,13 +1,19 @@
1
1
  // technical-enricher/index.ts — Agnostic enricher + disk runner (SPEC-696)
2
- import { readFile, writeFile } from 'node:fs/promises';
2
+ import { readFile } from 'node:fs/promises';
3
3
  import { join } from 'node:path';
4
4
  import { parseTechnicalMd } from './parse-technical.js';
5
5
  import { detectSkip } from './skip-detector.js';
6
6
  import { extractSignatures } from './signature-extractor.js';
7
7
  import { scanTestPattern } from './test-pattern-scanner.js';
8
- import { renderEnriched } from './render-enriched.js';
8
+ import { hasGeneratedEnrichment, renderEnriched } from './render-enriched.js';
9
+ import { findMarkdownSectionRange } from '../spec-format/markdown-sections.js';
10
+ import { resolveContainedProjectFile } from '../safety/contained-project-file.js';
11
+ import { atomicWriteFile } from '../safety/atomic-write-file.js';
9
12
  export async function enrichTechnicalContent(input) {
10
13
  const { technicalContent, specScope, projectPath } = input;
14
+ if (hasGeneratedEnrichment(technicalContent)) {
15
+ return { enriched: false, reason: 'already-enriched' };
16
+ }
11
17
  const parsed = parseTechnicalMd(technicalContent);
12
18
  const skipReason = detectSkip(parsed, specScope);
13
19
  if (skipReason) {
@@ -42,20 +48,22 @@ async function runEnricherFromSpecSection(opts) {
42
48
  if (!specPath) {
43
49
  return;
44
50
  }
45
- const specRaw = await readFile(specPath, 'utf8');
46
- const sectionMatch = /\n## Technical\n([\s\S]*)$/.exec(specRaw);
47
- if (!sectionMatch?.[1]) {
51
+ const physicalSpecPath = await resolveContainedProjectFile(opts.projectPath, specPath);
52
+ const specRaw = await readFile(physicalSpecPath, 'utf8');
53
+ const section = findMarkdownSectionRange(specRaw, 'Technical');
54
+ if (!section) {
48
55
  return;
49
56
  }
57
+ const technicalContent = specRaw.slice(section.contentStart, section.end);
50
58
  const result = await enrichTechnicalContent({
51
59
  specId: opts.specId,
52
60
  projectPath: opts.projectPath,
53
- technicalContent: sectionMatch[1],
61
+ technicalContent,
54
62
  specScope: opts.specScope,
55
63
  });
56
64
  if (result.enriched && result.enrichedContent) {
57
- const updated = specRaw.replace(/\n## Technical\n[\s\S]*$/, `\n## Technical\n\n${result.enrichedContent}`);
58
- await writeFile(specPath, updated, 'utf8');
65
+ const updated = `${specRaw.slice(0, section.contentStart)}${result.enrichedContent}${specRaw.slice(section.end)}`;
66
+ await atomicWriteFile(physicalSpecPath, updated);
59
67
  }
60
68
  }
61
69
  //# sourceMappingURL=index.js.map
@@ -1,3 +1,4 @@
1
1
  import type { RenderInput } from '../../types/technical-enrichment.js';
2
+ export declare function hasGeneratedEnrichment(content: string): boolean;
2
3
  export declare function renderEnriched(input: RenderInput): string;
3
4
  //# sourceMappingURL=render-enriched.d.ts.map
@@ -1,18 +1,41 @@
1
1
  const MAX_CHARS = 3200;
2
- export function renderEnriched(input) {
3
- const { originalContent, signatures, testSample } = input;
4
- const lines = [
5
- originalContent.trimEnd(),
6
- '',
2
+ const GENERATED_START = '<!-- planu:technical-enrichment:start -->';
3
+ const GENERATED_END = '<!-- planu:technical-enrichment:end -->';
4
+ export function hasGeneratedEnrichment(content) {
5
+ return (content.includes(GENERATED_START) ||
6
+ /^#{2,3}[ \t]+Real Signatures \(auto-extracted by SPEC-696\)[ \t]*$/m.test(content));
7
+ }
8
+ function appendGenerated(originalContent, blocks) {
9
+ const separator = originalContent.endsWith('\n') ? '' : '\n\n';
10
+ const generated = [
11
+ GENERATED_START,
7
12
  '---',
8
13
  '',
9
- '## Real Signatures (auto-extracted by SPEC-696)',
10
- ];
14
+ '### Real Signatures (auto-extracted by SPEC-696)',
15
+ ...blocks,
16
+ GENERATED_END,
17
+ '',
18
+ '',
19
+ ].join('\n');
20
+ return `${originalContent}${separator}${generated}`;
21
+ }
22
+ export function renderEnriched(input) {
23
+ const { originalContent, signatures, testSample } = input;
24
+ if (originalContent.length >= MAX_CHARS || hasGeneratedEnrichment(originalContent)) {
25
+ return originalContent;
26
+ }
27
+ const blocks = [];
28
+ const tryAppendBlock = (block) => {
29
+ const candidateBlocks = [...blocks, block];
30
+ if (appendGenerated(originalContent, candidateBlocks).length <= MAX_CHARS) {
31
+ blocks.push(block);
32
+ }
33
+ };
11
34
  for (const sig of signatures) {
12
35
  if (sig.exports.length === 0 && sig.interfaces.length === 0) {
13
36
  continue;
14
37
  }
15
- lines.push('', `### \`${sig.relPath}\``, '```typescript');
38
+ const lines = ['', `#### \`${sig.relPath}\``, '```typescript'];
16
39
  for (const exp of sig.exports.slice(0, 5)) {
17
40
  lines.push(exp);
18
41
  }
@@ -20,15 +43,20 @@ export function renderEnriched(input) {
20
43
  lines.push(iface);
21
44
  }
22
45
  lines.push('```');
46
+ tryAppendBlock(lines.join('\n'));
23
47
  }
24
48
  if (testSample) {
25
- lines.push('', '## Test Patterns (from existing tests)', `- Mock style: \`${testSample.mockStyle}\``, `- Has \`beforeEach\`: ${String(testSample.hasBeforeEach)}`);
49
+ tryAppendBlock([
50
+ '',
51
+ '### Test Patterns (from existing tests)',
52
+ `- Mock style: \`${testSample.mockStyle}\``,
53
+ `- Has \`beforeEach\`: ${String(testSample.hasBeforeEach)}`,
54
+ ].join('\n'));
26
55
  }
27
- const result = lines.join('\n');
28
- if (result.length <= MAX_CHARS) {
29
- return result;
56
+ if (blocks.length === 0) {
57
+ return originalContent;
30
58
  }
31
- return (result.slice(0, MAX_CHARS) +
32
- '\n\n> [truncated spec too large, enrich remaining files manually]');
59
+ const result = appendGenerated(originalContent, blocks);
60
+ return result.length <= MAX_CHARS ? result : originalContent;
33
61
  }
34
62
  //# sourceMappingURL=render-enriched.js.map
@@ -2,17 +2,18 @@
2
2
  // engine/telemetry/health-checker.ts — SPEC-346: Diagnose why telemetry data is not reaching the dashboard.
3
3
  import { readFile } from 'node:fs/promises';
4
4
  import { join } from 'node:path';
5
- import { readTelemetryConfig } from './telemetry-store.js';
5
+ import { isTelemetryEnabled } from './telemetry-store.js';
6
6
  import { sendTelemetryEvent } from './telemetry-client.js';
7
7
  import { hashProjectPath, projectDataDir } from '../../storage/base-store.js';
8
+ import { networkFetch, withNetworkConsent } from '../network-policy.js';
8
9
  const NETWORK_TIMEOUT_MS = 5_000;
9
10
  const RECENT_EVENT_THRESHOLD_DAYS = 7;
10
11
  // ---------------------------------------------------------------------------
11
12
  // Individual checks
12
13
  // ---------------------------------------------------------------------------
13
14
  async function checkConfigEnabled() {
14
- const config = await readTelemetryConfig();
15
- if (!config?.enabled) {
15
+ const enabled = await isTelemetryEnabled();
16
+ if (!enabled) {
16
17
  return {
17
18
  check: {
18
19
  name: 'config-enabled',
@@ -32,19 +33,46 @@ async function checkConfigEnabled() {
32
33
  };
33
34
  }
34
35
  function checkEndpointConfigured(endpoint) {
35
- if (!endpoint || endpoint.trim() === '') {
36
+ if (!isValidTelemetryEndpoint(endpoint)) {
36
37
  return {
37
38
  name: 'endpoint-configured',
38
39
  status: 'fail',
39
- detail: 'No telemetry endpoint configured. Set PLANU_TELEMETRY_ENDPOINT env var.',
40
+ detail: 'Telemetry endpoint is missing or invalid. Use HTTPS, or HTTP only for an explicit loopback host.',
40
41
  };
41
42
  }
42
43
  return {
43
44
  name: 'endpoint-configured',
44
45
  status: 'ok',
45
- detail: `Endpoint: ${endpoint}`,
46
+ detail: 'Telemetry endpoint configured',
46
47
  };
47
48
  }
49
+ function isLoopbackHost(hostname) {
50
+ return hostname === 'localhost' || hostname === '127.0.0.1' || hostname === '[::1]';
51
+ }
52
+ function isValidTelemetryEndpoint(endpoint) {
53
+ if (!endpoint || endpoint.trim() === '') {
54
+ return false;
55
+ }
56
+ try {
57
+ const parsed = new URL(endpoint.trim());
58
+ return (parsed.username === '' &&
59
+ parsed.password === '' &&
60
+ (parsed.protocol === 'https:' ||
61
+ (parsed.protocol === 'http:' && isLoopbackHost(parsed.hostname))));
62
+ }
63
+ catch {
64
+ return false;
65
+ }
66
+ }
67
+ function checkTokenConfigured(token) {
68
+ return token?.trim()
69
+ ? { name: 'token-configured', status: 'ok', detail: 'Telemetry token configured' }
70
+ : {
71
+ name: 'token-configured',
72
+ status: 'fail',
73
+ detail: 'Telemetry token is missing. Set PLANU_TELEMETRY_TOKEN env var.',
74
+ };
75
+ }
48
76
  async function checkNetworkReachable(endpoint) {
49
77
  if (!endpoint) {
50
78
  return {
@@ -54,10 +82,10 @@ async function checkNetworkReachable(endpoint) {
54
82
  };
55
83
  }
56
84
  try {
57
- const response = await fetch(endpoint, {
85
+ const response = await withNetworkConsent('telemetry', (receipt) => networkFetch(receipt, endpoint, {
58
86
  method: 'GET',
59
87
  signal: AbortSignal.timeout(NETWORK_TIMEOUT_MS),
60
- });
88
+ }));
61
89
  if (response.ok || response.status < 500) {
62
90
  return {
63
91
  name: 'network-reachable',
@@ -208,7 +236,7 @@ function buildRecommendation(status) {
208
236
  case 'disabled':
209
237
  return 'Run configure_telemetry to enable usage tracking';
210
238
  case 'misconfigured':
211
- return 'Set PLANU_TELEMETRY_ENDPOINT or run configure_telemetry';
239
+ return 'Set valid PLANU_TELEMETRY_ENDPOINT and PLANU_TELEMETRY_TOKEN values.';
212
240
  case 'degraded':
213
241
  return 'Check network connectivity or firewall rules blocking outbound requests';
214
242
  case 'healthy':
@@ -232,8 +260,8 @@ function computeStatus(isEnabled, checks) {
232
260
  if (!isEnabled) {
233
261
  return 'disabled';
234
262
  }
235
- const endpointCheck = checks.find((c) => c.name === 'endpoint-configured');
236
- if (endpointCheck?.status === 'fail') {
263
+ const configurationChecks = checks.filter((check) => check.name === 'endpoint-configured' || check.name === 'token-configured');
264
+ if (configurationChecks.some((check) => check.status === 'fail')) {
237
265
  return 'misconfigured';
238
266
  }
239
267
  const hasWarn = checks.some((c) => c.status === 'warn');
@@ -257,26 +285,34 @@ export async function checkTelemetryHealth(projectPath, options) {
257
285
  humanSummary: buildHumanSummary(status, checks),
258
286
  };
259
287
  }
260
- // Default endpoint from telemetry-client
261
- const defaultEndpoint = 'https://canusqigtzldstnjddio.supabase.co/rest/v1/telemetry_events';
262
- const endpoint = process.env.PLANU_TELEMETRY_ENDPOINT?.trim() ?? defaultEndpoint;
288
+ const endpoint = process.env.PLANU_TELEMETRY_ENDPOINT?.trim();
289
+ const token = process.env.PLANU_TELEMETRY_TOKEN;
290
+ const endpointCheck = checkEndpointConfigured(endpoint ?? '');
291
+ const tokenCheck = checkTokenConfigured(token);
292
+ const configured = endpointCheck.status === 'ok' && tokenCheck.status === 'ok';
263
293
  const [networkCheck, lastEventCheck, errorLogCheck, versionCheck] = await Promise.all([
264
- checkNetworkReachable(endpoint),
294
+ configured
295
+ ? checkNetworkReachable(endpoint ?? '')
296
+ : Promise.resolve({
297
+ name: 'network-reachable',
298
+ status: 'skip',
299
+ detail: 'Skipped until endpoint and token configuration is valid',
300
+ }),
265
301
  checkLastEvent(projectPath),
266
302
  checkErrorLog(projectPath),
267
303
  checkClientVersion(projectPath),
268
304
  ]);
269
- const endpointCheck = checkEndpointConfigured(endpoint);
270
305
  const checks = [
271
306
  enabledCheck,
272
307
  endpointCheck,
308
+ tokenCheck,
273
309
  networkCheck,
274
310
  lastEventCheck,
275
311
  errorLogCheck,
276
312
  versionCheck,
277
313
  ];
278
314
  const status = computeStatus(enabled, checks);
279
- if (options?.sendTestEvent === true) {
315
+ if (options?.sendTestEvent === true && configured) {
280
316
  sendTestDiagnosticEvent();
281
317
  }
282
318
  return {
@@ -2,9 +2,27 @@
2
2
  import { networkFetch, withNetworkConsent } from '../network-policy.js';
3
3
  import { redactSensitiveValue } from '../../security/redactor.js';
4
4
  import { isTelemetryEnabled } from './telemetry-store.js';
5
- const SUPABASE_URL = 'https://canusqigtzldstnjddio.supabase.co';
6
- const SUPABASE_ANON_KEY = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6ImNhbnVzcWlndHpsZHN0bmpkZGlvIiwicm9sZSI6ImFub24iLCJpYXQiOjE3NzI3NTk2MjgsImV4cCI6MjA4ODMzNTYyOH0.Thw1Xgf9_iabOD75J00u35d2k5NsIpUBU9PrqOShxMU';
7
- const TELEMETRY_ENDPOINT = `${SUPABASE_URL}/rest/v1/telemetry_events`;
5
+ function isLoopbackHost(hostname) {
6
+ return hostname === 'localhost' || hostname === '127.0.0.1' || hostname === '[::1]';
7
+ }
8
+ function isValidTelemetryEndpoint(endpoint) {
9
+ if (!endpoint || endpoint.trim() === '') {
10
+ return false;
11
+ }
12
+ try {
13
+ const parsed = new URL(endpoint.trim());
14
+ return (parsed.username === '' &&
15
+ parsed.password === '' &&
16
+ (parsed.protocol === 'https:' ||
17
+ (parsed.protocol === 'http:' && isLoopbackHost(parsed.hostname))));
18
+ }
19
+ catch {
20
+ return false;
21
+ }
22
+ }
23
+ function isConfiguredTelemetryToken(token) {
24
+ return Boolean(token?.trim());
25
+ }
8
26
  const ALLOWED_PROPERTIES = new Set([
9
27
  'allowed',
10
28
  'context',
@@ -37,16 +55,16 @@ function safeProperties(properties) {
37
55
  export function sendTelemetryEvent(event) {
38
56
  void isTelemetryEnabled()
39
57
  .then((enabled) => {
40
- if (!enabled) {
58
+ const endpoint = process.env.PLANU_TELEMETRY_ENDPOINT?.trim();
59
+ const token = process.env.PLANU_TELEMETRY_TOKEN?.trim();
60
+ if (!enabled || !isValidTelemetryEndpoint(endpoint) || !isConfiguredTelemetryToken(token)) {
41
61
  return undefined;
42
62
  }
43
- return withNetworkConsent('telemetry', (receipt) => networkFetch(receipt, TELEMETRY_ENDPOINT, {
63
+ return withNetworkConsent('telemetry', (receipt) => networkFetch(receipt, endpoint, {
44
64
  method: 'POST',
45
65
  headers: {
46
66
  'Content-Type': 'application/json',
47
- apikey: SUPABASE_ANON_KEY,
48
- Authorization: `Bearer ${SUPABASE_ANON_KEY}`,
49
- Prefer: 'return=minimal',
67
+ Authorization: `Bearer ${token}`,
50
68
  },
51
69
  body: JSON.stringify({
52
70
  event_name: event.event,
@@ -1,5 +1,7 @@
1
1
  /** Remove fenced code and quoted examples before keyword-driven inference. */
2
2
  export declare function stripNonContractText(text: string): string;
3
+ /** Remove clauses whose subject is detector analysis or test evidence, not product behavior. */
4
+ export declare function stripMetaAnalysisText(text: string): string;
3
5
  /** True only when a regex match appears in contract prose and is not negated/excluded. */
4
6
  export declare function hasAffirmedMatch(text: string, pattern: RegExp): boolean;
5
7
  export declare function hasAnyAffirmedMatch(text: string, patterns: RegExp[]): boolean;
@@ -1,4 +1,5 @@
1
1
  // engine/text-signal-boundaries.ts — Boundary-aware keyword matching for advisory signals.
2
+ import { stripFrontmatter } from './frontmatter-parser.js';
2
3
  const NEGATION_TERMS = new Set([
3
4
  'no',
4
5
  'not',
@@ -15,7 +16,15 @@ const NEGATION_TERMS = new Set([
15
16
  ]);
16
17
  const OUT_OF_SCOPE_RE = /\b(?:out\s+of\s+scope|not\s+in\s+scope|no\s+scope|do\s+not|don't)\b/i;
17
18
  const NON_AFFIRMING_CONTEXT_RE = /\b(?:false[ -]?positive|incorrect(?:ly)?|wrong(?:ly)?|irrelevant|unrelated|noise|repeated\s+failure)\b/i;
18
- const EXCLUDED_SECTION_RE = /^(?:out\s+of\s+scope|non[ -]?goals?|examples?|anticipated\s+test\s+breaks?)\b/i;
19
+ const EXCLUDED_SECTION_RE = /^(?:out\s+of\s+scope|non[ -]?goals?|examples?|anticipated\s+test\s+breaks?|challenge\s+resolution)\b/i;
20
+ const META_ANALYSIS_CLAUSE_BOUNDARY_RE = /(?:[.!?;。!?]\s+|\n+|,\s+(?:but|while|whereas)\s+)/iu;
21
+ const META_ANALYSIS_SUBJECT_RE = /^\s*(?:[-*+>]\s+|\d+[.)]\s+)?(?:(?:given|when|then|and)\s+)?(?:(?:negative|positive)\s+examples?\s*:|(?:(?:the|this|that|a|an|any|each|our|existing|current)\s+)?(?:capability\s+)?(?:detector|classifier|matcher|challenge\s+(?:finding|selection|scenario)|regression\s+(?:test|fixture)|fixture|positive\s+control|expected\s+(?:absence|selection|result)|(?:tooling|test|regression)\s+spec|paired\s+product\s+specs|spec\s+(?:title|tag|criterion)|(?:dogfood\s+)?reproducer|documentation|prose|titles?|criteria|criterion)(?:\s|[:,])|(?:the|this|that|a|an|any|each|our|existing|current)?\s*table(?:\s+rows?)?\s+(?:contains?|pairs?|asserts?|names?|uses?)\b|(?:add|use|write|run|extend|preserve)\s+(?:(?:paired|table-driven|positive|negative|regression)\s+)*(?:tests?|fixtures?|examples?|assertions?|rows?)\b|(?:apply|reuse|compose)\s+(?:the\s+)?(?:shared\s+|generic\s+|meta-analysis\s+)?boundary\b)/iu;
22
+ const ACTION_LED_META_ANALYSIS_RE = /^\s*(?:[-*+>]\s+|\d+[.)]\s+)?(?:fix|prevent|ignore|document|repair|correct|avoid|remove|stop|resolve|investigate|verify)\b(?=[^.!?。!?]{0,240}\b(?:detector|classifier|matcher)\b)(?=[^.!?。!?]{0,240}\b(?:false[ -]?positives?|expected\s+(?:absence|selection|result)|irrelevant\s+findings?|scenario\s+selection)\b)/iu;
23
+ const META_ANALYSIS_EVIDENCE_RE = /\b(?:is|are|as)\s+(?:an?\s+)?(?:positive[ -]?control|regression|detector)\s+(?:evidence|fixtures?|examples?|findings?)\b/iu;
24
+ const EXPECTED_META_ANALYSIS_RE = /^\s*expected\s*:(?=[^.!?。!?]{0,240}\b(?:detector|classifier|matcher|fixture|regression\s+test|expected\s+result)\b)/iu;
25
+ const QUALIFIED_EVIDENCE_SUBJECT_RE = /^\s*(?:[-*+>]\s+|\d+[.)]\s+)?(?:(?:given|when|then|and)\s+)?(?=[^.!?。!?]{0,240}\b(?:positive|negative|affirmative|meta-analysis(?:-only)?|regression|table-driven)\b)(?=[^.!?。!?]{0,240}\b(?:fixtures?|controls?|tests?|examples?|assertions?|findings?)\b)[^.!?。!?]{1,240}$/iu;
26
+ const CONTRACT_ANALYSIS_SUBJECT_RE = /^\s*(?:[-*+>]\s+|\d+[.)]\s+)?(?:(?:given|when|then|and)\s+)?(?:(?:the|this|that|a|an|any|each|our|existing|current)\s+)?(?:[\p{L}\p{N}_-]+\s+){0,2}contract\s+(?:evidence|inference|requirements?)\b/iu;
27
+ const CONTRACT_ANALYSIS_PREDICATE_RE = /\b(?:(?:is|are)\s+(?:inferred|expected|selected|classified|matched|detected|discussed|reported|recorded)|expects?|records?|describes?)\b|\b(?:detector|classifier|matcher)\s+evidence\b|\b(?:positive[ -]?control|expected\s+absence|meta-analysis(?:-only)?)\b/iu;
19
28
  function stripExcludedMarkdownSections(text) {
20
29
  const lines = text.split('\n');
21
30
  const kept = [];
@@ -45,12 +54,26 @@ function stripExcludedMarkdownSections(text) {
45
54
  }
46
55
  /** Remove fenced code and quoted examples before keyword-driven inference. */
47
56
  export function stripNonContractText(text) {
48
- return stripExcludedMarkdownSections(text)
57
+ return stripExcludedMarkdownSections(stripFrontmatter(text.replace(/^\uFEFF/, '')))
49
58
  .replace(/```[\s\S]*?```/g, ' ')
50
59
  .replace(/`[^`\n]+`/g, ' ')
51
60
  .replace(/"[^"\n]+"/g, ' ')
52
61
  .replace(/'[^'\n]+'/g, ' ');
53
62
  }
63
+ /** Remove clauses whose subject is detector analysis or test evidence, not product behavior. */
64
+ export function stripMetaAnalysisText(text) {
65
+ return text
66
+ .replace(/\n[\t ]+(?=[a-z0-9`])/giu, ' ')
67
+ .split(META_ANALYSIS_CLAUSE_BOUNDARY_RE)
68
+ .filter((clause) => clause.trim().length > 0 &&
69
+ !META_ANALYSIS_SUBJECT_RE.test(clause) &&
70
+ !ACTION_LED_META_ANALYSIS_RE.test(clause) &&
71
+ !META_ANALYSIS_EVIDENCE_RE.test(clause) &&
72
+ !EXPECTED_META_ANALYSIS_RE.test(clause) &&
73
+ !QUALIFIED_EVIDENCE_SUBJECT_RE.test(clause) &&
74
+ !(CONTRACT_ANALYSIS_SUBJECT_RE.test(clause) && CONTRACT_ANALYSIS_PREDICATE_RE.test(clause)))
75
+ .join('\n');
76
+ }
54
77
  function clauseBefore(text, index) {
55
78
  const before = text.slice(Math.max(0, index - 320), index);
56
79
  const boundary = Math.max(before.lastIndexOf('\n'), before.lastIndexOf('.'), before.lastIndexOf(';'));
@@ -0,0 +1,3 @@
1
+ /** Format a relative date string for a canonical timestamp. */
2
+ export declare function formatRelativeDate(timestamp: string): string;
3
+ //# sourceMappingURL=relative-date.d.ts.map
@@ -0,0 +1,16 @@
1
+ /** Format a relative date string for a canonical timestamp. */
2
+ export function formatRelativeDate(timestamp) {
3
+ const date = new Date(timestamp);
4
+ const diffDays = Math.floor((Date.now() - date.getTime()) / (1000 * 60 * 60 * 24));
5
+ if (diffDays === 0) {
6
+ return 'hoy';
7
+ }
8
+ if (diffDays === 1) {
9
+ return 'hace 1 dia';
10
+ }
11
+ if (diffDays <= 30) {
12
+ return `hace ${String(diffDays)} dias`;
13
+ }
14
+ return timestamp.slice(0, 10);
15
+ }
16
+ //# sourceMappingURL=relative-date.js.map
@@ -9,10 +9,21 @@ export declare function withBudget<T>(step: string, budgetMs: number, fn: (signa
9
9
  export declare function unwrapBudget<T>(result: BudgetResult<T>, fallback: T, warnings?: string[]): T;
10
10
  /** Applies the same cancellation tree to a complete operation ceiling. */
11
11
  export declare function withTotalBudget<T>(operationName: string, ceilingMs: number, fn: (signal: AbortSignal) => Promise<T>): Promise<{
12
+ status: 'ok';
12
13
  timedOut: false;
13
14
  value: T;
15
+ elapsedMs: number;
14
16
  } | {
17
+ status: 'failed';
18
+ timedOut: false;
19
+ category: 'operation_failed';
20
+ elapsedMs: number;
21
+ warning: string;
22
+ } | {
23
+ status: 'timed_out';
15
24
  timedOut: true;
25
+ ceilingMs: number;
26
+ elapsedMs: number;
16
27
  warning: string;
17
28
  }>;
18
29
  //# sourceMappingURL=budget.d.ts.map
@@ -26,13 +26,21 @@ function executeWithinBudget(requestedMs, fn) {
26
26
  }
27
27
  };
28
28
  const onAbort = () => {
29
- settle({ ok: false, error: new Error(errorMessage(scope.signal.reason)) });
29
+ settle({
30
+ ok: false,
31
+ error: new Error(errorMessage(scope.signal.reason)),
32
+ source: scope.didDeadlineElapse() ? 'deadline' : 'operation',
33
+ });
30
34
  };
31
35
  scope.signal.addEventListener('abort', onAbort, { once: true });
32
36
  task.then((value) => {
33
37
  settle({ ok: true, value });
34
38
  }, (error) => {
35
- settle({ ok: false, error: error instanceof Error ? error : new Error(String(error)) });
39
+ settle({
40
+ ok: false,
41
+ error: error instanceof Error ? error : new Error(String(error)),
42
+ source: 'operation',
43
+ });
36
44
  });
37
45
  if (scope.signal.aborted) {
38
46
  onAbort();
@@ -60,7 +68,7 @@ export async function withBudget(step, budgetMs, fn) {
60
68
  if (outcome.ok) {
61
69
  return { exceeded: false, value: outcome.value, elapsedMs };
62
70
  }
63
- const thrownBeforeDeadline = elapsedMs < execution.timeoutMs;
71
+ const thrownBeforeDeadline = outcome.source === 'operation';
64
72
  return {
65
73
  exceeded: true,
66
74
  step,
@@ -88,16 +96,26 @@ export async function withTotalBudget(operationName, ceilingMs, fn) {
88
96
  const execution = executeWithinBudget(ceilingMs, fn);
89
97
  try {
90
98
  const outcome = await execution.result;
99
+ const elapsedMs = elapsedSince(start);
91
100
  if (outcome.ok) {
92
- return { timedOut: false, value: outcome.value };
101
+ return { status: 'ok', timedOut: false, value: outcome.value, elapsedMs };
102
+ }
103
+ const thrownBeforeDeadline = outcome.source === 'operation';
104
+ if (thrownBeforeDeadline) {
105
+ return {
106
+ status: 'failed',
107
+ timedOut: false,
108
+ category: 'operation_failed',
109
+ elapsedMs,
110
+ warning: `[SPEC-713] Operation "${operationName}" failed after ${String(elapsedMs)}ms (operation_failed)`,
111
+ };
93
112
  }
94
- const elapsedMs = elapsedSince(start);
95
- const thrownBeforeDeadline = elapsedMs < execution.timeoutMs;
96
113
  return {
114
+ status: 'timed_out',
97
115
  timedOut: true,
98
- warning: thrownBeforeDeadline
99
- ? `[SPEC-713] Operation "${operationName}" threw after ${String(elapsedMs)}ms: ${errorMessage(outcome.error)}`
100
- : `[SPEC-713] Operation "${operationName}" exceeded ${String(execution.timeoutMs)}ms ceiling (actual: ${String(elapsedMs)}ms) — aborted cleanly`,
116
+ ceilingMs: execution.timeoutMs,
117
+ elapsedMs,
118
+ warning: `[SPEC-713] Operation "${operationName}" exceeded ${String(execution.timeoutMs)}ms ceiling (actual: ${String(elapsedMs)}ms) — aborted cleanly`,
101
119
  };
102
120
  }
103
121
  finally {
@@ -4,7 +4,7 @@ export declare function aggregateEntries(entries: LedgerEntry[]): LedgerAggregat
4
4
  export declare function computeDailyBreakdown(entries: LedgerEntry[]): DailyBreakdown[];
5
5
  export declare function detectTrend(dailyBreakdown: DailyBreakdown[]): TrendResult;
6
6
  export declare function detectAnomalies(entries: LedgerEntry[], dailyBreakdown: DailyBreakdown[]): Anomaly[];
7
- export declare function computeBudgetStatus(entries: LedgerEntry[], config: BudgetConfig): LedgerBudgetStatus;
7
+ export declare function computeBudgetStatus(entries: LedgerEntry[], config: BudgetConfig, now?: Date): LedgerBudgetStatus;
8
8
  export declare function getTopConsumers(entries: LedgerEntry[], limit?: number): TopConsumer[];
9
9
  export declare function computeOptimizationSavings(entries: LedgerEntry[]): OptimizationSavings;
10
10
  //# sourceMappingURL=analytics.d.ts.map