wispy-cli 2.7.4 → 2.7.6

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/bin/wispy-mjs ADDED
@@ -0,0 +1,3 @@
1
+ case "ws":
2
+ if (args.length > 1 && !['start-client', 'run-debug'].includes(args[1])) {
3
+ console.error(`Error: Unrecognized option \${args[1]}`}
package/bin/wispy.mjs CHANGED
@@ -1,60 +1,341 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  /**
4
- * wispy-cli
5
- * Async refactored dispatcher avoids direct top-level awaits in Node modules.
4
+ * wispy-cli — main entry point
5
+ *
6
+ * Dispatches to the correct handler based on the first argument:
7
+ * - Operator commands (setup, config, doctor, etc.) → operator CLI or inline handler
8
+ * - Server commands → server manager
9
+ * - Everything else → REPL (interactive chat)
10
+ *
11
+ * v2.7.x — rewired to properly route all commands.
6
12
  */
7
- (async () => {
8
- const { fileURLToPath } = await import('url');
9
- const { dirname } = await import('path');
10
- const baseDir = dirname(fileURLToPath(import.meta.url));
11
-
12
- const args = process.argv.slice(2);
13
-
14
- async function handleCommand(command) {
15
- const inquirer = (await import('inquirer')).default;
16
-
17
- if (!command) {
18
- let answers;
19
- try {
20
- answers = await inquirer.prompt([
21
- {
22
- type: 'list',
23
- name: 'selectedCommand',
24
- message: 'What would you like to do?',
25
- choices: [
26
- { name: 'Run WebSocket command', value: 'ws' },
27
- { name: 'Get help', value: 'help' },
28
- { name: 'Exit', value: null }
29
- ],
30
- }
31
- ]);
32
- } catch (error) {
33
- if (error.isTtyError) {
34
- console.error("Prompt couldn't be rendered in the current environment.");
35
- } else {
36
- console.log("Prompt closed by the user. Exiting gracefully.");
13
+
14
+ import { fileURLToPath } from "node:url";
15
+ import { dirname, join } from "node:path";
16
+ import { readFile } from "node:fs/promises";
17
+ import { existsSync } from "node:fs";
18
+
19
+ const __filename = fileURLToPath(import.meta.url);
20
+ const __dirname = dirname(__filename);
21
+ const rootDir = join(__dirname, "..");
22
+
23
+ const args = process.argv.slice(2);
24
+ const command = args[0];
25
+
26
+ // ── Flags ─────────────────────────────────────────────────────────────────────
27
+
28
+ if (args.includes("--version") || command === "--version" || command === "-v") {
29
+ try {
30
+ const pkg = JSON.parse(await readFile(join(rootDir, "package.json"), "utf8"));
31
+ console.log(`wispy-cli v${pkg.version}`);
32
+ } catch {
33
+ console.error("Failed to read version from package.json");
34
+ }
35
+ process.exit(0);
36
+ }
37
+
38
+ if (args.includes("--help") || command === "--help" || command === "-h" || command === "help") {
39
+ const pkg = JSON.parse(await readFile(join(rootDir, "package.json"), "utf8"));
40
+ console.log(`
41
+ wispy-cli v${pkg.version}
42
+ AI workspace assistant chat, automate, and orchestrate from the terminal.
43
+
44
+ Usage:
45
+ wispy Start interactive REPL
46
+ wispy <message> One-shot chat
47
+ wispy setup Run first-time setup wizard
48
+ wispy config [show|get|set|delete|reset|path|edit]
49
+ Manage configuration
50
+ wispy model Show or change AI model
51
+ wispy doctor Check system health
52
+ wispy trust [level|log] Security level & audit
53
+ wispy ws [start-client|run-debug]
54
+ WebSocket operations
55
+ wispy skill [list|run] Manage learned skills
56
+ wispy teach <name> Create skill from conversation
57
+ wispy improve <name> Improve a skill
58
+ wispy where Show current mode (local/cloud)
59
+ wispy handoff [cloud|local] Sync between environments
60
+ wispy server [start|stop|status]
61
+ Manage HTTP/WS server
62
+ wispy tui Launch full terminal UI
63
+ wispy overview Director view of workstreams
64
+
65
+ Options:
66
+ -w, --workstream <name> Set active workstream
67
+ --session <id> Resume a session
68
+ --model <name> Override AI model
69
+ --provider <name> Override AI provider
70
+ --help, -h Show this help
71
+ --version, -v Show version
72
+ `);
73
+ process.exit(0);
74
+ }
75
+
76
+ // ── Setup (first-run wizard) ──────────────────────────────────────────────────
77
+
78
+ if (command === "setup") {
79
+ try {
80
+ const { OnboardingWizard } = await import(join(rootDir, "core/onboarding.mjs"));
81
+ const wizard = new OnboardingWizard();
82
+ await wizard.run();
83
+ } catch (err) {
84
+ console.error("Setup failed:", err.message);
85
+ process.exit(1);
86
+ }
87
+ process.exit(0);
88
+ }
89
+
90
+ // ── Config ────────────────────────────────────────────────────────────────────
91
+
92
+ if (command === "config") {
93
+ try {
94
+ const { loadConfig, saveConfig, WISPY_DIR, CONFIG_PATH } = await import(join(rootDir, "core/config.mjs"));
95
+ const sub = args[1];
96
+ const config = await loadConfig();
97
+
98
+ if (!sub || sub === "show") {
99
+ console.log(JSON.stringify(config, null, 2));
100
+ } else if (sub === "path") {
101
+ console.log(CONFIG_PATH);
102
+ } else if (sub === "get") {
103
+ const key = args[2];
104
+ if (!key) { console.error("Usage: wispy config get <key>"); process.exit(1); }
105
+ console.log(config[key] !== undefined ? JSON.stringify(config[key], null, 2) : `Key '${key}' not found`);
106
+ } else if (sub === "set") {
107
+ const key = args[2], value = args[3];
108
+ if (!key || value === undefined) { console.error("Usage: wispy config set <key> <value>"); process.exit(1); }
109
+ try { config[key] = JSON.parse(value); } catch { config[key] = value; }
110
+ await saveConfig(config);
111
+ console.log(`Set ${key} = ${JSON.stringify(config[key])}`);
112
+ } else if (sub === "delete") {
113
+ const key = args[2];
114
+ if (!key) { console.error("Usage: wispy config delete <key>"); process.exit(1); }
115
+ delete config[key];
116
+ await saveConfig(config);
117
+ console.log(`Deleted '${key}'`);
118
+ } else if (sub === "reset") {
119
+ const { confirm } = await import("@inquirer/prompts");
120
+ const yes = await confirm({ message: "Reset all config to defaults?", default: false });
121
+ if (yes) { await saveConfig({}); console.log("Config reset to defaults."); }
122
+ } else if (sub === "edit") {
123
+ const editor = process.env.EDITOR || "nano";
124
+ const { execSync } = await import("node:child_process");
125
+ execSync(`${editor} "${CONFIG_PATH}"`, { stdio: "inherit" });
126
+ } else {
127
+ console.error(`Unknown config subcommand: ${sub}`);
128
+ console.log("Available: show, get, set, delete, reset, path, edit");
129
+ process.exit(1);
130
+ }
131
+ } catch (err) {
132
+ console.error("Config error:", err.message);
133
+ process.exit(1);
134
+ }
135
+ process.exit(0);
136
+ }
137
+
138
+ // ── Doctor ────────────────────────────────────────────────────────────────────
139
+
140
+ if (command === "doctor") {
141
+ try {
142
+ const { loadConfig, detectProvider, WISPY_DIR } = await import(join(rootDir, "core/config.mjs"));
143
+ const config = await loadConfig();
144
+ const provider = await detectProvider();
145
+ const pkg = JSON.parse(await readFile(join(rootDir, "package.json"), "utf8"));
146
+
147
+ console.log(`\n wispy-cli v${pkg.version}`);
148
+ console.log(` Config dir: ${WISPY_DIR}`);
149
+ console.log(` Provider: ${provider?.provider ?? "none"}`);
150
+ console.log(` Model: ${provider?.model ?? "none"}`);
151
+ console.log(` API key: ${provider?.key ? "✓ set" : "✗ missing"}`);
152
+
153
+ // Check optional components
154
+ const checks = [
155
+ { name: "Config file", ok: existsSync(join(WISPY_DIR, "config.json")) },
156
+ { name: "Memory dir", ok: existsSync(join(WISPY_DIR, "memory")) },
157
+ { name: "Sessions dir", ok: existsSync(join(WISPY_DIR, "sessions")) },
158
+ { name: "MCP config", ok: existsSync(join(WISPY_DIR, "mcp.json")) },
159
+ ];
160
+ console.log("");
161
+ for (const c of checks) {
162
+ console.log(` ${c.ok ? "✓" : "✗"} ${c.name}`);
163
+ }
164
+ console.log("");
165
+
166
+ if (!provider?.key) {
167
+ console.log(" Run 'wispy setup' to configure an API key.\n");
168
+ } else {
169
+ console.log(" All good!\n");
170
+ }
171
+ } catch (err) {
172
+ console.error("Doctor error:", err.message);
173
+ process.exit(1);
174
+ }
175
+ process.exit(0);
176
+ }
177
+
178
+ // ── Trust ─────────────────────────────────────────────────────────────────────
179
+
180
+ if (command === "trust") {
181
+ try {
182
+ const { handleTrustCommand } = await import(join(rootDir, "lib/commands/trust.mjs"));
183
+ await handleTrustCommand(args.slice(1));
184
+ } catch (err) {
185
+ console.error("Trust command error:", err.message);
186
+ process.exit(1);
187
+ }
188
+ process.exit(0);
189
+ }
190
+
191
+ // ── WebSocket ─────────────────────────────────────────────────────────────────
192
+
193
+ if (command === "ws") {
194
+ try {
195
+ const { handleWsCommand } = await import(join(rootDir, "lib/commands/ws.mjs"));
196
+ await handleWsCommand(args);
197
+ } catch (err) {
198
+ console.error("WS command error:", err.message);
199
+ process.exit(1);
200
+ }
201
+ process.exit(0);
202
+ }
203
+
204
+ // ── Skills ────────────────────────────────────────────────────────────────────
205
+
206
+ if (command === "skill" || command === "skills") {
207
+ try {
208
+ const { handleSkillCommand } = await import(join(rootDir, "lib/commands/skills-cmd.mjs"));
209
+ await handleSkillCommand(args.slice(1));
210
+ } catch (err) {
211
+ console.error("Skill command error:", err.message);
212
+ process.exit(1);
213
+ }
214
+ process.exit(0);
215
+ }
216
+
217
+ if (command === "teach") {
218
+ try {
219
+ const { cmdTeach } = await import(join(rootDir, "lib/commands/skills-cmd.mjs"));
220
+ await cmdTeach(args[1]);
221
+ } catch (err) {
222
+ console.error("Teach error:", err.message);
223
+ process.exit(1);
224
+ }
225
+ process.exit(0);
226
+ }
227
+
228
+ if (command === "improve") {
229
+ try {
230
+ const { cmdImproveSkill } = await import(join(rootDir, "lib/commands/skills-cmd.mjs"));
231
+ await cmdImproveSkill(args[1], args.slice(2).join(" "));
232
+ } catch (err) {
233
+ console.error("Improve error:", err.message);
234
+ process.exit(1);
235
+ }
236
+ process.exit(0);
237
+ }
238
+
239
+ // ── Continuity ────────────────────────────────────────────────────────────────
240
+
241
+ if (command === "where") {
242
+ try {
243
+ const { cmdWhere } = await import(join(rootDir, "lib/commands/continuity.mjs"));
244
+ await cmdWhere();
245
+ } catch (err) {
246
+ console.error("Where error:", err.message);
247
+ process.exit(1);
248
+ }
249
+ process.exit(0);
250
+ }
251
+
252
+ if (command === "handoff") {
253
+ try {
254
+ const { handleContinuityCommand } = await import(join(rootDir, "lib/commands/continuity.mjs"));
255
+ await handleContinuityCommand(args.slice(1));
256
+ } catch (err) {
257
+ console.error("Handoff error:", err.message);
258
+ process.exit(1);
259
+ }
260
+ process.exit(0);
261
+ }
262
+
263
+ // ── Model ─────────────────────────────────────────────────────────────────────
264
+
265
+ if (command === "model") {
266
+ try {
267
+ const { loadConfig, saveConfig, detectProvider, PROVIDERS } = await import(join(rootDir, "core/config.mjs"));
268
+ const config = await loadConfig();
269
+ const detected = await detectProvider();
270
+
271
+ if (args[1]) {
272
+ config.model = args[1];
273
+ await saveConfig(config);
274
+ console.log(`Model set to: ${args[1]}`);
275
+ } else {
276
+ console.log(`\n Provider: ${detected?.provider ?? "none"}`);
277
+ console.log(` Model: ${detected?.model ?? "none"}`);
278
+ console.log(`\n Override: wispy model <model-name>`);
279
+ console.log(` Env var: WISPY_MODEL=<model-name>`);
280
+
281
+ if (PROVIDERS) {
282
+ console.log(`\n Available providers:`);
283
+ for (const [key, p] of Object.entries(PROVIDERS)) {
284
+ console.log(` ${key.padEnd(14)} ${p.defaultModel}`);
37
285
  }
38
- process.exit(0);
39
286
  }
40
- command = answers.selectedCommand;
287
+ console.log("");
41
288
  }
289
+ } catch (err) {
290
+ console.error("Model error:", err.message);
291
+ process.exit(1);
292
+ }
293
+ process.exit(0);
294
+ }
42
295
 
43
- switch (command) {
44
- case "ws":
45
- const { handleWsCommand } = await import(baseDir + '/../lib/commands/ws.mjs');
46
- return await handleWsCommand(args);
47
- case "help":
48
- console.log(`\nWispy CLI Help:\n\nCommands:\n ws - Run WebSocket command (e.g., 'node bin/wispy.mjs ws')\n help - Show this help message (e.g., 'node bin/wispy.mjs --help')\n\nExamples:\n $ node bin/wispy.mjs ws\n $ node bin/wispy.mjs --help\n\nTip: Run 'node bin/wispy.mjs' with no arguments for an interactive prompt.\n`);
49
- break;
50
- case null:
51
- console.log("Goodbye!");
52
- break;
53
- default:
54
- console.error(`Unknown command: ${command}`);
55
- console.log("Try one of the available commands: ws, help");
56
- }
296
+ // ── TUI ───────────────────────────────────────────────────────────────────────
297
+
298
+ if (command === "tui") {
299
+ try {
300
+ await import(join(rootDir, "bin/wispy-tui.mjs"));
301
+ } catch (err) {
302
+ console.error("TUI launch error:", err.message);
303
+ process.exit(1);
57
304
  }
305
+ // TUI handles its own lifecycle
306
+ }
58
307
 
59
- await handleCommand(args[0] === '--help' ? 'help' : args[0]);
60
- })();
308
+ // ── Overview ──────────────────────────────────────────────────────────────────
309
+
310
+ if (command === "overview") {
311
+ // Delegate to REPL with overview flag
312
+ process.env.WISPY_OVERVIEW = "1";
313
+ }
314
+
315
+ // ── Server ────────────────────────────────────────────────────────────────────
316
+
317
+ if (command === "server") {
318
+ // Delegate to REPL which has server handling built in
319
+ // (lib/wispy-repl.mjs handles server start/stop/status)
320
+ }
321
+
322
+ // ── Default: launch REPL ──────────────────────────────────────────────────────
323
+ // For no args, one-shot messages, or unrecognized commands that the REPL handles
324
+
325
+ if (command === "server" || command === "overview") {
326
+ // Already set up env flags above, fall through to REPL
327
+ }
328
+
329
+ // If we get here and it's not a recognized command above, go to REPL
330
+ // The REPL handles: interactive chat, one-shot "wispy <message>", server, overview
331
+ try {
332
+ await import(join(rootDir, "lib/wispy-repl.mjs"));
333
+ } catch (err) {
334
+ if (err.code === "ERR_MODULE_NOT_FOUND") {
335
+ console.error(`Module not found: ${err.message}`);
336
+ console.error("Try running: npm install");
337
+ } else {
338
+ console.error("Failed to start wispy:", err.message);
339
+ }
340
+ process.exit(1);
341
+ }
@@ -1,32 +1,41 @@
1
1
  import { select } from '@inquirer/prompts';
2
2
 
3
- export async function handleWsCommand() {
4
- try {
5
- const action = await select({
6
- message: 'What WebSocket action would you like to perform?',
7
- choices: [
8
- { name: 'Start WebSocket Client', value: 'startClient' },
9
- { name: 'Run WebSocket Server Debug Mode', value: 'runServerDebug' },
10
- { name: 'Exit', value: 'exit' }
11
- ]
12
- });
3
+ export async function handleWsCommand(args = []) {
4
+ try {
5
+ if (args.length > 0 && !['startClient', 'runServerDebug'].includes(args[0])) {
6
+ console.error(`\nError: Invalid WebSocket subcommand '${args[0]}'.\n`);
7
+ console.log('Available subcommands:');
8
+ console.log(' startClient Start WebSocket Client');
9
+ console.log(' runServerDebug Run WebSocket Server Debug Mode');
10
+ console.log('\nTip: Use no arguments to enter interactive mode.\n');
11
+ return;
12
+ }
13
13
 
14
- switch (action) {
15
- case 'startClient':
16
- console.log('Starting WebSocket client...');
17
- break;
18
- case 'runServerDebug':
19
- console.log('Running server in debug mode...');
20
- break;
21
- case 'exit':
22
- console.log('Exiting WebSocket command.');
23
- return;
24
- }
25
- } catch (err) {
26
- if (err.name === 'ExitPromptError') {
27
- console.log('Prompt closed. Exiting command gracefully.');
28
- } else {
29
- console.error('Unexpected error:', err);
14
+ const action = args[0] || await select({
15
+ message: 'What WebSocket action would you like to perform?',
16
+ choices: [
17
+ { name: 'Start WebSocket Client', value: 'startClient' },
18
+ { name: 'Run WebSocket Server Debug Mode', value: 'runServerDebug' },
19
+ { name: 'Exit', value: 'exit' }
20
+ ]
21
+ });
22
+
23
+ switch (action) {
24
+ case 'startClient':
25
+ console.log('Starting WebSocket client...');
26
+ break;
27
+ case 'runServerDebug':
28
+ console.log('Running server in debug mode...');
29
+ break;
30
+ case 'exit':
31
+ console.log('Exiting WebSocket command.');
32
+ return;
33
+ }
34
+ } catch (err) {
35
+ if (err.name === 'ExitPromptError') {
36
+ console.log('Prompt closed. Exiting command gracefully.');
37
+ } else {
38
+ console.error('Unexpected error:', err);
39
+ }
30
40
  }
31
- }
32
41
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wispy-cli",
3
- "version": "2.7.4",
3
+ "version": "2.7.6",
4
4
  "description": "🌿 Wispy — AI workspace assistant with trustworthy execution (harness, receipts, approvals, diffs)",
5
5
  "license": "MIT",
6
6
  "author": "Minseo & Poropo",