opencode-resolve 0.1.18 → 0.1.20
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/README.ko.md +2 -2
- package/README.md +2 -2
- package/package.json +1 -1
- package/scripts/postinstall.mjs +283 -37
package/README.ko.md
CHANGED
package/README.md
CHANGED
|
@@ -16,8 +16,8 @@
|
|
|
16
16
|
It is not a standalone app, model provider, API key manager, or replacement for `opencode.json`.
|
|
17
17
|
|
|
18
18
|
```sh
|
|
19
|
-
npm install -g opencode-resolve
|
|
20
|
-
opencode
|
|
19
|
+
npm install -g opencode-resolve@latest
|
|
20
|
+
opencode-resolve setup
|
|
21
21
|
```
|
|
22
22
|
|
|
23
23
|
## Contents
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "opencode-resolve",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.20",
|
|
4
4
|
"description": "OpenCode plugin that adds a lightweight resolver/coder harness for continuous agentic coding.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"homepage": "https://jshsakura.github.io/opencode-resolve/",
|
package/scripts/postinstall.mjs
CHANGED
|
@@ -20,9 +20,24 @@ const ADDITIVE_DEFAULTS = {
|
|
|
20
20
|
autoApprove: true,
|
|
21
21
|
}
|
|
22
22
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
const
|
|
23
|
+
// Heuristic strength scoring for an arbitrary provider/model id.
|
|
24
|
+
// Higher = "stronger" / more expensive in expected behavior. Heuristic-only.
|
|
25
|
+
const STRENGTH_BOOSTS = [
|
|
26
|
+
{ re: /\b(mini|flash|nano|lite|haiku|air|small)\b/i, score: -3 },
|
|
27
|
+
{ re: /\b(claude-3|gpt-3|gpt-4o-mini|o1-mini|o3-mini|4\.5)\b/i, score: -1 },
|
|
28
|
+
{ re: /\b(gpt-4o|opus|sonnet|gemini-1\.5)\b/i, score: 1 },
|
|
29
|
+
{ re: /\b(o1|o3|o4|reason|reasoning|pro|max|ultra|gold|sota)\b/i, score: 2 },
|
|
30
|
+
{ re: /\b(5\.5|5\.4|5\.3|5\.2|5\.1|sonnet-4|opus-4|opus-5|max-5|next|2026)\b/i, score: 2 },
|
|
31
|
+
{ re: /\b(codex|coder|code|coding)\b/i, score: 1 },
|
|
32
|
+
]
|
|
33
|
+
|
|
34
|
+
// Provider-neutral agents — safe to enable for everyone (no profile-specific model needed).
|
|
35
|
+
const DEFAULT_ENABLED_AGENTS = [
|
|
36
|
+
"coder", "resolver", "explorer", "reviewer", "deep-reviewer", "planner",
|
|
37
|
+
"architect", "debugger", "researcher",
|
|
38
|
+
]
|
|
39
|
+
const GPT_ENABLED_AGENTS = [...DEFAULT_ENABLED_AGENTS, "gpt", "gpt-coder", "codex"]
|
|
40
|
+
const GLM_ENABLED_AGENTS = [...DEFAULT_ENABLED_AGENTS, "glm"]
|
|
26
41
|
|
|
27
42
|
// ZAI local MCP server bootstrap for GLM users.
|
|
28
43
|
// Do not copy provider secrets from OpenCode's auth store into opencode.json.
|
|
@@ -293,11 +308,16 @@ async function chooseExistingResolveConfigAction(scriptedAnswers) {
|
|
|
293
308
|
const rl = createPromptInterface(scriptedAnswers)
|
|
294
309
|
try {
|
|
295
310
|
console.log("")
|
|
296
|
-
console.log(
|
|
297
|
-
console.log(
|
|
298
|
-
console.log(
|
|
299
|
-
console.log("
|
|
300
|
-
|
|
311
|
+
console.log("──────────────────────────────────────────────────────────────")
|
|
312
|
+
console.log(` opencode-resolve — existing config detected`)
|
|
313
|
+
console.log(` ${resolveConfigPath}`)
|
|
314
|
+
console.log("──────────────────────────────────────────────────────────────")
|
|
315
|
+
console.log("")
|
|
316
|
+
console.log("How do you want to handle it?")
|
|
317
|
+
console.log(" 1. keep — preserve existing settings, only add missing defaults (recommended)")
|
|
318
|
+
console.log(" 2. models — re-pick models only, keep the rest")
|
|
319
|
+
console.log(" 3. reset — back up the current file and run setup from scratch (model pins preserved)")
|
|
320
|
+
const answer = await askChoice(rl, "Choice [1=keep, 2=models, 3=reset, default 1]: ", ["1", "2", "3"], "1")
|
|
301
321
|
if (answer === "2") return "models"
|
|
302
322
|
return answer === "3" ? "fresh" : "update"
|
|
303
323
|
} finally {
|
|
@@ -337,7 +357,7 @@ async function createAdaptiveResolveConfig(opencodeConfig, scriptedAnswers, opti
|
|
|
337
357
|
(process.stdin.isTTY && process.stdout.isTTY) || forcePrompt,
|
|
338
358
|
)
|
|
339
359
|
const interactivePreset = canPrompt
|
|
340
|
-
? await buildInteractivePreset(currentModel, allModels, scriptedAnswers)
|
|
360
|
+
? await buildInteractivePreset(currentModel, allModels, scriptedAnswers, opencodeConfig)
|
|
341
361
|
: undefined
|
|
342
362
|
|
|
343
363
|
if (interactivePreset) {
|
|
@@ -413,7 +433,7 @@ async function reconfigureExistingModels(opencodeConfig, scriptedAnswers) {
|
|
|
413
433
|
const forcePrompt = readInstallerOption("force_prompt") === "1"
|
|
414
434
|
const canPrompt = Boolean((process.stdin.isTTY && process.stdout.isTTY) || forcePrompt)
|
|
415
435
|
const interactivePreset = canPrompt
|
|
416
|
-
? await buildInteractivePreset(currentModel, allModels, scriptedAnswers)
|
|
436
|
+
? await buildInteractivePreset(currentModel, allModels, scriptedAnswers, opencodeConfig)
|
|
417
437
|
: undefined
|
|
418
438
|
const preset = interactivePreset ?? {
|
|
419
439
|
label: getPresetLabel(currentModel),
|
|
@@ -582,7 +602,7 @@ function buildGPTOnlyPreset(model, gptModels) {
|
|
|
582
602
|
}
|
|
583
603
|
}
|
|
584
604
|
|
|
585
|
-
async function buildInteractivePreset(currentModel, allModels, scriptedAnswers) {
|
|
605
|
+
async function buildInteractivePreset(currentModel, allModels, scriptedAnswers, opencodeConfig) {
|
|
586
606
|
const choices = {
|
|
587
607
|
gpt: collectModelChoices(allModels, isGPTModel, OPENAI_MODEL_HINTS),
|
|
588
608
|
glm: collectModelChoices(allModels, isGLMModel, GLM_MODEL_HINTS),
|
|
@@ -592,6 +612,11 @@ async function buildInteractivePreset(currentModel, allModels, scriptedAnswers)
|
|
|
592
612
|
if (isGLMModel(currentModel)) choices.glm = unique([currentModel, ...choices.glm])
|
|
593
613
|
}
|
|
594
614
|
|
|
615
|
+
const hasGPTOrGLM = allModels.some((m) => isGPTModel(m) || isGLMModel(m))
|
|
616
|
+
const providerCount = opencodeConfig ? detectProvidersFromConfig(opencodeConfig).length : 0
|
|
617
|
+
const offerAuto = !hasGPTOrGLM && providerCount > 0
|
|
618
|
+
const defaultChoice = offerAuto ? "4" : "1"
|
|
619
|
+
|
|
595
620
|
const rl = createPromptInterface(scriptedAnswers)
|
|
596
621
|
try {
|
|
597
622
|
console.log("")
|
|
@@ -601,10 +626,22 @@ async function buildInteractivePreset(currentModel, allModels, scriptedAnswers)
|
|
|
601
626
|
console.log("──────────────────────────────────────────────────────────────")
|
|
602
627
|
console.log("")
|
|
603
628
|
console.log(`[${packageName}] Step 1/2 — Choose resolve profile:`)
|
|
604
|
-
console.log(
|
|
629
|
+
console.log(` 1. mix — neutral resolver plus optional Codex and GLM primary agents${offerAuto ? "" : " (recommended)"}`)
|
|
605
630
|
console.log(" 2. gpt — GPT/Codex-only, three-tier")
|
|
606
631
|
console.log(" 3. glm — GLM-only, three-tier")
|
|
607
|
-
|
|
632
|
+
if (offerAuto) {
|
|
633
|
+
console.log(` 4. auto — provider-agnostic tier setup from detected providers (recommended)`)
|
|
634
|
+
}
|
|
635
|
+
const validChoices = offerAuto ? ["1", "2", "3", "4"] : ["1", "2", "3"]
|
|
636
|
+
const profileAnswer = await askChoice(rl, `Profile [${validChoices.join(",")}, default ${defaultChoice}]: `, validChoices, defaultChoice)
|
|
637
|
+
|
|
638
|
+
if (profileAnswer === "4" && offerAuto) {
|
|
639
|
+
rl.close()
|
|
640
|
+
const generic = await buildGenericInteractivePreset(currentModel, opencodeConfig, scriptedAnswers)
|
|
641
|
+
if (generic) return generic
|
|
642
|
+
// fall through to mix if generic returned nothing
|
|
643
|
+
}
|
|
644
|
+
|
|
608
645
|
const profile = profileAnswer === "2" ? "gpt" : profileAnswer === "3" ? "glm" : "mix"
|
|
609
646
|
|
|
610
647
|
if (profile === "gpt") {
|
|
@@ -620,12 +657,9 @@ async function buildInteractivePreset(currentModel, allModels, scriptedAnswers)
|
|
|
620
657
|
}
|
|
621
658
|
|
|
622
659
|
if (profile === "glm") {
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
: choices.glm.map((m) => m.replace(/^zai-coding-plan\//, "zai/"))
|
|
627
|
-
const deduped = unique([...glmChoices.filter((m) => isGLMModel(m)), ...GLM_MODEL_HINTS])
|
|
628
|
-
const tiers = await askThreeTier(rl, "GLM", deduped)
|
|
660
|
+
// zai and zai-coding-plan are distinct providers — show whatever the user actually
|
|
661
|
+
// has configured. No rewriting between them.
|
|
662
|
+
const tiers = await askThreeTier(rl, "GLM", choices.glm)
|
|
629
663
|
return {
|
|
630
664
|
label: "glm-three-tier",
|
|
631
665
|
profile: "glm",
|
|
@@ -675,14 +709,22 @@ function createPromptInterface(scriptedAnswers) {
|
|
|
675
709
|
|
|
676
710
|
async function askThreeTier(rl, label, models) {
|
|
677
711
|
const choices = models.length > 0 ? models : (label.toLowerCase().includes("glm") ? GLM_MODEL_HINTS : OPENAI_MODEL_HINTS)
|
|
678
|
-
console.log("")
|
|
679
|
-
console.log(`[${packageName}] ${label} model choices:`)
|
|
680
|
-
choices.forEach((model, index) => console.log(` ${index + 1}. ${model}`))
|
|
681
712
|
const defaults = chooseThreeTier(choices, label.toLowerCase().includes("glm") ? "glm" : "gpt")
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
713
|
+
while (true) {
|
|
714
|
+
console.log("")
|
|
715
|
+
console.log(`[${packageName}] ${label} model choices:`)
|
|
716
|
+
choices.forEach((model, index) => console.log(` ${index + 1}. ${model}`))
|
|
717
|
+
const bronze = await askModel(rl, choices, `Pick ${label} bronze/scout [default ${defaults.bronze}]: `, defaults.bronze)
|
|
718
|
+
const silver = await askModel(rl, choices, `Pick ${label} silver/coder [default ${defaults.silver}]: `, defaults.silver)
|
|
719
|
+
const gold = await askModel(rl, choices, `Pick ${label} gold/reasoner [default ${defaults.gold}]: `, defaults.gold)
|
|
720
|
+
console.log("")
|
|
721
|
+
console.log(` → bronze: ${bronze}`)
|
|
722
|
+
console.log(` → silver: ${silver}`)
|
|
723
|
+
console.log(` → gold: ${gold}`)
|
|
724
|
+
const ok = await askYesNo(rl, `Confirm ${label} picks? [Y/n] (n re-asks all three): `, true)
|
|
725
|
+
if (ok) return { bronze, silver, gold }
|
|
726
|
+
console.log(`[${packageName}] re-asking ${label} picks…`)
|
|
727
|
+
}
|
|
686
728
|
}
|
|
687
729
|
|
|
688
730
|
async function askModel(rl, choices, question, defaultValue) {
|
|
@@ -771,21 +813,30 @@ function buildGLMThreeTierModels(tiers) {
|
|
|
771
813
|
|
|
772
814
|
function collectModelChoices(allModels, predicate, hints, includeFallbackHints = true) {
|
|
773
815
|
const detected = allModels.filter(predicate)
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
:
|
|
778
|
-
|
|
816
|
+
if (detected.length > 0) {
|
|
817
|
+
// Only show the user's own models when they have any — never pollute the picker
|
|
818
|
+
// with hint IDs they don't actually have configured in opencode.json.
|
|
819
|
+
return predicate === isGLMModel ? sortGLMModelChoices(unique(detected)) : unique(detected)
|
|
820
|
+
}
|
|
821
|
+
// Fallback: user has zero models of this family — show the hint list so the
|
|
822
|
+
// picker still has something to display.
|
|
823
|
+
if (!includeFallbackHints) return []
|
|
824
|
+
const choices = unique(hints)
|
|
779
825
|
return predicate === isGLMModel ? sortGLMModelChoices(choices) : choices
|
|
780
826
|
}
|
|
781
827
|
|
|
782
|
-
function chooseThreeTier(models, family
|
|
783
|
-
const
|
|
784
|
-
|
|
828
|
+
function chooseThreeTier(models, family) {
|
|
829
|
+
const choices = unique(models)
|
|
830
|
+
if (choices.length === 0) {
|
|
831
|
+
const fallback = family === "glm" ? GLM_MODEL_HINTS : OPENAI_MODEL_HINTS
|
|
832
|
+
return chooseThreeTier(fallback, family)
|
|
833
|
+
}
|
|
834
|
+
// Sort by inferred strength (weak → strong) and pick bronze/silver/gold by position.
|
|
835
|
+
const sorted = sortModelsByStrength(choices)
|
|
785
836
|
return {
|
|
786
|
-
bronze:
|
|
787
|
-
silver:
|
|
788
|
-
gold:
|
|
837
|
+
bronze: sorted[0],
|
|
838
|
+
silver: sorted[Math.min(1, sorted.length - 1)] ?? sorted[0],
|
|
839
|
+
gold: sorted[sorted.length - 1],
|
|
789
840
|
}
|
|
790
841
|
}
|
|
791
842
|
|
|
@@ -853,6 +904,201 @@ function getPresetLabel(currentModel) {
|
|
|
853
904
|
return "inherited"
|
|
854
905
|
}
|
|
855
906
|
|
|
907
|
+
function inferModelStrength(modelId) {
|
|
908
|
+
if (typeof modelId !== "string") return 0
|
|
909
|
+
let score = 0
|
|
910
|
+
for (const { re, score: s } of STRENGTH_BOOSTS) {
|
|
911
|
+
if (re.test(modelId)) score += s
|
|
912
|
+
}
|
|
913
|
+
return score
|
|
914
|
+
}
|
|
915
|
+
|
|
916
|
+
function sortModelsByStrength(models) {
|
|
917
|
+
return [...models].sort((a, b) => inferModelStrength(a) - inferModelStrength(b))
|
|
918
|
+
}
|
|
919
|
+
|
|
920
|
+
function detectProvidersFromConfig(config) {
|
|
921
|
+
const providers = new Map()
|
|
922
|
+
const note = (providerId, modelId) => {
|
|
923
|
+
if (!providerId) return
|
|
924
|
+
const list = providers.get(providerId) ?? []
|
|
925
|
+
if (modelId && !list.includes(modelId)) list.push(modelId)
|
|
926
|
+
providers.set(providerId, list)
|
|
927
|
+
}
|
|
928
|
+
if (isObject(config.provider)) {
|
|
929
|
+
for (const [providerId, providerConfig] of Object.entries(config.provider)) {
|
|
930
|
+
providers.set(providerId, providers.get(providerId) ?? [])
|
|
931
|
+
if (isObject(providerConfig) && isObject(providerConfig.models)) {
|
|
932
|
+
for (const [modelKey, modelEntry] of Object.entries(providerConfig.models)) {
|
|
933
|
+
if (typeof modelKey === "string" && modelKey.length > 0) {
|
|
934
|
+
note(providerId, qualifyModelId(providerId, modelKey))
|
|
935
|
+
}
|
|
936
|
+
if (typeof modelEntry === "string") note(providerId, qualifyModelId(providerId, modelEntry))
|
|
937
|
+
else if (isObject(modelEntry) && typeof modelEntry.id === "string") {
|
|
938
|
+
note(providerId, qualifyModelId(providerId, modelEntry.id))
|
|
939
|
+
}
|
|
940
|
+
}
|
|
941
|
+
}
|
|
942
|
+
}
|
|
943
|
+
}
|
|
944
|
+
// Add implicit providers from top-level model and agent overrides
|
|
945
|
+
const additional = []
|
|
946
|
+
if (typeof config.model === "string" && config.model.includes("/")) additional.push(config.model)
|
|
947
|
+
if (isObject(config.agent)) {
|
|
948
|
+
for (const agent of Object.values(config.agent)) {
|
|
949
|
+
if (isObject(agent) && typeof agent.model === "string" && agent.model.includes("/")) {
|
|
950
|
+
additional.push(agent.model)
|
|
951
|
+
}
|
|
952
|
+
}
|
|
953
|
+
}
|
|
954
|
+
for (const fullId of additional) {
|
|
955
|
+
const slash = fullId.indexOf("/")
|
|
956
|
+
if (slash < 0) continue
|
|
957
|
+
note(fullId.slice(0, slash), fullId)
|
|
958
|
+
}
|
|
959
|
+
return [...providers.entries()]
|
|
960
|
+
.map(([id, models]) => ({ id, models: unique(models) }))
|
|
961
|
+
.filter((p) => p.models.length > 0)
|
|
962
|
+
}
|
|
963
|
+
|
|
964
|
+
function pickTierShapeForModelCount(count) {
|
|
965
|
+
if (count >= 3) return "three"
|
|
966
|
+
if (count === 2) return "two"
|
|
967
|
+
return "single"
|
|
968
|
+
}
|
|
969
|
+
|
|
970
|
+
function buildGenericResolveModels(tiers) {
|
|
971
|
+
if (tiers.shape === "three") {
|
|
972
|
+
return {
|
|
973
|
+
bronze: tiers.bronze,
|
|
974
|
+
silver: tiers.silver,
|
|
975
|
+
gold: tiers.gold,
|
|
976
|
+
explorer: "bronze",
|
|
977
|
+
coder: "silver",
|
|
978
|
+
resolver: "gold",
|
|
979
|
+
reviewer: "gold",
|
|
980
|
+
"deep-reviewer": "gold",
|
|
981
|
+
planner: "gold",
|
|
982
|
+
architect: "gold",
|
|
983
|
+
debugger: "silver",
|
|
984
|
+
researcher: "bronze",
|
|
985
|
+
}
|
|
986
|
+
}
|
|
987
|
+
if (tiers.shape === "two") {
|
|
988
|
+
return {
|
|
989
|
+
silver: tiers.silver,
|
|
990
|
+
gold: tiers.gold,
|
|
991
|
+
explorer: "silver",
|
|
992
|
+
coder: "silver",
|
|
993
|
+
resolver: "gold",
|
|
994
|
+
reviewer: "gold",
|
|
995
|
+
"deep-reviewer": "gold",
|
|
996
|
+
planner: "gold",
|
|
997
|
+
architect: "gold",
|
|
998
|
+
debugger: "silver",
|
|
999
|
+
researcher: "silver",
|
|
1000
|
+
}
|
|
1001
|
+
}
|
|
1002
|
+
return {
|
|
1003
|
+
gold: tiers.gold,
|
|
1004
|
+
explorer: "gold",
|
|
1005
|
+
coder: "gold",
|
|
1006
|
+
resolver: "gold",
|
|
1007
|
+
reviewer: "gold",
|
|
1008
|
+
"deep-reviewer": "gold",
|
|
1009
|
+
planner: "gold",
|
|
1010
|
+
architect: "gold",
|
|
1011
|
+
debugger: "gold",
|
|
1012
|
+
researcher: "gold",
|
|
1013
|
+
}
|
|
1014
|
+
}
|
|
1015
|
+
|
|
1016
|
+
async function buildGenericInteractivePreset(currentModel, opencodeConfig, scriptedAnswers) {
|
|
1017
|
+
const providers = detectProvidersFromConfig(opencodeConfig)
|
|
1018
|
+
if (providers.length === 0) return undefined
|
|
1019
|
+
|
|
1020
|
+
const rl = createPromptInterface(scriptedAnswers)
|
|
1021
|
+
try {
|
|
1022
|
+
console.log("")
|
|
1023
|
+
console.log("──────────────────────────────────────────────────────────────")
|
|
1024
|
+
console.log(` opencode-resolve setup — provider-agnostic mode`)
|
|
1025
|
+
console.log(` ${providers.length} provider${providers.length === 1 ? "" : "s"} detected in opencode.json`)
|
|
1026
|
+
console.log("──────────────────────────────────────────────────────────────")
|
|
1027
|
+
|
|
1028
|
+
// 1. Pick provider
|
|
1029
|
+
let chosenProvider
|
|
1030
|
+
if (providers.length === 1) {
|
|
1031
|
+
chosenProvider = providers[0]
|
|
1032
|
+
console.log("")
|
|
1033
|
+
console.log(`[${packageName}] only one provider available — using "${chosenProvider.id}"`)
|
|
1034
|
+
} else {
|
|
1035
|
+
console.log("")
|
|
1036
|
+
console.log(`[${packageName}] Step 1/3 — Pick provider:`)
|
|
1037
|
+
providers.forEach((p, i) => {
|
|
1038
|
+
const marker = currentModel?.startsWith(`${p.id}/`) ? " ← top-level model" : ""
|
|
1039
|
+
console.log(` ${i + 1}. ${p.id} (${p.models.length} models)${marker}`)
|
|
1040
|
+
})
|
|
1041
|
+
const defaultIdx = Math.max(1, providers.findIndex((p) => currentModel?.startsWith(`${p.id}/`)) + 1)
|
|
1042
|
+
const valid = providers.map((_, i) => String(i + 1))
|
|
1043
|
+
const answer = await askChoice(rl, `Provider [1..${providers.length}, default ${defaultIdx}]: `, valid, String(defaultIdx))
|
|
1044
|
+
chosenProvider = providers[Number.parseInt(answer, 10) - 1]
|
|
1045
|
+
}
|
|
1046
|
+
|
|
1047
|
+
const sorted = sortModelsByStrength(chosenProvider.models)
|
|
1048
|
+
const defaultShape = pickTierShapeForModelCount(sorted.length)
|
|
1049
|
+
|
|
1050
|
+
// 2. Pick tier shape
|
|
1051
|
+
console.log("")
|
|
1052
|
+
console.log(`[${packageName}] Step 2/3 — Choose tier shape:`)
|
|
1053
|
+
console.log(` 1. single — one model for every role (good for cost simplicity)`)
|
|
1054
|
+
console.log(` 2. two — fast + strong split (explorer/coder + resolver/reviewer/…)`)
|
|
1055
|
+
console.log(` 3. three — bronze + silver + gold full split (recommended when available)`)
|
|
1056
|
+
const shapeDefault = defaultShape === "three" ? "3" : defaultShape === "two" ? "2" : "1"
|
|
1057
|
+
const shapeAns = await askChoice(rl, `Tier shape [1,2,3, default ${shapeDefault}]: `, ["1", "2", "3"], shapeDefault)
|
|
1058
|
+
const shape = shapeAns === "3" ? "three" : shapeAns === "2" ? "two" : "single"
|
|
1059
|
+
|
|
1060
|
+
// 3. Pick models
|
|
1061
|
+
const weakest = sorted[0]
|
|
1062
|
+
const strongest = sorted[sorted.length - 1]
|
|
1063
|
+
const middle = sorted[Math.floor((sorted.length - 1) / 2)]
|
|
1064
|
+
console.log("")
|
|
1065
|
+
console.log(`[${packageName}] Step 3/3 — Pick models (defaults inferred from name strength):`)
|
|
1066
|
+
sorted.forEach((m, i) => console.log(` ${i + 1}. ${m}`))
|
|
1067
|
+
|
|
1068
|
+
while (true) {
|
|
1069
|
+
const tiers = { shape }
|
|
1070
|
+
if (shape === "three") {
|
|
1071
|
+
tiers.bronze = await askModel(rl, sorted, `Bronze (scout for explorer/researcher) [default ${weakest}]: `, weakest)
|
|
1072
|
+
tiers.silver = await askModel(rl, sorted, `Silver (coder/debugger) [default ${middle}]: `, middle)
|
|
1073
|
+
tiers.gold = await askModel(rl, sorted, `Gold (resolver/reviewer/deep-reviewer/planner/architect) [default ${strongest}]: `, strongest)
|
|
1074
|
+
} else if (shape === "two") {
|
|
1075
|
+
tiers.silver = await askModel(rl, sorted, `Silver (fast: coder/explorer/debugger/researcher) [default ${weakest}]: `, weakest)
|
|
1076
|
+
tiers.gold = await askModel(rl, sorted, `Gold (strong: resolver/reviewer/…/architect) [default ${strongest}]: `, strongest)
|
|
1077
|
+
} else {
|
|
1078
|
+
tiers.gold = await askModel(rl, sorted, `Model for all roles [default ${strongest}]: `, strongest)
|
|
1079
|
+
}
|
|
1080
|
+
console.log("")
|
|
1081
|
+
for (const [k, v] of Object.entries(tiers)) {
|
|
1082
|
+
if (k === "shape") continue
|
|
1083
|
+
console.log(` → ${k.padEnd(7)} ${v}`)
|
|
1084
|
+
}
|
|
1085
|
+
const ok = await askYesNo(rl, `Confirm picks? [Y/n] (n re-asks all): `, true)
|
|
1086
|
+
if (ok) {
|
|
1087
|
+
return {
|
|
1088
|
+
label: `generic-${shape}-tier`,
|
|
1089
|
+
profile: "mix",
|
|
1090
|
+
enabled: DEFAULT_ENABLED_AGENTS,
|
|
1091
|
+
models: buildGenericResolveModels(tiers),
|
|
1092
|
+
agents: {},
|
|
1093
|
+
}
|
|
1094
|
+
}
|
|
1095
|
+
console.log(`[${packageName}] re-asking picks…`)
|
|
1096
|
+
}
|
|
1097
|
+
} finally {
|
|
1098
|
+
rl.close()
|
|
1099
|
+
}
|
|
1100
|
+
}
|
|
1101
|
+
|
|
856
1102
|
function isGLMModel(currentModel) {
|
|
857
1103
|
if (!currentModel) return false
|
|
858
1104
|
const lower = currentModel.toLowerCase()
|