pruff-skills 1.14.0 → 2.0.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/README.md +5 -6
- package/dist/cli.mjs +403 -588
- package/package.json +1 -1
package/dist/cli.mjs
CHANGED
|
@@ -799,7 +799,7 @@ const CLONE_TIMEOUT_MS = (() => {
|
|
|
799
799
|
const parsed = Number.parseInt(raw, 10);
|
|
800
800
|
return Number.isFinite(parsed) && parsed > 0 ? parsed : DEFAULT_CLONE_TIMEOUT_MS;
|
|
801
801
|
})();
|
|
802
|
-
const execFileAsync$
|
|
802
|
+
const execFileAsync$3 = promisify(execFile);
|
|
803
803
|
var GitCloneError = class extends Error {
|
|
804
804
|
url;
|
|
805
805
|
isTimeout;
|
|
@@ -877,7 +877,7 @@ async function resetTempDir(dir) {
|
|
|
877
877
|
async function tryGhClone(repo, tempDir, ref) {
|
|
878
878
|
let cloneTarget = repo.slug;
|
|
879
879
|
try {
|
|
880
|
-
const { stdout, stderr } = await execFileAsync$
|
|
880
|
+
const { stdout, stderr } = await execFileAsync$3("gh", [
|
|
881
881
|
"auth",
|
|
882
882
|
"status",
|
|
883
883
|
"-h",
|
|
@@ -894,7 +894,7 @@ async function tryGhClone(repo, tempDir, ref) {
|
|
|
894
894
|
} catch {
|
|
895
895
|
return false;
|
|
896
896
|
}
|
|
897
|
-
await execFileAsync$
|
|
897
|
+
await execFileAsync$3("gh", [
|
|
898
898
|
"repo",
|
|
899
899
|
"clone",
|
|
900
900
|
cloneTarget,
|
|
@@ -1322,16 +1322,16 @@ function filterSkills(skills, inputNames) {
|
|
|
1322
1322
|
return normalizedInputs.some((input) => input === name || input === displayName);
|
|
1323
1323
|
});
|
|
1324
1324
|
}
|
|
1325
|
-
const home
|
|
1326
|
-
const configHome = xdgConfig ?? join(home
|
|
1327
|
-
const codexHome = process.env.CODEX_HOME?.trim() || join(home
|
|
1328
|
-
const claudeHome$
|
|
1329
|
-
const vibeHome = process.env.VIBE_HOME?.trim() || join(home
|
|
1330
|
-
const hermesHome = process.env.HERMES_HOME?.trim() || join(home
|
|
1331
|
-
const autohandHome = process.env.AUTOHAND_HOME?.trim() || join(home
|
|
1325
|
+
const home = homedir();
|
|
1326
|
+
const configHome = xdgConfig ?? join(home, ".config");
|
|
1327
|
+
const codexHome = process.env.CODEX_HOME?.trim() || join(home, ".codex");
|
|
1328
|
+
const claudeHome$1 = process.env.CLAUDE_CONFIG_DIR?.trim() || join(home, ".claude");
|
|
1329
|
+
const vibeHome = process.env.VIBE_HOME?.trim() || join(home, ".vibe");
|
|
1330
|
+
const hermesHome = process.env.HERMES_HOME?.trim() || join(home, ".hermes");
|
|
1331
|
+
const autohandHome = process.env.AUTOHAND_HOME?.trim() || join(home, ".autohand");
|
|
1332
1332
|
const zedAppDataHome = process.env.APPDATA?.trim();
|
|
1333
1333
|
const zedFlatpakConfigHome = process.env.FLATPAK_XDG_CONFIG_HOME?.trim();
|
|
1334
|
-
function getOpenClawGlobalSkillsDir(homeDir = home
|
|
1334
|
+
function getOpenClawGlobalSkillsDir(homeDir = home, pathExists = existsSync) {
|
|
1335
1335
|
if (pathExists(join(homeDir, ".openclaw"))) return join(homeDir, ".openclaw/skills");
|
|
1336
1336
|
if (pathExists(join(homeDir, ".clawdbot"))) return join(homeDir, ".clawdbot/skills");
|
|
1337
1337
|
if (pathExists(join(homeDir, ".moltbot"))) return join(homeDir, ".moltbot/skills");
|
|
@@ -1342,9 +1342,9 @@ const agents = {
|
|
|
1342
1342
|
name: "aider-desk",
|
|
1343
1343
|
displayName: "AiderDesk",
|
|
1344
1344
|
skillsDir: ".aider-desk/skills",
|
|
1345
|
-
globalSkillsDir: join(home
|
|
1345
|
+
globalSkillsDir: join(home, ".aider-desk/skills"),
|
|
1346
1346
|
detectInstalled: async () => {
|
|
1347
|
-
return existsSync(join(home
|
|
1347
|
+
return existsSync(join(home, ".aider-desk"));
|
|
1348
1348
|
}
|
|
1349
1349
|
},
|
|
1350
1350
|
amp: {
|
|
@@ -1360,27 +1360,27 @@ const agents = {
|
|
|
1360
1360
|
name: "antigravity",
|
|
1361
1361
|
displayName: "Antigravity",
|
|
1362
1362
|
skillsDir: ".agents/skills",
|
|
1363
|
-
globalSkillsDir: join(home
|
|
1363
|
+
globalSkillsDir: join(home, ".gemini/antigravity/skills"),
|
|
1364
1364
|
detectInstalled: async () => {
|
|
1365
|
-
return existsSync(join(home
|
|
1365
|
+
return existsSync(join(home, ".gemini/antigravity"));
|
|
1366
1366
|
}
|
|
1367
1367
|
},
|
|
1368
1368
|
"antigravity-cli": {
|
|
1369
1369
|
name: "antigravity-cli",
|
|
1370
1370
|
displayName: "Antigravity CLI",
|
|
1371
1371
|
skillsDir: ".agents/skills",
|
|
1372
|
-
globalSkillsDir: join(home
|
|
1372
|
+
globalSkillsDir: join(home, ".gemini/antigravity-cli/skills"),
|
|
1373
1373
|
detectInstalled: async () => {
|
|
1374
|
-
return existsSync(join(home
|
|
1374
|
+
return existsSync(join(home, ".gemini/antigravity-cli"));
|
|
1375
1375
|
}
|
|
1376
1376
|
},
|
|
1377
1377
|
astrbot: {
|
|
1378
1378
|
name: "astrbot",
|
|
1379
1379
|
displayName: "AstrBot",
|
|
1380
1380
|
skillsDir: "data/skills",
|
|
1381
|
-
globalSkillsDir: join(home
|
|
1381
|
+
globalSkillsDir: join(home, ".astrbot/data/skills"),
|
|
1382
1382
|
detectInstalled: async () => {
|
|
1383
|
-
return existsSync(join(process.cwd(), "data/skills")) || existsSync(join(home
|
|
1383
|
+
return existsSync(join(process.cwd(), "data/skills")) || existsSync(join(home, ".astrbot"));
|
|
1384
1384
|
}
|
|
1385
1385
|
},
|
|
1386
1386
|
"autohand-code": {
|
|
@@ -1396,27 +1396,27 @@ const agents = {
|
|
|
1396
1396
|
name: "augment",
|
|
1397
1397
|
displayName: "Augment",
|
|
1398
1398
|
skillsDir: ".augment/skills",
|
|
1399
|
-
globalSkillsDir: join(home
|
|
1399
|
+
globalSkillsDir: join(home, ".augment/skills"),
|
|
1400
1400
|
detectInstalled: async () => {
|
|
1401
|
-
return existsSync(join(home
|
|
1401
|
+
return existsSync(join(home, ".augment"));
|
|
1402
1402
|
}
|
|
1403
1403
|
},
|
|
1404
1404
|
bob: {
|
|
1405
1405
|
name: "bob",
|
|
1406
1406
|
displayName: "IBM Bob",
|
|
1407
1407
|
skillsDir: ".bob/skills",
|
|
1408
|
-
globalSkillsDir: join(home
|
|
1408
|
+
globalSkillsDir: join(home, ".bob/skills"),
|
|
1409
1409
|
detectInstalled: async () => {
|
|
1410
|
-
return existsSync(join(home
|
|
1410
|
+
return existsSync(join(home, ".bob"));
|
|
1411
1411
|
}
|
|
1412
1412
|
},
|
|
1413
1413
|
"claude-code": {
|
|
1414
1414
|
name: "claude-code",
|
|
1415
1415
|
displayName: "Claude Code",
|
|
1416
1416
|
skillsDir: ".claude/skills",
|
|
1417
|
-
globalSkillsDir: join(claudeHome$
|
|
1417
|
+
globalSkillsDir: join(claudeHome$1, "skills"),
|
|
1418
1418
|
detectInstalled: async () => {
|
|
1419
|
-
return existsSync(claudeHome$
|
|
1419
|
+
return existsSync(claudeHome$1);
|
|
1420
1420
|
}
|
|
1421
1421
|
},
|
|
1422
1422
|
openclaw: {
|
|
@@ -1425,52 +1425,52 @@ const agents = {
|
|
|
1425
1425
|
skillsDir: "skills",
|
|
1426
1426
|
globalSkillsDir: getOpenClawGlobalSkillsDir(),
|
|
1427
1427
|
detectInstalled: async () => {
|
|
1428
|
-
return existsSync(join(home
|
|
1428
|
+
return existsSync(join(home, ".openclaw")) || existsSync(join(home, ".clawdbot")) || existsSync(join(home, ".moltbot"));
|
|
1429
1429
|
}
|
|
1430
1430
|
},
|
|
1431
1431
|
cline: {
|
|
1432
1432
|
name: "cline",
|
|
1433
1433
|
displayName: "Cline",
|
|
1434
1434
|
skillsDir: ".agents/skills",
|
|
1435
|
-
globalSkillsDir: join(home
|
|
1435
|
+
globalSkillsDir: join(home, ".agents", "skills"),
|
|
1436
1436
|
detectInstalled: async () => {
|
|
1437
|
-
return existsSync(join(home
|
|
1437
|
+
return existsSync(join(home, ".cline"));
|
|
1438
1438
|
}
|
|
1439
1439
|
},
|
|
1440
1440
|
"codearts-agent": {
|
|
1441
1441
|
name: "codearts-agent",
|
|
1442
1442
|
displayName: "CodeArts Agent",
|
|
1443
1443
|
skillsDir: ".codeartsdoer/skills",
|
|
1444
|
-
globalSkillsDir: join(home
|
|
1444
|
+
globalSkillsDir: join(home, ".codeartsdoer/skills"),
|
|
1445
1445
|
detectInstalled: async () => {
|
|
1446
|
-
return existsSync(join(home
|
|
1446
|
+
return existsSync(join(home, ".codeartsdoer"));
|
|
1447
1447
|
}
|
|
1448
1448
|
},
|
|
1449
1449
|
codebuddy: {
|
|
1450
1450
|
name: "codebuddy",
|
|
1451
1451
|
displayName: "CodeBuddy",
|
|
1452
1452
|
skillsDir: ".codebuddy/skills",
|
|
1453
|
-
globalSkillsDir: join(home
|
|
1453
|
+
globalSkillsDir: join(home, ".codebuddy/skills"),
|
|
1454
1454
|
detectInstalled: async () => {
|
|
1455
|
-
return existsSync(join(process.cwd(), ".codebuddy")) || existsSync(join(home
|
|
1455
|
+
return existsSync(join(process.cwd(), ".codebuddy")) || existsSync(join(home, ".codebuddy"));
|
|
1456
1456
|
}
|
|
1457
1457
|
},
|
|
1458
1458
|
codemaker: {
|
|
1459
1459
|
name: "codemaker",
|
|
1460
1460
|
displayName: "Codemaker",
|
|
1461
1461
|
skillsDir: ".codemaker/skills",
|
|
1462
|
-
globalSkillsDir: join(home
|
|
1462
|
+
globalSkillsDir: join(home, ".codemaker/skills"),
|
|
1463
1463
|
detectInstalled: async () => {
|
|
1464
|
-
return existsSync(join(home
|
|
1464
|
+
return existsSync(join(home, ".codemaker"));
|
|
1465
1465
|
}
|
|
1466
1466
|
},
|
|
1467
1467
|
codestudio: {
|
|
1468
1468
|
name: "codestudio",
|
|
1469
1469
|
displayName: "Code Studio",
|
|
1470
1470
|
skillsDir: ".codestudio/skills",
|
|
1471
|
-
globalSkillsDir: join(home
|
|
1471
|
+
globalSkillsDir: join(home, ".codestudio/skills"),
|
|
1472
1472
|
detectInstalled: async () => {
|
|
1473
|
-
return existsSync(join(home
|
|
1473
|
+
return existsSync(join(home, ".codestudio"));
|
|
1474
1474
|
}
|
|
1475
1475
|
},
|
|
1476
1476
|
codex: {
|
|
@@ -1486,54 +1486,54 @@ const agents = {
|
|
|
1486
1486
|
name: "command-code",
|
|
1487
1487
|
displayName: "Command Code",
|
|
1488
1488
|
skillsDir: ".commandcode/skills",
|
|
1489
|
-
globalSkillsDir: join(home
|
|
1489
|
+
globalSkillsDir: join(home, ".commandcode/skills"),
|
|
1490
1490
|
detectInstalled: async () => {
|
|
1491
|
-
return existsSync(join(home
|
|
1491
|
+
return existsSync(join(home, ".commandcode"));
|
|
1492
1492
|
}
|
|
1493
1493
|
},
|
|
1494
1494
|
continue: {
|
|
1495
1495
|
name: "continue",
|
|
1496
1496
|
displayName: "Continue",
|
|
1497
1497
|
skillsDir: ".continue/skills",
|
|
1498
|
-
globalSkillsDir: join(home
|
|
1498
|
+
globalSkillsDir: join(home, ".continue/skills"),
|
|
1499
1499
|
detectInstalled: async () => {
|
|
1500
|
-
return existsSync(join(process.cwd(), ".continue")) || existsSync(join(home
|
|
1500
|
+
return existsSync(join(process.cwd(), ".continue")) || existsSync(join(home, ".continue"));
|
|
1501
1501
|
}
|
|
1502
1502
|
},
|
|
1503
1503
|
cortex: {
|
|
1504
1504
|
name: "cortex",
|
|
1505
1505
|
displayName: "Cortex Code",
|
|
1506
1506
|
skillsDir: ".cortex/skills",
|
|
1507
|
-
globalSkillsDir: join(home
|
|
1507
|
+
globalSkillsDir: join(home, ".snowflake/cortex/skills"),
|
|
1508
1508
|
detectInstalled: async () => {
|
|
1509
|
-
return existsSync(join(home
|
|
1509
|
+
return existsSync(join(home, ".snowflake/cortex"));
|
|
1510
1510
|
}
|
|
1511
1511
|
},
|
|
1512
1512
|
crush: {
|
|
1513
1513
|
name: "crush",
|
|
1514
1514
|
displayName: "Crush",
|
|
1515
1515
|
skillsDir: ".crush/skills",
|
|
1516
|
-
globalSkillsDir: join(home
|
|
1516
|
+
globalSkillsDir: join(home, ".config/crush/skills"),
|
|
1517
1517
|
detectInstalled: async () => {
|
|
1518
|
-
return existsSync(join(home
|
|
1518
|
+
return existsSync(join(home, ".config/crush"));
|
|
1519
1519
|
}
|
|
1520
1520
|
},
|
|
1521
1521
|
cursor: {
|
|
1522
1522
|
name: "cursor",
|
|
1523
1523
|
displayName: "Cursor",
|
|
1524
1524
|
skillsDir: ".agents/skills",
|
|
1525
|
-
globalSkillsDir: join(home
|
|
1525
|
+
globalSkillsDir: join(home, ".cursor/skills"),
|
|
1526
1526
|
detectInstalled: async () => {
|
|
1527
|
-
return existsSync(join(home
|
|
1527
|
+
return existsSync(join(home, ".cursor"));
|
|
1528
1528
|
}
|
|
1529
1529
|
},
|
|
1530
1530
|
deepagents: {
|
|
1531
1531
|
name: "deepagents",
|
|
1532
1532
|
displayName: "Deep Agents",
|
|
1533
1533
|
skillsDir: ".agents/skills",
|
|
1534
|
-
globalSkillsDir: join(home
|
|
1534
|
+
globalSkillsDir: join(home, ".deepagents/agent/skills"),
|
|
1535
1535
|
detectInstalled: async () => {
|
|
1536
|
-
return existsSync(join(home
|
|
1536
|
+
return existsSync(join(home, ".deepagents"));
|
|
1537
1537
|
}
|
|
1538
1538
|
},
|
|
1539
1539
|
devin: {
|
|
@@ -1549,56 +1549,56 @@ const agents = {
|
|
|
1549
1549
|
name: "dexto",
|
|
1550
1550
|
displayName: "Dexto",
|
|
1551
1551
|
skillsDir: ".agents/skills",
|
|
1552
|
-
globalSkillsDir: join(home
|
|
1552
|
+
globalSkillsDir: join(home, ".agents/skills"),
|
|
1553
1553
|
showInUniversalPrompt: false,
|
|
1554
1554
|
detectInstalled: async () => {
|
|
1555
|
-
return existsSync(join(home
|
|
1555
|
+
return existsSync(join(home, ".dexto"));
|
|
1556
1556
|
}
|
|
1557
1557
|
},
|
|
1558
1558
|
droid: {
|
|
1559
1559
|
name: "droid",
|
|
1560
1560
|
displayName: "Droid",
|
|
1561
1561
|
skillsDir: ".factory/skills",
|
|
1562
|
-
globalSkillsDir: join(home
|
|
1562
|
+
globalSkillsDir: join(home, ".factory/skills"),
|
|
1563
1563
|
detectInstalled: async () => {
|
|
1564
|
-
return existsSync(join(home
|
|
1564
|
+
return existsSync(join(home, ".factory"));
|
|
1565
1565
|
}
|
|
1566
1566
|
},
|
|
1567
1567
|
firebender: {
|
|
1568
1568
|
name: "firebender",
|
|
1569
1569
|
displayName: "Firebender",
|
|
1570
1570
|
skillsDir: ".agents/skills",
|
|
1571
|
-
globalSkillsDir: join(home
|
|
1571
|
+
globalSkillsDir: join(home, ".firebender/skills"),
|
|
1572
1572
|
showInUniversalPrompt: false,
|
|
1573
1573
|
detectInstalled: async () => {
|
|
1574
|
-
return existsSync(join(home
|
|
1574
|
+
return existsSync(join(home, ".firebender"));
|
|
1575
1575
|
}
|
|
1576
1576
|
},
|
|
1577
1577
|
forgecode: {
|
|
1578
1578
|
name: "forgecode",
|
|
1579
1579
|
displayName: "ForgeCode",
|
|
1580
1580
|
skillsDir: ".forge/skills",
|
|
1581
|
-
globalSkillsDir: join(home
|
|
1581
|
+
globalSkillsDir: join(home, ".forge/skills"),
|
|
1582
1582
|
detectInstalled: async () => {
|
|
1583
|
-
return existsSync(join(home
|
|
1583
|
+
return existsSync(join(home, ".forge"));
|
|
1584
1584
|
}
|
|
1585
1585
|
},
|
|
1586
1586
|
"gemini-cli": {
|
|
1587
1587
|
name: "gemini-cli",
|
|
1588
1588
|
displayName: "Gemini CLI",
|
|
1589
1589
|
skillsDir: ".agents/skills",
|
|
1590
|
-
globalSkillsDir: join(home
|
|
1590
|
+
globalSkillsDir: join(home, ".gemini/skills"),
|
|
1591
1591
|
detectInstalled: async () => {
|
|
1592
|
-
return existsSync(join(home
|
|
1592
|
+
return existsSync(join(home, ".gemini"));
|
|
1593
1593
|
}
|
|
1594
1594
|
},
|
|
1595
1595
|
"github-copilot": {
|
|
1596
1596
|
name: "github-copilot",
|
|
1597
1597
|
displayName: "GitHub Copilot",
|
|
1598
1598
|
skillsDir: ".agents/skills",
|
|
1599
|
-
globalSkillsDir: join(home
|
|
1599
|
+
globalSkillsDir: join(home, ".copilot/skills"),
|
|
1600
1600
|
detectInstalled: async () => {
|
|
1601
|
-
return existsSync(join(home
|
|
1601
|
+
return existsSync(join(home, ".copilot"));
|
|
1602
1602
|
}
|
|
1603
1603
|
},
|
|
1604
1604
|
goose: {
|
|
@@ -1623,100 +1623,100 @@ const agents = {
|
|
|
1623
1623
|
name: "inference-sh",
|
|
1624
1624
|
displayName: "inference.sh",
|
|
1625
1625
|
skillsDir: ".inferencesh/skills",
|
|
1626
|
-
globalSkillsDir: join(home
|
|
1626
|
+
globalSkillsDir: join(home, ".inferencesh/skills"),
|
|
1627
1627
|
detectInstalled: async () => {
|
|
1628
|
-
return existsSync(join(home
|
|
1628
|
+
return existsSync(join(home, ".inferencesh"));
|
|
1629
1629
|
}
|
|
1630
1630
|
},
|
|
1631
1631
|
jazz: {
|
|
1632
1632
|
name: "jazz",
|
|
1633
1633
|
displayName: "Jazz",
|
|
1634
1634
|
skillsDir: ".jazz/skills",
|
|
1635
|
-
globalSkillsDir: join(home
|
|
1635
|
+
globalSkillsDir: join(home, ".jazz/skills"),
|
|
1636
1636
|
detectInstalled: async () => {
|
|
1637
|
-
return existsSync(join(home
|
|
1637
|
+
return existsSync(join(home, ".jazz")) || existsSync(join(process.cwd(), ".jazz"));
|
|
1638
1638
|
}
|
|
1639
1639
|
},
|
|
1640
1640
|
junie: {
|
|
1641
1641
|
name: "junie",
|
|
1642
1642
|
displayName: "Junie",
|
|
1643
1643
|
skillsDir: ".junie/skills",
|
|
1644
|
-
globalSkillsDir: join(home
|
|
1644
|
+
globalSkillsDir: join(home, ".junie/skills"),
|
|
1645
1645
|
detectInstalled: async () => {
|
|
1646
|
-
return existsSync(join(home
|
|
1646
|
+
return existsSync(join(home, ".junie"));
|
|
1647
1647
|
}
|
|
1648
1648
|
},
|
|
1649
1649
|
"iflow-cli": {
|
|
1650
1650
|
name: "iflow-cli",
|
|
1651
1651
|
displayName: "iFlow CLI",
|
|
1652
1652
|
skillsDir: ".iflow/skills",
|
|
1653
|
-
globalSkillsDir: join(home
|
|
1653
|
+
globalSkillsDir: join(home, ".iflow/skills"),
|
|
1654
1654
|
detectInstalled: async () => {
|
|
1655
|
-
return existsSync(join(home
|
|
1655
|
+
return existsSync(join(home, ".iflow"));
|
|
1656
1656
|
}
|
|
1657
1657
|
},
|
|
1658
1658
|
kilo: {
|
|
1659
1659
|
name: "kilo",
|
|
1660
1660
|
displayName: "Kilo Code",
|
|
1661
1661
|
skillsDir: ".kilocode/skills",
|
|
1662
|
-
globalSkillsDir: join(home
|
|
1662
|
+
globalSkillsDir: join(home, ".kilocode/skills"),
|
|
1663
1663
|
detectInstalled: async () => {
|
|
1664
|
-
return existsSync(join(home
|
|
1664
|
+
return existsSync(join(home, ".kilocode"));
|
|
1665
1665
|
}
|
|
1666
1666
|
},
|
|
1667
1667
|
"kimi-code-cli": {
|
|
1668
1668
|
name: "kimi-code-cli",
|
|
1669
1669
|
displayName: "Kimi Code CLI",
|
|
1670
1670
|
skillsDir: ".agents/skills",
|
|
1671
|
-
globalSkillsDir: join(home
|
|
1671
|
+
globalSkillsDir: join(home, ".agents/skills"),
|
|
1672
1672
|
detectInstalled: async () => {
|
|
1673
|
-
return existsSync(join(home
|
|
1673
|
+
return existsSync(join(home, ".kimi-code")) || existsSync(join(home, ".kimi"));
|
|
1674
1674
|
}
|
|
1675
1675
|
},
|
|
1676
1676
|
"kiro-cli": {
|
|
1677
1677
|
name: "kiro-cli",
|
|
1678
1678
|
displayName: "Kiro CLI",
|
|
1679
1679
|
skillsDir: ".kiro/skills",
|
|
1680
|
-
globalSkillsDir: join(home
|
|
1680
|
+
globalSkillsDir: join(home, ".kiro/skills"),
|
|
1681
1681
|
detectInstalled: async () => {
|
|
1682
|
-
return existsSync(join(home
|
|
1682
|
+
return existsSync(join(home, ".kiro"));
|
|
1683
1683
|
}
|
|
1684
1684
|
},
|
|
1685
1685
|
kode: {
|
|
1686
1686
|
name: "kode",
|
|
1687
1687
|
displayName: "Kode",
|
|
1688
1688
|
skillsDir: ".kode/skills",
|
|
1689
|
-
globalSkillsDir: join(home
|
|
1689
|
+
globalSkillsDir: join(home, ".kode/skills"),
|
|
1690
1690
|
detectInstalled: async () => {
|
|
1691
|
-
return existsSync(join(home
|
|
1691
|
+
return existsSync(join(home, ".kode"));
|
|
1692
1692
|
}
|
|
1693
1693
|
},
|
|
1694
1694
|
lingma: {
|
|
1695
1695
|
name: "lingma",
|
|
1696
1696
|
displayName: "Lingma",
|
|
1697
1697
|
skillsDir: ".lingma/skills",
|
|
1698
|
-
globalSkillsDir: join(home
|
|
1698
|
+
globalSkillsDir: join(home, ".lingma/skills"),
|
|
1699
1699
|
detectInstalled: async () => {
|
|
1700
|
-
return existsSync(join(home
|
|
1700
|
+
return existsSync(join(home, ".lingma"));
|
|
1701
1701
|
}
|
|
1702
1702
|
},
|
|
1703
1703
|
loaf: {
|
|
1704
1704
|
name: "loaf",
|
|
1705
1705
|
displayName: "Loaf",
|
|
1706
1706
|
skillsDir: ".agents/skills",
|
|
1707
|
-
globalSkillsDir: join(home
|
|
1707
|
+
globalSkillsDir: join(home, ".agents/skills"),
|
|
1708
1708
|
showInUniversalPrompt: false,
|
|
1709
1709
|
detectInstalled: async () => {
|
|
1710
|
-
return existsSync(join(home
|
|
1710
|
+
return existsSync(join(home, ".loaf"));
|
|
1711
1711
|
}
|
|
1712
1712
|
},
|
|
1713
1713
|
mcpjam: {
|
|
1714
1714
|
name: "mcpjam",
|
|
1715
1715
|
displayName: "MCPJam",
|
|
1716
1716
|
skillsDir: ".mcpjam/skills",
|
|
1717
|
-
globalSkillsDir: join(home
|
|
1717
|
+
globalSkillsDir: join(home, ".mcpjam/skills"),
|
|
1718
1718
|
detectInstalled: async () => {
|
|
1719
|
-
return existsSync(join(home
|
|
1719
|
+
return existsSync(join(home, ".mcpjam"));
|
|
1720
1720
|
}
|
|
1721
1721
|
},
|
|
1722
1722
|
"mistral-vibe": {
|
|
@@ -1732,18 +1732,18 @@ const agents = {
|
|
|
1732
1732
|
name: "moxby",
|
|
1733
1733
|
displayName: "Moxby",
|
|
1734
1734
|
skillsDir: ".moxby/skills",
|
|
1735
|
-
globalSkillsDir: join(home
|
|
1735
|
+
globalSkillsDir: join(home, ".moxby/skills"),
|
|
1736
1736
|
detectInstalled: async () => {
|
|
1737
|
-
return existsSync(join(home
|
|
1737
|
+
return existsSync(join(home, ".moxby"));
|
|
1738
1738
|
}
|
|
1739
1739
|
},
|
|
1740
1740
|
mux: {
|
|
1741
1741
|
name: "mux",
|
|
1742
1742
|
displayName: "Mux",
|
|
1743
1743
|
skillsDir: ".mux/skills",
|
|
1744
|
-
globalSkillsDir: join(home
|
|
1744
|
+
globalSkillsDir: join(home, ".mux/skills"),
|
|
1745
1745
|
detectInstalled: async () => {
|
|
1746
|
-
return existsSync(join(home
|
|
1746
|
+
return existsSync(join(home, ".mux"));
|
|
1747
1747
|
}
|
|
1748
1748
|
},
|
|
1749
1749
|
opencode: {
|
|
@@ -1759,54 +1759,54 @@ const agents = {
|
|
|
1759
1759
|
name: "openhands",
|
|
1760
1760
|
displayName: "OpenHands",
|
|
1761
1761
|
skillsDir: ".openhands/skills",
|
|
1762
|
-
globalSkillsDir: join(home
|
|
1762
|
+
globalSkillsDir: join(home, ".openhands/skills"),
|
|
1763
1763
|
detectInstalled: async () => {
|
|
1764
|
-
return existsSync(join(home
|
|
1764
|
+
return existsSync(join(home, ".openhands"));
|
|
1765
1765
|
}
|
|
1766
1766
|
},
|
|
1767
1767
|
ona: {
|
|
1768
1768
|
name: "ona",
|
|
1769
1769
|
displayName: "Ona",
|
|
1770
1770
|
skillsDir: ".ona/skills",
|
|
1771
|
-
globalSkillsDir: join(home
|
|
1771
|
+
globalSkillsDir: join(home, ".ona/skills"),
|
|
1772
1772
|
detectInstalled: async () => {
|
|
1773
|
-
return existsSync(join(home
|
|
1773
|
+
return existsSync(join(home, ".ona"));
|
|
1774
1774
|
}
|
|
1775
1775
|
},
|
|
1776
1776
|
pi: {
|
|
1777
1777
|
name: "pi",
|
|
1778
1778
|
displayName: "Pi",
|
|
1779
1779
|
skillsDir: ".pi/skills",
|
|
1780
|
-
globalSkillsDir: join(home
|
|
1780
|
+
globalSkillsDir: join(home, ".pi/agent/skills"),
|
|
1781
1781
|
detectInstalled: async () => {
|
|
1782
|
-
return existsSync(join(home
|
|
1782
|
+
return existsSync(join(home, ".pi/agent"));
|
|
1783
1783
|
}
|
|
1784
1784
|
},
|
|
1785
1785
|
qoder: {
|
|
1786
1786
|
name: "qoder",
|
|
1787
1787
|
displayName: "Qoder",
|
|
1788
1788
|
skillsDir: ".qoder/skills",
|
|
1789
|
-
globalSkillsDir: join(home
|
|
1789
|
+
globalSkillsDir: join(home, ".qoder/skills"),
|
|
1790
1790
|
detectInstalled: async () => {
|
|
1791
|
-
return existsSync(join(home
|
|
1791
|
+
return existsSync(join(home, ".qoder"));
|
|
1792
1792
|
}
|
|
1793
1793
|
},
|
|
1794
1794
|
"qoder-cn": {
|
|
1795
1795
|
name: "qoder-cn",
|
|
1796
1796
|
displayName: "Qoder CN",
|
|
1797
1797
|
skillsDir: ".qoder/skills",
|
|
1798
|
-
globalSkillsDir: join(home
|
|
1798
|
+
globalSkillsDir: join(home, ".qoder-cn/skills"),
|
|
1799
1799
|
detectInstalled: async () => {
|
|
1800
|
-
return existsSync(join(home
|
|
1800
|
+
return existsSync(join(home, ".qoder-cn"));
|
|
1801
1801
|
}
|
|
1802
1802
|
},
|
|
1803
1803
|
"qwen-code": {
|
|
1804
1804
|
name: "qwen-code",
|
|
1805
1805
|
displayName: "Qwen Code",
|
|
1806
1806
|
skillsDir: ".qwen/skills",
|
|
1807
|
-
globalSkillsDir: join(home
|
|
1807
|
+
globalSkillsDir: join(home, ".qwen/skills"),
|
|
1808
1808
|
detectInstalled: async () => {
|
|
1809
|
-
return existsSync(join(home
|
|
1809
|
+
return existsSync(join(home, ".qwen"));
|
|
1810
1810
|
}
|
|
1811
1811
|
},
|
|
1812
1812
|
replit: {
|
|
@@ -1823,97 +1823,97 @@ const agents = {
|
|
|
1823
1823
|
name: "reasonix",
|
|
1824
1824
|
displayName: "Reasonix",
|
|
1825
1825
|
skillsDir: ".reasonix/skills",
|
|
1826
|
-
globalSkillsDir: join(home
|
|
1826
|
+
globalSkillsDir: join(home, ".reasonix/skills"),
|
|
1827
1827
|
detectInstalled: async () => {
|
|
1828
|
-
return existsSync(join(home
|
|
1828
|
+
return existsSync(join(home, ".reasonix"));
|
|
1829
1829
|
}
|
|
1830
1830
|
},
|
|
1831
1831
|
rovodev: {
|
|
1832
1832
|
name: "rovodev",
|
|
1833
1833
|
displayName: "Rovo Dev",
|
|
1834
1834
|
skillsDir: ".rovodev/skills",
|
|
1835
|
-
globalSkillsDir: join(home
|
|
1835
|
+
globalSkillsDir: join(home, ".rovodev/skills"),
|
|
1836
1836
|
detectInstalled: async () => {
|
|
1837
|
-
return existsSync(join(home
|
|
1837
|
+
return existsSync(join(home, ".rovodev"));
|
|
1838
1838
|
}
|
|
1839
1839
|
},
|
|
1840
1840
|
roo: {
|
|
1841
1841
|
name: "roo",
|
|
1842
1842
|
displayName: "Roo Code",
|
|
1843
1843
|
skillsDir: ".roo/skills",
|
|
1844
|
-
globalSkillsDir: join(home
|
|
1844
|
+
globalSkillsDir: join(home, ".roo/skills"),
|
|
1845
1845
|
detectInstalled: async () => {
|
|
1846
|
-
return existsSync(join(home
|
|
1846
|
+
return existsSync(join(home, ".roo"));
|
|
1847
1847
|
}
|
|
1848
1848
|
},
|
|
1849
1849
|
"tabnine-cli": {
|
|
1850
1850
|
name: "tabnine-cli",
|
|
1851
1851
|
displayName: "Tabnine CLI",
|
|
1852
1852
|
skillsDir: ".tabnine/agent/skills",
|
|
1853
|
-
globalSkillsDir: join(home
|
|
1853
|
+
globalSkillsDir: join(home, ".tabnine/agent/skills"),
|
|
1854
1854
|
detectInstalled: async () => {
|
|
1855
|
-
return existsSync(join(home
|
|
1855
|
+
return existsSync(join(home, ".tabnine"));
|
|
1856
1856
|
}
|
|
1857
1857
|
},
|
|
1858
1858
|
terramind: {
|
|
1859
1859
|
name: "terramind",
|
|
1860
1860
|
displayName: "Terramind",
|
|
1861
1861
|
skillsDir: ".terramind/skills",
|
|
1862
|
-
globalSkillsDir: join(home
|
|
1862
|
+
globalSkillsDir: join(home, ".terramind/skills"),
|
|
1863
1863
|
detectInstalled: async () => {
|
|
1864
|
-
return existsSync(join(home
|
|
1864
|
+
return existsSync(join(home, ".terramind"));
|
|
1865
1865
|
}
|
|
1866
1866
|
},
|
|
1867
1867
|
tinycloud: {
|
|
1868
1868
|
name: "tinycloud",
|
|
1869
1869
|
displayName: "Tinycloud",
|
|
1870
1870
|
skillsDir: ".tinycloud/skills",
|
|
1871
|
-
globalSkillsDir: join(home
|
|
1871
|
+
globalSkillsDir: join(home, ".tinycloud/skills"),
|
|
1872
1872
|
detectInstalled: async () => {
|
|
1873
|
-
return existsSync(join(home
|
|
1873
|
+
return existsSync(join(home, ".tinycloud"));
|
|
1874
1874
|
}
|
|
1875
1875
|
},
|
|
1876
1876
|
trae: {
|
|
1877
1877
|
name: "trae",
|
|
1878
1878
|
displayName: "Trae",
|
|
1879
1879
|
skillsDir: ".trae/skills",
|
|
1880
|
-
globalSkillsDir: join(home
|
|
1880
|
+
globalSkillsDir: join(home, ".trae/skills"),
|
|
1881
1881
|
detectInstalled: async () => {
|
|
1882
|
-
return existsSync(join(home
|
|
1882
|
+
return existsSync(join(home, ".trae"));
|
|
1883
1883
|
}
|
|
1884
1884
|
},
|
|
1885
1885
|
"trae-cn": {
|
|
1886
1886
|
name: "trae-cn",
|
|
1887
1887
|
displayName: "Trae CN",
|
|
1888
1888
|
skillsDir: ".trae/skills",
|
|
1889
|
-
globalSkillsDir: join(home
|
|
1889
|
+
globalSkillsDir: join(home, ".trae-cn/skills"),
|
|
1890
1890
|
detectInstalled: async () => {
|
|
1891
|
-
return existsSync(join(home
|
|
1891
|
+
return existsSync(join(home, ".trae-cn"));
|
|
1892
1892
|
}
|
|
1893
1893
|
},
|
|
1894
1894
|
warp: {
|
|
1895
1895
|
name: "warp",
|
|
1896
1896
|
displayName: "Warp",
|
|
1897
1897
|
skillsDir: ".agents/skills",
|
|
1898
|
-
globalSkillsDir: join(home
|
|
1898
|
+
globalSkillsDir: join(home, ".agents/skills"),
|
|
1899
1899
|
detectInstalled: async () => {
|
|
1900
|
-
return existsSync(join(home
|
|
1900
|
+
return existsSync(join(home, ".warp"));
|
|
1901
1901
|
}
|
|
1902
1902
|
},
|
|
1903
1903
|
windsurf: {
|
|
1904
1904
|
name: "windsurf",
|
|
1905
1905
|
displayName: "Windsurf",
|
|
1906
1906
|
skillsDir: ".windsurf/skills",
|
|
1907
|
-
globalSkillsDir: join(home
|
|
1907
|
+
globalSkillsDir: join(home, ".codeium/windsurf/skills"),
|
|
1908
1908
|
detectInstalled: async () => {
|
|
1909
|
-
return existsSync(join(home
|
|
1909
|
+
return existsSync(join(home, ".codeium/windsurf"));
|
|
1910
1910
|
}
|
|
1911
1911
|
},
|
|
1912
1912
|
zed: {
|
|
1913
1913
|
name: "zed",
|
|
1914
1914
|
displayName: "Zed",
|
|
1915
1915
|
skillsDir: ".agents/skills",
|
|
1916
|
-
globalSkillsDir: join(home
|
|
1916
|
+
globalSkillsDir: join(home, ".agents/skills"),
|
|
1917
1917
|
detectInstalled: async () => {
|
|
1918
1918
|
return existsSync(join(configHome, "zed")) || !!zedAppDataHome && existsSync(join(zedAppDataHome, "Zed")) || !!zedFlatpakConfigHome && existsSync(join(zedFlatpakConfigHome, "zed"));
|
|
1919
1919
|
}
|
|
@@ -1922,36 +1922,36 @@ const agents = {
|
|
|
1922
1922
|
name: "zencoder",
|
|
1923
1923
|
displayName: "Zencoder",
|
|
1924
1924
|
skillsDir: ".zencoder/skills",
|
|
1925
|
-
globalSkillsDir: join(home
|
|
1925
|
+
globalSkillsDir: join(home, ".zencoder/skills"),
|
|
1926
1926
|
detectInstalled: async () => {
|
|
1927
|
-
return existsSync(join(home
|
|
1927
|
+
return existsSync(join(home, ".zencoder"));
|
|
1928
1928
|
}
|
|
1929
1929
|
},
|
|
1930
1930
|
zenflow: {
|
|
1931
1931
|
name: "zenflow",
|
|
1932
1932
|
displayName: "Zenflow",
|
|
1933
1933
|
skillsDir: ".zencoder/skills",
|
|
1934
|
-
globalSkillsDir: join(home
|
|
1934
|
+
globalSkillsDir: join(home, ".zencoder/skills"),
|
|
1935
1935
|
detectInstalled: async () => {
|
|
1936
|
-
return existsSync(join(home
|
|
1936
|
+
return existsSync(join(home, ".zencoder"));
|
|
1937
1937
|
}
|
|
1938
1938
|
},
|
|
1939
1939
|
neovate: {
|
|
1940
1940
|
name: "neovate",
|
|
1941
1941
|
displayName: "Neovate",
|
|
1942
1942
|
skillsDir: ".neovate/skills",
|
|
1943
|
-
globalSkillsDir: join(home
|
|
1943
|
+
globalSkillsDir: join(home, ".neovate/skills"),
|
|
1944
1944
|
detectInstalled: async () => {
|
|
1945
|
-
return existsSync(join(home
|
|
1945
|
+
return existsSync(join(home, ".neovate"));
|
|
1946
1946
|
}
|
|
1947
1947
|
},
|
|
1948
1948
|
pochi: {
|
|
1949
1949
|
name: "pochi",
|
|
1950
1950
|
displayName: "Pochi",
|
|
1951
1951
|
skillsDir: ".pochi/skills",
|
|
1952
|
-
globalSkillsDir: join(home
|
|
1952
|
+
globalSkillsDir: join(home, ".pochi/skills"),
|
|
1953
1953
|
detectInstalled: async () => {
|
|
1954
|
-
return existsSync(join(home
|
|
1954
|
+
return existsSync(join(home, ".pochi"));
|
|
1955
1955
|
}
|
|
1956
1956
|
},
|
|
1957
1957
|
promptscript: {
|
|
@@ -1968,9 +1968,9 @@ const agents = {
|
|
|
1968
1968
|
name: "adal",
|
|
1969
1969
|
displayName: "AdaL",
|
|
1970
1970
|
skillsDir: ".adal/skills",
|
|
1971
|
-
globalSkillsDir: join(home
|
|
1971
|
+
globalSkillsDir: join(home, ".adal/skills"),
|
|
1972
1972
|
detectInstalled: async () => {
|
|
1973
|
-
return existsSync(join(home
|
|
1973
|
+
return existsSync(join(home, ".adal"));
|
|
1974
1974
|
}
|
|
1975
1975
|
},
|
|
1976
1976
|
universal: {
|
|
@@ -4476,7 +4476,7 @@ function parseAddOptions(args) {
|
|
|
4476
4476
|
options
|
|
4477
4477
|
};
|
|
4478
4478
|
}
|
|
4479
|
-
const execFileAsync$
|
|
4479
|
+
const execFileAsync$2 = promisify(execFile);
|
|
4480
4480
|
const isLocalSource = (source) => source.startsWith(".") || isAbsolute(source);
|
|
4481
4481
|
const isGitHubSource = (source) => /^[^/]+\/[^/]+$/.test(source);
|
|
4482
4482
|
const ghEnv = () => ({
|
|
@@ -4511,7 +4511,7 @@ async function fetchCatalogFile(source, rel) {
|
|
|
4511
4511
|
return existsSync(file) ? readFile(file, "utf8") : null;
|
|
4512
4512
|
}
|
|
4513
4513
|
if (isGitHubSource(source)) try {
|
|
4514
|
-
const { stdout } = await execFileAsync$
|
|
4514
|
+
const { stdout } = await execFileAsync$2("gh", [
|
|
4515
4515
|
"api",
|
|
4516
4516
|
`repos/${source}/contents/${rel}`,
|
|
4517
4517
|
"--jq",
|
|
@@ -4535,7 +4535,7 @@ async function listCatalogDir(source, dir) {
|
|
|
4535
4535
|
return null;
|
|
4536
4536
|
}
|
|
4537
4537
|
if (isGitHubSource(source)) try {
|
|
4538
|
-
const { stdout } = await execFileAsync$
|
|
4538
|
+
const { stdout } = await execFileAsync$2("gh", [
|
|
4539
4539
|
"api",
|
|
4540
4540
|
`repos/${source}/contents/${dir}`,
|
|
4541
4541
|
"--jq",
|
|
@@ -4805,7 +4805,7 @@ async function runRules(args) {
|
|
|
4805
4805
|
const { names, options } = parseRulesOptions(sub === "add" || sub === "a" || sub === "install" || sub === "i" ? args.slice(1) : args);
|
|
4806
4806
|
await addRules(names, options);
|
|
4807
4807
|
}
|
|
4808
|
-
const execFileAsync$
|
|
4808
|
+
const execFileAsync$1 = promisify(execFile);
|
|
4809
4809
|
const RESET$4 = "\x1B[0m";
|
|
4810
4810
|
const BOLD$4 = "\x1B[1m";
|
|
4811
4811
|
const DIM$4 = "\x1B[38;5;102m";
|
|
@@ -4821,7 +4821,7 @@ async function fetchCatalogIndex(source) {
|
|
|
4821
4821
|
const rel = ".well-known/agent-skills/index.json";
|
|
4822
4822
|
if (source.startsWith(".") || isAbsolute(source)) return parseCatalogIndex(readFileSync(join(source, rel), "utf8"), source);
|
|
4823
4823
|
if (source.match(/^[^/]+\/[^/]+$/)) {
|
|
4824
|
-
const { stdout } = await execFileAsync$
|
|
4824
|
+
const { stdout } = await execFileAsync$1("gh", [
|
|
4825
4825
|
"api",
|
|
4826
4826
|
`repos/${source}/contents/${rel}`,
|
|
4827
4827
|
"--jq",
|
|
@@ -5837,6 +5837,252 @@ function parseRemoveOptions(args) {
|
|
|
5837
5837
|
options
|
|
5838
5838
|
};
|
|
5839
5839
|
}
|
|
5840
|
+
const claudeHome = () => process.env.CLAUDE_CONFIG_DIR?.trim() || join(homedir(), ".claude");
|
|
5841
|
+
const claudeSettingsPath = () => join(claudeHome(), "settings.json");
|
|
5842
|
+
const pruffHooksDir = () => join(claudeHome(), "hooks", "pruff");
|
|
5843
|
+
const statePath = () => join(claudeHome(), "pruff-skills", "state.json");
|
|
5844
|
+
function readClaudeSettings() {
|
|
5845
|
+
const path = claudeSettingsPath();
|
|
5846
|
+
if (!existsSync(path)) return {};
|
|
5847
|
+
let parsed;
|
|
5848
|
+
try {
|
|
5849
|
+
parsed = JSON.parse(readFileSync(path, "utf-8"));
|
|
5850
|
+
} catch {
|
|
5851
|
+
throw new Error(`${path} is not valid JSON. Fix it (or move it aside) before running this command.`);
|
|
5852
|
+
}
|
|
5853
|
+
return parsed && typeof parsed === "object" ? parsed : {};
|
|
5854
|
+
}
|
|
5855
|
+
function writeClaudeSettings(settings) {
|
|
5856
|
+
const path = claudeSettingsPath();
|
|
5857
|
+
const next = `${JSON.stringify(settings, null, 2)}\n`;
|
|
5858
|
+
const current = existsSync(path) ? readFileSync(path, "utf-8") : null;
|
|
5859
|
+
if (current === next) return null;
|
|
5860
|
+
mkdirSync(claudeHome(), { recursive: true });
|
|
5861
|
+
let backup = null;
|
|
5862
|
+
if (current !== null) {
|
|
5863
|
+
backup = `${path}.pruff-bak-${(/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-")}`;
|
|
5864
|
+
copyFileSync(path, backup);
|
|
5865
|
+
}
|
|
5866
|
+
writeFileSync(path, next, "utf-8");
|
|
5867
|
+
return backup;
|
|
5868
|
+
}
|
|
5869
|
+
function readState() {
|
|
5870
|
+
try {
|
|
5871
|
+
const parsed = JSON.parse(readFileSync(statePath(), "utf-8"));
|
|
5872
|
+
return {
|
|
5873
|
+
hooks: parsed.hooks ?? {},
|
|
5874
|
+
env: parsed.env ?? [],
|
|
5875
|
+
files: parsed.files ?? {}
|
|
5876
|
+
};
|
|
5877
|
+
} catch {
|
|
5878
|
+
return {
|
|
5879
|
+
hooks: {},
|
|
5880
|
+
env: [],
|
|
5881
|
+
files: {}
|
|
5882
|
+
};
|
|
5883
|
+
}
|
|
5884
|
+
}
|
|
5885
|
+
function writeState(state) {
|
|
5886
|
+
mkdirSync(join(claudeHome(), "pruff-skills"), { recursive: true });
|
|
5887
|
+
writeFileSync(statePath(), `${JSON.stringify(state, null, 2)}\n`, "utf-8");
|
|
5888
|
+
}
|
|
5889
|
+
function upsertHook(settings, event, matcher, command) {
|
|
5890
|
+
const hooks = { ...settings.hooks ?? {} };
|
|
5891
|
+
const groups = (hooks[event] ?? []).map((g) => ({
|
|
5892
|
+
...g,
|
|
5893
|
+
hooks: [...g.hooks]
|
|
5894
|
+
}));
|
|
5895
|
+
const existing = groups.find((g) => g.matcher === matcher && g.hooks.some((h) => h.command === command.command));
|
|
5896
|
+
if (existing) existing.hooks = existing.hooks.map((h) => h.command === command.command ? command : h);
|
|
5897
|
+
else groups.push(matcher === void 0 ? { hooks: [command] } : {
|
|
5898
|
+
matcher,
|
|
5899
|
+
hooks: [command]
|
|
5900
|
+
});
|
|
5901
|
+
hooks[event] = groups;
|
|
5902
|
+
return {
|
|
5903
|
+
...settings,
|
|
5904
|
+
hooks
|
|
5905
|
+
};
|
|
5906
|
+
}
|
|
5907
|
+
function removeHooks(settings, isOwned) {
|
|
5908
|
+
if (!settings.hooks) return settings;
|
|
5909
|
+
const hooks = {};
|
|
5910
|
+
for (const [event, groups] of Object.entries(settings.hooks)) {
|
|
5911
|
+
const kept = groups.map((g) => ({
|
|
5912
|
+
...g,
|
|
5913
|
+
hooks: g.hooks.filter((h) => !isOwned(h.command))
|
|
5914
|
+
})).filter((g) => g.hooks.length > 0);
|
|
5915
|
+
if (kept.length > 0) hooks[event] = kept;
|
|
5916
|
+
}
|
|
5917
|
+
const next = { ...settings };
|
|
5918
|
+
if (Object.keys(hooks).length === 0) delete next.hooks;
|
|
5919
|
+
else next.hooks = hooks;
|
|
5920
|
+
return next;
|
|
5921
|
+
}
|
|
5922
|
+
function applyOwnedEnv(settings, desired, previouslyOwned) {
|
|
5923
|
+
const env = { ...settings.env ?? {} };
|
|
5924
|
+
const owned = [];
|
|
5925
|
+
const keptUserValues = [];
|
|
5926
|
+
for (const key of previouslyOwned) if (!(key in desired)) delete env[key];
|
|
5927
|
+
for (const [key, value] of Object.entries(desired)) {
|
|
5928
|
+
const isOurs = previouslyOwned.includes(key);
|
|
5929
|
+
if (key in env && env[key] !== value && !isOurs) {
|
|
5930
|
+
keptUserValues.push(key);
|
|
5931
|
+
continue;
|
|
5932
|
+
}
|
|
5933
|
+
env[key] = value;
|
|
5934
|
+
owned.push(key);
|
|
5935
|
+
}
|
|
5936
|
+
const next = { ...settings };
|
|
5937
|
+
if (Object.keys(env).length === 0) delete next.env;
|
|
5938
|
+
else next.env = env;
|
|
5939
|
+
return {
|
|
5940
|
+
settings: next,
|
|
5941
|
+
owned,
|
|
5942
|
+
keptUserValues
|
|
5943
|
+
};
|
|
5944
|
+
}
|
|
5945
|
+
function addAllowEntries(settings, permissionAllow, autoModeAllow) {
|
|
5946
|
+
const isObject = (value) => !!value && typeof value === "object" && !Array.isArray(value);
|
|
5947
|
+
const strings = (value) => Array.isArray(value) ? value.filter((v) => typeof v === "string") : [];
|
|
5948
|
+
const merge = (current, added) => [...current, ...added.filter((entry) => !current.includes(entry))];
|
|
5949
|
+
const permissions = isObject(settings.permissions) ? settings.permissions : {};
|
|
5950
|
+
const autoMode = isObject(settings.autoMode) ? settings.autoMode : {};
|
|
5951
|
+
const currentAutoAllow = Array.isArray(autoMode.allow) ? strings(autoMode.allow) : ["$defaults"];
|
|
5952
|
+
return {
|
|
5953
|
+
...settings,
|
|
5954
|
+
permissions: {
|
|
5955
|
+
...permissions,
|
|
5956
|
+
allow: merge(strings(permissions.allow), permissionAllow)
|
|
5957
|
+
},
|
|
5958
|
+
autoMode: {
|
|
5959
|
+
...autoMode,
|
|
5960
|
+
allow: merge(currentAutoAllow, autoModeAllow)
|
|
5961
|
+
}
|
|
5962
|
+
};
|
|
5963
|
+
}
|
|
5964
|
+
const POSTHOG_HOST = "https://us.i.posthog.com";
|
|
5965
|
+
const POSTHOG_LOGS_ENDPOINT = `${POSTHOG_HOST}/i/v1/logs`;
|
|
5966
|
+
const POSTHOG_METRICS_ENDPOINT = `${POSTHOG_HOST}/i/v1/metrics`;
|
|
5967
|
+
const ANALYTICS_ENV_KEYS = [
|
|
5968
|
+
"OTEL_LOG_TOOL_DETAILS",
|
|
5969
|
+
"CLAUDE_CODE_ENABLE_TELEMETRY",
|
|
5970
|
+
"OTEL_LOGS_EXPORTER",
|
|
5971
|
+
"OTEL_METRICS_EXPORTER",
|
|
5972
|
+
"OTEL_EXPORTER_OTLP_PROTOCOL",
|
|
5973
|
+
"OTEL_EXPORTER_OTLP_LOGS_ENDPOINT",
|
|
5974
|
+
"OTEL_EXPORTER_OTLP_METRICS_ENDPOINT",
|
|
5975
|
+
"OTEL_EXPORTER_OTLP_LOGS_HEADERS",
|
|
5976
|
+
"OTEL_EXPORTER_OTLP_METRICS_HEADERS",
|
|
5977
|
+
"OTEL_SERVICE_NAME",
|
|
5978
|
+
"OTEL_RESOURCE_ATTRIBUTES",
|
|
5979
|
+
"OTEL_METRICS_INCLUDE_SESSION_ID",
|
|
5980
|
+
"OTEL_LOG_USER_PROMPTS",
|
|
5981
|
+
"OTEL_LOG_ASSISTANT_RESPONSES"
|
|
5982
|
+
];
|
|
5983
|
+
function hasAnalyticsEnv(settings) {
|
|
5984
|
+
return settings.env?.OTEL_EXPORTER_OTLP_LOGS_ENDPOINT === POSTHOG_LOGS_ENDPOINT || settings.env?.OTEL_EXPORTER_OTLP_METRICS_ENDPOINT === POSTHOG_METRICS_ENDPOINT;
|
|
5985
|
+
}
|
|
5986
|
+
function stripAnalyticsEnv(settings) {
|
|
5987
|
+
if (!settings.env || !hasAnalyticsEnv(settings)) return settings;
|
|
5988
|
+
const env = { ...settings.env };
|
|
5989
|
+
for (const key of ANALYTICS_ENV_KEYS) delete env[key];
|
|
5990
|
+
const next = { ...settings };
|
|
5991
|
+
if (Object.keys(env).length === 0) delete next.env;
|
|
5992
|
+
else next.env = env;
|
|
5993
|
+
return next;
|
|
5994
|
+
}
|
|
5995
|
+
function removeAnalyticsEnv(options = {}) {
|
|
5996
|
+
const settings = readClaudeSettings();
|
|
5997
|
+
if (!hasAnalyticsEnv(settings)) return { removed: false };
|
|
5998
|
+
if (options.dryRun) return {
|
|
5999
|
+
removed: true,
|
|
6000
|
+
dryRun: true,
|
|
6001
|
+
backup: null
|
|
6002
|
+
};
|
|
6003
|
+
return {
|
|
6004
|
+
removed: true,
|
|
6005
|
+
dryRun: false,
|
|
6006
|
+
backup: writeClaudeSettings(stripAnalyticsEnv(settings))
|
|
6007
|
+
};
|
|
6008
|
+
}
|
|
6009
|
+
function printRemoval(result) {
|
|
6010
|
+
if (result.dryRun) {
|
|
6011
|
+
console.log(` ${import_picocolors.default.yellow("!")} Would remove the PostHog usage exporter from ${import_picocolors.default.dim(claudeSettingsPath())}`);
|
|
6012
|
+
return;
|
|
6013
|
+
}
|
|
6014
|
+
console.log(` ${import_picocolors.default.green("✓")} Removed the PostHog usage exporter from ${import_picocolors.default.dim(claudeSettingsPath())}`);
|
|
6015
|
+
if (result.backup) console.log(import_picocolors.default.dim(` Backup: ${result.backup}`));
|
|
6016
|
+
console.log(import_picocolors.default.dim(" Restart Claude Code: the current session keeps exporting until then."));
|
|
6017
|
+
}
|
|
6018
|
+
function removeAnalyticsEnvOnUpdate(dryRun) {
|
|
6019
|
+
let result;
|
|
6020
|
+
try {
|
|
6021
|
+
result = removeAnalyticsEnv({ dryRun });
|
|
6022
|
+
} catch (error) {
|
|
6023
|
+
console.log(` ${import_picocolors.default.yellow("!")} Skipped the usage analytics cleanup: ${error instanceof Error ? error.message : String(error)}`);
|
|
6024
|
+
console.log();
|
|
6025
|
+
return;
|
|
6026
|
+
}
|
|
6027
|
+
if (!result.removed) return;
|
|
6028
|
+
console.log(`${import_picocolors.default.bold("Usage analytics")} ${import_picocolors.default.dim("(retired in 2.0.0)")}`);
|
|
6029
|
+
printRemoval(result);
|
|
6030
|
+
console.log();
|
|
6031
|
+
}
|
|
6032
|
+
function runStatus() {
|
|
6033
|
+
const enabled = hasAnalyticsEnv(readClaudeSettings());
|
|
6034
|
+
console.log();
|
|
6035
|
+
console.log(` ${import_picocolors.default.bold("Usage analytics")} ${enabled ? import_picocolors.default.yellow("on") : import_picocolors.default.dim("off")}`);
|
|
6036
|
+
console.log(` ${import_picocolors.default.dim("settings")} ${claudeSettingsPath()}`);
|
|
6037
|
+
console.log();
|
|
6038
|
+
console.log(import_picocolors.default.dim(enabled ? " Claude Code still exports to PostHog. Remove it: npx pruff-skills analytics disable" : " Retired in 2.0.0. Nothing is exported."));
|
|
6039
|
+
console.log();
|
|
6040
|
+
}
|
|
6041
|
+
function runDisable() {
|
|
6042
|
+
const result = removeAnalyticsEnv();
|
|
6043
|
+
console.log();
|
|
6044
|
+
if (result.removed) printRemoval(result);
|
|
6045
|
+
else console.log(` ${import_picocolors.default.dim("Nothing to remove: Claude Code is not exporting to PostHog.")}`);
|
|
6046
|
+
console.log();
|
|
6047
|
+
}
|
|
6048
|
+
function showAnalyticsHelp() {
|
|
6049
|
+
console.log(`
|
|
6050
|
+
${import_picocolors.default.bold("Usage:")} pruff-skills analytics <command>
|
|
6051
|
+
|
|
6052
|
+
status Show whether Claude Code still exports usage to Pruff's PostHog (default)
|
|
6053
|
+
disable Remove the exporter from ~/.claude/settings.json
|
|
6054
|
+
|
|
6055
|
+
Usage analytics was retired in 2.0.0. \`pruff-skills update\` also removes the exporter,
|
|
6056
|
+
leaving every other key in your settings untouched.
|
|
6057
|
+
`);
|
|
6058
|
+
}
|
|
6059
|
+
function runAnalytics(args = []) {
|
|
6060
|
+
const sub = args[0];
|
|
6061
|
+
switch (sub) {
|
|
6062
|
+
case void 0:
|
|
6063
|
+
case "status":
|
|
6064
|
+
runStatus();
|
|
6065
|
+
return 0;
|
|
6066
|
+
case "disable":
|
|
6067
|
+
case "off":
|
|
6068
|
+
runDisable();
|
|
6069
|
+
return 0;
|
|
6070
|
+
case "--help":
|
|
6071
|
+
case "-h":
|
|
6072
|
+
showAnalyticsHelp();
|
|
6073
|
+
return 0;
|
|
6074
|
+
case "enable":
|
|
6075
|
+
case "on":
|
|
6076
|
+
case "verify":
|
|
6077
|
+
console.error(`\`analytics ${sub}\` was removed in 2.0.0: usage analytics is retired.`);
|
|
6078
|
+
showAnalyticsHelp();
|
|
6079
|
+
return 1;
|
|
6080
|
+
default:
|
|
6081
|
+
console.error(`Unknown analytics command: ${sub}`);
|
|
6082
|
+
showAnalyticsHelp();
|
|
6083
|
+
return 1;
|
|
6084
|
+
}
|
|
6085
|
+
}
|
|
5840
6086
|
const RESET$2 = "\x1B[0m";
|
|
5841
6087
|
const BOLD$2 = "\x1B[1m";
|
|
5842
6088
|
const DIM$2 = "\x1B[38;5;102m";
|
|
@@ -6564,6 +6810,8 @@ async function syncAgentsForScope(isGlobal, options) {
|
|
|
6564
6810
|
}
|
|
6565
6811
|
async function runUpdate(args = []) {
|
|
6566
6812
|
const options = parseUpdateOptions(args);
|
|
6813
|
+
setStep("update:remove-analytics");
|
|
6814
|
+
removeAnalyticsEnvOnUpdate(options.dryRun === true);
|
|
6567
6815
|
setStep("update:resolve-scope");
|
|
6568
6816
|
const scope = await resolveUpdateScope(options);
|
|
6569
6817
|
if (options.skills) console.log(`${TEXT$1}Updating ${options.skills.join(", ")}...${RESET$1}`);
|
|
@@ -6992,130 +7240,6 @@ function fail(message) {
|
|
|
6992
7240
|
process.exit(1);
|
|
6993
7241
|
}
|
|
6994
7242
|
var UseCommandError = class extends Error {};
|
|
6995
|
-
const claudeHome$1 = () => process.env.CLAUDE_CONFIG_DIR?.trim() || join(homedir(), ".claude");
|
|
6996
|
-
const claudeSettingsPath$1 = () => join(claudeHome$1(), "settings.json");
|
|
6997
|
-
const pruffHooksDir = () => join(claudeHome$1(), "hooks", "pruff");
|
|
6998
|
-
const statePath = () => join(claudeHome$1(), "pruff-skills", "state.json");
|
|
6999
|
-
function readClaudeSettings$1() {
|
|
7000
|
-
const path = claudeSettingsPath$1();
|
|
7001
|
-
if (!existsSync(path)) return {};
|
|
7002
|
-
let parsed;
|
|
7003
|
-
try {
|
|
7004
|
-
parsed = JSON.parse(readFileSync(path, "utf-8"));
|
|
7005
|
-
} catch {
|
|
7006
|
-
throw new Error(`${path} is not valid JSON. Fix it (or move it aside) before running this command.`);
|
|
7007
|
-
}
|
|
7008
|
-
return parsed && typeof parsed === "object" ? parsed : {};
|
|
7009
|
-
}
|
|
7010
|
-
function writeClaudeSettings$1(settings) {
|
|
7011
|
-
const path = claudeSettingsPath$1();
|
|
7012
|
-
const next = `${JSON.stringify(settings, null, 2)}\n`;
|
|
7013
|
-
const current = existsSync(path) ? readFileSync(path, "utf-8") : null;
|
|
7014
|
-
if (current === next) return null;
|
|
7015
|
-
mkdirSync(claudeHome$1(), { recursive: true });
|
|
7016
|
-
let backup = null;
|
|
7017
|
-
if (current !== null) {
|
|
7018
|
-
backup = `${path}.pruff-bak-${(/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-")}`;
|
|
7019
|
-
copyFileSync(path, backup);
|
|
7020
|
-
}
|
|
7021
|
-
writeFileSync(path, next, "utf-8");
|
|
7022
|
-
return backup;
|
|
7023
|
-
}
|
|
7024
|
-
function readState() {
|
|
7025
|
-
try {
|
|
7026
|
-
const parsed = JSON.parse(readFileSync(statePath(), "utf-8"));
|
|
7027
|
-
return {
|
|
7028
|
-
hooks: parsed.hooks ?? {},
|
|
7029
|
-
env: parsed.env ?? [],
|
|
7030
|
-
files: parsed.files ?? {}
|
|
7031
|
-
};
|
|
7032
|
-
} catch {
|
|
7033
|
-
return {
|
|
7034
|
-
hooks: {},
|
|
7035
|
-
env: [],
|
|
7036
|
-
files: {}
|
|
7037
|
-
};
|
|
7038
|
-
}
|
|
7039
|
-
}
|
|
7040
|
-
function writeState(state) {
|
|
7041
|
-
mkdirSync(join(claudeHome$1(), "pruff-skills"), { recursive: true });
|
|
7042
|
-
writeFileSync(statePath(), `${JSON.stringify(state, null, 2)}\n`, "utf-8");
|
|
7043
|
-
}
|
|
7044
|
-
function upsertHook(settings, event, matcher, command) {
|
|
7045
|
-
const hooks = { ...settings.hooks ?? {} };
|
|
7046
|
-
const groups = (hooks[event] ?? []).map((g) => ({
|
|
7047
|
-
...g,
|
|
7048
|
-
hooks: [...g.hooks]
|
|
7049
|
-
}));
|
|
7050
|
-
const existing = groups.find((g) => g.matcher === matcher && g.hooks.some((h) => h.command === command.command));
|
|
7051
|
-
if (existing) existing.hooks = existing.hooks.map((h) => h.command === command.command ? command : h);
|
|
7052
|
-
else groups.push(matcher === void 0 ? { hooks: [command] } : {
|
|
7053
|
-
matcher,
|
|
7054
|
-
hooks: [command]
|
|
7055
|
-
});
|
|
7056
|
-
hooks[event] = groups;
|
|
7057
|
-
return {
|
|
7058
|
-
...settings,
|
|
7059
|
-
hooks
|
|
7060
|
-
};
|
|
7061
|
-
}
|
|
7062
|
-
function removeHooks(settings, isOwned) {
|
|
7063
|
-
if (!settings.hooks) return settings;
|
|
7064
|
-
const hooks = {};
|
|
7065
|
-
for (const [event, groups] of Object.entries(settings.hooks)) {
|
|
7066
|
-
const kept = groups.map((g) => ({
|
|
7067
|
-
...g,
|
|
7068
|
-
hooks: g.hooks.filter((h) => !isOwned(h.command))
|
|
7069
|
-
})).filter((g) => g.hooks.length > 0);
|
|
7070
|
-
if (kept.length > 0) hooks[event] = kept;
|
|
7071
|
-
}
|
|
7072
|
-
const next = { ...settings };
|
|
7073
|
-
if (Object.keys(hooks).length === 0) delete next.hooks;
|
|
7074
|
-
else next.hooks = hooks;
|
|
7075
|
-
return next;
|
|
7076
|
-
}
|
|
7077
|
-
function applyOwnedEnv(settings, desired, previouslyOwned) {
|
|
7078
|
-
const env = { ...settings.env ?? {} };
|
|
7079
|
-
const owned = [];
|
|
7080
|
-
const keptUserValues = [];
|
|
7081
|
-
for (const key of previouslyOwned) if (!(key in desired)) delete env[key];
|
|
7082
|
-
for (const [key, value] of Object.entries(desired)) {
|
|
7083
|
-
const isOurs = previouslyOwned.includes(key);
|
|
7084
|
-
if (key in env && env[key] !== value && !isOurs) {
|
|
7085
|
-
keptUserValues.push(key);
|
|
7086
|
-
continue;
|
|
7087
|
-
}
|
|
7088
|
-
env[key] = value;
|
|
7089
|
-
owned.push(key);
|
|
7090
|
-
}
|
|
7091
|
-
const next = { ...settings };
|
|
7092
|
-
if (Object.keys(env).length === 0) delete next.env;
|
|
7093
|
-
else next.env = env;
|
|
7094
|
-
return {
|
|
7095
|
-
settings: next,
|
|
7096
|
-
owned,
|
|
7097
|
-
keptUserValues
|
|
7098
|
-
};
|
|
7099
|
-
}
|
|
7100
|
-
function addAllowEntries(settings, permissionAllow, autoModeAllow) {
|
|
7101
|
-
const isObject = (value) => !!value && typeof value === "object" && !Array.isArray(value);
|
|
7102
|
-
const strings = (value) => Array.isArray(value) ? value.filter((v) => typeof v === "string") : [];
|
|
7103
|
-
const merge = (current, added) => [...current, ...added.filter((entry) => !current.includes(entry))];
|
|
7104
|
-
const permissions = isObject(settings.permissions) ? settings.permissions : {};
|
|
7105
|
-
const autoMode = isObject(settings.autoMode) ? settings.autoMode : {};
|
|
7106
|
-
const currentAutoAllow = Array.isArray(autoMode.allow) ? strings(autoMode.allow) : ["$defaults"];
|
|
7107
|
-
return {
|
|
7108
|
-
...settings,
|
|
7109
|
-
permissions: {
|
|
7110
|
-
...permissions,
|
|
7111
|
-
allow: merge(strings(permissions.allow), permissionAllow)
|
|
7112
|
-
},
|
|
7113
|
-
autoMode: {
|
|
7114
|
-
...autoMode,
|
|
7115
|
-
allow: merge(currentAutoAllow, autoModeAllow)
|
|
7116
|
-
}
|
|
7117
|
-
};
|
|
7118
|
-
}
|
|
7119
7243
|
const HOOK_EVENTS = [
|
|
7120
7244
|
"PreToolUse",
|
|
7121
7245
|
"PostToolUse",
|
|
@@ -7186,7 +7310,7 @@ async function installHooks(names, options = {}) {
|
|
|
7186
7310
|
backup: null
|
|
7187
7311
|
};
|
|
7188
7312
|
const state = readState();
|
|
7189
|
-
let settings = readClaudeSettings
|
|
7313
|
+
let settings = readClaudeSettings();
|
|
7190
7314
|
const writes = [];
|
|
7191
7315
|
for (const name of names) {
|
|
7192
7316
|
if (!isBareSkillName(name)) {
|
|
@@ -7252,13 +7376,13 @@ async function installHooks(names, options = {}) {
|
|
|
7252
7376
|
writeFileSync(w.path, w.content, "utf-8");
|
|
7253
7377
|
chmodSync(w.path, 493);
|
|
7254
7378
|
}
|
|
7255
|
-
result.backup = writeClaudeSettings
|
|
7379
|
+
result.backup = writeClaudeSettings(settings);
|
|
7256
7380
|
writeState(state);
|
|
7257
7381
|
return result;
|
|
7258
7382
|
}
|
|
7259
7383
|
function uninstallHooks(names, options = {}) {
|
|
7260
7384
|
const state = readState();
|
|
7261
|
-
let settings = readClaudeSettings
|
|
7385
|
+
let settings = readClaudeSettings();
|
|
7262
7386
|
const removed = [];
|
|
7263
7387
|
for (const name of names) {
|
|
7264
7388
|
if (!isBareSkillName(name)) continue;
|
|
@@ -7274,7 +7398,7 @@ function uninstallHooks(names, options = {}) {
|
|
|
7274
7398
|
});
|
|
7275
7399
|
}
|
|
7276
7400
|
if (!options.dryRun && removed.length > 0) {
|
|
7277
|
-
writeClaudeSettings
|
|
7401
|
+
writeClaudeSettings(settings);
|
|
7278
7402
|
writeState(state);
|
|
7279
7403
|
}
|
|
7280
7404
|
return removed;
|
|
@@ -7465,7 +7589,7 @@ async function installScripts() {
|
|
|
7465
7589
|
}
|
|
7466
7590
|
return null;
|
|
7467
7591
|
}
|
|
7468
|
-
function runVerify
|
|
7592
|
+
function runVerify() {
|
|
7469
7593
|
const dir = dbReaderBinDir();
|
|
7470
7594
|
const r = spawnSync(join(dir, "pruff-db-verify"), [DB_READER_VERIFY_DATABASE], {
|
|
7471
7595
|
encoding: "utf-8",
|
|
@@ -7525,7 +7649,7 @@ async function installReaderRule(workspace, note) {
|
|
|
7525
7649
|
}
|
|
7526
7650
|
function allowInUserSettings(note) {
|
|
7527
7651
|
try {
|
|
7528
|
-
writeClaudeSettings
|
|
7652
|
+
writeClaudeSettings(addAllowEntries(readClaudeSettings(), DB_READER_USER_ALLOW, [DB_READER_AUTO_MODE_ALLOW]));
|
|
7529
7653
|
M.success("pruff-db-query allowed in every folder, auto mode included");
|
|
7530
7654
|
} catch (error) {
|
|
7531
7655
|
note(`Could not update ~/.claude/settings.json (${error instanceof Error ? error.message : String(error)}). Auto mode may deny pruff-db-query with [Production Reads]: add ${import_picocolors.default.cyan(DB_READER_ALLOW_ENTRY)} to permissions.allow and an autoMode.allow rule for it by hand.`);
|
|
@@ -7621,7 +7745,7 @@ async function setupDbReader({ workspace, note }) {
|
|
|
7621
7745
|
M.success(`Installed ${DB_READER_SCRIPTS.join(" and ")} in ${dbReaderBinDir()}`);
|
|
7622
7746
|
if (!(process.env.PATH ?? "").split(delimiter).includes(dbReaderBinDir())) M.warn(`${dbReaderBinDir()} is not on your PATH. Add ${import_picocolors.default.cyan("export PATH=\"$HOME/.local/bin:$PATH\"")} to ~/.zshrc.`);
|
|
7623
7747
|
setStep("db-reader:verify");
|
|
7624
|
-
const verify = runVerify
|
|
7748
|
+
const verify = runVerify();
|
|
7625
7749
|
if (!verify.ok) {
|
|
7626
7750
|
note(`Prod DB access not enabled — ${import_picocolors.default.cyan("pruff-db-verify")} failed. Run ${import_picocolors.default.cyan(`${join(dbReaderBinDir(), "pruff-db-verify")} ${DB_READER_VERIFY_DATABASE}`)} and share the output with the infra owner.`);
|
|
7627
7751
|
M.warn(verify.out);
|
|
@@ -8988,8 +9112,8 @@ async function installHarness(workspace) {
|
|
|
8988
9112
|
M.step("Installing the team harness (hooks, settings, scripts)…");
|
|
8989
9113
|
try {
|
|
8990
9114
|
const state = readState();
|
|
8991
|
-
const env = applyOwnedEnv(readClaudeSettings
|
|
8992
|
-
writeClaudeSettings
|
|
9115
|
+
const env = applyOwnedEnv(readClaudeSettings(), TEAM_USER_ENV, state.env);
|
|
9116
|
+
writeClaudeSettings(env.settings);
|
|
8993
9117
|
writeState({
|
|
8994
9118
|
...readState(),
|
|
8995
9119
|
env: env.owned
|
|
@@ -9538,7 +9662,7 @@ function registerVault(vaultPath) {
|
|
|
9538
9662
|
writeFileSync(configPath, JSON.stringify(config), "utf-8");
|
|
9539
9663
|
M.success("Registered vault in Obsidian");
|
|
9540
9664
|
}
|
|
9541
|
-
const execFileAsync
|
|
9665
|
+
const execFileAsync = promisify(execFile);
|
|
9542
9666
|
const BUZZ_LOGO_LINES = [
|
|
9543
9667
|
"██████╗ ██╗ ██╗███████╗███████╗",
|
|
9544
9668
|
"██╔══██╗██║ ██║╚══███╔╝╚══███╔╝",
|
|
@@ -9652,7 +9776,7 @@ const TEAM_AGENTS = {
|
|
|
9652
9776
|
const TEAMS = Object.keys(TEAM_SKILLS);
|
|
9653
9777
|
async function isBuzzRunning() {
|
|
9654
9778
|
try {
|
|
9655
|
-
const { stdout } = await execFileAsync
|
|
9779
|
+
const { stdout } = await execFileAsync("pgrep", ["-f", "Buzz.app|buzz-desktop|buzz-acp"]);
|
|
9656
9780
|
return stdout.trim().length > 0;
|
|
9657
9781
|
} catch {
|
|
9658
9782
|
return false;
|
|
@@ -9660,7 +9784,7 @@ async function isBuzzRunning() {
|
|
|
9660
9784
|
}
|
|
9661
9785
|
async function claudeAuthStatus() {
|
|
9662
9786
|
try {
|
|
9663
|
-
const { stdout } = await execFileAsync
|
|
9787
|
+
const { stdout } = await execFileAsync("claude", ["auth", "status"]);
|
|
9664
9788
|
const parsed = JSON.parse(stdout);
|
|
9665
9789
|
const ok = parsed.apiProvider === "firstParty";
|
|
9666
9790
|
return {
|
|
@@ -9703,14 +9827,14 @@ async function syncVault() {
|
|
|
9703
9827
|
M.step("Pruff knowledge vault");
|
|
9704
9828
|
const dest = join(nestDir(), "REPOS", "pruff-knowledge");
|
|
9705
9829
|
if (existsSync(join(dest, ".git"))) try {
|
|
9706
|
-
await execFileAsync
|
|
9830
|
+
await execFileAsync("git", [
|
|
9707
9831
|
"-C",
|
|
9708
9832
|
dest,
|
|
9709
9833
|
"pull",
|
|
9710
9834
|
"--ff-only",
|
|
9711
9835
|
"--quiet"
|
|
9712
9836
|
]);
|
|
9713
|
-
const { stdout } = await execFileAsync
|
|
9837
|
+
const { stdout } = await execFileAsync("git", [
|
|
9714
9838
|
"-C",
|
|
9715
9839
|
dest,
|
|
9716
9840
|
"log",
|
|
@@ -9726,13 +9850,13 @@ async function syncVault() {
|
|
|
9726
9850
|
else {
|
|
9727
9851
|
mkdirSync(dirname(dest), { recursive: true });
|
|
9728
9852
|
try {
|
|
9729
|
-
await execFileAsync
|
|
9853
|
+
await execFileAsync("git", [
|
|
9730
9854
|
"clone",
|
|
9731
9855
|
"--quiet",
|
|
9732
9856
|
VAULT_REPO,
|
|
9733
9857
|
dest
|
|
9734
9858
|
]);
|
|
9735
|
-
const { stdout } = await execFileAsync
|
|
9859
|
+
const { stdout } = await execFileAsync("git", [
|
|
9736
9860
|
"-C",
|
|
9737
9861
|
dest,
|
|
9738
9862
|
"log",
|
|
@@ -10025,310 +10149,6 @@ async function runBuzz(args) {
|
|
|
10025
10149
|
console.log(BUZZ_HELP);
|
|
10026
10150
|
}
|
|
10027
10151
|
}
|
|
10028
|
-
const execFileAsync = promisify(execFile);
|
|
10029
|
-
const __analyticsDirname = dirname(fileURLToPath(import.meta.url));
|
|
10030
|
-
const POSTHOG_HOST = "https://us.i.posthog.com";
|
|
10031
|
-
const ANALYTICS_TOKEN_CATALOG_PATH = "analytics/posthog.json";
|
|
10032
|
-
let cachedToken = null;
|
|
10033
|
-
async function getAnalyticsToken() {
|
|
10034
|
-
const fromEnv = process.env.PRUFF_ANALYTICS_POSTHOG_TOKEN?.trim();
|
|
10035
|
-
if (fromEnv) return fromEnv;
|
|
10036
|
-
if (cachedToken !== null) return cachedToken;
|
|
10037
|
-
const raw = readLocalCheckoutToken() ?? await fetchTokenFile(PRUFF_CATALOG, ANALYTICS_TOKEN_CATALOG_PATH);
|
|
10038
|
-
cachedToken = raw ? JSON.parse(raw).token ?? "" : "";
|
|
10039
|
-
return cachedToken;
|
|
10040
|
-
}
|
|
10041
|
-
function readLocalCheckoutToken() {
|
|
10042
|
-
let dir = __analyticsDirname;
|
|
10043
|
-
for (let depth = 0; depth < 4; depth++) {
|
|
10044
|
-
const candidate = join(dir, ANALYTICS_TOKEN_CATALOG_PATH);
|
|
10045
|
-
if (existsSync(candidate)) return readFileSync(candidate, "utf-8");
|
|
10046
|
-
const parent = dirname(dir);
|
|
10047
|
-
if (parent === dir) break;
|
|
10048
|
-
dir = parent;
|
|
10049
|
-
}
|
|
10050
|
-
return null;
|
|
10051
|
-
}
|
|
10052
|
-
async function fetchTokenFile(source, rel) {
|
|
10053
|
-
if (source.startsWith(".") || isAbsolute(source)) {
|
|
10054
|
-
const file = join(source, rel);
|
|
10055
|
-
return existsSync(file) ? readFileSync(file, "utf-8") : null;
|
|
10056
|
-
}
|
|
10057
|
-
if (/^[^/]+\/[^/]+$/.test(source)) try {
|
|
10058
|
-
const { stdout } = await execFileAsync("gh", [
|
|
10059
|
-
"api",
|
|
10060
|
-
`repos/${source}/contents/${rel}`,
|
|
10061
|
-
"--jq",
|
|
10062
|
-
".content"
|
|
10063
|
-
], { env: {
|
|
10064
|
-
...process.env,
|
|
10065
|
-
GIT_TERMINAL_PROMPT: "0"
|
|
10066
|
-
} });
|
|
10067
|
-
return Buffer.from(stdout.trim(), "base64").toString("utf8");
|
|
10068
|
-
} catch {
|
|
10069
|
-
return null;
|
|
10070
|
-
}
|
|
10071
|
-
return null;
|
|
10072
|
-
}
|
|
10073
|
-
const home = homedir();
|
|
10074
|
-
const claudeHome = () => process.env.CLAUDE_CONFIG_DIR?.trim() || join(home, ".claude");
|
|
10075
|
-
const claudeSettingsPath = () => join(claudeHome(), "settings.json");
|
|
10076
|
-
function buildAnalyticsEnv(token, options = {}) {
|
|
10077
|
-
return {
|
|
10078
|
-
...options.withToolDetails ? { OTEL_LOG_TOOL_DETAILS: "1" } : {},
|
|
10079
|
-
CLAUDE_CODE_ENABLE_TELEMETRY: "1",
|
|
10080
|
-
OTEL_LOGS_EXPORTER: "otlp",
|
|
10081
|
-
OTEL_METRICS_EXPORTER: "otlp",
|
|
10082
|
-
OTEL_EXPORTER_OTLP_PROTOCOL: "http/protobuf",
|
|
10083
|
-
OTEL_EXPORTER_OTLP_LOGS_ENDPOINT: `${POSTHOG_HOST}/i/v1/logs`,
|
|
10084
|
-
OTEL_EXPORTER_OTLP_METRICS_ENDPOINT: `${POSTHOG_HOST}/i/v1/metrics`,
|
|
10085
|
-
OTEL_EXPORTER_OTLP_LOGS_HEADERS: `Authorization=Bearer ${token}`,
|
|
10086
|
-
OTEL_EXPORTER_OTLP_METRICS_HEADERS: `Authorization=Bearer ${token}`,
|
|
10087
|
-
OTEL_SERVICE_NAME: "claude-code",
|
|
10088
|
-
OTEL_RESOURCE_ATTRIBUTES: "pruff.rollout=team",
|
|
10089
|
-
OTEL_METRICS_INCLUDE_SESSION_ID: "false",
|
|
10090
|
-
OTEL_LOG_USER_PROMPTS: "0",
|
|
10091
|
-
OTEL_LOG_ASSISTANT_RESPONSES: "0"
|
|
10092
|
-
};
|
|
10093
|
-
}
|
|
10094
|
-
const ANALYTICS_ENV_KEYS = Object.keys(buildAnalyticsEnv("x", { withToolDetails: true }));
|
|
10095
|
-
function readClaudeSettings() {
|
|
10096
|
-
const path = claudeSettingsPath();
|
|
10097
|
-
if (!existsSync(path)) return {};
|
|
10098
|
-
try {
|
|
10099
|
-
const parsed = JSON.parse(readFileSync(path, "utf-8"));
|
|
10100
|
-
return parsed && typeof parsed === "object" ? parsed : {};
|
|
10101
|
-
} catch {
|
|
10102
|
-
throw new Error(`${path} is not valid JSON. Fix it (or move it aside) before running this command.`);
|
|
10103
|
-
}
|
|
10104
|
-
}
|
|
10105
|
-
function writeClaudeSettings(settings) {
|
|
10106
|
-
mkdirSync(claudeHome(), { recursive: true });
|
|
10107
|
-
writeFileSync(claudeSettingsPath(), `${JSON.stringify(settings, null, 2)}\n`, "utf-8");
|
|
10108
|
-
}
|
|
10109
|
-
function applyAnalyticsEnv(settings, token, options = {}) {
|
|
10110
|
-
const base = stripAnalyticsEnv(settings);
|
|
10111
|
-
return {
|
|
10112
|
-
...base,
|
|
10113
|
-
env: {
|
|
10114
|
-
...base.env ?? {},
|
|
10115
|
-
...buildAnalyticsEnv(token, options)
|
|
10116
|
-
}
|
|
10117
|
-
};
|
|
10118
|
-
}
|
|
10119
|
-
function stripAnalyticsEnv(settings) {
|
|
10120
|
-
if (!settings.env) return settings;
|
|
10121
|
-
const env = { ...settings.env };
|
|
10122
|
-
for (const key of ANALYTICS_ENV_KEYS) delete env[key];
|
|
10123
|
-
const next = { ...settings };
|
|
10124
|
-
if (Object.keys(env).length === 0) delete next.env;
|
|
10125
|
-
else next.env = env;
|
|
10126
|
-
return next;
|
|
10127
|
-
}
|
|
10128
|
-
function isAnalyticsEnabled(settings = readClaudeSettings()) {
|
|
10129
|
-
return settings.env?.CLAUDE_CODE_ENABLE_TELEMETRY === "1";
|
|
10130
|
-
}
|
|
10131
|
-
async function requireToken() {
|
|
10132
|
-
const token = await getAnalyticsToken();
|
|
10133
|
-
if (token.startsWith("phc_")) return token;
|
|
10134
|
-
console.log();
|
|
10135
|
-
console.log(import_picocolors.default.red(" Could not resolve the PostHog project token."));
|
|
10136
|
-
console.log(import_picocolors.default.dim(` Looked in three places, in order:
|
|
10137
|
-
1. \$PRUFF_ANALYTICS_POSTHOG_TOKEN (unset)
|
|
10138
|
-
2. ${ANALYTICS_TOKEN_CATALOG_PATH} in this checkout (not found)\n 3. ${ANALYTICS_TOKEN_CATALOG_PATH} in ${PRUFF_CATALOG} via gh (not found)\n Expected a file containing {"token":"phc_..."}.\n Check \`gh auth status\` — the catalog is private and read with your own
|
|
10139
|
-
GitHub auth. To test without the catalog, export PRUFF_ANALYTICS_POSTHOG_TOKEN.`));
|
|
10140
|
-
console.log();
|
|
10141
|
-
return null;
|
|
10142
|
-
}
|
|
10143
|
-
async function runStatus() {
|
|
10144
|
-
const settings = readClaudeSettings();
|
|
10145
|
-
const enabled = isAnalyticsEnabled(settings);
|
|
10146
|
-
const token = await getAnalyticsToken();
|
|
10147
|
-
console.log();
|
|
10148
|
-
console.log(` ${import_picocolors.default.bold("Usage analytics")} ${enabled ? import_picocolors.default.green("on") : import_picocolors.default.dim("off")}`);
|
|
10149
|
-
console.log(` ${import_picocolors.default.dim("settings")} ${claudeSettingsPath()}`);
|
|
10150
|
-
if (enabled) {
|
|
10151
|
-
console.log(` ${import_picocolors.default.dim("logs")} ${settings.env?.OTEL_EXPORTER_OTLP_LOGS_ENDPOINT}`);
|
|
10152
|
-
console.log(` ${import_picocolors.default.dim("metrics")} ${settings.env?.OTEL_EXPORTER_OTLP_METRICS_ENDPOINT}`);
|
|
10153
|
-
console.log(` ${import_picocolors.default.dim("token")} ${token ? `${token.slice(0, 12)}…` : import_picocolors.default.red("missing")}`);
|
|
10154
|
-
console.log(` ${import_picocolors.default.dim("prompts")} not exported (OTEL_LOG_USER_PROMPTS=0)`);
|
|
10155
|
-
const toolDetails = settings.env?.OTEL_LOG_TOOL_DETAILS === "1";
|
|
10156
|
-
console.log(` ${import_picocolors.default.dim("cmds")} ${toolDetails ? import_picocolors.default.yellow("EXPORTED verbatim (--with-tool-details on)") : "not exported — skills report as \"custom_skill\""}`);
|
|
10157
|
-
}
|
|
10158
|
-
console.log();
|
|
10159
|
-
console.log(import_picocolors.default.dim(enabled ? " Turn it off with: npx pruff-skills analytics disable" : " Turn it on with: npx pruff-skills analytics enable"));
|
|
10160
|
-
console.log();
|
|
10161
|
-
}
|
|
10162
|
-
async function runEnable(args) {
|
|
10163
|
-
const withToolDetails = args.includes("--with-tool-details");
|
|
10164
|
-
const token = await requireToken();
|
|
10165
|
-
if (!token) return;
|
|
10166
|
-
const settings = readClaudeSettings();
|
|
10167
|
-
const already = isAnalyticsEnabled(settings);
|
|
10168
|
-
writeClaudeSettings(applyAnalyticsEnv(settings, token, { withToolDetails }));
|
|
10169
|
-
console.log();
|
|
10170
|
-
console.log(` ${import_picocolors.default.green("✓")} Usage analytics ${already ? "refreshed" : "enabled"} in ${import_picocolors.default.dim(claudeSettingsPath())}`);
|
|
10171
|
-
console.log();
|
|
10172
|
-
console.log(` ${import_picocolors.default.dim("Exports")} token + cost usage, tool names, results and timings,`);
|
|
10173
|
-
console.log(` ${import_picocolors.default.dim(" ")} MCP tool calls, model, subagent vs main, your email.`);
|
|
10174
|
-
console.log(` ${import_picocolors.default.dim("Never")} prompts, responses, file contents, raw API bodies.`);
|
|
10175
|
-
console.log();
|
|
10176
|
-
if (withToolDetails) {
|
|
10177
|
-
console.log(` ${import_picocolors.default.yellow("!")} ${import_picocolors.default.bold("--with-tool-details is ON.")}`);
|
|
10178
|
-
console.log(import_picocolors.default.dim(" Real skill and slash-command names will be exported — and so will"));
|
|
10179
|
-
console.log(import_picocolors.default.dim(" every shell command and tool input, verbatim. Anything you type into"));
|
|
10180
|
-
console.log(import_picocolors.default.dim(" a command (RUTs, connection strings, tokens) reaches PostHog."));
|
|
10181
|
-
console.log(import_picocolors.default.dim(" Turn it back off by re-running `analytics enable` without the flag."));
|
|
10182
|
-
} else {
|
|
10183
|
-
console.log(import_picocolors.default.dim(" Pruff-authored skills report as \"custom_skill\", not their real names."));
|
|
10184
|
-
console.log(import_picocolors.default.dim(" `analytics enable --with-tool-details` unlocks the names — read what that"));
|
|
10185
|
-
console.log(import_picocolors.default.dim(" also exports first: npx pruff-skills analytics --help"));
|
|
10186
|
-
}
|
|
10187
|
-
console.log();
|
|
10188
|
-
console.log(` ${import_picocolors.default.bold("Restart Claude Code")} — the OTEL exporter is read once at session start.`);
|
|
10189
|
-
console.log(import_picocolors.default.dim(" Then confirm the pipeline works: npx pruff-skills analytics verify"));
|
|
10190
|
-
console.log();
|
|
10191
|
-
}
|
|
10192
|
-
function runDisable() {
|
|
10193
|
-
writeClaudeSettings(stripAnalyticsEnv(readClaudeSettings()));
|
|
10194
|
-
console.log();
|
|
10195
|
-
console.log(` ${import_picocolors.default.green("✓")} Usage analytics disabled — the env block is gone from`);
|
|
10196
|
-
console.log(import_picocolors.default.dim(` ${claudeSettingsPath()}, your own keys untouched.`));
|
|
10197
|
-
console.log(import_picocolors.default.dim(" Nothing turns it back on: only `analytics enable` ever writes it."));
|
|
10198
|
-
console.log(import_picocolors.default.dim(" Restart Claude Code to stop the current session exporting."));
|
|
10199
|
-
console.log();
|
|
10200
|
-
}
|
|
10201
|
-
async function verifyToken(token) {
|
|
10202
|
-
try {
|
|
10203
|
-
const res = await fetch(`${POSTHOG_HOST}/flags?v=2`, {
|
|
10204
|
-
method: "POST",
|
|
10205
|
-
headers: { "Content-Type": "application/json" },
|
|
10206
|
-
body: JSON.stringify({
|
|
10207
|
-
api_key: token,
|
|
10208
|
-
distinct_id: "pruff-analytics-verify"
|
|
10209
|
-
})
|
|
10210
|
-
});
|
|
10211
|
-
if (res.ok) {
|
|
10212
|
-
console.log(` ${import_picocolors.default.green("✓")} Token accepted by PostHog (HTTP ${res.status}).`);
|
|
10213
|
-
return true;
|
|
10214
|
-
}
|
|
10215
|
-
console.log(` ${import_picocolors.default.red("✗")} Token rejected (HTTP ${res.status}).`);
|
|
10216
|
-
console.log(import_picocolors.default.dim(` ${ANALYTICS_TOKEN_CATALOG_PATH} in ${PRUFF_CATALOG} does not hold a valid`));
|
|
10217
|
-
console.log(import_picocolors.default.dim(" project token for the Pruff PostHog project."));
|
|
10218
|
-
return false;
|
|
10219
|
-
} catch (error) {
|
|
10220
|
-
console.log(` ${import_picocolors.default.red("✗")} Could not reach PostHog: ${error instanceof Error ? error.message : error}`);
|
|
10221
|
-
return false;
|
|
10222
|
-
}
|
|
10223
|
-
}
|
|
10224
|
-
async function runVerify() {
|
|
10225
|
-
const token = await requireToken();
|
|
10226
|
-
if (!token) return;
|
|
10227
|
-
console.log();
|
|
10228
|
-
console.log(import_picocolors.default.dim(` POST ${POSTHOG_HOST}/flags?v=2 ${import_picocolors.default.dim("(does this token exist?)")}`));
|
|
10229
|
-
if (!await verifyToken(token)) {
|
|
10230
|
-
console.log();
|
|
10231
|
-
return;
|
|
10232
|
-
}
|
|
10233
|
-
const endpoint = `${POSTHOG_HOST}/i/v1/logs`;
|
|
10234
|
-
const body = { resourceLogs: [{
|
|
10235
|
-
resource: { attributes: [{
|
|
10236
|
-
key: "service.name",
|
|
10237
|
-
value: { stringValue: "claude-code" }
|
|
10238
|
-
}, {
|
|
10239
|
-
key: "pruff.rollout",
|
|
10240
|
-
value: { stringValue: "team" }
|
|
10241
|
-
}] },
|
|
10242
|
-
scopeLogs: [{ logRecords: [{
|
|
10243
|
-
timeUnixNano: `${Date.now() * 1e6}`,
|
|
10244
|
-
severityText: "INFO",
|
|
10245
|
-
body: { stringValue: "pruff_analytics_verify" },
|
|
10246
|
-
attributes: [{
|
|
10247
|
-
key: "pruff.verify",
|
|
10248
|
-
value: { boolValue: true }
|
|
10249
|
-
}]
|
|
10250
|
-
}] }]
|
|
10251
|
-
}] };
|
|
10252
|
-
console.log(import_picocolors.default.dim(` POST ${endpoint} ${import_picocolors.default.dim("(does the OTLP path accept our payload?)")}`));
|
|
10253
|
-
try {
|
|
10254
|
-
const res = await fetch(endpoint, {
|
|
10255
|
-
method: "POST",
|
|
10256
|
-
headers: {
|
|
10257
|
-
"Content-Type": "application/json",
|
|
10258
|
-
Authorization: `Bearer ${token}`
|
|
10259
|
-
},
|
|
10260
|
-
body: JSON.stringify(body)
|
|
10261
|
-
});
|
|
10262
|
-
if (res.ok) console.log(` ${import_picocolors.default.green("✓")} Payload accepted (HTTP ${res.status}).`);
|
|
10263
|
-
else {
|
|
10264
|
-
console.log(` ${import_picocolors.default.red("✗")} Rejected (HTTP ${res.status}).`);
|
|
10265
|
-
console.log(import_picocolors.default.dim(` ${(await res.text()).slice(0, 300)}`));
|
|
10266
|
-
console.log();
|
|
10267
|
-
return;
|
|
10268
|
-
}
|
|
10269
|
-
} catch (error) {
|
|
10270
|
-
console.log(` ${import_picocolors.default.red("✗")} Request failed: ${error instanceof Error ? error.message : error}`);
|
|
10271
|
-
console.log();
|
|
10272
|
-
return;
|
|
10273
|
-
}
|
|
10274
|
-
console.log();
|
|
10275
|
-
console.log(` ${import_picocolors.default.bold("Last step — confirm it landed:")}`);
|
|
10276
|
-
console.log(import_picocolors.default.dim(` PostHog → Logs, filter service = "claude-code", search "pruff_analytics_verify".`));
|
|
10277
|
-
console.log(import_picocolors.default.dim(" If the token check passed but nothing appears within a minute or two,"));
|
|
10278
|
-
console.log(import_picocolors.default.dim(" the project has log ingestion disabled or is over quota."));
|
|
10279
|
-
console.log();
|
|
10280
|
-
}
|
|
10281
|
-
function showAnalyticsHelp() {
|
|
10282
|
-
console.log(`
|
|
10283
|
-
${import_picocolors.default.bold("Usage:")} pruff-skills analytics <command>
|
|
10284
|
-
|
|
10285
|
-
status Show whether Claude Code is exporting to PostHog (default)
|
|
10286
|
-
enable [--with-tool-details] Turn the exporter on in ~/.claude/settings.json
|
|
10287
|
-
disable Turn the exporter off again
|
|
10288
|
-
verify Prove the PostHog ingest path works
|
|
10289
|
-
|
|
10290
|
-
${import_picocolors.default.bold("What is exported by default")}
|
|
10291
|
-
Token and cost usage, model, subagent vs main agent, tool names with their
|
|
10292
|
-
success and duration, MCP tool calls, session and prompt counts, your email.
|
|
10293
|
-
|
|
10294
|
-
${import_picocolors.default.bold("What is never exported")}
|
|
10295
|
-
Your prompts, Claude's responses, file contents and raw API bodies.
|
|
10296
|
-
|
|
10297
|
-
${import_picocolors.default.bold("--with-tool-details")}
|
|
10298
|
-
Unlocks real skill and slash-command names, which are otherwise anonymized to
|
|
10299
|
-
"custom_skill" / "custom". It ALSO exports every shell command and tool input
|
|
10300
|
-
verbatim — measured, not theoretical. Off by default: Pruff's own rule is that
|
|
10301
|
-
RUTs, SII credentials and API keys are never logged. MCP attribution does not
|
|
10302
|
-
need this flag; mcp_server.name and mcp_tool.name are exported either way.
|
|
10303
|
-
`);
|
|
10304
|
-
}
|
|
10305
|
-
async function runAnalytics(args = []) {
|
|
10306
|
-
const sub = args[0];
|
|
10307
|
-
switch (sub) {
|
|
10308
|
-
case void 0:
|
|
10309
|
-
case "status":
|
|
10310
|
-
await runStatus();
|
|
10311
|
-
break;
|
|
10312
|
-
case "enable":
|
|
10313
|
-
case "on":
|
|
10314
|
-
await runEnable(args.slice(1));
|
|
10315
|
-
break;
|
|
10316
|
-
case "disable":
|
|
10317
|
-
case "off":
|
|
10318
|
-
runDisable();
|
|
10319
|
-
break;
|
|
10320
|
-
case "verify":
|
|
10321
|
-
await runVerify();
|
|
10322
|
-
break;
|
|
10323
|
-
case "--help":
|
|
10324
|
-
case "-h":
|
|
10325
|
-
showAnalyticsHelp();
|
|
10326
|
-
break;
|
|
10327
|
-
default:
|
|
10328
|
-
console.log(`Unknown analytics command: ${sub}`);
|
|
10329
|
-
showAnalyticsHelp();
|
|
10330
|
-
}
|
|
10331
|
-
}
|
|
10332
10152
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
10333
10153
|
function getVersion() {
|
|
10334
10154
|
try {
|
|
@@ -10480,13 +10300,10 @@ ${BOLD}Hooks:${RESET}
|
|
|
10480
10300
|
hooks list Show installed and available hooks
|
|
10481
10301
|
hooks remove <name> Unregister a hook and delete its script
|
|
10482
10302
|
|
|
10483
|
-
${BOLD}Usage analytics:${RESET}
|
|
10484
|
-
analytics status
|
|
10485
|
-
analytics
|
|
10486
|
-
|
|
10487
|
-
analytics verify Prove the PostHog ingest path works before trusting it
|
|
10488
|
-
${DIM}Reports token/cost by skill, MCP tool, subagent and model.${RESET}
|
|
10489
|
-
${DIM}Never exports prompts, responses or file contents.${RESET}
|
|
10303
|
+
${BOLD}Usage analytics (retired):${RESET}
|
|
10304
|
+
analytics status Does Claude Code still report usage to Pruff's PostHog? (alias: telemetry)
|
|
10305
|
+
analytics disable Remove the exporter, leaving your own env keys untouched
|
|
10306
|
+
${DIM}\`update\` removes it too.${RESET}
|
|
10490
10307
|
|
|
10491
10308
|
${BOLD}Buzz nest:${RESET}
|
|
10492
10309
|
buzz setup <team> Provision a Buzz agent nest (~/.buzz): vault, team skills,
|
|
@@ -10736,9 +10553,7 @@ async function main() {
|
|
|
10736
10553
|
await runBuzz(restArgs);
|
|
10737
10554
|
break;
|
|
10738
10555
|
case "analytics":
|
|
10739
|
-
case "telemetry":
|
|
10740
|
-
await runAnalytics(restArgs);
|
|
10741
|
-
break;
|
|
10556
|
+
case "telemetry": return runAnalytics(restArgs);
|
|
10742
10557
|
case "onboarding":
|
|
10743
10558
|
case "onboard":
|
|
10744
10559
|
if (!inAgent) {
|