onto-mcp 0.3.2 → 0.4.1

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 +149 -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 +174 -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 +214 -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
@@ -0,0 +1,174 @@
1
+ import { execFileSync } from "node:child_process";
2
+ import { isCommandOnPath } from "./path-scan.js";
3
+ export const defaultCommandRunner = {
4
+ exists: (command) => isCommandOnPath(command),
5
+ run: (command, args, env) => {
6
+ try {
7
+ const stdout = execFileSync(command, args, {
8
+ encoding: "utf8",
9
+ ...(env ? { env: { ...process.env, ...env } } : {}),
10
+ });
11
+ return { status: 0, stdout, stderr: "" };
12
+ }
13
+ catch (error) {
14
+ const err = error;
15
+ return {
16
+ status: typeof err.status === "number" ? err.status : 1,
17
+ stdout: typeof err.stdout === "string" ? err.stdout : "",
18
+ stderr: typeof err.stderr === "string"
19
+ ? err.stderr
20
+ : error instanceof Error
21
+ ? error.message
22
+ : String(error),
23
+ };
24
+ }
25
+ },
26
+ };
27
+ function probeRegistered(spec, runner, entry) {
28
+ const result = runner.run(spec.cli, spec.listArgs(), spec.commandEnv);
29
+ if (result.status !== 0)
30
+ return "unknown";
31
+ const haystack = `${result.stdout}\n${result.stderr}`;
32
+ // CLIs print one server name per line; a word-boundary match avoids
33
+ // false positives from substrings of other server names.
34
+ const pattern = new RegExp(`(^|[^\\w-])${escapeRegExp(entry.name)}([^\\w-]|$)`, "m");
35
+ return pattern.test(haystack) ? "present" : "absent";
36
+ }
37
+ function escapeRegExp(value) {
38
+ return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
39
+ }
40
+ export function createCliHost(spec, runner = defaultCommandRunner) {
41
+ const detect = () => (runner.exists(spec.cli) ? "cli" : "absent");
42
+ const withNote = (summary) => spec.targetNote ? `${summary} (${spec.targetNote})` : summary;
43
+ return {
44
+ id: spec.id,
45
+ displayName: spec.displayName,
46
+ detect,
47
+ plan(entry) {
48
+ const detection = detect();
49
+ if (detection === "absent") {
50
+ return {
51
+ hostId: spec.id,
52
+ displayName: spec.displayName,
53
+ detection,
54
+ method: "manual",
55
+ summary: `${spec.cli} CLI not found — manual step required`,
56
+ manualInstructions: spec.manualInstructions(entry),
57
+ };
58
+ }
59
+ const commandLine = `${spec.cli} ${spec.addArgs(entry).join(" ")}`;
60
+ return {
61
+ hostId: spec.id,
62
+ displayName: spec.displayName,
63
+ detection,
64
+ method: "cli",
65
+ summary: withNote(`Run: ${commandLine}`),
66
+ commandLine,
67
+ };
68
+ },
69
+ async apply(entry, options) {
70
+ const base = { hostId: spec.id, displayName: spec.displayName };
71
+ if (detect() === "absent") {
72
+ return { ...base, outcome: "manual", detail: spec.manualInstructions(entry) };
73
+ }
74
+ const before = probeRegistered(spec, runner, entry);
75
+ if (before === "present" && !options.force) {
76
+ return {
77
+ ...base,
78
+ outcome: "skipped",
79
+ detail: `${entry.name} already registered (use --force to re-add)`,
80
+ };
81
+ }
82
+ if (before === "present" && options.force) {
83
+ runner.run(spec.cli, spec.removeArgs(entry), spec.commandEnv); // best effort
84
+ }
85
+ const result = runner.run(spec.cli, spec.addArgs(entry), spec.commandEnv);
86
+ if (result.status !== 0) {
87
+ return {
88
+ ...base,
89
+ outcome: "failed",
90
+ detail: (result.stderr || result.stdout || "command failed").trim(),
91
+ };
92
+ }
93
+ // Verify the add actually took effect. A CLI that exits 0 without
94
+ // registering (aliased/wrapper `claude`, wrong profile) must not be
95
+ // reported as success.
96
+ const after = probeRegistered(spec, runner, entry);
97
+ const outcome = before === "present" ? "updated" : "registered";
98
+ if (after === "present") {
99
+ return { ...base, outcome, detail: `${spec.cli} ${spec.addArgs(entry).join(" ")}` };
100
+ }
101
+ if (after === "unknown") {
102
+ return {
103
+ ...base,
104
+ outcome,
105
+ detail: `${spec.cli} ${spec.addArgs(entry).join(" ")} (could not verify via ${spec.cli} mcp list)`,
106
+ };
107
+ }
108
+ return {
109
+ ...base,
110
+ outcome: "failed",
111
+ detail: `${spec.cli} accepted the command but ${entry.name} is not listed afterward. ` +
112
+ `The ${spec.cli} on PATH may be an alias/wrapper or target a different profile` +
113
+ (spec.targetNote ? ` (${spec.targetNote})` : "") +
114
+ `. Try registering against the real CLI/profile directly.`,
115
+ };
116
+ },
117
+ };
118
+ }
119
+ /**
120
+ * Claude Code — registers at user scope so it applies across all projects.
121
+ *
122
+ * Config-dir resolution: explicit `configDir` wins; otherwise an ambient
123
+ * `CLAUDE_CONFIG_DIR` is honored (and shown in the plan); otherwise the claude
124
+ * default (`~/.claude`) applies.
125
+ */
126
+ export function createClaudeCodeHost(options = {}) {
127
+ const effectiveDir = options.configDir ?? process.env.CLAUDE_CONFIG_DIR;
128
+ const spec = {
129
+ id: "claude-code",
130
+ displayName: "Claude Code",
131
+ cli: "claude",
132
+ addArgs: (entry) => [
133
+ "mcp",
134
+ "add",
135
+ entry.name,
136
+ "-s",
137
+ "user",
138
+ "--",
139
+ entry.command,
140
+ ...entry.args,
141
+ ],
142
+ removeArgs: (entry) => ["mcp", "remove", entry.name, "-s", "user"],
143
+ listArgs: () => ["mcp", "list"],
144
+ manualInstructions: (entry) => `claude CLI not found. Install Claude Code, then run:\n` +
145
+ ` claude mcp add ${entry.name} -s user -- ${entry.command} ${entry.args.join(" ")}`,
146
+ ...(effectiveDir ? { commandEnv: { CLAUDE_CONFIG_DIR: effectiveDir } } : {}),
147
+ targetNote: effectiveDir ? `config dir: ${effectiveDir}` : "config dir: claude default (~/.claude)",
148
+ };
149
+ return createCliHost(spec, options.runner);
150
+ }
151
+ /** Codex CLI. */
152
+ export function createCodexHost(runner) {
153
+ return createCliHost({
154
+ id: "codex",
155
+ displayName: "Codex CLI",
156
+ cli: "codex",
157
+ addArgs: (entry) => [
158
+ "mcp",
159
+ "add",
160
+ entry.name,
161
+ "--",
162
+ entry.command,
163
+ ...entry.args,
164
+ ],
165
+ removeArgs: (entry) => ["mcp", "remove", entry.name],
166
+ listArgs: () => ["mcp", "list"],
167
+ manualInstructions: (entry) => `codex CLI not found. Either install Codex and run:\n` +
168
+ ` codex mcp add ${entry.name} -- ${entry.command} ${entry.args.join(" ")}\n` +
169
+ `or add this block to ~/.codex/config.toml:\n` +
170
+ ` [mcp_servers.${entry.name}]\n` +
171
+ ` command = "${entry.command}"\n` +
172
+ ` args = [${entry.args.map((a) => `"${a}"`).join(", ")}]`,
173
+ }, runner);
174
+ }
@@ -0,0 +1,22 @@
1
+ import { createClaudeCodeHost, createCodexHost } from "./cli-host.js";
2
+ import { claudeDesktopConfigPath, createJsonConfigHost, cursorConfigPath, } from "./json-config-host.js";
3
+ /**
4
+ * The four supported hosts in display order. CLI-backed hosts (Claude Code,
5
+ * Codex) come first; JSON-config hosts (Claude Desktop, Cursor) follow.
6
+ */
7
+ export function getDefaultHostTargets(options = {}) {
8
+ return [
9
+ createClaudeCodeHost(options.claudeConfigDir ? { configDir: options.claudeConfigDir } : {}),
10
+ createCodexHost(),
11
+ createJsonConfigHost({
12
+ id: "claude-desktop",
13
+ displayName: "Claude Desktop",
14
+ resolvePath: () => claudeDesktopConfigPath(),
15
+ }),
16
+ createJsonConfigHost({
17
+ id: "cursor",
18
+ displayName: "Cursor",
19
+ resolvePath: () => cursorConfigPath(),
20
+ }),
21
+ ];
22
+ }
@@ -0,0 +1,122 @@
1
+ import fs from "node:fs/promises";
2
+ import fsSync from "node:fs";
3
+ import path from "node:path";
4
+ import os from "node:os";
5
+ import { entryMatches, } from "./types.js";
6
+ /**
7
+ * Pure reconciliation: given the previously-parsed config (or undefined when the
8
+ * file is absent/empty), return the new config object and what changed. Our own
9
+ * `<name>` key is always brought to the desired value; sibling servers and other
10
+ * top-level keys are preserved.
11
+ */
12
+ export function reconcileMcpServers(existing, entry) {
13
+ const root = existing && typeof existing === "object" && !Array.isArray(existing)
14
+ ? { ...existing }
15
+ : {};
16
+ const priorServers = root.mcpServers && typeof root.mcpServers === "object" && !Array.isArray(root.mcpServers)
17
+ ? root.mcpServers
18
+ : {};
19
+ const servers = { ...priorServers };
20
+ const prior = servers[entry.name];
21
+ if (entryMatches(prior, entry)) {
22
+ return { config: root, outcome: "skipped" };
23
+ }
24
+ servers[entry.name] = { command: entry.command, args: [...entry.args] };
25
+ root.mcpServers = servers;
26
+ return {
27
+ config: root,
28
+ outcome: prior === undefined ? "registered" : "updated",
29
+ };
30
+ }
31
+ function defaultDetect(configPath) {
32
+ if (fsSync.existsSync(configPath))
33
+ return "config";
34
+ if (fsSync.existsSync(path.dirname(configPath)))
35
+ return "config";
36
+ return "absent";
37
+ }
38
+ async function readJsonIfPresent(filePath) {
39
+ let text;
40
+ try {
41
+ text = await fs.readFile(filePath, "utf8");
42
+ }
43
+ catch {
44
+ return undefined;
45
+ }
46
+ if (text.trim().length === 0)
47
+ return undefined;
48
+ try {
49
+ return JSON.parse(text);
50
+ }
51
+ catch (error) {
52
+ throw new Error(`Existing config at ${filePath} is not valid JSON: ${error instanceof Error ? error.message : String(error)}. Fix or remove it, then re-run.`);
53
+ }
54
+ }
55
+ export function createJsonConfigHost(spec) {
56
+ const detect = spec.detect ?? (() => defaultDetect(spec.resolvePath()));
57
+ return {
58
+ id: spec.id,
59
+ displayName: spec.displayName,
60
+ detect,
61
+ plan(entry) {
62
+ const targetPath = spec.resolvePath();
63
+ return {
64
+ hostId: spec.id,
65
+ displayName: spec.displayName,
66
+ detection: detect(),
67
+ method: "config",
68
+ summary: `Set mcpServers.${entry.name} → ${entry.command} ${entry.args.join(" ")}`.trim(),
69
+ targetPath,
70
+ };
71
+ },
72
+ async apply(entry, _options) {
73
+ const targetPath = spec.resolvePath();
74
+ try {
75
+ const existing = await readJsonIfPresent(targetPath);
76
+ const { config, outcome } = reconcileMcpServers(existing, entry);
77
+ if (outcome === "skipped") {
78
+ return {
79
+ hostId: spec.id,
80
+ displayName: spec.displayName,
81
+ outcome: "skipped",
82
+ detail: `${entry.name} already registered in ${targetPath}`,
83
+ };
84
+ }
85
+ await fs.mkdir(path.dirname(targetPath), { recursive: true });
86
+ await fs.writeFile(targetPath, `${JSON.stringify(config, null, 2)}\n`, "utf8");
87
+ return {
88
+ hostId: spec.id,
89
+ displayName: spec.displayName,
90
+ outcome,
91
+ detail: `${outcome === "registered" ? "Added" : "Updated"} ${entry.name} in ${targetPath}`,
92
+ };
93
+ }
94
+ catch (error) {
95
+ return {
96
+ hostId: spec.id,
97
+ displayName: spec.displayName,
98
+ outcome: "failed",
99
+ detail: error instanceof Error ? error.message : String(error),
100
+ };
101
+ }
102
+ },
103
+ };
104
+ }
105
+ /**
106
+ * Claude Desktop config path. macOS and Windows use app-data locations; other
107
+ * platforms fall back to a Linux-style XDG path (best effort).
108
+ */
109
+ export function claudeDesktopConfigPath(homedir = os.homedir(), platform = process.platform) {
110
+ if (platform === "darwin") {
111
+ return path.join(homedir, "Library", "Application Support", "Claude", "claude_desktop_config.json");
112
+ }
113
+ if (platform === "win32") {
114
+ const appData = process.env.APPDATA ?? path.join(homedir, "AppData", "Roaming");
115
+ return path.join(appData, "Claude", "claude_desktop_config.json");
116
+ }
117
+ return path.join(homedir, ".config", "Claude", "claude_desktop_config.json");
118
+ }
119
+ /** Cursor global MCP config path. */
120
+ export function cursorConfigPath(homedir = os.homedir()) {
121
+ return path.join(homedir, ".cursor", "mcp.json");
122
+ }
@@ -0,0 +1,26 @@
1
+ import fsSync from "node:fs";
2
+ import path from "node:path";
3
+ /**
4
+ * True when `command` resolves to an executable on PATH.
5
+ *
6
+ * Mirrors the PATH-scan strategy in
7
+ * `src/core-runtime/discovery/host-detection.ts` (`detectCodexBinaryAvailable`),
8
+ * but is generic and does not require any auth file. On Windows it also probes
9
+ * the common executable extensions.
10
+ */
11
+ export function isCommandOnPath(command, env = process.env) {
12
+ const pathEnv = env.PATH ?? env.Path ?? "";
13
+ const isWindows = process.platform === "win32";
14
+ const candidates = isWindows
15
+ ? [command, `${command}.cmd`, `${command}.exe`, `${command}.bat`]
16
+ : [command];
17
+ for (const dir of pathEnv.split(path.delimiter)) {
18
+ if (!dir)
19
+ continue;
20
+ for (const candidate of candidates) {
21
+ if (fsSync.existsSync(path.join(dir, candidate)))
22
+ return true;
23
+ }
24
+ }
25
+ return false;
26
+ }
@@ -0,0 +1,51 @@
1
+ import readline from "node:readline";
2
+ function createInterface() {
3
+ return readline.createInterface({ input: process.stdin, output: process.stdout });
4
+ }
5
+ function ask(rl, question) {
6
+ return new Promise((resolve) => rl.question(question, resolve));
7
+ }
8
+ /**
9
+ * Numbered multi-select. The user enters comma/space-separated numbers; empty
10
+ * input accepts `defaultSelectedIds`. Returns the chosen choice ids in list order.
11
+ */
12
+ export async function promptMultiSelect(title, choices, defaultSelectedIds) {
13
+ const rl = createInterface();
14
+ try {
15
+ console.log(title);
16
+ choices.forEach((choice, index) => {
17
+ const mark = defaultSelectedIds.includes(choice.id) ? "*" : " ";
18
+ const detail = choice.detail ? ` — ${choice.detail}` : "";
19
+ console.log(` [${mark}] ${index + 1}) ${choice.label}${detail}`);
20
+ });
21
+ const answer = (await ask(rl, "Select numbers (comma/space separated, blank = starred defaults): ")).trim();
22
+ if (answer.length === 0) {
23
+ return choices.filter((c) => defaultSelectedIds.includes(c.id)).map((c) => c.id);
24
+ }
25
+ const picked = new Set();
26
+ for (const token of answer.split(/[\s,]+/).filter(Boolean)) {
27
+ const index = Number.parseInt(token, 10) - 1;
28
+ if (Number.isInteger(index) && index >= 0 && index < choices.length) {
29
+ picked.add(choices[index].id);
30
+ }
31
+ }
32
+ return choices.filter((c) => picked.has(c.id)).map((c) => c.id);
33
+ }
34
+ finally {
35
+ rl.close();
36
+ }
37
+ }
38
+ /** Yes/no confirmation. Empty input returns `defaultYes`. */
39
+ export async function promptYesNo(question, defaultYes) {
40
+ const rl = createInterface();
41
+ try {
42
+ const suffix = defaultYes ? "[Y/n]" : "[y/N]";
43
+ const answer = (await ask(rl, `${question} ${suffix} `)).trim().toLowerCase();
44
+ if (answer.length === 0)
45
+ return defaultYes;
46
+ return answer === "y" || answer === "yes";
47
+ }
48
+ finally {
49
+ rl.close();
50
+ }
51
+ }
@@ -0,0 +1,214 @@
1
+ import { ALL_HOST_IDS, } from "./types.js";
2
+ import { getDefaultHostTargets } from "./host-target.js";
3
+ import { promptMultiSelect, promptYesNo } from "./prompt.js";
4
+ const USAGE = [
5
+ "Usage: onto register [options]",
6
+ "",
7
+ "Register the onto MCP server into supported hosts so they launch `onto mcp`.",
8
+ "Interactive when run in a terminal with no --hosts/--all; flag-driven otherwise.",
9
+ "",
10
+ "Hosts: claude-code, codex, claude-desktop, cursor",
11
+ "",
12
+ "Options:",
13
+ " --hosts <a,b,...> Comma-separated host ids to register",
14
+ " --all Register all supported hosts",
15
+ " --list Show host detection status and exit",
16
+ " --dry-run Show intended changes without writing",
17
+ " --yes, -y Skip the confirmation prompt (required in non-TTY)",
18
+ " --force Re-register CLI hosts even if already present",
19
+ " --name <id> MCP server name (default: onto)",
20
+ " --command <cmd> Executable the host launches (default: onto)",
21
+ " --claude-config-dir <path> Target a Claude Code profile (sets",
22
+ " CLAUDE_CONFIG_DIR; default: ambient env or ~/.claude)",
23
+ " --help, -h Show this help",
24
+ ].join("\n");
25
+ export function parseRegisterArgs(argv) {
26
+ const parsed = {
27
+ hosts: undefined,
28
+ yes: false,
29
+ dryRun: false,
30
+ force: false,
31
+ list: false,
32
+ help: false,
33
+ name: "onto",
34
+ command: "onto",
35
+ claudeConfigDir: undefined,
36
+ unknownFlags: [],
37
+ invalidHosts: [],
38
+ };
39
+ for (let i = 0; i < argv.length; i++) {
40
+ const arg = argv[i];
41
+ switch (arg) {
42
+ case "--all":
43
+ parsed.hosts = "all";
44
+ break;
45
+ case "--yes":
46
+ case "-y":
47
+ parsed.yes = true;
48
+ break;
49
+ case "--dry-run":
50
+ parsed.dryRun = true;
51
+ break;
52
+ case "--force":
53
+ parsed.force = true;
54
+ break;
55
+ case "--list":
56
+ parsed.list = true;
57
+ break;
58
+ case "--help":
59
+ case "-h":
60
+ parsed.help = true;
61
+ break;
62
+ case "--hosts": {
63
+ const value = argv[++i] ?? "";
64
+ const ids = [];
65
+ for (const raw of value.split(",").map((s) => s.trim()).filter(Boolean)) {
66
+ if (ALL_HOST_IDS.includes(raw)) {
67
+ ids.push(raw);
68
+ }
69
+ else {
70
+ parsed.invalidHosts.push(raw);
71
+ }
72
+ }
73
+ parsed.hosts = parsed.hosts === "all" ? "all" : ids;
74
+ break;
75
+ }
76
+ case "--name":
77
+ parsed.name = argv[++i] ?? parsed.name;
78
+ break;
79
+ case "--command":
80
+ parsed.command = argv[++i] ?? parsed.command;
81
+ break;
82
+ case "--claude-config-dir":
83
+ parsed.claudeConfigDir = argv[++i] ?? parsed.claudeConfigDir;
84
+ break;
85
+ default:
86
+ parsed.unknownFlags.push(arg);
87
+ break;
88
+ }
89
+ }
90
+ return parsed;
91
+ }
92
+ function resolveSelection(parsed, targets) {
93
+ if (parsed.hosts === "all")
94
+ return targets.map((t) => t.id);
95
+ if (Array.isArray(parsed.hosts)) {
96
+ const requested = new Set(parsed.hosts);
97
+ return targets.map((t) => t.id).filter((id) => requested.has(id));
98
+ }
99
+ return [];
100
+ }
101
+ function detectionLabel(status) {
102
+ if (status === "cli")
103
+ return "detected (CLI)";
104
+ if (status === "config")
105
+ return "detected (config)";
106
+ return "not detected";
107
+ }
108
+ function printPlans(plans) {
109
+ console.log("\nPlanned changes:");
110
+ for (const plan of plans) {
111
+ console.log(` • ${plan.displayName} [${detectionLabel(plan.detection)}]`);
112
+ console.log(` ${plan.summary}`);
113
+ if (plan.method === "manual" && plan.manualInstructions) {
114
+ for (const line of plan.manualInstructions.split("\n")) {
115
+ console.log(` ${line}`);
116
+ }
117
+ }
118
+ }
119
+ }
120
+ function printResults(results) {
121
+ console.log("\nResults:");
122
+ for (const result of results) {
123
+ console.log(` • ${result.displayName}: ${result.outcome} — ${result.detail}`);
124
+ }
125
+ }
126
+ export async function runRegister(argv, deps = {}) {
127
+ const parsed = parseRegisterArgs(argv);
128
+ if (parsed.help) {
129
+ console.log(USAGE);
130
+ return 0;
131
+ }
132
+ if (parsed.unknownFlags.length > 0) {
133
+ console.error(`[onto register] Unknown option(s): ${parsed.unknownFlags.join(", ")}`);
134
+ console.error(USAGE);
135
+ return 1;
136
+ }
137
+ if (parsed.invalidHosts.length > 0) {
138
+ console.error(`[onto register] Unknown host(s): ${parsed.invalidHosts.join(", ")}. ` +
139
+ `Valid: ${ALL_HOST_IDS.join(", ")}`);
140
+ return 1;
141
+ }
142
+ const targets = deps.targets ??
143
+ getDefaultHostTargets(parsed.claudeConfigDir ? { claudeConfigDir: parsed.claudeConfigDir } : {});
144
+ const isTty = deps.isTty ?? Boolean(process.stdin.isTTY);
145
+ if (parsed.list) {
146
+ console.log("Host detection:");
147
+ for (const target of targets) {
148
+ console.log(` • ${target.displayName}: ${detectionLabel(target.detect())}`);
149
+ }
150
+ return 0;
151
+ }
152
+ const entry = {
153
+ name: parsed.name,
154
+ command: parsed.command,
155
+ args: ["mcp"],
156
+ };
157
+ // Determine which hosts to register.
158
+ let selected = resolveSelection(parsed, targets);
159
+ const selectionGivenByFlag = parsed.hosts !== undefined;
160
+ if (!selectionGivenByFlag) {
161
+ if (!isTty) {
162
+ console.error("[onto register] No hosts specified. Use --hosts <ids> or --all " +
163
+ "(interactive selection requires a terminal).");
164
+ return 1;
165
+ }
166
+ const defaults = targets.filter((t) => t.detect() !== "absent").map((t) => t.id);
167
+ const chosen = await promptMultiSelect("Select hosts to register onto with:", targets.map((t) => ({
168
+ id: t.id,
169
+ label: t.displayName,
170
+ detail: detectionLabel(t.detect()),
171
+ })), defaults.length > 0 ? defaults : targets.map((t) => t.id));
172
+ selected = targets.map((t) => t.id).filter((id) => chosen.includes(id));
173
+ }
174
+ if (selected.length === 0) {
175
+ console.error("[onto register] No hosts selected. Nothing to do.");
176
+ return 1;
177
+ }
178
+ const selectedTargets = targets.filter((t) => selected.includes(t.id));
179
+ const options = { force: parsed.force, dryRun: parsed.dryRun };
180
+ const plans = selectedTargets.map((t) => t.plan(entry, options));
181
+ printPlans(plans);
182
+ if (parsed.dryRun) {
183
+ console.log("\n(dry-run) No changes were written.");
184
+ return 0;
185
+ }
186
+ // Confirmation gate (write-before-confirm governance, product-locality §5.3 spirit).
187
+ if (!parsed.yes) {
188
+ if (!isTty) {
189
+ console.error("\n[onto register] Refusing to write without confirmation. Re-run with --yes.");
190
+ return 1;
191
+ }
192
+ const proceed = await promptYesNo("\nApply these changes?", true);
193
+ if (!proceed) {
194
+ console.log("Aborted. No changes written.");
195
+ return 0;
196
+ }
197
+ }
198
+ const results = [];
199
+ for (const target of selectedTargets) {
200
+ results.push(await target.apply(entry, options));
201
+ }
202
+ printResults(results);
203
+ const failed = results.filter((r) => r.outcome === "failed");
204
+ const manual = results.filter((r) => r.outcome === "manual");
205
+ if (manual.length > 0) {
206
+ console.log(`\nNote: ${manual.length} host(s) need a manual step (CLI not found) — see above.`);
207
+ }
208
+ if (failed.length > 0) {
209
+ console.error(`\n${failed.length} host(s) failed to register.`);
210
+ return 1;
211
+ }
212
+ console.log("\nDone. Restart the host app(s) to pick up the onto MCP server.");
213
+ return 0;
214
+ }
@@ -0,0 +1,27 @@
1
+ /**
2
+ * Host MCP registration types.
3
+ *
4
+ * `onto register` writes the onto MCP server into each supported host's own
5
+ * configuration so the host launches `onto mcp` as a stdio MCP server. This is
6
+ * onboarding/integration, not onto runtime data: targets write to host-owned
7
+ * config (user home), never to `{product}/.onto/`.
8
+ */
9
+ export const ALL_HOST_IDS = [
10
+ "claude-code",
11
+ "codex",
12
+ "claude-desktop",
13
+ "cursor",
14
+ ];
15
+ /** True when `command` and `args` of an existing host entry already match. */
16
+ export function entryMatches(existing, entry) {
17
+ if (!existing || typeof existing !== "object")
18
+ return false;
19
+ if (existing.command !== entry.command)
20
+ return false;
21
+ const existingArgs = existing.args;
22
+ if (!Array.isArray(existingArgs))
23
+ return entry.args.length === 0;
24
+ if (existingArgs.length !== entry.args.length)
25
+ return false;
26
+ return existingArgs.every((value, index) => value === entry.args[index]);
27
+ }