wispy-cli 2.0.2 → 2.1.0
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 +2 -1
- package/lib/wispy-repl.mjs +245 -3
- package/package.json +1 -1
package/bin/wispy.mjs
CHANGED
|
@@ -1102,7 +1102,8 @@ if (serveMode || telegramMode || discordMode || slackMode) {
|
|
|
1102
1102
|
const isInteractiveStart = !args.some(a =>
|
|
1103
1103
|
["--serve", "--telegram", "--discord", "--slack", "--server",
|
|
1104
1104
|
"status", "setup", "init", "connect", "disconnect", "deploy",
|
|
1105
|
-
"cron", "audit", "log", "server", "node", "channel", "sync", "tui"
|
|
1105
|
+
"cron", "audit", "log", "server", "node", "channel", "sync", "tui",
|
|
1106
|
+
"ws", "trust", "where", "handoff", "skill", "teach", "improve", "dry"].includes(a)
|
|
1106
1107
|
);
|
|
1107
1108
|
|
|
1108
1109
|
if (isInteractiveStart) {
|
package/lib/wispy-repl.mjs
CHANGED
|
@@ -331,6 +331,41 @@ ${bold("Wispy Commands:")}
|
|
|
331
331
|
${cyan("/memories")} List all memory files
|
|
332
332
|
${cyan("/recall")} <query> Search memories
|
|
333
333
|
|
|
334
|
+
${bold("Workstream (v1.4):")}
|
|
335
|
+
${cyan("/ws")} List workstreams
|
|
336
|
+
${cyan("/ws")} <name> Switch to workstream
|
|
337
|
+
${cyan("/ws new")} <name> Create new workstream
|
|
338
|
+
${cyan("/ws archive")} <name> Archive workstream
|
|
339
|
+
${cyan("/ws status")} All workstreams status
|
|
340
|
+
${cyan("/ws search")} <q> Cross-workstream search
|
|
341
|
+
|
|
342
|
+
${bold("Trust (v1.4):")}
|
|
343
|
+
${cyan("/trust")} Show security + recent approvals
|
|
344
|
+
${cyan("/trust")} <level> Change level (careful/balanced/yolo)
|
|
345
|
+
${cyan("/trust log")} Audit log
|
|
346
|
+
${cyan("/receipt")} Show last receipt
|
|
347
|
+
${cyan("/dry")} Toggle dry-run mode
|
|
348
|
+
|
|
349
|
+
${bold("Continuity (v1.4):")}
|
|
350
|
+
${cyan("/where")} Show current mode
|
|
351
|
+
${cyan("/handoff")} Sync + context summary
|
|
352
|
+
${cyan("/sync")} Trigger sync
|
|
353
|
+
|
|
354
|
+
${bold("Skills (v1.4):")}
|
|
355
|
+
${cyan("/skills")} List skills
|
|
356
|
+
${cyan("/teach")} <name> Create skill from this conversation
|
|
357
|
+
${cyan("/improve")} <name> Improve skill
|
|
358
|
+
${cyan("/<skill-name>")} Run any learned skill directly
|
|
359
|
+
|
|
360
|
+
${bold("Quick shortcuts:")}
|
|
361
|
+
${cyan("/o")} Overview (alias for /ws status)
|
|
362
|
+
${cyan("/w")} Workstream list
|
|
363
|
+
${cyan("/a")} Agents list
|
|
364
|
+
${cyan("/m")} Memories
|
|
365
|
+
${cyan("/t")} Timeline (recent actions)
|
|
366
|
+
${cyan("/s")} Sync status
|
|
367
|
+
${cyan("/d")} Toggle dry-run
|
|
368
|
+
|
|
334
369
|
${bold("Sub-agent Commands (v0.9):")}
|
|
335
370
|
${cyan("/agents")} List active/recent sub-agents
|
|
336
371
|
${cyan("/agent")} <id> Show sub-agent details and result
|
|
@@ -399,7 +434,200 @@ ${bold("Permissions & Audit (v1.1):")}
|
|
|
399
434
|
return true;
|
|
400
435
|
}
|
|
401
436
|
|
|
402
|
-
|
|
437
|
+
// ── Quick single-letter shortcuts ────────────────────────────────────────────
|
|
438
|
+
if (cmd === "/o") {
|
|
439
|
+
// Overview — alias for /ws status
|
|
440
|
+
const { cmdWsStatus } = await import("./commands/ws.mjs");
|
|
441
|
+
await cmdWsStatus();
|
|
442
|
+
return true;
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
if (cmd === "/w") {
|
|
446
|
+
// Workstream list
|
|
447
|
+
const { cmdWsList } = await import("./commands/ws.mjs");
|
|
448
|
+
await cmdWsList();
|
|
449
|
+
return true;
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
if (cmd === "/a") {
|
|
453
|
+
// Agents — handled below (delegates to /agents)
|
|
454
|
+
const inMemory = engine.subagents.list();
|
|
455
|
+
const history = await engine.subagents.listHistory(10);
|
|
456
|
+
const inMemoryIds = new Set(inMemory.map(a => a.id));
|
|
457
|
+
const all = [
|
|
458
|
+
...inMemory.map(a => a.toJSON()),
|
|
459
|
+
...history.filter(h => !inMemoryIds.has(h.id)),
|
|
460
|
+
].slice(0, 20);
|
|
461
|
+
if (all.length === 0) {
|
|
462
|
+
console.log(dim("No sub-agents yet."));
|
|
463
|
+
} else {
|
|
464
|
+
console.log(bold(`\n🤖 Agents (${all.length}):\n`));
|
|
465
|
+
for (const a of all) {
|
|
466
|
+
const icon = { completed: green("✓"), failed: red("✗"), running: cyan("⟳"), killed: dim("✕") }[a.status] ?? "?";
|
|
467
|
+
console.log(` ${icon} ${bold(a.id)} ${dim("·")} ${a.label}`);
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
return true;
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
if (cmd === "/m") {
|
|
474
|
+
// Memories — alias for /memories
|
|
475
|
+
const keys = await engine.memory.list();
|
|
476
|
+
if (keys.length === 0) {
|
|
477
|
+
console.log(dim("No memories stored yet. Use /remember <text>"));
|
|
478
|
+
} else {
|
|
479
|
+
console.log(bold(`\n🧠 Memories (${keys.length}):\n`));
|
|
480
|
+
for (const k of keys) {
|
|
481
|
+
console.log(` ${cyan(k.key.padEnd(30))} ${dim(k.preview ?? "")}`);
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
return true;
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
if (cmd === "/t") {
|
|
488
|
+
// Timeline — recent audit events
|
|
489
|
+
const events = await engine.audit.getRecent(15);
|
|
490
|
+
if (events.length === 0) {
|
|
491
|
+
console.log(dim("No recent activity."));
|
|
492
|
+
} else {
|
|
493
|
+
console.log(bold(`\n⏱ Timeline (last ${events.length} actions):\n`));
|
|
494
|
+
for (const evt of events) {
|
|
495
|
+
const ts = new Date(evt.timestamp).toLocaleTimeString();
|
|
496
|
+
const icons = {
|
|
497
|
+
tool_call: "🔧", tool_result: "✅", approval_requested: "⚠️ ",
|
|
498
|
+
approval_granted: "✅", approval_denied: "❌", message_sent: "🌿",
|
|
499
|
+
subagent_spawned: "🤖", error: "🚨",
|
|
500
|
+
};
|
|
501
|
+
const icon = icons[evt.type] ?? "•";
|
|
502
|
+
const detail = evt.tool ? ` ${cyan(evt.tool)}` : evt.content ? ` ${dim(evt.content.slice(0, 50))}` : "";
|
|
503
|
+
console.log(` ${dim(ts)} ${icon} ${evt.type}${detail}`);
|
|
504
|
+
}
|
|
505
|
+
}
|
|
506
|
+
return true;
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
if (cmd === "/s") {
|
|
510
|
+
// Sync status
|
|
511
|
+
try {
|
|
512
|
+
const { SyncManager } = await import("../core/sync.mjs");
|
|
513
|
+
const cfg = await SyncManager.loadConfig();
|
|
514
|
+
if (!cfg.remoteUrl) {
|
|
515
|
+
console.log(dim("No remote sync configured. Use: wispy sync auto --remote <url>"));
|
|
516
|
+
} else {
|
|
517
|
+
console.log(bold(`\n🔄 Sync Status\n`));
|
|
518
|
+
console.log(` Remote: ${cyan(cfg.remoteUrl)}`);
|
|
519
|
+
const lastSync = cfg.lastSync ? new Date(cfg.lastSync).toLocaleString() : "never";
|
|
520
|
+
console.log(` Last sync: ${dim(lastSync)}`);
|
|
521
|
+
console.log(` Auto-sync: ${cfg.autoSync ? green("enabled") : dim("disabled")}`);
|
|
522
|
+
}
|
|
523
|
+
} catch {
|
|
524
|
+
console.log(dim("Sync not available."));
|
|
525
|
+
}
|
|
526
|
+
return true;
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
if (cmd === "/d") {
|
|
530
|
+
// Toggle dry-run
|
|
531
|
+
if (engine.dryRunMode) {
|
|
532
|
+
engine.dryRunMode = false;
|
|
533
|
+
console.log(green("🌿 Dry-run OFF — tools will execute normally"));
|
|
534
|
+
} else {
|
|
535
|
+
engine.dryRunMode = true;
|
|
536
|
+
console.log(yellow("🔍 Dry-run ON — all tool calls will be previewed only"));
|
|
537
|
+
}
|
|
538
|
+
return true;
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
// ── /ws <subcommand> ─────────────────────────────────────────────────────────
|
|
542
|
+
if (cmd === "/ws") {
|
|
543
|
+
const { handleWsCommand } = await import("./commands/ws.mjs");
|
|
544
|
+
// Convert from /ws args to ws args format
|
|
545
|
+
const wsArgs = ["ws", ...parts.slice(1)];
|
|
546
|
+
await handleWsCommand(wsArgs);
|
|
547
|
+
return true;
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
// ── /trust <subcommand> ──────────────────────────────────────────────────────
|
|
551
|
+
if (cmd === "/trust") {
|
|
552
|
+
const { handleTrustCommand } = await import("./commands/trust.mjs");
|
|
553
|
+
const trustArgs = ["trust", ...parts.slice(1)];
|
|
554
|
+
await handleTrustCommand(trustArgs);
|
|
555
|
+
return true;
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
// ── /receipt ─────────────────────────────────────────────────────────────────
|
|
559
|
+
if (cmd === "/receipt") {
|
|
560
|
+
const { cmdTrustReceipt } = await import("./commands/trust.mjs");
|
|
561
|
+
const sessionId = parts[1] ?? engine.sessions?.list?.()?.find(Boolean)?.id;
|
|
562
|
+
await cmdTrustReceipt(sessionId);
|
|
563
|
+
return true;
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
// ── /dry (toggle dry-run) ────────────────────────────────────────────────────
|
|
567
|
+
if (cmd === "/dry") {
|
|
568
|
+
engine.dryRunMode = !engine.dryRunMode;
|
|
569
|
+
if (engine.dryRunMode) {
|
|
570
|
+
console.log(yellow("🔍 Dry-run ON — all tool calls will be previewed only"));
|
|
571
|
+
} else {
|
|
572
|
+
console.log(green("🌿 Dry-run OFF — tools will execute normally"));
|
|
573
|
+
}
|
|
574
|
+
return true;
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
// ── /where ────────────────────────────────────────────────────────────────────
|
|
578
|
+
if (cmd === "/where") {
|
|
579
|
+
const { cmdWhere } = await import("./commands/continuity.mjs");
|
|
580
|
+
await cmdWhere();
|
|
581
|
+
return true;
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
// ── /handoff ──────────────────────────────────────────────────────────────────
|
|
585
|
+
if (cmd === "/handoff") {
|
|
586
|
+
const { handleContinuityCommand } = await import("./commands/continuity.mjs");
|
|
587
|
+
const sub = parts[1] ?? "cloud";
|
|
588
|
+
await handleContinuityCommand(["handoff", sub]);
|
|
589
|
+
return true;
|
|
590
|
+
}
|
|
591
|
+
|
|
592
|
+
// ── /sync ─────────────────────────────────────────────────────────────────────
|
|
593
|
+
if (cmd === "/sync") {
|
|
594
|
+
try {
|
|
595
|
+
const { SyncManager } = await import("../core/sync.mjs");
|
|
596
|
+
const cfg = await SyncManager.loadConfig();
|
|
597
|
+
if (!cfg.remoteUrl) {
|
|
598
|
+
console.log(dim("No remote sync configured. Run: wispy sync auto --remote <url>"));
|
|
599
|
+
} else {
|
|
600
|
+
process.stdout.write(dim(`🔄 Syncing with ${cfg.remoteUrl}...`));
|
|
601
|
+
const mgr = new SyncManager({ remoteUrl: cfg.remoteUrl, token: cfg.token, strategy: "newer-wins" });
|
|
602
|
+
const result = await mgr.sync(cfg.remoteUrl, cfg.token, {});
|
|
603
|
+
console.log(green(` ✓ pushed ${result.pushed}, pulled ${result.pulled}`));
|
|
604
|
+
}
|
|
605
|
+
} catch (err) {
|
|
606
|
+
console.log(red(`✗ Sync failed: ${err.message.slice(0, 80)}`));
|
|
607
|
+
}
|
|
608
|
+
return true;
|
|
609
|
+
}
|
|
610
|
+
|
|
611
|
+
// ── /teach <name> ─────────────────────────────────────────────────────────────
|
|
612
|
+
if (cmd === "/teach") {
|
|
613
|
+
const skillName = parts[1];
|
|
614
|
+
if (!skillName) { console.log(yellow("Usage: /teach <name>")); return true; }
|
|
615
|
+
const { cmdTeach } = await import("./commands/skills-cmd.mjs");
|
|
616
|
+
await cmdTeach(skillName);
|
|
617
|
+
return true;
|
|
618
|
+
}
|
|
619
|
+
|
|
620
|
+
// ── /improve <name> ───────────────────────────────────────────────────────────
|
|
621
|
+
if (cmd === "/improve") {
|
|
622
|
+
const skillName = parts[1];
|
|
623
|
+
const feedback = parts.slice(2).join(" ").replace(/^["']|["']$/g, "");
|
|
624
|
+
if (!skillName || !feedback) { console.log(yellow('Usage: /improve <name> "feedback"')); return true; }
|
|
625
|
+
const { cmdImproveSkill } = await import("./commands/skills-cmd.mjs");
|
|
626
|
+
await cmdImproveSkill(skillName, feedback);
|
|
627
|
+
return true;
|
|
628
|
+
}
|
|
629
|
+
|
|
630
|
+
if (cmd === "/workstreams" || (cmd === "/ws" && !parts[1])) {
|
|
403
631
|
const wsList = await listWorkstreams();
|
|
404
632
|
if (wsList.length === 0) { console.log(dim("No workstreams yet.")); return true; }
|
|
405
633
|
console.log(bold("\n📋 Workstreams:\n"));
|
|
@@ -857,9 +1085,15 @@ ${bold("Permissions & Audit (v1.1):")}
|
|
|
857
1085
|
// ---------------------------------------------------------------------------
|
|
858
1086
|
|
|
859
1087
|
async function runRepl(engine) {
|
|
1088
|
+
// Initialize dry-run from env flag (set by `wispy dry "..."`)
|
|
1089
|
+
if (process.env.WISPY_DRY_RUN === "1") {
|
|
1090
|
+
engine.dryRunMode = true;
|
|
1091
|
+
}
|
|
1092
|
+
|
|
860
1093
|
const wsLabel = ACTIVE_WORKSTREAM === "default" ? "" : ` ${dim("·")} ${cyan(ACTIVE_WORKSTREAM)}`;
|
|
1094
|
+
const dryLabel = engine.dryRunMode ? ` ${yellow("· dry-run")}` : "";
|
|
861
1095
|
console.log(`
|
|
862
|
-
${bold("🌿 Wispy")}${wsLabel} ${dim(`· ${engine.model}`)}
|
|
1096
|
+
${bold("🌿 Wispy")}${wsLabel}${dryLabel} ${dim(`· ${engine.model}`)}
|
|
863
1097
|
${dim(`${engine.provider} · /help for commands · Ctrl+C to exit`)}
|
|
864
1098
|
`);
|
|
865
1099
|
|
|
@@ -872,6 +1106,13 @@ async function runRepl(engine) {
|
|
|
872
1106
|
historySize: 100,
|
|
873
1107
|
});
|
|
874
1108
|
|
|
1109
|
+
// Dynamic prompt — shows workstream and dry-run status
|
|
1110
|
+
function updatePrompt() {
|
|
1111
|
+
const ws = ACTIVE_WORKSTREAM !== "default" ? `${cyan(ACTIVE_WORKSTREAM)} ` : "";
|
|
1112
|
+
const dry = engine.dryRunMode ? yellow("(dry) ") : "";
|
|
1113
|
+
rl.setPrompt(ws + dry + green("› "));
|
|
1114
|
+
}
|
|
1115
|
+
updatePrompt();
|
|
875
1116
|
rl.prompt();
|
|
876
1117
|
|
|
877
1118
|
rl.on("line", async (line) => {
|
|
@@ -880,7 +1121,7 @@ async function runRepl(engine) {
|
|
|
880
1121
|
|
|
881
1122
|
if (input.startsWith("/")) {
|
|
882
1123
|
const handled = await handleSlashCommand(input, engine, conversation);
|
|
883
|
-
if (handled) { rl.prompt(); return; }
|
|
1124
|
+
if (handled) { updatePrompt(); rl.prompt(); return; }
|
|
884
1125
|
}
|
|
885
1126
|
|
|
886
1127
|
conversation.push({ role: "user", content: input });
|
|
@@ -895,6 +1136,7 @@ async function runRepl(engine) {
|
|
|
895
1136
|
onChunk: (chunk) => process.stdout.write(chunk),
|
|
896
1137
|
systemPrompt: await engine._buildSystemPrompt(input),
|
|
897
1138
|
noSave: true,
|
|
1139
|
+
dryRun: engine.dryRunMode ?? false,
|
|
898
1140
|
onSkillLearned: (skill) => {
|
|
899
1141
|
console.log(cyan(`\n💡 Learned new skill: '${skill.name}' — use /${skill.name} next time`));
|
|
900
1142
|
},
|