@principles/pd-cli 1.152.11 → 1.152.13

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 (303) hide show
  1. package/dist/commands/candidate.d.ts +3 -3
  2. package/dist/commands/candidate.d.ts.map +1 -1
  3. package/dist/commands/candidate.js +9 -21
  4. package/dist/commands/candidate.js.map +1 -1
  5. package/dist/index.js +0 -0
  6. package/dist/services/rulehost-pipeline-runner.d.ts.map +1 -1
  7. package/dist/services/rulehost-pipeline-runner.js +4 -17
  8. package/dist/services/rulehost-pipeline-runner.js.map +1 -1
  9. package/package.json +13 -7
  10. package/dist/commands/__tests__/intent-flag-wiring.test.d.ts +0 -9
  11. package/dist/commands/__tests__/intent-flag-wiring.test.d.ts.map +0 -1
  12. package/dist/commands/__tests__/intent-flag-wiring.test.js +0 -237
  13. package/dist/commands/__tests__/intent-flag-wiring.test.js.map +0 -1
  14. package/dist/commands/__tests__/legacy-cleanup.test.d.ts +0 -18
  15. package/dist/commands/__tests__/legacy-cleanup.test.d.ts.map +0 -1
  16. package/dist/commands/__tests__/legacy-cleanup.test.js +0 -467
  17. package/dist/commands/__tests__/legacy-cleanup.test.js.map +0 -1
  18. package/dist/commands/__tests__/mvp-smoke.test.d.ts +0 -15
  19. package/dist/commands/__tests__/mvp-smoke.test.d.ts.map +0 -1
  20. package/dist/commands/__tests__/mvp-smoke.test.js +0 -262
  21. package/dist/commands/__tests__/mvp-smoke.test.js.map +0 -1
  22. package/dist/commands/__tests__/pain-retry-maxTokens-flag.test.d.ts +0 -2
  23. package/dist/commands/__tests__/pain-retry-maxTokens-flag.test.d.ts.map +0 -1
  24. package/dist/commands/__tests__/pain-retry-maxTokens-flag.test.js +0 -60
  25. package/dist/commands/__tests__/pain-retry-maxTokens-flag.test.js.map +0 -1
  26. package/dist/commands/__tests__/rulecode-flag-wiring.test.d.ts +0 -21
  27. package/dist/commands/__tests__/rulecode-flag-wiring.test.d.ts.map +0 -1
  28. package/dist/commands/__tests__/rulecode-flag-wiring.test.js +0 -186
  29. package/dist/commands/__tests__/rulecode-flag-wiring.test.js.map +0 -1
  30. package/dist/commands/__tests__/rulecode-handler.test.d.ts +0 -16
  31. package/dist/commands/__tests__/rulecode-handler.test.d.ts.map +0 -1
  32. package/dist/commands/__tests__/rulecode-handler.test.js +0 -285
  33. package/dist/commands/__tests__/rulecode-handler.test.js.map +0 -1
  34. package/dist/commands/__tests__/run-rulehost-flag-wiring.test.d.ts +0 -24
  35. package/dist/commands/__tests__/run-rulehost-flag-wiring.test.d.ts.map +0 -1
  36. package/dist/commands/__tests__/run-rulehost-flag-wiring.test.js +0 -232
  37. package/dist/commands/__tests__/run-rulehost-flag-wiring.test.js.map +0 -1
  38. package/dist/commands/__tests__/runtime-activation-nextaction.test.d.ts +0 -15
  39. package/dist/commands/__tests__/runtime-activation-nextaction.test.d.ts.map +0 -1
  40. package/dist/commands/__tests__/runtime-activation-nextaction.test.js +0 -73
  41. package/dist/commands/__tests__/runtime-activation-nextaction.test.js.map +0 -1
  42. package/dist/commands/__tests__/runtime-probe-config.test.d.ts +0 -20
  43. package/dist/commands/__tests__/runtime-probe-config.test.d.ts.map +0 -1
  44. package/dist/commands/__tests__/runtime-probe-config.test.js +0 -389
  45. package/dist/commands/__tests__/runtime-probe-config.test.js.map +0 -1
  46. package/dist/commands/__tests__/telemetry-flag-wiring.test.d.ts +0 -9
  47. package/dist/commands/__tests__/telemetry-flag-wiring.test.d.ts.map +0 -1
  48. package/dist/commands/__tests__/telemetry-flag-wiring.test.js +0 -76
  49. package/dist/commands/__tests__/telemetry-flag-wiring.test.js.map +0 -1
  50. package/dist/commands/__tests__/version.test.d.ts +0 -2
  51. package/dist/commands/__tests__/version.test.d.ts.map +0 -1
  52. package/dist/commands/__tests__/version.test.js +0 -238
  53. package/dist/commands/__tests__/version.test.js.map +0 -1
  54. package/dist/commands/runtime-uat.guard.test.d.ts +0 -2
  55. package/dist/commands/runtime-uat.guard.test.d.ts.map +0 -1
  56. package/dist/commands/runtime-uat.guard.test.js +0 -153
  57. package/dist/commands/runtime-uat.guard.test.js.map +0 -1
  58. package/dist/services/__tests__/cli-output.test.d.ts +0 -18
  59. package/dist/services/__tests__/cli-output.test.d.ts.map +0 -1
  60. package/dist/services/__tests__/cli-output.test.js +0 -103
  61. package/dist/services/__tests__/cli-output.test.js.map +0 -1
  62. package/dist/services/__tests__/evaluator-runner-deps.test.d.ts +0 -2
  63. package/dist/services/__tests__/evaluator-runner-deps.test.d.ts.map +0 -1
  64. package/dist/services/__tests__/evaluator-runner-deps.test.js +0 -343
  65. package/dist/services/__tests__/evaluator-runner-deps.test.js.map +0 -1
  66. package/dist/services/__tests__/rulehost-readiness.test.d.ts +0 -2
  67. package/dist/services/__tests__/rulehost-readiness.test.d.ts.map +0 -1
  68. package/dist/services/__tests__/rulehost-readiness.test.js +0 -311
  69. package/dist/services/__tests__/rulehost-readiness.test.js.map +0 -1
  70. package/dist/services/__tests__/runtime-adapter-resolver.test.d.ts +0 -18
  71. package/dist/services/__tests__/runtime-adapter-resolver.test.d.ts.map +0 -1
  72. package/dist/services/__tests__/runtime-adapter-resolver.test.js +0 -673
  73. package/dist/services/__tests__/runtime-adapter-resolver.test.js.map +0 -1
  74. package/dist/utils/production-workspace-guard.test.d.ts +0 -2
  75. package/dist/utils/production-workspace-guard.test.d.ts.map +0 -1
  76. package/dist/utils/production-workspace-guard.test.js +0 -137
  77. package/dist/utils/production-workspace-guard.test.js.map +0 -1
  78. package/scripts/llm-dogfood.ts +0 -456
  79. package/scripts/migrate-illegal-expected-decision.ts +0 -252
  80. package/src/commands/__tests__/intent-flag-wiring.test.ts +0 -282
  81. package/src/commands/__tests__/legacy-cleanup.test.ts +0 -602
  82. package/src/commands/__tests__/mvp-smoke.test.ts +0 -300
  83. package/src/commands/__tests__/pain-retry-maxTokens-flag.test.ts +0 -73
  84. package/src/commands/__tests__/rulecode-flag-wiring.test.ts +0 -237
  85. package/src/commands/__tests__/rulecode-handler.test.ts +0 -369
  86. package/src/commands/__tests__/run-rulehost-flag-wiring.test.ts +0 -292
  87. package/src/commands/__tests__/runtime-activation-nextaction.test.ts +0 -81
  88. package/src/commands/__tests__/runtime-probe-config.test.ts +0 -432
  89. package/src/commands/__tests__/telemetry-flag-wiring.test.ts +0 -84
  90. package/src/commands/__tests__/version.test.ts +0 -251
  91. package/src/commands/admission-gate.ts +0 -30
  92. package/src/commands/artifact.ts +0 -82
  93. package/src/commands/build-trajectory-evidence.ts +0 -298
  94. package/src/commands/candidate.ts +0 -1260
  95. package/src/commands/codex-ingest-catchup.ts +0 -114
  96. package/src/commands/codex-ingest-quarantine.ts +0 -127
  97. package/src/commands/codex-reconcile.ts +0 -84
  98. package/src/commands/codex-setup.ts +0 -476
  99. package/src/commands/codex-worker.ts +0 -185
  100. package/src/commands/command-helpers.ts +0 -24
  101. package/src/commands/config-doctor.ts +0 -193
  102. package/src/commands/console.ts +0 -797
  103. package/src/commands/context.ts +0 -72
  104. package/src/commands/demo-story-a.ts +0 -241
  105. package/src/commands/diagnose.ts +0 -756
  106. package/src/commands/errors-list.ts +0 -441
  107. package/src/commands/evolution-tasks-list.ts +0 -48
  108. package/src/commands/evolution-tasks-show.ts +0 -62
  109. package/src/commands/flow.ts +0 -60
  110. package/src/commands/health-codex.ts +0 -576
  111. package/src/commands/health.ts +0 -315
  112. package/src/commands/history.ts +0 -63
  113. package/src/commands/intent.ts +0 -457
  114. package/src/commands/legacy-cleanup.ts +0 -531
  115. package/src/commands/legacy-import.ts +0 -104
  116. package/src/commands/mvp-smoke.ts +0 -160
  117. package/src/commands/pain-evidence.ts +0 -226
  118. package/src/commands/pain-list.ts +0 -239
  119. package/src/commands/pain-record.ts +0 -612
  120. package/src/commands/pain-retry.ts +0 -916
  121. package/src/commands/principles-stats.ts +0 -700
  122. package/src/commands/proven-channel-baseline.ts +0 -115
  123. package/src/commands/quality-scorecard.ts +0 -272
  124. package/src/commands/remediation-output.ts +0 -66
  125. package/src/commands/rulecode.ts +0 -465
  126. package/src/commands/run.ts +0 -89
  127. package/src/commands/runtime-activation.ts +0 -1629
  128. package/src/commands/runtime-artifact-repair.ts +0 -524
  129. package/src/commands/runtime-canary.ts +0 -395
  130. package/src/commands/runtime-compatibility-scan.ts +0 -105
  131. package/src/commands/runtime-diagnostics-export.ts +0 -229
  132. package/src/commands/runtime-features.ts +0 -153
  133. package/src/commands/runtime-gfi-snapshot.ts +0 -135
  134. package/src/commands/runtime-health-snapshot.ts +0 -108
  135. package/src/commands/runtime-init.ts +0 -429
  136. package/src/commands/runtime-internalization-enqueue-successors.ts +0 -530
  137. package/src/commands/runtime-internalization-integrity-repair.ts +0 -79
  138. package/src/commands/runtime-internalization-integrity.ts +0 -107
  139. package/src/commands/runtime-internalization-queue.ts +0 -135
  140. package/src/commands/runtime-internalization-retry.ts +0 -191
  141. package/src/commands/runtime-internalization-run-once.ts +0 -791
  142. package/src/commands/runtime-internalization-run-rulehost.ts +0 -749
  143. package/src/commands/runtime-internalization-wake-once.ts +0 -87
  144. package/src/commands/runtime-pain-flood-simulation.ts +0 -121
  145. package/src/commands/runtime-pruning.ts +0 -438
  146. package/src/commands/runtime-recovery-failed-tasks.ts +0 -258
  147. package/src/commands/runtime-recovery.ts +0 -109
  148. package/src/commands/runtime-synthetic-baseline.ts +0 -70
  149. package/src/commands/runtime-uat.guard.test.ts +0 -175
  150. package/src/commands/runtime-uat.ts +0 -380
  151. package/src/commands/runtime.ts +0 -473
  152. package/src/commands/samples-list.ts +0 -39
  153. package/src/commands/samples-review.ts +0 -36
  154. package/src/commands/task.ts +0 -271
  155. package/src/commands/telemetry.ts +0 -349
  156. package/src/commands/trace.ts +0 -174
  157. package/src/commands/trajectory-db-unavailable.ts +0 -19
  158. package/src/commands/trajectory.ts +0 -64
  159. package/src/commands/version.ts +0 -49
  160. package/src/config-reader.ts +0 -122
  161. package/src/index.ts +0 -1276
  162. package/src/legacy/legacy-import.ts +0 -179
  163. package/src/legacy/session-history-import.ts +0 -203
  164. package/src/resolve-workspace.ts +0 -106
  165. package/src/services/__tests__/cli-output.test.ts +0 -130
  166. package/src/services/__tests__/evaluator-runner-deps.test.ts +0 -382
  167. package/src/services/__tests__/rulehost-readiness.test.ts +0 -363
  168. package/src/services/__tests__/runtime-adapter-resolver.test.ts +0 -793
  169. package/src/services/cli-output.ts +0 -95
  170. package/src/services/config-doctor.ts +0 -697
  171. package/src/services/console-launcher.ts +0 -537
  172. package/src/services/demo-rule-compiler.ts +0 -40
  173. package/src/services/demo-story-a-runner.ts +0 -471
  174. package/src/services/mainline-snapshot-assembler.ts +0 -579
  175. package/src/services/pain-flood-simulation-runner.ts +0 -462
  176. package/src/services/pd-config-loader.ts +0 -433
  177. package/src/services/proven-channel-baseline-runner.ts +0 -157
  178. package/src/services/quality-scorecard/data-extractor.ts +0 -150
  179. package/src/services/quality-scorecard/local-evaluator.ts +0 -142
  180. package/src/services/quality-scorecard/strong-model-gate.ts +0 -202
  181. package/src/services/resolve-runtime-from-pd-config.ts +0 -230
  182. package/src/services/rulehost-pipeline-runner.ts +0 -1216
  183. package/src/services/rulehost-readiness.ts +0 -326
  184. package/src/services/runtime-adapter-resolver.ts +0 -364
  185. package/src/services/synthetic-baseline-runner.ts +0 -400
  186. package/src/services/version-report.ts +0 -253
  187. package/src/services/workspace-tool-semantics.ts +0 -26
  188. package/src/utils/path-security.ts +0 -96
  189. package/src/utils/production-workspace-guard.test.ts +0 -157
  190. package/src/utils/production-workspace-guard.ts +0 -210
  191. package/tests/bdd/cli-contract.steps.ts +0 -346
  192. package/tests/bdd/principles-stats.steps.ts +0 -211
  193. package/tests/bdd/support/gherkin-loader.ts +0 -147
  194. package/tests/bdd/support/repo-root.ts +0 -62
  195. package/tests/bdd/support/vitest-bdd.ts +0 -179
  196. package/tests/commands/admission-gate-coverage.test.ts +0 -79
  197. package/tests/commands/build-trajectory-evidence.test.ts +0 -508
  198. package/tests/commands/candidate-audit-repair.test.ts +0 -421
  199. package/tests/commands/candidate-intake.test.ts +0 -732
  200. package/tests/commands/candidate-internalization-backfill.test.ts +0 -548
  201. package/tests/commands/candidate-internalize-lineage.test.ts +0 -565
  202. package/tests/commands/candidate-internalize.test.ts +0 -299
  203. package/tests/commands/candidate-route.test.ts +0 -329
  204. package/tests/commands/candidate-show.test.ts +0 -95
  205. package/tests/commands/cli-command-tree.test.ts +0 -144
  206. package/tests/commands/cli-help-snapshot.test.ts +0 -155
  207. package/tests/commands/cli-skill-contract.test.ts +0 -121
  208. package/tests/commands/codex-consent-reversibility.steps.test.ts +0 -299
  209. package/tests/commands/codex-ingest-quarantine.test.ts +0 -136
  210. package/tests/commands/codex-setup.test.ts +0 -297
  211. package/tests/commands/codex-worker-daemon.test.ts +0 -95
  212. package/tests/commands/codex-worker-registration.test.ts +0 -207
  213. package/tests/commands/config-doctor.test.ts +0 -336
  214. package/tests/commands/console-launcher-edge-cases.test.ts +0 -388
  215. package/tests/commands/console-open.test.ts +0 -1381
  216. package/tests/commands/context.test.ts +0 -114
  217. package/tests/commands/demo-story-a.test.ts +0 -318
  218. package/tests/commands/diagnose.test.ts +0 -1803
  219. package/tests/commands/errors-list.test.ts +0 -679
  220. package/tests/commands/evolution-tasks-list.test.ts +0 -41
  221. package/tests/commands/evolution-tasks-show.test.ts +0 -46
  222. package/tests/commands/health-codex.test.ts +0 -349
  223. package/tests/commands/health-host-wiring.test.ts +0 -76
  224. package/tests/commands/health-reliability.test.ts +0 -99
  225. package/tests/commands/health.test.ts +0 -413
  226. package/tests/commands/intent.test.ts +0 -321
  227. package/tests/commands/legacy-cleanup.test.ts +0 -148
  228. package/tests/commands/pain-evidence.test.ts +0 -518
  229. package/tests/commands/pain-list.test.ts +0 -219
  230. package/tests/commands/pain-record-async.test.ts +0 -335
  231. package/tests/commands/pain-record-session-parser.test.ts +0 -228
  232. package/tests/commands/pain-record.test.ts +0 -851
  233. package/tests/commands/pain-retry.test.ts +0 -1121
  234. package/tests/commands/plugin-config-resolution-cutover.test.ts +0 -229
  235. package/tests/commands/pri-393-runtime-config-unification.test.ts +0 -443
  236. package/tests/commands/principles-stats-wiring.test.ts +0 -135
  237. package/tests/commands/principles-stats.test.ts +0 -274
  238. package/tests/commands/product-path-regression.test.ts +0 -86
  239. package/tests/commands/proven-channel-baseline.test.ts +0 -441
  240. package/tests/commands/rulecode.test.ts +0 -177
  241. package/tests/commands/run-rulehost-handler.test.ts +0 -908
  242. package/tests/commands/runtime-activation-approve-flag-wiring.test.ts +0 -227
  243. package/tests/commands/runtime-activation-deactivate-flag-wiring.test.ts +0 -167
  244. package/tests/commands/runtime-activation-dispatch-flag-wiring.test.ts +0 -262
  245. package/tests/commands/runtime-activation-governance-audit.test.ts +0 -95
  246. package/tests/commands/runtime-activation-list-flag-wiring.test.ts +0 -193
  247. package/tests/commands/runtime-activation-promote-flag-wiring.test.ts +0 -248
  248. package/tests/commands/runtime-activation-shadow-telemetry.test.ts +0 -130
  249. package/tests/commands/runtime-activation.test.ts +0 -2052
  250. package/tests/commands/runtime-artifact-repair-registration.test.ts +0 -74
  251. package/tests/commands/runtime-artifact-repair.test.ts +0 -404
  252. package/tests/commands/runtime-canary.test.ts +0 -430
  253. package/tests/commands/runtime-compatibility-scan.test.ts +0 -146
  254. package/tests/commands/runtime-diagnostics-export.test.ts +0 -175
  255. package/tests/commands/runtime-features.test.ts +0 -249
  256. package/tests/commands/runtime-health-snapshot.test.ts +0 -358
  257. package/tests/commands/runtime-init-empty-workspace.test.ts +0 -252
  258. package/tests/commands/runtime-init.test.ts +0 -420
  259. package/tests/commands/runtime-internalization-enqueue-successors.test.ts +0 -782
  260. package/tests/commands/runtime-internalization-integrity-repair.test.ts +0 -239
  261. package/tests/commands/runtime-internalization-integrity.test.ts +0 -140
  262. package/tests/commands/runtime-internalization-queue.test.ts +0 -380
  263. package/tests/commands/runtime-internalization-retry-owner-authority.test.ts +0 -431
  264. package/tests/commands/runtime-internalization-run-once-evaluator-parity.test.ts +0 -269
  265. package/tests/commands/runtime-internalization-run-once-language.test.ts +0 -303
  266. package/tests/commands/runtime-internalization-run-once-real-config.test.ts +0 -237
  267. package/tests/commands/runtime-internalization-run-once-rollout-parity.test.ts +0 -706
  268. package/tests/commands/runtime-internalization-run-once.test.ts +0 -1791
  269. package/tests/commands/runtime-internalization-wake-once.test.ts +0 -171
  270. package/tests/commands/runtime-internalization.test.ts +0 -53
  271. package/tests/commands/runtime-pain-flood-simulation.test.ts +0 -418
  272. package/tests/commands/runtime-pruning.test.ts +0 -694
  273. package/tests/commands/runtime-recovery-failed-tasks.test.ts +0 -328
  274. package/tests/commands/runtime-recovery.test.ts +0 -120
  275. package/tests/commands/runtime-synthetic-baseline.test.ts +0 -249
  276. package/tests/commands/runtime-uat.test.ts +0 -397
  277. package/tests/commands/runtime.test.ts +0 -392
  278. package/tests/commands/samples-list.test.ts +0 -43
  279. package/tests/commands/samples-review.test.ts +0 -41
  280. package/tests/commands/task.test.ts +0 -153
  281. package/tests/commands/telemetry.test.ts +0 -190
  282. package/tests/commands/trace.test.ts +0 -316
  283. package/tests/config-reader.test.ts +0 -143
  284. package/tests/e2e/candidate-intake-e2e.test.ts +0 -338
  285. package/tests/e2e/cli-full-flow.test.ts +0 -233
  286. package/tests/e2e/cross-package-acceptance.test.ts +0 -635
  287. package/tests/helpers/pd-cli-path.ts +0 -5
  288. package/tests/pd-cli-smoke.test.ts +0 -13
  289. package/tests/resolve-workspace.test.ts +0 -196
  290. package/tests/services/config-doctor-signal-health.test.ts +0 -109
  291. package/tests/services/console-launcher.test.ts +0 -114
  292. package/tests/services/demo-rule-compiler.test.ts +0 -242
  293. package/tests/services/mainline-snapshot-assembler.test.ts +0 -572
  294. package/tests/services/pd-config-loader.test.ts +0 -505
  295. package/tests/services/proven-channel-baseline-runner.test.ts +0 -30
  296. package/tests/services/quality-scorecard/strong-model-gate.test.ts +0 -133
  297. package/tests/services/resolve-runtime-from-pd-config.test.ts +0 -485
  298. package/tests/services/rulehost-pipeline-e2e.test.ts +0 -538
  299. package/tests/services/rulehost-pipeline-runner.test.ts +0 -1175
  300. package/tests/services/workspace-tool-semantics.test.ts +0 -76
  301. package/tests/utils/path-security.test.ts +0 -180
  302. package/tsconfig.json +0 -26
  303. package/vitest.config.ts +0 -9
@@ -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
- }