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