onto-mcp 0.3.2 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (300) hide show
  1. package/.onto/processes/reconstruct/actionable-ontology-seed-recomposition-design.md +447 -0
  2. package/.onto/processes/reconstruct/foundry-style-ontology-seed-contract.md +934 -0
  3. package/.onto/processes/reconstruct/reconstruct-boundary-contract.md +303 -725
  4. package/.onto/processes/reconstruct/reconstruct-contract-registry.yaml +1645 -0
  5. package/.onto/processes/reconstruct/reconstruct-execution-ux-contract.md +26 -22
  6. package/.onto/processes/reconstruct/source-profile-contract.md +49 -23
  7. package/.onto/processes/reconstruct/source-profiles/code.md +6 -3
  8. package/.onto/processes/reconstruct/source-profiles/database.md +5 -2
  9. package/.onto/processes/reconstruct/source-profiles/document.md +5 -2
  10. package/.onto/processes/reconstruct/source-profiles/spreadsheet.md +5 -4
  11. package/.onto/processes/review/review-execution-ux-contract.md +40 -0
  12. package/.onto/processes/shared/pipeline-execution-ledger-contract.md +26 -10
  13. package/.onto/processes/shared/target-material-kind-contract.md +29 -16
  14. package/AGENTS.md +6 -4
  15. package/README.md +135 -76
  16. package/dist/cli.js +8 -8
  17. package/dist/core-api/reconstruct-api.js +117 -31
  18. package/dist/core-api/review-api.js +47 -0
  19. package/dist/core-runtime/cli/codex-review-unit-executor.js +39 -2
  20. package/dist/core-runtime/cli/complete-review-session.js +2 -2
  21. package/dist/core-runtime/cli/mock-review-unit-executor.js +1 -1
  22. package/dist/core-runtime/cli/review-invoke.js +9 -9
  23. package/dist/core-runtime/cli/run-review-prompt-execution.js +39 -5
  24. package/dist/core-runtime/cli/spawn-watcher.js +266 -47
  25. package/dist/core-runtime/cli/start-review-session.js +3 -3
  26. package/dist/core-runtime/llm/llm-caller.js +11 -0
  27. package/dist/core-runtime/llm/llm-tool-loop.js +2 -0
  28. package/dist/core-runtime/observability/runtime-stream-observation.js +118 -0
  29. package/dist/core-runtime/onboard/cli-host.js +149 -0
  30. package/dist/core-runtime/onboard/host-target.js +22 -0
  31. package/dist/core-runtime/onboard/json-config-host.js +122 -0
  32. package/dist/core-runtime/onboard/path-scan.js +26 -0
  33. package/dist/core-runtime/onboard/prompt.js +51 -0
  34. package/dist/core-runtime/onboard/register.js +207 -0
  35. package/dist/core-runtime/onboard/types.js +27 -0
  36. package/dist/core-runtime/reconstruct/actionable-seed-validation.js +1777 -0
  37. package/dist/core-runtime/reconstruct/artifact-types.js +10 -4
  38. package/dist/core-runtime/reconstruct/contract-registry.js +623 -0
  39. package/dist/core-runtime/reconstruct/domain-id.js +10 -0
  40. package/dist/core-runtime/reconstruct/governing-snapshot.js +716 -0
  41. package/dist/core-runtime/reconstruct/material-profile-validation.js +191 -0
  42. package/dist/core-runtime/reconstruct/materialize-preparation.js +49 -11
  43. package/dist/core-runtime/reconstruct/pipeline-execution-ledger.js +269 -79
  44. package/dist/core-runtime/reconstruct/post-seed-validation.js +1194 -51
  45. package/dist/core-runtime/reconstruct/record.js +104 -20
  46. package/dist/core-runtime/reconstruct/run.js +2107 -413
  47. package/dist/core-runtime/reconstruct/seed-claim-projections.js +268 -0
  48. package/dist/core-runtime/reconstruct/source-profiles.js +93 -4
  49. package/dist/core-runtime/reconstruct/terminal-validation.js +807 -0
  50. package/dist/core-runtime/review/review-invocation-runner.js +4 -4
  51. package/dist/mcp/server.js +110 -38
  52. package/dist/mcp/tool-schemas.js +20 -6
  53. package/package.json +8 -17
  54. package/scripts/onto-review-watch.sh +486 -0
  55. package/scripts/onto-runtime-watch.sh +122 -0
  56. package/scripts/postinstall-hint.js +22 -0
  57. package/.onto/processes/reconstruct/top-level-concept-discovery-contract.md +0 -387
  58. package/dist/core-runtime/cli/bootstrap-review-binding.js +0 -186
  59. package/dist/core-runtime/cli/codex-nested-dispatch.test.js +0 -390
  60. package/dist/core-runtime/cli/codex-nested-teamlead-executor.test.js +0 -335
  61. package/dist/core-runtime/cli/coordinator-helpers.js +0 -583
  62. package/dist/core-runtime/cli/coordinator-state-machine-deliberation.test.js +0 -167
  63. package/dist/core-runtime/cli/coordinator-state-machine.js +0 -794
  64. package/dist/core-runtime/cli/e2e-codex-multi-agent-fixes.test.js +0 -615
  65. package/dist/core-runtime/cli/e2e-start-review-session.test.js +0 -312
  66. package/dist/core-runtime/cli/health.js +0 -44
  67. package/dist/core-runtime/cli/inline-http-review-unit-executor.test.js +0 -567
  68. package/dist/core-runtime/cli/materialize-review-execution-preparation.js +0 -104
  69. package/dist/core-runtime/cli/migrate-session-roots.js +0 -118
  70. package/dist/core-runtime/cli/repo-layout-migration-replace.smoke.test.js +0 -106
  71. package/dist/core-runtime/cli/review-invoke-auto-resolution.test.js +0 -268
  72. package/dist/core-runtime/cli/review-invoke-coordinator-topology.test.js +0 -136
  73. package/dist/core-runtime/cli/review-invoke-resolver-caching.test.js +0 -201
  74. package/dist/core-runtime/cli/review-invoke-topology-dispatch.test.js +0 -192
  75. package/dist/core-runtime/cli/session-root-guard.js +0 -168
  76. package/dist/core-runtime/cli/spawn-watcher.test.js +0 -457
  77. package/dist/core-runtime/cli/strip-wrapping-code-fence.test.js +0 -79
  78. package/dist/core-runtime/cli/teamcreate-lens-deliberation-executor.js +0 -412
  79. package/dist/core-runtime/cli/teamcreate-lens-deliberation-executor.test.js +0 -351
  80. package/dist/core-runtime/cli/topology-executor-mapping.js +0 -139
  81. package/dist/core-runtime/cli/topology-executor-mapping.test.js +0 -173
  82. package/dist/core-runtime/cli/write-review-interpretation.js +0 -81
  83. package/dist/core-runtime/config/onto-config-cli.js +0 -278
  84. package/dist/core-runtime/config/onto-config-key-path.js +0 -288
  85. package/dist/core-runtime/config/onto-config-key-path.test.js +0 -195
  86. package/dist/core-runtime/config/onto-config-preview.js +0 -108
  87. package/dist/core-runtime/config/onto-config-preview.test.js +0 -132
  88. package/dist/core-runtime/discovery/config-chain.js +0 -118
  89. package/dist/core-runtime/discovery/config-chain.test.js +0 -103
  90. package/dist/core-runtime/discovery/config-profile.js +0 -199
  91. package/dist/core-runtime/discovery/config-profile.test.js +0 -233
  92. package/dist/core-runtime/discovery/host-detection.test.js +0 -186
  93. package/dist/core-runtime/discovery/installation-paths.test.js +0 -65
  94. package/dist/core-runtime/discovery/lens-registry.test.js +0 -81
  95. package/dist/core-runtime/discovery/path-normalization.test.js +0 -22
  96. package/dist/core-runtime/discovery/plugin-path.js +0 -72
  97. package/dist/core-runtime/discovery/plugin-path.test.js +0 -95
  98. package/dist/core-runtime/evolve/adapters/code-product/compile/compile-defense.js +0 -344
  99. package/dist/core-runtime/evolve/adapters/code-product/compile/compile-defense.test.js +0 -915
  100. package/dist/core-runtime/evolve/adapters/code-product/compile/compile.js +0 -564
  101. package/dist/core-runtime/evolve/adapters/code-product/compile/compile.test.js +0 -708
  102. package/dist/core-runtime/evolve/adapters/code-product/parsers/brief-parser.js +0 -165
  103. package/dist/core-runtime/evolve/adapters/code-product/parsers/brief-parser.test.js +0 -227
  104. package/dist/core-runtime/evolve/adapters/code-product/validators/validate.js +0 -59
  105. package/dist/core-runtime/evolve/adapters/code-product/validators/validate.test.js +0 -205
  106. package/dist/core-runtime/evolve/adapters/methodology/adapter.js +0 -16
  107. package/dist/core-runtime/evolve/adapters/methodology/adapter.test.js +0 -9
  108. package/dist/core-runtime/evolve/adapters/methodology/perspectives/authority-consistency.js +0 -298
  109. package/dist/core-runtime/evolve/adapters/methodology/perspectives/authority-consistency.test.js +0 -70
  110. package/dist/core-runtime/evolve/adapters/methodology/scope-types/process.js +0 -46
  111. package/dist/core-runtime/evolve/adapters/methodology/scope-types/process.test.js +0 -73
  112. package/dist/core-runtime/evolve/adapters/registry.js +0 -47
  113. package/dist/core-runtime/evolve/adapters/registry.test.js +0 -67
  114. package/dist/core-runtime/evolve/cli.js +0 -256
  115. package/dist/core-runtime/evolve/commands/align.js +0 -194
  116. package/dist/core-runtime/evolve/commands/align.test.js +0 -82
  117. package/dist/core-runtime/evolve/commands/apply.js +0 -161
  118. package/dist/core-runtime/evolve/commands/apply.test.js +0 -138
  119. package/dist/core-runtime/evolve/commands/close.js +0 -39
  120. package/dist/core-runtime/evolve/commands/close.test.js +0 -99
  121. package/dist/core-runtime/evolve/commands/defer.js +0 -40
  122. package/dist/core-runtime/evolve/commands/defer.test.js +0 -134
  123. package/dist/core-runtime/evolve/commands/draft.js +0 -323
  124. package/dist/core-runtime/evolve/commands/draft.test.js +0 -178
  125. package/dist/core-runtime/evolve/commands/e2e-evolve-full-cycle.test.js +0 -208
  126. package/dist/core-runtime/evolve/commands/error-messages.js +0 -125
  127. package/dist/core-runtime/evolve/commands/error-messages.test.js +0 -167
  128. package/dist/core-runtime/evolve/commands/propose-align.js +0 -222
  129. package/dist/core-runtime/evolve/commands/propose-align.test.js +0 -136
  130. package/dist/core-runtime/evolve/commands/reconstruct.js +0 -330
  131. package/dist/core-runtime/evolve/commands/reconstruct.test.js +0 -278
  132. package/dist/core-runtime/evolve/commands/shared.js +0 -22
  133. package/dist/core-runtime/evolve/commands/stale-check.js +0 -103
  134. package/dist/core-runtime/evolve/commands/stale-check.test.js +0 -84
  135. package/dist/core-runtime/evolve/commands/start.js +0 -887
  136. package/dist/core-runtime/evolve/commands/start.test.js +0 -396
  137. package/dist/core-runtime/evolve/config/project-config.js +0 -99
  138. package/dist/core-runtime/evolve/config/project-config.test.js +0 -170
  139. package/dist/core-runtime/evolve/renderers/align-packet.js +0 -280
  140. package/dist/core-runtime/evolve/renderers/align-packet.test.js +0 -332
  141. package/dist/core-runtime/evolve/renderers/draft-packet.js +0 -303
  142. package/dist/core-runtime/evolve/renderers/draft-packet.test.js +0 -377
  143. package/dist/core-runtime/evolve/renderers/format.js +0 -5
  144. package/dist/core-runtime/evolve/renderers/scope-md.js +0 -237
  145. package/dist/core-runtime/evolve/renderers/scope-md.test.js +0 -306
  146. package/dist/core-runtime/govern/cli.js +0 -369
  147. package/dist/core-runtime/govern/cli.test.js +0 -314
  148. package/dist/core-runtime/govern/drift-engine.js +0 -103
  149. package/dist/core-runtime/govern/drift-engine.test.js +0 -319
  150. package/dist/core-runtime/govern/promote-principle.js +0 -206
  151. package/dist/core-runtime/govern/promote-principle.test.js +0 -368
  152. package/dist/core-runtime/govern/queue.js +0 -81
  153. package/dist/core-runtime/govern/types.js +0 -16
  154. package/dist/core-runtime/install/cli.js +0 -530
  155. package/dist/core-runtime/install/detect.js +0 -128
  156. package/dist/core-runtime/install/detect.test.js +0 -155
  157. package/dist/core-runtime/install/gitignore-update.js +0 -74
  158. package/dist/core-runtime/install/gitignore-update.test.js +0 -64
  159. package/dist/core-runtime/install/install-integration.test.js +0 -373
  160. package/dist/core-runtime/install/prompts.js +0 -389
  161. package/dist/core-runtime/install/prompts.test.js +0 -293
  162. package/dist/core-runtime/install/types.js +0 -26
  163. package/dist/core-runtime/install/validation.js +0 -295
  164. package/dist/core-runtime/install/validation.test.js +0 -313
  165. package/dist/core-runtime/install/writer.js +0 -254
  166. package/dist/core-runtime/install/writer.test.js +0 -218
  167. package/dist/core-runtime/learning/extractor.js +0 -461
  168. package/dist/core-runtime/learning/feedback.js +0 -179
  169. package/dist/core-runtime/learning/health-report.js +0 -165
  170. package/dist/core-runtime/learning/health-report.test.js +0 -169
  171. package/dist/core-runtime/learning/loader.js +0 -388
  172. package/dist/core-runtime/learning/loader.test.js +0 -102
  173. package/dist/core-runtime/learning/promote/apply-state.js +0 -240
  174. package/dist/core-runtime/learning/promote/audit-obligation.js +0 -195
  175. package/dist/core-runtime/learning/promote/collector.js +0 -432
  176. package/dist/core-runtime/learning/promote/degraded-state.js +0 -125
  177. package/dist/core-runtime/learning/promote/domain-doc-proposer.js +0 -166
  178. package/dist/core-runtime/learning/promote/e2e-promote.test.js +0 -6385
  179. package/dist/core-runtime/learning/promote/health-snapshot.js +0 -150
  180. package/dist/core-runtime/learning/promote/insight-reclassifier.js +0 -544
  181. package/dist/core-runtime/learning/promote/judgment-auditor.js +0 -517
  182. package/dist/core-runtime/learning/promote/panel-reviewer.js +0 -1158
  183. package/dist/core-runtime/learning/promote/promote-executor.js +0 -1675
  184. package/dist/core-runtime/learning/promote/promoter.js +0 -307
  185. package/dist/core-runtime/learning/promote/retirement.js +0 -122
  186. package/dist/core-runtime/learning/promote/types.js +0 -23
  187. package/dist/core-runtime/learning/prompt-sections.js +0 -51
  188. package/dist/core-runtime/learning/shared/artifact-registry-init.js +0 -45
  189. package/dist/core-runtime/learning/shared/artifact-registry.js +0 -254
  190. package/dist/core-runtime/learning/shared/audit-obligation-kernel.js +0 -73
  191. package/dist/core-runtime/learning/shared/audit-state.js +0 -99
  192. package/dist/core-runtime/learning/shared/duplicate-check.js +0 -28
  193. package/dist/core-runtime/learning/shared/llm-caller.js +0 -831
  194. package/dist/core-runtime/learning/shared/llm-caller.test.js +0 -601
  195. package/dist/core-runtime/learning/shared/llm-tool-loop.js +0 -393
  196. package/dist/core-runtime/learning/shared/mode.js +0 -25
  197. package/dist/core-runtime/learning/shared/paths.js +0 -84
  198. package/dist/core-runtime/learning/shared/paths.test.js +0 -79
  199. package/dist/core-runtime/learning/shared/patterns.js +0 -37
  200. package/dist/core-runtime/learning/shared/recoverability.js +0 -355
  201. package/dist/core-runtime/learning/shared/recovery-context.js +0 -374
  202. package/dist/core-runtime/learning/shared/scope.js +0 -1
  203. package/dist/core-runtime/learning/shared/semantic-classifier.js +0 -94
  204. package/dist/core-runtime/learning/shared/specs/apply-execution-state-spec.js +0 -42
  205. package/dist/core-runtime/learning/shared/specs/audit-state-spec.js +0 -37
  206. package/dist/core-runtime/learning/shared/specs/backup-metadata-spec.js +0 -39
  207. package/dist/core-runtime/learning/shared/specs/emergency-log-spec.js +0 -41
  208. package/dist/core-runtime/learning/shared/specs/layout-version-spec.js +0 -38
  209. package/dist/core-runtime/learning/shared/specs/promote-decisions-spec.js +0 -43
  210. package/dist/core-runtime/learning/shared/specs/promote-report-spec.js +0 -113
  211. package/dist/core-runtime/learning/shared/specs/prune-log-spec.js +0 -36
  212. package/dist/core-runtime/learning/shared/specs/recovery-resolution-spec.js +0 -48
  213. package/dist/core-runtime/learning/shared/specs/restore-manifest-spec.js +0 -43
  214. package/dist/core-runtime/learning/shared/specs/spec-helpers.js +0 -64
  215. package/dist/core-runtime/learning/usage-tracker.js +0 -190
  216. package/dist/core-runtime/learning/usage-tracker.test.js +0 -176
  217. package/dist/core-runtime/onboard/detect-review-axes.js +0 -122
  218. package/dist/core-runtime/onboard/detect-review-axes.test.js +0 -127
  219. package/dist/core-runtime/onboard/write-review-block.js +0 -188
  220. package/dist/core-runtime/onboard/write-review-block.test.js +0 -240
  221. package/dist/core-runtime/readers/brownfield-builder.js +0 -150
  222. package/dist/core-runtime/readers/brownfield-builder.test.js +0 -136
  223. package/dist/core-runtime/readers/code-chunk-collector.js +0 -53
  224. package/dist/core-runtime/readers/code-chunk-collector.test.js +0 -136
  225. package/dist/core-runtime/readers/file-utils.js +0 -240
  226. package/dist/core-runtime/readers/file-utils.test.js +0 -146
  227. package/dist/core-runtime/readers/lexicon-citation-check.js +0 -93
  228. package/dist/core-runtime/readers/lexicon-citation-check.test.js +0 -77
  229. package/dist/core-runtime/readers/mcp-figma.js +0 -30
  230. package/dist/core-runtime/readers/mcp-figma.test.js +0 -82
  231. package/dist/core-runtime/readers/mcp-generic.js +0 -31
  232. package/dist/core-runtime/readers/mcp-generic.test.js +0 -76
  233. package/dist/core-runtime/readers/ontology-index.js +0 -148
  234. package/dist/core-runtime/readers/ontology-index.test.js +0 -245
  235. package/dist/core-runtime/readers/ontology-query.js +0 -168
  236. package/dist/core-runtime/readers/ontology-query.test.js +0 -311
  237. package/dist/core-runtime/readers/ontology-resolve.js +0 -48
  238. package/dist/core-runtime/readers/ontology-resolve.test.js +0 -48
  239. package/dist/core-runtime/readers/patterns/index.js +0 -7
  240. package/dist/core-runtime/readers/review-log.js +0 -213
  241. package/dist/core-runtime/readers/review-log.test.js +0 -313
  242. package/dist/core-runtime/readers/scan-local.js +0 -102
  243. package/dist/core-runtime/readers/scan-local.test.js +0 -102
  244. package/dist/core-runtime/readers/scan-tarball.js +0 -121
  245. package/dist/core-runtime/readers/scan-tarball.test.js +0 -283
  246. package/dist/core-runtime/readers/scan-vault.js +0 -34
  247. package/dist/core-runtime/readers/scan-vault.test.js +0 -81
  248. package/dist/core-runtime/readers/types.js +0 -42
  249. package/dist/core-runtime/readers/types.test.js +0 -94
  250. package/dist/core-runtime/readers/viewpoint-collectors.js +0 -229
  251. package/dist/core-runtime/reconstruct/seed-candidate-validation.js +0 -385
  252. package/dist/core-runtime/review/citation-audit.test.js +0 -165
  253. package/dist/core-runtime/review/execution-plan-resolver.js +0 -247
  254. package/dist/core-runtime/review/execution-plan-resolver.test.js +0 -243
  255. package/dist/core-runtime/review/execution-topology-resolver-axis-first.test.js +0 -246
  256. package/dist/core-runtime/review/execution-topology-resolver.js +0 -401
  257. package/dist/core-runtime/review/execution-topology-resolver.test.js +0 -315
  258. package/dist/core-runtime/review/inline-context-embedder.test.js +0 -154
  259. package/dist/core-runtime/review/legacy-mode-policy.js +0 -88
  260. package/dist/core-runtime/review/materializers-effort-persist.test.js +0 -79
  261. package/dist/core-runtime/review/ontology-path-classifier.js +0 -179
  262. package/dist/core-runtime/review/ontology-path-classifier.test.js +0 -216
  263. package/dist/core-runtime/review/packet-boundary-policy.test.js +0 -107
  264. package/dist/core-runtime/review/participating-lens-paths.test.js +0 -73
  265. package/dist/core-runtime/review/review-config-legacy-translate.js +0 -244
  266. package/dist/core-runtime/review/review-config-legacy-translate.test.js +0 -161
  267. package/dist/core-runtime/review/review-config-validator.js +0 -289
  268. package/dist/core-runtime/review/review-config-validator.test.js +0 -236
  269. package/dist/core-runtime/review/shape-pipeline-audit.test.js +0 -311
  270. package/dist/core-runtime/review/shape-to-topology-id.js +0 -117
  271. package/dist/core-runtime/review/shape-to-topology-id.test.js +0 -132
  272. package/dist/core-runtime/review/topology-shape-derivation.js +0 -155
  273. package/dist/core-runtime/review/topology-shape-derivation.test.js +0 -195
  274. package/dist/core-runtime/scope-runtime/constants.js +0 -12
  275. package/dist/core-runtime/scope-runtime/constraint-pool.js +0 -166
  276. package/dist/core-runtime/scope-runtime/constraint-pool.test.js +0 -674
  277. package/dist/core-runtime/scope-runtime/domain-validation-log.js +0 -135
  278. package/dist/core-runtime/scope-runtime/domain-validation-log.test.js +0 -156
  279. package/dist/core-runtime/scope-runtime/eval-persistence.js +0 -65
  280. package/dist/core-runtime/scope-runtime/eval-persistence.test.js +0 -84
  281. package/dist/core-runtime/scope-runtime/event-pipeline.js +0 -64
  282. package/dist/core-runtime/scope-runtime/event-pipeline.test.js +0 -450
  283. package/dist/core-runtime/scope-runtime/event-store.js +0 -39
  284. package/dist/core-runtime/scope-runtime/event-store.test.js +0 -95
  285. package/dist/core-runtime/scope-runtime/gate-guard.js +0 -348
  286. package/dist/core-runtime/scope-runtime/gate-guard.test.js +0 -1047
  287. package/dist/core-runtime/scope-runtime/hash.js +0 -4
  288. package/dist/core-runtime/scope-runtime/hash.test.js +0 -33
  289. package/dist/core-runtime/scope-runtime/id.js +0 -4
  290. package/dist/core-runtime/scope-runtime/id.test.js +0 -17
  291. package/dist/core-runtime/scope-runtime/reducer.js +0 -297
  292. package/dist/core-runtime/scope-runtime/reducer.test.js +0 -759
  293. package/dist/core-runtime/scope-runtime/scope-manager.js +0 -161
  294. package/dist/core-runtime/scope-runtime/state-machine.js +0 -309
  295. package/dist/core-runtime/scope-runtime/state-machine.test.js +0 -704
  296. package/dist/core-runtime/scope-runtime/types.js +0 -116
  297. package/dist/core-runtime/scope-runtime/types.test.js +0 -69
  298. package/dist/core-runtime/translate/render-for-user.js +0 -169
  299. package/dist/core-runtime/translate/render-for-user.test.js +0 -122
  300. package/dist/providers/capability-contract.js +0 -1
@@ -1,457 +0,0 @@
1
- import fs from "node:fs";
2
- import os from "node:os";
3
- import path from "node:path";
4
- import { afterEach, beforeEach, describe, expect, it, vi, } from "vitest";
5
- // Module-level mock of node:child_process.spawnSync. Hoisted by vitest
6
- // before the spawn-watcher import below, so spawn-watcher.ts picks up
7
- // the mocked binding at module-load time. The dry-run test paths in
8
- // other describe blocks never call spawnSync (they short-circuit on
9
- // `dry_run: true`), so the mock is inert for them.
10
- vi.mock("node:child_process", async (importOriginal) => {
11
- const actual = await importOriginal();
12
- return {
13
- ...actual,
14
- spawnSync: vi.fn(),
15
- };
16
- });
17
- import { spawnSync } from "node:child_process";
18
- import { spawnWatcherPane } from "./spawn-watcher.js";
19
- const mockedSpawnSync = vi.mocked(spawnSync);
20
- function mkProjectRoot(withWatcherScript) {
21
- const projectRoot = fs.mkdtempSync(path.join(os.tmpdir(), "onto-watcher-test-"));
22
- const sessionRoot = path.join(projectRoot, ".onto", "review", "20260422-fixture");
23
- fs.mkdirSync(sessionRoot, { recursive: true });
24
- if (withWatcherScript) {
25
- const scriptsDir = path.join(projectRoot, "scripts");
26
- fs.mkdirSync(scriptsDir, { recursive: true });
27
- fs.writeFileSync(path.join(scriptsDir, "onto-review-watch.sh"), "#!/usr/bin/env bash\n# test fixture — no-op\n", { mode: 0o755 });
28
- }
29
- return {
30
- projectRoot,
31
- sessionRoot,
32
- cleanup: () => fs.rmSync(projectRoot, { recursive: true, force: true }),
33
- };
34
- }
35
- // ---------------------------------------------------------------------------
36
- // Platform stubbing — iTerm2 / Apple Terminal branches gate on
37
- // `process.platform === "darwin"`. Tests must match so they exercise
38
- // the same branches regardless of the host OS running vitest.
39
- // ---------------------------------------------------------------------------
40
- function stubDarwin() {
41
- const original = Object.getOwnPropertyDescriptor(process, "platform");
42
- Object.defineProperty(process, "platform", {
43
- value: "darwin",
44
- configurable: true,
45
- });
46
- return () => {
47
- if (original) {
48
- Object.defineProperty(process, "platform", original);
49
- }
50
- };
51
- }
52
- // ---------------------------------------------------------------------------
53
- // Env stubbing — save + restore the env vars `spawnWatcherPane` inspects.
54
- // ---------------------------------------------------------------------------
55
- const WATCHED_ENV_KEYS = [
56
- "ONTO_WATCHER_DRY_RUN",
57
- "TMUX",
58
- "TMUX_PANE",
59
- "TERM_PROGRAM",
60
- "ITERM_SESSION_ID",
61
- ];
62
- function saveEnv() {
63
- const snapshot = {};
64
- for (const key of WATCHED_ENV_KEYS) {
65
- snapshot[key] = process.env[key];
66
- }
67
- return snapshot;
68
- }
69
- function clearWatchedEnv() {
70
- for (const key of WATCHED_ENV_KEYS) {
71
- delete process.env[key];
72
- }
73
- }
74
- function restoreEnv(snapshot) {
75
- for (const key of WATCHED_ENV_KEYS) {
76
- const prev = snapshot[key];
77
- if (prev === undefined) {
78
- delete process.env[key];
79
- }
80
- else {
81
- process.env[key] = prev;
82
- }
83
- }
84
- }
85
- // ---------------------------------------------------------------------------
86
- // Tests
87
- // ---------------------------------------------------------------------------
88
- describe("spawnWatcherPane — prereq failure", () => {
89
- let fixture;
90
- let envSnapshot;
91
- beforeEach(() => {
92
- envSnapshot = saveEnv();
93
- clearWatchedEnv();
94
- });
95
- afterEach(() => {
96
- restoreEnv(envSnapshot);
97
- if (fixture)
98
- fixture.cleanup();
99
- });
100
- it("returns spawned=false when watcher script is missing", () => {
101
- fixture = mkProjectRoot(false);
102
- process.env.TMUX = "/tmp/tmux-1000/default,12345,0";
103
- process.env.ONTO_WATCHER_DRY_RUN = "1";
104
- const result = spawnWatcherPane(fixture.projectRoot, fixture.sessionRoot);
105
- expect(result.spawned).toBe(false);
106
- expect(result.reason).toContain("watcher script not found");
107
- });
108
- it("returns spawned=false when no terminal multiplexer signal detected", () => {
109
- fixture = mkProjectRoot(true);
110
- // Every detection-priority env is unset — even dry-run cannot fake a
111
- // mechanism that has no signal to match.
112
- process.env.ONTO_WATCHER_DRY_RUN = "1";
113
- const result = spawnWatcherPane(fixture.projectRoot, fixture.sessionRoot);
114
- expect(result.spawned).toBe(false);
115
- expect(result.reason).toContain("no supported terminal multiplexer");
116
- });
117
- });
118
- describe("spawnWatcherPane — ontoHome fallback", () => {
119
- let projectFixture;
120
- let ontoHomeFixture;
121
- let envSnapshot;
122
- beforeEach(() => {
123
- envSnapshot = saveEnv();
124
- clearWatchedEnv();
125
- process.env.ONTO_WATCHER_DRY_RUN = "1";
126
- process.env.TMUX = "/tmp/tmux-1000/default,12345,0";
127
- });
128
- afterEach(() => {
129
- restoreEnv(envSnapshot);
130
- if (projectFixture)
131
- projectFixture.cleanup();
132
- if (ontoHomeFixture)
133
- ontoHomeFixture.cleanup();
134
- });
135
- it("finds watcher script in ontoHome when projectRoot has none", () => {
136
- // Regression guard for 2026-04-22 self-review finding: scripts/review-pr.sh
137
- // puts config.yml + target.diff in an isolated tmp project-root that has
138
- // no scripts/ subdirectory. Without ontoHome fallback the watcher would
139
- // silently degrade to `watcher script not found` and emit the manual-hint
140
- // branch instead of the intended dry-run detection.
141
- projectFixture = mkProjectRoot(false); // no scripts/ in project
142
- ontoHomeFixture = mkProjectRoot(true); // scripts/ present here
143
- const result = spawnWatcherPane(projectFixture.projectRoot, projectFixture.sessionRoot, ontoHomeFixture.projectRoot);
144
- expect(result.spawned).toBe(true);
145
- expect(result.mechanism).toBe("tmux");
146
- });
147
- it("prefers projectRoot over ontoHome when both have the script", () => {
148
- projectFixture = mkProjectRoot(true);
149
- ontoHomeFixture = mkProjectRoot(true);
150
- const result = spawnWatcherPane(projectFixture.projectRoot, projectFixture.sessionRoot, ontoHomeFixture.projectRoot);
151
- expect(result.spawned).toBe(true);
152
- });
153
- it("still fails when neither projectRoot nor ontoHome has the script", () => {
154
- projectFixture = mkProjectRoot(false);
155
- ontoHomeFixture = mkProjectRoot(false);
156
- const result = spawnWatcherPane(projectFixture.projectRoot, projectFixture.sessionRoot, ontoHomeFixture.projectRoot);
157
- expect(result.spawned).toBe(false);
158
- expect(result.reason).toContain("watcher script not found");
159
- });
160
- });
161
- describe("spawnWatcherPane — dry-run detection priority", () => {
162
- let fixture;
163
- let envSnapshot;
164
- let restorePlatform;
165
- beforeEach(() => {
166
- envSnapshot = saveEnv();
167
- clearWatchedEnv();
168
- fixture = mkProjectRoot(true);
169
- process.env.ONTO_WATCHER_DRY_RUN = "1";
170
- // spawn-watcher's iTerm2 and Apple Terminal branches require
171
- // process.platform === "darwin". Stub so the tests exercise the
172
- // intended branches on any host OS.
173
- restorePlatform = stubDarwin();
174
- });
175
- afterEach(() => {
176
- restorePlatform();
177
- restoreEnv(envSnapshot);
178
- if (fixture)
179
- fixture.cleanup();
180
- });
181
- it("detects tmux when $TMUX is set (priority 1)", () => {
182
- process.env.TMUX = "/tmp/tmux-1000/default,12345,0";
183
- const result = spawnWatcherPane(fixture.projectRoot, fixture.sessionRoot);
184
- expect(result.spawned).toBe(true);
185
- expect(result.mechanism).toBe("tmux");
186
- expect(result.dry_run).toBe(true);
187
- });
188
- it("detects iTerm2 when TERM_PROGRAM=iTerm.app AND ITERM_SESSION_ID is set (priority 2)", () => {
189
- process.env.TERM_PROGRAM = "iTerm.app";
190
- process.env.ITERM_SESSION_ID = "w0t0p0:ABCD1234-FAKE-UUID";
191
- const result = spawnWatcherPane(fixture.projectRoot, fixture.sessionRoot);
192
- expect(result.spawned).toBe(true);
193
- expect(result.mechanism).toBe("iterm2");
194
- expect(result.dry_run).toBe(true);
195
- });
196
- it("detects Apple Terminal when TERM_PROGRAM=Apple_Terminal (priority 3)", () => {
197
- process.env.TERM_PROGRAM = "Apple_Terminal";
198
- const result = spawnWatcherPane(fixture.projectRoot, fixture.sessionRoot);
199
- expect(result.spawned).toBe(true);
200
- expect(result.mechanism).toBe("apple_terminal");
201
- expect(result.dry_run).toBe(true);
202
- });
203
- it("prefers tmux over iTerm2 when both signals present (priority order)", () => {
204
- process.env.TMUX = "/tmp/tmux-1000/default,12345,0";
205
- process.env.TERM_PROGRAM = "iTerm.app";
206
- process.env.ITERM_SESSION_ID = "w0t0p0:ABCD1234-FAKE-UUID";
207
- const result = spawnWatcherPane(fixture.projectRoot, fixture.sessionRoot);
208
- expect(result.mechanism).toBe("tmux");
209
- });
210
- it("iTerm2 without ITERM_SESSION_ID falls through (does NOT spawn)", () => {
211
- // Regression guard: spawning into the currently-focused iTerm2 tab
212
- // would land on the wrong tab. spawn-watcher.ts must refuse, not
213
- // fall back.
214
- process.env.TERM_PROGRAM = "iTerm.app";
215
- // ITERM_SESSION_ID intentionally unset.
216
- const result = spawnWatcherPane(fixture.projectRoot, fixture.sessionRoot);
217
- expect(result.spawned).toBe(false);
218
- expect(result.reason).toContain("no supported terminal multiplexer");
219
- });
220
- });
221
- describe("spawnWatcherPane — darwin platform gate", () => {
222
- let fixture;
223
- let envSnapshot;
224
- beforeEach(() => {
225
- envSnapshot = saveEnv();
226
- clearWatchedEnv();
227
- fixture = mkProjectRoot(true);
228
- process.env.ONTO_WATCHER_DRY_RUN = "1";
229
- });
230
- afterEach(() => {
231
- restoreEnv(envSnapshot);
232
- if (fixture)
233
- fixture.cleanup();
234
- });
235
- it("iTerm2 branch skips when process.platform !== darwin", () => {
236
- // On non-darwin platforms, the iTerm2 osascript path would fail;
237
- // spawn-watcher must gate on platform and fall through. Regression
238
- // guard: Apple Terminal / iTerm2 SHOULD NOT fire on Linux/Windows
239
- // even if (somehow) the env vars match.
240
- const restore = (() => {
241
- const original = Object.getOwnPropertyDescriptor(process, "platform");
242
- Object.defineProperty(process, "platform", {
243
- value: "linux",
244
- configurable: true,
245
- });
246
- return () => {
247
- if (original)
248
- Object.defineProperty(process, "platform", original);
249
- };
250
- })();
251
- try {
252
- process.env.TERM_PROGRAM = "iTerm.app";
253
- process.env.ITERM_SESSION_ID = "w0t0p0:fake";
254
- const result = spawnWatcherPane(fixture.projectRoot, fixture.sessionRoot);
255
- expect(result.spawned).toBe(false);
256
- }
257
- finally {
258
- restore();
259
- }
260
- });
261
- it("Apple Terminal branch skips when process.platform !== darwin", () => {
262
- const restore = (() => {
263
- const original = Object.getOwnPropertyDescriptor(process, "platform");
264
- Object.defineProperty(process, "platform", {
265
- value: "linux",
266
- configurable: true,
267
- });
268
- return () => {
269
- if (original)
270
- Object.defineProperty(process, "platform", original);
271
- };
272
- })();
273
- try {
274
- process.env.TERM_PROGRAM = "Apple_Terminal";
275
- const result = spawnWatcherPane(fixture.projectRoot, fixture.sessionRoot);
276
- expect(result.spawned).toBe(false);
277
- }
278
- finally {
279
- restore();
280
- }
281
- });
282
- });
283
- // ---------------------------------------------------------------------------
284
- // PR #185 follow-up #2 — real-attach paths via child_process.spawnSync mock.
285
- //
286
- // Why this exists:
287
- // The dry-run describe block above covers detection priority, but the
288
- // actual osascript / tmux split-window invocation paths (spawn-watcher.ts
289
- // L118-L122 tmux, L180-L185 iterm2, L206-L209 apple_terminal) are NEVER
290
- // exercised by dry-run (it returns early on `dry_run: true` before the
291
- // spawnSync call). Smoke scripts run with `ONTO_WATCHER_DRY_RUN=1` for
292
- // the same reason — they cannot regress-guard the real spawnSync
293
- // arguments. A change to `split-window` flags or the iTerm2 osascript
294
- // "matched" sentinel parsing would slip through both layers silently.
295
- //
296
- // What this block adds:
297
- // spawnSync is mocked at the module boundary (top of this file). Each
298
- // test sets ONTO_WATCHER_DRY_RUN unset (so the real-attach path runs)
299
- // and asserts on the args + return contract.
300
- // ---------------------------------------------------------------------------
301
- describe("spawnWatcherPane — real-attach (spawnSync mock)", () => {
302
- let fixture;
303
- let envSnapshot;
304
- let restorePlatform;
305
- beforeEach(() => {
306
- envSnapshot = saveEnv();
307
- clearWatchedEnv();
308
- fixture = mkProjectRoot(true);
309
- // Real-attach path requires dry-run UNSET. Tests below add per-case env.
310
- restorePlatform = stubDarwin();
311
- mockedSpawnSync.mockReset();
312
- });
313
- afterEach(() => {
314
- restorePlatform();
315
- restoreEnv(envSnapshot);
316
- if (fixture)
317
- fixture.cleanup();
318
- });
319
- it("tmux real-attach passes split-window args + targets $TMUX_PANE + refocuses", () => {
320
- process.env.TMUX = "/tmp/tmux-1000/default,12345,0";
321
- process.env.TMUX_PANE = "%5";
322
- // Two spawnSync calls expected: split-window then select-pane -l.
323
- mockedSpawnSync
324
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
325
- .mockReturnValueOnce({ status: 0, stdout: "", stderr: "", pid: 1, output: [], signal: null })
326
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
327
- .mockReturnValueOnce({ status: 0, stdout: "", stderr: "", pid: 2, output: [], signal: null });
328
- const result = spawnWatcherPane(fixture.projectRoot, fixture.sessionRoot);
329
- expect(result.spawned).toBe(true);
330
- expect(result.mechanism).toBe("tmux");
331
- expect(result.dry_run).toBeUndefined();
332
- expect(mockedSpawnSync).toHaveBeenCalledTimes(2);
333
- const [tmuxBin, tmuxArgs] = mockedSpawnSync.mock.calls[0];
334
- expect(tmuxBin).toBe("tmux");
335
- // Args contract (regression guard for split-window flags + pane target):
336
- // split-window -h -l 60 -t %5 <bash watcher cmd>
337
- expect(tmuxArgs).toEqual([
338
- "split-window",
339
- "-h",
340
- "-l",
341
- "60",
342
- "-t",
343
- "%5",
344
- expect.stringContaining("onto-review-watch.sh"),
345
- ]);
346
- // Refocus call comes second.
347
- const [refocusBin, refocusArgs] = mockedSpawnSync.mock.calls[1];
348
- expect(refocusBin).toBe("tmux");
349
- expect(refocusArgs).toEqual(["select-pane", "-l"]);
350
- });
351
- it("tmux without TMUX_PANE omits the -t flag", () => {
352
- // Regression guard: spawn-watcher pushes -t only when TMUX_PANE is set.
353
- // If someone always passes -t with empty value, tmux would error out.
354
- process.env.TMUX = "/tmp/tmux-1000/default,12345,0";
355
- // TMUX_PANE intentionally unset.
356
- mockedSpawnSync
357
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
358
- .mockReturnValueOnce({ status: 0, stdout: "", stderr: "", pid: 1, output: [], signal: null })
359
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
360
- .mockReturnValueOnce({ status: 0, stdout: "", stderr: "", pid: 2, output: [], signal: null });
361
- const result = spawnWatcherPane(fixture.projectRoot, fixture.sessionRoot);
362
- expect(result.spawned).toBe(true);
363
- const [, tmuxArgs] = mockedSpawnSync.mock.calls[0];
364
- expect(tmuxArgs).not.toContain("-t");
365
- expect(tmuxArgs).toEqual([
366
- "split-window",
367
- "-h",
368
- "-l",
369
- "60",
370
- expect.stringContaining("onto-review-watch.sh"),
371
- ]);
372
- });
373
- it("tmux split failing (status != 0) falls through, no spawned=true", () => {
374
- process.env.TMUX = "/tmp/tmux-1000/default,12345,0";
375
- // Tmux split returns non-zero. Without other multiplexer signals,
376
- // the function must report spawned=false (NOT bubble the failed
377
- // split as a success).
378
- mockedSpawnSync
379
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
380
- .mockReturnValueOnce({ status: 1, stdout: "", stderr: "split failed", pid: 1, output: [], signal: null });
381
- const result = spawnWatcherPane(fixture.projectRoot, fixture.sessionRoot);
382
- expect(result.spawned).toBe(false);
383
- expect(result.reason).toContain("no supported terminal multiplexer");
384
- // Refocus must NOT run when split itself failed.
385
- expect(mockedSpawnSync).toHaveBeenCalledTimes(1);
386
- });
387
- it("iTerm2 real-attach passes osascript with embedded UUID + 'matched' sentinel grants success", () => {
388
- process.env.TERM_PROGRAM = "iTerm.app";
389
- process.env.ITERM_SESSION_ID = "w0t1p2:ABCD1234-FAKE-UUID";
390
- mockedSpawnSync.mockReturnValueOnce({
391
- status: 0,
392
- stdout: "matched",
393
- stderr: "",
394
- pid: 1,
395
- output: [],
396
- signal: null,
397
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
398
- });
399
- const result = spawnWatcherPane(fixture.projectRoot, fixture.sessionRoot);
400
- expect(result.spawned).toBe(true);
401
- expect(result.mechanism).toBe("iterm2");
402
- expect(mockedSpawnSync).toHaveBeenCalledTimes(1);
403
- const [bin, args] = mockedSpawnSync.mock.calls[0];
404
- expect(bin).toBe("osascript");
405
- // -e flag + script string. The script must reference both the UUID
406
- // suffix and the full session id (spawn-watcher matches either form).
407
- expect(args[0]).toBe("-e");
408
- const script = String(args[1]);
409
- expect(script).toContain("ABCD1234-FAKE-UUID");
410
- expect(script).toContain("w0t1p2:ABCD1234-FAKE-UUID");
411
- expect(script).toContain("split vertically");
412
- expect(script).toContain("onto-review-watch.sh");
413
- expect(script).toContain('return "matched"');
414
- });
415
- it("iTerm2 osascript returning 'no-match' falls through, no spawned=true", () => {
416
- // Regression guard: spawn-watcher requires the explicit "matched"
417
- // sentinel. If the originating tab is closed (no-match), the osascript
418
- // returns "no-match" and we must NOT report spawned=true (which would
419
- // imply a pane is visible, when none was attached).
420
- process.env.TERM_PROGRAM = "iTerm.app";
421
- process.env.ITERM_SESSION_ID = "w0t1p2:ABCD1234-FAKE-UUID";
422
- mockedSpawnSync.mockReturnValueOnce({
423
- status: 0,
424
- stdout: "no-match",
425
- stderr: "",
426
- pid: 1,
427
- output: [],
428
- signal: null,
429
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
430
- });
431
- const result = spawnWatcherPane(fixture.projectRoot, fixture.sessionRoot);
432
- expect(result.spawned).toBe(false);
433
- expect(result.reason).toContain("no supported terminal multiplexer");
434
- });
435
- it("Apple Terminal real-attach passes osascript with do-script", () => {
436
- process.env.TERM_PROGRAM = "Apple_Terminal";
437
- mockedSpawnSync.mockReturnValueOnce({
438
- status: 0,
439
- stdout: "",
440
- stderr: "",
441
- pid: 1,
442
- output: [],
443
- signal: null,
444
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
445
- });
446
- const result = spawnWatcherPane(fixture.projectRoot, fixture.sessionRoot);
447
- expect(result.spawned).toBe(true);
448
- expect(result.mechanism).toBe("apple_terminal");
449
- const [bin, args] = mockedSpawnSync.mock.calls[0];
450
- expect(bin).toBe("osascript");
451
- expect(args[0]).toBe("-e");
452
- const script = String(args[1]);
453
- expect(script).toContain('tell application "Terminal"');
454
- expect(script).toContain("do script");
455
- expect(script).toContain("onto-review-watch.sh");
456
- });
457
- });
@@ -1,79 +0,0 @@
1
- import { describe, it, expect } from "vitest";
2
- import { stripWrappingCodeFence } from "./strip-wrapping-code-fence.js";
3
- describe("stripWrappingCodeFence", () => {
4
- it("strips ```yaml wrapper observed on 30B synthesize output", () => {
5
- const raw = [
6
- "```yaml",
7
- "---",
8
- "deliberation_status: performed",
9
- "---",
10
- "### Consensus",
11
- "- All three lenses identify a systemic pattern.",
12
- "```",
13
- ].join("\n");
14
- const stripped = stripWrappingCodeFence(raw);
15
- expect(stripped.startsWith("---")).toBe(true);
16
- expect(stripped.endsWith("- All three lenses identify a systemic pattern.")).toBe(true);
17
- expect(stripped).not.toContain("```");
18
- });
19
- it("strips ```markdown wrapper", () => {
20
- const raw = "```markdown\n### Consensus\n- one\n```";
21
- expect(stripWrappingCodeFence(raw)).toBe("### Consensus\n- one");
22
- });
23
- it("strips fence with no language tag", () => {
24
- const raw = "```\n### Consensus\n- one\n```";
25
- expect(stripWrappingCodeFence(raw)).toBe("### Consensus\n- one");
26
- });
27
- it("tolerates leading and trailing whitespace around the fence", () => {
28
- const raw = " \n```yaml\nbody\n```\n \n";
29
- expect(stripWrappingCodeFence(raw)).toBe("body");
30
- });
31
- it("tolerates trailing whitespace on the closing fence line", () => {
32
- const raw = "```yaml\nbody\n``` ";
33
- expect(stripWrappingCodeFence(raw)).toBe("body");
34
- });
35
- it("preserves inner code blocks within a wrapped outer fence", () => {
36
- const raw = [
37
- "```markdown",
38
- "### Example",
39
- "```ts",
40
- "const x = 1;",
41
- "```",
42
- "end of example",
43
- "```",
44
- ].join("\n");
45
- const stripped = stripWrappingCodeFence(raw);
46
- expect(stripped.startsWith("### Example")).toBe(true);
47
- expect(stripped.endsWith("end of example")).toBe(true);
48
- expect(stripped).toContain("```ts");
49
- expect(stripped).toContain("const x = 1;");
50
- });
51
- it("is a no-op when no outer wrapping fence is present", () => {
52
- const raw = "### Consensus\n- some finding\n\n```ts\nconst x = 1;\n```\n\n### Disagreement\n- another";
53
- expect(stripWrappingCodeFence(raw)).toBe(raw.trim());
54
- });
55
- it("is a no-op on plain markdown without any fences", () => {
56
- const raw = "---\ndeliberation_status: not_needed\n---\n### Consensus\n- finding";
57
- expect(stripWrappingCodeFence(raw)).toBe(raw);
58
- });
59
- it("leaves partial fence (open without close) untouched", () => {
60
- const raw = "```yaml\n---\ndeliberation_status: performed\n---\n### Consensus\n- finding";
61
- expect(stripWrappingCodeFence(raw)).toBe(raw);
62
- });
63
- it("leaves partial fence (close without open) untouched", () => {
64
- const raw = "### Consensus\n- finding\n```";
65
- expect(stripWrappingCodeFence(raw)).toBe(raw);
66
- });
67
- it("handles CRLF line endings", () => {
68
- const raw = "```yaml\r\nbody line\r\n```";
69
- expect(stripWrappingCodeFence(raw)).toBe("body line");
70
- });
71
- it("does not strip when opening fence is not at the very start", () => {
72
- const raw = "Prefix text\n```yaml\nbody\n```";
73
- expect(stripWrappingCodeFence(raw)).toBe(raw);
74
- });
75
- it("does not strip when closing fence has trailing content after it", () => {
76
- const raw = "```yaml\nbody\n```\nTrailing commentary";
77
- expect(stripWrappingCodeFence(raw)).toBe(raw);
78
- });
79
- });