wispy-cli 0.1.0 → 0.1.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.
- package/lib/wispy-repl.mjs +130 -7
- package/package.json +1 -1
package/lib/wispy-repl.mjs
CHANGED
|
@@ -130,10 +130,119 @@ ${bold("macOS Keychain (auto-detected):")}
|
|
|
130
130
|
`);
|
|
131
131
|
}
|
|
132
132
|
|
|
133
|
-
|
|
134
|
-
const
|
|
135
|
-
|
|
136
|
-
|
|
133
|
+
async function runOnboarding() {
|
|
134
|
+
const { createInterface: createRL } = await import("node:readline");
|
|
135
|
+
|
|
136
|
+
console.log(`
|
|
137
|
+
${bold("🌿 Welcome to Wispy!")}
|
|
138
|
+
${dim("Let's get you set up in 30 seconds.")}
|
|
139
|
+
`);
|
|
140
|
+
|
|
141
|
+
const rl = createRL({ input: process.stdin, output: process.stdout });
|
|
142
|
+
const ask = (q) => new Promise(r => rl.question(q, r));
|
|
143
|
+
|
|
144
|
+
// Step 1: Choose provider
|
|
145
|
+
console.log(`${bold("Step 1:")} Choose your AI provider\n`);
|
|
146
|
+
console.log(` ${green("1")} Google AI (Gemini) ${dim("← free, recommended")}`)
|
|
147
|
+
console.log(` ${cyan("2")} Anthropic (Claude) ${dim("paid")}`)
|
|
148
|
+
console.log(` ${cyan("3")} OpenAI (GPT-4o) ${dim("paid")}`)
|
|
149
|
+
console.log(` ${cyan("4")} OpenRouter ${dim("any model, some free")}`)
|
|
150
|
+
console.log(` ${cyan("5")} Groq ${dim("free, fast")}`)
|
|
151
|
+
console.log(` ${cyan("6")} DeepSeek ${dim("cheap")}`)
|
|
152
|
+
console.log(` ${cyan("7")} Ollama ${dim("local, no key needed")}`)
|
|
153
|
+
console.log("");
|
|
154
|
+
|
|
155
|
+
const choice = await ask(green(" Pick a number (1-7): "));
|
|
156
|
+
const providerMap = { "1": "google", "2": "anthropic", "3": "openai", "4": "openrouter", "5": "groq", "6": "deepseek", "7": "ollama" };
|
|
157
|
+
const chosenProvider = providerMap[choice.trim()] ?? "google";
|
|
158
|
+
const chosenConfig = PROVIDERS[chosenProvider];
|
|
159
|
+
|
|
160
|
+
let apiKey = null;
|
|
161
|
+
|
|
162
|
+
if (chosenProvider === "ollama") {
|
|
163
|
+
console.log(`\n${green("✅")} Ollama selected — no API key needed!`);
|
|
164
|
+
console.log(dim(" Make sure Ollama is running: ollama serve"));
|
|
165
|
+
const host = await ask(green(" Ollama host (enter for default localhost:11434): "));
|
|
166
|
+
const ollamaHost = host.trim() || "http://localhost:11434";
|
|
167
|
+
|
|
168
|
+
// Save config
|
|
169
|
+
const configPath = path.join(WISPY_DIR, "config.json");
|
|
170
|
+
await mkdir(WISPY_DIR, { recursive: true });
|
|
171
|
+
const existing = await readFileOr(configPath, "{}");
|
|
172
|
+
const config = JSON.parse(existing);
|
|
173
|
+
config.provider = "ollama";
|
|
174
|
+
config.ollamaHost = ollamaHost;
|
|
175
|
+
await writeFile(configPath, JSON.stringify(config, null, 2) + "\n", "utf8");
|
|
176
|
+
|
|
177
|
+
process.env.OLLAMA_HOST = ollamaHost;
|
|
178
|
+
} else {
|
|
179
|
+
// Step 2: Get API key
|
|
180
|
+
console.log(`\n${bold("Step 2:")} Enter your API key`);
|
|
181
|
+
if (chosenConfig.signupUrl) {
|
|
182
|
+
console.log(dim(` Get one here: ${chosenConfig.signupUrl}`));
|
|
183
|
+
}
|
|
184
|
+
console.log("");
|
|
185
|
+
|
|
186
|
+
apiKey = await ask(green(" API key: "));
|
|
187
|
+
apiKey = apiKey.trim();
|
|
188
|
+
|
|
189
|
+
if (!apiKey) {
|
|
190
|
+
console.log(red("\n No key entered. Run wispy again when you have one."));
|
|
191
|
+
rl.close();
|
|
192
|
+
process.exit(1);
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
// Save to config
|
|
196
|
+
const configPath = path.join(WISPY_DIR, "config.json");
|
|
197
|
+
await mkdir(WISPY_DIR, { recursive: true });
|
|
198
|
+
const existing = await readFileOr(configPath, "{}");
|
|
199
|
+
const config = JSON.parse(existing);
|
|
200
|
+
config.provider = chosenProvider;
|
|
201
|
+
config.apiKey = apiKey;
|
|
202
|
+
await writeFile(configPath, JSON.stringify(config, null, 2) + "\n", "utf8");
|
|
203
|
+
|
|
204
|
+
// Set env for current session
|
|
205
|
+
process.env[chosenConfig.envKeys[0]] = apiKey;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
// Step 3: Name (optional)
|
|
209
|
+
console.log(`\n${bold("Step 3:")} What should I call you? ${dim("(enter to skip)")}`);
|
|
210
|
+
const userName = await ask(green(" Your name: "));
|
|
211
|
+
if (userName.trim()) {
|
|
212
|
+
await mkdir(path.join(WISPY_DIR, "memory"), { recursive: true });
|
|
213
|
+
await appendFile(
|
|
214
|
+
path.join(WISPY_DIR, "memory", "user.md"),
|
|
215
|
+
`\n- [${new Date().toISOString().slice(0,16)}] Name: ${userName.trim()}\n`,
|
|
216
|
+
"utf8",
|
|
217
|
+
);
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
rl.close();
|
|
221
|
+
|
|
222
|
+
console.log(`
|
|
223
|
+
${green("✅ Setup complete!")}
|
|
224
|
+
|
|
225
|
+
${bold("You're ready to go:")}
|
|
226
|
+
${cyan("wispy")} Start chatting
|
|
227
|
+
${cyan('wispy "hello"')} Quick message
|
|
228
|
+
${cyan("wispy -w project")} Use a workstream
|
|
229
|
+
${cyan("wispy --help")} All options
|
|
230
|
+
|
|
231
|
+
${dim("🌿 Enjoy Wispy!")}
|
|
232
|
+
`);
|
|
233
|
+
|
|
234
|
+
// Re-detect provider with new config
|
|
235
|
+
const newDetected = await detectProvider();
|
|
236
|
+
if (newDetected) {
|
|
237
|
+
// Update globals — but since they're const, we need to restart
|
|
238
|
+
// For this session, just continue with the values we set
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
let detected = await detectProvider();
|
|
243
|
+
let PROVIDER = detected?.provider ?? "none";
|
|
244
|
+
let API_KEY = detected?.key ?? null;
|
|
245
|
+
let MODEL = detected?.model ?? "unknown";
|
|
137
246
|
const MAX_CONTEXT_CHARS = 40_000;
|
|
138
247
|
|
|
139
248
|
// ---------------------------------------------------------------------------
|
|
@@ -1991,10 +2100,24 @@ if (args[0] && operatorCommands.has(args[0])) {
|
|
|
1991
2100
|
process.exit(0);
|
|
1992
2101
|
}
|
|
1993
2102
|
|
|
1994
|
-
// Check API key
|
|
2103
|
+
// Check API key — if none found, run interactive onboarding
|
|
1995
2104
|
if (!API_KEY && PROVIDER !== "ollama") {
|
|
1996
|
-
|
|
1997
|
-
|
|
2105
|
+
await runOnboarding();
|
|
2106
|
+
// Re-detect after onboarding saved config
|
|
2107
|
+
const redetected = await detectProvider();
|
|
2108
|
+
if (redetected) {
|
|
2109
|
+
// Patch module-level state for this session
|
|
2110
|
+
Object.assign(detected, redetected);
|
|
2111
|
+
}
|
|
2112
|
+
if (redetected) {
|
|
2113
|
+
PROVIDER = redetected.provider;
|
|
2114
|
+
API_KEY = redetected.key;
|
|
2115
|
+
MODEL = redetected.model;
|
|
2116
|
+
}
|
|
2117
|
+
if (!API_KEY && PROVIDER !== "ollama") {
|
|
2118
|
+
console.log(dim("\nRun wispy again to start chatting!"));
|
|
2119
|
+
process.exit(0);
|
|
2120
|
+
}
|
|
1998
2121
|
}
|
|
1999
2122
|
|
|
2000
2123
|
// Auto-start server before entering REPL or one-shot
|