onto-mcp 0.3.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 (447) hide show
  1. package/.onto/authority/core-lens-registry.yaml +134 -0
  2. package/.onto/authority/core-lexicon.yaml +1562 -0
  3. package/.onto/authority/diagnostic-codes.yaml +94 -0
  4. package/.onto/domains/accounting/competency_qs.md +384 -0
  5. package/.onto/domains/accounting/concepts.md +186 -0
  6. package/.onto/domains/accounting/conciseness_rules.md +160 -0
  7. package/.onto/domains/accounting/dependency_rules.md +239 -0
  8. package/.onto/domains/accounting/domain_scope.md +213 -0
  9. package/.onto/domains/accounting/extension_cases.md +416 -0
  10. package/.onto/domains/accounting/logic_rules.md +226 -0
  11. package/.onto/domains/accounting/structure_spec.md +298 -0
  12. package/.onto/domains/accounting-kr/competency_qs.md +562 -0
  13. package/.onto/domains/accounting-kr/concepts.md +187 -0
  14. package/.onto/domains/accounting-kr/conciseness_rules.md +125 -0
  15. package/.onto/domains/accounting-kr/dependency_rules.md +93 -0
  16. package/.onto/domains/accounting-kr/domain_scope.md +140 -0
  17. package/.onto/domains/accounting-kr/extension_cases.md +343 -0
  18. package/.onto/domains/accounting-kr/logic_rules.md +160 -0
  19. package/.onto/domains/accounting-kr/structure_spec.md +85 -0
  20. package/.onto/domains/business/competency_qs.md +263 -0
  21. package/.onto/domains/business/concepts.md +200 -0
  22. package/.onto/domains/business/conciseness_rules.md +135 -0
  23. package/.onto/domains/business/dependency_rules.md +113 -0
  24. package/.onto/domains/business/domain_scope.md +240 -0
  25. package/.onto/domains/business/extension_cases.md +249 -0
  26. package/.onto/domains/business/logic_rules.md +134 -0
  27. package/.onto/domains/business/structure_spec.md +114 -0
  28. package/.onto/domains/finance/competency_qs.md +362 -0
  29. package/.onto/domains/finance/concepts.md +194 -0
  30. package/.onto/domains/finance/conciseness_rules.md +155 -0
  31. package/.onto/domains/finance/dependency_rules.md +171 -0
  32. package/.onto/domains/finance/domain_scope.md +215 -0
  33. package/.onto/domains/finance/extension_cases.md +350 -0
  34. package/.onto/domains/finance/logic_rules.md +191 -0
  35. package/.onto/domains/finance/structure_spec.md +182 -0
  36. package/.onto/domains/llm-native-development/competency_qs.md +430 -0
  37. package/.onto/domains/llm-native-development/concepts.md +242 -0
  38. package/.onto/domains/llm-native-development/conciseness_rules.md +163 -0
  39. package/.onto/domains/llm-native-development/dependency_rules.md +216 -0
  40. package/.onto/domains/llm-native-development/domain_scope.md +197 -0
  41. package/.onto/domains/llm-native-development/extension_cases.md +474 -0
  42. package/.onto/domains/llm-native-development/logic_rules.md +123 -0
  43. package/.onto/domains/llm-native-development/prompt_interface.md +49 -0
  44. package/.onto/domains/llm-native-development/structure_spec.md +245 -0
  45. package/.onto/domains/market-intelligence/competency_qs.md +274 -0
  46. package/.onto/domains/market-intelligence/concepts.md +233 -0
  47. package/.onto/domains/market-intelligence/conciseness_rules.md +165 -0
  48. package/.onto/domains/market-intelligence/dependency_rules.md +197 -0
  49. package/.onto/domains/market-intelligence/domain_scope.md +231 -0
  50. package/.onto/domains/market-intelligence/extension_cases.md +425 -0
  51. package/.onto/domains/market-intelligence/logic_rules.md +247 -0
  52. package/.onto/domains/market-intelligence/structure_spec.md +209 -0
  53. package/.onto/domains/ontology/competency_qs.md +394 -0
  54. package/.onto/domains/ontology/concepts.md +172 -0
  55. package/.onto/domains/ontology/conciseness_rules.md +134 -0
  56. package/.onto/domains/ontology/dependency_rules.md +125 -0
  57. package/.onto/domains/ontology/domain_scope.md +114 -0
  58. package/.onto/domains/ontology/extension_cases.md +501 -0
  59. package/.onto/domains/ontology/logic_rules.md +114 -0
  60. package/.onto/domains/ontology/problem_framing_profile.md +67 -0
  61. package/.onto/domains/ontology/structure_spec.md +115 -0
  62. package/.onto/domains/palantir-foundry/RESEARCH_NOTES.md +911 -0
  63. package/.onto/domains/palantir-foundry/competency_qs.md +191 -0
  64. package/.onto/domains/palantir-foundry/competitive_comparison.md +329 -0
  65. package/.onto/domains/palantir-foundry/concepts.md +197 -0
  66. package/.onto/domains/palantir-foundry/conciseness_rules.md +245 -0
  67. package/.onto/domains/palantir-foundry/dependency_rules.md +135 -0
  68. package/.onto/domains/palantir-foundry/domain_scope.md +395 -0
  69. package/.onto/domains/palantir-foundry/extension_cases.md +210 -0
  70. package/.onto/domains/palantir-foundry/logic_rules.md +172 -0
  71. package/.onto/domains/palantir-foundry/structure_spec.md +291 -0
  72. package/.onto/domains/software-engineering/competency_qs.md +538 -0
  73. package/.onto/domains/software-engineering/concepts.md +238 -0
  74. package/.onto/domains/software-engineering/conciseness_rules.md +167 -0
  75. package/.onto/domains/software-engineering/dependency_rules.md +216 -0
  76. package/.onto/domains/software-engineering/domain_scope.md +183 -0
  77. package/.onto/domains/software-engineering/extension_cases.md +551 -0
  78. package/.onto/domains/software-engineering/logic_rules.md +240 -0
  79. package/.onto/domains/software-engineering/problem_framing_profile.md +68 -0
  80. package/.onto/domains/software-engineering/structure_spec.md +185 -0
  81. package/.onto/domains/ui-design/competency_qs.md +567 -0
  82. package/.onto/domains/ui-design/concepts.md +194 -0
  83. package/.onto/domains/ui-design/conciseness_rules.md +190 -0
  84. package/.onto/domains/ui-design/dependency_rules.md +323 -0
  85. package/.onto/domains/ui-design/domain_scope.md +340 -0
  86. package/.onto/domains/ui-design/extension_cases.md +563 -0
  87. package/.onto/domains/ui-design/logic_rules.md +349 -0
  88. package/.onto/domains/ui-design/structure_spec.md +252 -0
  89. package/.onto/domains/visual-design/competency_qs.md +472 -0
  90. package/.onto/domains/visual-design/concepts.md +147 -0
  91. package/.onto/domains/visual-design/conciseness_rules.md +186 -0
  92. package/.onto/domains/visual-design/dependency_rules.md +282 -0
  93. package/.onto/domains/visual-design/domain_scope.md +290 -0
  94. package/.onto/domains/visual-design/extension_cases.md +480 -0
  95. package/.onto/domains/visual-design/logic_rules.md +232 -0
  96. package/.onto/domains/visual-design/structure_spec.md +213 -0
  97. package/.onto/principles/llm-native-development-guideline.md +401 -0
  98. package/.onto/principles/llm-runtime-interface-principles.md +665 -0
  99. package/.onto/principles/non-specialist-communication-guideline.md +74 -0
  100. package/.onto/principles/ontology-as-code-guideline.md +243 -0
  101. package/.onto/principles/ontology-as-code-naming-charter.md +130 -0
  102. package/.onto/principles/product-locality-principle.md +129 -0
  103. package/.onto/principles/productization-charter.md +569 -0
  104. package/.onto/processes/evolve/material-kind-adapter-contract.md +113 -0
  105. package/.onto/processes/reconstruct/reconstruct-boundary-contract.md +366 -0
  106. package/.onto/processes/reconstruct/source-profile-contract.md +107 -0
  107. package/.onto/processes/reconstruct/source-profiles/code.md +72 -0
  108. package/.onto/processes/reconstruct/source-profiles/database.md +74 -0
  109. package/.onto/processes/reconstruct/source-profiles/document.md +71 -0
  110. package/.onto/processes/reconstruct/source-profiles/spreadsheet.md +79 -0
  111. package/.onto/processes/review/binding-contract.md +270 -0
  112. package/.onto/processes/review/execution-preparation-artifacts.md +281 -0
  113. package/.onto/processes/review/interpretation-contract.md +245 -0
  114. package/.onto/processes/review/issue-stance-deliberation-contract.md +761 -0
  115. package/.onto/processes/review/lens-prompt-contract.md +402 -0
  116. package/.onto/processes/review/lens-registry.md +127 -0
  117. package/.onto/processes/review/pre-dispatch-contracts.md +428 -0
  118. package/.onto/processes/review/productized-live-path.md +398 -0
  119. package/.onto/processes/review/prompt-execution-runner-contract.md +187 -0
  120. package/.onto/processes/review/record-contract.md +427 -0
  121. package/.onto/processes/review/record-field-mapping.md +337 -0
  122. package/.onto/processes/review/review-context-manifest-contract.md +356 -0
  123. package/.onto/processes/review/review-execution-ux-contract.md +809 -0
  124. package/.onto/processes/review/review-target-profile-contract.md +259 -0
  125. package/.onto/processes/review/shared-phenomenon-contract.md +129 -0
  126. package/.onto/processes/review/synthesize-prompt-contract.md +343 -0
  127. package/.onto/processes/shared/target-material-kind-contract.md +198 -0
  128. package/.onto/roles/axiology.md +81 -0
  129. package/.onto/roles/conciseness.md +36 -0
  130. package/.onto/roles/coverage.md +34 -0
  131. package/.onto/roles/dependency.md +37 -0
  132. package/.onto/roles/evolution.md +35 -0
  133. package/.onto/roles/logic.md +104 -0
  134. package/.onto/roles/pragmatics.md +32 -0
  135. package/.onto/roles/semantics.md +36 -0
  136. package/.onto/roles/structure.md +33 -0
  137. package/.onto/roles/synthesize.md +92 -0
  138. package/AGENTS.md +240 -0
  139. package/CLAUDE.md +39 -0
  140. package/README.md +287 -0
  141. package/bin/onto +92 -0
  142. package/dist/cli.js +101 -0
  143. package/dist/core-api/reconstruct-api.js +222 -0
  144. package/dist/core-api/review-api.js +1271 -0
  145. package/dist/core-runtime/cli/assemble-review-record.js +431 -0
  146. package/dist/core-runtime/cli/bootstrap-review-binding.js +186 -0
  147. package/dist/core-runtime/cli/codex-nested-dispatch.js +226 -0
  148. package/dist/core-runtime/cli/codex-nested-dispatch.test.js +390 -0
  149. package/dist/core-runtime/cli/codex-nested-teamlead-executor.js +464 -0
  150. package/dist/core-runtime/cli/codex-nested-teamlead-executor.test.js +335 -0
  151. package/dist/core-runtime/cli/codex-review-unit-executor.js +228 -0
  152. package/dist/core-runtime/cli/complete-review-session.js +64 -0
  153. package/dist/core-runtime/cli/complexity-assessment.js +153 -0
  154. package/dist/core-runtime/cli/coordinator-helpers.js +583 -0
  155. package/dist/core-runtime/cli/coordinator-state-machine-deliberation.test.js +167 -0
  156. package/dist/core-runtime/cli/coordinator-state-machine.js +794 -0
  157. package/dist/core-runtime/cli/e2e-codex-multi-agent-fixes.test.js +615 -0
  158. package/dist/core-runtime/cli/e2e-start-review-session.test.js +312 -0
  159. package/dist/core-runtime/cli/health.js +44 -0
  160. package/dist/core-runtime/cli/inline-http-review-unit-executor.js +656 -0
  161. package/dist/core-runtime/cli/inline-http-review-unit-executor.test.js +567 -0
  162. package/dist/core-runtime/cli/materialize-review-execution-preparation.js +104 -0
  163. package/dist/core-runtime/cli/materialize-review-prompt-packets.js +952 -0
  164. package/dist/core-runtime/cli/migrate-session-roots.js +118 -0
  165. package/dist/core-runtime/cli/mock-review-unit-executor.js +285 -0
  166. package/dist/core-runtime/cli/onto-tools.js +369 -0
  167. package/dist/core-runtime/cli/prepare-review-session.js +272 -0
  168. package/dist/core-runtime/cli/render-review-final-output.js +350 -0
  169. package/dist/core-runtime/cli/repo-layout-migration-replace.smoke.test.js +106 -0
  170. package/dist/core-runtime/cli/review-invoke-auto-resolution.test.js +268 -0
  171. package/dist/core-runtime/cli/review-invoke-coordinator-topology.test.js +136 -0
  172. package/dist/core-runtime/cli/review-invoke-resolver-caching.test.js +201 -0
  173. package/dist/core-runtime/cli/review-invoke-topology-dispatch.test.js +192 -0
  174. package/dist/core-runtime/cli/review-invoke.js +2030 -0
  175. package/dist/core-runtime/cli/run-review-prompt-execution.js +2152 -0
  176. package/dist/core-runtime/cli/session-root-guard.js +168 -0
  177. package/dist/core-runtime/cli/spawn-watcher.js +173 -0
  178. package/dist/core-runtime/cli/spawn-watcher.test.js +457 -0
  179. package/dist/core-runtime/cli/start-review-session.js +68 -0
  180. package/dist/core-runtime/cli/strip-wrapping-code-fence.js +56 -0
  181. package/dist/core-runtime/cli/strip-wrapping-code-fence.test.js +79 -0
  182. package/dist/core-runtime/cli/teamcreate-lens-deliberation-executor.js +412 -0
  183. package/dist/core-runtime/cli/teamcreate-lens-deliberation-executor.test.js +351 -0
  184. package/dist/core-runtime/cli/topology-executor-mapping.js +139 -0
  185. package/dist/core-runtime/cli/topology-executor-mapping.test.js +173 -0
  186. package/dist/core-runtime/cli/write-review-interpretation.js +81 -0
  187. package/dist/core-runtime/config/onto-config-cli.js +278 -0
  188. package/dist/core-runtime/config/onto-config-key-path.js +288 -0
  189. package/dist/core-runtime/config/onto-config-key-path.test.js +195 -0
  190. package/dist/core-runtime/config/onto-config-preview.js +108 -0
  191. package/dist/core-runtime/config/onto-config-preview.test.js +132 -0
  192. package/dist/core-runtime/discovery/config-chain.js +118 -0
  193. package/dist/core-runtime/discovery/config-chain.test.js +103 -0
  194. package/dist/core-runtime/discovery/config-profile.js +199 -0
  195. package/dist/core-runtime/discovery/config-profile.test.js +233 -0
  196. package/dist/core-runtime/discovery/host-detection.js +33 -0
  197. package/dist/core-runtime/discovery/host-detection.test.js +186 -0
  198. package/dist/core-runtime/discovery/installation-paths.js +21 -0
  199. package/dist/core-runtime/discovery/installation-paths.test.js +65 -0
  200. package/dist/core-runtime/discovery/lens-registry.js +60 -0
  201. package/dist/core-runtime/discovery/lens-registry.test.js +81 -0
  202. package/dist/core-runtime/discovery/onto-home.js +71 -0
  203. package/dist/core-runtime/discovery/path-normalization.js +28 -0
  204. package/dist/core-runtime/discovery/path-normalization.test.js +22 -0
  205. package/dist/core-runtime/discovery/plugin-path.js +72 -0
  206. package/dist/core-runtime/discovery/plugin-path.test.js +95 -0
  207. package/dist/core-runtime/discovery/project-root.js +47 -0
  208. package/dist/core-runtime/discovery/settings-chain.js +353 -0
  209. package/dist/core-runtime/discovery/walk-up.js +17 -0
  210. package/dist/core-runtime/evolve/adapters/code-product/compile/compile-defense.js +344 -0
  211. package/dist/core-runtime/evolve/adapters/code-product/compile/compile-defense.test.js +915 -0
  212. package/dist/core-runtime/evolve/adapters/code-product/compile/compile.js +564 -0
  213. package/dist/core-runtime/evolve/adapters/code-product/compile/compile.test.js +708 -0
  214. package/dist/core-runtime/evolve/adapters/code-product/parsers/brief-parser.js +165 -0
  215. package/dist/core-runtime/evolve/adapters/code-product/parsers/brief-parser.test.js +227 -0
  216. package/dist/core-runtime/evolve/adapters/code-product/validators/validate.js +59 -0
  217. package/dist/core-runtime/evolve/adapters/code-product/validators/validate.test.js +205 -0
  218. package/dist/core-runtime/evolve/adapters/methodology/adapter.js +16 -0
  219. package/dist/core-runtime/evolve/adapters/methodology/adapter.test.js +9 -0
  220. package/dist/core-runtime/evolve/adapters/methodology/perspectives/authority-consistency.js +298 -0
  221. package/dist/core-runtime/evolve/adapters/methodology/perspectives/authority-consistency.test.js +70 -0
  222. package/dist/core-runtime/evolve/adapters/methodology/scope-types/process.js +46 -0
  223. package/dist/core-runtime/evolve/adapters/methodology/scope-types/process.test.js +73 -0
  224. package/dist/core-runtime/evolve/adapters/registry.js +47 -0
  225. package/dist/core-runtime/evolve/adapters/registry.test.js +67 -0
  226. package/dist/core-runtime/evolve/cli.js +256 -0
  227. package/dist/core-runtime/evolve/commands/align.js +194 -0
  228. package/dist/core-runtime/evolve/commands/align.test.js +82 -0
  229. package/dist/core-runtime/evolve/commands/apply.js +161 -0
  230. package/dist/core-runtime/evolve/commands/apply.test.js +138 -0
  231. package/dist/core-runtime/evolve/commands/close.js +39 -0
  232. package/dist/core-runtime/evolve/commands/close.test.js +99 -0
  233. package/dist/core-runtime/evolve/commands/defer.js +40 -0
  234. package/dist/core-runtime/evolve/commands/defer.test.js +134 -0
  235. package/dist/core-runtime/evolve/commands/draft.js +323 -0
  236. package/dist/core-runtime/evolve/commands/draft.test.js +178 -0
  237. package/dist/core-runtime/evolve/commands/e2e-evolve-full-cycle.test.js +208 -0
  238. package/dist/core-runtime/evolve/commands/error-messages.js +125 -0
  239. package/dist/core-runtime/evolve/commands/error-messages.test.js +167 -0
  240. package/dist/core-runtime/evolve/commands/propose-align.js +222 -0
  241. package/dist/core-runtime/evolve/commands/propose-align.test.js +136 -0
  242. package/dist/core-runtime/evolve/commands/reconstruct.js +330 -0
  243. package/dist/core-runtime/evolve/commands/reconstruct.test.js +278 -0
  244. package/dist/core-runtime/evolve/commands/shared.js +22 -0
  245. package/dist/core-runtime/evolve/commands/stale-check.js +103 -0
  246. package/dist/core-runtime/evolve/commands/stale-check.test.js +84 -0
  247. package/dist/core-runtime/evolve/commands/start.js +887 -0
  248. package/dist/core-runtime/evolve/commands/start.test.js +396 -0
  249. package/dist/core-runtime/evolve/config/project-config.js +99 -0
  250. package/dist/core-runtime/evolve/config/project-config.test.js +170 -0
  251. package/dist/core-runtime/evolve/renderers/align-packet.js +280 -0
  252. package/dist/core-runtime/evolve/renderers/align-packet.test.js +332 -0
  253. package/dist/core-runtime/evolve/renderers/draft-packet.js +303 -0
  254. package/dist/core-runtime/evolve/renderers/draft-packet.test.js +377 -0
  255. package/dist/core-runtime/evolve/renderers/format.js +5 -0
  256. package/dist/core-runtime/evolve/renderers/scope-md.js +237 -0
  257. package/dist/core-runtime/evolve/renderers/scope-md.test.js +306 -0
  258. package/dist/core-runtime/govern/cli.js +369 -0
  259. package/dist/core-runtime/govern/cli.test.js +314 -0
  260. package/dist/core-runtime/govern/drift-engine.js +103 -0
  261. package/dist/core-runtime/govern/drift-engine.test.js +319 -0
  262. package/dist/core-runtime/govern/promote-principle.js +206 -0
  263. package/dist/core-runtime/govern/promote-principle.test.js +368 -0
  264. package/dist/core-runtime/govern/queue.js +81 -0
  265. package/dist/core-runtime/govern/types.js +16 -0
  266. package/dist/core-runtime/install/cli.js +530 -0
  267. package/dist/core-runtime/install/detect.js +128 -0
  268. package/dist/core-runtime/install/detect.test.js +155 -0
  269. package/dist/core-runtime/install/gitignore-update.js +74 -0
  270. package/dist/core-runtime/install/gitignore-update.test.js +64 -0
  271. package/dist/core-runtime/install/install-integration.test.js +373 -0
  272. package/dist/core-runtime/install/prompts.js +389 -0
  273. package/dist/core-runtime/install/prompts.test.js +293 -0
  274. package/dist/core-runtime/install/types.js +26 -0
  275. package/dist/core-runtime/install/validation.js +295 -0
  276. package/dist/core-runtime/install/validation.test.js +313 -0
  277. package/dist/core-runtime/install/writer.js +254 -0
  278. package/dist/core-runtime/install/writer.test.js +218 -0
  279. package/dist/core-runtime/learning/extractor.js +461 -0
  280. package/dist/core-runtime/learning/feedback.js +179 -0
  281. package/dist/core-runtime/learning/health-report.js +165 -0
  282. package/dist/core-runtime/learning/health-report.test.js +169 -0
  283. package/dist/core-runtime/learning/loader.js +388 -0
  284. package/dist/core-runtime/learning/loader.test.js +102 -0
  285. package/dist/core-runtime/learning/promote/apply-state.js +240 -0
  286. package/dist/core-runtime/learning/promote/audit-obligation.js +195 -0
  287. package/dist/core-runtime/learning/promote/collector.js +432 -0
  288. package/dist/core-runtime/learning/promote/degraded-state.js +125 -0
  289. package/dist/core-runtime/learning/promote/domain-doc-proposer.js +166 -0
  290. package/dist/core-runtime/learning/promote/e2e-promote.test.js +6385 -0
  291. package/dist/core-runtime/learning/promote/health-snapshot.js +150 -0
  292. package/dist/core-runtime/learning/promote/insight-reclassifier.js +544 -0
  293. package/dist/core-runtime/learning/promote/judgment-auditor.js +517 -0
  294. package/dist/core-runtime/learning/promote/panel-reviewer.js +1158 -0
  295. package/dist/core-runtime/learning/promote/promote-executor.js +1675 -0
  296. package/dist/core-runtime/learning/promote/promoter.js +307 -0
  297. package/dist/core-runtime/learning/promote/retirement.js +122 -0
  298. package/dist/core-runtime/learning/promote/types.js +23 -0
  299. package/dist/core-runtime/learning/prompt-sections.js +51 -0
  300. package/dist/core-runtime/learning/shared/artifact-registry-init.js +45 -0
  301. package/dist/core-runtime/learning/shared/artifact-registry.js +254 -0
  302. package/dist/core-runtime/learning/shared/audit-obligation-kernel.js +73 -0
  303. package/dist/core-runtime/learning/shared/audit-state.js +99 -0
  304. package/dist/core-runtime/learning/shared/duplicate-check.js +28 -0
  305. package/dist/core-runtime/learning/shared/llm-caller.js +831 -0
  306. package/dist/core-runtime/learning/shared/llm-caller.test.js +601 -0
  307. package/dist/core-runtime/learning/shared/llm-tool-loop.js +393 -0
  308. package/dist/core-runtime/learning/shared/mode.js +25 -0
  309. package/dist/core-runtime/learning/shared/paths.js +84 -0
  310. package/dist/core-runtime/learning/shared/paths.test.js +79 -0
  311. package/dist/core-runtime/learning/shared/patterns.js +37 -0
  312. package/dist/core-runtime/learning/shared/recoverability.js +355 -0
  313. package/dist/core-runtime/learning/shared/recovery-context.js +374 -0
  314. package/dist/core-runtime/learning/shared/scope.js +1 -0
  315. package/dist/core-runtime/learning/shared/semantic-classifier.js +94 -0
  316. package/dist/core-runtime/learning/shared/specs/apply-execution-state-spec.js +42 -0
  317. package/dist/core-runtime/learning/shared/specs/audit-state-spec.js +37 -0
  318. package/dist/core-runtime/learning/shared/specs/backup-metadata-spec.js +39 -0
  319. package/dist/core-runtime/learning/shared/specs/emergency-log-spec.js +41 -0
  320. package/dist/core-runtime/learning/shared/specs/layout-version-spec.js +38 -0
  321. package/dist/core-runtime/learning/shared/specs/promote-decisions-spec.js +43 -0
  322. package/dist/core-runtime/learning/shared/specs/promote-report-spec.js +113 -0
  323. package/dist/core-runtime/learning/shared/specs/prune-log-spec.js +36 -0
  324. package/dist/core-runtime/learning/shared/specs/recovery-resolution-spec.js +48 -0
  325. package/dist/core-runtime/learning/shared/specs/restore-manifest-spec.js +43 -0
  326. package/dist/core-runtime/learning/shared/specs/spec-helpers.js +64 -0
  327. package/dist/core-runtime/learning/usage-tracker.js +190 -0
  328. package/dist/core-runtime/learning/usage-tracker.test.js +176 -0
  329. package/dist/core-runtime/llm/llm-caller.js +649 -0
  330. package/dist/core-runtime/llm/llm-tool-loop.js +401 -0
  331. package/dist/core-runtime/llm/model-switcher.js +62 -0
  332. package/dist/core-runtime/logger.js +22 -0
  333. package/dist/core-runtime/onboard/detect-review-axes.js +122 -0
  334. package/dist/core-runtime/onboard/detect-review-axes.test.js +127 -0
  335. package/dist/core-runtime/onboard/write-review-block.js +188 -0
  336. package/dist/core-runtime/onboard/write-review-block.test.js +240 -0
  337. package/dist/core-runtime/readers/brownfield-builder.js +150 -0
  338. package/dist/core-runtime/readers/brownfield-builder.test.js +136 -0
  339. package/dist/core-runtime/readers/code-chunk-collector.js +53 -0
  340. package/dist/core-runtime/readers/code-chunk-collector.test.js +136 -0
  341. package/dist/core-runtime/readers/file-utils.js +240 -0
  342. package/dist/core-runtime/readers/file-utils.test.js +146 -0
  343. package/dist/core-runtime/readers/lexicon-citation-check.js +93 -0
  344. package/dist/core-runtime/readers/lexicon-citation-check.test.js +77 -0
  345. package/dist/core-runtime/readers/mcp-figma.js +30 -0
  346. package/dist/core-runtime/readers/mcp-figma.test.js +82 -0
  347. package/dist/core-runtime/readers/mcp-generic.js +31 -0
  348. package/dist/core-runtime/readers/mcp-generic.test.js +76 -0
  349. package/dist/core-runtime/readers/ontology-index.js +148 -0
  350. package/dist/core-runtime/readers/ontology-index.test.js +245 -0
  351. package/dist/core-runtime/readers/ontology-query.js +168 -0
  352. package/dist/core-runtime/readers/ontology-query.test.js +311 -0
  353. package/dist/core-runtime/readers/ontology-resolve.js +48 -0
  354. package/dist/core-runtime/readers/ontology-resolve.test.js +48 -0
  355. package/dist/core-runtime/readers/patterns/index.js +7 -0
  356. package/dist/core-runtime/readers/review-log.js +213 -0
  357. package/dist/core-runtime/readers/review-log.test.js +313 -0
  358. package/dist/core-runtime/readers/scan-local.js +102 -0
  359. package/dist/core-runtime/readers/scan-local.test.js +102 -0
  360. package/dist/core-runtime/readers/scan-tarball.js +121 -0
  361. package/dist/core-runtime/readers/scan-tarball.test.js +283 -0
  362. package/dist/core-runtime/readers/scan-vault.js +34 -0
  363. package/dist/core-runtime/readers/scan-vault.test.js +81 -0
  364. package/dist/core-runtime/readers/types.js +42 -0
  365. package/dist/core-runtime/readers/types.test.js +94 -0
  366. package/dist/core-runtime/readers/viewpoint-collectors.js +229 -0
  367. package/dist/core-runtime/reconstruct/artifact-types.js +1 -0
  368. package/dist/core-runtime/reconstruct/directive-validation.js +123 -0
  369. package/dist/core-runtime/reconstruct/materialize-preparation.js +251 -0
  370. package/dist/core-runtime/reconstruct/record.js +198 -0
  371. package/dist/core-runtime/reconstruct/run.js +578 -0
  372. package/dist/core-runtime/reconstruct/seed-candidate-validation.js +356 -0
  373. package/dist/core-runtime/reconstruct/source-observations.js +62 -0
  374. package/dist/core-runtime/reconstruct/source-profiles.js +73 -0
  375. package/dist/core-runtime/release-channel/release-channel.js +90 -0
  376. package/dist/core-runtime/review/artifact-types.js +13 -0
  377. package/dist/core-runtime/review/citation-audit.js +204 -0
  378. package/dist/core-runtime/review/citation-audit.test.js +165 -0
  379. package/dist/core-runtime/review/controlled-lens-deliberation.js +125 -0
  380. package/dist/core-runtime/review/execution-plan-resolver.js +247 -0
  381. package/dist/core-runtime/review/execution-plan-resolver.test.js +243 -0
  382. package/dist/core-runtime/review/execution-topology-resolver-axis-first.test.js +246 -0
  383. package/dist/core-runtime/review/execution-topology-resolver.js +401 -0
  384. package/dist/core-runtime/review/execution-topology-resolver.test.js +315 -0
  385. package/dist/core-runtime/review/failure-records.js +57 -0
  386. package/dist/core-runtime/review/inline-context-embedder.js +141 -0
  387. package/dist/core-runtime/review/inline-context-embedder.test.js +154 -0
  388. package/dist/core-runtime/review/issue-artifact-runtime.js +859 -0
  389. package/dist/core-runtime/review/legacy-mode-policy.js +88 -0
  390. package/dist/core-runtime/review/lens-completion-policy.js +17 -0
  391. package/dist/core-runtime/review/materializers-effort-persist.test.js +79 -0
  392. package/dist/core-runtime/review/materializers.js +963 -0
  393. package/dist/core-runtime/review/ontology-path-classifier.js +179 -0
  394. package/dist/core-runtime/review/ontology-path-classifier.test.js +216 -0
  395. package/dist/core-runtime/review/packet-boundary-policy.js +215 -0
  396. package/dist/core-runtime/review/packet-boundary-policy.test.js +107 -0
  397. package/dist/core-runtime/review/participating-lens-paths.js +61 -0
  398. package/dist/core-runtime/review/participating-lens-paths.test.js +73 -0
  399. package/dist/core-runtime/review/review-artifact-utils.js +287 -0
  400. package/dist/core-runtime/review/review-config-legacy-translate.js +244 -0
  401. package/dist/core-runtime/review/review-config-legacy-translate.test.js +161 -0
  402. package/dist/core-runtime/review/review-config-validator.js +289 -0
  403. package/dist/core-runtime/review/review-config-validator.test.js +236 -0
  404. package/dist/core-runtime/review/review-execution-profile.js +193 -0
  405. package/dist/core-runtime/review/review-execution-route.js +52 -0
  406. package/dist/core-runtime/review/review-progress-contract.js +123 -0
  407. package/dist/core-runtime/review/review-record-validation.js +251 -0
  408. package/dist/core-runtime/review/review-result-classification.js +379 -0
  409. package/dist/core-runtime/review/review-state-machine.js +39 -0
  410. package/dist/core-runtime/review/route-visibility.js +125 -0
  411. package/dist/core-runtime/review/shape-pipeline-audit.test.js +311 -0
  412. package/dist/core-runtime/review/shape-to-topology-id.js +117 -0
  413. package/dist/core-runtime/review/shape-to-topology-id.test.js +132 -0
  414. package/dist/core-runtime/review/topology-shape-derivation.js +155 -0
  415. package/dist/core-runtime/review/topology-shape-derivation.test.js +195 -0
  416. package/dist/core-runtime/scope-runtime/constants.js +12 -0
  417. package/dist/core-runtime/scope-runtime/constraint-pool.js +166 -0
  418. package/dist/core-runtime/scope-runtime/constraint-pool.test.js +674 -0
  419. package/dist/core-runtime/scope-runtime/domain-validation-log.js +135 -0
  420. package/dist/core-runtime/scope-runtime/domain-validation-log.test.js +156 -0
  421. package/dist/core-runtime/scope-runtime/eval-persistence.js +65 -0
  422. package/dist/core-runtime/scope-runtime/eval-persistence.test.js +84 -0
  423. package/dist/core-runtime/scope-runtime/event-pipeline.js +64 -0
  424. package/dist/core-runtime/scope-runtime/event-pipeline.test.js +450 -0
  425. package/dist/core-runtime/scope-runtime/event-store.js +39 -0
  426. package/dist/core-runtime/scope-runtime/event-store.test.js +95 -0
  427. package/dist/core-runtime/scope-runtime/gate-guard.js +348 -0
  428. package/dist/core-runtime/scope-runtime/gate-guard.test.js +1047 -0
  429. package/dist/core-runtime/scope-runtime/hash.js +4 -0
  430. package/dist/core-runtime/scope-runtime/hash.test.js +33 -0
  431. package/dist/core-runtime/scope-runtime/id.js +4 -0
  432. package/dist/core-runtime/scope-runtime/id.test.js +17 -0
  433. package/dist/core-runtime/scope-runtime/reducer.js +297 -0
  434. package/dist/core-runtime/scope-runtime/reducer.test.js +759 -0
  435. package/dist/core-runtime/scope-runtime/scope-manager.js +161 -0
  436. package/dist/core-runtime/scope-runtime/state-machine.js +309 -0
  437. package/dist/core-runtime/scope-runtime/state-machine.test.js +704 -0
  438. package/dist/core-runtime/scope-runtime/types.js +116 -0
  439. package/dist/core-runtime/scope-runtime/types.test.js +69 -0
  440. package/dist/core-runtime/target-material-kind.js +256 -0
  441. package/dist/core-runtime/translate/render-for-user.js +169 -0
  442. package/dist/core-runtime/translate/render-for-user.test.js +122 -0
  443. package/dist/mcp/server.js +1011 -0
  444. package/dist/mcp/tool-schemas.js +93 -0
  445. package/dist/providers/capability-contract.js +1 -0
  446. package/package.json +68 -0
  447. package/settings.example.json +33 -0
@@ -0,0 +1,254 @@
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
+ }
@@ -0,0 +1,218 @@
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
+ }