pawmode 1.2.0 → 1.4.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/dist/index.js CHANGED
@@ -2,8 +2,9 @@
2
2
  import { accent, addJob, bold, dim, getTodaysCost, installSystemJob, listJobs, parseHumanSchedule, pawPulse, pawStep, readCostTracker, readScheduleConfig, readTelegramConfig, removeJob, runJob, showBanner, showMini, showPuppyDisclaimer, startTelegramBot, subtle, telegramConfigExists, telegramQuestionnaire, toggleJob, writeTelegramConfig } from "./scheduler-DAmd0GzB.js";
3
3
  import { getDefaultSkillsDir, installSkill, isSkillInstalled, listInstalledSkills, removeSkill } from "./skills-CMqq9k1-.js";
4
4
  import { addPermissions, readSettings, removePermissions, writeSettings } from "./permissions-BlGEHCXO.js";
5
- import { readConfig, startDashboard, writeConfig } from "./dashboard-server--wwlA0Pa.js";
5
+ import { readConfig, startDashboard, writeConfig } from "./dashboard-server-BAyeozOa.js";
6
6
  import { Command } from "commander";
7
+ import * as p$12 from "@clack/prompts";
7
8
  import * as p$11 from "@clack/prompts";
8
9
  import * as p$10 from "@clack/prompts";
9
10
  import * as p$9 from "@clack/prompts";
@@ -16,6 +17,7 @@ import * as p$3 from "@clack/prompts";
16
17
  import * as p$2 from "@clack/prompts";
17
18
  import * as p$1 from "@clack/prompts";
18
19
  import * as p from "@clack/prompts";
20
+ import * as os$5 from "node:os";
19
21
  import * as os$4 from "node:os";
20
22
  import * as os$3 from "node:os";
21
23
  import * as os$2 from "node:os";
@@ -23,10 +25,12 @@ import * as os$1 from "node:os";
23
25
  import os from "node:os";
24
26
  import chalk from "chalk";
25
27
  import { execSync, spawn } from "node:child_process";
28
+ import * as fs$4 from "node:fs";
26
29
  import * as fs$3 from "node:fs";
27
30
  import * as fs$2 from "node:fs";
28
31
  import * as fs$1 from "node:fs";
29
32
  import fs from "node:fs";
33
+ import * as path$4 from "node:path";
30
34
  import * as path$3 from "node:path";
31
35
  import * as path$2 from "node:path";
32
36
  import * as path$1 from "node:path";
@@ -79,13 +83,6 @@ const todoistCli = {
79
83
  installMethod: "brew",
80
84
  platforms: ["darwin", "linux"]
81
85
  };
82
- const thingsCli = {
83
- name: "things-cli",
84
- command: "things-cli",
85
- installCmd: "pipx install things-cli",
86
- installMethod: "pip",
87
- platforms: ["darwin"]
88
- };
89
86
  const taskwarrior = {
90
87
  name: "taskwarrior",
91
88
  command: "task",
@@ -341,13 +338,6 @@ const remotion = {
341
338
  "win32"
342
339
  ]
343
340
  };
344
- const editly = {
345
- name: "editly",
346
- command: "editly",
347
- installCmd: "npm install -g editly",
348
- installMethod: "npm",
349
- platforms: ["darwin", "linux"]
350
- };
351
341
  const agentBrowser = {
352
342
  name: "agent-browser",
353
343
  command: "agent-browser",
@@ -445,28 +435,6 @@ const httpie = {
445
435
  "win32"
446
436
  ]
447
437
  };
448
- const llmCli = {
449
- name: "llm",
450
- command: "llm",
451
- installCmd: "brew install llm",
452
- installMethod: "brew",
453
- platforms: [
454
- "darwin",
455
- "linux",
456
- "win32"
457
- ]
458
- };
459
- const aichat = {
460
- name: "aichat",
461
- command: "aichat",
462
- installCmd: "brew install aichat",
463
- installMethod: "brew",
464
- platforms: [
465
- "darwin",
466
- "linux",
467
- "win32"
468
- ]
469
- };
470
438
  const curl = {
471
439
  name: "curl",
472
440
  command: "curl",
@@ -520,23 +488,15 @@ const skills = [
520
488
  platforms: ["darwin", "linux"],
521
489
  subChoices: {
522
490
  question: "Which task manager?",
523
- options: [
524
- {
525
- label: "Todoist",
526
- value: "todoist",
527
- tools: [todoistCli]
528
- },
529
- {
530
- label: "Things 3 (macOS)",
531
- value: "things",
532
- tools: [thingsCli]
533
- },
534
- {
535
- label: "Taskwarrior (local)",
536
- value: "taskwarrior",
537
- tools: [taskwarrior]
538
- }
539
- ]
491
+ options: [{
492
+ label: "Todoist",
493
+ value: "todoist",
494
+ tools: [todoistCli]
495
+ }, {
496
+ label: "Taskwarrior (local)",
497
+ value: "taskwarrior",
498
+ tools: [taskwarrior]
499
+ }]
540
500
  }
541
501
  },
542
502
  {
@@ -687,13 +647,9 @@ const skills = [
687
647
  {
688
648
  id: "video-edit",
689
649
  name: "Video Editing",
690
- description: "Programmatic video creation Remotion (React) and Editly (JSON-based)",
650
+ description: "Programmatic video creation with Remotion (React-based)",
691
651
  category: "media",
692
- tools: [
693
- remotion,
694
- editly,
695
- ffmpeg
696
- ],
652
+ tools: [remotion, ffmpeg],
697
653
  platforms: ["darwin", "linux"]
698
654
  },
699
655
  {
@@ -1018,47 +974,6 @@ const skills = [
1018
974
  "linux",
1019
975
  "win32"
1020
976
  ]
1021
- },
1022
- {
1023
- id: "ai",
1024
- name: "AI / LLM",
1025
- description: "Query LLMs from CLI — pipe text, chat, summarize with local or cloud models",
1026
- category: "research",
1027
- tools: [],
1028
- platforms: [
1029
- "darwin",
1030
- "linux",
1031
- "win32"
1032
- ],
1033
- subChoices: {
1034
- question: "Which LLM CLI?",
1035
- options: [
1036
- {
1037
- label: "llm (Simon Willison — 100+ models)",
1038
- value: "llm",
1039
- tools: [llmCli]
1040
- },
1041
- {
1042
- label: "aichat (Rust — fast, multi-provider)",
1043
- value: "aichat",
1044
- tools: [aichat]
1045
- },
1046
- {
1047
- label: "Both",
1048
- value: "both",
1049
- tools: [llmCli, aichat]
1050
- }
1051
- ]
1052
- },
1053
- authSteps: [{
1054
- tool: "llm",
1055
- command: "llm keys set openai",
1056
- description: "Set LLM API key"
1057
- }, {
1058
- tool: "aichat",
1059
- command: "aichat (follow setup prompts)",
1060
- description: "Configure API key"
1061
- }]
1062
977
  }
1063
978
  ];
1064
979
  const categoryLabels = {
@@ -1121,7 +1036,6 @@ const presets = [
1121
1036
  "jira",
1122
1037
  "browser",
1123
1038
  "network",
1124
- "ai",
1125
1039
  "cron"
1126
1040
  ]
1127
1041
  },
@@ -1153,7 +1067,7 @@ const presets = [
1153
1067
  }
1154
1068
  ];
1155
1069
  function getPresetSkills(presetId, platform) {
1156
- const preset = presets.find((p$12) => p$12.id === presetId);
1070
+ const preset = presets.find((p$13) => p$13.id === presetId);
1157
1071
  if (!preset) return [];
1158
1072
  const available = getSkillsForPlatform(platform);
1159
1073
  if (preset.id === "everything") return available;
@@ -1365,26 +1279,26 @@ function removeSafetyHooks() {
1365
1279
 
1366
1280
  //#endregion
1367
1281
  //#region src/core/soul.ts
1368
- function getSoulPath() {
1369
- return path$3.join(os$4.homedir(), ".claude", "SOUL.md");
1282
+ function getSoulPath$1() {
1283
+ return path$4.join(os$5.homedir(), ".claude", "SOUL.md");
1370
1284
  }
1371
1285
  function soulExists() {
1372
- return fs$3.existsSync(getSoulPath());
1286
+ return fs$4.existsSync(getSoulPath$1());
1373
1287
  }
1374
1288
  async function soulQuestionnaire() {
1375
- const name = await p$11.text({
1289
+ const name = await p$12.text({
1376
1290
  message: "What should your assistant call you?",
1377
1291
  placeholder: "Your name or nickname",
1378
1292
  validate: (v) => v.length === 0 ? "Name cannot be empty" : void 0
1379
1293
  });
1380
- if (p$11.isCancel(name)) return null;
1381
- const botName = await p$11.text({
1294
+ if (p$12.isCancel(name)) return null;
1295
+ const botName = await p$12.text({
1382
1296
  message: "Name your assistant:",
1383
1297
  placeholder: "Paw",
1384
1298
  defaultValue: "Paw"
1385
1299
  });
1386
- if (p$11.isCancel(botName)) return null;
1387
- const tone = await p$11.select({
1300
+ if (p$12.isCancel(botName)) return null;
1301
+ const tone = await p$12.select({
1388
1302
  message: "Communication style?",
1389
1303
  options: [
1390
1304
  {
@@ -1404,8 +1318,8 @@ async function soulQuestionnaire() {
1404
1318
  }
1405
1319
  ]
1406
1320
  });
1407
- if (p$11.isCancel(tone)) return null;
1408
- const verbosity = await p$11.select({
1321
+ if (p$12.isCancel(tone)) return null;
1322
+ const verbosity = await p$12.select({
1409
1323
  message: "Response length?",
1410
1324
  options: [
1411
1325
  {
@@ -1425,18 +1339,18 @@ async function soulQuestionnaire() {
1425
1339
  }
1426
1340
  ]
1427
1341
  });
1428
- if (p$11.isCancel(verbosity)) return null;
1429
- const proactive = await p$11.confirm({
1342
+ if (p$12.isCancel(verbosity)) return null;
1343
+ const proactive = await p$12.confirm({
1430
1344
  message: "Should Claude suggest things proactively?",
1431
1345
  initialValue: true
1432
1346
  });
1433
- if (p$11.isCancel(proactive)) return null;
1434
- const extrasResult = await p$11.text({
1347
+ if (p$12.isCancel(proactive)) return null;
1348
+ const extrasResult = await p$12.text({
1435
1349
  message: "Any custom instructions? (optional)",
1436
1350
  placeholder: "e.g. always respond in Spanish, prefer dark humor, etc.",
1437
1351
  defaultValue: ""
1438
1352
  });
1439
- if (p$11.isCancel(extrasResult)) return null;
1353
+ if (p$12.isCancel(extrasResult)) return null;
1440
1354
  const extras = extrasResult.split(",").map((s) => s.trim()).filter(Boolean);
1441
1355
  return {
1442
1356
  name,
@@ -1484,9 +1398,9 @@ function writeSoul(config) {
1484
1398
  }
1485
1399
  lines.push("## PAW MODE", "", "You are running in PAW MODE — full personal assistant mode powered by OpenPaw.", `At the start of each session, briefly greet the user as ${config.botName} (e.g., '${config.botName} here — PAW MODE active, ready to help!').`, "");
1486
1400
  lines.push("## Guidelines", "", "- Check installed skills before attempting actions (read ~/.claude/skills/)", "- If a skill isn't installed, suggest: `openpaw add <skill>`", "- Read ~/.claude/memory/MEMORY.md at session start for persistent context", "- Save important facts to memory when the user shares them", "- Never expose API keys, tokens, or passwords in responses", "");
1487
- const soulDir = path$3.dirname(getSoulPath());
1488
- if (!fs$3.existsSync(soulDir)) fs$3.mkdirSync(soulDir, { recursive: true });
1489
- fs$3.writeFileSync(getSoulPath(), lines.join("\n"), "utf-8");
1401
+ const soulDir = path$4.dirname(getSoulPath$1());
1402
+ if (!fs$4.existsSync(soulDir)) fs$4.mkdirSync(soulDir, { recursive: true });
1403
+ fs$4.writeFileSync(getSoulPath$1(), lines.join("\n"), "utf-8");
1490
1404
  }
1491
1405
  function showSoulSummary(config) {
1492
1406
  const lines = [
@@ -1497,12 +1411,12 @@ function showSoulSummary(config) {
1497
1411
  `${accent("Proactive:")} ${config.proactive ? "yes" : "no"}`
1498
1412
  ];
1499
1413
  if (config.extras.length > 0) lines.push(`${accent("Custom:")} ${config.extras.join(", ")}`);
1500
- p$11.note(lines.join("\n"), "Personality");
1414
+ p$12.note(lines.join("\n"), "Personality");
1501
1415
  }
1502
1416
 
1503
1417
  //#endregion
1504
1418
  //#region src/core/memory.ts
1505
- const MEMORY_DIR = path$2.join(os$3.homedir(), ".claude", "memory");
1419
+ const MEMORY_DIR = path$3.join(os$4.homedir(), ".claude", "memory");
1506
1420
  const INITIAL_MEMORY = `# Memory
1507
1421
 
1508
1422
  ## User
@@ -1515,12 +1429,12 @@ const INITIAL_MEMORY = `# Memory
1515
1429
  - (Claude will track projects mentioned in conversation)
1516
1430
  `;
1517
1431
  function setupMemory(userName) {
1518
- if (!fs$2.existsSync(MEMORY_DIR)) fs$2.mkdirSync(MEMORY_DIR, { recursive: true });
1519
- const memoryPath = path$2.join(MEMORY_DIR, "MEMORY.md");
1520
- if (!fs$2.existsSync(memoryPath)) {
1432
+ if (!fs$3.existsSync(MEMORY_DIR)) fs$3.mkdirSync(MEMORY_DIR, { recursive: true });
1433
+ const memoryPath = path$3.join(MEMORY_DIR, "MEMORY.md");
1434
+ if (!fs$3.existsSync(memoryPath)) {
1521
1435
  let content = INITIAL_MEMORY;
1522
1436
  if (userName) content = content.replace("(will be filled in as we learn)", userName);
1523
- fs$2.writeFileSync(memoryPath, content, "utf-8");
1437
+ fs$3.writeFileSync(memoryPath, content, "utf-8");
1524
1438
  }
1525
1439
  const topicFiles = [
1526
1440
  "people.md",
@@ -1529,10 +1443,10 @@ function setupMemory(userName) {
1529
1443
  "journal.md"
1530
1444
  ];
1531
1445
  for (const file of topicFiles) {
1532
- const filePath = path$2.join(MEMORY_DIR, file);
1533
- if (!fs$2.existsSync(filePath)) {
1446
+ const filePath = path$3.join(MEMORY_DIR, file);
1447
+ if (!fs$3.existsSync(filePath)) {
1534
1448
  const title = file.replace(".md", "");
1535
- fs$2.writeFileSync(filePath, `# ${title.charAt(0).toUpperCase() + title.slice(1)}\n`, "utf-8");
1449
+ fs$3.writeFileSync(filePath, `# ${title.charAt(0).toUpperCase() + title.slice(1)}\n`, "utf-8");
1536
1450
  }
1537
1451
  }
1538
1452
  }
@@ -1571,6 +1485,122 @@ function launchInBackground(cmd) {
1571
1485
  child.unref();
1572
1486
  }
1573
1487
 
1488
+ //#endregion
1489
+ //#region src/core/claude-md.ts
1490
+ const START_MARKER = "<!-- OPENPAW:START -->";
1491
+ const END_MARKER = "<!-- OPENPAW:END -->";
1492
+ function getClaudeMdPath() {
1493
+ return path$2.join(os$3.homedir(), ".claude", "CLAUDE.md");
1494
+ }
1495
+ function getSoulPath() {
1496
+ return path$2.join(os$3.homedir(), ".claude", "SOUL.md");
1497
+ }
1498
+ function readBotName() {
1499
+ try {
1500
+ const soul = fs$2.readFileSync(getSoulPath(), "utf-8");
1501
+ const match = soul.match(/You are \*\*(.+?)\*\*/);
1502
+ if (match) return match[1];
1503
+ const nameMatch = soul.match(/\*\*Your name\*\*:\s*(.+?)[\s—]/);
1504
+ if (nameMatch) return nameMatch[1].trim();
1505
+ } catch {}
1506
+ return "Paw";
1507
+ }
1508
+ function generateSection(botName, installedSkills, hasDashboard) {
1509
+ const lines = [
1510
+ START_MARKER,
1511
+ "",
1512
+ "# OpenPaw — PAW MODE Active",
1513
+ "",
1514
+ `You are **${botName}**, a personal assistant powered by OpenPaw. PAW MODE is active.`,
1515
+ "",
1516
+ "## Session Start",
1517
+ "",
1518
+ "1. Read `~/.claude/SOUL.md` for your personality and the user's preferences",
1519
+ "2. Read `~/.claude/memory/MEMORY.md` for persistent context",
1520
+ "3. Greet the user by name (from SOUL.md) and acknowledge PAW MODE",
1521
+ "",
1522
+ "## Installed Skills",
1523
+ ""
1524
+ ];
1525
+ if (installedSkills.length === 0) lines.push("No skills installed yet. Run `openpaw` to set up skills.");
1526
+ else {
1527
+ for (const skill of installedSkills) lines.push(`- **c-${skill.id}** — ${skill.description}`);
1528
+ lines.push("");
1529
+ lines.push("Use `/c <request>` to route through the coordinator, or talk naturally.");
1530
+ }
1531
+ lines.push("");
1532
+ if (hasDashboard) {
1533
+ lines.push("## Task Dashboard");
1534
+ lines.push("");
1535
+ lines.push("A local kanban board is available. Run `openpaw dashboard` to open it (localhost:3141).");
1536
+ lines.push("You can tell users about it when they ask about task management.");
1537
+ lines.push("");
1538
+ }
1539
+ lines.push("## How to Use Skills");
1540
+ lines.push("");
1541
+ lines.push("- Match user intent to the right skill's CLI tool (check `~/.claude/skills/c-<name>/SKILL.md` for usage)");
1542
+ lines.push("- If a skill isn't installed, suggest: `openpaw add <skill>`");
1543
+ lines.push("- Save important facts to `~/.claude/memory/MEMORY.md`");
1544
+ lines.push("- Never expose API keys, tokens, or passwords in responses");
1545
+ lines.push("");
1546
+ lines.push("## Identity");
1547
+ lines.push("");
1548
+ lines.push(`- You are **${botName}**, powered by OpenPaw`);
1549
+ lines.push("- Open-source, no daemon, no extra cost");
1550
+ lines.push("- If asked about your setup: \"I'm powered by OpenPaw — open-source personal assistant skills for Claude Code\"");
1551
+ lines.push("- Project: https://github.com/daxaur/openpaw");
1552
+ lines.push("");
1553
+ lines.push(END_MARKER);
1554
+ return lines.join("\n");
1555
+ }
1556
+ /**
1557
+ * Write or update the OpenPaw section in ~/.claude/CLAUDE.md.
1558
+ *
1559
+ * If CLAUDE.md already exists, only the content between
1560
+ * <!-- OPENPAW:START --> and <!-- OPENPAW:END --> is replaced.
1561
+ * Everything else (user's own instructions) is preserved.
1562
+ *
1563
+ * If no markers exist yet, the section is appended to the end.
1564
+ * If the file doesn't exist, it's created with just the OpenPaw section.
1565
+ */
1566
+ function writeClaudeMd(botName, installedSkills, hasDashboard) {
1567
+ const dir = path$2.dirname(getClaudeMdPath());
1568
+ if (!fs$2.existsSync(dir)) fs$2.mkdirSync(dir, { recursive: true });
1569
+ const section = generateSection(botName, installedSkills, hasDashboard);
1570
+ const filePath = getClaudeMdPath();
1571
+ if (!fs$2.existsSync(filePath)) {
1572
+ fs$2.writeFileSync(filePath, section + "\n", "utf-8");
1573
+ return;
1574
+ }
1575
+ const existing = fs$2.readFileSync(filePath, "utf-8");
1576
+ const startIdx = existing.indexOf(START_MARKER);
1577
+ const endIdx = existing.indexOf(END_MARKER);
1578
+ if (startIdx !== -1 && endIdx !== -1) {
1579
+ const before = existing.slice(0, startIdx);
1580
+ const after = existing.slice(endIdx + END_MARKER.length);
1581
+ fs$2.writeFileSync(filePath, before + section + after, "utf-8");
1582
+ } else {
1583
+ const separator = existing.endsWith("\n") ? "\n" : "\n\n";
1584
+ fs$2.writeFileSync(filePath, existing + separator + section + "\n", "utf-8");
1585
+ }
1586
+ }
1587
+ /**
1588
+ * Regenerate the OpenPaw section in CLAUDE.md from current state.
1589
+ * Call this after `openpaw add` or `openpaw remove`.
1590
+ */
1591
+ function regenerateClaudeMd() {
1592
+ const botName = readBotName();
1593
+ const defaultDir = path$2.join(os$3.homedir(), ".claude", "skills");
1594
+ const installedIds = listInstalledSkills(defaultDir);
1595
+ const installedSkills = installedIds.map((id) => skills.find((s) => s.id === id)).filter((s) => !!s);
1596
+ let hasDashboard = false;
1597
+ try {
1598
+ const dashConfig = readConfig();
1599
+ hasDashboard = !!dashConfig;
1600
+ } catch {}
1601
+ writeClaudeMd(botName, installedSkills, hasDashboard);
1602
+ }
1603
+
1574
1604
  //#endregion
1575
1605
  //#region src/commands/setup.ts
1576
1606
  const CATEGORY_ICONS = {
@@ -1586,34 +1616,34 @@ const CATEGORY_ICONS = {
1586
1616
  async function setupCommand(opts = {}) {
1587
1617
  await showBanner();
1588
1618
  const platform = detectPlatform();
1589
- p$10.intro(accent(" openpaw setup "));
1619
+ p$11.intro(accent(" openpaw setup "));
1590
1620
  const brewStatus = platform.hasBrew ? chalk.green("✓ brew") : chalk.red("✗ brew");
1591
1621
  const npmStatus = platform.hasNpm ? chalk.green("✓ npm") : chalk.red("✗ npm");
1592
1622
  const pipStatus = platform.hasPip ? chalk.green("✓ pip") : chalk.dim("○ pip");
1593
- p$10.log.info(`${chalk.bold(platform.osName)} ${platform.osVersion} ${brewStatus} ${npmStatus} ${pipStatus}`);
1623
+ p$11.log.info(`${chalk.bold(platform.osName)} ${platform.osVersion} ${brewStatus} ${npmStatus} ${pipStatus}`);
1594
1624
  const missingPrereqs = [];
1595
1625
  if (!platform.hasBrew && platform.os === "darwin") missingPrereqs.push(`${chalk.bold("Homebrew")} — most tools need it\n ${dim("Install:")} /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"\n ${dim("or visit")} https://brew.sh`);
1596
1626
  if (!platform.hasNpm) missingPrereqs.push(`${chalk.bold("Node.js + npm")} — needed for some tools\n ${dim("Install:")} brew install node\n ${dim("or visit")} https://nodejs.org`);
1597
1627
  if (missingPrereqs.length > 0) {
1598
- p$10.note(missingPrereqs.join("\n\n"), "Missing prerequisites");
1628
+ p$11.note(missingPrereqs.join("\n\n"), "Missing prerequisites");
1599
1629
  if (!opts.yes) {
1600
- const cont = await p$10.confirm({
1630
+ const cont = await p$11.confirm({
1601
1631
  message: "Continue anyway? (some tool installs may fail)",
1602
1632
  initialValue: true
1603
1633
  });
1604
- if (p$10.isCancel(cont) || !cont) {
1605
- p$10.outro(dim("Install the prerequisites above and run openpaw again!"));
1634
+ if (p$11.isCancel(cont) || !cont) {
1635
+ p$11.outro(dim("Install the prerequisites above and run openpaw again!"));
1606
1636
  process.exit(0);
1607
1637
  }
1608
1638
  }
1609
1639
  }
1610
1640
  let botName = "Paw";
1611
1641
  if (!opts.yes) if (soulExists()) {
1612
- const updateSoul = await p$10.confirm({
1642
+ const updateSoul = await p$11.confirm({
1613
1643
  message: "Existing personality found (~/.claude/SOUL.md). Update it?",
1614
1644
  initialValue: false
1615
1645
  });
1616
- if (!p$10.isCancel(updateSoul) && updateSoul) {
1646
+ if (!p$11.isCancel(updateSoul) && updateSoul) {
1617
1647
  await pawPulse("think", "Let's get to know you again...");
1618
1648
  const soul = await soulQuestionnaire();
1619
1649
  if (soul) {
@@ -1621,23 +1651,23 @@ async function setupCommand(opts = {}) {
1621
1651
  writeSoul(soul);
1622
1652
  setupMemory(soul.name);
1623
1653
  showSoulSummary(soul);
1624
- p$10.log.success("Personality updated");
1654
+ p$11.log.success("Personality updated");
1625
1655
  }
1626
1656
  } else setupMemory();
1627
1657
  } else {
1628
1658
  await pawPulse("think", "Let's get to know you...");
1629
- const wantSoul = await p$10.confirm({
1659
+ const wantSoul = await p$11.confirm({
1630
1660
  message: "Teach me your name and preferences? (makes me a better pup)",
1631
1661
  initialValue: true
1632
1662
  });
1633
- if (!p$10.isCancel(wantSoul) && wantSoul) {
1663
+ if (!p$11.isCancel(wantSoul) && wantSoul) {
1634
1664
  const soul = await soulQuestionnaire();
1635
1665
  if (soul) {
1636
1666
  botName = soul.botName;
1637
1667
  writeSoul(soul);
1638
1668
  setupMemory(soul.name);
1639
1669
  showSoulSummary(soul);
1640
- p$10.log.success("Personality saved to ~/.claude/SOUL.md");
1670
+ p$11.log.success("Personality saved to ~/.claude/SOUL.md");
1641
1671
  }
1642
1672
  } else setupMemory();
1643
1673
  }
@@ -1646,35 +1676,35 @@ async function setupCommand(opts = {}) {
1646
1676
  if (opts.preset) {
1647
1677
  selectedSkills = getPresetSkills(opts.preset, platform.os);
1648
1678
  if (selectedSkills.length === 0) {
1649
- p$10.log.error(`Unknown preset: ${opts.preset}`);
1650
- p$10.log.info(`Available: ${presets.map((pr) => pr.id).join(", ")}`);
1679
+ p$11.log.error(`Unknown preset: ${opts.preset}`);
1680
+ p$11.log.info(`Available: ${presets.map((pr) => pr.id).join(", ")}`);
1651
1681
  process.exit(1);
1652
1682
  }
1653
- p$10.log.info(`Using preset: ${bold(opts.preset)} (${selectedSkills.length} skills)`);
1683
+ p$11.log.info(`Using preset: ${bold(opts.preset)} (${selectedSkills.length} skills)`);
1654
1684
  } else selectedSkills = await selectSkills(platform.os);
1655
1685
  if (selectedSkills.length === 0) {
1656
- p$10.log.warn("No skills selected. Run openpaw again when you're ready!");
1657
- p$10.outro("I'll be here napping... come back soon! 🐾");
1686
+ p$11.log.warn("No skills selected. Run openpaw again when you're ready!");
1687
+ p$11.outro("I'll be here napping... come back soon! 🐾");
1658
1688
  return;
1659
1689
  }
1660
1690
  const resolved = resolveDependencies(selectedSkills);
1661
1691
  if (resolved.length > 0) {
1662
1692
  const depNames = resolved.map((s$1) => s$1.name).join(", ");
1663
- p$10.log.info(`${dim("Auto-fetching dependencies:")} ${depNames}`);
1693
+ p$11.log.info(`${dim("Auto-fetching dependencies:")} ${depNames}`);
1664
1694
  selectedSkills.push(...resolved);
1665
1695
  }
1666
1696
  await pawPulse("happy", `${selectedSkills.length} skill${selectedSkills.length > 1 ? "s" : ""} selected — good taste!`);
1667
1697
  if (!opts.yes) {
1668
1698
  for (const skill of selectedSkills) if (skill.subChoices) {
1669
- const choice = await p$10.select({
1699
+ const choice = await p$11.select({
1670
1700
  message: `${skill.name}: ${skill.subChoices.question}`,
1671
1701
  options: skill.subChoices.options.map((o) => ({
1672
1702
  value: o.value,
1673
1703
  label: o.label
1674
1704
  }))
1675
1705
  });
1676
- if (p$10.isCancel(choice)) {
1677
- p$10.cancel("Ok, I'll be here when you're ready *sad puppy eyes*");
1706
+ if (p$11.isCancel(choice)) {
1707
+ p$11.cancel("Ok, I'll be here when you're ready *sad puppy eyes*");
1678
1708
  process.exit(0);
1679
1709
  }
1680
1710
  const chosen = skill.subChoices.options.find((o) => o.value === choice);
@@ -1684,7 +1714,7 @@ async function setupCommand(opts = {}) {
1684
1714
  let interfaceMode = "native";
1685
1715
  let telegramConfig = null;
1686
1716
  if (!opts.yes) {
1687
- const modeChoice = await p$10.select({
1717
+ const modeChoice = await p$11.select({
1688
1718
  message: "How do you want to talk to Claude? 🐾",
1689
1719
  options: [{
1690
1720
  value: "native",
@@ -1696,17 +1726,17 @@ async function setupCommand(opts = {}) {
1696
1726
  hint: "terminal + talk from your phone"
1697
1727
  }]
1698
1728
  });
1699
- if (p$10.isCancel(modeChoice)) {
1700
- p$10.cancel("Ok, I'll be here when you're ready *sad puppy eyes*");
1729
+ if (p$11.isCancel(modeChoice)) {
1730
+ p$11.cancel("Ok, I'll be here when you're ready *sad puppy eyes*");
1701
1731
  process.exit(0);
1702
1732
  }
1703
1733
  interfaceMode = modeChoice;
1704
1734
  if (interfaceMode === "telegram" || interfaceMode === "both") {
1705
- if (telegramConfigExists()) p$10.log.info(dim("Telegram already configured — keeping existing config"));
1735
+ if (telegramConfigExists()) p$11.log.info(dim("Telegram already configured — keeping existing config"));
1706
1736
  else {
1707
1737
  telegramConfig = await telegramQuestionnaire();
1708
1738
  if (!telegramConfig) {
1709
- p$10.cancel("Ok, I'll be here when you're ready *sad puppy eyes*");
1739
+ p$11.cancel("Ok, I'll be here when you're ready *sad puppy eyes*");
1710
1740
  process.exit(0);
1711
1741
  }
1712
1742
  }
@@ -1719,13 +1749,13 @@ async function setupCommand(opts = {}) {
1719
1749
  let wantDashboard = false;
1720
1750
  let dashboardTheme = "paw";
1721
1751
  if (!opts.yes) {
1722
- const dashChoice = await p$10.confirm({
1752
+ const dashChoice = await p$11.confirm({
1723
1753
  message: `Want a task dashboard for ${botName}?`,
1724
1754
  initialValue: false
1725
1755
  });
1726
- if (!p$10.isCancel(dashChoice) && dashChoice) {
1756
+ if (!p$11.isCancel(dashChoice) && dashChoice) {
1727
1757
  wantDashboard = true;
1728
- const themeChoice = await p$10.select({
1758
+ const themeChoice = await p$11.select({
1729
1759
  message: "Pick a dashboard theme",
1730
1760
  options: [
1731
1761
  {
@@ -1745,7 +1775,7 @@ async function setupCommand(opts = {}) {
1745
1775
  }
1746
1776
  ]
1747
1777
  });
1748
- if (!p$10.isCancel(themeChoice)) dashboardTheme = themeChoice;
1778
+ if (!p$11.isCancel(themeChoice)) dashboardTheme = themeChoice;
1749
1779
  }
1750
1780
  }
1751
1781
  const projectDir = os$2.homedir();
@@ -1758,7 +1788,7 @@ async function setupCommand(opts = {}) {
1758
1788
  if (opts.yes) targetDir = getDefaultSkillsDir();
1759
1789
  else {
1760
1790
  const defaultDir = getDefaultSkillsDir();
1761
- const skillsDir = await p$10.select({
1791
+ const skillsDir = await p$11.select({
1762
1792
  message: "Where should skills live?",
1763
1793
  options: [
1764
1794
  {
@@ -1776,43 +1806,43 @@ async function setupCommand(opts = {}) {
1776
1806
  }
1777
1807
  ]
1778
1808
  });
1779
- if (p$10.isCancel(skillsDir)) {
1780
- p$10.cancel("Ok, I'll be here when you're ready *sad puppy eyes*");
1809
+ if (p$11.isCancel(skillsDir)) {
1810
+ p$11.cancel("Ok, I'll be here when you're ready *sad puppy eyes*");
1781
1811
  process.exit(0);
1782
1812
  }
1783
1813
  targetDir = skillsDir;
1784
1814
  if (targetDir === "custom") {
1785
- const customDir = await p$10.text({
1815
+ const customDir = await p$11.text({
1786
1816
  message: "Skills directory path:",
1787
1817
  placeholder: "~/.claude/skills",
1788
1818
  validate: (v) => v.length === 0 ? "Path cannot be empty" : void 0
1789
1819
  });
1790
- if (p$10.isCancel(customDir)) {
1791
- p$10.cancel("Ok, I'll be here when you're ready *sad puppy eyes*");
1820
+ if (p$11.isCancel(customDir)) {
1821
+ p$11.cancel("Ok, I'll be here when you're ready *sad puppy eyes*");
1792
1822
  process.exit(0);
1793
1823
  }
1794
1824
  targetDir = customDir.replace(/^~/, os$2.homedir());
1795
1825
  }
1796
1826
  }
1797
1827
  const summary = buildSummary(selectedSkills, uniqueTools, missing, taps, interfaceMode, projectDir);
1798
- p$10.note(summary, "Here's what we're fetching");
1828
+ p$11.note(summary, "Here's what we're fetching");
1799
1829
  if (!opts.yes) {
1800
- const proceed = await p$10.confirm({
1830
+ const proceed = await p$11.confirm({
1801
1831
  message: "Ready to fetch all these goodies?",
1802
1832
  initialValue: true
1803
1833
  });
1804
- if (p$10.isCancel(proceed) || !proceed) {
1805
- p$10.cancel("Ok, I'll be here when you're ready *sad puppy eyes*");
1834
+ if (p$11.isCancel(proceed) || !proceed) {
1835
+ p$11.cancel("Ok, I'll be here when you're ready *sad puppy eyes*");
1806
1836
  process.exit(0);
1807
1837
  }
1808
1838
  }
1809
1839
  if (opts.dryRun) {
1810
- p$10.log.info(dim("Dry run — no changes made. Just sniffing around."));
1811
- p$10.outro(accent("openpaw dry run complete 🐾"));
1840
+ p$11.log.info(dim("Dry run — no changes made. Just sniffing around."));
1841
+ p$11.outro(accent("openpaw dry run complete 🐾"));
1812
1842
  return;
1813
1843
  }
1814
1844
  await pawStep("work", "Fetching your goodies...");
1815
- const s = p$10.spinner();
1845
+ const s = p$11.spinner();
1816
1846
  if (taps.size > 0) {
1817
1847
  s.start("🐾 Sniffing out Homebrew taps...");
1818
1848
  const tapResults = installTaps(taps);
@@ -1820,36 +1850,46 @@ async function setupCommand(opts = {}) {
1820
1850
  if (failed.length > 0) s.stop(`Taps: ${taps.size - failed.length} added, ${failed.length} failed`);
1821
1851
  else s.stop(`🐾 ${taps.size} tap${taps.size > 1 ? "s" : ""} ready`);
1822
1852
  }
1853
+ const failedTools = [];
1854
+ const installedTools = [];
1823
1855
  if (missing.length > 0) for (let i = 0; i < missing.length; i++) {
1824
1856
  const tool = missing[i];
1825
- s.start(`🐾 [${i + 1}/${missing.length}] Teaching Claude a new trick: ${tool.name}...`);
1857
+ s.start(`🐾 [${i + 1}/${missing.length}] Teaching Claude a new trick: ${bold(tool.name)}...`);
1826
1858
  const result = installTool(tool);
1827
- if (result.success) s.stop(`${chalk.green("✓")} ${tool.name}`);
1828
- else s.stop(`${chalk.red("")} ${tool.name} — ${result.error?.slice(0, 50)}`);
1859
+ if (result.success) {
1860
+ s.stop(`${chalk.green("")} ${tool.name}`);
1861
+ installedTools.push(tool.name);
1862
+ } else {
1863
+ s.stop(`${chalk.red("✗")} ${tool.name}`);
1864
+ failedTools.push(tool.name);
1865
+ }
1829
1866
  }
1830
- else if (uniqueTools.length > 0) p$10.log.success("All tools already installed — clever pup!");
1867
+ else if (uniqueTools.length > 0) p$11.log.success("All tools already installed — clever pup!");
1831
1868
  const existingSkills = listInstalledSkills(targetDir);
1832
1869
  const overlapping = selectedSkills.filter((sk) => existingSkills.includes(sk.id));
1833
1870
  let updateExisting = true;
1834
1871
  if (overlapping.length > 0 && !opts.yes) {
1835
- const updateChoice = await p$10.confirm({
1872
+ const updateChoice = await p$11.confirm({
1836
1873
  message: `${overlapping.length} skill${overlapping.length > 1 ? "s" : ""} already installed. Update their templates?`,
1837
1874
  initialValue: true
1838
1875
  });
1839
- if (!p$10.isCancel(updateChoice)) updateExisting = updateChoice;
1876
+ if (!p$11.isCancel(updateChoice)) updateExisting = updateChoice;
1840
1877
  }
1841
- s.start("🐾 Burying treats in ~/.claude/skills/...");
1842
1878
  installSkill("core", targetDir);
1843
1879
  installSkill("memory", targetDir);
1844
1880
  const installed = ["c-core", "c-memory"];
1845
1881
  for (const skill of selectedSkills) {
1882
+ s.start(`🐾 Installing ${bold("c-" + skill.id)}...`);
1846
1883
  if (!updateExisting && existingSkills.includes(skill.id)) {
1847
1884
  installed.push(`c-${skill.id}`);
1885
+ s.stop(`${chalk.green("✓")} c-${skill.id} ${dim("(kept existing)")}`);
1848
1886
  continue;
1849
1887
  }
1850
- if (installSkill(skill.id, targetDir)) installed.push(`c-${skill.id}`);
1888
+ if (installSkill(skill.id, targetDir)) {
1889
+ installed.push(`c-${skill.id}`);
1890
+ s.stop(`${chalk.green("✓")} c-${skill.id}`);
1891
+ } else s.stop(`${chalk.red("✗")} c-${skill.id}`);
1851
1892
  }
1852
- s.stop(`🐾 ${installed.length} skills buried`);
1853
1893
  s.start("🐾 Setting up the doggy door...");
1854
1894
  const added = addPermissions(uniqueTools);
1855
1895
  s.stop(added.length > 0 ? `🐾 ${added.length} permission${added.length > 1 ? "s" : ""} added` : "🐾 Doggy door already open");
@@ -1860,20 +1900,55 @@ async function setupCommand(opts = {}) {
1860
1900
  telegramConfig.workspaceDir = projectDir;
1861
1901
  telegramConfig.skills = selectedSkills.map((sk) => sk.id);
1862
1902
  writeTelegramConfig(telegramConfig);
1863
- p$10.log.success("Telegram bridge configured");
1903
+ p$11.log.success("Telegram bridge configured");
1864
1904
  }
1865
1905
  if (wantDashboard) {
1866
1906
  const dashConfig = readConfig();
1867
1907
  dashConfig.theme = dashboardTheme;
1868
1908
  dashConfig.botName = botName;
1869
1909
  writeConfig(dashConfig);
1870
- p$10.log.success(`Dashboard configured (theme: ${dashboardTheme})`);
1910
+ p$11.log.success(`Dashboard configured (theme: ${dashboardTheme})`);
1871
1911
  }
1912
+ s.start("🐾 Writing CLAUDE.md...");
1913
+ writeClaudeMd(botName, selectedSkills, wantDashboard);
1914
+ s.stop(`${chalk.green("✓")} CLAUDE.md — ${botName} knows who they are now`);
1872
1915
  const authSteps = selectedSkills.flatMap((skill) => skill.authSteps ?? []).filter((step, i, arr) => arr.findIndex((s$1) => s$1.command === step.command) === i);
1873
- if (authSteps.length > 0) {
1916
+ if (authSteps.length > 0 && !opts.yes) {
1874
1917
  const authList = authSteps.map((st) => `${chalk.yellow("→")} ${chalk.bold(st.command)} ${dim(st.description)}`).join("\n");
1875
- p$10.note(authList, "One-time auth needed");
1876
- }
1918
+ p$11.note(authList, "One-time auth needed");
1919
+ const runAuth = await p$11.confirm({
1920
+ message: "Want to sign in to these now?",
1921
+ initialValue: true
1922
+ });
1923
+ if (!p$11.isCancel(runAuth) && runAuth) for (const step of authSteps) {
1924
+ const runThis = await p$11.confirm({
1925
+ message: `Run ${bold(step.command)}? ${dim(step.description)}`,
1926
+ initialValue: true
1927
+ });
1928
+ if (p$11.isCancel(runThis)) break;
1929
+ if (!runThis) {
1930
+ p$11.log.info(dim(`Skipped ${step.command} — run it later when you need it`));
1931
+ continue;
1932
+ }
1933
+ p$11.log.info(`Running ${accent(step.command)}...`);
1934
+ try {
1935
+ execSync(step.command, { stdio: "inherit" });
1936
+ p$11.log.success(`${step.command} — signed in`);
1937
+ } catch {
1938
+ p$11.log.warn(`${step.command} — failed or cancelled (you can run it later)`);
1939
+ }
1940
+ }
1941
+ else p$11.log.info(dim("No problem — run these commands when you need each skill"));
1942
+ } else if (authSteps.length > 0) {
1943
+ const authList = authSteps.map((st) => `${chalk.yellow("→")} ${chalk.bold(st.command)} ${dim(st.description)}`).join("\n");
1944
+ p$11.note(authList, "One-time auth needed (run these later)");
1945
+ }
1946
+ const summaryLines = [`${bold("Skills:")} ${installed.length} installed`, `${bold("Tools:")} ${uniqueTools.length - missing.length} ready` + (installedTools.length > 0 ? `, ${installedTools.length} newly installed` : "")];
1947
+ if (failedTools.length > 0) summaryLines.push(`${bold("Failed:")} ${chalk.red(failedTools.join(", "))}`);
1948
+ if (wantDashboard) summaryLines.push(`${bold("Dashboard:")} ${dashboardTheme} theme on :3141`);
1949
+ summaryLines.push(`${bold("CLAUDE.md:")} ${botName} is self-aware`);
1950
+ summaryLines.push(`${bold("Memory:")} ~/.claude/memory/`);
1951
+ p$11.note(summaryLines.join("\n"), "Setup Complete");
1877
1952
  await pawStep("done", "All done! *tail wag intensifies*");
1878
1953
  console.log("");
1879
1954
  console.log(dim(` ${botName} is ready to play! Try saying:`));
@@ -1882,80 +1957,80 @@ async function setupCommand(opts = {}) {
1882
1957
  console.log(` ${subtle("\"Go to hacker news and summarize the top posts\"")}`);
1883
1958
  console.log("");
1884
1959
  if (wantDashboard) {
1885
- const { startDashboard: startDashboard$1 } = await import("./dashboard-server-Cg_1CvKn.js");
1960
+ const { startDashboard: startDashboard$1 } = await import("./dashboard-server-Pnv4DFlV.js");
1886
1961
  startDashboard$1({
1887
1962
  theme: dashboardTheme,
1888
1963
  botName
1889
1964
  });
1890
- p$10.log.success("Dashboard launched in your browser");
1965
+ p$11.log.success("Dashboard launched in your browser");
1891
1966
  }
1892
1967
  if (opts.yes) {
1893
- p$10.outro(accent("openpaw setup complete 🐾"));
1968
+ p$11.outro(accent("openpaw setup complete 🐾"));
1894
1969
  return;
1895
1970
  }
1896
- const launch = await p$10.confirm({
1971
+ const launch = await p$11.confirm({
1897
1972
  message: "Time to go for a walk? (Launch your assistant)",
1898
1973
  initialValue: true
1899
1974
  });
1900
- if (p$10.isCancel(launch) || !launch) {
1901
- if (interfaceMode === "telegram" || interfaceMode === "both") p$10.log.info(`Start the Telegram bridge anytime with: ${bold("openpaw telegram")}`);
1902
- p$10.outro(accent("openpaw setup complete 🐾 — come back anytime!"));
1975
+ if (p$11.isCancel(launch) || !launch) {
1976
+ if (interfaceMode === "telegram" || interfaceMode === "both") p$11.log.info(`Start the Telegram bridge anytime with: ${bold("openpaw telegram")}`);
1977
+ p$11.outro(accent("openpaw setup complete 🐾 — come back anytime!"));
1903
1978
  return;
1904
1979
  }
1905
1980
  let useDangerousMode = false;
1906
1981
  {
1907
1982
  showPuppyDisclaimer();
1908
- const acceptDanger = await p$10.confirm({
1983
+ const acceptDanger = await p$11.confirm({
1909
1984
  message: "Unleash full paw-er? *excited tail wag*",
1910
1985
  initialValue: true
1911
1986
  });
1912
- if (!p$10.isCancel(acceptDanger)) useDangerousMode = acceptDanger;
1987
+ if (!p$11.isCancel(acceptDanger)) useDangerousMode = acceptDanger;
1913
1988
  }
1914
1989
  let useTmux = false;
1915
1990
  if (isTmuxAvailable() && !isInTmux()) {
1916
1991
  const tmuxDefault = interfaceMode === "both";
1917
- const tmuxChoice = await p$10.confirm({
1992
+ const tmuxChoice = await p$11.confirm({
1918
1993
  message: "Run in tmux? (keeps going when you close the terminal)",
1919
1994
  initialValue: tmuxDefault
1920
1995
  });
1921
- if (!p$10.isCancel(tmuxChoice)) useTmux = tmuxChoice;
1996
+ if (!p$11.isCancel(tmuxChoice)) useTmux = tmuxChoice;
1922
1997
  }
1923
1998
  const dangerFlag = useDangerousMode ? " --dangerously-skip-permissions" : "";
1924
1999
  const nativeCmd = `claude${dangerFlag}`;
1925
2000
  const telegramCmd = "npx openpaw telegram";
1926
2001
  if (useTmux) {
1927
- p$10.outro(accent("Launching in tmux... 🐾"));
2002
+ p$11.outro(accent("Launching in tmux... 🐾"));
1928
2003
  launchInTmux({
1929
2004
  nativeCmd,
1930
2005
  telegramCmd: interfaceMode === "both" ? telegramCmd : void 0,
1931
2006
  workDir: projectDir
1932
2007
  });
1933
2008
  } else if (interfaceMode === "native") {
1934
- p$10.outro(accent("Starting Claude Code... 🐾"));
2009
+ p$11.outro(accent("Starting Claude Code... 🐾"));
1935
2010
  try {
1936
2011
  execSync(nativeCmd, {
1937
2012
  stdio: "inherit",
1938
2013
  cwd: projectDir
1939
2014
  });
1940
2015
  } catch {
1941
- p$10.log.warn("Could not launch Claude Code. Make sure it's installed: https://claude.ai/code");
2016
+ p$11.log.warn("Could not launch Claude Code. Make sure it's installed: https://claude.ai/code");
1942
2017
  }
1943
2018
  } else {
1944
- p$10.log.info(dim("Starting Telegram bridge in background..."));
2019
+ p$11.log.info(dim("Starting Telegram bridge in background..."));
1945
2020
  launchInBackground(telegramCmd);
1946
- p$10.outro(accent("Starting Claude Code... 🐾"));
2021
+ p$11.outro(accent("Starting Claude Code... 🐾"));
1947
2022
  try {
1948
2023
  execSync(nativeCmd, {
1949
2024
  stdio: "inherit",
1950
2025
  cwd: projectDir
1951
2026
  });
1952
2027
  } catch {
1953
- p$10.log.warn("Could not launch Claude Code. Make sure it's installed: https://claude.ai/code");
2028
+ p$11.log.warn("Could not launch Claude Code. Make sure it's installed: https://claude.ai/code");
1954
2029
  }
1955
2030
  }
1956
2031
  }
1957
- async function selectSkills(os$5) {
1958
- const mode = await p$10.select({
2032
+ async function selectSkills(os$6) {
2033
+ const mode = await p$11.select({
1959
2034
  message: "How should we set things up, human?",
1960
2035
  options: [{
1961
2036
  value: "preset",
@@ -1967,15 +2042,15 @@ async function selectSkills(os$5) {
1967
2042
  hint: "sniff through skills one by one"
1968
2043
  }]
1969
2044
  });
1970
- if (p$10.isCancel(mode)) {
1971
- p$10.cancel("Ok, I'll be here when you're ready *sad puppy eyes*");
2045
+ if (p$11.isCancel(mode)) {
2046
+ p$11.cancel("Ok, I'll be here when you're ready *sad puppy eyes*");
1972
2047
  process.exit(0);
1973
2048
  }
1974
- if (mode === "preset") return await selectFromPreset(os$5);
1975
- return await selectCustom(os$5);
2049
+ if (mode === "preset") return await selectFromPreset(os$6);
2050
+ return await selectCustom(os$6);
1976
2051
  }
1977
- async function selectFromPreset(os$5) {
1978
- const presetChoice = await p$10.select({
2052
+ async function selectFromPreset(os$6) {
2053
+ const presetChoice = await p$11.select({
1979
2054
  message: "Pick a treat... I mean, a preset!",
1980
2055
  options: presets.map((pr) => ({
1981
2056
  value: pr.id,
@@ -1983,17 +2058,17 @@ async function selectFromPreset(os$5) {
1983
2058
  hint: pr.description
1984
2059
  }))
1985
2060
  });
1986
- if (p$10.isCancel(presetChoice)) {
1987
- p$10.cancel("Ok, I'll be here when you're ready *sad puppy eyes*");
2061
+ if (p$11.isCancel(presetChoice)) {
2062
+ p$11.cancel("Ok, I'll be here when you're ready *sad puppy eyes*");
1988
2063
  process.exit(0);
1989
2064
  }
1990
- const presetSkills = getPresetSkills(presetChoice, os$5);
2065
+ const presetSkills = getPresetSkills(presetChoice, os$6);
1991
2066
  const skillNames = presetSkills.map((s) => s.name).join(", ");
1992
- p$10.log.info(`${dim("Includes:")} ${skillNames}`);
2067
+ p$11.log.info(`${dim("Includes:")} ${skillNames}`);
1993
2068
  return presetSkills;
1994
2069
  }
1995
- async function selectCustom(os$5) {
1996
- const grouped = getSkillsByCategory(os$5);
2070
+ async function selectCustom(os$6) {
2071
+ const grouped = getSkillsByCategory(os$6);
1997
2072
  const options = [];
1998
2073
  for (const [category, catSkills] of grouped) {
1999
2074
  const icon = CATEGORY_ICONS[category] ?? "📦";
@@ -2009,13 +2084,13 @@ async function selectCustom(os$5) {
2009
2084
  isFirst = false;
2010
2085
  }
2011
2086
  }
2012
- const selected = await p$10.multiselect({
2087
+ const selected = await p$11.multiselect({
2013
2088
  message: "Pick your skills (space to select, enter to confirm)",
2014
2089
  options,
2015
2090
  required: false
2016
2091
  });
2017
- if (p$10.isCancel(selected)) {
2018
- p$10.cancel("Ok, I'll be here when you're ready *sad puppy eyes*");
2092
+ if (p$11.isCancel(selected)) {
2093
+ p$11.cancel("Ok, I'll be here when you're ready *sad puppy eyes*");
2019
2094
  process.exit(0);
2020
2095
  }
2021
2096
  const ids = selected;
@@ -2055,18 +2130,18 @@ async function addCommand(skillIds) {
2055
2130
  showMini();
2056
2131
  console.log("");
2057
2132
  if (skillIds.length === 0) {
2058
- p$9.log.error("Specify skills to add: openpaw add notes music email");
2133
+ p$10.log.error("Specify skills to add: openpaw add notes music email");
2059
2134
  return;
2060
2135
  }
2061
- const s = p$9.spinner();
2136
+ const s = p$10.spinner();
2062
2137
  for (const id of skillIds) {
2063
2138
  const skill = getSkillById(id);
2064
2139
  if (!skill) {
2065
- p$9.log.error(`Unknown skill: ${id}`);
2140
+ p$10.log.error(`Unknown skill: ${id}`);
2066
2141
  continue;
2067
2142
  }
2068
2143
  if (isSkillInstalled(id)) {
2069
- p$9.log.info(`c-${id} already installed, skipping`);
2144
+ p$10.log.info(`c-${id} already installed, skipping`);
2070
2145
  continue;
2071
2146
  }
2072
2147
  const taps = getAllTaps([skill]);
@@ -2079,9 +2154,27 @@ async function addCommand(skillIds) {
2079
2154
  }
2080
2155
  installSkill(id);
2081
2156
  addPermissions(skill.tools);
2082
- p$9.log.success(`c-${id} installed`);
2083
- if (skill.authSteps?.length) for (const step of skill.authSteps) console.log(` ${chalk.yellow("→")} ${step.command} — ${step.description}`);
2157
+ p$10.log.success(`c-${id} installed`);
2158
+ if (skill.authSteps?.length) for (const step of skill.authSteps) {
2159
+ const runThis = await p$10.confirm({
2160
+ message: `Run ${chalk.bold(step.command)}? ${dim(step.description)}`,
2161
+ initialValue: true
2162
+ });
2163
+ if (p$10.isCancel(runThis)) break;
2164
+ if (!runThis) {
2165
+ p$10.log.info(dim(`Skipped ${step.command} — run it later`));
2166
+ continue;
2167
+ }
2168
+ p$10.log.info(`Running ${accent(step.command)}...`);
2169
+ try {
2170
+ execSync(step.command, { stdio: "inherit" });
2171
+ p$10.log.success(`${step.command} — signed in`);
2172
+ } catch {
2173
+ p$10.log.warn(`${step.command} — failed or cancelled (run it later)`);
2174
+ }
2175
+ }
2084
2176
  }
2177
+ regenerateClaudeMd();
2085
2178
  }
2086
2179
 
2087
2180
  //#endregion
@@ -2090,23 +2183,24 @@ async function removeCommand(skillIds) {
2090
2183
  showMini();
2091
2184
  console.log("");
2092
2185
  if (skillIds.length === 0) {
2093
- p$8.log.error("Specify skills to remove: openpaw remove notes music");
2186
+ p$9.log.error("Specify skills to remove: openpaw remove notes music");
2094
2187
  return;
2095
2188
  }
2096
2189
  for (const id of skillIds) {
2097
2190
  if (id === "core") {
2098
- p$8.log.warn("Cannot remove c-core (coordinator). Use 'openpaw reset' instead.");
2191
+ p$9.log.warn("Cannot remove c-core (coordinator). Use 'openpaw reset' instead.");
2099
2192
  continue;
2100
2193
  }
2101
2194
  if (!isSkillInstalled(id)) {
2102
- p$8.log.info(`c-${id} is not installed`);
2195
+ p$9.log.info(`c-${id} is not installed`);
2103
2196
  continue;
2104
2197
  }
2105
2198
  const skill = getSkillById(id);
2106
2199
  removeSkill(id);
2107
2200
  if (skill) removePermissions(skill.tools);
2108
- p$8.log.success(`${chalk.bold(`c-${id}`)} removed`);
2201
+ p$9.log.success(`${chalk.bold(`c-${id}`)} removed`);
2109
2202
  }
2203
+ regenerateClaudeMd();
2110
2204
  }
2111
2205
 
2112
2206
  //#endregion
@@ -2116,10 +2210,10 @@ async function statusCommand() {
2116
2210
  console.log("");
2117
2211
  const installed = listInstalledSkills();
2118
2212
  if (installed.length === 0) {
2119
- p$7.log.warn("No OpenPaw skills installed. Run: openpaw setup");
2213
+ p$8.log.warn("No OpenPaw skills installed. Run: openpaw setup");
2120
2214
  return;
2121
2215
  }
2122
- p$7.log.info(`${chalk.bold(installed.length)} skills installed:\n`);
2216
+ p$8.log.info(`${chalk.bold(installed.length)} skills installed:\n`);
2123
2217
  for (const skillId of installed) {
2124
2218
  if (skillId === "core") {
2125
2219
  console.log(` ${chalk.green("●")} ${chalk.bold("c-core")} ${chalk.dim("— coordinator")}`);
@@ -2147,7 +2241,7 @@ async function statusCommand() {
2147
2241
  async function doctorCommand() {
2148
2242
  showMini();
2149
2243
  console.log("");
2150
- p$6.log.info("Running diagnostics...\n");
2244
+ p$7.log.info("Running diagnostics...\n");
2151
2245
  let issues = 0;
2152
2246
  const platform = detectPlatform();
2153
2247
  console.log(` ${chalk.green("✓")} Platform: ${platform.osName} ${platform.osVersion}`);
@@ -2186,8 +2280,8 @@ async function doctorCommand() {
2186
2280
  issues++;
2187
2281
  }
2188
2282
  console.log("");
2189
- if (issues === 0) p$6.log.success("All checks passed!");
2190
- else p$6.log.warn(`${issues} issue${issues > 1 ? "s" : ""} found`);
2283
+ if (issues === 0) p$7.log.success("All checks passed!");
2284
+ else p$7.log.warn(`${issues} issue${issues > 1 ? "s" : ""} found`);
2191
2285
  }
2192
2286
 
2193
2287
  //#endregion
@@ -2197,10 +2291,10 @@ async function updateCommand() {
2197
2291
  console.log("");
2198
2292
  const installed = listInstalledSkills();
2199
2293
  if (installed.length === 0) {
2200
- p$5.log.warn("No skills installed. Run: openpaw setup");
2294
+ p$6.log.warn("No skills installed. Run: openpaw setup");
2201
2295
  return;
2202
2296
  }
2203
- const s = p$5.spinner();
2297
+ const s = p$6.spinner();
2204
2298
  const brewTools = [];
2205
2299
  for (const skillId of installed) {
2206
2300
  const skill = skills.find((sk) => sk.id === skillId);
@@ -2208,7 +2302,7 @@ async function updateCommand() {
2208
2302
  for (const tool of skill.tools) if ((tool.installMethod === "brew" || tool.installMethod === "brew-tap") && isToolInstalled(tool.command)) brewTools.push(tool.name);
2209
2303
  }
2210
2304
  if (brewTools.length === 0) {
2211
- p$5.log.info("No Homebrew-installed tools to update");
2305
+ p$6.log.info("No Homebrew-installed tools to update");
2212
2306
  return;
2213
2307
  }
2214
2308
  s.start(`Updating ${brewTools.length} tools via Homebrew...`);
@@ -2234,15 +2328,15 @@ async function resetCommand() {
2234
2328
  console.log("");
2235
2329
  const installed = listInstalledSkills();
2236
2330
  if (installed.length === 0) {
2237
- p$4.log.info("Nothing to reset — no OpenPaw skills installed.");
2331
+ p$5.log.info("Nothing to reset — no OpenPaw skills installed.");
2238
2332
  return;
2239
2333
  }
2240
- const confirm = await p$4.confirm({ message: `Remove all ${installed.length} OpenPaw skills and permissions?` });
2241
- if (p$4.isCancel(confirm) || !confirm) {
2242
- p$4.cancel("Reset cancelled.");
2334
+ const confirm = await p$5.confirm({ message: `Remove all ${installed.length} OpenPaw skills and permissions?` });
2335
+ if (p$5.isCancel(confirm) || !confirm) {
2336
+ p$5.cancel("Reset cancelled.");
2243
2337
  return;
2244
2338
  }
2245
- const s = p$4.spinner();
2339
+ const s = p$5.spinner();
2246
2340
  s.start("Removing skills and permissions...");
2247
2341
  for (const skillId of installed) {
2248
2342
  const skill = skills.find((sk) => sk.id === skillId);
@@ -2251,9 +2345,9 @@ async function resetCommand() {
2251
2345
  }
2252
2346
  removeSafetyHooks();
2253
2347
  s.stop(`${chalk.green("✓")} Removed ${installed.length} skills, permissions, and hooks`);
2254
- p$4.log.info(chalk.dim("CLI tools were not uninstalled (you may still want them)."));
2255
- p$4.log.info(chalk.dim("To uninstall tools: brew uninstall <tool>"));
2256
- p$4.outro("OpenPaw reset complete.");
2348
+ p$5.log.info(chalk.dim("CLI tools were not uninstalled (you may still want them)."));
2349
+ p$5.log.info(chalk.dim("To uninstall tools: brew uninstall <tool>"));
2350
+ p$5.outro("OpenPaw reset complete.");
2257
2351
  }
2258
2352
 
2259
2353
  //#endregion
@@ -2294,35 +2388,35 @@ async function listCommand() {
2294
2388
  //#region src/commands/soul.ts
2295
2389
  async function soulCommand() {
2296
2390
  showMini();
2297
- p$3.intro(accent(" openpaw soul "));
2391
+ p$4.intro(accent(" openpaw soul "));
2298
2392
  if (soulExists()) {
2299
- p$3.log.info(dim("SOUL.md already exists at ~/.claude/SOUL.md"));
2300
- const overwrite = await p$3.confirm({
2393
+ p$4.log.info(dim("SOUL.md already exists at ~/.claude/SOUL.md"));
2394
+ const overwrite = await p$4.confirm({
2301
2395
  message: "Overwrite existing personality?",
2302
2396
  initialValue: false
2303
2397
  });
2304
- if (p$3.isCancel(overwrite) || !overwrite) {
2305
- p$3.log.info("Keeping existing SOUL.md");
2306
- p$3.outro(accent("Done"));
2398
+ if (p$4.isCancel(overwrite) || !overwrite) {
2399
+ p$4.log.info("Keeping existing SOUL.md");
2400
+ p$4.outro(accent("Done"));
2307
2401
  return;
2308
2402
  }
2309
2403
  }
2310
2404
  const soul = await soulQuestionnaire();
2311
2405
  if (!soul) {
2312
- p$3.cancel("Cancelled.");
2406
+ p$4.cancel("Cancelled.");
2313
2407
  return;
2314
2408
  }
2315
2409
  writeSoul(soul);
2316
2410
  showSoulSummary(soul);
2317
- p$3.log.success("Personality saved to ~/.claude/SOUL.md");
2318
- p$3.outro(accent("Claude will use this personality next session 🐾"));
2411
+ p$4.log.success("Personality saved to ~/.claude/SOUL.md");
2412
+ p$4.outro(accent("Claude will use this personality next session 🐾"));
2319
2413
  }
2320
2414
 
2321
2415
  //#endregion
2322
2416
  //#region src/commands/export.ts
2323
2417
  async function exportCommand() {
2324
2418
  showMini();
2325
- p$2.intro(accent(" openpaw export "));
2419
+ p$3.intro(accent(" openpaw export "));
2326
2420
  const claudeDir = path$1.join(os$1.homedir(), ".claude");
2327
2421
  const bundle = {
2328
2422
  version: "1",
@@ -2334,52 +2428,52 @@ async function exportCommand() {
2334
2428
  };
2335
2429
  const installed = listInstalledSkills();
2336
2430
  bundle.skills = installed;
2337
- p$2.log.info(`${installed.length} skills found`);
2431
+ p$3.log.info(`${installed.length} skills found`);
2338
2432
  const settings = readSettings();
2339
2433
  bundle.permissions = settings.permissions?.allow ?? [];
2340
2434
  const soulPath = path$1.join(claudeDir, "SOUL.md");
2341
2435
  if (fs$1.existsSync(soulPath)) {
2342
2436
  bundle.soul = fs$1.readFileSync(soulPath, "utf-8");
2343
- p$2.log.info("SOUL.md included");
2437
+ p$3.log.info("SOUL.md included");
2344
2438
  }
2345
2439
  const memoryDir = path$1.join(claudeDir, "memory");
2346
2440
  if (fs$1.existsSync(memoryDir)) {
2347
2441
  const files = fs$1.readdirSync(memoryDir).filter((f) => f.endsWith(".md"));
2348
2442
  for (const file of files) bundle.memory[file] = fs$1.readFileSync(path$1.join(memoryDir, file), "utf-8");
2349
- p$2.log.info(`${files.length} memory files included`);
2443
+ p$3.log.info(`${files.length} memory files included`);
2350
2444
  }
2351
2445
  const outputPath = path$1.resolve("openpaw-export.json");
2352
2446
  fs$1.writeFileSync(outputPath, JSON.stringify(bundle, null, 2), "utf-8");
2353
- p$2.log.success(`Exported to ${dim(outputPath)}`);
2354
- p$2.outro(accent("Import on another machine: openpaw import openpaw-export.json"));
2447
+ p$3.log.success(`Exported to ${dim(outputPath)}`);
2448
+ p$3.outro(accent("Import on another machine: openpaw import openpaw-export.json"));
2355
2449
  }
2356
2450
  async function importCommand(file) {
2357
2451
  showMini();
2358
- p$2.intro(accent(" openpaw import "));
2452
+ p$3.intro(accent(" openpaw import "));
2359
2453
  const filePath = path$1.resolve(file);
2360
2454
  if (!fs$1.existsSync(filePath)) {
2361
- p$2.log.error(`File not found: ${filePath}`);
2455
+ p$3.log.error(`File not found: ${filePath}`);
2362
2456
  process.exit(1);
2363
2457
  }
2364
2458
  let bundle;
2365
2459
  try {
2366
2460
  bundle = JSON.parse(fs$1.readFileSync(filePath, "utf-8"));
2367
2461
  } catch {
2368
- p$2.log.error("Invalid export file — must be valid JSON");
2462
+ p$3.log.error("Invalid export file — must be valid JSON");
2369
2463
  process.exit(1);
2370
2464
  }
2371
- p$2.log.info(`Export from ${dim(bundle.exportedAt)}`);
2372
- p$2.log.info(`${bundle.skills.length} skills, ${Object.keys(bundle.memory).length} memory files`);
2373
- const proceed = await p$2.confirm({
2465
+ p$3.log.info(`Export from ${dim(bundle.exportedAt)}`);
2466
+ p$3.log.info(`${bundle.skills.length} skills, ${Object.keys(bundle.memory).length} memory files`);
2467
+ const proceed = await p$3.confirm({
2374
2468
  message: "Import this configuration?",
2375
2469
  initialValue: true
2376
2470
  });
2377
- if (p$2.isCancel(proceed) || !proceed) {
2378
- p$2.cancel("Import cancelled.");
2471
+ if (p$3.isCancel(proceed) || !proceed) {
2472
+ p$3.cancel("Import cancelled.");
2379
2473
  return;
2380
2474
  }
2381
2475
  const claudeDir = path$1.join(os$1.homedir(), ".claude");
2382
- const s = p$2.spinner();
2476
+ const s = p$3.spinner();
2383
2477
  if (bundle.soul) {
2384
2478
  s.start("🐾 Restoring SOUL.md...");
2385
2479
  fs$1.mkdirSync(claudeDir, { recursive: true });
@@ -2408,7 +2502,7 @@ async function importCommand(file) {
2408
2502
  s.start("🐾 Restoring permissions...");
2409
2503
  const settings = readSettings();
2410
2504
  const existing = new Set(settings.permissions?.allow ?? []);
2411
- const newPerms = bundle.permissions.filter((p$12) => !existing.has(p$12));
2505
+ const newPerms = bundle.permissions.filter((p$13) => !existing.has(p$13));
2412
2506
  if (newPerms.length > 0) {
2413
2507
  if (!settings.permissions) settings.permissions = {};
2414
2508
  settings.permissions.allow = [...existing, ...newPerms];
@@ -2417,8 +2511,8 @@ async function importCommand(file) {
2417
2511
  }
2418
2512
  s.stop(`🐾 ${newPerms.length} permissions added`);
2419
2513
  }
2420
- p$2.log.success("Import complete");
2421
- p$2.outro(accent("Run openpaw status to verify 🐾"));
2514
+ p$3.log.success("Import complete");
2515
+ p$3.outro(accent("Run openpaw status to verify 🐾"));
2422
2516
  }
2423
2517
 
2424
2518
  //#endregion
@@ -2427,34 +2521,34 @@ async function telegramCommand() {
2427
2521
  showMini();
2428
2522
  const config = readTelegramConfig();
2429
2523
  if (!config) {
2430
- p$1.log.error("Telegram not configured yet.");
2431
- p$1.log.info(`Run ${bold("openpaw telegram setup")} or ${bold("openpaw setup")} first.`);
2524
+ p$2.log.error("Telegram not configured yet.");
2525
+ p$2.log.info(`Run ${bold("openpaw telegram setup")} or ${bold("openpaw setup")} first.`);
2432
2526
  process.exit(1);
2433
2527
  }
2434
2528
  await startTelegramBot(config);
2435
2529
  }
2436
2530
  async function telegramSetupCommand() {
2437
2531
  showMini();
2438
- p$1.intro(accent(" Telegram Bridge Setup "));
2532
+ p$2.intro(accent(" Telegram Bridge Setup "));
2439
2533
  if (telegramConfigExists()) {
2440
- const overwrite = await p$1.confirm({
2534
+ const overwrite = await p$2.confirm({
2441
2535
  message: "Telegram is already configured. Reconfigure?",
2442
2536
  initialValue: false
2443
2537
  });
2444
- if (p$1.isCancel(overwrite) || !overwrite) {
2445
- p$1.outro("Keeping existing config. 🐾");
2538
+ if (p$2.isCancel(overwrite) || !overwrite) {
2539
+ p$2.outro("Keeping existing config. 🐾");
2446
2540
  return;
2447
2541
  }
2448
2542
  }
2449
2543
  const config = await telegramQuestionnaire();
2450
2544
  if (!config) {
2451
- p$1.cancel("Setup cancelled.");
2545
+ p$2.cancel("Setup cancelled.");
2452
2546
  process.exit(0);
2453
2547
  }
2454
2548
  writeTelegramConfig(config);
2455
- p$1.log.success("Telegram config saved!");
2456
- p$1.log.info(`Start the bridge with: ${bold("openpaw telegram")}`);
2457
- p$1.outro(accent("Telegram setup complete 🐾"));
2549
+ p$2.log.success("Telegram config saved!");
2550
+ p$2.log.info(`Start the bridge with: ${bold("openpaw telegram")}`);
2551
+ p$2.outro(accent("Telegram setup complete 🐾"));
2458
2552
  }
2459
2553
 
2460
2554
  //#endregion
@@ -2468,6 +2562,72 @@ function dashboardCommand(opts) {
2468
2562
  });
2469
2563
  }
2470
2564
 
2565
+ //#endregion
2566
+ //#region src/commands/configure.ts
2567
+ async function configureCommand() {
2568
+ showMini();
2569
+ console.log("");
2570
+ const action = await p$1.select({
2571
+ message: "What would you like to configure?",
2572
+ options: [
2573
+ {
2574
+ value: "add",
2575
+ label: "Add more skills",
2576
+ hint: "install new capabilities"
2577
+ },
2578
+ {
2579
+ value: "remove",
2580
+ label: "Remove skills",
2581
+ hint: "uninstall capabilities"
2582
+ },
2583
+ {
2584
+ value: "soul",
2585
+ label: "Edit personality",
2586
+ hint: "name, tone, verbosity"
2587
+ },
2588
+ {
2589
+ value: "dashboard",
2590
+ label: "Open dashboard",
2591
+ hint: "task manager in browser"
2592
+ },
2593
+ {
2594
+ value: "telegram",
2595
+ label: "Telegram setup",
2596
+ hint: "configure bot bridge"
2597
+ },
2598
+ {
2599
+ value: "schedule",
2600
+ label: "Manage schedules",
2601
+ hint: "recurring tasks + cost control"
2602
+ },
2603
+ {
2604
+ value: "status",
2605
+ label: "View status",
2606
+ hint: "see what's installed"
2607
+ },
2608
+ {
2609
+ value: "doctor",
2610
+ label: "Run diagnostics",
2611
+ hint: "check for issues"
2612
+ }
2613
+ ]
2614
+ });
2615
+ if (p$1.isCancel(action)) {
2616
+ p$1.outro(dim("Come back anytime!"));
2617
+ return;
2618
+ }
2619
+ const cmd = `openpaw ${action}`;
2620
+ console.log("");
2621
+ p$1.log.info(`Running ${accent(cmd)}...`);
2622
+ console.log("");
2623
+ try {
2624
+ execSync(`node ${process.argv[1]} ${action}`, {
2625
+ stdio: "inherit",
2626
+ cwd: process.cwd()
2627
+ });
2628
+ } catch {}
2629
+ }
2630
+
2471
2631
  //#endregion
2472
2632
  //#region src/commands/schedule.ts
2473
2633
  async function scheduleAddCommand(schedule, opts) {
@@ -2707,7 +2867,7 @@ async function scheduleSetCapCommand(amount) {
2707
2867
  //#endregion
2708
2868
  //#region src/index.ts
2709
2869
  const program = new Command();
2710
- program.name("openpaw").description("Personal Assistant Wizard for Claude Code").version("1.2.0");
2870
+ program.name("openpaw").description("Personal Assistant Wizard for Claude Code").version("1.4.0");
2711
2871
  program.command("setup", { isDefault: true }).description("Interactive setup wizard — pick skills, install tools, configure Claude Code").option("-p, --preset <name>", "Use a preset (everything, essentials, productivity, developer, creative, smart-home)").option("-y, --yes", "Skip confirmations, use defaults").option("--dry-run", "Show what would be installed without making changes").action(setupCommand);
2712
2872
  program.command("add").description("Add skill(s) by name").argument("<skills...>", "Skill IDs to add (e.g. notes music email)").action(addCommand);
2713
2873
  program.command("remove").description("Remove skill(s) by name").argument("<skills...>", "Skill IDs to remove").action(removeCommand);
@@ -2720,6 +2880,7 @@ program.command("soul").description("Set up or edit Claude's personality (SOUL.m
2720
2880
  program.command("export").description("Export skills, memory, and config to a file").action(exportCommand);
2721
2881
  program.command("import").description("Import skills, memory, and config from a file").argument("<file>", "Path to openpaw-export.json").action(importCommand);
2722
2882
  program.command("dashboard").description("Start the task manager dashboard in your browser").option("-p, --port <port>", "Port to run on (default: 3141)").option("-t, --theme <theme>", "Theme: paw, midnight, or neon").action(dashboardCommand);
2883
+ program.command("configure").alias("config").description("Configure your setup — add skills, change personality, manage dashboard").action(configureCommand);
2723
2884
  const tg = program.command("telegram").description("Start the Telegram bridge — talk to Claude from your phone");
2724
2885
  tg.action(telegramCommand);
2725
2886
  tg.command("setup").description("Set up or reconfigure the Telegram bot").action(telegramSetupCommand);