@staff0rd/assist 0.14.0 → 0.16.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.
Files changed (2) hide show
  1. package/dist/index.js +162 -58
  2. package/package.json +4 -1
package/dist/index.js CHANGED
@@ -1227,14 +1227,8 @@ function createSettingsJson() {
1227
1227
  const settings = {
1228
1228
  "editor.defaultFormatter": "biomejs.biome",
1229
1229
  "editor.formatOnSave": true,
1230
- "[json]": {
1231
- "editor.defaultFormatter": "biomejs.biome"
1232
- },
1233
- "[typescript]": {
1234
- "editor.defaultFormatter": "biomejs.biome"
1235
- },
1236
- "[typescriptreact]": {
1237
- "editor.defaultFormatter": "biomejs.biome"
1230
+ "editor.codeActionsOnSave": {
1231
+ "source.organizeImports.biome": "explicit"
1238
1232
  }
1239
1233
  };
1240
1234
  const settingsPath = path9.join(process.cwd(), ".vscode", "settings.json");
@@ -1495,6 +1489,122 @@ function addViteBaseConfig() {
1495
1489
  }
1496
1490
  }
1497
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
+ }
1575
+ notifier.notify(notificationOptions);
1576
+ return true;
1577
+ }
1578
+
1579
+ // src/commands/notify/index.ts
1580
+ async function notify() {
1581
+ const inputData = await readStdin();
1582
+ const data = JSON.parse(inputData);
1583
+ const { notification_type, cwd, message } = data;
1584
+ const projectName = cwd?.split(/[/\\]/).pop() ?? "Unknown Project";
1585
+ let title;
1586
+ let body;
1587
+ let sound;
1588
+ switch (notification_type) {
1589
+ case "permission_prompt":
1590
+ title = "Claude needs permission";
1591
+ body = `${projectName} - ${message || "Permission required"}`;
1592
+ sound = "Alarm";
1593
+ break;
1594
+ case "idle_prompt":
1595
+ title = "Claude is waiting";
1596
+ body = `${projectName} - Waiting for your input`;
1597
+ sound = "Reminder";
1598
+ break;
1599
+ default:
1600
+ title = "Claude Code";
1601
+ body = message ? `${projectName} - ${message}` : projectName;
1602
+ sound = "Default";
1603
+ }
1604
+ showNotification({ title, message: body, sound });
1605
+ console.log(`Notification sent: ${notification_type} for ${projectName}`);
1606
+ }
1607
+
1498
1608
  // src/commands/prs.ts
1499
1609
  import { execSync as execSync10 } from "child_process";
1500
1610
  import chalk25 from "chalk";
@@ -1589,22 +1699,22 @@ Page ${page + 1} of ${totalPages} (${pullRequests.length} total)
1589
1699
  }
1590
1700
 
1591
1701
  // src/commands/refactor/check.ts
1592
- import { spawn } from "child_process";
1593
- import * as path13 from "path";
1702
+ import { spawn as spawn2 } from "child_process";
1703
+ import * as path14 from "path";
1594
1704
 
1595
1705
  // src/commands/refactor/getViolations.ts
1596
1706
  import { execSync as execSync11 } from "child_process";
1597
- import fs10 from "fs";
1707
+ import fs11 from "fs";
1598
1708
  import { minimatch } from "minimatch";
1599
1709
 
1600
1710
  // src/commands/refactor/getIgnoredFiles.ts
1601
- import fs9 from "fs";
1711
+ import fs10 from "fs";
1602
1712
  var REFACTOR_YML_PATH = "refactor.yml";
1603
1713
  function parseRefactorYml() {
1604
- if (!fs9.existsSync(REFACTOR_YML_PATH)) {
1714
+ if (!fs10.existsSync(REFACTOR_YML_PATH)) {
1605
1715
  return [];
1606
1716
  }
1607
- const content = fs9.readFileSync(REFACTOR_YML_PATH, "utf-8");
1717
+ const content = fs10.readFileSync(REFACTOR_YML_PATH, "utf-8");
1608
1718
  const entries = [];
1609
1719
  const lines = content.split("\n");
1610
1720
  let currentEntry = {};
@@ -1689,7 +1799,7 @@ Refactor check failed:
1689
1799
 
1690
1800
  // src/commands/refactor/getViolations.ts
1691
1801
  function countLines(filePath) {
1692
- const content = fs10.readFileSync(filePath, "utf-8");
1802
+ const content = fs11.readFileSync(filePath, "utf-8");
1693
1803
  return content.split("\n").length;
1694
1804
  }
1695
1805
  function getGitFiles(options) {
@@ -1741,12 +1851,12 @@ async function runVerifyQuietly() {
1741
1851
  return true;
1742
1852
  }
1743
1853
  const { packageJsonPath, verifyScripts } = result;
1744
- const packageDir = path13.dirname(packageJsonPath);
1854
+ const packageDir = path14.dirname(packageJsonPath);
1745
1855
  const results = await Promise.all(
1746
1856
  verifyScripts.map(
1747
1857
  (script) => new Promise(
1748
1858
  (resolve) => {
1749
- const child = spawn("npm", ["run", script], {
1859
+ const child = spawn2("npm", ["run", script], {
1750
1860
  stdio: "pipe",
1751
1861
  shell: true,
1752
1862
  cwd: packageDir
@@ -1794,25 +1904,25 @@ async function check(pattern2, options) {
1794
1904
  }
1795
1905
 
1796
1906
  // src/commands/refactor/ignore.ts
1797
- import fs11 from "fs";
1907
+ import fs12 from "fs";
1798
1908
  import chalk27 from "chalk";
1799
1909
  var REFACTOR_YML_PATH2 = "refactor.yml";
1800
1910
  function ignore(file) {
1801
- if (!fs11.existsSync(file)) {
1911
+ if (!fs12.existsSync(file)) {
1802
1912
  console.error(chalk27.red(`Error: File does not exist: ${file}`));
1803
1913
  process.exit(1);
1804
1914
  }
1805
- const content = fs11.readFileSync(file, "utf-8");
1915
+ const content = fs12.readFileSync(file, "utf-8");
1806
1916
  const lineCount = content.split("\n").length;
1807
1917
  const maxLines = lineCount + 10;
1808
1918
  const entry = `- file: ${file}
1809
1919
  maxLines: ${maxLines}
1810
1920
  `;
1811
- if (fs11.existsSync(REFACTOR_YML_PATH2)) {
1812
- const existing = fs11.readFileSync(REFACTOR_YML_PATH2, "utf-8");
1813
- fs11.writeFileSync(REFACTOR_YML_PATH2, existing + entry);
1921
+ if (fs12.existsSync(REFACTOR_YML_PATH2)) {
1922
+ const existing = fs12.readFileSync(REFACTOR_YML_PATH2, "utf-8");
1923
+ fs12.writeFileSync(REFACTOR_YML_PATH2, existing + entry);
1814
1924
  } else {
1815
- fs11.writeFileSync(REFACTOR_YML_PATH2, entry);
1925
+ fs12.writeFileSync(REFACTOR_YML_PATH2, entry);
1816
1926
  }
1817
1927
  console.log(
1818
1928
  chalk27.green(
@@ -1822,7 +1932,7 @@ function ignore(file) {
1822
1932
  }
1823
1933
 
1824
1934
  // src/commands/run.ts
1825
- import { spawn as spawn2 } from "child_process";
1935
+ import { spawn as spawn3 } from "child_process";
1826
1936
  function run(name, args) {
1827
1937
  const config = loadConfig();
1828
1938
  if (!config.run || config.run.length === 0) {
@@ -1844,7 +1954,7 @@ function run(name, args) {
1844
1954
  (arg) => arg.includes(" ") ? `"${arg}"` : arg
1845
1955
  );
1846
1956
  const fullCommand = [command, ...quotedArgs].join(" ");
1847
- const child = spawn2(fullCommand, [], {
1957
+ const child = spawn3(fullCommand, [], {
1848
1958
  stdio: "inherit",
1849
1959
  shell: true
1850
1960
  });
@@ -1888,20 +1998,11 @@ function add() {
1888
1998
  }
1889
1999
 
1890
2000
  // src/commands/statusLine.ts
1891
- import * as readline from "readline";
1892
2001
  function formatNumber(num) {
1893
2002
  return num.toLocaleString("en-US");
1894
2003
  }
1895
2004
  async function statusLine() {
1896
- const rl = readline.createInterface({
1897
- input: process.stdin,
1898
- output: process.stdout,
1899
- terminal: false
1900
- });
1901
- let inputData = "";
1902
- for await (const line of rl) {
1903
- inputData += line;
1904
- }
2005
+ const inputData = await readStdin();
1905
2006
  const data = JSON.parse(inputData);
1906
2007
  const model = data.model.display_name;
1907
2008
  const totalInput = data.context_window.total_input_tokens;
@@ -1915,22 +2016,22 @@ async function statusLine() {
1915
2016
  }
1916
2017
 
1917
2018
  // src/commands/sync.ts
1918
- import * as fs13 from "fs";
2019
+ import * as fs14 from "fs";
1919
2020
  import * as os from "os";
1920
- import * as path15 from "path";
2021
+ import * as path16 from "path";
1921
2022
  import { fileURLToPath as fileURLToPath4 } from "url";
1922
2023
 
1923
2024
  // src/commands/sync/syncSettings.ts
1924
- import * as fs12 from "fs";
1925
- import * as path14 from "path";
2025
+ import * as fs13 from "fs";
2026
+ import * as path15 from "path";
1926
2027
  import chalk28 from "chalk";
1927
2028
  async function syncSettings(claudeDir, targetBase) {
1928
- const source = path14.join(claudeDir, "settings.json");
1929
- const target = path14.join(targetBase, "settings.json");
1930
- const sourceContent = fs12.readFileSync(source, "utf-8");
2029
+ const source = path15.join(claudeDir, "settings.json");
2030
+ const target = path15.join(targetBase, "settings.json");
2031
+ const sourceContent = fs13.readFileSync(source, "utf-8");
1931
2032
  const normalizedSource = JSON.stringify(JSON.parse(sourceContent), null, 2);
1932
- if (fs12.existsSync(target)) {
1933
- const targetContent = fs12.readFileSync(target, "utf-8");
2033
+ if (fs13.existsSync(target)) {
2034
+ const targetContent = fs13.readFileSync(target, "utf-8");
1934
2035
  const normalizedTarget = JSON.stringify(JSON.parse(targetContent), null, 2);
1935
2036
  if (normalizedSource !== normalizedTarget) {
1936
2037
  console.log(
@@ -1948,26 +2049,26 @@ async function syncSettings(claudeDir, targetBase) {
1948
2049
  }
1949
2050
  }
1950
2051
  }
1951
- fs12.copyFileSync(source, target);
2052
+ fs13.copyFileSync(source, target);
1952
2053
  console.log("Copied settings.json to ~/.claude/settings.json");
1953
2054
  }
1954
2055
 
1955
2056
  // src/commands/sync.ts
1956
2057
  var __filename2 = fileURLToPath4(import.meta.url);
1957
- var __dirname5 = path15.dirname(__filename2);
2058
+ var __dirname5 = path16.dirname(__filename2);
1958
2059
  async function sync() {
1959
- const claudeDir = path15.join(__dirname5, "..", "claude");
1960
- const targetBase = path15.join(os.homedir(), ".claude");
2060
+ const claudeDir = path16.join(__dirname5, "..", "claude");
2061
+ const targetBase = path16.join(os.homedir(), ".claude");
1961
2062
  syncCommands(claudeDir, targetBase);
1962
2063
  await syncSettings(claudeDir, targetBase);
1963
2064
  }
1964
2065
  function syncCommands(claudeDir, targetBase) {
1965
- const sourceDir = path15.join(claudeDir, "commands");
1966
- const targetDir = path15.join(targetBase, "commands");
1967
- fs13.mkdirSync(targetDir, { recursive: true });
1968
- const files = fs13.readdirSync(sourceDir);
2066
+ const sourceDir = path16.join(claudeDir, "commands");
2067
+ const targetDir = path16.join(targetBase, "commands");
2068
+ fs14.mkdirSync(targetDir, { recursive: true });
2069
+ const files = fs14.readdirSync(sourceDir);
1969
2070
  for (const file of files) {
1970
- fs13.copyFileSync(path15.join(sourceDir, file), path15.join(targetDir, file));
2071
+ fs14.copyFileSync(path16.join(sourceDir, file), path16.join(targetDir, file));
1971
2072
  console.log(`Copied ${file} to ${targetDir}`);
1972
2073
  }
1973
2074
  console.log(`Synced ${files.length} command(s) to ~/.claude/commands`);
@@ -2007,8 +2108,8 @@ Total: ${lines.length} hardcoded color(s)`);
2007
2108
  }
2008
2109
 
2009
2110
  // src/commands/verify/run.ts
2010
- import { spawn as spawn3 } from "child_process";
2011
- import * as path16 from "path";
2111
+ import { spawn as spawn4 } from "child_process";
2112
+ import * as path17 from "path";
2012
2113
  function formatDuration(ms) {
2013
2114
  if (ms < 1e3) {
2014
2115
  return `${ms}ms`;
@@ -2038,7 +2139,7 @@ async function run2(options = {}) {
2038
2139
  return;
2039
2140
  }
2040
2141
  const { packageJsonPath, verifyScripts } = result;
2041
- const packageDir = path16.dirname(packageJsonPath);
2142
+ const packageDir = path17.dirname(packageJsonPath);
2042
2143
  console.log(`Running ${verifyScripts.length} verify script(s) in parallel:`);
2043
2144
  for (const script of verifyScripts) {
2044
2145
  console.log(` - ${script}`);
@@ -2050,7 +2151,7 @@ async function run2(options = {}) {
2050
2151
  const results = await Promise.all(
2051
2152
  verifyScripts.map(
2052
2153
  (script, index) => new Promise((resolve) => {
2053
- const child = spawn3("npm", ["run", script], {
2154
+ const child = spawn4("npm", ["run", script], {
2054
2155
  stdio: "inherit",
2055
2156
  shell: true,
2056
2157
  cwd: packageDir
@@ -2124,4 +2225,7 @@ deployCommand.command("init").description("Initialize Netlify project and config
2124
2225
  deployCommand.command("redirect").description("Add trailing slash redirect script to index.html").action(redirect);
2125
2226
  program.command("enable-ralph").description("Enable ralph-wiggum plugin for spacetraders").action(enableRalph);
2126
2227
  program.command("status-line").description("Format Claude Code status line from JSON stdin").action(statusLine);
2228
+ program.command("notify").description(
2229
+ "Show notification from Claude Code hook (reads JSON from stdin)"
2230
+ ).action(notify);
2127
2231
  program.parse();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@staff0rd/assist",
3
- "version": "0.14.0",
3
+ "version": "0.16.0",
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",