@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.
Files changed (2) hide show
  1. package/dist/index.js +162 -50
  2. 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 path13 from "path";
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 fs10 from "fs";
1709
+ import fs11 from "fs";
1592
1710
  import { minimatch } from "minimatch";
1593
1711
 
1594
1712
  // src/commands/refactor/getIgnoredFiles.ts
1595
- import fs9 from "fs";
1713
+ import fs10 from "fs";
1596
1714
  var REFACTOR_YML_PATH = "refactor.yml";
1597
1715
  function parseRefactorYml() {
1598
- if (!fs9.existsSync(REFACTOR_YML_PATH)) {
1716
+ if (!fs10.existsSync(REFACTOR_YML_PATH)) {
1599
1717
  return [];
1600
1718
  }
1601
- const content = fs9.readFileSync(REFACTOR_YML_PATH, "utf-8");
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 = fs10.readFileSync(filePath, "utf-8");
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 = path13.dirname(packageJsonPath);
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 = spawn("npm", ["run", script], {
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 fs11 from "fs";
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 (!fs11.existsSync(file)) {
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 = fs11.readFileSync(file, "utf-8");
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 (fs11.existsSync(REFACTOR_YML_PATH2)) {
1806
- const existing = fs11.readFileSync(REFACTOR_YML_PATH2, "utf-8");
1807
- fs11.writeFileSync(REFACTOR_YML_PATH2, existing + entry);
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
- fs11.writeFileSync(REFACTOR_YML_PATH2, entry);
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 spawn2 } from "child_process";
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 = spawn2(fullCommand, [], {
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 rl = readline.createInterface({
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 fs13 from "fs";
2021
+ import * as fs14 from "fs";
1913
2022
  import * as os from "os";
1914
- import * as path15 from "path";
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 fs12 from "fs";
1919
- import * as path14 from "path";
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 = path14.join(claudeDir, "settings.json");
1923
- const target = path14.join(targetBase, "settings.json");
1924
- const sourceContent = fs12.readFileSync(source, "utf-8");
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 (fs12.existsSync(target)) {
1927
- const targetContent = fs12.readFileSync(target, "utf-8");
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
- fs12.copyFileSync(source, target);
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 = path15.dirname(__filename2);
2060
+ var __dirname5 = path16.dirname(__filename2);
1952
2061
  async function sync() {
1953
- const claudeDir = path15.join(__dirname5, "..", "claude");
1954
- const targetBase = path15.join(os.homedir(), ".claude");
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 = path15.join(claudeDir, "commands");
1960
- const targetDir = path15.join(targetBase, "commands");
1961
- fs13.mkdirSync(targetDir, { recursive: true });
1962
- const files = fs13.readdirSync(sourceDir);
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
- fs13.copyFileSync(path15.join(sourceDir, file), path15.join(targetDir, file));
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 spawn3 } from "child_process";
2005
- import * as path16 from "path";
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 = path16.dirname(packageJsonPath);
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 = spawn3("npm", ["run", script], {
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.15.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",