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.
Files changed (2) hide show
  1. package/lib/wispy-repl.mjs +130 -7
  2. package/package.json +1 -1
@@ -130,10 +130,119 @@ ${bold("macOS Keychain (auto-detected):")}
130
130
  `);
131
131
  }
132
132
 
133
- const detected = await detectProvider();
134
- const PROVIDER = detected?.provider ?? "none";
135
- const API_KEY = detected?.key ?? null;
136
- const MODEL = detected?.model ?? "unknown";
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
- printSetupGuide();
1997
- process.exit(1);
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
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wispy-cli",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "description": "🌿 Wispy — AI workspace assistant with multi-agent orchestration",
5
5
  "license": "Apache-2.0",
6
6
  "author": "Minseo & Poropo",