@gr8ful/spf 0.9.1 → 0.9.2

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.
@@ -95,25 +95,49 @@ export async function runInterview(asker, ctx) {
95
95
  defaults.coding_agent = codingAgent;
96
96
  if (codingAgent === "claude_code") {
97
97
  if (!ctx.claudeOnPath) {
98
- asker.note("warning: `claude` was not found on PATH — install it (or set a launch command below) before running spf.");
98
+ asker.note("warning: `claude` was not found on PATH — install it (or pick a launch command below) before running spf.");
99
+ }
100
+ // A select, not free text: the three shapes below are the only ones
101
+ // agent_cc.ts actually supports (see its module doc comment), and
102
+ // getting one wrong by hand-typing is exactly how the two doctor.ts
103
+ // hard-fails here (a missing "--" separator, a missing "--model" before
104
+ // it) get triggered in practice. "ollama" is fully determined by
105
+ // picking it — no further typing, and no way to omit the `--model`
106
+ // flag or the `{model}` token by mistake. "custom" is the escape hatch
107
+ // for anything else (a proxy, a bespoke wrapper script).
108
+ const launchMode = await asker.select("Launch command for the `claude` CLI", [
109
+ { value: "claude", label: "claude — resolved from PATH" },
110
+ { value: "ollama", label: "ollama launch claude --model {model} — routes through Ollama, per-agent model" },
111
+ { value: "custom", label: "custom — type your own launch command" },
112
+ ], "claude");
113
+ let launchCommand = "claude";
114
+ if (launchMode === "ollama") {
115
+ launchCommand = "ollama launch claude --model {model}";
116
+ asker.note("{model} substitutes with each agent's own model: field before spawning — the model asked for next, or per-agent later via \"Customize models per agent?\".");
117
+ }
118
+ else if (launchMode === "custom") {
119
+ launchCommand = await asker.text("Custom launch command", { default: "claude" });
99
120
  }
100
- // Kept short on purpose: TextPromptView renders the label and the
101
- // live-growing input on one unwrapped terminal row, so a long label
102
- // desyncs Ink's redraw math the moment label+input exceeds the terminal
103
- // width (reproduced live: a ~195-char label here produced garbled,
104
- // overlapping keystrokes). Put anything beyond a one-line question in a
105
- // note() instead — printed once, never re-rendered.
106
- asker.note('e.g. "ollama launch claude --model {model}" to route through a wrapper — {model} substitutes per-agent, written to spf.config.yaml\'s env.SPF_CLAUDE_CMD');
107
- const launchCommand = await asker.text("Launch command for the `claude` CLI", { default: "claude" });
108
121
  if (launchCommand !== "claude")
109
122
  configEnv["SPF_CLAUDE_CMD"] = launchCommand;
110
- const model = await asker.select("Model (Claude Code's own vocabulary — not provider/model-id)", [
111
- { value: "sonnet", label: "sonnet" },
112
- { value: "opus", label: "opus" },
113
- { value: "haiku", label: "haiku" },
114
- { value: "custom", label: "custom type an exact model name" },
115
- ], "sonnet");
116
- defaults.model = model === "custom" ? await asker.text("Exact model name") : model;
123
+ let defaultModel;
124
+ if (launchMode === "ollama") {
125
+ // Not Claude Code's own alias vocabulary (sonnet/opus/haiku) — under
126
+ // this launcher, `ollama launch`'s OWN --model is what actually picks
127
+ // the served model (see agent_cc.ts's module doc comment), so this
128
+ // has to be a real Ollama tag from `ollama list`.
129
+ defaultModel = await asker.text("Ollama model tag (from `ollama list`)");
130
+ }
131
+ else {
132
+ const model = await asker.select("Model (Claude Code's own vocabulary — not provider/model-id)", [
133
+ { value: "sonnet", label: "sonnet" },
134
+ { value: "opus", label: "opus" },
135
+ { value: "haiku", label: "haiku" },
136
+ { value: "custom", label: "custom — type an exact model name" },
137
+ ], "sonnet");
138
+ defaultModel = model === "custom" ? await asker.text("Exact model name") : model;
139
+ }
140
+ defaults.model = defaultModel;
117
141
  const auth = await asker.select("Authentication", [
118
142
  { value: "login", label: "already logged in via `claude login`", hint: "writes nothing" },
119
143
  { value: "key", label: "ANTHROPIC_API_KEY" },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gr8ful/spf",
3
- "version": "0.9.1",
3
+ "version": "0.9.2",
4
4
  "description": "Super Portable Factory — a global CLI for repeatable agents-plus-code workflows (ADWs)",
5
5
  "type": "module",
6
6
  "license": "MIT",