@staff0rd/assist 0.15.0 → 0.16.1
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 +162 -50
- package/package.json +4 -1
package/dist/index.js
CHANGED
|
@@ -1489,6 +1489,124 @@ function addViteBaseConfig() {
|
|
|
1489
1489
|
}
|
|
1490
1490
|
}
|
|
1491
1491
|
|
|
1492
|
+
// src/lib/readStdin.ts
|
|
1493
|
+
import * as readline from "readline";
|
|
1494
|
+
async function readStdin() {
|
|
1495
|
+
const rl = readline.createInterface({
|
|
1496
|
+
input: process.stdin,
|
|
1497
|
+
output: process.stdout,
|
|
1498
|
+
terminal: false
|
|
1499
|
+
});
|
|
1500
|
+
let inputData = "";
|
|
1501
|
+
for await (const line of rl) {
|
|
1502
|
+
inputData += line;
|
|
1503
|
+
}
|
|
1504
|
+
return inputData;
|
|
1505
|
+
}
|
|
1506
|
+
|
|
1507
|
+
// src/commands/notify/showNotification.ts
|
|
1508
|
+
import notifier from "node-notifier";
|
|
1509
|
+
|
|
1510
|
+
// src/lib/detectPlatform.ts
|
|
1511
|
+
import isWsl from "is-wsl";
|
|
1512
|
+
function detectPlatform() {
|
|
1513
|
+
if (isWsl) return "wsl";
|
|
1514
|
+
switch (process.platform) {
|
|
1515
|
+
case "win32":
|
|
1516
|
+
return "windows";
|
|
1517
|
+
case "darwin":
|
|
1518
|
+
return "macos";
|
|
1519
|
+
case "linux":
|
|
1520
|
+
return "linux";
|
|
1521
|
+
default:
|
|
1522
|
+
return "unknown";
|
|
1523
|
+
}
|
|
1524
|
+
}
|
|
1525
|
+
|
|
1526
|
+
// src/commands/notify/showWindowsNotificationFromWsl.ts
|
|
1527
|
+
import { spawn } from "child_process";
|
|
1528
|
+
import fs9 from "fs";
|
|
1529
|
+
import { createRequire } from "module";
|
|
1530
|
+
import path13 from "path";
|
|
1531
|
+
var require2 = createRequire(import.meta.url);
|
|
1532
|
+
function getSnoreToastPath() {
|
|
1533
|
+
const notifierPath = path13.dirname(require2.resolve("node-notifier"));
|
|
1534
|
+
return path13.join(notifierPath, "vendor", "snoreToast", "snoretoast-x64.exe");
|
|
1535
|
+
}
|
|
1536
|
+
function showWindowsNotificationFromWsl(options) {
|
|
1537
|
+
const { title, message, sound } = options;
|
|
1538
|
+
const snoreToastPath = getSnoreToastPath();
|
|
1539
|
+
try {
|
|
1540
|
+
fs9.chmodSync(snoreToastPath, 493);
|
|
1541
|
+
} catch {
|
|
1542
|
+
}
|
|
1543
|
+
const args = ["-t", title, "-m", message];
|
|
1544
|
+
if (sound === "Alarm") {
|
|
1545
|
+
args.push("-s", "ms-winsoundevent:Notification.Looping.Alarm");
|
|
1546
|
+
} else if (sound === "Reminder") {
|
|
1547
|
+
args.push("-s", "ms-winsoundevent:Notification.Reminder");
|
|
1548
|
+
}
|
|
1549
|
+
const child = spawn(snoreToastPath, args, {
|
|
1550
|
+
detached: true,
|
|
1551
|
+
stdio: "ignore"
|
|
1552
|
+
});
|
|
1553
|
+
child.unref();
|
|
1554
|
+
return true;
|
|
1555
|
+
}
|
|
1556
|
+
|
|
1557
|
+
// src/commands/notify/showNotification.ts
|
|
1558
|
+
function showNotification(options) {
|
|
1559
|
+
const { title, message, sound } = options;
|
|
1560
|
+
const platform = detectPlatform();
|
|
1561
|
+
if (platform === "wsl") {
|
|
1562
|
+
return showWindowsNotificationFromWsl({ title, message, sound });
|
|
1563
|
+
}
|
|
1564
|
+
const notificationOptions = {
|
|
1565
|
+
title,
|
|
1566
|
+
message,
|
|
1567
|
+
wait: false
|
|
1568
|
+
};
|
|
1569
|
+
if (platform === "windows") {
|
|
1570
|
+
notificationOptions.appID = "Claude Code";
|
|
1571
|
+
}
|
|
1572
|
+
if (platform === "macos") {
|
|
1573
|
+
notificationOptions.sound = sound === "Alarm" ? "Basso" : "Submarine";
|
|
1574
|
+
notificationOptions.timeout = 99999999999;
|
|
1575
|
+
notificationOptions.wait = true;
|
|
1576
|
+
}
|
|
1577
|
+
notifier.notify(notificationOptions);
|
|
1578
|
+
return true;
|
|
1579
|
+
}
|
|
1580
|
+
|
|
1581
|
+
// src/commands/notify/index.ts
|
|
1582
|
+
async function notify() {
|
|
1583
|
+
const inputData = await readStdin();
|
|
1584
|
+
const data = JSON.parse(inputData);
|
|
1585
|
+
const { notification_type, cwd, message } = data;
|
|
1586
|
+
const projectName = cwd?.split(/[/\\]/).pop() ?? "Unknown Project";
|
|
1587
|
+
let title;
|
|
1588
|
+
let body;
|
|
1589
|
+
let sound;
|
|
1590
|
+
switch (notification_type) {
|
|
1591
|
+
case "permission_prompt":
|
|
1592
|
+
title = "Claude needs permission";
|
|
1593
|
+
body = `${projectName} - ${message || "Permission required"}`;
|
|
1594
|
+
sound = "Alarm";
|
|
1595
|
+
break;
|
|
1596
|
+
case "idle_prompt":
|
|
1597
|
+
title = "Claude is waiting";
|
|
1598
|
+
body = `${projectName} - Waiting for your input`;
|
|
1599
|
+
sound = "Reminder";
|
|
1600
|
+
break;
|
|
1601
|
+
default:
|
|
1602
|
+
title = "Claude Code";
|
|
1603
|
+
body = message ? `${projectName} - ${message}` : projectName;
|
|
1604
|
+
sound = "Default";
|
|
1605
|
+
}
|
|
1606
|
+
showNotification({ title, message: body, sound });
|
|
1607
|
+
console.log(`Notification sent: ${notification_type} for ${projectName}`);
|
|
1608
|
+
}
|
|
1609
|
+
|
|
1492
1610
|
// src/commands/prs.ts
|
|
1493
1611
|
import { execSync as execSync10 } from "child_process";
|
|
1494
1612
|
import chalk25 from "chalk";
|
|
@@ -1583,22 +1701,22 @@ Page ${page + 1} of ${totalPages} (${pullRequests.length} total)
|
|
|
1583
1701
|
}
|
|
1584
1702
|
|
|
1585
1703
|
// src/commands/refactor/check.ts
|
|
1586
|
-
import { spawn } from "child_process";
|
|
1587
|
-
import * as
|
|
1704
|
+
import { spawn as spawn2 } from "child_process";
|
|
1705
|
+
import * as path14 from "path";
|
|
1588
1706
|
|
|
1589
1707
|
// src/commands/refactor/getViolations.ts
|
|
1590
1708
|
import { execSync as execSync11 } from "child_process";
|
|
1591
|
-
import
|
|
1709
|
+
import fs11 from "fs";
|
|
1592
1710
|
import { minimatch } from "minimatch";
|
|
1593
1711
|
|
|
1594
1712
|
// src/commands/refactor/getIgnoredFiles.ts
|
|
1595
|
-
import
|
|
1713
|
+
import fs10 from "fs";
|
|
1596
1714
|
var REFACTOR_YML_PATH = "refactor.yml";
|
|
1597
1715
|
function parseRefactorYml() {
|
|
1598
|
-
if (!
|
|
1716
|
+
if (!fs10.existsSync(REFACTOR_YML_PATH)) {
|
|
1599
1717
|
return [];
|
|
1600
1718
|
}
|
|
1601
|
-
const content =
|
|
1719
|
+
const content = fs10.readFileSync(REFACTOR_YML_PATH, "utf-8");
|
|
1602
1720
|
const entries = [];
|
|
1603
1721
|
const lines = content.split("\n");
|
|
1604
1722
|
let currentEntry = {};
|
|
@@ -1683,7 +1801,7 @@ Refactor check failed:
|
|
|
1683
1801
|
|
|
1684
1802
|
// src/commands/refactor/getViolations.ts
|
|
1685
1803
|
function countLines(filePath) {
|
|
1686
|
-
const content =
|
|
1804
|
+
const content = fs11.readFileSync(filePath, "utf-8");
|
|
1687
1805
|
return content.split("\n").length;
|
|
1688
1806
|
}
|
|
1689
1807
|
function getGitFiles(options) {
|
|
@@ -1735,12 +1853,12 @@ async function runVerifyQuietly() {
|
|
|
1735
1853
|
return true;
|
|
1736
1854
|
}
|
|
1737
1855
|
const { packageJsonPath, verifyScripts } = result;
|
|
1738
|
-
const packageDir =
|
|
1856
|
+
const packageDir = path14.dirname(packageJsonPath);
|
|
1739
1857
|
const results = await Promise.all(
|
|
1740
1858
|
verifyScripts.map(
|
|
1741
1859
|
(script) => new Promise(
|
|
1742
1860
|
(resolve) => {
|
|
1743
|
-
const child =
|
|
1861
|
+
const child = spawn2("npm", ["run", script], {
|
|
1744
1862
|
stdio: "pipe",
|
|
1745
1863
|
shell: true,
|
|
1746
1864
|
cwd: packageDir
|
|
@@ -1788,25 +1906,25 @@ async function check(pattern2, options) {
|
|
|
1788
1906
|
}
|
|
1789
1907
|
|
|
1790
1908
|
// src/commands/refactor/ignore.ts
|
|
1791
|
-
import
|
|
1909
|
+
import fs12 from "fs";
|
|
1792
1910
|
import chalk27 from "chalk";
|
|
1793
1911
|
var REFACTOR_YML_PATH2 = "refactor.yml";
|
|
1794
1912
|
function ignore(file) {
|
|
1795
|
-
if (!
|
|
1913
|
+
if (!fs12.existsSync(file)) {
|
|
1796
1914
|
console.error(chalk27.red(`Error: File does not exist: ${file}`));
|
|
1797
1915
|
process.exit(1);
|
|
1798
1916
|
}
|
|
1799
|
-
const content =
|
|
1917
|
+
const content = fs12.readFileSync(file, "utf-8");
|
|
1800
1918
|
const lineCount = content.split("\n").length;
|
|
1801
1919
|
const maxLines = lineCount + 10;
|
|
1802
1920
|
const entry = `- file: ${file}
|
|
1803
1921
|
maxLines: ${maxLines}
|
|
1804
1922
|
`;
|
|
1805
|
-
if (
|
|
1806
|
-
const existing =
|
|
1807
|
-
|
|
1923
|
+
if (fs12.existsSync(REFACTOR_YML_PATH2)) {
|
|
1924
|
+
const existing = fs12.readFileSync(REFACTOR_YML_PATH2, "utf-8");
|
|
1925
|
+
fs12.writeFileSync(REFACTOR_YML_PATH2, existing + entry);
|
|
1808
1926
|
} else {
|
|
1809
|
-
|
|
1927
|
+
fs12.writeFileSync(REFACTOR_YML_PATH2, entry);
|
|
1810
1928
|
}
|
|
1811
1929
|
console.log(
|
|
1812
1930
|
chalk27.green(
|
|
@@ -1816,7 +1934,7 @@ function ignore(file) {
|
|
|
1816
1934
|
}
|
|
1817
1935
|
|
|
1818
1936
|
// src/commands/run.ts
|
|
1819
|
-
import { spawn as
|
|
1937
|
+
import { spawn as spawn3 } from "child_process";
|
|
1820
1938
|
function run(name, args) {
|
|
1821
1939
|
const config = loadConfig();
|
|
1822
1940
|
if (!config.run || config.run.length === 0) {
|
|
@@ -1838,7 +1956,7 @@ function run(name, args) {
|
|
|
1838
1956
|
(arg) => arg.includes(" ") ? `"${arg}"` : arg
|
|
1839
1957
|
);
|
|
1840
1958
|
const fullCommand = [command, ...quotedArgs].join(" ");
|
|
1841
|
-
const child =
|
|
1959
|
+
const child = spawn3(fullCommand, [], {
|
|
1842
1960
|
stdio: "inherit",
|
|
1843
1961
|
shell: true
|
|
1844
1962
|
});
|
|
@@ -1882,20 +2000,11 @@ function add() {
|
|
|
1882
2000
|
}
|
|
1883
2001
|
|
|
1884
2002
|
// src/commands/statusLine.ts
|
|
1885
|
-
import * as readline from "readline";
|
|
1886
2003
|
function formatNumber(num) {
|
|
1887
2004
|
return num.toLocaleString("en-US");
|
|
1888
2005
|
}
|
|
1889
2006
|
async function statusLine() {
|
|
1890
|
-
const
|
|
1891
|
-
input: process.stdin,
|
|
1892
|
-
output: process.stdout,
|
|
1893
|
-
terminal: false
|
|
1894
|
-
});
|
|
1895
|
-
let inputData = "";
|
|
1896
|
-
for await (const line of rl) {
|
|
1897
|
-
inputData += line;
|
|
1898
|
-
}
|
|
2007
|
+
const inputData = await readStdin();
|
|
1899
2008
|
const data = JSON.parse(inputData);
|
|
1900
2009
|
const model = data.model.display_name;
|
|
1901
2010
|
const totalInput = data.context_window.total_input_tokens;
|
|
@@ -1909,22 +2018,22 @@ async function statusLine() {
|
|
|
1909
2018
|
}
|
|
1910
2019
|
|
|
1911
2020
|
// src/commands/sync.ts
|
|
1912
|
-
import * as
|
|
2021
|
+
import * as fs14 from "fs";
|
|
1913
2022
|
import * as os from "os";
|
|
1914
|
-
import * as
|
|
2023
|
+
import * as path16 from "path";
|
|
1915
2024
|
import { fileURLToPath as fileURLToPath4 } from "url";
|
|
1916
2025
|
|
|
1917
2026
|
// src/commands/sync/syncSettings.ts
|
|
1918
|
-
import * as
|
|
1919
|
-
import * as
|
|
2027
|
+
import * as fs13 from "fs";
|
|
2028
|
+
import * as path15 from "path";
|
|
1920
2029
|
import chalk28 from "chalk";
|
|
1921
2030
|
async function syncSettings(claudeDir, targetBase) {
|
|
1922
|
-
const source =
|
|
1923
|
-
const target =
|
|
1924
|
-
const sourceContent =
|
|
2031
|
+
const source = path15.join(claudeDir, "settings.json");
|
|
2032
|
+
const target = path15.join(targetBase, "settings.json");
|
|
2033
|
+
const sourceContent = fs13.readFileSync(source, "utf-8");
|
|
1925
2034
|
const normalizedSource = JSON.stringify(JSON.parse(sourceContent), null, 2);
|
|
1926
|
-
if (
|
|
1927
|
-
const targetContent =
|
|
2035
|
+
if (fs13.existsSync(target)) {
|
|
2036
|
+
const targetContent = fs13.readFileSync(target, "utf-8");
|
|
1928
2037
|
const normalizedTarget = JSON.stringify(JSON.parse(targetContent), null, 2);
|
|
1929
2038
|
if (normalizedSource !== normalizedTarget) {
|
|
1930
2039
|
console.log(
|
|
@@ -1942,26 +2051,26 @@ async function syncSettings(claudeDir, targetBase) {
|
|
|
1942
2051
|
}
|
|
1943
2052
|
}
|
|
1944
2053
|
}
|
|
1945
|
-
|
|
2054
|
+
fs13.copyFileSync(source, target);
|
|
1946
2055
|
console.log("Copied settings.json to ~/.claude/settings.json");
|
|
1947
2056
|
}
|
|
1948
2057
|
|
|
1949
2058
|
// src/commands/sync.ts
|
|
1950
2059
|
var __filename2 = fileURLToPath4(import.meta.url);
|
|
1951
|
-
var __dirname5 =
|
|
2060
|
+
var __dirname5 = path16.dirname(__filename2);
|
|
1952
2061
|
async function sync() {
|
|
1953
|
-
const claudeDir =
|
|
1954
|
-
const targetBase =
|
|
2062
|
+
const claudeDir = path16.join(__dirname5, "..", "claude");
|
|
2063
|
+
const targetBase = path16.join(os.homedir(), ".claude");
|
|
1955
2064
|
syncCommands(claudeDir, targetBase);
|
|
1956
2065
|
await syncSettings(claudeDir, targetBase);
|
|
1957
2066
|
}
|
|
1958
2067
|
function syncCommands(claudeDir, targetBase) {
|
|
1959
|
-
const sourceDir =
|
|
1960
|
-
const targetDir =
|
|
1961
|
-
|
|
1962
|
-
const files =
|
|
2068
|
+
const sourceDir = path16.join(claudeDir, "commands");
|
|
2069
|
+
const targetDir = path16.join(targetBase, "commands");
|
|
2070
|
+
fs14.mkdirSync(targetDir, { recursive: true });
|
|
2071
|
+
const files = fs14.readdirSync(sourceDir);
|
|
1963
2072
|
for (const file of files) {
|
|
1964
|
-
|
|
2073
|
+
fs14.copyFileSync(path16.join(sourceDir, file), path16.join(targetDir, file));
|
|
1965
2074
|
console.log(`Copied ${file} to ${targetDir}`);
|
|
1966
2075
|
}
|
|
1967
2076
|
console.log(`Synced ${files.length} command(s) to ~/.claude/commands`);
|
|
@@ -2001,8 +2110,8 @@ Total: ${lines.length} hardcoded color(s)`);
|
|
|
2001
2110
|
}
|
|
2002
2111
|
|
|
2003
2112
|
// src/commands/verify/run.ts
|
|
2004
|
-
import { spawn as
|
|
2005
|
-
import * as
|
|
2113
|
+
import { spawn as spawn4 } from "child_process";
|
|
2114
|
+
import * as path17 from "path";
|
|
2006
2115
|
function formatDuration(ms) {
|
|
2007
2116
|
if (ms < 1e3) {
|
|
2008
2117
|
return `${ms}ms`;
|
|
@@ -2032,7 +2141,7 @@ async function run2(options = {}) {
|
|
|
2032
2141
|
return;
|
|
2033
2142
|
}
|
|
2034
2143
|
const { packageJsonPath, verifyScripts } = result;
|
|
2035
|
-
const packageDir =
|
|
2144
|
+
const packageDir = path17.dirname(packageJsonPath);
|
|
2036
2145
|
console.log(`Running ${verifyScripts.length} verify script(s) in parallel:`);
|
|
2037
2146
|
for (const script of verifyScripts) {
|
|
2038
2147
|
console.log(` - ${script}`);
|
|
@@ -2044,7 +2153,7 @@ async function run2(options = {}) {
|
|
|
2044
2153
|
const results = await Promise.all(
|
|
2045
2154
|
verifyScripts.map(
|
|
2046
2155
|
(script, index) => new Promise((resolve) => {
|
|
2047
|
-
const child =
|
|
2156
|
+
const child = spawn4("npm", ["run", script], {
|
|
2048
2157
|
stdio: "inherit",
|
|
2049
2158
|
shell: true,
|
|
2050
2159
|
cwd: packageDir
|
|
@@ -2118,4 +2227,7 @@ deployCommand.command("init").description("Initialize Netlify project and config
|
|
|
2118
2227
|
deployCommand.command("redirect").description("Add trailing slash redirect script to index.html").action(redirect);
|
|
2119
2228
|
program.command("enable-ralph").description("Enable ralph-wiggum plugin for spacetraders").action(enableRalph);
|
|
2120
2229
|
program.command("status-line").description("Format Claude Code status line from JSON stdin").action(statusLine);
|
|
2230
|
+
program.command("notify").description(
|
|
2231
|
+
"Show notification from Claude Code hook (reads JSON from stdin)"
|
|
2232
|
+
).action(notify);
|
|
2121
2233
|
program.parse();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@staff0rd/assist",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.16.1",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"bin": {
|
|
@@ -34,7 +34,9 @@
|
|
|
34
34
|
"commander": "^14.0.2",
|
|
35
35
|
"diff": "^8.0.2",
|
|
36
36
|
"enquirer": "^2.4.1",
|
|
37
|
+
"is-wsl": "^3.1.0",
|
|
37
38
|
"minimatch": "^10.1.1",
|
|
39
|
+
"node-notifier": "^10.0.1",
|
|
38
40
|
"semver": "^7.7.3",
|
|
39
41
|
"yaml": "^2.8.2"
|
|
40
42
|
},
|
|
@@ -43,6 +45,7 @@
|
|
|
43
45
|
"@semantic-release/changelog": "^6.0.3",
|
|
44
46
|
"@semantic-release/git": "^10.0.1",
|
|
45
47
|
"@types/node": "^24.10.1",
|
|
48
|
+
"@types/node-notifier": "^8.0.5",
|
|
46
49
|
"@types/semver": "^7.7.1",
|
|
47
50
|
"jscpd": "^4.0.5",
|
|
48
51
|
"knip": "^5.71.0",
|