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,254 +0,0 @@
1
- /**
2
- * File writer for `onto install` — persists the resolved decisions to
3
- * `config.yml`, `.env`, and `.env.example` atomically.
4
- *
5
- * # What this module is
6
- *
7
- * A pure function seat (where possible): the rendering helpers
8
- * (`renderConfigYml`, `renderEnvExample`, `renderEnv`, `parseEnv`) are
9
- * filesystem-free so they can be unit-tested without tmpdirs. Only
10
- * `writeInstallFiles` touches disk, and even that delegates content
11
- * generation to the renderers.
12
- *
13
- * # Why atomic writes
14
- *
15
- * Both `config.yml` and `.env` are read on every onto invocation — a
16
- * partial write (e.g. disk full mid-write, process killed) would leave
17
- * an invalid file that blocks all future commands. Writing to a temp
18
- * file in the same directory and renaming (POSIX atomic on same
19
- * filesystem) means a crashed install leaves the prior state intact.
20
- *
21
- * # Why merge `.env` instead of overwrite
22
- *
23
- * The user may have unrelated keys in `.env` (third-party tool
24
- * credentials, local overrides). Install touches only the keys it
25
- * captured this session — the merge step preserves everything else.
26
- * `.env.example` is overwritten wholesale since it's a template
27
- * derived purely from the current install decisions.
28
- *
29
- * # File modes
30
- *
31
- * `.env` is set to 0600 so other users on the machine can't read the
32
- * secrets file. On Windows, Node's fs module accepts the mode flag but
33
- * NTFS ACLs don't map cleanly — treat the mode as best-effort and rely
34
- * on filesystem isolation on that platform.
35
- */
36
- import fs from "node:fs";
37
- import os from "node:os";
38
- import path from "node:path";
39
- import yaml from "yaml";
40
- // ---------------------------------------------------------------------------
41
- // Path resolution
42
- // ---------------------------------------------------------------------------
43
- /**
44
- * Resolve the four file targets for a given profile scope.
45
- *
46
- * Global scope uses `~/.onto/` (no gitignore touched — there's no repo
47
- * at the user-home layer). Project scope uses `<projectRoot>/.onto/`
48
- * and also needs the project's root `.gitignore` path so the write
49
- * step can ensure `.onto/.env` is ignored.
50
- */
51
- export function resolveInstallPaths(scope, projectRoot, homeDir = os.homedir()) {
52
- if (scope === "global") {
53
- const ontoDir = path.join(homeDir, ".onto");
54
- return {
55
- ontoDir,
56
- configYmlPath: path.join(ontoDir, "config.yml"),
57
- envPath: path.join(ontoDir, ".env"),
58
- envExamplePath: path.join(ontoDir, ".env.example"),
59
- };
60
- }
61
- const ontoDir = path.join(projectRoot, ".onto");
62
- return {
63
- ontoDir,
64
- configYmlPath: path.join(ontoDir, "config.yml"),
65
- envPath: path.join(ontoDir, ".env"),
66
- envExamplePath: path.join(ontoDir, ".env.example"),
67
- gitignorePath: path.join(projectRoot, ".gitignore"),
68
- };
69
- }
70
- // ---------------------------------------------------------------------------
71
- // config.yml rendering
72
- // ---------------------------------------------------------------------------
73
- /**
74
- * Produce the YAML text that install writes for the current decisions.
75
- *
76
- * Shape rationale:
77
- *
78
- * - `output_language` is always set (user-facing render boundary).
79
- * - `review.subagent.provider` records the review channel choice.
80
- * For `main-native` this is sufficient — no model_id / effort
81
- * fields at install time.
82
- * - `subagent_llm.provider` records the learn/background channel.
83
- * - `external_http_provider` is ALSO set when learn provider is
84
- * anthropic/openai/litellm — it's the ladder's user-override seat
85
- * in `llm-caller.ts`, so explicitly matching the user's choice
86
- * makes the intent sticky across future env drift.
87
- * - `external_http_provider` is NOT set for codex learn — codex is
88
- * not a valid value for that field (see config-chain.ts L37).
89
- * - Model fields are omitted intentionally. `onto config edit` is
90
- * the next step for users who want to pin a specific model; the
91
- * install completion message points there.
92
- */
93
- export function renderConfigYml(decisions) {
94
- const config = {
95
- output_language: decisions.outputLanguage,
96
- review: {
97
- subagent: { provider: decisions.reviewProvider },
98
- },
99
- subagent_llm: {
100
- provider: decisions.learnProvider,
101
- },
102
- };
103
- if (decisions.learnProvider === "anthropic" ||
104
- decisions.learnProvider === "openai" ||
105
- decisions.learnProvider === "litellm") {
106
- config.external_http_provider = decisions.learnProvider;
107
- }
108
- const header = [
109
- "# Written by `onto install` — edit with `onto config edit` or",
110
- "# re-run `onto install --reconfigure`. Full key reference:",
111
- "# .onto/processes/configuration.md",
112
- "",
113
- ].join("\n");
114
- return header + yaml.stringify(config);
115
- }
116
- // ---------------------------------------------------------------------------
117
- // .env / .env.example rendering
118
- // ---------------------------------------------------------------------------
119
- /**
120
- * Lightweight `.env` parser. Preserves KEY=VALUE lines, drops comments
121
- * and blanks. NOT a full shell parser — no variable expansion, no
122
- * quoting nuance. The write path likewise emits unquoted values.
123
- *
124
- * Keys are trimmed; values are taken verbatim after the first `=`
125
- * (including leading/trailing whitespace, which `.env` consumers
126
- * typically preserve).
127
- */
128
- export function parseEnv(contents) {
129
- const out = new Map();
130
- for (const rawLine of contents.split(/\r?\n/)) {
131
- const line = rawLine.trim();
132
- if (!line || line.startsWith("#"))
133
- continue;
134
- const eq = line.indexOf("=");
135
- if (eq === -1)
136
- continue;
137
- const key = line.slice(0, eq).trim();
138
- const value = line.slice(eq + 1);
139
- if (key)
140
- out.set(key, value);
141
- }
142
- return out;
143
- }
144
- /**
145
- * Serialize a KEY=VALUE map deterministically (sorted by key).
146
- *
147
- * Deterministic order matters so a reconfigure that doesn't change any
148
- * key produces an identical file — no spurious diffs, easy to grep.
149
- */
150
- export function renderEnv(kv) {
151
- const header = "# onto runtime credentials — managed by `onto install`.\n";
152
- const lines = [header];
153
- const sorted = [...kv.entries()].sort((a, b) => a[0].localeCompare(b[0]));
154
- for (const [k, v] of sorted) {
155
- lines.push(`${k}=${v}`);
156
- }
157
- return lines.join("\n") + "\n";
158
- }
159
- /**
160
- * Build the `.env.example` text for the current install decisions.
161
- *
162
- * Only includes sections for providers actually selected — no point
163
- * in shipping an ANTHROPIC_API_KEY template to a user who picked
164
- * codex + openai. The LiteLLM section includes the expanded guidance
165
- * block (local runtime examples) because that field is the one new
166
- * users most often fumble — "just the port" is a common mistake.
167
- */
168
- export function renderEnvExample(decisions) {
169
- const needsAnthropic = decisions.reviewProvider === "anthropic" ||
170
- decisions.learnProvider === "anthropic";
171
- const needsOpenai = decisions.reviewProvider === "openai" ||
172
- decisions.learnProvider === "openai";
173
- const needsLitellm = decisions.reviewProvider === "litellm" ||
174
- decisions.learnProvider === "litellm";
175
- const lines = [
176
- "# ---------------------------------------------------------------------------",
177
- "# onto runtime credentials",
178
- "# Copy this file to .env and fill in values as needed.",
179
- "# Tracked in git; .env is gitignored.",
180
- "# ---------------------------------------------------------------------------",
181
- "",
182
- ];
183
- if (needsAnthropic) {
184
- lines.push("# Anthropic API (per-token)", "# ANTHROPIC_API_KEY=sk-ant-...", "");
185
- }
186
- if (needsOpenai) {
187
- lines.push("# OpenAI API (per-token)", "# OPENAI_API_KEY=sk-...", "");
188
- }
189
- if (needsLitellm) {
190
- lines.push("# LiteLLM / OpenAI-compatible endpoint base URL", "#", "# Full URL required: scheme + host + port + /v1 path prefix.", "# Accepts any OpenAI-compatible endpoint — local runtime or remote proxy.", "#", "# Local examples:", "# LiteLLM proxy http://localhost:4000/v1", "# Ollama http://localhost:11434/v1", "# LM Studio http://localhost:1234/v1", "# vLLM http://localhost:8000/v1", "# MLX http://localhost:8080/v1", "#", "# Remote example:", "# https://litellm.example.com/v1", "#", "# LITELLM_BASE_URL=http://localhost:4000/v1", "", "# Optional — only when the endpoint enforces authentication.", "# Local endpoints without auth (Ollama 등) leave this blank.", "# LITELLM_API_KEY=sk-proxy-token", "");
191
- }
192
- return lines.join("\n");
193
- }
194
- // ---------------------------------------------------------------------------
195
- // Atomic file write
196
- // ---------------------------------------------------------------------------
197
- function atomicWrite(target, content, mode) {
198
- const dir = path.dirname(target);
199
- const tmp = path.join(dir, `.${path.basename(target)}.tmp-${process.pid}-${Date.now()}`);
200
- fs.writeFileSync(tmp, content, { encoding: "utf8", mode });
201
- fs.renameSync(tmp, target);
202
- }
203
- /**
204
- * Write (or dry-run) the three install output files.
205
- *
206
- * Order:
207
- * 1. Ensure `.onto/` dir exists (idempotent).
208
- * 2. Read existing `.env` to preserve unrelated keys.
209
- * 3. Merge newly-captured secrets into the key map.
210
- * 4. Atomic-write config.yml (0644), .env (0600), .env.example (0644).
211
- *
212
- * Returns the content strings for display / testing. Callers handle
213
- * side-effects like the completion summary print — writer stays focused
214
- * on file state.
215
- */
216
- export function writeInstallFiles(args) {
217
- const { paths, decisions, secrets, dryRun } = args;
218
- const configYml = renderConfigYml(decisions);
219
- const envExample = renderEnvExample(decisions);
220
- // Merge existing .env (if any) with newly-captured secrets.
221
- let envMap = new Map();
222
- if (!dryRun && fs.existsSync(paths.envPath)) {
223
- envMap = parseEnv(fs.readFileSync(paths.envPath, "utf8"));
224
- }
225
- if (secrets.anthropicApiKey) {
226
- envMap.set("ANTHROPIC_API_KEY", secrets.anthropicApiKey);
227
- }
228
- if (secrets.openaiApiKey) {
229
- envMap.set("OPENAI_API_KEY", secrets.openaiApiKey);
230
- }
231
- if (secrets.litellmBaseUrl) {
232
- envMap.set("LITELLM_BASE_URL", secrets.litellmBaseUrl);
233
- }
234
- if (secrets.litellmApiKey) {
235
- envMap.set("LITELLM_API_KEY", secrets.litellmApiKey);
236
- }
237
- const env = renderEnv(envMap);
238
- if (!dryRun) {
239
- fs.mkdirSync(paths.ontoDir, { recursive: true });
240
- atomicWrite(paths.configYmlPath, configYml, 0o644);
241
- atomicWrite(paths.envPath, env, 0o600);
242
- atomicWrite(paths.envExamplePath, envExample, 0o644);
243
- }
244
- return {
245
- configYml,
246
- env,
247
- envExample,
248
- writtenTo: {
249
- configYml: paths.configYmlPath,
250
- env: paths.envPath,
251
- envExample: paths.envExamplePath,
252
- },
253
- };
254
- }
@@ -1,218 +0,0 @@
1
- import { describe, it, expect, beforeEach, afterEach } from "vitest";
2
- import fs from "node:fs";
3
- import os from "node:os";
4
- import path from "node:path";
5
- import yaml from "yaml";
6
- import { parseEnv, renderConfigYml, renderEnv, renderEnvExample, resolveInstallPaths, writeInstallFiles, } from "./writer.js";
7
- const SAMPLE = {
8
- profileScope: "project",
9
- reviewProvider: "anthropic",
10
- learnProvider: "anthropic",
11
- outputLanguage: "ko",
12
- };
13
- describe("resolveInstallPaths", () => {
14
- it("global scope points at ~/.onto/ and omits gitignorePath", () => {
15
- const paths = resolveInstallPaths("global", "/unused", "/home/tester");
16
- expect(paths.ontoDir).toBe("/home/tester/.onto");
17
- expect(paths.configYmlPath).toBe("/home/tester/.onto/config.yml");
18
- expect(paths.envPath).toBe("/home/tester/.onto/.env");
19
- expect(paths.envExamplePath).toBe("/home/tester/.onto/.env.example");
20
- expect(paths.gitignorePath).toBeUndefined();
21
- });
22
- it("project scope points at <projectRoot>/.onto/ and sets gitignorePath", () => {
23
- const paths = resolveInstallPaths("project", "/tmp/proj", "/home/tester");
24
- expect(paths.ontoDir).toBe("/tmp/proj/.onto");
25
- expect(paths.configYmlPath).toBe("/tmp/proj/.onto/config.yml");
26
- expect(paths.envPath).toBe("/tmp/proj/.onto/.env");
27
- expect(paths.envExamplePath).toBe("/tmp/proj/.onto/.env.example");
28
- expect(paths.gitignorePath).toBe("/tmp/proj/.gitignore");
29
- });
30
- });
31
- describe("renderConfigYml", () => {
32
- it("sets review.subagent.provider and subagent_llm.provider", () => {
33
- const out = renderConfigYml(SAMPLE);
34
- const parsed = yaml.parse(stripHeader(out));
35
- expect(parsed.output_language).toBe("ko");
36
- expect(parsed.review).toEqual({ subagent: { provider: "anthropic" } });
37
- expect(parsed.subagent_llm).toEqual({ provider: "anthropic" });
38
- expect(parsed.external_http_provider).toBe("anthropic");
39
- });
40
- it("omits external_http_provider for codex learn (codex not a valid value)", () => {
41
- const decisions = {
42
- ...SAMPLE,
43
- reviewProvider: "codex",
44
- learnProvider: "codex",
45
- };
46
- const parsed = yaml.parse(stripHeader(renderConfigYml(decisions)));
47
- expect(parsed.external_http_provider).toBeUndefined();
48
- expect(parsed.subagent_llm).toEqual({ provider: "codex" });
49
- });
50
- it("records main-native review provider without external fields", () => {
51
- const decisions = {
52
- ...SAMPLE,
53
- reviewProvider: "main-native",
54
- learnProvider: "anthropic",
55
- };
56
- const parsed = yaml.parse(stripHeader(renderConfigYml(decisions)));
57
- expect(parsed.review).toEqual({
58
- subagent: { provider: "main-native" },
59
- });
60
- expect(parsed.external_http_provider).toBe("anthropic");
61
- });
62
- it("emits a human-readable header comment at the top", () => {
63
- const out = renderConfigYml(SAMPLE);
64
- expect(out.startsWith("# Written by `onto install`")).toBe(true);
65
- expect(out).toContain("onto config edit");
66
- });
67
- });
68
- describe("parseEnv / renderEnv", () => {
69
- it("parses KEY=VALUE lines and skips comments/blank lines", () => {
70
- const input = [
71
- "# comment",
72
- "",
73
- "FOO=bar",
74
- " # indented comment",
75
- "BAZ=quux=extra",
76
- "malformed-line-no-equals",
77
- ].join("\n");
78
- const kv = parseEnv(input);
79
- expect(kv.get("FOO")).toBe("bar");
80
- expect(kv.get("BAZ")).toBe("quux=extra");
81
- expect(kv.has("malformed-line-no-equals")).toBe(false);
82
- });
83
- it("renderEnv emits keys sorted alphabetically", () => {
84
- const kv = new Map([
85
- ["ZZZ", "last"],
86
- ["AAA", "first"],
87
- ["MMM", "middle"],
88
- ]);
89
- const out = renderEnv(kv);
90
- const keyOrder = out
91
- .split("\n")
92
- .filter((line) => !line.startsWith("#") && line.includes("="))
93
- .map((line) => line.split("=")[0]);
94
- expect(keyOrder).toEqual(["AAA", "MMM", "ZZZ"]);
95
- });
96
- it("roundtrips through parse → render", () => {
97
- const original = new Map([
98
- ["ANTHROPIC_API_KEY", "sk-ant-xyz"],
99
- ["LITELLM_BASE_URL", "http://localhost:4000/v1"],
100
- ]);
101
- const rendered = renderEnv(original);
102
- const reparsed = parseEnv(rendered);
103
- expect(reparsed).toEqual(original);
104
- });
105
- });
106
- describe("renderEnvExample", () => {
107
- it("includes sections only for selected providers", () => {
108
- const anthOnly = renderEnvExample({
109
- ...SAMPLE,
110
- reviewProvider: "anthropic",
111
- learnProvider: "anthropic",
112
- });
113
- expect(anthOnly).toContain("ANTHROPIC_API_KEY");
114
- expect(anthOnly).not.toContain("OPENAI_API_KEY");
115
- expect(anthOnly).not.toContain("LITELLM_BASE_URL");
116
- });
117
- it("includes LiteLLM guidance block with local runtime examples", () => {
118
- const out = renderEnvExample({
119
- ...SAMPLE,
120
- reviewProvider: "litellm",
121
- learnProvider: "litellm",
122
- });
123
- expect(out).toContain("LITELLM_BASE_URL");
124
- expect(out).toContain("Ollama");
125
- expect(out).toContain("http://localhost:11434/v1");
126
- expect(out).toContain("# LITELLM_API_KEY=");
127
- });
128
- it("merges sections when review and learn providers differ", () => {
129
- const out = renderEnvExample({
130
- ...SAMPLE,
131
- reviewProvider: "openai",
132
- learnProvider: "anthropic",
133
- });
134
- expect(out).toContain("OPENAI_API_KEY");
135
- expect(out).toContain("ANTHROPIC_API_KEY");
136
- });
137
- it("produces no provider sections for codex-only setup", () => {
138
- const out = renderEnvExample({
139
- ...SAMPLE,
140
- reviewProvider: "codex",
141
- learnProvider: "codex",
142
- });
143
- expect(out).not.toContain("ANTHROPIC_API_KEY");
144
- expect(out).not.toContain("OPENAI_API_KEY");
145
- expect(out).not.toContain("LITELLM_BASE_URL");
146
- });
147
- });
148
- describe("writeInstallFiles", () => {
149
- let tmpHome;
150
- beforeEach(() => {
151
- tmpHome = fs.mkdtempSync(path.join(os.tmpdir(), "onto-install-writer-"));
152
- });
153
- afterEach(() => {
154
- fs.rmSync(tmpHome, { recursive: true, force: true });
155
- });
156
- it("creates .onto/ and writes all three files with correct modes", () => {
157
- const paths = resolveInstallPaths("global", "/unused", tmpHome);
158
- const result = writeInstallFiles({
159
- paths,
160
- decisions: SAMPLE,
161
- secrets: { anthropicApiKey: "sk-ant-test" },
162
- });
163
- expect(fs.existsSync(paths.ontoDir)).toBe(true);
164
- expect(fs.existsSync(paths.configYmlPath)).toBe(true);
165
- expect(fs.existsSync(paths.envPath)).toBe(true);
166
- expect(fs.existsSync(paths.envExamplePath)).toBe(true);
167
- expect(fs.readFileSync(paths.configYmlPath, "utf8")).toBe(result.configYml);
168
- expect(fs.readFileSync(paths.envPath, "utf8")).toBe(result.env);
169
- // .env mode should be 0600 on POSIX.
170
- if (process.platform !== "win32") {
171
- const mode = fs.statSync(paths.envPath).mode & 0o777;
172
- expect(mode).toBe(0o600);
173
- }
174
- });
175
- it("preserves unrelated keys during .env merge", () => {
176
- const paths = resolveInstallPaths("global", "/unused", tmpHome);
177
- fs.mkdirSync(paths.ontoDir, { recursive: true });
178
- fs.writeFileSync(paths.envPath, "# user\nCUSTOM_TOOL_KEY=preserve-me\nANTHROPIC_API_KEY=old-value\n");
179
- writeInstallFiles({
180
- paths,
181
- decisions: SAMPLE,
182
- secrets: { anthropicApiKey: "new-value" },
183
- });
184
- const merged = parseEnv(fs.readFileSync(paths.envPath, "utf8"));
185
- expect(merged.get("CUSTOM_TOOL_KEY")).toBe("preserve-me");
186
- expect(merged.get("ANTHROPIC_API_KEY")).toBe("new-value");
187
- });
188
- it("dry-run returns content without touching the filesystem", () => {
189
- const paths = resolveInstallPaths("global", "/unused", tmpHome);
190
- const result = writeInstallFiles({
191
- paths,
192
- decisions: SAMPLE,
193
- secrets: { anthropicApiKey: "sk-ant-test" },
194
- dryRun: true,
195
- });
196
- expect(fs.existsSync(paths.ontoDir)).toBe(false);
197
- expect(result.configYml).toContain("output_language: ko");
198
- expect(result.env).toContain("ANTHROPIC_API_KEY=sk-ant-test");
199
- });
200
- it("atomic write: no partial temp file left behind on success", () => {
201
- const paths = resolveInstallPaths("global", "/unused", tmpHome);
202
- writeInstallFiles({
203
- paths,
204
- decisions: SAMPLE,
205
- secrets: { anthropicApiKey: "sk" },
206
- });
207
- const files = fs.readdirSync(paths.ontoDir);
208
- const tmpFiles = files.filter((f) => f.startsWith(".") && f.includes(".tmp-"));
209
- expect(tmpFiles).toEqual([]);
210
- });
211
- });
212
- /** Strip the leading comment header block so `yaml.parse` sees clean YAML. */
213
- function stripHeader(text) {
214
- return text
215
- .split("\n")
216
- .filter((line) => !line.startsWith("#"))
217
- .join("\n");
218
- }