@principles/pd-cli 1.152.12 → 1.152.14

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 (305) hide show
  1. package/dist/commands/candidate.d.ts.map +1 -1
  2. package/dist/commands/candidate.js +34 -7
  3. package/dist/commands/candidate.js.map +1 -1
  4. package/dist/commands/diagnose.d.ts.map +1 -1
  5. package/dist/commands/diagnose.js +23 -7
  6. package/dist/commands/diagnose.js.map +1 -1
  7. package/dist/commands/pain-retry.d.ts.map +1 -1
  8. package/dist/commands/pain-retry.js +21 -7
  9. package/dist/commands/pain-retry.js.map +1 -1
  10. package/package.json +13 -7
  11. package/CHANGELOG.md +0 -23
  12. package/dist/commands/__tests__/intent-flag-wiring.test.d.ts +0 -9
  13. package/dist/commands/__tests__/intent-flag-wiring.test.d.ts.map +0 -1
  14. package/dist/commands/__tests__/intent-flag-wiring.test.js +0 -237
  15. package/dist/commands/__tests__/intent-flag-wiring.test.js.map +0 -1
  16. package/dist/commands/__tests__/legacy-cleanup.test.d.ts +0 -18
  17. package/dist/commands/__tests__/legacy-cleanup.test.d.ts.map +0 -1
  18. package/dist/commands/__tests__/legacy-cleanup.test.js +0 -467
  19. package/dist/commands/__tests__/legacy-cleanup.test.js.map +0 -1
  20. package/dist/commands/__tests__/mvp-smoke.test.d.ts +0 -15
  21. package/dist/commands/__tests__/mvp-smoke.test.d.ts.map +0 -1
  22. package/dist/commands/__tests__/mvp-smoke.test.js +0 -262
  23. package/dist/commands/__tests__/mvp-smoke.test.js.map +0 -1
  24. package/dist/commands/__tests__/pain-retry-maxTokens-flag.test.d.ts +0 -2
  25. package/dist/commands/__tests__/pain-retry-maxTokens-flag.test.d.ts.map +0 -1
  26. package/dist/commands/__tests__/pain-retry-maxTokens-flag.test.js +0 -60
  27. package/dist/commands/__tests__/pain-retry-maxTokens-flag.test.js.map +0 -1
  28. package/dist/commands/__tests__/rulecode-flag-wiring.test.d.ts +0 -21
  29. package/dist/commands/__tests__/rulecode-flag-wiring.test.d.ts.map +0 -1
  30. package/dist/commands/__tests__/rulecode-flag-wiring.test.js +0 -186
  31. package/dist/commands/__tests__/rulecode-flag-wiring.test.js.map +0 -1
  32. package/dist/commands/__tests__/rulecode-handler.test.d.ts +0 -16
  33. package/dist/commands/__tests__/rulecode-handler.test.d.ts.map +0 -1
  34. package/dist/commands/__tests__/rulecode-handler.test.js +0 -285
  35. package/dist/commands/__tests__/rulecode-handler.test.js.map +0 -1
  36. package/dist/commands/__tests__/run-rulehost-flag-wiring.test.d.ts +0 -24
  37. package/dist/commands/__tests__/run-rulehost-flag-wiring.test.d.ts.map +0 -1
  38. package/dist/commands/__tests__/run-rulehost-flag-wiring.test.js +0 -232
  39. package/dist/commands/__tests__/run-rulehost-flag-wiring.test.js.map +0 -1
  40. package/dist/commands/__tests__/runtime-activation-nextaction.test.d.ts +0 -15
  41. package/dist/commands/__tests__/runtime-activation-nextaction.test.d.ts.map +0 -1
  42. package/dist/commands/__tests__/runtime-activation-nextaction.test.js +0 -73
  43. package/dist/commands/__tests__/runtime-activation-nextaction.test.js.map +0 -1
  44. package/dist/commands/__tests__/runtime-probe-config.test.d.ts +0 -20
  45. package/dist/commands/__tests__/runtime-probe-config.test.d.ts.map +0 -1
  46. package/dist/commands/__tests__/runtime-probe-config.test.js +0 -389
  47. package/dist/commands/__tests__/runtime-probe-config.test.js.map +0 -1
  48. package/dist/commands/__tests__/telemetry-flag-wiring.test.d.ts +0 -9
  49. package/dist/commands/__tests__/telemetry-flag-wiring.test.d.ts.map +0 -1
  50. package/dist/commands/__tests__/telemetry-flag-wiring.test.js +0 -76
  51. package/dist/commands/__tests__/telemetry-flag-wiring.test.js.map +0 -1
  52. package/dist/commands/__tests__/version.test.d.ts +0 -2
  53. package/dist/commands/__tests__/version.test.d.ts.map +0 -1
  54. package/dist/commands/__tests__/version.test.js +0 -238
  55. package/dist/commands/__tests__/version.test.js.map +0 -1
  56. package/dist/commands/runtime-uat.guard.test.d.ts +0 -2
  57. package/dist/commands/runtime-uat.guard.test.d.ts.map +0 -1
  58. package/dist/commands/runtime-uat.guard.test.js +0 -153
  59. package/dist/commands/runtime-uat.guard.test.js.map +0 -1
  60. package/dist/services/__tests__/cli-output.test.d.ts +0 -18
  61. package/dist/services/__tests__/cli-output.test.d.ts.map +0 -1
  62. package/dist/services/__tests__/cli-output.test.js +0 -103
  63. package/dist/services/__tests__/cli-output.test.js.map +0 -1
  64. package/dist/services/__tests__/evaluator-runner-deps.test.d.ts +0 -2
  65. package/dist/services/__tests__/evaluator-runner-deps.test.d.ts.map +0 -1
  66. package/dist/services/__tests__/evaluator-runner-deps.test.js +0 -343
  67. package/dist/services/__tests__/evaluator-runner-deps.test.js.map +0 -1
  68. package/dist/services/__tests__/rulehost-readiness.test.d.ts +0 -2
  69. package/dist/services/__tests__/rulehost-readiness.test.d.ts.map +0 -1
  70. package/dist/services/__tests__/rulehost-readiness.test.js +0 -311
  71. package/dist/services/__tests__/rulehost-readiness.test.js.map +0 -1
  72. package/dist/services/__tests__/runtime-adapter-resolver.test.d.ts +0 -18
  73. package/dist/services/__tests__/runtime-adapter-resolver.test.d.ts.map +0 -1
  74. package/dist/services/__tests__/runtime-adapter-resolver.test.js +0 -673
  75. package/dist/services/__tests__/runtime-adapter-resolver.test.js.map +0 -1
  76. package/dist/utils/production-workspace-guard.test.d.ts +0 -2
  77. package/dist/utils/production-workspace-guard.test.d.ts.map +0 -1
  78. package/dist/utils/production-workspace-guard.test.js +0 -137
  79. package/dist/utils/production-workspace-guard.test.js.map +0 -1
  80. package/scripts/llm-dogfood.ts +0 -456
  81. package/scripts/migrate-illegal-expected-decision.ts +0 -252
  82. package/src/commands/__tests__/intent-flag-wiring.test.ts +0 -282
  83. package/src/commands/__tests__/legacy-cleanup.test.ts +0 -602
  84. package/src/commands/__tests__/mvp-smoke.test.ts +0 -300
  85. package/src/commands/__tests__/pain-retry-maxTokens-flag.test.ts +0 -73
  86. package/src/commands/__tests__/rulecode-flag-wiring.test.ts +0 -237
  87. package/src/commands/__tests__/rulecode-handler.test.ts +0 -369
  88. package/src/commands/__tests__/run-rulehost-flag-wiring.test.ts +0 -292
  89. package/src/commands/__tests__/runtime-activation-nextaction.test.ts +0 -81
  90. package/src/commands/__tests__/runtime-probe-config.test.ts +0 -432
  91. package/src/commands/__tests__/telemetry-flag-wiring.test.ts +0 -84
  92. package/src/commands/__tests__/version.test.ts +0 -251
  93. package/src/commands/admission-gate.ts +0 -30
  94. package/src/commands/artifact.ts +0 -82
  95. package/src/commands/build-trajectory-evidence.ts +0 -298
  96. package/src/commands/candidate.ts +0 -1250
  97. package/src/commands/codex-ingest-catchup.ts +0 -114
  98. package/src/commands/codex-ingest-quarantine.ts +0 -127
  99. package/src/commands/codex-reconcile.ts +0 -84
  100. package/src/commands/codex-setup.ts +0 -476
  101. package/src/commands/codex-worker.ts +0 -185
  102. package/src/commands/command-helpers.ts +0 -24
  103. package/src/commands/config-doctor.ts +0 -193
  104. package/src/commands/console.ts +0 -797
  105. package/src/commands/context.ts +0 -72
  106. package/src/commands/demo-story-a.ts +0 -241
  107. package/src/commands/diagnose.ts +0 -756
  108. package/src/commands/errors-list.ts +0 -441
  109. package/src/commands/evolution-tasks-list.ts +0 -48
  110. package/src/commands/evolution-tasks-show.ts +0 -62
  111. package/src/commands/flow.ts +0 -60
  112. package/src/commands/health-codex.ts +0 -576
  113. package/src/commands/health.ts +0 -315
  114. package/src/commands/history.ts +0 -63
  115. package/src/commands/intent.ts +0 -457
  116. package/src/commands/legacy-cleanup.ts +0 -531
  117. package/src/commands/legacy-import.ts +0 -104
  118. package/src/commands/mvp-smoke.ts +0 -160
  119. package/src/commands/pain-evidence.ts +0 -226
  120. package/src/commands/pain-list.ts +0 -239
  121. package/src/commands/pain-record.ts +0 -612
  122. package/src/commands/pain-retry.ts +0 -916
  123. package/src/commands/principles-stats.ts +0 -700
  124. package/src/commands/proven-channel-baseline.ts +0 -115
  125. package/src/commands/quality-scorecard.ts +0 -272
  126. package/src/commands/remediation-output.ts +0 -66
  127. package/src/commands/rulecode.ts +0 -465
  128. package/src/commands/run.ts +0 -89
  129. package/src/commands/runtime-activation.ts +0 -1629
  130. package/src/commands/runtime-artifact-repair.ts +0 -524
  131. package/src/commands/runtime-canary.ts +0 -395
  132. package/src/commands/runtime-compatibility-scan.ts +0 -105
  133. package/src/commands/runtime-diagnostics-export.ts +0 -229
  134. package/src/commands/runtime-features.ts +0 -153
  135. package/src/commands/runtime-gfi-snapshot.ts +0 -135
  136. package/src/commands/runtime-health-snapshot.ts +0 -108
  137. package/src/commands/runtime-init.ts +0 -429
  138. package/src/commands/runtime-internalization-enqueue-successors.ts +0 -530
  139. package/src/commands/runtime-internalization-integrity-repair.ts +0 -79
  140. package/src/commands/runtime-internalization-integrity.ts +0 -107
  141. package/src/commands/runtime-internalization-queue.ts +0 -135
  142. package/src/commands/runtime-internalization-retry.ts +0 -191
  143. package/src/commands/runtime-internalization-run-once.ts +0 -791
  144. package/src/commands/runtime-internalization-run-rulehost.ts +0 -749
  145. package/src/commands/runtime-internalization-wake-once.ts +0 -87
  146. package/src/commands/runtime-pain-flood-simulation.ts +0 -121
  147. package/src/commands/runtime-pruning.ts +0 -438
  148. package/src/commands/runtime-recovery-failed-tasks.ts +0 -258
  149. package/src/commands/runtime-recovery.ts +0 -109
  150. package/src/commands/runtime-synthetic-baseline.ts +0 -70
  151. package/src/commands/runtime-uat.guard.test.ts +0 -175
  152. package/src/commands/runtime-uat.ts +0 -380
  153. package/src/commands/runtime.ts +0 -473
  154. package/src/commands/samples-list.ts +0 -39
  155. package/src/commands/samples-review.ts +0 -36
  156. package/src/commands/task.ts +0 -271
  157. package/src/commands/telemetry.ts +0 -349
  158. package/src/commands/trace.ts +0 -174
  159. package/src/commands/trajectory-db-unavailable.ts +0 -19
  160. package/src/commands/trajectory.ts +0 -64
  161. package/src/commands/version.ts +0 -49
  162. package/src/config-reader.ts +0 -122
  163. package/src/index.ts +0 -1276
  164. package/src/legacy/legacy-import.ts +0 -179
  165. package/src/legacy/session-history-import.ts +0 -203
  166. package/src/resolve-workspace.ts +0 -106
  167. package/src/services/__tests__/cli-output.test.ts +0 -130
  168. package/src/services/__tests__/evaluator-runner-deps.test.ts +0 -382
  169. package/src/services/__tests__/rulehost-readiness.test.ts +0 -363
  170. package/src/services/__tests__/runtime-adapter-resolver.test.ts +0 -793
  171. package/src/services/cli-output.ts +0 -95
  172. package/src/services/config-doctor.ts +0 -697
  173. package/src/services/console-launcher.ts +0 -537
  174. package/src/services/demo-rule-compiler.ts +0 -40
  175. package/src/services/demo-story-a-runner.ts +0 -471
  176. package/src/services/mainline-snapshot-assembler.ts +0 -579
  177. package/src/services/pain-flood-simulation-runner.ts +0 -462
  178. package/src/services/pd-config-loader.ts +0 -433
  179. package/src/services/proven-channel-baseline-runner.ts +0 -157
  180. package/src/services/quality-scorecard/data-extractor.ts +0 -150
  181. package/src/services/quality-scorecard/local-evaluator.ts +0 -142
  182. package/src/services/quality-scorecard/strong-model-gate.ts +0 -202
  183. package/src/services/resolve-runtime-from-pd-config.ts +0 -230
  184. package/src/services/rulehost-pipeline-runner.ts +0 -1214
  185. package/src/services/rulehost-readiness.ts +0 -326
  186. package/src/services/runtime-adapter-resolver.ts +0 -364
  187. package/src/services/synthetic-baseline-runner.ts +0 -400
  188. package/src/services/version-report.ts +0 -253
  189. package/src/services/workspace-tool-semantics.ts +0 -26
  190. package/src/utils/path-security.ts +0 -96
  191. package/src/utils/production-workspace-guard.test.ts +0 -157
  192. package/src/utils/production-workspace-guard.ts +0 -210
  193. package/tests/bdd/cli-contract.steps.ts +0 -346
  194. package/tests/bdd/principles-stats.steps.ts +0 -211
  195. package/tests/bdd/support/gherkin-loader.ts +0 -147
  196. package/tests/bdd/support/repo-root.ts +0 -62
  197. package/tests/bdd/support/vitest-bdd.ts +0 -179
  198. package/tests/commands/admission-gate-coverage.test.ts +0 -79
  199. package/tests/commands/build-trajectory-evidence.test.ts +0 -508
  200. package/tests/commands/candidate-audit-repair.test.ts +0 -421
  201. package/tests/commands/candidate-intake.test.ts +0 -732
  202. package/tests/commands/candidate-internalization-backfill.test.ts +0 -548
  203. package/tests/commands/candidate-internalize-lineage.test.ts +0 -628
  204. package/tests/commands/candidate-internalize.test.ts +0 -299
  205. package/tests/commands/candidate-route.test.ts +0 -329
  206. package/tests/commands/candidate-show.test.ts +0 -95
  207. package/tests/commands/cli-command-tree.test.ts +0 -144
  208. package/tests/commands/cli-help-snapshot.test.ts +0 -155
  209. package/tests/commands/cli-skill-contract.test.ts +0 -121
  210. package/tests/commands/codex-consent-reversibility.steps.test.ts +0 -299
  211. package/tests/commands/codex-ingest-quarantine.test.ts +0 -136
  212. package/tests/commands/codex-setup.test.ts +0 -297
  213. package/tests/commands/codex-worker-daemon.test.ts +0 -95
  214. package/tests/commands/codex-worker-registration.test.ts +0 -207
  215. package/tests/commands/config-doctor.test.ts +0 -336
  216. package/tests/commands/console-launcher-edge-cases.test.ts +0 -388
  217. package/tests/commands/console-open.test.ts +0 -1381
  218. package/tests/commands/context.test.ts +0 -114
  219. package/tests/commands/demo-story-a.test.ts +0 -318
  220. package/tests/commands/diagnose.test.ts +0 -1803
  221. package/tests/commands/errors-list.test.ts +0 -679
  222. package/tests/commands/evolution-tasks-list.test.ts +0 -41
  223. package/tests/commands/evolution-tasks-show.test.ts +0 -46
  224. package/tests/commands/health-codex.test.ts +0 -349
  225. package/tests/commands/health-host-wiring.test.ts +0 -76
  226. package/tests/commands/health-reliability.test.ts +0 -99
  227. package/tests/commands/health.test.ts +0 -413
  228. package/tests/commands/intent.test.ts +0 -321
  229. package/tests/commands/legacy-cleanup.test.ts +0 -148
  230. package/tests/commands/pain-evidence.test.ts +0 -518
  231. package/tests/commands/pain-list.test.ts +0 -219
  232. package/tests/commands/pain-record-async.test.ts +0 -335
  233. package/tests/commands/pain-record-session-parser.test.ts +0 -228
  234. package/tests/commands/pain-record.test.ts +0 -851
  235. package/tests/commands/pain-retry.test.ts +0 -1121
  236. package/tests/commands/plugin-config-resolution-cutover.test.ts +0 -229
  237. package/tests/commands/pri-393-runtime-config-unification.test.ts +0 -443
  238. package/tests/commands/principles-stats-wiring.test.ts +0 -135
  239. package/tests/commands/principles-stats.test.ts +0 -274
  240. package/tests/commands/product-path-regression.test.ts +0 -86
  241. package/tests/commands/proven-channel-baseline.test.ts +0 -441
  242. package/tests/commands/rulecode.test.ts +0 -177
  243. package/tests/commands/run-rulehost-handler.test.ts +0 -908
  244. package/tests/commands/runtime-activation-approve-flag-wiring.test.ts +0 -227
  245. package/tests/commands/runtime-activation-deactivate-flag-wiring.test.ts +0 -167
  246. package/tests/commands/runtime-activation-dispatch-flag-wiring.test.ts +0 -262
  247. package/tests/commands/runtime-activation-governance-audit.test.ts +0 -95
  248. package/tests/commands/runtime-activation-list-flag-wiring.test.ts +0 -193
  249. package/tests/commands/runtime-activation-promote-flag-wiring.test.ts +0 -248
  250. package/tests/commands/runtime-activation-shadow-telemetry.test.ts +0 -130
  251. package/tests/commands/runtime-activation.test.ts +0 -2052
  252. package/tests/commands/runtime-artifact-repair-registration.test.ts +0 -74
  253. package/tests/commands/runtime-artifact-repair.test.ts +0 -404
  254. package/tests/commands/runtime-canary.test.ts +0 -430
  255. package/tests/commands/runtime-compatibility-scan.test.ts +0 -146
  256. package/tests/commands/runtime-diagnostics-export.test.ts +0 -175
  257. package/tests/commands/runtime-features.test.ts +0 -249
  258. package/tests/commands/runtime-health-snapshot.test.ts +0 -358
  259. package/tests/commands/runtime-init-empty-workspace.test.ts +0 -252
  260. package/tests/commands/runtime-init.test.ts +0 -420
  261. package/tests/commands/runtime-internalization-enqueue-successors.test.ts +0 -782
  262. package/tests/commands/runtime-internalization-integrity-repair.test.ts +0 -239
  263. package/tests/commands/runtime-internalization-integrity.test.ts +0 -140
  264. package/tests/commands/runtime-internalization-queue.test.ts +0 -380
  265. package/tests/commands/runtime-internalization-retry-owner-authority.test.ts +0 -431
  266. package/tests/commands/runtime-internalization-run-once-evaluator-parity.test.ts +0 -269
  267. package/tests/commands/runtime-internalization-run-once-language.test.ts +0 -303
  268. package/tests/commands/runtime-internalization-run-once-real-config.test.ts +0 -237
  269. package/tests/commands/runtime-internalization-run-once-rollout-parity.test.ts +0 -706
  270. package/tests/commands/runtime-internalization-run-once.test.ts +0 -1791
  271. package/tests/commands/runtime-internalization-wake-once.test.ts +0 -171
  272. package/tests/commands/runtime-internalization.test.ts +0 -53
  273. package/tests/commands/runtime-pain-flood-simulation.test.ts +0 -418
  274. package/tests/commands/runtime-pruning.test.ts +0 -694
  275. package/tests/commands/runtime-recovery-failed-tasks.test.ts +0 -328
  276. package/tests/commands/runtime-recovery.test.ts +0 -120
  277. package/tests/commands/runtime-synthetic-baseline.test.ts +0 -249
  278. package/tests/commands/runtime-uat.test.ts +0 -397
  279. package/tests/commands/runtime.test.ts +0 -392
  280. package/tests/commands/samples-list.test.ts +0 -43
  281. package/tests/commands/samples-review.test.ts +0 -41
  282. package/tests/commands/task.test.ts +0 -153
  283. package/tests/commands/telemetry.test.ts +0 -190
  284. package/tests/commands/trace.test.ts +0 -316
  285. package/tests/config-reader.test.ts +0 -143
  286. package/tests/e2e/candidate-intake-e2e.test.ts +0 -338
  287. package/tests/e2e/cli-full-flow.test.ts +0 -233
  288. package/tests/e2e/cross-package-acceptance.test.ts +0 -635
  289. package/tests/helpers/pd-cli-path.ts +0 -5
  290. package/tests/pd-cli-smoke.test.ts +0 -13
  291. package/tests/resolve-workspace.test.ts +0 -196
  292. package/tests/services/config-doctor-signal-health.test.ts +0 -109
  293. package/tests/services/console-launcher.test.ts +0 -114
  294. package/tests/services/demo-rule-compiler.test.ts +0 -242
  295. package/tests/services/mainline-snapshot-assembler.test.ts +0 -572
  296. package/tests/services/pd-config-loader.test.ts +0 -505
  297. package/tests/services/proven-channel-baseline-runner.test.ts +0 -30
  298. package/tests/services/quality-scorecard/strong-model-gate.test.ts +0 -133
  299. package/tests/services/resolve-runtime-from-pd-config.test.ts +0 -485
  300. package/tests/services/rulehost-pipeline-e2e.test.ts +0 -538
  301. package/tests/services/rulehost-pipeline-runner.test.ts +0 -1242
  302. package/tests/services/workspace-tool-semantics.test.ts +0 -76
  303. package/tests/utils/path-security.test.ts +0 -180
  304. package/tsconfig.json +0 -26
  305. package/vitest.config.ts +0 -10
@@ -1,160 +0,0 @@
1
- /**
2
- * pd mvp smoke — MVP mainline readiness check (PRI-397 / C5).
3
- *
4
- * Assembles a MainlineSnapshot from the shared reader, judges it via the
5
- * pure assertMainlineContract, and outputs a single JSON verdict.
6
- *
7
- * Rules (EP-04, EP-02):
8
- * - Read-only by default. NEVER mutates workspace state.
9
- * - Uses the shared mainline-snapshot-assembler (no new chain logic).
10
- * - --json mode: stdout = exactly one parseable JSON object — even on failure.
11
- * Every degraded/refused path emits a structured `{ok, reason, nextAction}`
12
- * JSON so CI/script consumers can branch on outcome (EP-04 Rule 6).
13
- * - Human mode: formatted output to stdout.
14
- * - Exit code: 0 on overall === 'ok', 1 on overall === 'violation' or failure.
15
- */
16
-
17
- import type { Command } from 'commander';
18
- import {
19
- assembleMainlineSnapshot,
20
- assertMainlineContract,
21
- } from '../services/mainline-snapshot-assembler.js';
22
- import { resolveWorkspaceDir } from '../resolve-workspace.js';
23
- import { withWorkspaceAndJson } from './command-helpers.js';
24
-
25
- export interface MvpSmokeOptions {
26
- workspace?: string;
27
- json?: boolean;
28
- }
29
-
30
- /**
31
- * Classify a thrown error so the failure JSON can name a real next action.
32
- * EP-03: never silently swallow; emit a structured reason + actionable next step.
33
- */
34
- function classifySmokeError(err: unknown): { reason: string; nextAction: string } {
35
- if (err instanceof Error) {
36
- const msg = err.message;
37
- const lower = msg.toLowerCase();
38
- // No .pd/state.db on disk (fresh / reset workspace) is the post-PRI-398
39
- // expected first failure — name the recovery path explicitly.
40
- if (
41
- msg.includes('SQLITE_CANTOPEN') ||
42
- lower.includes('no such file') ||
43
- lower.includes('no such table') ||
44
- lower.includes('cannot open database') ||
45
- lower.includes('directory does not exist')
46
- ) {
47
- return {
48
- reason: `Workspace database is missing or unreadable: ${msg}`,
49
- nextAction: 'Run "pd runtime internalization integrity-repair --confirm" or restore the workspace; this command requires a bootstrapped .pd/state.db.',
50
- };
51
- }
52
- if (lower.includes('workspace') && lower.includes('not configured')) {
53
- return {
54
- reason: msg,
55
- nextAction: 'Set --workspace <path>, PD_WORKSPACE_DIR, or add workspace.default to .pd/config.yaml.',
56
- };
57
- }
58
- return {
59
- reason: msg,
60
- nextAction: 'Inspect the workspace state and retry. Run "pd config doctor --json" to validate the workspace.',
61
- };
62
- }
63
- return {
64
- reason: `Unknown failure: ${String(err)}`,
65
- nextAction: 'Inspect the workspace state and retry. Run "pd config doctor --json" to validate the workspace.',
66
- };
67
- }
68
-
69
- export async function handleMvpSmoke(opts: MvpSmokeOptions): Promise<void> {
70
- // Pass through resolveWorkspaceDir to honor the consistency warning for
71
- // --workspace flags that disagree with the config default. The resolver
72
- // returns an absolute, normalized path either way.
73
- const workspaceDir = resolveWorkspaceDir(opts.workspace);
74
-
75
- let result;
76
- try {
77
- result = await assembleMainlineSnapshot({ workspaceDir });
78
- } catch (err: unknown) {
79
- const { reason, nextAction } = classifySmokeError(err);
80
- if (opts.json) {
81
- // EP-04 Rule 1 + 6: stdout = exactly one parseable JSON object carrying
82
- // a structured reason and nextAction, even on the failure path.
83
- console.log(JSON.stringify({
84
- ok: false,
85
- reason,
86
- nextAction,
87
- workspace: workspaceDir,
88
- }, null, 2));
89
- } else {
90
- console.error(`MVP smoke failed: ${reason}`);
91
- console.error(`nextAction: ${nextAction}`);
92
- }
93
- process.exit(1);
94
- return;
95
- }
96
-
97
- const verdict = assertMainlineContract(result.snapshot);
98
-
99
- if (opts.json) {
100
- console.log(JSON.stringify({
101
- ok: verdict.overall === 'ok',
102
- verdict,
103
- warnings: result.warnings,
104
- resolvedPainId: result.resolvedPainId,
105
- }, null, 2));
106
- } else {
107
- console.log(`\nMVP Smoke — ${workspaceDir}\n`);
108
- console.log(` Overall: ${verdict.overall}`);
109
- console.log(` Pain ID: ${verdict.painId ?? '(none)'}`);
110
- console.log(` Generated At: ${verdict.generatedAt}\n`);
111
-
112
- console.log(' Stages:');
113
- for (const s of verdict.stages) {
114
- const icon = s.status === 'ok' ? ' OK' : s.status === 'violation' ? ' VIOLATION' : ' SKIP';
115
- console.log(` [${icon}] ${s.stage}`);
116
- console.log(` ${s.reason}`);
117
- if (s.nextAction) {
118
- console.log(` nextAction: ${s.nextAction}`);
119
- }
120
- console.log('');
121
- }
122
-
123
- if (result.warnings.length > 0) {
124
- console.log(' Warnings:');
125
- for (const w of result.warnings) {
126
- console.log(` - ${w}`);
127
- }
128
- console.log('');
129
- }
130
- }
131
-
132
- if (verdict.overall === 'violation') {
133
- process.exit(1);
134
- return;
135
- }
136
- }
137
-
138
- /**
139
- * Register the `pd mvp` parent command and its `smoke` subcommand.
140
- *
141
- * Single source of truth for both production (`index.ts`) and parser tests
142
- * (`mvp-smoke.test.ts`). If a test passes against this function, the same
143
- * registration runs in production — flag typos surface at parseAsync time.
144
- */
145
- export function registerMvpCommands(program: Command): Command {
146
- const mvpCmd = program
147
- .command('mvp', { hidden: true })
148
- .description('MVP readiness commands');
149
-
150
- withWorkspaceAndJson(
151
- mvpCmd
152
- .command('smoke')
153
- .description('Check MVP mainline readiness: assemble snapshot → assert contract → structured verdict')
154
- .action(async (opts: { workspace?: string; json?: boolean }) => {
155
- await handleMvpSmoke({ workspace: opts.workspace, json: opts.json });
156
- }),
157
- );
158
-
159
- return mvpCmd;
160
- }
@@ -1,226 +0,0 @@
1
- /**
2
- * pd pain evidence command — PEAT-B2
3
- *
4
- * Query recent admission/trigger decisions from PD logs.
5
- * This is a read-only diagnostic command — no side effects.
6
- *
7
- * Usage:
8
- * pd pain evidence [--workspace <path>] [--limit N] [--json]
9
- *
10
- * Shows the most recent TRIGGER_DECISION log entries.
11
- *
12
- * Note: TRIGGER_DECISION entries are pain-signal admission gate decisions
13
- * (Gate B / TriggerController). These are distinct from GFI (General
14
- * Friction Index) friction events like GFI_SLICE_RESET, which track
15
- * session-level tool-friction resets and are NOT pain signals. This command
16
- * does not surface GFI events; use `pd runtime health gfi` for GFI state.
17
- */
18
-
19
- import * as fs from 'fs';
20
- import * as path from 'path';
21
- import { resolveWorkspaceDir } from '../resolve-workspace.js';
22
- import { assertSafeDirectoryRoot, isPathInside } from '../utils/path-security.js';
23
-
24
- interface EvidenceOptions {
25
- workspace?: string;
26
- limit?: number;
27
- json?: boolean;
28
- }
29
-
30
- interface TriggerDecisionEntry {
31
- timestamp: string;
32
- outcome: string;
33
- sourceKind: string;
34
- reason: string;
35
- nextAction: string;
36
- tool?: string;
37
- path?: string;
38
- painId?: string;
39
- score?: number;
40
- sessionId?: string;
41
- }
42
-
43
- /**
44
- * Get the memory/logs directory for a workspace (canonical root).
45
- * SystemLogger writes to <workspace>/memory/logs/SYSTEM_YYYY-MM-DD.log.
46
- * The workspace root is canonicalized once here so downstream containment
47
- * checks compare canonical paths (relative --workspace inputs work).
48
- */
49
- function getLogDir(workspaceDir: string): string {
50
- const root = assertSafeDirectoryRoot(workspaceDir, 'workspace path');
51
- return path.join(root, 'memory', 'logs');
52
- }
53
-
54
- /**
55
- * Parse TRIGGER_DECISION entries from a log file.
56
- * Returns entries in reverse chronological order (newest first).
57
- */
58
- function parseTriggerDecisions(logContent: string): TriggerDecisionEntry[] {
59
- const entries: TriggerDecisionEntry[] = [];
60
- const lines = logContent.split('\n');
61
-
62
- for (const line of lines) {
63
- if (!line.includes('TRIGGER_DECISION')) continue;
64
-
65
- // Extract JSON payload from log line
66
- const jsonStart = line.indexOf('{');
67
- if (jsonStart === -1) continue;
68
-
69
- try {
70
- const payload = JSON.parse(line.slice(jsonStart));
71
- // Extract timestamp from log line prefix.
72
- // SystemLogger produces two formats:
73
- // - Plain: "2026-06-08 10:16:00 [INFO] ..."
74
- // - Bracketed ISO: "[2026-06-08T10:16:00.123Z] [INFO] ..."
75
- const tsMatch = /^(\d{4}-\d{2}-\d{2}\s+\d{2}:\d{2}:\d{2})/.exec(line)
76
- ?? /^\[(\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d+)?Z)\]/.exec(line);
77
- entries.push({
78
- timestamp: tsMatch?.[1] ?? new Date().toISOString(),
79
- outcome: typeof payload.outcome === 'string' ? payload.outcome : 'unknown',
80
- sourceKind: typeof payload.sourceKind === 'string' ? payload.sourceKind : 'unknown',
81
- reason: typeof payload.reason === 'string' ? payload.reason : '',
82
- nextAction: typeof payload.nextAction === 'string' ? payload.nextAction : '',
83
- tool: typeof payload.tool === 'string' ? payload.tool : undefined,
84
- path: typeof payload.path === 'string' ? payload.path : undefined,
85
- painId: typeof payload.painId === 'string' ? payload.painId : undefined,
86
- score: typeof payload.score === 'number' ? payload.score : undefined,
87
- sessionId: typeof payload.sessionId === 'string' ? payload.sessionId : undefined,
88
- });
89
- } catch (e) {
90
- // Skip malformed entries — always log for operator visibility (EP-03)
91
- console.error(`WARN: Malformed TRIGGER_DECISION entry: ${String(e).slice(0, 100)}`);
92
- }
93
- }
94
-
95
- return entries;
96
- }
97
-
98
- /**
99
- * Read recent trigger decisions from memory/logs files.
100
- * SystemLogger writes date-stamped files: SYSTEM_YYYY-MM-DD.log.
101
- */
102
- function readRecentDecisions(logDir: string, limit: number): TriggerDecisionEntry[] {
103
- if (!fs.existsSync(logDir)) return [];
104
-
105
- const logFiles = fs.readdirSync(logDir)
106
- .filter(f => f.startsWith('SYSTEM_') && f.endsWith('.log'))
107
- .sort()
108
- .reverse(); // newest first
109
-
110
- const allEntries: TriggerDecisionEntry[] = [];
111
-
112
- for (const logFile of logFiles) {
113
- if (allEntries.length >= limit) break;
114
-
115
- // CWE-22: verify the canonical log path stays inside the canonical
116
- // logDir before reading (canonical-vs-canonical containment, so relative
117
- // workspace roots work).
118
- const filePath = path.resolve(logDir, logFile);
119
- if (!isPathInside(logDir, filePath)) continue;
120
- try {
121
- const content = fs.readFileSync(filePath, 'utf8');
122
- const entries = parseTriggerDecisions(content);
123
- // Reverse per-file entries so combined list is newest-first.
124
- // Log files are already sorted newest-first, but entries within
125
- // each file are in chronological (oldest-first) order.
126
- allEntries.push(...entries.reverse());
127
- } catch (e) {
128
- // Skip unreadable files — always log for operator visibility (EP-03)
129
- console.error(`WARN: Could not read log file ${logFile}: ${String(e).slice(0, 100)}`);
130
- }
131
- }
132
-
133
- return allEntries.slice(0, limit);
134
- }
135
-
136
- function getOutcomeEmoji(outcome: string): string {
137
- switch (outcome) {
138
- case 'diagnosis_created': return '🔧';
139
- case 'manual_owner_admitted': return '✅';
140
- case 'evidence_only': return '📋';
141
- case 'health_only': return '💚';
142
- case 'cooldown_skipped': return '⏳';
143
- case 'owner_confirm_required': return '❓';
144
- case 'refused': return '🚫';
145
- default: return '❔';
146
- }
147
- }
148
-
149
- function truncate(s: string, maxLen: number): string {
150
- if (s.length <= maxLen) return s;
151
- return s.slice(0, maxLen - 3) + '...';
152
- }
153
-
154
- export async function handlePainEvidence(opts: EvidenceOptions): Promise<void> {
155
- const { workspace, limit: rawLimit, json } = opts;
156
- const workspaceDir = resolveWorkspaceDir(workspace);
157
- const logDir = getLogDir(workspaceDir);
158
-
159
- // Validate limit — fail loud for invalid values
160
- let effectiveLimit = 20;
161
- if (rawLimit !== undefined) {
162
- if (!Number.isInteger(rawLimit) || rawLimit < 1 || rawLimit > 10000) {
163
- const err = {
164
- status: 'refused',
165
- reason: `invalid_limit: limit must be an integer between 1 and 10000, got ${rawLimit}`,
166
- nextAction: 'Pass --limit with a valid integer (1-10000)',
167
- };
168
- const { reason, nextAction } = err;
169
- if (json) {
170
- console.log(JSON.stringify({ count: 0, error: err }));
171
- } else {
172
- console.error('Error: ' + reason);
173
- console.error('Next: ' + nextAction);
174
- }
175
- process.exit(1);
176
- return; // guard: test stubs of process.exit continue execution
177
- }
178
- effectiveLimit = rawLimit;
179
- }
180
-
181
- const decisions = readRecentDecisions(logDir, effectiveLimit);
182
-
183
- if (json) {
184
- console.log(JSON.stringify({ count: decisions.length, decisions, searchedPath: path.join(logDir, 'SYSTEM_*.log') }, null, 2));
185
- return;
186
- }
187
-
188
- if (decisions.length === 0) {
189
- console.log('No trigger decisions found in logs.');
190
- console.log(`Searched: ${path.join(logDir, 'SYSTEM_*.log')}`);
191
- // PRI-763: the historic painEvidenceAdmission flag (and its advice) was
192
- // retired — it never had executable consumers; Gate B records
193
- // unconditionally when the plugin runs.
194
- return;
195
- }
196
-
197
- console.log(`Recent Pain Evidence Admission Decisions (${decisions.length}):`);
198
- console.log('─'.repeat(80));
199
-
200
- for (const d of decisions) {
201
- const outcomeEmoji = getOutcomeEmoji(d.outcome);
202
- console.log(` ${outcomeEmoji} [${d.timestamp}] ${d.outcome}`);
203
- console.log(` Source: ${d.sourceKind} | Score: ${d.score ?? 'N/A'}`);
204
- console.log(` Reason: ${truncate(d.reason, 100)}`);
205
- if (d.nextAction && d.nextAction !== 'none') {
206
- console.log(` Next: ${truncate(d.nextAction, 80)}`);
207
- }
208
- if (d.tool) console.log(` Tool: ${d.tool} | Path: ${d.path ?? 'N/A'}`);
209
- console.log();
210
- }
211
-
212
- // Summary
213
- const byOutcome = new Map<string, number>();
214
- for (const d of decisions) {
215
- byOutcome.set(d.outcome, (byOutcome.get(d.outcome) ?? 0) + 1);
216
- }
217
- console.log('─'.repeat(80));
218
- console.log('Summary:');
219
- for (const [outcome, count] of byOutcome) {
220
- console.log(` ${getOutcomeEmoji(outcome)} ${outcome}: ${count}`);
221
- }
222
- }
223
-
224
- // Export for testing
225
- // istanbul ignore next
226
- export { parseTriggerDecisions, getLogDir };
@@ -1,239 +0,0 @@
1
- /**
2
- * pd pain list command — PRI-640 (Governance Signal Host Attribution v0.1)
3
- *
4
- * Read-only: lists canonical pain_events rows from trajectory.db with their
5
- * host attribution. `--host` filters by openclaw / codex / unknown.
6
- * PRI-743: the result (human and --json) also carries `byHost`, the
7
- * full-table host distribution independent of --host/--limit.
8
- *
9
- * Usage:
10
- * pd pain list [--workspace <path>] [--limit N] [--host openclaw|codex|unknown] [--json]
11
- *
12
- * Host semantics (SPEC §6/§12): host_kind is observability metadata only.
13
- * NULL (legacy / manual / unprovable) is reported as `unknown` — never guessed.
14
- * On a pre-PRI-640 database without the host_kind column every row reports
15
- * `unknown` and a `host_kind_column_missing` warning is emitted (rc-9).
16
- */
17
-
18
- import * as fs from 'fs';
19
- import * as path from 'path';
20
- import { resolveWorkspaceDir } from '../resolve-workspace.js';
21
-
22
- export type PainListHostFilter = 'openclaw' | 'codex' | 'unknown';
23
-
24
- export interface PainListOptions {
25
- workspace?: string;
26
- limit?: number;
27
- host?: string;
28
- json?: boolean;
29
- }
30
-
31
- export interface PainListEntry {
32
- /** Canonical pain identity; `row:<id>` when the legacy row has none. */
33
- painId: string;
34
- source: string;
35
- /** 'openclaw' | 'codex' | 'unknown' — unknown covers NULL/unprovable. */
36
- host: PainListHostFilter | 'unknown';
37
- score: number;
38
- severity: string | null;
39
- createdAt: string;
40
- runtimeTaskId: string | null;
41
- }
42
-
43
- export interface PainListResult {
44
- count: number;
45
- pains: PainListEntry[];
46
- workspace: string;
47
- hostFilter: PainListHostFilter | null;
48
- /**
49
- * PRI-743: full-table host distribution (independent of --limit and --host
50
- * filtering) so operator tooling can group by host without an external
51
- * script. `null` only on a pre-PRI-640 database without the host_kind
52
- * column — the distribution is unprovable there, never guessed (rc-3/rc-9;
53
- * see the accompanying host_kind_column_missing warning).
54
- */
55
- byHost: { openclaw: number; codex: number; unknown: number } | null;
56
- warnings: string[];
57
- }
58
-
59
- function isRecord(value: unknown): value is Record<string, unknown> {
60
- return typeof value === 'object' && value !== null && !Array.isArray(value);
61
- }
62
-
63
- function ownField(row: unknown, key: string): unknown {
64
- return isRecord(row) && Object.hasOwn(row, key) ? row[key] : undefined;
65
- }
66
-
67
- /** Runtime Contract #1/#4: validate each DB row; skip malformed rows loudly. */
68
- function toPainEntry(row: unknown): PainListEntry | null {
69
- const id = ownField(row, 'id');
70
- const source = ownField(row, 'source');
71
- const score = ownField(row, 'score');
72
- const createdAt = ownField(row, 'created_at');
73
- if (typeof id !== 'number' || typeof source !== 'string' || typeof score !== 'number' || typeof createdAt !== 'string') {
74
- return null;
75
- }
76
- const canonicalPainId = ownField(row, 'canonical_pain_id');
77
- const hostKind = ownField(row, 'host_kind');
78
- const severity = ownField(row, 'severity');
79
- const runtimeTaskId = ownField(row, 'runtime_task_id');
80
- const host: PainListEntry['host'] =
81
- hostKind === 'openclaw' || hostKind === 'codex' ? hostKind : 'unknown';
82
- return {
83
- painId: typeof canonicalPainId === 'string' && canonicalPainId.length > 0 ? canonicalPainId : `row:${id}`,
84
- source,
85
- host,
86
- score,
87
- severity: typeof severity === 'string' ? severity : null,
88
- createdAt,
89
- runtimeTaskId: typeof runtimeTaskId === 'string' && runtimeTaskId.length > 0 ? runtimeTaskId : null,
90
- };
91
- }
92
-
93
- export async function listPains(dbPath: string, options: { limit?: number; host?: PainListHostFilter } = {}): Promise<PainListResult> {
94
- const Database = (await import('better-sqlite3')).default;
95
- const db = new Database(dbPath, { readonly: true });
96
- try {
97
- const warnings: string[] = [];
98
- const columns: unknown[] = db.prepare('PRAGMA table_info(pain_events)').all();
99
- const hasHostKindColumn = columns.some((row) => ownField(row, 'name') === 'host_kind');
100
- if (!hasHostKindColumn) {
101
- warnings.push('host_kind_column_missing');
102
- }
103
-
104
- // PRI-743: full-table distribution, deliberately NOT narrowed by the
105
- // --host filter or --limit below — the operator question this answers is
106
- // "how do pains split across hosts in this workspace", which a filtered
107
- // count cannot answer. Non-openclaw/codex values (incl. NULL) count as
108
- // unknown, mirroring toPainEntry's read-side normalization.
109
- const byHost: PainListResult['byHost'] = hasHostKindColumn
110
- ? { openclaw: 0, codex: 0, unknown: 0 }
111
- : null;
112
- if (byHost) {
113
- const distRows: unknown[] = db
114
- .prepare("SELECT COALESCE(host_kind, 'unknown') AS host_bucket, COUNT(*) AS n FROM pain_events GROUP BY host_kind")
115
- .all();
116
- for (const row of distRows) {
117
- const bucket = ownField(row, 'host_bucket');
118
- const n = ownField(row, 'n');
119
- if ((bucket === 'openclaw' || bucket === 'codex' || bucket === 'unknown') && typeof n === 'number') {
120
- byHost[bucket] += n;
121
- }
122
- }
123
- }
124
-
125
- const params: (string | number)[] = [];
126
- let query = 'SELECT id, source, score, severity, created_at, canonical_pain_id, runtime_task_id';
127
- query += hasHostKindColumn ? ', host_kind' : ", NULL AS host_kind";
128
- query += ' FROM pain_events WHERE 1=1';
129
- if (options.host === 'unknown') {
130
- query += hasHostKindColumn ? ' AND host_kind IS NULL' : '';
131
- } else if (options.host === 'openclaw' || options.host === 'codex') {
132
- if (!hasHostKindColumn) {
133
- return { count: 0, pains: [], workspace: path.dirname(path.dirname(dbPath)), hostFilter: options.host, byHost, warnings };
134
- }
135
- query += ' AND host_kind = ?';
136
- params.push(options.host);
137
- }
138
- query += ' ORDER BY created_at DESC, id DESC';
139
- const limit = options.limit ?? 20;
140
- query += ' LIMIT ?';
141
- params.push(limit);
142
-
143
- const rawRows: unknown[] = db.prepare(query).all(...params);
144
- const skipped = rawRows.length - rawRows.map(toPainEntry).filter((entry) => entry !== null).length;
145
- if (skipped > 0) {
146
- warnings.push(`malformed_rows_skipped:${skipped}`);
147
- }
148
- const pains = rawRows
149
- .map(toPainEntry)
150
- .filter((entry): entry is PainListEntry => entry !== null);
151
- return {
152
- count: pains.length,
153
- pains,
154
- workspace: path.dirname(path.dirname(dbPath)),
155
- hostFilter: options.host ?? null,
156
- byHost,
157
- warnings,
158
- };
159
- } finally {
160
- db.close();
161
- }
162
- }
163
-
164
- function printHuman(result: PainListResult): void {
165
- const filterNote = result.hostFilter ? ` (host: ${result.hostFilter})` : '';
166
- console.log(`Pain events — ${result.count} shown${filterNote}`);
167
- if (result.byHost) {
168
- const { openclaw, codex, unknown } = result.byHost;
169
- console.log(`By host: openclaw=${openclaw} codex=${codex} unknown=${unknown}`);
170
- }
171
- console.log('─'.repeat(96));
172
- for (const pain of result.pains) {
173
- const painId = pain.painId.length > 40 ? `${pain.painId.slice(0, 37)}...` : pain.painId;
174
- console.log(` ${pain.createdAt} host=${pain.host.padEnd(8)} score=${String(pain.score).padEnd(3)} ${pain.severity ?? '-'.padEnd(6)} ${pain.source}`);
175
- console.log(` id: ${painId}${pain.runtimeTaskId ? ` | task: ${pain.runtimeTaskId}` : ''}`);
176
- }
177
- console.log('─'.repeat(96));
178
- for (const warning of result.warnings) {
179
- console.error(`WARN: ${warning}`);
180
- }
181
- if (result.warnings.includes('host_kind_column_missing')) {
182
- console.error('Next: run `pd runtime init --workspace <dir>` (or restart the OpenClaw plugin) to migrate this workspace schema.');
183
- }
184
- }
185
-
186
- export async function handlePainList(opts: PainListOptions): Promise<void> {
187
- const { workspace, limit: rawLimit, host: rawHost, json } = opts;
188
-
189
- if (rawHost !== undefined && rawHost !== 'openclaw' && rawHost !== 'codex' && rawHost !== 'unknown') {
190
- const message = `invalid host filter: expected openclaw | codex | unknown, got ${rawHost}`;
191
- if (json) {
192
- console.log(JSON.stringify({ status: 'failed', reason: 'invalid_host_filter', message, nextAction: 'Pass --host openclaw, --host codex, or --host unknown.' }));
193
- } else {
194
- console.error('Error: ' + message);
195
- console.error('Next: Pass --host openclaw, --host codex, or --host unknown.');
196
- }
197
- process.exit(1);
198
- return; // guard: test stubs of process.exit continue execution (cli-2-exit-stops)
199
- }
200
- const hostFilter: PainListHostFilter | undefined = rawHost;
201
-
202
- let effectiveLimit = 20;
203
- if (rawLimit !== undefined) {
204
- if (!Number.isInteger(rawLimit) || rawLimit < 1 || rawLimit > 10000) {
205
- const message = `invalid limit: limit must be an integer between 1 and 10000, got ${rawLimit}`;
206
- if (json) {
207
- console.log(JSON.stringify({ status: 'failed', reason: 'invalid_limit', message, nextAction: 'Pass --limit with a valid integer (1-10000).' }));
208
- } else {
209
- console.error('Error: ' + message);
210
- console.error('Next: Pass --limit with a valid integer (1-10000).');
211
- }
212
- process.exit(1);
213
- return; // guard: test stubs of process.exit continue execution (cli-2-exit-stops)
214
- }
215
- effectiveLimit = rawLimit;
216
- }
217
-
218
- const workspaceDir = resolveWorkspaceDir(workspace);
219
- const dbPath = path.join(workspaceDir, '.state', 'trajectory.db');
220
- if (!fs.existsSync(dbPath)) {
221
- const message = `trajectory database not found at ${dbPath}`;
222
- if (json) {
223
- console.log(JSON.stringify({ status: 'failed', reason: 'trajectory_db_not_found', message, nextAction: 'Initialize the PD workspace first (pd runtime init), or pass --workspace pointing at an initialized workspace.' }));
224
- } else {
225
- console.error('Error: ' + message);
226
- console.error('Next: Initialize the PD workspace first (pd runtime init), or pass --workspace pointing at an initialized workspace.');
227
- }
228
- process.exit(1);
229
- return; // guard: test stubs of process.exit continue execution (cli-2-exit-stops)
230
- }
231
-
232
- const result = await listPains(dbPath, { limit: effectiveLimit, ...(hostFilter ? { host: hostFilter } : {}) });
233
-
234
- if (json) {
235
- console.log(JSON.stringify(result, null, 2));
236
- return;
237
- }
238
- printHuman(result);
239
- }