onto-mcp 0.3.2 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.onto/processes/reconstruct/actionable-ontology-seed-recomposition-design.md +447 -0
- package/.onto/processes/reconstruct/foundry-style-ontology-seed-contract.md +934 -0
- package/.onto/processes/reconstruct/reconstruct-boundary-contract.md +303 -725
- package/.onto/processes/reconstruct/reconstruct-contract-registry.yaml +1645 -0
- package/.onto/processes/reconstruct/reconstruct-execution-ux-contract.md +26 -22
- package/.onto/processes/reconstruct/source-profile-contract.md +49 -23
- package/.onto/processes/reconstruct/source-profiles/code.md +6 -3
- package/.onto/processes/reconstruct/source-profiles/database.md +5 -2
- package/.onto/processes/reconstruct/source-profiles/document.md +5 -2
- package/.onto/processes/reconstruct/source-profiles/spreadsheet.md +5 -4
- package/.onto/processes/review/review-execution-ux-contract.md +40 -0
- package/.onto/processes/shared/pipeline-execution-ledger-contract.md +26 -10
- package/.onto/processes/shared/target-material-kind-contract.md +29 -16
- package/AGENTS.md +6 -4
- package/README.md +135 -76
- package/dist/cli.js +8 -8
- package/dist/core-api/reconstruct-api.js +117 -31
- package/dist/core-api/review-api.js +47 -0
- package/dist/core-runtime/cli/codex-review-unit-executor.js +39 -2
- package/dist/core-runtime/cli/complete-review-session.js +2 -2
- package/dist/core-runtime/cli/mock-review-unit-executor.js +1 -1
- package/dist/core-runtime/cli/review-invoke.js +9 -9
- package/dist/core-runtime/cli/run-review-prompt-execution.js +39 -5
- package/dist/core-runtime/cli/spawn-watcher.js +266 -47
- package/dist/core-runtime/cli/start-review-session.js +3 -3
- package/dist/core-runtime/llm/llm-caller.js +11 -0
- package/dist/core-runtime/llm/llm-tool-loop.js +2 -0
- package/dist/core-runtime/observability/runtime-stream-observation.js +118 -0
- package/dist/core-runtime/onboard/cli-host.js +149 -0
- package/dist/core-runtime/onboard/host-target.js +22 -0
- package/dist/core-runtime/onboard/json-config-host.js +122 -0
- package/dist/core-runtime/onboard/path-scan.js +26 -0
- package/dist/core-runtime/onboard/prompt.js +51 -0
- package/dist/core-runtime/onboard/register.js +207 -0
- package/dist/core-runtime/onboard/types.js +27 -0
- package/dist/core-runtime/reconstruct/actionable-seed-validation.js +1777 -0
- package/dist/core-runtime/reconstruct/artifact-types.js +10 -4
- package/dist/core-runtime/reconstruct/contract-registry.js +623 -0
- package/dist/core-runtime/reconstruct/domain-id.js +10 -0
- package/dist/core-runtime/reconstruct/governing-snapshot.js +716 -0
- package/dist/core-runtime/reconstruct/material-profile-validation.js +191 -0
- package/dist/core-runtime/reconstruct/materialize-preparation.js +49 -11
- package/dist/core-runtime/reconstruct/pipeline-execution-ledger.js +269 -79
- package/dist/core-runtime/reconstruct/post-seed-validation.js +1194 -51
- package/dist/core-runtime/reconstruct/record.js +104 -20
- package/dist/core-runtime/reconstruct/run.js +2107 -413
- package/dist/core-runtime/reconstruct/seed-claim-projections.js +268 -0
- package/dist/core-runtime/reconstruct/source-profiles.js +93 -4
- package/dist/core-runtime/reconstruct/terminal-validation.js +807 -0
- package/dist/core-runtime/review/review-invocation-runner.js +4 -4
- package/dist/mcp/server.js +110 -38
- package/dist/mcp/tool-schemas.js +20 -6
- package/package.json +8 -17
- package/scripts/onto-review-watch.sh +486 -0
- package/scripts/onto-runtime-watch.sh +122 -0
- package/scripts/postinstall-hint.js +22 -0
- package/.onto/processes/reconstruct/top-level-concept-discovery-contract.md +0 -387
- package/dist/core-runtime/cli/bootstrap-review-binding.js +0 -186
- package/dist/core-runtime/cli/codex-nested-dispatch.test.js +0 -390
- package/dist/core-runtime/cli/codex-nested-teamlead-executor.test.js +0 -335
- package/dist/core-runtime/cli/coordinator-helpers.js +0 -583
- package/dist/core-runtime/cli/coordinator-state-machine-deliberation.test.js +0 -167
- package/dist/core-runtime/cli/coordinator-state-machine.js +0 -794
- package/dist/core-runtime/cli/e2e-codex-multi-agent-fixes.test.js +0 -615
- package/dist/core-runtime/cli/e2e-start-review-session.test.js +0 -312
- package/dist/core-runtime/cli/health.js +0 -44
- package/dist/core-runtime/cli/inline-http-review-unit-executor.test.js +0 -567
- package/dist/core-runtime/cli/materialize-review-execution-preparation.js +0 -104
- package/dist/core-runtime/cli/migrate-session-roots.js +0 -118
- package/dist/core-runtime/cli/repo-layout-migration-replace.smoke.test.js +0 -106
- package/dist/core-runtime/cli/review-invoke-auto-resolution.test.js +0 -268
- package/dist/core-runtime/cli/review-invoke-coordinator-topology.test.js +0 -136
- package/dist/core-runtime/cli/review-invoke-resolver-caching.test.js +0 -201
- package/dist/core-runtime/cli/review-invoke-topology-dispatch.test.js +0 -192
- package/dist/core-runtime/cli/session-root-guard.js +0 -168
- package/dist/core-runtime/cli/spawn-watcher.test.js +0 -457
- package/dist/core-runtime/cli/strip-wrapping-code-fence.test.js +0 -79
- package/dist/core-runtime/cli/teamcreate-lens-deliberation-executor.js +0 -412
- package/dist/core-runtime/cli/teamcreate-lens-deliberation-executor.test.js +0 -351
- package/dist/core-runtime/cli/topology-executor-mapping.js +0 -139
- package/dist/core-runtime/cli/topology-executor-mapping.test.js +0 -173
- package/dist/core-runtime/cli/write-review-interpretation.js +0 -81
- package/dist/core-runtime/config/onto-config-cli.js +0 -278
- package/dist/core-runtime/config/onto-config-key-path.js +0 -288
- package/dist/core-runtime/config/onto-config-key-path.test.js +0 -195
- package/dist/core-runtime/config/onto-config-preview.js +0 -108
- package/dist/core-runtime/config/onto-config-preview.test.js +0 -132
- package/dist/core-runtime/discovery/config-chain.js +0 -118
- package/dist/core-runtime/discovery/config-chain.test.js +0 -103
- package/dist/core-runtime/discovery/config-profile.js +0 -199
- package/dist/core-runtime/discovery/config-profile.test.js +0 -233
- package/dist/core-runtime/discovery/host-detection.test.js +0 -186
- package/dist/core-runtime/discovery/installation-paths.test.js +0 -65
- package/dist/core-runtime/discovery/lens-registry.test.js +0 -81
- package/dist/core-runtime/discovery/path-normalization.test.js +0 -22
- package/dist/core-runtime/discovery/plugin-path.js +0 -72
- package/dist/core-runtime/discovery/plugin-path.test.js +0 -95
- package/dist/core-runtime/evolve/adapters/code-product/compile/compile-defense.js +0 -344
- package/dist/core-runtime/evolve/adapters/code-product/compile/compile-defense.test.js +0 -915
- package/dist/core-runtime/evolve/adapters/code-product/compile/compile.js +0 -564
- package/dist/core-runtime/evolve/adapters/code-product/compile/compile.test.js +0 -708
- package/dist/core-runtime/evolve/adapters/code-product/parsers/brief-parser.js +0 -165
- package/dist/core-runtime/evolve/adapters/code-product/parsers/brief-parser.test.js +0 -227
- package/dist/core-runtime/evolve/adapters/code-product/validators/validate.js +0 -59
- package/dist/core-runtime/evolve/adapters/code-product/validators/validate.test.js +0 -205
- package/dist/core-runtime/evolve/adapters/methodology/adapter.js +0 -16
- package/dist/core-runtime/evolve/adapters/methodology/adapter.test.js +0 -9
- package/dist/core-runtime/evolve/adapters/methodology/perspectives/authority-consistency.js +0 -298
- package/dist/core-runtime/evolve/adapters/methodology/perspectives/authority-consistency.test.js +0 -70
- package/dist/core-runtime/evolve/adapters/methodology/scope-types/process.js +0 -46
- package/dist/core-runtime/evolve/adapters/methodology/scope-types/process.test.js +0 -73
- package/dist/core-runtime/evolve/adapters/registry.js +0 -47
- package/dist/core-runtime/evolve/adapters/registry.test.js +0 -67
- package/dist/core-runtime/evolve/cli.js +0 -256
- package/dist/core-runtime/evolve/commands/align.js +0 -194
- package/dist/core-runtime/evolve/commands/align.test.js +0 -82
- package/dist/core-runtime/evolve/commands/apply.js +0 -161
- package/dist/core-runtime/evolve/commands/apply.test.js +0 -138
- package/dist/core-runtime/evolve/commands/close.js +0 -39
- package/dist/core-runtime/evolve/commands/close.test.js +0 -99
- package/dist/core-runtime/evolve/commands/defer.js +0 -40
- package/dist/core-runtime/evolve/commands/defer.test.js +0 -134
- package/dist/core-runtime/evolve/commands/draft.js +0 -323
- package/dist/core-runtime/evolve/commands/draft.test.js +0 -178
- package/dist/core-runtime/evolve/commands/e2e-evolve-full-cycle.test.js +0 -208
- package/dist/core-runtime/evolve/commands/error-messages.js +0 -125
- package/dist/core-runtime/evolve/commands/error-messages.test.js +0 -167
- package/dist/core-runtime/evolve/commands/propose-align.js +0 -222
- package/dist/core-runtime/evolve/commands/propose-align.test.js +0 -136
- package/dist/core-runtime/evolve/commands/reconstruct.js +0 -330
- package/dist/core-runtime/evolve/commands/reconstruct.test.js +0 -278
- package/dist/core-runtime/evolve/commands/shared.js +0 -22
- package/dist/core-runtime/evolve/commands/stale-check.js +0 -103
- package/dist/core-runtime/evolve/commands/stale-check.test.js +0 -84
- package/dist/core-runtime/evolve/commands/start.js +0 -887
- package/dist/core-runtime/evolve/commands/start.test.js +0 -396
- package/dist/core-runtime/evolve/config/project-config.js +0 -99
- package/dist/core-runtime/evolve/config/project-config.test.js +0 -170
- package/dist/core-runtime/evolve/renderers/align-packet.js +0 -280
- package/dist/core-runtime/evolve/renderers/align-packet.test.js +0 -332
- package/dist/core-runtime/evolve/renderers/draft-packet.js +0 -303
- package/dist/core-runtime/evolve/renderers/draft-packet.test.js +0 -377
- package/dist/core-runtime/evolve/renderers/format.js +0 -5
- package/dist/core-runtime/evolve/renderers/scope-md.js +0 -237
- package/dist/core-runtime/evolve/renderers/scope-md.test.js +0 -306
- package/dist/core-runtime/govern/cli.js +0 -369
- package/dist/core-runtime/govern/cli.test.js +0 -314
- package/dist/core-runtime/govern/drift-engine.js +0 -103
- package/dist/core-runtime/govern/drift-engine.test.js +0 -319
- package/dist/core-runtime/govern/promote-principle.js +0 -206
- package/dist/core-runtime/govern/promote-principle.test.js +0 -368
- package/dist/core-runtime/govern/queue.js +0 -81
- package/dist/core-runtime/govern/types.js +0 -16
- package/dist/core-runtime/install/cli.js +0 -530
- package/dist/core-runtime/install/detect.js +0 -128
- package/dist/core-runtime/install/detect.test.js +0 -155
- package/dist/core-runtime/install/gitignore-update.js +0 -74
- package/dist/core-runtime/install/gitignore-update.test.js +0 -64
- package/dist/core-runtime/install/install-integration.test.js +0 -373
- package/dist/core-runtime/install/prompts.js +0 -389
- package/dist/core-runtime/install/prompts.test.js +0 -293
- package/dist/core-runtime/install/types.js +0 -26
- package/dist/core-runtime/install/validation.js +0 -295
- package/dist/core-runtime/install/validation.test.js +0 -313
- package/dist/core-runtime/install/writer.js +0 -254
- package/dist/core-runtime/install/writer.test.js +0 -218
- package/dist/core-runtime/learning/extractor.js +0 -461
- package/dist/core-runtime/learning/feedback.js +0 -179
- package/dist/core-runtime/learning/health-report.js +0 -165
- package/dist/core-runtime/learning/health-report.test.js +0 -169
- package/dist/core-runtime/learning/loader.js +0 -388
- package/dist/core-runtime/learning/loader.test.js +0 -102
- package/dist/core-runtime/learning/promote/apply-state.js +0 -240
- package/dist/core-runtime/learning/promote/audit-obligation.js +0 -195
- package/dist/core-runtime/learning/promote/collector.js +0 -432
- package/dist/core-runtime/learning/promote/degraded-state.js +0 -125
- package/dist/core-runtime/learning/promote/domain-doc-proposer.js +0 -166
- package/dist/core-runtime/learning/promote/e2e-promote.test.js +0 -6385
- package/dist/core-runtime/learning/promote/health-snapshot.js +0 -150
- package/dist/core-runtime/learning/promote/insight-reclassifier.js +0 -544
- package/dist/core-runtime/learning/promote/judgment-auditor.js +0 -517
- package/dist/core-runtime/learning/promote/panel-reviewer.js +0 -1158
- package/dist/core-runtime/learning/promote/promote-executor.js +0 -1675
- package/dist/core-runtime/learning/promote/promoter.js +0 -307
- package/dist/core-runtime/learning/promote/retirement.js +0 -122
- package/dist/core-runtime/learning/promote/types.js +0 -23
- package/dist/core-runtime/learning/prompt-sections.js +0 -51
- package/dist/core-runtime/learning/shared/artifact-registry-init.js +0 -45
- package/dist/core-runtime/learning/shared/artifact-registry.js +0 -254
- package/dist/core-runtime/learning/shared/audit-obligation-kernel.js +0 -73
- package/dist/core-runtime/learning/shared/audit-state.js +0 -99
- package/dist/core-runtime/learning/shared/duplicate-check.js +0 -28
- package/dist/core-runtime/learning/shared/llm-caller.js +0 -831
- package/dist/core-runtime/learning/shared/llm-caller.test.js +0 -601
- package/dist/core-runtime/learning/shared/llm-tool-loop.js +0 -393
- package/dist/core-runtime/learning/shared/mode.js +0 -25
- package/dist/core-runtime/learning/shared/paths.js +0 -84
- package/dist/core-runtime/learning/shared/paths.test.js +0 -79
- package/dist/core-runtime/learning/shared/patterns.js +0 -37
- package/dist/core-runtime/learning/shared/recoverability.js +0 -355
- package/dist/core-runtime/learning/shared/recovery-context.js +0 -374
- package/dist/core-runtime/learning/shared/scope.js +0 -1
- package/dist/core-runtime/learning/shared/semantic-classifier.js +0 -94
- package/dist/core-runtime/learning/shared/specs/apply-execution-state-spec.js +0 -42
- package/dist/core-runtime/learning/shared/specs/audit-state-spec.js +0 -37
- package/dist/core-runtime/learning/shared/specs/backup-metadata-spec.js +0 -39
- package/dist/core-runtime/learning/shared/specs/emergency-log-spec.js +0 -41
- package/dist/core-runtime/learning/shared/specs/layout-version-spec.js +0 -38
- package/dist/core-runtime/learning/shared/specs/promote-decisions-spec.js +0 -43
- package/dist/core-runtime/learning/shared/specs/promote-report-spec.js +0 -113
- package/dist/core-runtime/learning/shared/specs/prune-log-spec.js +0 -36
- package/dist/core-runtime/learning/shared/specs/recovery-resolution-spec.js +0 -48
- package/dist/core-runtime/learning/shared/specs/restore-manifest-spec.js +0 -43
- package/dist/core-runtime/learning/shared/specs/spec-helpers.js +0 -64
- package/dist/core-runtime/learning/usage-tracker.js +0 -190
- package/dist/core-runtime/learning/usage-tracker.test.js +0 -176
- package/dist/core-runtime/onboard/detect-review-axes.js +0 -122
- package/dist/core-runtime/onboard/detect-review-axes.test.js +0 -127
- package/dist/core-runtime/onboard/write-review-block.js +0 -188
- package/dist/core-runtime/onboard/write-review-block.test.js +0 -240
- package/dist/core-runtime/readers/brownfield-builder.js +0 -150
- package/dist/core-runtime/readers/brownfield-builder.test.js +0 -136
- package/dist/core-runtime/readers/code-chunk-collector.js +0 -53
- package/dist/core-runtime/readers/code-chunk-collector.test.js +0 -136
- package/dist/core-runtime/readers/file-utils.js +0 -240
- package/dist/core-runtime/readers/file-utils.test.js +0 -146
- package/dist/core-runtime/readers/lexicon-citation-check.js +0 -93
- package/dist/core-runtime/readers/lexicon-citation-check.test.js +0 -77
- package/dist/core-runtime/readers/mcp-figma.js +0 -30
- package/dist/core-runtime/readers/mcp-figma.test.js +0 -82
- package/dist/core-runtime/readers/mcp-generic.js +0 -31
- package/dist/core-runtime/readers/mcp-generic.test.js +0 -76
- package/dist/core-runtime/readers/ontology-index.js +0 -148
- package/dist/core-runtime/readers/ontology-index.test.js +0 -245
- package/dist/core-runtime/readers/ontology-query.js +0 -168
- package/dist/core-runtime/readers/ontology-query.test.js +0 -311
- package/dist/core-runtime/readers/ontology-resolve.js +0 -48
- package/dist/core-runtime/readers/ontology-resolve.test.js +0 -48
- package/dist/core-runtime/readers/patterns/index.js +0 -7
- package/dist/core-runtime/readers/review-log.js +0 -213
- package/dist/core-runtime/readers/review-log.test.js +0 -313
- package/dist/core-runtime/readers/scan-local.js +0 -102
- package/dist/core-runtime/readers/scan-local.test.js +0 -102
- package/dist/core-runtime/readers/scan-tarball.js +0 -121
- package/dist/core-runtime/readers/scan-tarball.test.js +0 -283
- package/dist/core-runtime/readers/scan-vault.js +0 -34
- package/dist/core-runtime/readers/scan-vault.test.js +0 -81
- package/dist/core-runtime/readers/types.js +0 -42
- package/dist/core-runtime/readers/types.test.js +0 -94
- package/dist/core-runtime/readers/viewpoint-collectors.js +0 -229
- package/dist/core-runtime/reconstruct/seed-candidate-validation.js +0 -385
- package/dist/core-runtime/review/citation-audit.test.js +0 -165
- package/dist/core-runtime/review/execution-plan-resolver.js +0 -247
- package/dist/core-runtime/review/execution-plan-resolver.test.js +0 -243
- package/dist/core-runtime/review/execution-topology-resolver-axis-first.test.js +0 -246
- package/dist/core-runtime/review/execution-topology-resolver.js +0 -401
- package/dist/core-runtime/review/execution-topology-resolver.test.js +0 -315
- package/dist/core-runtime/review/inline-context-embedder.test.js +0 -154
- package/dist/core-runtime/review/legacy-mode-policy.js +0 -88
- package/dist/core-runtime/review/materializers-effort-persist.test.js +0 -79
- package/dist/core-runtime/review/ontology-path-classifier.js +0 -179
- package/dist/core-runtime/review/ontology-path-classifier.test.js +0 -216
- package/dist/core-runtime/review/packet-boundary-policy.test.js +0 -107
- package/dist/core-runtime/review/participating-lens-paths.test.js +0 -73
- package/dist/core-runtime/review/review-config-legacy-translate.js +0 -244
- package/dist/core-runtime/review/review-config-legacy-translate.test.js +0 -161
- package/dist/core-runtime/review/review-config-validator.js +0 -289
- package/dist/core-runtime/review/review-config-validator.test.js +0 -236
- package/dist/core-runtime/review/shape-pipeline-audit.test.js +0 -311
- package/dist/core-runtime/review/shape-to-topology-id.js +0 -117
- package/dist/core-runtime/review/shape-to-topology-id.test.js +0 -132
- package/dist/core-runtime/review/topology-shape-derivation.js +0 -155
- package/dist/core-runtime/review/topology-shape-derivation.test.js +0 -195
- package/dist/core-runtime/scope-runtime/constants.js +0 -12
- package/dist/core-runtime/scope-runtime/constraint-pool.js +0 -166
- package/dist/core-runtime/scope-runtime/constraint-pool.test.js +0 -674
- package/dist/core-runtime/scope-runtime/domain-validation-log.js +0 -135
- package/dist/core-runtime/scope-runtime/domain-validation-log.test.js +0 -156
- package/dist/core-runtime/scope-runtime/eval-persistence.js +0 -65
- package/dist/core-runtime/scope-runtime/eval-persistence.test.js +0 -84
- package/dist/core-runtime/scope-runtime/event-pipeline.js +0 -64
- package/dist/core-runtime/scope-runtime/event-pipeline.test.js +0 -450
- package/dist/core-runtime/scope-runtime/event-store.js +0 -39
- package/dist/core-runtime/scope-runtime/event-store.test.js +0 -95
- package/dist/core-runtime/scope-runtime/gate-guard.js +0 -348
- package/dist/core-runtime/scope-runtime/gate-guard.test.js +0 -1047
- package/dist/core-runtime/scope-runtime/hash.js +0 -4
- package/dist/core-runtime/scope-runtime/hash.test.js +0 -33
- package/dist/core-runtime/scope-runtime/id.js +0 -4
- package/dist/core-runtime/scope-runtime/id.test.js +0 -17
- package/dist/core-runtime/scope-runtime/reducer.js +0 -297
- package/dist/core-runtime/scope-runtime/reducer.test.js +0 -759
- package/dist/core-runtime/scope-runtime/scope-manager.js +0 -161
- package/dist/core-runtime/scope-runtime/state-machine.js +0 -309
- package/dist/core-runtime/scope-runtime/state-machine.test.js +0 -704
- package/dist/core-runtime/scope-runtime/types.js +0 -116
- package/dist/core-runtime/scope-runtime/types.test.js +0 -69
- package/dist/core-runtime/translate/render-for-user.js +0 -169
- package/dist/core-runtime/translate/render-for-user.test.js +0 -122
- package/dist/providers/capability-contract.js +0 -1
|
@@ -1,389 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Interactive prompts for `onto install`.
|
|
3
|
-
*
|
|
4
|
-
* # Module layout
|
|
5
|
-
*
|
|
6
|
-
* The `PromptIO` interface is the seam between interactive behavior
|
|
7
|
-
* and stdin/stdout. Every prompt function takes a `PromptIO` so tests
|
|
8
|
-
* can inject a scripted mock that returns predetermined answers —
|
|
9
|
-
* no real terminal, no stdin mocking gymnastics.
|
|
10
|
-
*
|
|
11
|
-
* `createReadlineIo()` is the default production implementation,
|
|
12
|
-
* backed by `node:readline/promises`.
|
|
13
|
-
*
|
|
14
|
-
* # Secret input
|
|
15
|
-
*
|
|
16
|
-
* `askSecret()` masks input with asterisks on TTY. It closes the
|
|
17
|
-
* active readline interface, takes over stdin in raw mode, reads
|
|
18
|
-
* character by character echoing `*`, and reopens readline
|
|
19
|
-
* afterwards. Non-TTY environments fall back to plain readline
|
|
20
|
-
* (masking isn't possible without raw-mode support). See
|
|
21
|
-
* `readMaskedFromStdin` below for the recognized key bindings
|
|
22
|
-
* (Enter, Ctrl-C, Ctrl-D, Backspace).
|
|
23
|
-
*
|
|
24
|
-
* # Step functions
|
|
25
|
-
*
|
|
26
|
-
* One function per interactive step, each accepting `(io, flags,
|
|
27
|
-
* detection, priorDecisions)` as needed and returning the resolved
|
|
28
|
-
* value. They respect `InstallFlags` first: if the flag is set the
|
|
29
|
-
* prompt is skipped; otherwise the user is asked with a sensible
|
|
30
|
-
* default derived from `PreflightDetection`.
|
|
31
|
-
*
|
|
32
|
-
* # Orchestrator
|
|
33
|
-
*
|
|
34
|
-
* `runInteractivePrompts()` runs all six steps in order and returns
|
|
35
|
-
* the fully-resolved `{ decisions, secrets }` pair that writer.ts
|
|
36
|
-
* consumes. The non-interactive orchestration path (`install/cli.ts
|
|
37
|
-
* #runNonInteractive`) reuses the step functions' flag-respect logic
|
|
38
|
-
* — when every flag is pre-populated, the prompts are skipped and
|
|
39
|
-
* the mode difference collapses to the flag resolution logic.
|
|
40
|
-
*/
|
|
41
|
-
import readline from "node:readline/promises";
|
|
42
|
-
/**
|
|
43
|
-
* Read a line from stdin with echo masking (asterisks per keypress).
|
|
44
|
-
*
|
|
45
|
-
* Takes full control of stdin via raw mode, consuming data events
|
|
46
|
-
* directly rather than through readline. The caller must have any
|
|
47
|
-
* active readline interface closed before entering this function —
|
|
48
|
-
* otherwise readline's own data listener would race with ours and
|
|
49
|
-
* eat characters before the mask loop sees them.
|
|
50
|
-
*
|
|
51
|
-
* Recognized inputs:
|
|
52
|
-
* - Enter / newline → resolve with buffered string
|
|
53
|
-
* - Ctrl-C (0x03) → reject with cancellation error
|
|
54
|
-
* - Ctrl-D (0x04) → resolve with buffered string (EOF)
|
|
55
|
-
* - Backspace (0x7f, 0x08) → remove last char + visual backspace
|
|
56
|
-
* - Printable chars → append to buffer, echo "*"
|
|
57
|
-
* - Other control chars → ignored
|
|
58
|
-
*/
|
|
59
|
-
async function readMaskedFromStdin(prompt) {
|
|
60
|
-
process.stdout.write(`${prompt}: `);
|
|
61
|
-
return new Promise((resolve, reject) => {
|
|
62
|
-
const stdin = process.stdin;
|
|
63
|
-
stdin.setRawMode?.(true);
|
|
64
|
-
stdin.resume();
|
|
65
|
-
stdin.setEncoding("utf8");
|
|
66
|
-
let buffer = "";
|
|
67
|
-
const cleanup = () => {
|
|
68
|
-
stdin.off("data", onData);
|
|
69
|
-
stdin.setRawMode?.(false);
|
|
70
|
-
stdin.pause();
|
|
71
|
-
};
|
|
72
|
-
const onData = (chunk) => {
|
|
73
|
-
for (const ch of chunk) {
|
|
74
|
-
const code = ch.charCodeAt(0);
|
|
75
|
-
if (ch === "\n" || ch === "\r") {
|
|
76
|
-
cleanup();
|
|
77
|
-
process.stdout.write("\n");
|
|
78
|
-
resolve(buffer);
|
|
79
|
-
return;
|
|
80
|
-
}
|
|
81
|
-
if (code === 3) {
|
|
82
|
-
cleanup();
|
|
83
|
-
process.stdout.write("\n");
|
|
84
|
-
reject(new Error("Install canceled (Ctrl-C)"));
|
|
85
|
-
return;
|
|
86
|
-
}
|
|
87
|
-
if (code === 4) {
|
|
88
|
-
cleanup();
|
|
89
|
-
process.stdout.write("\n");
|
|
90
|
-
resolve(buffer);
|
|
91
|
-
return;
|
|
92
|
-
}
|
|
93
|
-
if (code === 127 || code === 8) {
|
|
94
|
-
if (buffer.length > 0) {
|
|
95
|
-
buffer = buffer.slice(0, -1);
|
|
96
|
-
// Visually erase the last asterisk.
|
|
97
|
-
process.stdout.write("\b \b");
|
|
98
|
-
}
|
|
99
|
-
continue;
|
|
100
|
-
}
|
|
101
|
-
if (code < 32)
|
|
102
|
-
continue;
|
|
103
|
-
buffer += ch;
|
|
104
|
-
process.stdout.write("*");
|
|
105
|
-
}
|
|
106
|
-
};
|
|
107
|
-
stdin.on("data", onData);
|
|
108
|
-
});
|
|
109
|
-
}
|
|
110
|
-
/** Default PromptIO backed by `node:readline/promises`. */
|
|
111
|
-
export function createReadlineIo() {
|
|
112
|
-
let rl = readline.createInterface({
|
|
113
|
-
input: process.stdin,
|
|
114
|
-
output: process.stdout,
|
|
115
|
-
});
|
|
116
|
-
const ask = async (prompt, options) => {
|
|
117
|
-
const suffix = options?.default !== undefined ? ` [${options.default}]: ` : ": ";
|
|
118
|
-
const answer = (await rl.question(`${prompt}${suffix}`)).trim();
|
|
119
|
-
if (!answer && options?.default !== undefined)
|
|
120
|
-
return options.default;
|
|
121
|
-
return answer;
|
|
122
|
-
};
|
|
123
|
-
const askChoice = async (prompt, choices, options) => {
|
|
124
|
-
while (true) {
|
|
125
|
-
const hint = choices.join(" | ");
|
|
126
|
-
const answer = (await rl.question(`${prompt}\n (${hint})${options?.default ? ` [${options.default}]` : ""}: `)).trim();
|
|
127
|
-
const picked = !answer && options?.default ? options.default : answer;
|
|
128
|
-
if (choices.includes(picked)) {
|
|
129
|
-
return picked;
|
|
130
|
-
}
|
|
131
|
-
process.stdout.write(` '${picked}' 는 유효한 선택이 아닙니다. 다시 입력해 주세요.\n`);
|
|
132
|
-
}
|
|
133
|
-
};
|
|
134
|
-
const askConfirm = async (prompt, defaultValue) => {
|
|
135
|
-
const defaultHint = defaultValue ? "Y/n" : "y/N";
|
|
136
|
-
const raw = (await rl.question(`${prompt} [${defaultHint}]: `))
|
|
137
|
-
.trim()
|
|
138
|
-
.toLowerCase();
|
|
139
|
-
if (!raw)
|
|
140
|
-
return defaultValue;
|
|
141
|
-
if (raw === "y" || raw === "yes")
|
|
142
|
-
return true;
|
|
143
|
-
if (raw === "n" || raw === "no")
|
|
144
|
-
return false;
|
|
145
|
-
return defaultValue;
|
|
146
|
-
};
|
|
147
|
-
/**
|
|
148
|
-
* TTY → masked input (asterisks). Non-TTY → fall back to plain
|
|
149
|
-
* readline (no masking possible without raw mode).
|
|
150
|
-
*
|
|
151
|
-
* The TTY path must close + reopen the readline interface because
|
|
152
|
-
* both readline and our raw-mode listener bind to `process.stdin`;
|
|
153
|
-
* leaving readline open during the masked read makes it race us
|
|
154
|
-
* for input bytes. Close before, recreate after — any in-flight
|
|
155
|
-
* state (cursor line, completion state) is irrelevant across a
|
|
156
|
-
* secret prompt boundary.
|
|
157
|
-
*/
|
|
158
|
-
const askSecret = async (prompt) => {
|
|
159
|
-
const stdin = process.stdin;
|
|
160
|
-
const canRawMode = Boolean(stdin.isTTY) && typeof stdin.setRawMode === "function";
|
|
161
|
-
if (!canRawMode) {
|
|
162
|
-
return (await rl.question(`${prompt}: `)).trim();
|
|
163
|
-
}
|
|
164
|
-
rl.close();
|
|
165
|
-
try {
|
|
166
|
-
const answer = await readMaskedFromStdin(prompt);
|
|
167
|
-
return answer.trim();
|
|
168
|
-
}
|
|
169
|
-
finally {
|
|
170
|
-
rl = readline.createInterface({
|
|
171
|
-
input: process.stdin,
|
|
172
|
-
output: process.stdout,
|
|
173
|
-
});
|
|
174
|
-
}
|
|
175
|
-
};
|
|
176
|
-
const print = (text) => {
|
|
177
|
-
process.stdout.write(`${text}\n`);
|
|
178
|
-
};
|
|
179
|
-
const close = () => {
|
|
180
|
-
rl.close();
|
|
181
|
-
};
|
|
182
|
-
return { ask, askChoice, askConfirm, askSecret, print, close };
|
|
183
|
-
}
|
|
184
|
-
// ---------------------------------------------------------------------------
|
|
185
|
-
// Step 1: profile scope
|
|
186
|
-
// ---------------------------------------------------------------------------
|
|
187
|
-
export async function stepProfileScope(io, flags) {
|
|
188
|
-
if (flags.profileScope)
|
|
189
|
-
return flags.profileScope;
|
|
190
|
-
return io.askChoice("이 프로파일을 어디에 저장하시겠어요?", ["global", "project"], { default: "project" });
|
|
191
|
-
}
|
|
192
|
-
// ---------------------------------------------------------------------------
|
|
193
|
-
// Step 2: review provider
|
|
194
|
-
// ---------------------------------------------------------------------------
|
|
195
|
-
const REVIEW_PROVIDERS = [
|
|
196
|
-
"main-native",
|
|
197
|
-
"codex",
|
|
198
|
-
"anthropic",
|
|
199
|
-
"openai",
|
|
200
|
-
"litellm",
|
|
201
|
-
];
|
|
202
|
-
/**
|
|
203
|
-
* Derive the suggested default review provider from the pre-flight
|
|
204
|
-
* snapshot. Priority favors "least friction" — if the user is
|
|
205
|
-
* currently in a Claude Code session with no external keys, main-native
|
|
206
|
-
* costs nothing. Otherwise the highest-priority detected credential
|
|
207
|
-
* wins.
|
|
208
|
-
*/
|
|
209
|
-
function suggestReviewDefault(detection) {
|
|
210
|
-
if (detection.hostIsClaudeCode)
|
|
211
|
-
return "main-native";
|
|
212
|
-
if (detection.hasCodexBinary && detection.hasCodexAuth)
|
|
213
|
-
return "codex";
|
|
214
|
-
if (detection.hasAnthropicKey)
|
|
215
|
-
return "anthropic";
|
|
216
|
-
if (detection.hasOpenAiKey)
|
|
217
|
-
return "openai";
|
|
218
|
-
if (detection.hasLitellmBaseUrl)
|
|
219
|
-
return "litellm";
|
|
220
|
-
return "main-native";
|
|
221
|
-
}
|
|
222
|
-
export async function stepReviewProvider(io, flags, detection) {
|
|
223
|
-
if (flags.reviewProvider) {
|
|
224
|
-
if (flags.reviewProvider === "main-native" && !detection.hostIsClaudeCode) {
|
|
225
|
-
io.print("[warning] main-native는 Claude Code 세션 내에서만 동작합니다. " +
|
|
226
|
-
"현재는 Claude Code가 감지되지 않았습니다.");
|
|
227
|
-
}
|
|
228
|
-
return flags.reviewProvider;
|
|
229
|
-
}
|
|
230
|
-
const suggested = suggestReviewDefault(detection);
|
|
231
|
-
while (true) {
|
|
232
|
-
const picked = await io.askChoice("Review (9-lens) 실행에 사용할 provider를 선택하세요.", REVIEW_PROVIDERS, { default: suggested });
|
|
233
|
-
if (picked === "main-native" && !detection.hostIsClaudeCode) {
|
|
234
|
-
io.print(" [warning] main-native는 Claude Code 세션이 아닐 때 동작하지 " +
|
|
235
|
-
"않습니다. 그래도 이 값으로 기록할까요? (npm CLI 사용 시 review가 " +
|
|
236
|
-
"실패할 수 있습니다.)");
|
|
237
|
-
const proceed = await io.askConfirm("계속", false);
|
|
238
|
-
if (!proceed)
|
|
239
|
-
continue;
|
|
240
|
-
}
|
|
241
|
-
return picked;
|
|
242
|
-
}
|
|
243
|
-
}
|
|
244
|
-
// ---------------------------------------------------------------------------
|
|
245
|
-
// Step 3: review provider auth
|
|
246
|
-
// ---------------------------------------------------------------------------
|
|
247
|
-
export async function stepReviewAuth(io, provider, detection) {
|
|
248
|
-
switch (provider) {
|
|
249
|
-
case "main-native":
|
|
250
|
-
return {};
|
|
251
|
-
case "codex":
|
|
252
|
-
return stepCodexAuth(io, detection);
|
|
253
|
-
case "anthropic":
|
|
254
|
-
return stepAnthropicAuth(io, detection);
|
|
255
|
-
case "openai":
|
|
256
|
-
return stepOpenaiAuth(io, detection);
|
|
257
|
-
case "litellm":
|
|
258
|
-
return stepLitellmAuth(io, detection);
|
|
259
|
-
}
|
|
260
|
-
}
|
|
261
|
-
async function stepCodexAuth(io, detection) {
|
|
262
|
-
if (detection.hasCodexBinary && detection.hasCodexAuth) {
|
|
263
|
-
io.print(" [ok] codex binary와 ~/.codex/auth.json이 확인됐습니다.");
|
|
264
|
-
return {};
|
|
265
|
-
}
|
|
266
|
-
if (!detection.hasCodexBinary) {
|
|
267
|
-
io.print(" [error] codex CLI가 설치돼 있지 않습니다. https://github.com/openai/codex " +
|
|
268
|
-
"에서 먼저 설치해 주세요.");
|
|
269
|
-
return {};
|
|
270
|
-
}
|
|
271
|
-
io.print([
|
|
272
|
-
" [action] codex 로그인이 필요합니다.",
|
|
273
|
-
" 다른 터미널에서 다음을 실행한 뒤 Enter를 눌러 주세요:",
|
|
274
|
-
"",
|
|
275
|
-
" codex login",
|
|
276
|
-
"",
|
|
277
|
-
].join("\n"));
|
|
278
|
-
await io.ask("완료되면 Enter", { default: "" });
|
|
279
|
-
return {};
|
|
280
|
-
}
|
|
281
|
-
async function stepAnthropicAuth(io, detection) {
|
|
282
|
-
if (detection.hasAnthropicKey) {
|
|
283
|
-
const reuse = await io.askConfirm(" ANTHROPIC_API_KEY가 이미 환경에 있습니다. 그대로 사용할까요?", true);
|
|
284
|
-
if (reuse)
|
|
285
|
-
return {};
|
|
286
|
-
}
|
|
287
|
-
const key = await io.askSecret(" ANTHROPIC_API_KEY 입력");
|
|
288
|
-
return key.trim() ? { anthropicApiKey: key.trim() } : {};
|
|
289
|
-
}
|
|
290
|
-
async function stepOpenaiAuth(io, detection) {
|
|
291
|
-
if (detection.hasOpenAiKey) {
|
|
292
|
-
const reuse = await io.askConfirm(" OPENAI_API_KEY가 이미 환경에 있습니다. 그대로 사용할까요?", true);
|
|
293
|
-
if (reuse)
|
|
294
|
-
return {};
|
|
295
|
-
}
|
|
296
|
-
const key = await io.askSecret(" OPENAI_API_KEY 입력");
|
|
297
|
-
return key.trim() ? { openaiApiKey: key.trim() } : {};
|
|
298
|
-
}
|
|
299
|
-
async function stepLitellmAuth(io, detection) {
|
|
300
|
-
io.print([
|
|
301
|
-
" LiteLLM / OpenAI-compatible 엔드포인트를 사용합니다.",
|
|
302
|
-
" 로컬에서 띄운 경우 포트를 포함한 전체 URL을 입력하세요.",
|
|
303
|
-
" 예) LiteLLM proxy: http://localhost:4000/v1",
|
|
304
|
-
" Ollama: http://localhost:11434/v1",
|
|
305
|
-
].join("\n"));
|
|
306
|
-
const baseUrl = await io.ask(" Base URL", {
|
|
307
|
-
default: detection.litellmBaseUrlValue ?? "http://localhost:4000/v1",
|
|
308
|
-
});
|
|
309
|
-
const needsAuth = await io.askConfirm(" 엔드포인트에 API key 인증이 필요한가요?", false);
|
|
310
|
-
const out = { litellmBaseUrl: baseUrl.trim() };
|
|
311
|
-
if (needsAuth) {
|
|
312
|
-
const apiKey = await io.askSecret(" LITELLM_API_KEY 입력");
|
|
313
|
-
if (apiKey.trim())
|
|
314
|
-
out.litellmApiKey = apiKey.trim();
|
|
315
|
-
}
|
|
316
|
-
return out;
|
|
317
|
-
}
|
|
318
|
-
// ---------------------------------------------------------------------------
|
|
319
|
-
// Step 4: learn provider
|
|
320
|
-
// ---------------------------------------------------------------------------
|
|
321
|
-
const LEARN_PROVIDERS = [
|
|
322
|
-
"codex",
|
|
323
|
-
"anthropic",
|
|
324
|
-
"openai",
|
|
325
|
-
"litellm",
|
|
326
|
-
];
|
|
327
|
-
export async function stepLearnProvider(io, flags, reviewProvider) {
|
|
328
|
-
if (flags.learnProvider === "same") {
|
|
329
|
-
if (reviewProvider === "main-native") {
|
|
330
|
-
io.print(" [warning] learn은 main-native를 지원하지 않습니다. " +
|
|
331
|
-
"별도 provider를 선택해야 합니다.");
|
|
332
|
-
}
|
|
333
|
-
else {
|
|
334
|
-
return reviewProvider;
|
|
335
|
-
}
|
|
336
|
-
}
|
|
337
|
-
else if (flags.learnProvider) {
|
|
338
|
-
return flags.learnProvider;
|
|
339
|
-
}
|
|
340
|
-
if (reviewProvider !== "main-native") {
|
|
341
|
-
const reuse = await io.askConfirm(`Learn (background 작업)도 review와 동일하게 ${reviewProvider}를 사용할까요?`, true);
|
|
342
|
-
if (reuse)
|
|
343
|
-
return reviewProvider;
|
|
344
|
-
}
|
|
345
|
-
return io.askChoice("Learn / govern / promote용 provider를 선택하세요 (main-native는 지원되지 않습니다).", LEARN_PROVIDERS, { default: "anthropic" });
|
|
346
|
-
}
|
|
347
|
-
// ---------------------------------------------------------------------------
|
|
348
|
-
// Step 5: learn provider auth (only if distinct from review)
|
|
349
|
-
// ---------------------------------------------------------------------------
|
|
350
|
-
export async function stepLearnAuth(io, learnProvider, reviewProvider, detection, priorSecrets) {
|
|
351
|
-
if (learnProvider === reviewProvider)
|
|
352
|
-
return priorSecrets;
|
|
353
|
-
const learnSecrets = await stepReviewAuth(io, learnProvider, detection);
|
|
354
|
-
return { ...priorSecrets, ...learnSecrets };
|
|
355
|
-
}
|
|
356
|
-
// ---------------------------------------------------------------------------
|
|
357
|
-
// Step 6: output language
|
|
358
|
-
// ---------------------------------------------------------------------------
|
|
359
|
-
export async function stepOutputLanguage(io, flags) {
|
|
360
|
-
if (flags.outputLanguage)
|
|
361
|
-
return flags.outputLanguage;
|
|
362
|
-
return io.askChoice("출력 언어 (principal-facing)", ["ko", "en"], {
|
|
363
|
-
default: "ko",
|
|
364
|
-
});
|
|
365
|
-
}
|
|
366
|
-
/**
|
|
367
|
-
* Run all six steps in sequence and return the fully-resolved
|
|
368
|
-
* decisions + secrets. Skips any step whose value is already fixed
|
|
369
|
-
* via flags.
|
|
370
|
-
*/
|
|
371
|
-
export async function runInteractivePrompts(args) {
|
|
372
|
-
const { io, flags, detection } = args;
|
|
373
|
-
const profileScope = await stepProfileScope(io, flags);
|
|
374
|
-
const reviewProvider = await stepReviewProvider(io, flags, detection);
|
|
375
|
-
const reviewSecrets = await stepReviewAuth(io, reviewProvider, detection);
|
|
376
|
-
const learnProvider = await stepLearnProvider(io, flags, reviewProvider);
|
|
377
|
-
const secrets = await stepLearnAuth(io, learnProvider, reviewProvider, detection, reviewSecrets);
|
|
378
|
-
const outputLanguage = await stepOutputLanguage(io, flags);
|
|
379
|
-
const decisions = {
|
|
380
|
-
profileScope,
|
|
381
|
-
reviewProvider,
|
|
382
|
-
learnProvider,
|
|
383
|
-
outputLanguage,
|
|
384
|
-
};
|
|
385
|
-
if (secrets.litellmBaseUrl) {
|
|
386
|
-
decisions.litellmBaseUrl = secrets.litellmBaseUrl;
|
|
387
|
-
}
|
|
388
|
-
return { decisions, secrets };
|
|
389
|
-
}
|
|
@@ -1,293 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect } from "vitest";
|
|
2
|
-
import { runInteractivePrompts, stepLearnProvider, stepOutputLanguage, stepProfileScope, stepReviewAuth, stepReviewProvider, } from "./prompts.js";
|
|
3
|
-
class ScriptedIo {
|
|
4
|
-
script;
|
|
5
|
-
log = [];
|
|
6
|
-
i = 0;
|
|
7
|
-
constructor(script) {
|
|
8
|
-
this.script = script;
|
|
9
|
-
}
|
|
10
|
-
next(kind) {
|
|
11
|
-
if (this.i >= this.script.length) {
|
|
12
|
-
throw new Error(`ScriptedIo: no more scripted answers (requested ${kind}; log=${this.log.join(" | ")})`);
|
|
13
|
-
}
|
|
14
|
-
const entry = this.script[this.i++];
|
|
15
|
-
if (!entry || entry.kind !== kind) {
|
|
16
|
-
throw new Error(`ScriptedIo: expected ${kind}, got ${entry?.kind ?? "end-of-script"} at step ${this.i}`);
|
|
17
|
-
}
|
|
18
|
-
return entry;
|
|
19
|
-
}
|
|
20
|
-
async ask(prompt) {
|
|
21
|
-
this.log.push(`ask:${prompt}`);
|
|
22
|
-
return this.next("ask").answer;
|
|
23
|
-
}
|
|
24
|
-
async askChoice(prompt) {
|
|
25
|
-
this.log.push(`askChoice:${prompt}`);
|
|
26
|
-
return this.next("askChoice")
|
|
27
|
-
.answer;
|
|
28
|
-
}
|
|
29
|
-
async askConfirm(prompt) {
|
|
30
|
-
this.log.push(`askConfirm:${prompt}`);
|
|
31
|
-
return this.next("askConfirm")
|
|
32
|
-
.answer;
|
|
33
|
-
}
|
|
34
|
-
async askSecret(prompt) {
|
|
35
|
-
this.log.push(`askSecret:${prompt}`);
|
|
36
|
-
return this.next("askSecret").answer;
|
|
37
|
-
}
|
|
38
|
-
print(text) {
|
|
39
|
-
this.log.push(`print:${text}`);
|
|
40
|
-
}
|
|
41
|
-
close() {
|
|
42
|
-
this.log.push("close");
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
const BLANK_DETECTION = {
|
|
46
|
-
existingGlobalConfig: false,
|
|
47
|
-
existingProjectConfig: false,
|
|
48
|
-
hasAnthropicKey: false,
|
|
49
|
-
hasOpenAiKey: false,
|
|
50
|
-
hasLitellmBaseUrl: false,
|
|
51
|
-
hasCodexBinary: false,
|
|
52
|
-
hasCodexAuth: false,
|
|
53
|
-
hostIsClaudeCode: false,
|
|
54
|
-
};
|
|
55
|
-
const EMPTY_FLAGS = {
|
|
56
|
-
nonInteractive: false,
|
|
57
|
-
reconfigure: false,
|
|
58
|
-
skipValidation: false,
|
|
59
|
-
dryRun: false,
|
|
60
|
-
};
|
|
61
|
-
// ---------------------------------------------------------------------------
|
|
62
|
-
// Step 1: profile scope
|
|
63
|
-
// ---------------------------------------------------------------------------
|
|
64
|
-
describe("stepProfileScope", () => {
|
|
65
|
-
it("prompts when flag is unset", async () => {
|
|
66
|
-
const io = new ScriptedIo([{ kind: "askChoice", answer: "global" }]);
|
|
67
|
-
const scope = await stepProfileScope(io, EMPTY_FLAGS);
|
|
68
|
-
expect(scope).toBe("global");
|
|
69
|
-
});
|
|
70
|
-
it("skips prompt when flag is preset", async () => {
|
|
71
|
-
const io = new ScriptedIo([]);
|
|
72
|
-
const scope = await stepProfileScope(io, {
|
|
73
|
-
...EMPTY_FLAGS,
|
|
74
|
-
profileScope: "project",
|
|
75
|
-
});
|
|
76
|
-
expect(scope).toBe("project");
|
|
77
|
-
expect(io.log).toEqual([]);
|
|
78
|
-
});
|
|
79
|
-
});
|
|
80
|
-
// ---------------------------------------------------------------------------
|
|
81
|
-
// Step 2: review provider
|
|
82
|
-
// ---------------------------------------------------------------------------
|
|
83
|
-
describe("stepReviewProvider", () => {
|
|
84
|
-
it("accepts flag verbatim", async () => {
|
|
85
|
-
const io = new ScriptedIo([]);
|
|
86
|
-
const provider = await stepReviewProvider(io, { ...EMPTY_FLAGS, reviewProvider: "codex" }, BLANK_DETECTION);
|
|
87
|
-
expect(provider).toBe("codex");
|
|
88
|
-
});
|
|
89
|
-
it("warns when flag=main-native but host is not Claude Code", async () => {
|
|
90
|
-
const io = new ScriptedIo([]);
|
|
91
|
-
await stepReviewProvider(io, { ...EMPTY_FLAGS, reviewProvider: "main-native" }, BLANK_DETECTION);
|
|
92
|
-
expect(io.log.some((l) => l.includes("main-native"))).toBe(true);
|
|
93
|
-
});
|
|
94
|
-
it("prompts and requires confirmation for main-native without Claude Code", async () => {
|
|
95
|
-
const io = new ScriptedIo([
|
|
96
|
-
{ kind: "askChoice", answer: "main-native" },
|
|
97
|
-
{ kind: "askConfirm", answer: false },
|
|
98
|
-
{ kind: "askChoice", answer: "anthropic" },
|
|
99
|
-
]);
|
|
100
|
-
const provider = await stepReviewProvider(io, EMPTY_FLAGS, BLANK_DETECTION);
|
|
101
|
-
expect(provider).toBe("anthropic");
|
|
102
|
-
});
|
|
103
|
-
it("accepts main-native without reprompt when Claude Code is detected", async () => {
|
|
104
|
-
const io = new ScriptedIo([{ kind: "askChoice", answer: "main-native" }]);
|
|
105
|
-
const provider = await stepReviewProvider(io, EMPTY_FLAGS, {
|
|
106
|
-
...BLANK_DETECTION,
|
|
107
|
-
hostIsClaudeCode: true,
|
|
108
|
-
});
|
|
109
|
-
expect(provider).toBe("main-native");
|
|
110
|
-
});
|
|
111
|
-
});
|
|
112
|
-
// ---------------------------------------------------------------------------
|
|
113
|
-
// Step 3: review auth (anthropic / openai / litellm / codex / main-native)
|
|
114
|
-
// ---------------------------------------------------------------------------
|
|
115
|
-
describe("stepReviewAuth", () => {
|
|
116
|
-
it("main-native returns empty secrets with no prompts", async () => {
|
|
117
|
-
const io = new ScriptedIo([]);
|
|
118
|
-
const secrets = await stepReviewAuth(io, "main-native", BLANK_DETECTION);
|
|
119
|
-
expect(secrets).toEqual({});
|
|
120
|
-
});
|
|
121
|
-
it("anthropic: reuse existing env key when user confirms", async () => {
|
|
122
|
-
const io = new ScriptedIo([{ kind: "askConfirm", answer: true }]);
|
|
123
|
-
const secrets = await stepReviewAuth(io, "anthropic", {
|
|
124
|
-
...BLANK_DETECTION,
|
|
125
|
-
hasAnthropicKey: true,
|
|
126
|
-
});
|
|
127
|
-
expect(secrets).toEqual({});
|
|
128
|
-
});
|
|
129
|
-
it("anthropic: prompts for key when none present", async () => {
|
|
130
|
-
const io = new ScriptedIo([
|
|
131
|
-
{ kind: "askSecret", answer: "sk-ant-xyz" },
|
|
132
|
-
]);
|
|
133
|
-
const secrets = await stepReviewAuth(io, "anthropic", BLANK_DETECTION);
|
|
134
|
-
expect(secrets).toEqual({ anthropicApiKey: "sk-ant-xyz" });
|
|
135
|
-
});
|
|
136
|
-
it("litellm: captures base_url and optional auth key", async () => {
|
|
137
|
-
const io = new ScriptedIo([
|
|
138
|
-
{ kind: "ask", answer: "http://localhost:11434/v1" },
|
|
139
|
-
{ kind: "askConfirm", answer: true },
|
|
140
|
-
{ kind: "askSecret", answer: "sk-proxy-xxx" },
|
|
141
|
-
]);
|
|
142
|
-
const secrets = await stepReviewAuth(io, "litellm", BLANK_DETECTION);
|
|
143
|
-
expect(secrets).toEqual({
|
|
144
|
-
litellmBaseUrl: "http://localhost:11434/v1",
|
|
145
|
-
litellmApiKey: "sk-proxy-xxx",
|
|
146
|
-
});
|
|
147
|
-
});
|
|
148
|
-
it("codex: reports ready when binary and auth both present", async () => {
|
|
149
|
-
const io = new ScriptedIo([]);
|
|
150
|
-
const secrets = await stepReviewAuth(io, "codex", {
|
|
151
|
-
...BLANK_DETECTION,
|
|
152
|
-
hasCodexBinary: true,
|
|
153
|
-
hasCodexAuth: true,
|
|
154
|
-
});
|
|
155
|
-
expect(secrets).toEqual({});
|
|
156
|
-
expect(io.log.some((l) => l.includes("[ok]"))).toBe(true);
|
|
157
|
-
});
|
|
158
|
-
it("codex: surfaces install instructions when binary missing", async () => {
|
|
159
|
-
const io = new ScriptedIo([]);
|
|
160
|
-
await stepReviewAuth(io, "codex", BLANK_DETECTION);
|
|
161
|
-
expect(io.log.some((l) => l.includes("codex CLI가 설치"))).toBe(true);
|
|
162
|
-
});
|
|
163
|
-
it("codex: guides user through `codex login` when auth missing", async () => {
|
|
164
|
-
const io = new ScriptedIo([{ kind: "ask", answer: "" }]);
|
|
165
|
-
await stepReviewAuth(io, "codex", {
|
|
166
|
-
...BLANK_DETECTION,
|
|
167
|
-
hasCodexBinary: true,
|
|
168
|
-
});
|
|
169
|
-
expect(io.log.some((l) => l.includes("codex login"))).toBe(true);
|
|
170
|
-
});
|
|
171
|
-
});
|
|
172
|
-
// ---------------------------------------------------------------------------
|
|
173
|
-
// Step 4: learn provider
|
|
174
|
-
// ---------------------------------------------------------------------------
|
|
175
|
-
describe("stepLearnProvider", () => {
|
|
176
|
-
it("respects learn=same when review is not main-native", async () => {
|
|
177
|
-
const io = new ScriptedIo([]);
|
|
178
|
-
const learn = await stepLearnProvider(io, { ...EMPTY_FLAGS, learnProvider: "same" }, "anthropic");
|
|
179
|
-
expect(learn).toBe("anthropic");
|
|
180
|
-
});
|
|
181
|
-
it("warns when learn=same but review=main-native (not supported)", async () => {
|
|
182
|
-
const io = new ScriptedIo([
|
|
183
|
-
{ kind: "askChoice", answer: "anthropic" },
|
|
184
|
-
]);
|
|
185
|
-
const learn = await stepLearnProvider(io, { ...EMPTY_FLAGS, learnProvider: "same" }, "main-native");
|
|
186
|
-
expect(learn).toBe("anthropic");
|
|
187
|
-
expect(io.log.some((l) => l.includes("main-native"))).toBe(true);
|
|
188
|
-
});
|
|
189
|
-
it("offers review provider as reuse default when compatible", async () => {
|
|
190
|
-
const io = new ScriptedIo([{ kind: "askConfirm", answer: true }]);
|
|
191
|
-
const learn = await stepLearnProvider(io, EMPTY_FLAGS, "anthropic");
|
|
192
|
-
expect(learn).toBe("anthropic");
|
|
193
|
-
});
|
|
194
|
-
it("prompts for separate learn provider when user declines reuse", async () => {
|
|
195
|
-
const io = new ScriptedIo([
|
|
196
|
-
{ kind: "askConfirm", answer: false },
|
|
197
|
-
{ kind: "askChoice", answer: "codex" },
|
|
198
|
-
]);
|
|
199
|
-
const learn = await stepLearnProvider(io, EMPTY_FLAGS, "anthropic");
|
|
200
|
-
expect(learn).toBe("codex");
|
|
201
|
-
});
|
|
202
|
-
});
|
|
203
|
-
// ---------------------------------------------------------------------------
|
|
204
|
-
// Step 6: output language
|
|
205
|
-
// ---------------------------------------------------------------------------
|
|
206
|
-
describe("stepOutputLanguage", () => {
|
|
207
|
-
it("returns flag value without prompting", async () => {
|
|
208
|
-
const io = new ScriptedIo([]);
|
|
209
|
-
const lang = await stepOutputLanguage(io, {
|
|
210
|
-
...EMPTY_FLAGS,
|
|
211
|
-
outputLanguage: "en",
|
|
212
|
-
});
|
|
213
|
-
expect(lang).toBe("en");
|
|
214
|
-
});
|
|
215
|
-
it("prompts when flag absent", async () => {
|
|
216
|
-
const io = new ScriptedIo([{ kind: "askChoice", answer: "ko" }]);
|
|
217
|
-
const lang = await stepOutputLanguage(io, EMPTY_FLAGS);
|
|
218
|
-
expect(lang).toBe("ko");
|
|
219
|
-
});
|
|
220
|
-
});
|
|
221
|
-
// ---------------------------------------------------------------------------
|
|
222
|
-
// Full orchestrator
|
|
223
|
-
// ---------------------------------------------------------------------------
|
|
224
|
-
describe("runInteractivePrompts — end-to-end", () => {
|
|
225
|
-
it("main-native review + anthropic learn path", async () => {
|
|
226
|
-
const io = new ScriptedIo([
|
|
227
|
-
// Step 1: profile scope
|
|
228
|
-
{ kind: "askChoice", answer: "project" },
|
|
229
|
-
// Step 2: review provider
|
|
230
|
-
{ kind: "askChoice", answer: "main-native" },
|
|
231
|
-
// Step 3: no auth for main-native (no prompts)
|
|
232
|
-
// Step 4: learn provider (reuse review? → main-native can't, so pick)
|
|
233
|
-
{ kind: "askChoice", answer: "anthropic" },
|
|
234
|
-
// Step 5: learn auth (anthropic, not in env, needs key)
|
|
235
|
-
{ kind: "askSecret", answer: "sk-ant-xyz" },
|
|
236
|
-
// Step 6: output language
|
|
237
|
-
{ kind: "askChoice", answer: "ko" },
|
|
238
|
-
]);
|
|
239
|
-
const result = await runInteractivePrompts({
|
|
240
|
-
io,
|
|
241
|
-
flags: EMPTY_FLAGS,
|
|
242
|
-
detection: { ...BLANK_DETECTION, hostIsClaudeCode: true },
|
|
243
|
-
});
|
|
244
|
-
expect(result.decisions).toEqual({
|
|
245
|
-
profileScope: "project",
|
|
246
|
-
reviewProvider: "main-native",
|
|
247
|
-
learnProvider: "anthropic",
|
|
248
|
-
outputLanguage: "ko",
|
|
249
|
-
});
|
|
250
|
-
expect(result.secrets).toEqual({ anthropicApiKey: "sk-ant-xyz" });
|
|
251
|
-
});
|
|
252
|
-
it("anthropic review + same learn provider → single auth prompt", async () => {
|
|
253
|
-
const io = new ScriptedIo([
|
|
254
|
-
{ kind: "askChoice", answer: "project" },
|
|
255
|
-
{ kind: "askChoice", answer: "anthropic" },
|
|
256
|
-
{ kind: "askSecret", answer: "sk-ant-1" },
|
|
257
|
-
{ kind: "askConfirm", answer: true },
|
|
258
|
-
{ kind: "askChoice", answer: "ko" },
|
|
259
|
-
]);
|
|
260
|
-
const result = await runInteractivePrompts({
|
|
261
|
-
io,
|
|
262
|
-
flags: EMPTY_FLAGS,
|
|
263
|
-
detection: BLANK_DETECTION,
|
|
264
|
-
});
|
|
265
|
-
expect(result.decisions.reviewProvider).toBe("anthropic");
|
|
266
|
-
expect(result.decisions.learnProvider).toBe("anthropic");
|
|
267
|
-
expect(result.secrets.anthropicApiKey).toBe("sk-ant-1");
|
|
268
|
-
});
|
|
269
|
-
it("flag-only path skips all prompts (hypothetical non-interactive)", async () => {
|
|
270
|
-
const io = new ScriptedIo([]);
|
|
271
|
-
const result = await runInteractivePrompts({
|
|
272
|
-
io,
|
|
273
|
-
flags: {
|
|
274
|
-
...EMPTY_FLAGS,
|
|
275
|
-
profileScope: "global",
|
|
276
|
-
reviewProvider: "codex",
|
|
277
|
-
learnProvider: "codex",
|
|
278
|
-
outputLanguage: "en",
|
|
279
|
-
},
|
|
280
|
-
detection: {
|
|
281
|
-
...BLANK_DETECTION,
|
|
282
|
-
hasCodexBinary: true,
|
|
283
|
-
hasCodexAuth: true,
|
|
284
|
-
},
|
|
285
|
-
});
|
|
286
|
-
expect(result.decisions).toEqual({
|
|
287
|
-
profileScope: "global",
|
|
288
|
-
reviewProvider: "codex",
|
|
289
|
-
learnProvider: "codex",
|
|
290
|
-
outputLanguage: "en",
|
|
291
|
-
});
|
|
292
|
-
});
|
|
293
|
-
});
|