pawmode 1.2.0 → 1.3.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
|
|
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
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
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
|
|
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$
|
|
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$
|
|
1282
|
+
function getSoulPath$1() {
|
|
1283
|
+
return path$4.join(os$5.homedir(), ".claude", "SOUL.md");
|
|
1370
1284
|
}
|
|
1371
1285
|
function soulExists() {
|
|
1372
|
-
return fs$
|
|
1286
|
+
return fs$4.existsSync(getSoulPath$1());
|
|
1373
1287
|
}
|
|
1374
1288
|
async function soulQuestionnaire() {
|
|
1375
|
-
const name = await p$
|
|
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$
|
|
1381
|
-
const botName = await p$
|
|
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$
|
|
1387
|
-
const tone = await p$
|
|
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$
|
|
1408
|
-
const verbosity = await p$
|
|
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$
|
|
1429
|
-
const proactive = await p$
|
|
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$
|
|
1434
|
-
const extrasResult = await p$
|
|
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$
|
|
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$
|
|
1488
|
-
if (!fs$
|
|
1489
|
-
fs$
|
|
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$
|
|
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$
|
|
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$
|
|
1519
|
-
const memoryPath = path$
|
|
1520
|
-
if (!fs$
|
|
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$
|
|
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$
|
|
1533
|
-
if (!fs$
|
|
1446
|
+
const filePath = path$3.join(MEMORY_DIR, file);
|
|
1447
|
+
if (!fs$3.existsSync(filePath)) {
|
|
1534
1448
|
const title = file.replace(".md", "");
|
|
1535
|
-
fs$
|
|
1449
|
+
fs$3.writeFileSync(filePath, `# ${title.charAt(0).toUpperCase() + title.slice(1)}\n`, "utf-8");
|
|
1536
1450
|
}
|
|
1537
1451
|
}
|
|
1538
1452
|
}
|
|
@@ -1571,6 +1485,92 @@ function launchInBackground(cmd) {
|
|
|
1571
1485
|
child.unref();
|
|
1572
1486
|
}
|
|
1573
1487
|
|
|
1488
|
+
//#endregion
|
|
1489
|
+
//#region src/core/claude-md.ts
|
|
1490
|
+
function getClaudeMdPath() {
|
|
1491
|
+
return path$2.join(os$3.homedir(), ".claude", "CLAUDE.md");
|
|
1492
|
+
}
|
|
1493
|
+
function getSoulPath() {
|
|
1494
|
+
return path$2.join(os$3.homedir(), ".claude", "SOUL.md");
|
|
1495
|
+
}
|
|
1496
|
+
function readBotName() {
|
|
1497
|
+
try {
|
|
1498
|
+
const soul = fs$2.readFileSync(getSoulPath(), "utf-8");
|
|
1499
|
+
const match = soul.match(/You are \*\*(.+?)\*\*/);
|
|
1500
|
+
if (match) return match[1];
|
|
1501
|
+
const nameMatch = soul.match(/\*\*Your name\*\*:\s*(.+?)[\s—]/);
|
|
1502
|
+
if (nameMatch) return nameMatch[1].trim();
|
|
1503
|
+
} catch {}
|
|
1504
|
+
return "Paw";
|
|
1505
|
+
}
|
|
1506
|
+
/**
|
|
1507
|
+
* Write ~/.claude/CLAUDE.md with identity, installed skills, and dashboard info.
|
|
1508
|
+
* This is what Claude Code auto-reads at every session start.
|
|
1509
|
+
*/
|
|
1510
|
+
function writeClaudeMd(botName, installedSkills, hasDashboard) {
|
|
1511
|
+
const lines = [
|
|
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
|
+
const dir = path$2.dirname(getClaudeMdPath());
|
|
1554
|
+
if (!fs$2.existsSync(dir)) fs$2.mkdirSync(dir, { recursive: true });
|
|
1555
|
+
fs$2.writeFileSync(getClaudeMdPath(), lines.join("\n"), "utf-8");
|
|
1556
|
+
}
|
|
1557
|
+
/**
|
|
1558
|
+
* Regenerate CLAUDE.md from current state (installed skills, SOUL.md, dashboard config).
|
|
1559
|
+
* Call this after `openpaw add` or `openpaw remove`.
|
|
1560
|
+
*/
|
|
1561
|
+
function regenerateClaudeMd() {
|
|
1562
|
+
const botName = readBotName();
|
|
1563
|
+
const defaultDir = path$2.join(os$3.homedir(), ".claude", "skills");
|
|
1564
|
+
const installedIds = listInstalledSkills(defaultDir);
|
|
1565
|
+
const installedSkills = installedIds.map((id) => skills.find((s) => s.id === id)).filter((s) => !!s);
|
|
1566
|
+
let hasDashboard = false;
|
|
1567
|
+
try {
|
|
1568
|
+
const dashConfig = readConfig();
|
|
1569
|
+
hasDashboard = !!dashConfig;
|
|
1570
|
+
} catch {}
|
|
1571
|
+
writeClaudeMd(botName, installedSkills, hasDashboard);
|
|
1572
|
+
}
|
|
1573
|
+
|
|
1574
1574
|
//#endregion
|
|
1575
1575
|
//#region src/commands/setup.ts
|
|
1576
1576
|
const CATEGORY_ICONS = {
|
|
@@ -1586,34 +1586,34 @@ const CATEGORY_ICONS = {
|
|
|
1586
1586
|
async function setupCommand(opts = {}) {
|
|
1587
1587
|
await showBanner();
|
|
1588
1588
|
const platform = detectPlatform();
|
|
1589
|
-
p$
|
|
1589
|
+
p$11.intro(accent(" openpaw setup "));
|
|
1590
1590
|
const brewStatus = platform.hasBrew ? chalk.green("✓ brew") : chalk.red("✗ brew");
|
|
1591
1591
|
const npmStatus = platform.hasNpm ? chalk.green("✓ npm") : chalk.red("✗ npm");
|
|
1592
1592
|
const pipStatus = platform.hasPip ? chalk.green("✓ pip") : chalk.dim("○ pip");
|
|
1593
|
-
p$
|
|
1593
|
+
p$11.log.info(`${chalk.bold(platform.osName)} ${platform.osVersion} ${brewStatus} ${npmStatus} ${pipStatus}`);
|
|
1594
1594
|
const missingPrereqs = [];
|
|
1595
1595
|
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
1596
|
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
1597
|
if (missingPrereqs.length > 0) {
|
|
1598
|
-
p$
|
|
1598
|
+
p$11.note(missingPrereqs.join("\n\n"), "Missing prerequisites");
|
|
1599
1599
|
if (!opts.yes) {
|
|
1600
|
-
const cont = await p$
|
|
1600
|
+
const cont = await p$11.confirm({
|
|
1601
1601
|
message: "Continue anyway? (some tool installs may fail)",
|
|
1602
1602
|
initialValue: true
|
|
1603
1603
|
});
|
|
1604
|
-
if (p$
|
|
1605
|
-
p$
|
|
1604
|
+
if (p$11.isCancel(cont) || !cont) {
|
|
1605
|
+
p$11.outro(dim("Install the prerequisites above and run openpaw again!"));
|
|
1606
1606
|
process.exit(0);
|
|
1607
1607
|
}
|
|
1608
1608
|
}
|
|
1609
1609
|
}
|
|
1610
1610
|
let botName = "Paw";
|
|
1611
1611
|
if (!opts.yes) if (soulExists()) {
|
|
1612
|
-
const updateSoul = await p$
|
|
1612
|
+
const updateSoul = await p$11.confirm({
|
|
1613
1613
|
message: "Existing personality found (~/.claude/SOUL.md). Update it?",
|
|
1614
1614
|
initialValue: false
|
|
1615
1615
|
});
|
|
1616
|
-
if (!p$
|
|
1616
|
+
if (!p$11.isCancel(updateSoul) && updateSoul) {
|
|
1617
1617
|
await pawPulse("think", "Let's get to know you again...");
|
|
1618
1618
|
const soul = await soulQuestionnaire();
|
|
1619
1619
|
if (soul) {
|
|
@@ -1621,23 +1621,23 @@ async function setupCommand(opts = {}) {
|
|
|
1621
1621
|
writeSoul(soul);
|
|
1622
1622
|
setupMemory(soul.name);
|
|
1623
1623
|
showSoulSummary(soul);
|
|
1624
|
-
p$
|
|
1624
|
+
p$11.log.success("Personality updated");
|
|
1625
1625
|
}
|
|
1626
1626
|
} else setupMemory();
|
|
1627
1627
|
} else {
|
|
1628
1628
|
await pawPulse("think", "Let's get to know you...");
|
|
1629
|
-
const wantSoul = await p$
|
|
1629
|
+
const wantSoul = await p$11.confirm({
|
|
1630
1630
|
message: "Teach me your name and preferences? (makes me a better pup)",
|
|
1631
1631
|
initialValue: true
|
|
1632
1632
|
});
|
|
1633
|
-
if (!p$
|
|
1633
|
+
if (!p$11.isCancel(wantSoul) && wantSoul) {
|
|
1634
1634
|
const soul = await soulQuestionnaire();
|
|
1635
1635
|
if (soul) {
|
|
1636
1636
|
botName = soul.botName;
|
|
1637
1637
|
writeSoul(soul);
|
|
1638
1638
|
setupMemory(soul.name);
|
|
1639
1639
|
showSoulSummary(soul);
|
|
1640
|
-
p$
|
|
1640
|
+
p$11.log.success("Personality saved to ~/.claude/SOUL.md");
|
|
1641
1641
|
}
|
|
1642
1642
|
} else setupMemory();
|
|
1643
1643
|
}
|
|
@@ -1646,35 +1646,35 @@ async function setupCommand(opts = {}) {
|
|
|
1646
1646
|
if (opts.preset) {
|
|
1647
1647
|
selectedSkills = getPresetSkills(opts.preset, platform.os);
|
|
1648
1648
|
if (selectedSkills.length === 0) {
|
|
1649
|
-
p$
|
|
1650
|
-
p$
|
|
1649
|
+
p$11.log.error(`Unknown preset: ${opts.preset}`);
|
|
1650
|
+
p$11.log.info(`Available: ${presets.map((pr) => pr.id).join(", ")}`);
|
|
1651
1651
|
process.exit(1);
|
|
1652
1652
|
}
|
|
1653
|
-
p$
|
|
1653
|
+
p$11.log.info(`Using preset: ${bold(opts.preset)} (${selectedSkills.length} skills)`);
|
|
1654
1654
|
} else selectedSkills = await selectSkills(platform.os);
|
|
1655
1655
|
if (selectedSkills.length === 0) {
|
|
1656
|
-
p$
|
|
1657
|
-
p$
|
|
1656
|
+
p$11.log.warn("No skills selected. Run openpaw again when you're ready!");
|
|
1657
|
+
p$11.outro("I'll be here napping... come back soon! 🐾");
|
|
1658
1658
|
return;
|
|
1659
1659
|
}
|
|
1660
1660
|
const resolved = resolveDependencies(selectedSkills);
|
|
1661
1661
|
if (resolved.length > 0) {
|
|
1662
1662
|
const depNames = resolved.map((s$1) => s$1.name).join(", ");
|
|
1663
|
-
p$
|
|
1663
|
+
p$11.log.info(`${dim("Auto-fetching dependencies:")} ${depNames}`);
|
|
1664
1664
|
selectedSkills.push(...resolved);
|
|
1665
1665
|
}
|
|
1666
1666
|
await pawPulse("happy", `${selectedSkills.length} skill${selectedSkills.length > 1 ? "s" : ""} selected — good taste!`);
|
|
1667
1667
|
if (!opts.yes) {
|
|
1668
1668
|
for (const skill of selectedSkills) if (skill.subChoices) {
|
|
1669
|
-
const choice = await p$
|
|
1669
|
+
const choice = await p$11.select({
|
|
1670
1670
|
message: `${skill.name}: ${skill.subChoices.question}`,
|
|
1671
1671
|
options: skill.subChoices.options.map((o) => ({
|
|
1672
1672
|
value: o.value,
|
|
1673
1673
|
label: o.label
|
|
1674
1674
|
}))
|
|
1675
1675
|
});
|
|
1676
|
-
if (p$
|
|
1677
|
-
p$
|
|
1676
|
+
if (p$11.isCancel(choice)) {
|
|
1677
|
+
p$11.cancel("Ok, I'll be here when you're ready *sad puppy eyes*");
|
|
1678
1678
|
process.exit(0);
|
|
1679
1679
|
}
|
|
1680
1680
|
const chosen = skill.subChoices.options.find((o) => o.value === choice);
|
|
@@ -1684,7 +1684,7 @@ async function setupCommand(opts = {}) {
|
|
|
1684
1684
|
let interfaceMode = "native";
|
|
1685
1685
|
let telegramConfig = null;
|
|
1686
1686
|
if (!opts.yes) {
|
|
1687
|
-
const modeChoice = await p$
|
|
1687
|
+
const modeChoice = await p$11.select({
|
|
1688
1688
|
message: "How do you want to talk to Claude? 🐾",
|
|
1689
1689
|
options: [{
|
|
1690
1690
|
value: "native",
|
|
@@ -1696,17 +1696,17 @@ async function setupCommand(opts = {}) {
|
|
|
1696
1696
|
hint: "terminal + talk from your phone"
|
|
1697
1697
|
}]
|
|
1698
1698
|
});
|
|
1699
|
-
if (p$
|
|
1700
|
-
p$
|
|
1699
|
+
if (p$11.isCancel(modeChoice)) {
|
|
1700
|
+
p$11.cancel("Ok, I'll be here when you're ready *sad puppy eyes*");
|
|
1701
1701
|
process.exit(0);
|
|
1702
1702
|
}
|
|
1703
1703
|
interfaceMode = modeChoice;
|
|
1704
1704
|
if (interfaceMode === "telegram" || interfaceMode === "both") {
|
|
1705
|
-
if (telegramConfigExists()) p$
|
|
1705
|
+
if (telegramConfigExists()) p$11.log.info(dim("Telegram already configured — keeping existing config"));
|
|
1706
1706
|
else {
|
|
1707
1707
|
telegramConfig = await telegramQuestionnaire();
|
|
1708
1708
|
if (!telegramConfig) {
|
|
1709
|
-
p$
|
|
1709
|
+
p$11.cancel("Ok, I'll be here when you're ready *sad puppy eyes*");
|
|
1710
1710
|
process.exit(0);
|
|
1711
1711
|
}
|
|
1712
1712
|
}
|
|
@@ -1719,13 +1719,13 @@ async function setupCommand(opts = {}) {
|
|
|
1719
1719
|
let wantDashboard = false;
|
|
1720
1720
|
let dashboardTheme = "paw";
|
|
1721
1721
|
if (!opts.yes) {
|
|
1722
|
-
const dashChoice = await p$
|
|
1722
|
+
const dashChoice = await p$11.confirm({
|
|
1723
1723
|
message: `Want a task dashboard for ${botName}?`,
|
|
1724
1724
|
initialValue: false
|
|
1725
1725
|
});
|
|
1726
|
-
if (!p$
|
|
1726
|
+
if (!p$11.isCancel(dashChoice) && dashChoice) {
|
|
1727
1727
|
wantDashboard = true;
|
|
1728
|
-
const themeChoice = await p$
|
|
1728
|
+
const themeChoice = await p$11.select({
|
|
1729
1729
|
message: "Pick a dashboard theme",
|
|
1730
1730
|
options: [
|
|
1731
1731
|
{
|
|
@@ -1745,7 +1745,7 @@ async function setupCommand(opts = {}) {
|
|
|
1745
1745
|
}
|
|
1746
1746
|
]
|
|
1747
1747
|
});
|
|
1748
|
-
if (!p$
|
|
1748
|
+
if (!p$11.isCancel(themeChoice)) dashboardTheme = themeChoice;
|
|
1749
1749
|
}
|
|
1750
1750
|
}
|
|
1751
1751
|
const projectDir = os$2.homedir();
|
|
@@ -1758,7 +1758,7 @@ async function setupCommand(opts = {}) {
|
|
|
1758
1758
|
if (opts.yes) targetDir = getDefaultSkillsDir();
|
|
1759
1759
|
else {
|
|
1760
1760
|
const defaultDir = getDefaultSkillsDir();
|
|
1761
|
-
const skillsDir = await p$
|
|
1761
|
+
const skillsDir = await p$11.select({
|
|
1762
1762
|
message: "Where should skills live?",
|
|
1763
1763
|
options: [
|
|
1764
1764
|
{
|
|
@@ -1776,43 +1776,43 @@ async function setupCommand(opts = {}) {
|
|
|
1776
1776
|
}
|
|
1777
1777
|
]
|
|
1778
1778
|
});
|
|
1779
|
-
if (p$
|
|
1780
|
-
p$
|
|
1779
|
+
if (p$11.isCancel(skillsDir)) {
|
|
1780
|
+
p$11.cancel("Ok, I'll be here when you're ready *sad puppy eyes*");
|
|
1781
1781
|
process.exit(0);
|
|
1782
1782
|
}
|
|
1783
1783
|
targetDir = skillsDir;
|
|
1784
1784
|
if (targetDir === "custom") {
|
|
1785
|
-
const customDir = await p$
|
|
1785
|
+
const customDir = await p$11.text({
|
|
1786
1786
|
message: "Skills directory path:",
|
|
1787
1787
|
placeholder: "~/.claude/skills",
|
|
1788
1788
|
validate: (v) => v.length === 0 ? "Path cannot be empty" : void 0
|
|
1789
1789
|
});
|
|
1790
|
-
if (p$
|
|
1791
|
-
p$
|
|
1790
|
+
if (p$11.isCancel(customDir)) {
|
|
1791
|
+
p$11.cancel("Ok, I'll be here when you're ready *sad puppy eyes*");
|
|
1792
1792
|
process.exit(0);
|
|
1793
1793
|
}
|
|
1794
1794
|
targetDir = customDir.replace(/^~/, os$2.homedir());
|
|
1795
1795
|
}
|
|
1796
1796
|
}
|
|
1797
1797
|
const summary = buildSummary(selectedSkills, uniqueTools, missing, taps, interfaceMode, projectDir);
|
|
1798
|
-
p$
|
|
1798
|
+
p$11.note(summary, "Here's what we're fetching");
|
|
1799
1799
|
if (!opts.yes) {
|
|
1800
|
-
const proceed = await p$
|
|
1800
|
+
const proceed = await p$11.confirm({
|
|
1801
1801
|
message: "Ready to fetch all these goodies?",
|
|
1802
1802
|
initialValue: true
|
|
1803
1803
|
});
|
|
1804
|
-
if (p$
|
|
1805
|
-
p$
|
|
1804
|
+
if (p$11.isCancel(proceed) || !proceed) {
|
|
1805
|
+
p$11.cancel("Ok, I'll be here when you're ready *sad puppy eyes*");
|
|
1806
1806
|
process.exit(0);
|
|
1807
1807
|
}
|
|
1808
1808
|
}
|
|
1809
1809
|
if (opts.dryRun) {
|
|
1810
|
-
p$
|
|
1811
|
-
p$
|
|
1810
|
+
p$11.log.info(dim("Dry run — no changes made. Just sniffing around."));
|
|
1811
|
+
p$11.outro(accent("openpaw dry run complete 🐾"));
|
|
1812
1812
|
return;
|
|
1813
1813
|
}
|
|
1814
1814
|
await pawStep("work", "Fetching your goodies...");
|
|
1815
|
-
const s = p$
|
|
1815
|
+
const s = p$11.spinner();
|
|
1816
1816
|
if (taps.size > 0) {
|
|
1817
1817
|
s.start("🐾 Sniffing out Homebrew taps...");
|
|
1818
1818
|
const tapResults = installTaps(taps);
|
|
@@ -1820,36 +1820,46 @@ async function setupCommand(opts = {}) {
|
|
|
1820
1820
|
if (failed.length > 0) s.stop(`Taps: ${taps.size - failed.length} added, ${failed.length} failed`);
|
|
1821
1821
|
else s.stop(`🐾 ${taps.size} tap${taps.size > 1 ? "s" : ""} ready`);
|
|
1822
1822
|
}
|
|
1823
|
+
const failedTools = [];
|
|
1824
|
+
const installedTools = [];
|
|
1823
1825
|
if (missing.length > 0) for (let i = 0; i < missing.length; i++) {
|
|
1824
1826
|
const tool = missing[i];
|
|
1825
|
-
s.start(`🐾 [${i + 1}/${missing.length}] Teaching Claude a new trick: ${tool.name}...`);
|
|
1827
|
+
s.start(`🐾 [${i + 1}/${missing.length}] Teaching Claude a new trick: ${bold(tool.name)}...`);
|
|
1826
1828
|
const result = installTool(tool);
|
|
1827
|
-
if (result.success)
|
|
1828
|
-
|
|
1829
|
+
if (result.success) {
|
|
1830
|
+
s.stop(`${chalk.green("✓")} ${tool.name}`);
|
|
1831
|
+
installedTools.push(tool.name);
|
|
1832
|
+
} else {
|
|
1833
|
+
s.stop(`${chalk.red("✗")} ${tool.name}`);
|
|
1834
|
+
failedTools.push(tool.name);
|
|
1835
|
+
}
|
|
1829
1836
|
}
|
|
1830
|
-
else if (uniqueTools.length > 0) p$
|
|
1837
|
+
else if (uniqueTools.length > 0) p$11.log.success("All tools already installed — clever pup!");
|
|
1831
1838
|
const existingSkills = listInstalledSkills(targetDir);
|
|
1832
1839
|
const overlapping = selectedSkills.filter((sk) => existingSkills.includes(sk.id));
|
|
1833
1840
|
let updateExisting = true;
|
|
1834
1841
|
if (overlapping.length > 0 && !opts.yes) {
|
|
1835
|
-
const updateChoice = await p$
|
|
1842
|
+
const updateChoice = await p$11.confirm({
|
|
1836
1843
|
message: `${overlapping.length} skill${overlapping.length > 1 ? "s" : ""} already installed. Update their templates?`,
|
|
1837
1844
|
initialValue: true
|
|
1838
1845
|
});
|
|
1839
|
-
if (!p$
|
|
1846
|
+
if (!p$11.isCancel(updateChoice)) updateExisting = updateChoice;
|
|
1840
1847
|
}
|
|
1841
|
-
s.start("🐾 Burying treats in ~/.claude/skills/...");
|
|
1842
1848
|
installSkill("core", targetDir);
|
|
1843
1849
|
installSkill("memory", targetDir);
|
|
1844
1850
|
const installed = ["c-core", "c-memory"];
|
|
1845
1851
|
for (const skill of selectedSkills) {
|
|
1852
|
+
s.start(`🐾 Installing ${bold("c-" + skill.id)}...`);
|
|
1846
1853
|
if (!updateExisting && existingSkills.includes(skill.id)) {
|
|
1847
1854
|
installed.push(`c-${skill.id}`);
|
|
1855
|
+
s.stop(`${chalk.green("✓")} c-${skill.id} ${dim("(kept existing)")}`);
|
|
1848
1856
|
continue;
|
|
1849
1857
|
}
|
|
1850
|
-
if (installSkill(skill.id, targetDir))
|
|
1858
|
+
if (installSkill(skill.id, targetDir)) {
|
|
1859
|
+
installed.push(`c-${skill.id}`);
|
|
1860
|
+
s.stop(`${chalk.green("✓")} c-${skill.id}`);
|
|
1861
|
+
} else s.stop(`${chalk.red("✗")} c-${skill.id}`);
|
|
1851
1862
|
}
|
|
1852
|
-
s.stop(`🐾 ${installed.length} skills buried`);
|
|
1853
1863
|
s.start("🐾 Setting up the doggy door...");
|
|
1854
1864
|
const added = addPermissions(uniqueTools);
|
|
1855
1865
|
s.stop(added.length > 0 ? `🐾 ${added.length} permission${added.length > 1 ? "s" : ""} added` : "🐾 Doggy door already open");
|
|
@@ -1860,20 +1870,55 @@ async function setupCommand(opts = {}) {
|
|
|
1860
1870
|
telegramConfig.workspaceDir = projectDir;
|
|
1861
1871
|
telegramConfig.skills = selectedSkills.map((sk) => sk.id);
|
|
1862
1872
|
writeTelegramConfig(telegramConfig);
|
|
1863
|
-
p$
|
|
1873
|
+
p$11.log.success("Telegram bridge configured");
|
|
1864
1874
|
}
|
|
1865
1875
|
if (wantDashboard) {
|
|
1866
1876
|
const dashConfig = readConfig();
|
|
1867
1877
|
dashConfig.theme = dashboardTheme;
|
|
1868
1878
|
dashConfig.botName = botName;
|
|
1869
1879
|
writeConfig(dashConfig);
|
|
1870
|
-
p$
|
|
1880
|
+
p$11.log.success(`Dashboard configured (theme: ${dashboardTheme})`);
|
|
1871
1881
|
}
|
|
1882
|
+
s.start("🐾 Writing CLAUDE.md...");
|
|
1883
|
+
writeClaudeMd(botName, selectedSkills, wantDashboard);
|
|
1884
|
+
s.stop(`${chalk.green("✓")} CLAUDE.md — ${botName} knows who they are now`);
|
|
1872
1885
|
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) {
|
|
1886
|
+
if (authSteps.length > 0 && !opts.yes) {
|
|
1874
1887
|
const authList = authSteps.map((st) => `${chalk.yellow("→")} ${chalk.bold(st.command)} ${dim(st.description)}`).join("\n");
|
|
1875
|
-
p$
|
|
1876
|
-
|
|
1888
|
+
p$11.note(authList, "One-time auth needed");
|
|
1889
|
+
const runAuth = await p$11.confirm({
|
|
1890
|
+
message: "Want to sign in to these now?",
|
|
1891
|
+
initialValue: true
|
|
1892
|
+
});
|
|
1893
|
+
if (!p$11.isCancel(runAuth) && runAuth) for (const step of authSteps) {
|
|
1894
|
+
const runThis = await p$11.confirm({
|
|
1895
|
+
message: `Run ${bold(step.command)}? ${dim(step.description)}`,
|
|
1896
|
+
initialValue: true
|
|
1897
|
+
});
|
|
1898
|
+
if (p$11.isCancel(runThis)) break;
|
|
1899
|
+
if (!runThis) {
|
|
1900
|
+
p$11.log.info(dim(`Skipped ${step.command} — run it later when you need it`));
|
|
1901
|
+
continue;
|
|
1902
|
+
}
|
|
1903
|
+
p$11.log.info(`Running ${accent(step.command)}...`);
|
|
1904
|
+
try {
|
|
1905
|
+
execSync(step.command, { stdio: "inherit" });
|
|
1906
|
+
p$11.log.success(`${step.command} — signed in`);
|
|
1907
|
+
} catch {
|
|
1908
|
+
p$11.log.warn(`${step.command} — failed or cancelled (you can run it later)`);
|
|
1909
|
+
}
|
|
1910
|
+
}
|
|
1911
|
+
else p$11.log.info(dim("No problem — run these commands when you need each skill"));
|
|
1912
|
+
} else if (authSteps.length > 0) {
|
|
1913
|
+
const authList = authSteps.map((st) => `${chalk.yellow("→")} ${chalk.bold(st.command)} ${dim(st.description)}`).join("\n");
|
|
1914
|
+
p$11.note(authList, "One-time auth needed (run these later)");
|
|
1915
|
+
}
|
|
1916
|
+
const summaryLines = [`${bold("Skills:")} ${installed.length} installed`, `${bold("Tools:")} ${uniqueTools.length - missing.length} ready` + (installedTools.length > 0 ? `, ${installedTools.length} newly installed` : "")];
|
|
1917
|
+
if (failedTools.length > 0) summaryLines.push(`${bold("Failed:")} ${chalk.red(failedTools.join(", "))}`);
|
|
1918
|
+
if (wantDashboard) summaryLines.push(`${bold("Dashboard:")} ${dashboardTheme} theme on :3141`);
|
|
1919
|
+
summaryLines.push(`${bold("CLAUDE.md:")} ${botName} is self-aware`);
|
|
1920
|
+
summaryLines.push(`${bold("Memory:")} ~/.claude/memory/`);
|
|
1921
|
+
p$11.note(summaryLines.join("\n"), "Setup Complete");
|
|
1877
1922
|
await pawStep("done", "All done! *tail wag intensifies*");
|
|
1878
1923
|
console.log("");
|
|
1879
1924
|
console.log(dim(` ${botName} is ready to play! Try saying:`));
|
|
@@ -1882,80 +1927,80 @@ async function setupCommand(opts = {}) {
|
|
|
1882
1927
|
console.log(` ${subtle("\"Go to hacker news and summarize the top posts\"")}`);
|
|
1883
1928
|
console.log("");
|
|
1884
1929
|
if (wantDashboard) {
|
|
1885
|
-
const { startDashboard: startDashboard$1 } = await import("./dashboard-server-
|
|
1930
|
+
const { startDashboard: startDashboard$1 } = await import("./dashboard-server-Pnv4DFlV.js");
|
|
1886
1931
|
startDashboard$1({
|
|
1887
1932
|
theme: dashboardTheme,
|
|
1888
1933
|
botName
|
|
1889
1934
|
});
|
|
1890
|
-
p$
|
|
1935
|
+
p$11.log.success("Dashboard launched in your browser");
|
|
1891
1936
|
}
|
|
1892
1937
|
if (opts.yes) {
|
|
1893
|
-
p$
|
|
1938
|
+
p$11.outro(accent("openpaw setup complete 🐾"));
|
|
1894
1939
|
return;
|
|
1895
1940
|
}
|
|
1896
|
-
const launch = await p$
|
|
1941
|
+
const launch = await p$11.confirm({
|
|
1897
1942
|
message: "Time to go for a walk? (Launch your assistant)",
|
|
1898
1943
|
initialValue: true
|
|
1899
1944
|
});
|
|
1900
|
-
if (p$
|
|
1901
|
-
if (interfaceMode === "telegram" || interfaceMode === "both") p$
|
|
1902
|
-
p$
|
|
1945
|
+
if (p$11.isCancel(launch) || !launch) {
|
|
1946
|
+
if (interfaceMode === "telegram" || interfaceMode === "both") p$11.log.info(`Start the Telegram bridge anytime with: ${bold("openpaw telegram")}`);
|
|
1947
|
+
p$11.outro(accent("openpaw setup complete 🐾 — come back anytime!"));
|
|
1903
1948
|
return;
|
|
1904
1949
|
}
|
|
1905
1950
|
let useDangerousMode = false;
|
|
1906
1951
|
{
|
|
1907
1952
|
showPuppyDisclaimer();
|
|
1908
|
-
const acceptDanger = await p$
|
|
1953
|
+
const acceptDanger = await p$11.confirm({
|
|
1909
1954
|
message: "Unleash full paw-er? *excited tail wag*",
|
|
1910
1955
|
initialValue: true
|
|
1911
1956
|
});
|
|
1912
|
-
if (!p$
|
|
1957
|
+
if (!p$11.isCancel(acceptDanger)) useDangerousMode = acceptDanger;
|
|
1913
1958
|
}
|
|
1914
1959
|
let useTmux = false;
|
|
1915
1960
|
if (isTmuxAvailable() && !isInTmux()) {
|
|
1916
1961
|
const tmuxDefault = interfaceMode === "both";
|
|
1917
|
-
const tmuxChoice = await p$
|
|
1962
|
+
const tmuxChoice = await p$11.confirm({
|
|
1918
1963
|
message: "Run in tmux? (keeps going when you close the terminal)",
|
|
1919
1964
|
initialValue: tmuxDefault
|
|
1920
1965
|
});
|
|
1921
|
-
if (!p$
|
|
1966
|
+
if (!p$11.isCancel(tmuxChoice)) useTmux = tmuxChoice;
|
|
1922
1967
|
}
|
|
1923
1968
|
const dangerFlag = useDangerousMode ? " --dangerously-skip-permissions" : "";
|
|
1924
1969
|
const nativeCmd = `claude${dangerFlag}`;
|
|
1925
1970
|
const telegramCmd = "npx openpaw telegram";
|
|
1926
1971
|
if (useTmux) {
|
|
1927
|
-
p$
|
|
1972
|
+
p$11.outro(accent("Launching in tmux... 🐾"));
|
|
1928
1973
|
launchInTmux({
|
|
1929
1974
|
nativeCmd,
|
|
1930
1975
|
telegramCmd: interfaceMode === "both" ? telegramCmd : void 0,
|
|
1931
1976
|
workDir: projectDir
|
|
1932
1977
|
});
|
|
1933
1978
|
} else if (interfaceMode === "native") {
|
|
1934
|
-
p$
|
|
1979
|
+
p$11.outro(accent("Starting Claude Code... 🐾"));
|
|
1935
1980
|
try {
|
|
1936
1981
|
execSync(nativeCmd, {
|
|
1937
1982
|
stdio: "inherit",
|
|
1938
1983
|
cwd: projectDir
|
|
1939
1984
|
});
|
|
1940
1985
|
} catch {
|
|
1941
|
-
p$
|
|
1986
|
+
p$11.log.warn("Could not launch Claude Code. Make sure it's installed: https://claude.ai/code");
|
|
1942
1987
|
}
|
|
1943
1988
|
} else {
|
|
1944
|
-
p$
|
|
1989
|
+
p$11.log.info(dim("Starting Telegram bridge in background..."));
|
|
1945
1990
|
launchInBackground(telegramCmd);
|
|
1946
|
-
p$
|
|
1991
|
+
p$11.outro(accent("Starting Claude Code... 🐾"));
|
|
1947
1992
|
try {
|
|
1948
1993
|
execSync(nativeCmd, {
|
|
1949
1994
|
stdio: "inherit",
|
|
1950
1995
|
cwd: projectDir
|
|
1951
1996
|
});
|
|
1952
1997
|
} catch {
|
|
1953
|
-
p$
|
|
1998
|
+
p$11.log.warn("Could not launch Claude Code. Make sure it's installed: https://claude.ai/code");
|
|
1954
1999
|
}
|
|
1955
2000
|
}
|
|
1956
2001
|
}
|
|
1957
|
-
async function selectSkills(os$
|
|
1958
|
-
const mode = await p$
|
|
2002
|
+
async function selectSkills(os$6) {
|
|
2003
|
+
const mode = await p$11.select({
|
|
1959
2004
|
message: "How should we set things up, human?",
|
|
1960
2005
|
options: [{
|
|
1961
2006
|
value: "preset",
|
|
@@ -1967,15 +2012,15 @@ async function selectSkills(os$5) {
|
|
|
1967
2012
|
hint: "sniff through skills one by one"
|
|
1968
2013
|
}]
|
|
1969
2014
|
});
|
|
1970
|
-
if (p$
|
|
1971
|
-
p$
|
|
2015
|
+
if (p$11.isCancel(mode)) {
|
|
2016
|
+
p$11.cancel("Ok, I'll be here when you're ready *sad puppy eyes*");
|
|
1972
2017
|
process.exit(0);
|
|
1973
2018
|
}
|
|
1974
|
-
if (mode === "preset") return await selectFromPreset(os$
|
|
1975
|
-
return await selectCustom(os$
|
|
2019
|
+
if (mode === "preset") return await selectFromPreset(os$6);
|
|
2020
|
+
return await selectCustom(os$6);
|
|
1976
2021
|
}
|
|
1977
|
-
async function selectFromPreset(os$
|
|
1978
|
-
const presetChoice = await p$
|
|
2022
|
+
async function selectFromPreset(os$6) {
|
|
2023
|
+
const presetChoice = await p$11.select({
|
|
1979
2024
|
message: "Pick a treat... I mean, a preset!",
|
|
1980
2025
|
options: presets.map((pr) => ({
|
|
1981
2026
|
value: pr.id,
|
|
@@ -1983,17 +2028,17 @@ async function selectFromPreset(os$5) {
|
|
|
1983
2028
|
hint: pr.description
|
|
1984
2029
|
}))
|
|
1985
2030
|
});
|
|
1986
|
-
if (p$
|
|
1987
|
-
p$
|
|
2031
|
+
if (p$11.isCancel(presetChoice)) {
|
|
2032
|
+
p$11.cancel("Ok, I'll be here when you're ready *sad puppy eyes*");
|
|
1988
2033
|
process.exit(0);
|
|
1989
2034
|
}
|
|
1990
|
-
const presetSkills = getPresetSkills(presetChoice, os$
|
|
2035
|
+
const presetSkills = getPresetSkills(presetChoice, os$6);
|
|
1991
2036
|
const skillNames = presetSkills.map((s) => s.name).join(", ");
|
|
1992
|
-
p$
|
|
2037
|
+
p$11.log.info(`${dim("Includes:")} ${skillNames}`);
|
|
1993
2038
|
return presetSkills;
|
|
1994
2039
|
}
|
|
1995
|
-
async function selectCustom(os$
|
|
1996
|
-
const grouped = getSkillsByCategory(os$
|
|
2040
|
+
async function selectCustom(os$6) {
|
|
2041
|
+
const grouped = getSkillsByCategory(os$6);
|
|
1997
2042
|
const options = [];
|
|
1998
2043
|
for (const [category, catSkills] of grouped) {
|
|
1999
2044
|
const icon = CATEGORY_ICONS[category] ?? "📦";
|
|
@@ -2009,13 +2054,13 @@ async function selectCustom(os$5) {
|
|
|
2009
2054
|
isFirst = false;
|
|
2010
2055
|
}
|
|
2011
2056
|
}
|
|
2012
|
-
const selected = await p$
|
|
2057
|
+
const selected = await p$11.multiselect({
|
|
2013
2058
|
message: "Pick your skills (space to select, enter to confirm)",
|
|
2014
2059
|
options,
|
|
2015
2060
|
required: false
|
|
2016
2061
|
});
|
|
2017
|
-
if (p$
|
|
2018
|
-
p$
|
|
2062
|
+
if (p$11.isCancel(selected)) {
|
|
2063
|
+
p$11.cancel("Ok, I'll be here when you're ready *sad puppy eyes*");
|
|
2019
2064
|
process.exit(0);
|
|
2020
2065
|
}
|
|
2021
2066
|
const ids = selected;
|
|
@@ -2055,18 +2100,18 @@ async function addCommand(skillIds) {
|
|
|
2055
2100
|
showMini();
|
|
2056
2101
|
console.log("");
|
|
2057
2102
|
if (skillIds.length === 0) {
|
|
2058
|
-
p$
|
|
2103
|
+
p$10.log.error("Specify skills to add: openpaw add notes music email");
|
|
2059
2104
|
return;
|
|
2060
2105
|
}
|
|
2061
|
-
const s = p$
|
|
2106
|
+
const s = p$10.spinner();
|
|
2062
2107
|
for (const id of skillIds) {
|
|
2063
2108
|
const skill = getSkillById(id);
|
|
2064
2109
|
if (!skill) {
|
|
2065
|
-
p$
|
|
2110
|
+
p$10.log.error(`Unknown skill: ${id}`);
|
|
2066
2111
|
continue;
|
|
2067
2112
|
}
|
|
2068
2113
|
if (isSkillInstalled(id)) {
|
|
2069
|
-
p$
|
|
2114
|
+
p$10.log.info(`c-${id} already installed, skipping`);
|
|
2070
2115
|
continue;
|
|
2071
2116
|
}
|
|
2072
2117
|
const taps = getAllTaps([skill]);
|
|
@@ -2079,9 +2124,27 @@ async function addCommand(skillIds) {
|
|
|
2079
2124
|
}
|
|
2080
2125
|
installSkill(id);
|
|
2081
2126
|
addPermissions(skill.tools);
|
|
2082
|
-
p$
|
|
2083
|
-
if (skill.authSteps?.length) for (const step of skill.authSteps)
|
|
2127
|
+
p$10.log.success(`c-${id} installed`);
|
|
2128
|
+
if (skill.authSteps?.length) for (const step of skill.authSteps) {
|
|
2129
|
+
const runThis = await p$10.confirm({
|
|
2130
|
+
message: `Run ${chalk.bold(step.command)}? ${dim(step.description)}`,
|
|
2131
|
+
initialValue: true
|
|
2132
|
+
});
|
|
2133
|
+
if (p$10.isCancel(runThis)) break;
|
|
2134
|
+
if (!runThis) {
|
|
2135
|
+
p$10.log.info(dim(`Skipped ${step.command} — run it later`));
|
|
2136
|
+
continue;
|
|
2137
|
+
}
|
|
2138
|
+
p$10.log.info(`Running ${accent(step.command)}...`);
|
|
2139
|
+
try {
|
|
2140
|
+
execSync(step.command, { stdio: "inherit" });
|
|
2141
|
+
p$10.log.success(`${step.command} — signed in`);
|
|
2142
|
+
} catch {
|
|
2143
|
+
p$10.log.warn(`${step.command} — failed or cancelled (run it later)`);
|
|
2144
|
+
}
|
|
2145
|
+
}
|
|
2084
2146
|
}
|
|
2147
|
+
regenerateClaudeMd();
|
|
2085
2148
|
}
|
|
2086
2149
|
|
|
2087
2150
|
//#endregion
|
|
@@ -2090,23 +2153,24 @@ async function removeCommand(skillIds) {
|
|
|
2090
2153
|
showMini();
|
|
2091
2154
|
console.log("");
|
|
2092
2155
|
if (skillIds.length === 0) {
|
|
2093
|
-
p$
|
|
2156
|
+
p$9.log.error("Specify skills to remove: openpaw remove notes music");
|
|
2094
2157
|
return;
|
|
2095
2158
|
}
|
|
2096
2159
|
for (const id of skillIds) {
|
|
2097
2160
|
if (id === "core") {
|
|
2098
|
-
p$
|
|
2161
|
+
p$9.log.warn("Cannot remove c-core (coordinator). Use 'openpaw reset' instead.");
|
|
2099
2162
|
continue;
|
|
2100
2163
|
}
|
|
2101
2164
|
if (!isSkillInstalled(id)) {
|
|
2102
|
-
p$
|
|
2165
|
+
p$9.log.info(`c-${id} is not installed`);
|
|
2103
2166
|
continue;
|
|
2104
2167
|
}
|
|
2105
2168
|
const skill = getSkillById(id);
|
|
2106
2169
|
removeSkill(id);
|
|
2107
2170
|
if (skill) removePermissions(skill.tools);
|
|
2108
|
-
p$
|
|
2171
|
+
p$9.log.success(`${chalk.bold(`c-${id}`)} removed`);
|
|
2109
2172
|
}
|
|
2173
|
+
regenerateClaudeMd();
|
|
2110
2174
|
}
|
|
2111
2175
|
|
|
2112
2176
|
//#endregion
|
|
@@ -2116,10 +2180,10 @@ async function statusCommand() {
|
|
|
2116
2180
|
console.log("");
|
|
2117
2181
|
const installed = listInstalledSkills();
|
|
2118
2182
|
if (installed.length === 0) {
|
|
2119
|
-
p$
|
|
2183
|
+
p$8.log.warn("No OpenPaw skills installed. Run: openpaw setup");
|
|
2120
2184
|
return;
|
|
2121
2185
|
}
|
|
2122
|
-
p$
|
|
2186
|
+
p$8.log.info(`${chalk.bold(installed.length)} skills installed:\n`);
|
|
2123
2187
|
for (const skillId of installed) {
|
|
2124
2188
|
if (skillId === "core") {
|
|
2125
2189
|
console.log(` ${chalk.green("●")} ${chalk.bold("c-core")} ${chalk.dim("— coordinator")}`);
|
|
@@ -2147,7 +2211,7 @@ async function statusCommand() {
|
|
|
2147
2211
|
async function doctorCommand() {
|
|
2148
2212
|
showMini();
|
|
2149
2213
|
console.log("");
|
|
2150
|
-
p$
|
|
2214
|
+
p$7.log.info("Running diagnostics...\n");
|
|
2151
2215
|
let issues = 0;
|
|
2152
2216
|
const platform = detectPlatform();
|
|
2153
2217
|
console.log(` ${chalk.green("✓")} Platform: ${platform.osName} ${platform.osVersion}`);
|
|
@@ -2186,8 +2250,8 @@ async function doctorCommand() {
|
|
|
2186
2250
|
issues++;
|
|
2187
2251
|
}
|
|
2188
2252
|
console.log("");
|
|
2189
|
-
if (issues === 0) p$
|
|
2190
|
-
else p$
|
|
2253
|
+
if (issues === 0) p$7.log.success("All checks passed!");
|
|
2254
|
+
else p$7.log.warn(`${issues} issue${issues > 1 ? "s" : ""} found`);
|
|
2191
2255
|
}
|
|
2192
2256
|
|
|
2193
2257
|
//#endregion
|
|
@@ -2197,10 +2261,10 @@ async function updateCommand() {
|
|
|
2197
2261
|
console.log("");
|
|
2198
2262
|
const installed = listInstalledSkills();
|
|
2199
2263
|
if (installed.length === 0) {
|
|
2200
|
-
p$
|
|
2264
|
+
p$6.log.warn("No skills installed. Run: openpaw setup");
|
|
2201
2265
|
return;
|
|
2202
2266
|
}
|
|
2203
|
-
const s = p$
|
|
2267
|
+
const s = p$6.spinner();
|
|
2204
2268
|
const brewTools = [];
|
|
2205
2269
|
for (const skillId of installed) {
|
|
2206
2270
|
const skill = skills.find((sk) => sk.id === skillId);
|
|
@@ -2208,7 +2272,7 @@ async function updateCommand() {
|
|
|
2208
2272
|
for (const tool of skill.tools) if ((tool.installMethod === "brew" || tool.installMethod === "brew-tap") && isToolInstalled(tool.command)) brewTools.push(tool.name);
|
|
2209
2273
|
}
|
|
2210
2274
|
if (brewTools.length === 0) {
|
|
2211
|
-
p$
|
|
2275
|
+
p$6.log.info("No Homebrew-installed tools to update");
|
|
2212
2276
|
return;
|
|
2213
2277
|
}
|
|
2214
2278
|
s.start(`Updating ${brewTools.length} tools via Homebrew...`);
|
|
@@ -2234,15 +2298,15 @@ async function resetCommand() {
|
|
|
2234
2298
|
console.log("");
|
|
2235
2299
|
const installed = listInstalledSkills();
|
|
2236
2300
|
if (installed.length === 0) {
|
|
2237
|
-
p$
|
|
2301
|
+
p$5.log.info("Nothing to reset — no OpenPaw skills installed.");
|
|
2238
2302
|
return;
|
|
2239
2303
|
}
|
|
2240
|
-
const confirm = await p$
|
|
2241
|
-
if (p$
|
|
2242
|
-
p$
|
|
2304
|
+
const confirm = await p$5.confirm({ message: `Remove all ${installed.length} OpenPaw skills and permissions?` });
|
|
2305
|
+
if (p$5.isCancel(confirm) || !confirm) {
|
|
2306
|
+
p$5.cancel("Reset cancelled.");
|
|
2243
2307
|
return;
|
|
2244
2308
|
}
|
|
2245
|
-
const s = p$
|
|
2309
|
+
const s = p$5.spinner();
|
|
2246
2310
|
s.start("Removing skills and permissions...");
|
|
2247
2311
|
for (const skillId of installed) {
|
|
2248
2312
|
const skill = skills.find((sk) => sk.id === skillId);
|
|
@@ -2251,9 +2315,9 @@ async function resetCommand() {
|
|
|
2251
2315
|
}
|
|
2252
2316
|
removeSafetyHooks();
|
|
2253
2317
|
s.stop(`${chalk.green("✓")} Removed ${installed.length} skills, permissions, and hooks`);
|
|
2254
|
-
p$
|
|
2255
|
-
p$
|
|
2256
|
-
p$
|
|
2318
|
+
p$5.log.info(chalk.dim("CLI tools were not uninstalled (you may still want them)."));
|
|
2319
|
+
p$5.log.info(chalk.dim("To uninstall tools: brew uninstall <tool>"));
|
|
2320
|
+
p$5.outro("OpenPaw reset complete.");
|
|
2257
2321
|
}
|
|
2258
2322
|
|
|
2259
2323
|
//#endregion
|
|
@@ -2294,35 +2358,35 @@ async function listCommand() {
|
|
|
2294
2358
|
//#region src/commands/soul.ts
|
|
2295
2359
|
async function soulCommand() {
|
|
2296
2360
|
showMini();
|
|
2297
|
-
p$
|
|
2361
|
+
p$4.intro(accent(" openpaw soul "));
|
|
2298
2362
|
if (soulExists()) {
|
|
2299
|
-
p$
|
|
2300
|
-
const overwrite = await p$
|
|
2363
|
+
p$4.log.info(dim("SOUL.md already exists at ~/.claude/SOUL.md"));
|
|
2364
|
+
const overwrite = await p$4.confirm({
|
|
2301
2365
|
message: "Overwrite existing personality?",
|
|
2302
2366
|
initialValue: false
|
|
2303
2367
|
});
|
|
2304
|
-
if (p$
|
|
2305
|
-
p$
|
|
2306
|
-
p$
|
|
2368
|
+
if (p$4.isCancel(overwrite) || !overwrite) {
|
|
2369
|
+
p$4.log.info("Keeping existing SOUL.md");
|
|
2370
|
+
p$4.outro(accent("Done"));
|
|
2307
2371
|
return;
|
|
2308
2372
|
}
|
|
2309
2373
|
}
|
|
2310
2374
|
const soul = await soulQuestionnaire();
|
|
2311
2375
|
if (!soul) {
|
|
2312
|
-
p$
|
|
2376
|
+
p$4.cancel("Cancelled.");
|
|
2313
2377
|
return;
|
|
2314
2378
|
}
|
|
2315
2379
|
writeSoul(soul);
|
|
2316
2380
|
showSoulSummary(soul);
|
|
2317
|
-
p$
|
|
2318
|
-
p$
|
|
2381
|
+
p$4.log.success("Personality saved to ~/.claude/SOUL.md");
|
|
2382
|
+
p$4.outro(accent("Claude will use this personality next session 🐾"));
|
|
2319
2383
|
}
|
|
2320
2384
|
|
|
2321
2385
|
//#endregion
|
|
2322
2386
|
//#region src/commands/export.ts
|
|
2323
2387
|
async function exportCommand() {
|
|
2324
2388
|
showMini();
|
|
2325
|
-
p$
|
|
2389
|
+
p$3.intro(accent(" openpaw export "));
|
|
2326
2390
|
const claudeDir = path$1.join(os$1.homedir(), ".claude");
|
|
2327
2391
|
const bundle = {
|
|
2328
2392
|
version: "1",
|
|
@@ -2334,52 +2398,52 @@ async function exportCommand() {
|
|
|
2334
2398
|
};
|
|
2335
2399
|
const installed = listInstalledSkills();
|
|
2336
2400
|
bundle.skills = installed;
|
|
2337
|
-
p$
|
|
2401
|
+
p$3.log.info(`${installed.length} skills found`);
|
|
2338
2402
|
const settings = readSettings();
|
|
2339
2403
|
bundle.permissions = settings.permissions?.allow ?? [];
|
|
2340
2404
|
const soulPath = path$1.join(claudeDir, "SOUL.md");
|
|
2341
2405
|
if (fs$1.existsSync(soulPath)) {
|
|
2342
2406
|
bundle.soul = fs$1.readFileSync(soulPath, "utf-8");
|
|
2343
|
-
p$
|
|
2407
|
+
p$3.log.info("SOUL.md included");
|
|
2344
2408
|
}
|
|
2345
2409
|
const memoryDir = path$1.join(claudeDir, "memory");
|
|
2346
2410
|
if (fs$1.existsSync(memoryDir)) {
|
|
2347
2411
|
const files = fs$1.readdirSync(memoryDir).filter((f) => f.endsWith(".md"));
|
|
2348
2412
|
for (const file of files) bundle.memory[file] = fs$1.readFileSync(path$1.join(memoryDir, file), "utf-8");
|
|
2349
|
-
p$
|
|
2413
|
+
p$3.log.info(`${files.length} memory files included`);
|
|
2350
2414
|
}
|
|
2351
2415
|
const outputPath = path$1.resolve("openpaw-export.json");
|
|
2352
2416
|
fs$1.writeFileSync(outputPath, JSON.stringify(bundle, null, 2), "utf-8");
|
|
2353
|
-
p$
|
|
2354
|
-
p$
|
|
2417
|
+
p$3.log.success(`Exported to ${dim(outputPath)}`);
|
|
2418
|
+
p$3.outro(accent("Import on another machine: openpaw import openpaw-export.json"));
|
|
2355
2419
|
}
|
|
2356
2420
|
async function importCommand(file) {
|
|
2357
2421
|
showMini();
|
|
2358
|
-
p$
|
|
2422
|
+
p$3.intro(accent(" openpaw import "));
|
|
2359
2423
|
const filePath = path$1.resolve(file);
|
|
2360
2424
|
if (!fs$1.existsSync(filePath)) {
|
|
2361
|
-
p$
|
|
2425
|
+
p$3.log.error(`File not found: ${filePath}`);
|
|
2362
2426
|
process.exit(1);
|
|
2363
2427
|
}
|
|
2364
2428
|
let bundle;
|
|
2365
2429
|
try {
|
|
2366
2430
|
bundle = JSON.parse(fs$1.readFileSync(filePath, "utf-8"));
|
|
2367
2431
|
} catch {
|
|
2368
|
-
p$
|
|
2432
|
+
p$3.log.error("Invalid export file — must be valid JSON");
|
|
2369
2433
|
process.exit(1);
|
|
2370
2434
|
}
|
|
2371
|
-
p$
|
|
2372
|
-
p$
|
|
2373
|
-
const proceed = await p$
|
|
2435
|
+
p$3.log.info(`Export from ${dim(bundle.exportedAt)}`);
|
|
2436
|
+
p$3.log.info(`${bundle.skills.length} skills, ${Object.keys(bundle.memory).length} memory files`);
|
|
2437
|
+
const proceed = await p$3.confirm({
|
|
2374
2438
|
message: "Import this configuration?",
|
|
2375
2439
|
initialValue: true
|
|
2376
2440
|
});
|
|
2377
|
-
if (p$
|
|
2378
|
-
p$
|
|
2441
|
+
if (p$3.isCancel(proceed) || !proceed) {
|
|
2442
|
+
p$3.cancel("Import cancelled.");
|
|
2379
2443
|
return;
|
|
2380
2444
|
}
|
|
2381
2445
|
const claudeDir = path$1.join(os$1.homedir(), ".claude");
|
|
2382
|
-
const s = p$
|
|
2446
|
+
const s = p$3.spinner();
|
|
2383
2447
|
if (bundle.soul) {
|
|
2384
2448
|
s.start("🐾 Restoring SOUL.md...");
|
|
2385
2449
|
fs$1.mkdirSync(claudeDir, { recursive: true });
|
|
@@ -2408,7 +2472,7 @@ async function importCommand(file) {
|
|
|
2408
2472
|
s.start("🐾 Restoring permissions...");
|
|
2409
2473
|
const settings = readSettings();
|
|
2410
2474
|
const existing = new Set(settings.permissions?.allow ?? []);
|
|
2411
|
-
const newPerms = bundle.permissions.filter((p$
|
|
2475
|
+
const newPerms = bundle.permissions.filter((p$13) => !existing.has(p$13));
|
|
2412
2476
|
if (newPerms.length > 0) {
|
|
2413
2477
|
if (!settings.permissions) settings.permissions = {};
|
|
2414
2478
|
settings.permissions.allow = [...existing, ...newPerms];
|
|
@@ -2417,8 +2481,8 @@ async function importCommand(file) {
|
|
|
2417
2481
|
}
|
|
2418
2482
|
s.stop(`🐾 ${newPerms.length} permissions added`);
|
|
2419
2483
|
}
|
|
2420
|
-
p$
|
|
2421
|
-
p$
|
|
2484
|
+
p$3.log.success("Import complete");
|
|
2485
|
+
p$3.outro(accent("Run openpaw status to verify 🐾"));
|
|
2422
2486
|
}
|
|
2423
2487
|
|
|
2424
2488
|
//#endregion
|
|
@@ -2427,34 +2491,34 @@ async function telegramCommand() {
|
|
|
2427
2491
|
showMini();
|
|
2428
2492
|
const config = readTelegramConfig();
|
|
2429
2493
|
if (!config) {
|
|
2430
|
-
p$
|
|
2431
|
-
p$
|
|
2494
|
+
p$2.log.error("Telegram not configured yet.");
|
|
2495
|
+
p$2.log.info(`Run ${bold("openpaw telegram setup")} or ${bold("openpaw setup")} first.`);
|
|
2432
2496
|
process.exit(1);
|
|
2433
2497
|
}
|
|
2434
2498
|
await startTelegramBot(config);
|
|
2435
2499
|
}
|
|
2436
2500
|
async function telegramSetupCommand() {
|
|
2437
2501
|
showMini();
|
|
2438
|
-
p$
|
|
2502
|
+
p$2.intro(accent(" Telegram Bridge Setup "));
|
|
2439
2503
|
if (telegramConfigExists()) {
|
|
2440
|
-
const overwrite = await p$
|
|
2504
|
+
const overwrite = await p$2.confirm({
|
|
2441
2505
|
message: "Telegram is already configured. Reconfigure?",
|
|
2442
2506
|
initialValue: false
|
|
2443
2507
|
});
|
|
2444
|
-
if (p$
|
|
2445
|
-
p$
|
|
2508
|
+
if (p$2.isCancel(overwrite) || !overwrite) {
|
|
2509
|
+
p$2.outro("Keeping existing config. 🐾");
|
|
2446
2510
|
return;
|
|
2447
2511
|
}
|
|
2448
2512
|
}
|
|
2449
2513
|
const config = await telegramQuestionnaire();
|
|
2450
2514
|
if (!config) {
|
|
2451
|
-
p$
|
|
2515
|
+
p$2.cancel("Setup cancelled.");
|
|
2452
2516
|
process.exit(0);
|
|
2453
2517
|
}
|
|
2454
2518
|
writeTelegramConfig(config);
|
|
2455
|
-
p$
|
|
2456
|
-
p$
|
|
2457
|
-
p$
|
|
2519
|
+
p$2.log.success("Telegram config saved!");
|
|
2520
|
+
p$2.log.info(`Start the bridge with: ${bold("openpaw telegram")}`);
|
|
2521
|
+
p$2.outro(accent("Telegram setup complete 🐾"));
|
|
2458
2522
|
}
|
|
2459
2523
|
|
|
2460
2524
|
//#endregion
|
|
@@ -2468,6 +2532,72 @@ function dashboardCommand(opts) {
|
|
|
2468
2532
|
});
|
|
2469
2533
|
}
|
|
2470
2534
|
|
|
2535
|
+
//#endregion
|
|
2536
|
+
//#region src/commands/configure.ts
|
|
2537
|
+
async function configureCommand() {
|
|
2538
|
+
showMini();
|
|
2539
|
+
console.log("");
|
|
2540
|
+
const action = await p$1.select({
|
|
2541
|
+
message: "What would you like to configure?",
|
|
2542
|
+
options: [
|
|
2543
|
+
{
|
|
2544
|
+
value: "add",
|
|
2545
|
+
label: "Add more skills",
|
|
2546
|
+
hint: "install new capabilities"
|
|
2547
|
+
},
|
|
2548
|
+
{
|
|
2549
|
+
value: "remove",
|
|
2550
|
+
label: "Remove skills",
|
|
2551
|
+
hint: "uninstall capabilities"
|
|
2552
|
+
},
|
|
2553
|
+
{
|
|
2554
|
+
value: "soul",
|
|
2555
|
+
label: "Edit personality",
|
|
2556
|
+
hint: "name, tone, verbosity"
|
|
2557
|
+
},
|
|
2558
|
+
{
|
|
2559
|
+
value: "dashboard",
|
|
2560
|
+
label: "Open dashboard",
|
|
2561
|
+
hint: "task manager in browser"
|
|
2562
|
+
},
|
|
2563
|
+
{
|
|
2564
|
+
value: "telegram",
|
|
2565
|
+
label: "Telegram setup",
|
|
2566
|
+
hint: "configure bot bridge"
|
|
2567
|
+
},
|
|
2568
|
+
{
|
|
2569
|
+
value: "schedule",
|
|
2570
|
+
label: "Manage schedules",
|
|
2571
|
+
hint: "recurring tasks + cost control"
|
|
2572
|
+
},
|
|
2573
|
+
{
|
|
2574
|
+
value: "status",
|
|
2575
|
+
label: "View status",
|
|
2576
|
+
hint: "see what's installed"
|
|
2577
|
+
},
|
|
2578
|
+
{
|
|
2579
|
+
value: "doctor",
|
|
2580
|
+
label: "Run diagnostics",
|
|
2581
|
+
hint: "check for issues"
|
|
2582
|
+
}
|
|
2583
|
+
]
|
|
2584
|
+
});
|
|
2585
|
+
if (p$1.isCancel(action)) {
|
|
2586
|
+
p$1.outro(dim("Come back anytime!"));
|
|
2587
|
+
return;
|
|
2588
|
+
}
|
|
2589
|
+
const cmd = `openpaw ${action}`;
|
|
2590
|
+
console.log("");
|
|
2591
|
+
p$1.log.info(`Running ${accent(cmd)}...`);
|
|
2592
|
+
console.log("");
|
|
2593
|
+
try {
|
|
2594
|
+
execSync(`node ${process.argv[1]} ${action}`, {
|
|
2595
|
+
stdio: "inherit",
|
|
2596
|
+
cwd: process.cwd()
|
|
2597
|
+
});
|
|
2598
|
+
} catch {}
|
|
2599
|
+
}
|
|
2600
|
+
|
|
2471
2601
|
//#endregion
|
|
2472
2602
|
//#region src/commands/schedule.ts
|
|
2473
2603
|
async function scheduleAddCommand(schedule, opts) {
|
|
@@ -2707,7 +2837,7 @@ async function scheduleSetCapCommand(amount) {
|
|
|
2707
2837
|
//#endregion
|
|
2708
2838
|
//#region src/index.ts
|
|
2709
2839
|
const program = new Command();
|
|
2710
|
-
program.name("openpaw").description("Personal Assistant Wizard for Claude Code").version("1.
|
|
2840
|
+
program.name("openpaw").description("Personal Assistant Wizard for Claude Code").version("1.3.0");
|
|
2711
2841
|
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
2842
|
program.command("add").description("Add skill(s) by name").argument("<skills...>", "Skill IDs to add (e.g. notes music email)").action(addCommand);
|
|
2713
2843
|
program.command("remove").description("Remove skill(s) by name").argument("<skills...>", "Skill IDs to remove").action(removeCommand);
|
|
@@ -2720,6 +2850,7 @@ program.command("soul").description("Set up or edit Claude's personality (SOUL.m
|
|
|
2720
2850
|
program.command("export").description("Export skills, memory, and config to a file").action(exportCommand);
|
|
2721
2851
|
program.command("import").description("Import skills, memory, and config from a file").argument("<file>", "Path to openpaw-export.json").action(importCommand);
|
|
2722
2852
|
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);
|
|
2853
|
+
program.command("configure").alias("config").description("Configure your setup — add skills, change personality, manage dashboard").action(configureCommand);
|
|
2723
2854
|
const tg = program.command("telegram").description("Start the Telegram bridge — talk to Claude from your phone");
|
|
2724
2855
|
tg.action(telegramCommand);
|
|
2725
2856
|
tg.command("setup").description("Set up or reconfigure the Telegram bot").action(telegramSetupCommand);
|