maxsimcli 5.0.7 → 5.1.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 (91) hide show
  1. package/README.md +101 -99
  2. package/dist/assets/CHANGELOG.md +7 -0
  3. package/dist/assets/hooks/maxsim-capture-learnings.cjs +128 -0
  4. package/dist/assets/hooks/maxsim-capture-learnings.cjs.map +1 -0
  5. package/dist/assets/hooks/maxsim-check-update.cjs +126 -88
  6. package/dist/assets/hooks/maxsim-check-update.cjs.map +1 -1
  7. package/dist/assets/hooks/maxsim-notification-sound.cjs +87 -43
  8. package/dist/assets/hooks/maxsim-notification-sound.cjs.map +1 -1
  9. package/dist/assets/hooks/maxsim-statusline.cjs +45 -171
  10. package/dist/assets/hooks/maxsim-statusline.cjs.map +1 -1
  11. package/dist/assets/hooks/maxsim-stop-sound.cjs +86 -43
  12. package/dist/assets/hooks/maxsim-stop-sound.cjs.map +1 -1
  13. package/dist/assets/hooks/maxsim-sync-reminder.cjs +72 -21
  14. package/dist/assets/hooks/maxsim-sync-reminder.cjs.map +1 -1
  15. package/dist/assets/templates/agents/AGENTS.md +62 -51
  16. package/dist/assets/templates/agents/executor.md +44 -59
  17. package/dist/assets/templates/agents/planner.md +36 -31
  18. package/dist/assets/templates/agents/researcher.md +35 -43
  19. package/dist/assets/templates/agents/verifier.md +29 -31
  20. package/dist/assets/templates/commands/maxsim/debug.md +20 -154
  21. package/dist/assets/templates/commands/maxsim/execute.md +19 -33
  22. package/dist/assets/templates/commands/maxsim/go.md +21 -20
  23. package/dist/assets/templates/commands/maxsim/help.md +5 -14
  24. package/dist/assets/templates/commands/maxsim/init.md +18 -40
  25. package/dist/assets/templates/commands/maxsim/plan.md +22 -37
  26. package/dist/assets/templates/commands/maxsim/progress.md +15 -16
  27. package/dist/assets/templates/commands/maxsim/quick.md +18 -29
  28. package/dist/assets/templates/commands/maxsim/settings.md +18 -26
  29. package/dist/assets/templates/references/continuation-format.md +2 -4
  30. package/dist/assets/templates/references/model-profiles.md +2 -2
  31. package/dist/assets/templates/references/planning-config.md +10 -11
  32. package/dist/assets/templates/references/self-improvement.md +120 -0
  33. package/dist/assets/templates/rules/conventions.md +1 -1
  34. package/dist/assets/templates/rules/verification-protocol.md +1 -1
  35. package/dist/assets/templates/skills/brainstorming/SKILL.md +35 -26
  36. package/dist/assets/templates/skills/code-review/SKILL.md +78 -55
  37. package/dist/assets/templates/skills/commit-conventions/SKILL.md +70 -36
  38. package/dist/assets/templates/skills/github-operations/SKILL.md +142 -0
  39. package/dist/assets/templates/skills/handoff-contract/SKILL.md +62 -28
  40. package/dist/assets/templates/skills/maxsim-batch/SKILL.md +68 -42
  41. package/dist/assets/templates/skills/maxsim-simplify/SKILL.md +65 -40
  42. package/dist/assets/templates/skills/project-memory/SKILL.md +121 -0
  43. package/dist/assets/templates/skills/research/SKILL.md +126 -0
  44. package/dist/assets/templates/skills/roadmap-writing/SKILL.md +71 -68
  45. package/dist/assets/templates/skills/systematic-debugging/SKILL.md +37 -25
  46. package/dist/assets/templates/skills/tdd/SKILL.md +36 -39
  47. package/dist/assets/templates/skills/using-maxsim/SKILL.md +69 -55
  48. package/dist/assets/templates/skills/verification/SKILL.md +167 -0
  49. package/dist/assets/templates/workflows/batch.md +249 -268
  50. package/dist/assets/templates/workflows/diagnose-issues.md +225 -151
  51. package/dist/assets/templates/workflows/execute-plan.md +191 -981
  52. package/dist/assets/templates/workflows/execute.md +350 -309
  53. package/dist/assets/templates/workflows/go.md +119 -138
  54. package/dist/assets/templates/workflows/health.md +71 -114
  55. package/dist/assets/templates/workflows/help.md +85 -147
  56. package/dist/assets/templates/workflows/init-existing.md +180 -1373
  57. package/dist/assets/templates/workflows/init.md +53 -165
  58. package/dist/assets/templates/workflows/new-milestone.md +91 -334
  59. package/dist/assets/templates/workflows/new-project.md +165 -1384
  60. package/dist/assets/templates/workflows/plan-create.md +182 -73
  61. package/dist/assets/templates/workflows/plan-discuss.md +89 -82
  62. package/dist/assets/templates/workflows/plan-research.md +191 -85
  63. package/dist/assets/templates/workflows/plan.md +122 -58
  64. package/dist/assets/templates/workflows/progress.md +76 -310
  65. package/dist/assets/templates/workflows/quick.md +70 -495
  66. package/dist/assets/templates/workflows/sdd.md +231 -221
  67. package/dist/assets/templates/workflows/settings.md +90 -120
  68. package/dist/assets/templates/workflows/verify-phase.md +296 -258
  69. package/dist/cli.cjs +17 -23465
  70. package/dist/cli.cjs.map +1 -1
  71. package/dist/install.cjs +356 -8358
  72. package/dist/install.cjs.map +1 -1
  73. package/package.json +16 -22
  74. package/dist/assets/templates/skills/agent-system-map/SKILL.md +0 -92
  75. package/dist/assets/templates/skills/evidence-collection/SKILL.md +0 -87
  76. package/dist/assets/templates/skills/github-artifact-protocol/SKILL.md +0 -67
  77. package/dist/assets/templates/skills/github-tools-guide/SKILL.md +0 -89
  78. package/dist/assets/templates/skills/input-validation/SKILL.md +0 -51
  79. package/dist/assets/templates/skills/memory-management/SKILL.md +0 -75
  80. package/dist/assets/templates/skills/research-methodology/SKILL.md +0 -137
  81. package/dist/assets/templates/skills/sdd/SKILL.md +0 -91
  82. package/dist/assets/templates/skills/tool-priority-guide/SKILL.md +0 -80
  83. package/dist/assets/templates/skills/verification-before-completion/SKILL.md +0 -71
  84. package/dist/assets/templates/skills/verification-gates/SKILL.md +0 -169
  85. package/dist/assets/templates/workflows/discuss-phase.md +0 -683
  86. package/dist/assets/templates/workflows/research-phase.md +0 -73
  87. package/dist/assets/templates/workflows/verify-work.md +0 -572
  88. package/dist/core-D5zUr9cb.cjs +0 -4305
  89. package/dist/core-D5zUr9cb.cjs.map +0 -1
  90. package/dist/skills-CjFWZIGM.cjs +0 -6824
  91. package/dist/skills-CjFWZIGM.cjs.map +0 -1
@@ -1,5 +1,3 @@
1
- #!/usr/bin/env node
2
- Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
3
1
  //#region \0rolldown/runtime.js
4
2
  var __create = Object.create;
5
3
  var __defProp = Object.defineProperty;
@@ -29,110 +27,150 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
29
27
  //#endregion
30
28
  let node_fs = require("node:fs");
31
29
  node_fs = __toESM(node_fs);
32
- let node_path = require("node:path");
33
- node_path = __toESM(node_path);
34
30
  let node_os = require("node:os");
35
31
  node_os = __toESM(node_os);
32
+ let node_path = require("node:path");
33
+ node_path = __toESM(node_path);
36
34
  let node_child_process = require("node:child_process");
37
35
 
38
36
  //#region src/hooks/shared.ts
39
- /** The '.claude' path segment -- template marker replaced during install. */
40
- const CLAUDE_DIR = ".claude";
37
+ /** Shared utilities for MAXSIM hooks. */
38
+ function readStdinJson(callback) {
39
+ let input = "";
40
+ process.stdin.setEncoding("utf8");
41
+ process.stdin.on("data", (chunk) => {
42
+ input += chunk;
43
+ });
44
+ process.stdin.on("end", () => {
45
+ try {
46
+ callback(JSON.parse(input));
47
+ } catch {
48
+ process.exit(0);
49
+ }
50
+ });
51
+ }
41
52
 
42
53
  //#endregion
43
54
  //#region src/hooks/maxsim-check-update.ts
44
55
  /**
45
- * Check for MAXSIM updates in background, write result to cache.
46
- * Called by SessionStart hook - runs once per session.
47
- */
48
- function checkForUpdate(options) {
49
- const { homeDir, cwd } = options;
50
- const cacheDir = node_path.join(homeDir, CLAUDE_DIR, "cache");
51
- const cacheFile = node_path.join(cacheDir, "maxsim-update-check.json");
52
- const projectVersionFile = node_path.join(cwd, CLAUDE_DIR, "maxsim", "VERSION");
53
- const globalVersionFile = node_path.join(homeDir, CLAUDE_DIR, "maxsim", "VERSION");
54
- if (!node_fs.existsSync(cacheDir)) node_fs.mkdirSync(cacheDir, { recursive: true });
55
- const isWindows = process.platform === "win32";
56
- (0, node_child_process.spawn)(process.execPath, ["-e", `
57
- const fs = require('fs');
58
- const { execSync } = require('child_process');
59
-
60
- const cacheFile = ${JSON.stringify(cacheFile)};
61
- const projectVersionFile = ${JSON.stringify(projectVersionFile)};
62
- const globalVersionFile = ${JSON.stringify(globalVersionFile)};
63
-
64
- // Check project directory first (local install), then global
65
- let installed = '0.0.0';
66
- try {
67
- if (fs.existsSync(projectVersionFile)) {
68
- installed = fs.readFileSync(projectVersionFile, 'utf8').trim();
69
- } else if (fs.existsSync(globalVersionFile)) {
70
- installed = fs.readFileSync(globalVersionFile, 'utf8').trim();
71
- }
72
- } catch (e) {}
73
-
74
- let latest = null;
75
- try {
76
- latest = execSync('npm view maxsimcli version', { encoding: 'utf8', timeout: 10000, windowsHide: true }).trim();
77
- } catch (e) {}
78
-
79
- const result = {
80
- update_available: latest && installed !== latest,
81
- installed,
82
- latest: latest || 'unknown',
83
- checked: Math.floor(Date.now() / 1000)
84
- };
85
-
86
- fs.writeFileSync(cacheFile, JSON.stringify(result));
87
- `], {
88
- stdio: "ignore",
89
- windowsHide: true,
90
- detached: !isWindows
91
- }).unref();
92
- }
93
- /**
94
- * Create a backup of the current MAXSIM installation before an update.
95
- * Called by the installer (not by the SessionStart hook).
56
+ * SessionStart hook check if a newer maxsimcli version is available on npm.
96
57
  *
97
- * @param cwd - The project working directory containing .claude/
98
- * @returns The backup directory path on success, null on failure.
58
+ * Behaviour:
59
+ * - Reads session info from stdin (ignored; hook fires on every session start).
60
+ * - Uses a one-hour cache file so the npm registry is queried at most once/hour.
61
+ * - Spawns a detached background process when the cache is stale so the check
62
+ * never blocks the session from starting.
63
+ * - If a newer version is found (from the cache), emits an additionalContext
64
+ * JSON message to stdout.
65
+ * - Always exits 0 — never blocks the user's session.
99
66
  */
100
- function createBackupBeforeUpdate(cwd) {
67
+ const PACKAGE_NAME = "maxsimcli";
68
+ const CACHE_TTL_MS = 3600 * 1e3;
69
+ const CACHE_FILE = node_path.join(node_os.tmpdir(), ".maxsimcli-update-cache.json");
70
+ /** Read the currently installed version from package.json next to this script. */
71
+ function getInstalledVersion() {
101
72
  try {
102
- const sourceDir = node_path.join(cwd, CLAUDE_DIR);
103
- const backupDir = node_path.join(sourceDir, "maxsim-backup");
104
- node_fs.mkdirSync(backupDir, { recursive: true });
105
- for (const relDir of [
106
- "commands/maxsim",
107
- "maxsim",
108
- "hooks",
109
- "agents",
110
- "skills"
111
- ]) {
112
- const src = node_path.join(sourceDir, relDir);
113
- if (!node_fs.existsSync(src)) continue;
114
- const dest = node_path.join(backupDir, relDir);
115
- node_fs.mkdirSync(node_path.dirname(dest), { recursive: true });
116
- node_fs.cpSync(src, dest, { recursive: true });
73
+ let dir = node_path.dirname(process.argv[1] ?? __filename);
74
+ for (let i = 0; i < 6; i++) {
75
+ const pkgPath = node_path.join(dir, "package.json");
76
+ if (node_fs.existsSync(pkgPath)) {
77
+ const pkg = JSON.parse(node_fs.readFileSync(pkgPath, "utf8"));
78
+ if (pkg.name === PACKAGE_NAME && pkg.version) return pkg.version;
79
+ }
80
+ dir = node_path.dirname(dir);
117
81
  }
118
- let version = "unknown";
119
- const versionFile = node_path.join(sourceDir, "maxsim", "VERSION");
120
- if (node_fs.existsSync(versionFile)) version = node_fs.readFileSync(versionFile, "utf8").trim();
121
- node_fs.writeFileSync(node_path.join(backupDir, "backup-meta.json"), JSON.stringify({
122
- created: (/* @__PURE__ */ new Date()).toISOString(),
123
- version
124
- }, null, 2));
125
- return backupDir;
82
+ } catch {}
83
+ return "0.0.0";
84
+ }
85
+ /** Read the update cache from disk. Returns null if absent or expired. */
86
+ function readCache() {
87
+ try {
88
+ if (!node_fs.existsSync(CACHE_FILE)) return null;
89
+ const entry = JSON.parse(node_fs.readFileSync(CACHE_FILE, "utf8"));
90
+ if (Date.now() - entry.checkedAt > CACHE_TTL_MS) return null;
91
+ return entry;
126
92
  } catch {
127
93
  return null;
128
94
  }
129
95
  }
130
- if (require.main === module) checkForUpdate({
131
- homeDir: node_os.homedir(),
132
- cwd: process.cwd()
96
+ /** Write a cache entry to disk (best-effort). */
97
+ function writeCache(latestVersion) {
98
+ try {
99
+ const entry = {
100
+ checkedAt: Date.now(),
101
+ latestVersion
102
+ };
103
+ node_fs.writeFileSync(CACHE_FILE, JSON.stringify(entry), "utf8");
104
+ } catch {}
105
+ }
106
+ /** Compare semver strings. Returns true if b > a. */
107
+ function isNewer(installed, latest) {
108
+ const parse = (v) => v.replace(/^[^0-9]*/, "").split(".").map(Number);
109
+ const [aMaj = 0, aMin = 0, aPat = 0] = parse(installed);
110
+ const [bMaj = 0, bMin = 0, bPat = 0] = parse(latest);
111
+ if (bMaj !== aMaj) return bMaj > aMaj;
112
+ if (bMin !== aMin) return bMin > aMin;
113
+ return bPat > aPat;
114
+ }
115
+ /** Spawn a background npm view query and write its result to the cache file. */
116
+ function spawnBackgroundCheck() {
117
+ try {
118
+ const child = (0, node_child_process.spawn)("npm", [
119
+ "view",
120
+ PACKAGE_NAME,
121
+ "version",
122
+ "--json"
123
+ ], {
124
+ detached: true,
125
+ stdio: [
126
+ "ignore",
127
+ "pipe",
128
+ "ignore"
129
+ ],
130
+ windowsHide: true
131
+ });
132
+ let stdout = "";
133
+ child.stdout?.on("data", (d) => {
134
+ stdout += d.toString();
135
+ });
136
+ child.on("close", (code) => {
137
+ if (code === 0) try {
138
+ writeCache(JSON.parse(stdout.trim()));
139
+ } catch {}
140
+ });
141
+ child.unref();
142
+ } catch {}
143
+ }
144
+ readStdinJson(() => {
145
+ const installed = getInstalledVersion();
146
+ const cache = readCache();
147
+ if (cache === null) try {
148
+ const result = (0, node_child_process.spawnSync)("npm", [
149
+ "view",
150
+ PACKAGE_NAME,
151
+ "version",
152
+ "--json"
153
+ ], {
154
+ encoding: "utf8",
155
+ timeout: 4e3,
156
+ windowsHide: true,
157
+ stdio: [
158
+ "ignore",
159
+ "pipe",
160
+ "ignore"
161
+ ]
162
+ });
163
+ if (result.status === 0 && result.stdout) {
164
+ const latest = JSON.parse(result.stdout.trim());
165
+ writeCache(latest);
166
+ if (isNewer(installed, latest)) process.stdout.write(JSON.stringify({ additionalContext: `MaxsimCLI update available: v${installed} → v${latest}. Run: npm i -g maxsimcli` }) + "\n");
167
+ } else spawnBackgroundCheck();
168
+ } catch {
169
+ spawnBackgroundCheck();
170
+ }
171
+ else if (isNewer(installed, cache.latestVersion)) process.stdout.write(JSON.stringify({ additionalContext: `MaxsimCLI update available: v${installed} → v${cache.latestVersion}. Run: npm i -g maxsimcli` }) + "\n");
172
+ process.exit(0);
133
173
  });
134
174
 
135
175
  //#endregion
136
- exports.checkForUpdate = checkForUpdate;
137
- exports.createBackupBeforeUpdate = createBackupBeforeUpdate;
138
176
  //# sourceMappingURL=maxsim-check-update.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"maxsim-check-update.cjs","names":["path","fs","os"],"sources":["../../../src/hooks/shared.ts","../../../src/hooks/maxsim-check-update.ts"],"sourcesContent":["/**\n * Shared utilities for MAXSIM hooks.\n */\n\n/**\n * Read all stdin as a string, then invoke callback with parsed JSON.\n * Used by statusline and sync-reminder hooks.\n */\nexport function readStdinJson<T>(callback: (data: T) => void): void {\n let input = '';\n process.stdin.setEncoding('utf8');\n process.stdin.on('data', (chunk: string) => (input += chunk));\n process.stdin.on('end', () => {\n try {\n const data = JSON.parse(input) as T;\n callback(data);\n } catch {\n // Silent fail -- never block hook execution\n process.exit(0);\n }\n });\n}\n\n/** The '.claude' path segment -- template marker replaced during install. */\nexport const CLAUDE_DIR = '.claude';\n\n/**\n * Play a system sound for notifications. Fire-and-forget, never blocks.\n * Suppressed when MAXSIM_SOUND=0, CI=true, or SSH_CONNECTION is set.\n */\nexport function playSound(type: 'question' | 'stop'): void {\n try {\n if (\n process.env.MAXSIM_SOUND === '0' ||\n process.env.CI === 'true' ||\n process.env.SSH_CONNECTION\n ) {\n return;\n }\n\n const platform = process.platform;\n\n if (platform === 'win32') {\n const file =\n type === 'question'\n ? 'C:\\\\Windows\\\\Media\\\\notify.wav'\n : 'C:\\\\Windows\\\\Media\\\\chimes.wav';\n const { spawn } = require('node:child_process') as typeof import('node:child_process');\n const child = spawn(\n 'powershell',\n ['-NoProfile', '-WindowStyle', 'Hidden', '-Command', `(New-Object Media.SoundPlayer '${file}').PlaySync()`],\n { stdio: 'ignore', windowsHide: true },\n );\n child.unref();\n } else if (platform === 'darwin') {\n const file =\n type === 'question'\n ? '/System/Library/Sounds/Ping.aiff'\n : '/System/Library/Sounds/Glass.aiff';\n const { spawn } = require('node:child_process') as typeof import('node:child_process');\n const child = spawn('afplay', [file], {\n stdio: 'ignore',\n detached: true,\n });\n child.unref();\n } else {\n // Linux / unknown — terminal bell fallback\n process.stderr.write('\\x07');\n }\n } catch {\n // Silent fail — never block hook execution\n }\n}\n","#!/usr/bin/env node\n/**\n * Check for MAXSIM updates in background, write result to cache.\n * Called by SessionStart hook - runs once per session.\n */\n\nimport * as fs from 'node:fs';\nimport * as path from 'node:path';\nimport * as os from 'node:os';\nimport { spawn } from 'node:child_process';\nimport { CLAUDE_DIR } from './shared';\n\nexport interface UpdateCheckResult {\n update_available: boolean;\n installed: string;\n latest: string;\n checked: number;\n}\n\nexport interface CheckForUpdateOptions {\n homeDir: string;\n cwd: string;\n}\n\nexport function checkForUpdate(options: CheckForUpdateOptions): void {\n const { homeDir, cwd } = options;\n const cacheDir = path.join(homeDir, CLAUDE_DIR, 'cache');\n const cacheFile = path.join(cacheDir, 'maxsim-update-check.json');\n\n // VERSION file locations (check project first, then global)\n const projectVersionFile = path.join(cwd, CLAUDE_DIR, 'maxsim', 'VERSION');\n const globalVersionFile = path.join(homeDir, CLAUDE_DIR, 'maxsim', 'VERSION');\n\n // Ensure cache directory exists\n if (!fs.existsSync(cacheDir)) {\n fs.mkdirSync(cacheDir, { recursive: true });\n }\n\n // Run check in background (spawn background process, windowsHide prevents console flash)\n const isWindows = process.platform === 'win32';\n const child = spawn(process.execPath, ['-e', `\n const fs = require('fs');\n const { execSync } = require('child_process');\n\n const cacheFile = ${JSON.stringify(cacheFile)};\n const projectVersionFile = ${JSON.stringify(projectVersionFile)};\n const globalVersionFile = ${JSON.stringify(globalVersionFile)};\n\n // Check project directory first (local install), then global\n let installed = '0.0.0';\n try {\n if (fs.existsSync(projectVersionFile)) {\n installed = fs.readFileSync(projectVersionFile, 'utf8').trim();\n } else if (fs.existsSync(globalVersionFile)) {\n installed = fs.readFileSync(globalVersionFile, 'utf8').trim();\n }\n } catch (e) {}\n\n let latest = null;\n try {\n latest = execSync('npm view maxsimcli version', { encoding: 'utf8', timeout: 10000, windowsHide: true }).trim();\n } catch (e) {}\n\n const result = {\n update_available: latest && installed !== latest,\n installed,\n latest: latest || 'unknown',\n checked: Math.floor(Date.now() / 1000)\n };\n\n fs.writeFileSync(cacheFile, JSON.stringify(result));\n`], {\n stdio: 'ignore',\n windowsHide: true,\n detached: !isWindows,\n });\n\n child.unref();\n}\n\n/**\n * Create a backup of the current MAXSIM installation before an update.\n * Called by the installer (not by the SessionStart hook).\n *\n * @param cwd - The project working directory containing .claude/\n * @returns The backup directory path on success, null on failure.\n */\nexport function createBackupBeforeUpdate(cwd: string): string | null {\n try {\n const sourceDir = path.join(cwd, CLAUDE_DIR);\n const backupDir = path.join(sourceDir, 'maxsim-backup');\n\n fs.mkdirSync(backupDir, { recursive: true });\n\n // Key directories to back up\n const dirsToBackup = [\n 'commands/maxsim',\n 'maxsim',\n 'hooks',\n 'agents',\n 'skills',\n ];\n\n for (const relDir of dirsToBackup) {\n const src = path.join(sourceDir, relDir);\n if (!fs.existsSync(src)) continue;\n\n const dest = path.join(backupDir, relDir);\n fs.mkdirSync(path.dirname(dest), { recursive: true });\n fs.cpSync(src, dest, { recursive: true });\n }\n\n // Write backup metadata\n let version = 'unknown';\n const versionFile = path.join(sourceDir, 'maxsim', 'VERSION');\n if (fs.existsSync(versionFile)) {\n version = fs.readFileSync(versionFile, 'utf8').trim();\n }\n\n fs.writeFileSync(\n path.join(backupDir, 'backup-meta.json'),\n JSON.stringify(\n { created: new Date().toISOString(), version },\n null,\n 2,\n ),\n );\n\n return backupDir;\n } catch {\n // Backup failure should not block the update\n return null;\n }\n}\n\n// Standalone entry\nif (require.main === module) {\n checkForUpdate({ homeDir: os.homedir(), cwd: process.cwd() });\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAwBA,MAAa,aAAa;;;;;;;;ACA1B,SAAgB,eAAe,SAAsC;CACnE,MAAM,EAAE,SAAS,QAAQ;CACzB,MAAM,WAAWA,UAAK,KAAK,SAAS,YAAY,QAAQ;CACxD,MAAM,YAAYA,UAAK,KAAK,UAAU,2BAA2B;CAGjE,MAAM,qBAAqBA,UAAK,KAAK,KAAK,YAAY,UAAU,UAAU;CAC1E,MAAM,oBAAoBA,UAAK,KAAK,SAAS,YAAY,UAAU,UAAU;AAG7E,KAAI,CAACC,QAAG,WAAW,SAAS,CAC1B,SAAG,UAAU,UAAU,EAAE,WAAW,MAAM,CAAC;CAI7C,MAAM,YAAY,QAAQ,aAAa;AAsCvC,+BArCoB,QAAQ,UAAU,CAAC,MAAM;;;;sBAIzB,KAAK,UAAU,UAAU,CAAC;+BACjB,KAAK,UAAU,mBAAmB,CAAC;8BACpC,KAAK,UAAU,kBAAkB,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;EAyB9D,EAAE;EACA,OAAO;EACP,aAAa;EACb,UAAU,CAAC;EACZ,CAAC,CAEI,OAAO;;;;;;;;;AAUf,SAAgB,yBAAyB,KAA4B;AACnE,KAAI;EACF,MAAM,YAAYD,UAAK,KAAK,KAAK,WAAW;EAC5C,MAAM,YAAYA,UAAK,KAAK,WAAW,gBAAgB;AAEvD,UAAG,UAAU,WAAW,EAAE,WAAW,MAAM,CAAC;AAW5C,OAAK,MAAM,UARU;GACnB;GACA;GACA;GACA;GACA;GACD,EAEkC;GACjC,MAAM,MAAMA,UAAK,KAAK,WAAW,OAAO;AACxC,OAAI,CAACC,QAAG,WAAW,IAAI,CAAE;GAEzB,MAAM,OAAOD,UAAK,KAAK,WAAW,OAAO;AACzC,WAAG,UAAUA,UAAK,QAAQ,KAAK,EAAE,EAAE,WAAW,MAAM,CAAC;AACrD,WAAG,OAAO,KAAK,MAAM,EAAE,WAAW,MAAM,CAAC;;EAI3C,IAAI,UAAU;EACd,MAAM,cAAcA,UAAK,KAAK,WAAW,UAAU,UAAU;AAC7D,MAAIC,QAAG,WAAW,YAAY,CAC5B,WAAUA,QAAG,aAAa,aAAa,OAAO,CAAC,MAAM;AAGvD,UAAG,cACDD,UAAK,KAAK,WAAW,mBAAmB,EACxC,KAAK,UACH;GAAE,0BAAS,IAAI,MAAM,EAAC,aAAa;GAAE;GAAS,EAC9C,MACA,EACD,CACF;AAED,SAAO;SACD;AAEN,SAAO;;;AAKX,IAAI,QAAQ,SAAS,OACnB,gBAAe;CAAE,SAASE,QAAG,SAAS;CAAE,KAAK,QAAQ,KAAK;CAAE,CAAC"}
1
+ {"version":3,"file":"maxsim-check-update.cjs","names":["path","os","fs"],"sources":["../../../src/hooks/shared.ts","../../../src/hooks/maxsim-check-update.ts"],"sourcesContent":["/** Shared utilities for MAXSIM hooks. */\n\nimport { spawnSync } from 'node:child_process';\nimport * as os from 'node:os';\n\nexport function readStdinJson<T>(callback: (data: T) => void): void {\n let input = '';\n process.stdin.setEncoding('utf8');\n process.stdin.on('data', (chunk: string) => {\n input += chunk;\n });\n process.stdin.on('end', () => {\n try {\n const data = JSON.parse(input) as T;\n callback(data);\n } catch {\n process.exit(0);\n }\n });\n}\n\nexport const CLAUDE_DIR = '.claude';\n\n/** Returns true when running on Windows. */\nexport function isWindows(): boolean {\n return os.platform() === 'win32';\n}\n\n/** Returns true when running on macOS. */\nexport function isMac(): boolean {\n return os.platform() === 'darwin';\n}\n\n/**\n * Play a system sound file cross-platform.\n * Never throws — sound failure is always silently swallowed.\n *\n * @param soundFile Absolute path to a WAV/MP3/etc. file, or a named system\n * sound token recognised by the platform helper (e.g. the\n * Windows-only SystemAsterisk token).\n */\nexport function playSound(soundFile: string): void {\n try {\n if (isWindows()) {\n // PowerShell's SoundPlayer works with WAV files synchronously.\n // For named system sounds (no extension) fall back to rundll32.\n const isWav = soundFile.toLowerCase().endsWith('.wav');\n if (isWav) {\n spawnSync(\n 'powershell',\n [\n '-NoProfile',\n '-NonInteractive',\n '-Command',\n `(New-Object System.Media.SoundPlayer '${soundFile.replace(/'/g, \"''\")}').PlaySync()`,\n ],\n { stdio: 'ignore' },\n );\n } else {\n // Named system sound token (e.g. \"SystemAsterisk\") or unsupported format —\n // use the rundll32 winsound bridge.\n spawnSync(\n 'rundll32',\n ['user32.dll,MessageBeep'],\n { stdio: 'ignore' },\n );\n }\n } else if (isMac()) {\n spawnSync('afplay', [soundFile], { stdio: 'ignore' });\n } else {\n // Linux: try paplay (PulseAudio) then aplay (ALSA)\n const paplay = spawnSync('paplay', [soundFile], { stdio: 'ignore' });\n if (paplay.status !== 0) {\n spawnSync('aplay', [soundFile], { stdio: 'ignore' });\n }\n }\n } catch {\n // Never crash on sound failure\n }\n}\n","/**\n * SessionStart hook — check if a newer maxsimcli version is available on npm.\n *\n * Behaviour:\n * - Reads session info from stdin (ignored; hook fires on every session start).\n * - Uses a one-hour cache file so the npm registry is queried at most once/hour.\n * - Spawns a detached background process when the cache is stale so the check\n * never blocks the session from starting.\n * - If a newer version is found (from the cache), emits an additionalContext\n * JSON message to stdout.\n * - Always exits 0 — never blocks the user's session.\n */\n\nimport * as fs from 'node:fs';\nimport * as os from 'node:os';\nimport * as path from 'node:path';\nimport { spawnSync, spawn } from 'node:child_process';\nimport { readStdinJson } from './shared.js';\n\nconst PACKAGE_NAME = 'maxsimcli';\nconst CACHE_TTL_MS = 60 * 60 * 1000; // 1 hour\nconst CACHE_FILE = path.join(os.tmpdir(), '.maxsimcli-update-cache.json');\n\ninterface CacheEntry {\n checkedAt: number;\n latestVersion: string;\n}\n\ninterface SessionStartInput {\n session_id?: string;\n [key: string]: unknown;\n}\n\n/** Read the currently installed version from package.json next to this script. */\nfunction getInstalledVersion(): string {\n try {\n // Walk up from this file's location to find the package root\n let dir = path.dirname(process.argv[1] ?? __filename);\n for (let i = 0; i < 6; i++) {\n const pkgPath = path.join(dir, 'package.json');\n if (fs.existsSync(pkgPath)) {\n const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf8')) as { name?: string; version?: string };\n if (pkg.name === PACKAGE_NAME && pkg.version) {\n return pkg.version;\n }\n }\n dir = path.dirname(dir);\n }\n } catch {\n // Ignore\n }\n return '0.0.0';\n}\n\n/** Read the update cache from disk. Returns null if absent or expired. */\nfunction readCache(): CacheEntry | null {\n try {\n if (!fs.existsSync(CACHE_FILE)) return null;\n const entry = JSON.parse(fs.readFileSync(CACHE_FILE, 'utf8')) as CacheEntry;\n if (Date.now() - entry.checkedAt > CACHE_TTL_MS) return null;\n return entry;\n } catch {\n return null;\n }\n}\n\n/** Write a cache entry to disk (best-effort). */\nfunction writeCache(latestVersion: string): void {\n try {\n const entry: CacheEntry = { checkedAt: Date.now(), latestVersion };\n fs.writeFileSync(CACHE_FILE, JSON.stringify(entry), 'utf8');\n } catch {\n // Ignore write failures\n }\n}\n\n/** Compare semver strings. Returns true if b > a. */\nfunction isNewer(installed: string, latest: string): boolean {\n const parse = (v: string) => v.replace(/^[^0-9]*/, '').split('.').map(Number);\n const [aMaj = 0, aMin = 0, aPat = 0] = parse(installed);\n const [bMaj = 0, bMin = 0, bPat = 0] = parse(latest);\n if (bMaj !== aMaj) return bMaj > aMaj;\n if (bMin !== aMin) return bMin > aMin;\n return bPat > aPat;\n}\n\n/** Spawn a background npm view query and write its result to the cache file. */\nfunction spawnBackgroundCheck(): void {\n try {\n const child = spawn(\n 'npm',\n ['view', PACKAGE_NAME, 'version', '--json'],\n {\n detached: true,\n stdio: ['ignore', 'pipe', 'ignore'],\n windowsHide: true,\n },\n );\n\n let stdout = '';\n child.stdout?.on('data', (d: Buffer) => { stdout += d.toString(); });\n child.on('close', (code: number) => {\n if (code === 0) {\n try {\n const version = JSON.parse(stdout.trim()) as string;\n writeCache(version);\n } catch {\n // Malformed output — ignore\n }\n }\n });\n\n child.unref();\n } catch {\n // Ignore spawn failures (npm not available, etc.)\n }\n}\n\nreadStdinJson<SessionStartInput>(() => {\n const installed = getInstalledVersion();\n const cache = readCache();\n\n if (cache === null) {\n // Cache is stale or absent — kick off a background refresh.\n // We do a quick synchronous check first so we don't miss the very first run.\n try {\n const result = spawnSync('npm', ['view', PACKAGE_NAME, 'version', '--json'], {\n encoding: 'utf8',\n timeout: 4000,\n windowsHide: true,\n stdio: ['ignore', 'pipe', 'ignore'],\n });\n if (result.status === 0 && result.stdout) {\n const latest = JSON.parse(result.stdout.trim()) as string;\n writeCache(latest);\n if (isNewer(installed, latest)) {\n process.stdout.write(\n JSON.stringify({\n additionalContext: `MaxsimCLI update available: v${installed} → v${latest}. Run: npm i -g maxsimcli`,\n }) + '\\n',\n );\n }\n } else {\n // npm timed out or failed — schedule a detached background check for next time\n spawnBackgroundCheck();\n }\n } catch {\n spawnBackgroundCheck();\n }\n } else if (isNewer(installed, cache.latestVersion)) {\n process.stdout.write(\n JSON.stringify({\n additionalContext: `MaxsimCLI update available: v${installed} → v${cache.latestVersion}. Run: npm i -g maxsimcli`,\n }) + '\\n',\n );\n }\n\n process.exit(0);\n});\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAKA,SAAgB,cAAiB,UAAmC;CAClE,IAAI,QAAQ;AACZ,SAAQ,MAAM,YAAY,OAAO;AACjC,SAAQ,MAAM,GAAG,SAAS,UAAkB;AAC1C,WAAS;GACT;AACF,SAAQ,MAAM,GAAG,aAAa;AAC5B,MAAI;AAEF,YADa,KAAK,MAAM,MAAM,CAChB;UACR;AACN,WAAQ,KAAK,EAAE;;GAEjB;;;;;;;;;;;;;;;;;ACCJ,MAAM,eAAe;AACrB,MAAM,eAAe,OAAU;AAC/B,MAAM,aAAaA,UAAK,KAAKC,QAAG,QAAQ,EAAE,+BAA+B;;AAazE,SAAS,sBAA8B;AACrC,KAAI;EAEF,IAAI,MAAMD,UAAK,QAAQ,QAAQ,KAAK,MAAM,WAAW;AACrD,OAAK,IAAI,IAAI,GAAG,IAAI,GAAG,KAAK;GAC1B,MAAM,UAAUA,UAAK,KAAK,KAAK,eAAe;AAC9C,OAAIE,QAAG,WAAW,QAAQ,EAAE;IAC1B,MAAM,MAAM,KAAK,MAAMA,QAAG,aAAa,SAAS,OAAO,CAAC;AACxD,QAAI,IAAI,SAAS,gBAAgB,IAAI,QACnC,QAAO,IAAI;;AAGf,SAAMF,UAAK,QAAQ,IAAI;;SAEnB;AAGR,QAAO;;;AAIT,SAAS,YAA+B;AACtC,KAAI;AACF,MAAI,CAACE,QAAG,WAAW,WAAW,CAAE,QAAO;EACvC,MAAM,QAAQ,KAAK,MAAMA,QAAG,aAAa,YAAY,OAAO,CAAC;AAC7D,MAAI,KAAK,KAAK,GAAG,MAAM,YAAY,aAAc,QAAO;AACxD,SAAO;SACD;AACN,SAAO;;;;AAKX,SAAS,WAAW,eAA6B;AAC/C,KAAI;EACF,MAAM,QAAoB;GAAE,WAAW,KAAK,KAAK;GAAE;GAAe;AAClE,UAAG,cAAc,YAAY,KAAK,UAAU,MAAM,EAAE,OAAO;SACrD;;;AAMV,SAAS,QAAQ,WAAmB,QAAyB;CAC3D,MAAM,SAAS,MAAc,EAAE,QAAQ,YAAY,GAAG,CAAC,MAAM,IAAI,CAAC,IAAI,OAAO;CAC7E,MAAM,CAAC,OAAO,GAAG,OAAO,GAAG,OAAO,KAAK,MAAM,UAAU;CACvD,MAAM,CAAC,OAAO,GAAG,OAAO,GAAG,OAAO,KAAK,MAAM,OAAO;AACpD,KAAI,SAAS,KAAM,QAAO,OAAO;AACjC,KAAI,SAAS,KAAM,QAAO,OAAO;AACjC,QAAO,OAAO;;;AAIhB,SAAS,uBAA6B;AACpC,KAAI;EACF,MAAM,sCACJ,OACA;GAAC;GAAQ;GAAc;GAAW;GAAS,EAC3C;GACE,UAAU;GACV,OAAO;IAAC;IAAU;IAAQ;IAAS;GACnC,aAAa;GACd,CACF;EAED,IAAI,SAAS;AACb,QAAM,QAAQ,GAAG,SAAS,MAAc;AAAE,aAAU,EAAE,UAAU;IAAI;AACpE,QAAM,GAAG,UAAU,SAAiB;AAClC,OAAI,SAAS,EACX,KAAI;AAEF,eADgB,KAAK,MAAM,OAAO,MAAM,CAAC,CACtB;WACb;IAIV;AAEF,QAAM,OAAO;SACP;;AAKV,oBAAuC;CACrC,MAAM,YAAY,qBAAqB;CACvC,MAAM,QAAQ,WAAW;AAEzB,KAAI,UAAU,KAGZ,KAAI;EACF,MAAM,2CAAmB,OAAO;GAAC;GAAQ;GAAc;GAAW;GAAS,EAAE;GAC3E,UAAU;GACV,SAAS;GACT,aAAa;GACb,OAAO;IAAC;IAAU;IAAQ;IAAS;GACpC,CAAC;AACF,MAAI,OAAO,WAAW,KAAK,OAAO,QAAQ;GACxC,MAAM,SAAS,KAAK,MAAM,OAAO,OAAO,MAAM,CAAC;AAC/C,cAAW,OAAO;AAClB,OAAI,QAAQ,WAAW,OAAO,CAC5B,SAAQ,OAAO,MACb,KAAK,UAAU,EACb,mBAAmB,gCAAgC,UAAU,MAAM,OAAO,4BAC3E,CAAC,GAAG,KACN;QAIH,uBAAsB;SAElB;AACN,wBAAsB;;UAEf,QAAQ,WAAW,MAAM,cAAc,CAChD,SAAQ,OAAO,MACb,KAAK,UAAU,EACb,mBAAmB,gCAAgC,UAAU,MAAM,MAAM,cAAc,4BACxF,CAAC,GAAG,KACN;AAGH,SAAQ,KAAK,EAAE;EACf"}
@@ -1,18 +1,44 @@
1
- #!/usr/bin/env node
2
- Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
1
+ //#region \0rolldown/runtime.js
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __copyProps = (to, from, except, desc) => {
9
+ if (from && typeof from === "object" || typeof from === "function") {
10
+ for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
11
+ key = keys[i];
12
+ if (!__hasOwnProp.call(to, key) && key !== except) {
13
+ __defProp(to, key, {
14
+ get: ((k) => from[k]).bind(null, key),
15
+ enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
16
+ });
17
+ }
18
+ }
19
+ }
20
+ return to;
21
+ };
22
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
23
+ value: mod,
24
+ enumerable: true
25
+ }) : target, mod));
26
+
27
+ //#endregion
28
+ let node_path = require("node:path");
29
+ node_path = __toESM(node_path);
30
+ let node_child_process = require("node:child_process");
31
+ let node_os = require("node:os");
32
+ node_os = __toESM(node_os);
3
33
 
4
34
  //#region src/hooks/shared.ts
5
- /**
6
- * Shared utilities for MAXSIM hooks.
7
- */
8
- /**
9
- * Read all stdin as a string, then invoke callback with parsed JSON.
10
- * Used by statusline and sync-reminder hooks.
11
- */
35
+ /** Shared utilities for MAXSIM hooks. */
12
36
  function readStdinJson(callback) {
13
37
  let input = "";
14
38
  process.stdin.setEncoding("utf8");
15
- process.stdin.on("data", (chunk) => input += chunk);
39
+ process.stdin.on("data", (chunk) => {
40
+ input += chunk;
41
+ });
16
42
  process.stdin.on("end", () => {
17
43
  try {
18
44
  callback(JSON.parse(input));
@@ -21,51 +47,69 @@ function readStdinJson(callback) {
21
47
  }
22
48
  });
23
49
  }
50
+ /** Returns true when running on Windows. */
51
+ function isWindows() {
52
+ return node_os.platform() === "win32";
53
+ }
54
+ /** Returns true when running on macOS. */
55
+ function isMac() {
56
+ return node_os.platform() === "darwin";
57
+ }
24
58
  /**
25
- * Play a system sound for notifications. Fire-and-forget, never blocks.
26
- * Suppressed when MAXSIM_SOUND=0, CI=true, or SSH_CONNECTION is set.
59
+ * Play a system sound file cross-platform.
60
+ * Never throws sound failure is always silently swallowed.
61
+ *
62
+ * @param soundFile Absolute path to a WAV/MP3/etc. file, or a named system
63
+ * sound token recognised by the platform helper (e.g. the
64
+ * Windows-only SystemAsterisk token).
27
65
  */
28
- function playSound(type) {
66
+ function playSound(soundFile) {
29
67
  try {
30
- if (process.env.MAXSIM_SOUND === "0" || process.env.CI === "true" || process.env.SSH_CONNECTION) return;
31
- const platform = process.platform;
32
- if (platform === "win32") {
33
- const file = type === "question" ? "C:\\Windows\\Media\\notify.wav" : "C:\\Windows\\Media\\chimes.wav";
34
- const { spawn } = require("node:child_process");
35
- spawn("powershell", [
36
- "-NoProfile",
37
- "-WindowStyle",
38
- "Hidden",
39
- "-Command",
40
- `(New-Object Media.SoundPlayer '${file}').PlaySync()`
41
- ], {
42
- stdio: "ignore",
43
- windowsHide: true
44
- }).unref();
45
- } else if (platform === "darwin") {
46
- const file = type === "question" ? "/System/Library/Sounds/Ping.aiff" : "/System/Library/Sounds/Glass.aiff";
47
- const { spawn } = require("node:child_process");
48
- spawn("afplay", [file], {
49
- stdio: "ignore",
50
- detached: true
51
- }).unref();
52
- } else process.stderr.write("\x07");
68
+ if (isWindows()) if (soundFile.toLowerCase().endsWith(".wav")) (0, node_child_process.spawnSync)("powershell", [
69
+ "-NoProfile",
70
+ "-NonInteractive",
71
+ "-Command",
72
+ `(New-Object System.Media.SoundPlayer '${soundFile.replace(/'/g, "''")}').PlaySync()`
73
+ ], { stdio: "ignore" });
74
+ else (0, node_child_process.spawnSync)("rundll32", ["user32.dll,MessageBeep"], { stdio: "ignore" });
75
+ else if (isMac()) (0, node_child_process.spawnSync)("afplay", [soundFile], { stdio: "ignore" });
76
+ else if ((0, node_child_process.spawnSync)("paplay", [soundFile], { stdio: "ignore" }).status !== 0) (0, node_child_process.spawnSync)("aplay", [soundFile], { stdio: "ignore" });
53
77
  } catch {}
54
78
  }
55
79
 
56
80
  //#endregion
57
81
  //#region src/hooks/maxsim-notification-sound.ts
58
82
  /**
59
- * Notification Sound Hook PostToolUse hook that plays a sound
60
- * when Claude asks the user a question (AskUserQuestion tool).
83
+ * Notification hookplay a short, non-intrusive sound when Claude asks a
84
+ * question (i.e. the Notification event fires).
85
+ *
86
+ * Uses the platform's built-in system sounds so no external audio files are
87
+ * required. Falls through silently if playback fails for any reason.
61
88
  */
62
- function processNotificationSound(data) {
63
- playSound("question");
89
+ /** Resolve a bundled WAV asset relative to this script, or return null. */
90
+ function bundledSound(name) {
91
+ const candidates = [node_path.join(node_path.dirname(process.argv[1] ?? __filename), "sounds", name), node_path.join(__dirname, "sounds", name)];
92
+ for (const p of candidates) try {
93
+ require("node:fs").existsSync(p);
94
+ if (require("node:fs").existsSync(p)) return p;
95
+ } catch {}
96
+ return null;
97
+ }
98
+ /** Play the best available notification sound for the current platform. */
99
+ function playNotification() {
100
+ const wav = bundledSound("notification.wav");
101
+ if (wav) {
102
+ playSound(wav);
103
+ return;
104
+ }
105
+ if (isWindows()) playSound("SystemAsterisk");
106
+ else if (isMac()) playSound("/System/Library/Sounds/Funk.aiff");
107
+ else playSound("/usr/share/sounds/freedesktop/stereo/message-new-instant.oga");
64
108
  }
65
- if (require.main === module) readStdinJson((data) => {
66
- processNotificationSound(data);
109
+ readStdinJson((_input) => {
110
+ playNotification();
111
+ process.exit(0);
67
112
  });
68
113
 
69
114
  //#endregion
70
- exports.processNotificationSound = processNotificationSound;
71
115
  //# sourceMappingURL=maxsim-notification-sound.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"maxsim-notification-sound.cjs","names":[],"sources":["../../../src/hooks/shared.ts","../../../src/hooks/maxsim-notification-sound.ts"],"sourcesContent":["/**\n * Shared utilities for MAXSIM hooks.\n */\n\n/**\n * Read all stdin as a string, then invoke callback with parsed JSON.\n * Used by statusline and sync-reminder hooks.\n */\nexport function readStdinJson<T>(callback: (data: T) => void): void {\n let input = '';\n process.stdin.setEncoding('utf8');\n process.stdin.on('data', (chunk: string) => (input += chunk));\n process.stdin.on('end', () => {\n try {\n const data = JSON.parse(input) as T;\n callback(data);\n } catch {\n // Silent fail -- never block hook execution\n process.exit(0);\n }\n });\n}\n\n/** The '.claude' path segment -- template marker replaced during install. */\nexport const CLAUDE_DIR = '.claude';\n\n/**\n * Play a system sound for notifications. Fire-and-forget, never blocks.\n * Suppressed when MAXSIM_SOUND=0, CI=true, or SSH_CONNECTION is set.\n */\nexport function playSound(type: 'question' | 'stop'): void {\n try {\n if (\n process.env.MAXSIM_SOUND === '0' ||\n process.env.CI === 'true' ||\n process.env.SSH_CONNECTION\n ) {\n return;\n }\n\n const platform = process.platform;\n\n if (platform === 'win32') {\n const file =\n type === 'question'\n ? 'C:\\\\Windows\\\\Media\\\\notify.wav'\n : 'C:\\\\Windows\\\\Media\\\\chimes.wav';\n const { spawn } = require('node:child_process') as typeof import('node:child_process');\n const child = spawn(\n 'powershell',\n ['-NoProfile', '-WindowStyle', 'Hidden', '-Command', `(New-Object Media.SoundPlayer '${file}').PlaySync()`],\n { stdio: 'ignore', windowsHide: true },\n );\n child.unref();\n } else if (platform === 'darwin') {\n const file =\n type === 'question'\n ? '/System/Library/Sounds/Ping.aiff'\n : '/System/Library/Sounds/Glass.aiff';\n const { spawn } = require('node:child_process') as typeof import('node:child_process');\n const child = spawn('afplay', [file], {\n stdio: 'ignore',\n detached: true,\n });\n child.unref();\n } else {\n // Linux / unknown terminal bell fallback\n process.stderr.write('\\x07');\n }\n } catch {\n // Silent fail never block hook execution\n }\n}\n","#!/usr/bin/env node\n/**\n * Notification Sound Hook PostToolUse hook that plays a sound\n * when Claude asks the user a question (AskUserQuestion tool).\n */\n\nimport { readStdinJson, playSound } from './shared';\n\ninterface NotificationSoundInput {\n tool_name?: string;\n}\n\nexport function processNotificationSound(data: NotificationSoundInput): void {\n playSound('question');\n}\n\n// Standalone entry\nif (require.main === module) {\n readStdinJson<NotificationSoundInput>((data) => {\n processNotificationSound(data);\n });\n}\n"],"mappings":";;;;;;;;;;;AAQA,SAAgB,cAAiB,UAAmC;CAClE,IAAI,QAAQ;AACZ,SAAQ,MAAM,YAAY,OAAO;AACjC,SAAQ,MAAM,GAAG,SAAS,UAAmB,SAAS,MAAO;AAC7D,SAAQ,MAAM,GAAG,aAAa;AAC5B,MAAI;AAEF,YADa,KAAK,MAAM,MAAM,CAChB;UACR;AAEN,WAAQ,KAAK,EAAE;;GAEjB;;;;;;AAUJ,SAAgB,UAAU,MAAiC;AACzD,KAAI;AACF,MACE,QAAQ,IAAI,iBAAiB,OAC7B,QAAQ,IAAI,OAAO,UACnB,QAAQ,IAAI,eAEZ;EAGF,MAAM,WAAW,QAAQ;AAEzB,MAAI,aAAa,SAAS;GACxB,MAAM,OACJ,SAAS,aACL,mCACA;GACN,MAAM,EAAE,UAAU,QAAQ,qBAAqB;AAM/C,GALc,MACZ,cACA;IAAC;IAAc;IAAgB;IAAU;IAAY,kCAAkC,KAAK;IAAe,EAC3G;IAAE,OAAO;IAAU,aAAa;IAAM,CACvC,CACK,OAAO;aACJ,aAAa,UAAU;GAChC,MAAM,OACJ,SAAS,aACL,qCACA;GACN,MAAM,EAAE,UAAU,QAAQ,qBAAqB;AAK/C,GAJc,MAAM,UAAU,CAAC,KAAK,EAAE;IACpC,OAAO;IACP,UAAU;IACX,CAAC,CACI,OAAO;QAGb,SAAQ,OAAO,MAAM,OAAO;SAExB;;;;;;;;;ACzDV,SAAgB,yBAAyB,MAAoC;AAC3E,WAAU,WAAW;;AAIvB,IAAI,QAAQ,SAAS,OACnB,gBAAuC,SAAS;AAC9C,0BAAyB,KAAK;EAC9B"}
1
+ {"version":3,"file":"maxsim-notification-sound.cjs","names":["os","path"],"sources":["../../../src/hooks/shared.ts","../../../src/hooks/maxsim-notification-sound.ts"],"sourcesContent":["/** Shared utilities for MAXSIM hooks. */\n\nimport { spawnSync } from 'node:child_process';\nimport * as os from 'node:os';\n\nexport function readStdinJson<T>(callback: (data: T) => void): void {\n let input = '';\n process.stdin.setEncoding('utf8');\n process.stdin.on('data', (chunk: string) => {\n input += chunk;\n });\n process.stdin.on('end', () => {\n try {\n const data = JSON.parse(input) as T;\n callback(data);\n } catch {\n process.exit(0);\n }\n });\n}\n\nexport const CLAUDE_DIR = '.claude';\n\n/** Returns true when running on Windows. */\nexport function isWindows(): boolean {\n return os.platform() === 'win32';\n}\n\n/** Returns true when running on macOS. */\nexport function isMac(): boolean {\n return os.platform() === 'darwin';\n}\n\n/**\n * Play a system sound file cross-platform.\n * Never throws — sound failure is always silently swallowed.\n *\n * @param soundFile Absolute path to a WAV/MP3/etc. file, or a named system\n * sound token recognised by the platform helper (e.g. the\n * Windows-only SystemAsterisk token).\n */\nexport function playSound(soundFile: string): void {\n try {\n if (isWindows()) {\n // PowerShell's SoundPlayer works with WAV files synchronously.\n // For named system sounds (no extension) fall back to rundll32.\n const isWav = soundFile.toLowerCase().endsWith('.wav');\n if (isWav) {\n spawnSync(\n 'powershell',\n [\n '-NoProfile',\n '-NonInteractive',\n '-Command',\n `(New-Object System.Media.SoundPlayer '${soundFile.replace(/'/g, \"''\")}').PlaySync()`,\n ],\n { stdio: 'ignore' },\n );\n } else {\n // Named system sound token (e.g. \"SystemAsterisk\") or unsupported format —\n // use the rundll32 winsound bridge.\n spawnSync(\n 'rundll32',\n ['user32.dll,MessageBeep'],\n { stdio: 'ignore' },\n );\n }\n } else if (isMac()) {\n spawnSync('afplay', [soundFile], { stdio: 'ignore' });\n } else {\n // Linux: try paplay (PulseAudio) then aplay (ALSA)\n const paplay = spawnSync('paplay', [soundFile], { stdio: 'ignore' });\n if (paplay.status !== 0) {\n spawnSync('aplay', [soundFile], { stdio: 'ignore' });\n }\n }\n } catch {\n // Never crash on sound failure\n }\n}\n","/**\n * Notification hookplay a short, non-intrusive sound when Claude asks a\n * question (i.e. the Notification event fires).\n *\n * Uses the platform's built-in system sounds so no external audio files are\n * required. Falls through silently if playback fails for any reason.\n */\n\nimport * as path from 'node:path';\nimport { readStdinJson, playSound, isWindows, isMac } from './shared.js';\n\ninterface NotificationInput {\n session_id?: string;\n message?: string;\n [key: string]: unknown;\n}\n\n/** Resolve a bundled WAV asset relative to this script, or return null. */\nfunction bundledSound(name: string): string | null {\n // When compiled to a CJS bundle the file sits next to the .cjs file in\n // dist/assets/hooks/. We look for a sibling sounds/ directory.\n const candidates = [\n path.join(path.dirname(process.argv[1] ?? __filename), 'sounds', name),\n path.join(__dirname, 'sounds', name),\n ];\n for (const p of candidates) {\n try {\n // Use dynamic require check — fs.existsSync would also work\n // eslint-disable-next-line @typescript-eslint/no-require-imports\n require('node:fs').existsSync(p);\n // eslint-disable-next-line @typescript-eslint/no-require-imports\n if (require('node:fs').existsSync(p)) return p;\n } catch {\n // ignore\n }\n }\n return null;\n}\n\n/** Play the best available notification sound for the current platform. */\nfunction playNotification(): void {\n // 1. Prefer a bundled WAV if present (future-proof for custom sounds)\n const wav = bundledSound('notification.wav');\n if (wav) {\n playSound(wav);\n return;\n }\n\n // 2. Fall back to a built-in system sound\n if (isWindows()) {\n // SystemAsterisk maps to the Windows \"Asterisk\" event sound\n playSound('SystemAsterisk');\n } else if (isMac()) {\n // /System/Library/Sounds/Funk.aiff short, non-intrusive\n playSound('/System/Library/Sounds/Funk.aiff');\n } else {\n // Linux: /usr/share/sounds/freedesktop/stereo/message-new-instant.oga\n playSound('/usr/share/sounds/freedesktop/stereo/message-new-instant.oga');\n }\n}\n\nreadStdinJson<NotificationInput>((_input) => {\n playNotification();\n process.exit(0);\n});\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAKA,SAAgB,cAAiB,UAAmC;CAClE,IAAI,QAAQ;AACZ,SAAQ,MAAM,YAAY,OAAO;AACjC,SAAQ,MAAM,GAAG,SAAS,UAAkB;AAC1C,WAAS;GACT;AACF,SAAQ,MAAM,GAAG,aAAa;AAC5B,MAAI;AAEF,YADa,KAAK,MAAM,MAAM,CAChB;UACR;AACN,WAAQ,KAAK,EAAE;;GAEjB;;;AAMJ,SAAgB,YAAqB;AACnC,QAAOA,QAAG,UAAU,KAAK;;;AAI3B,SAAgB,QAAiB;AAC/B,QAAOA,QAAG,UAAU,KAAK;;;;;;;;;;AAW3B,SAAgB,UAAU,WAAyB;AACjD,KAAI;AACF,MAAI,WAAW,CAIb,KADc,UAAU,aAAa,CAAC,SAAS,OAAO,CAEpD,mCACE,cACA;GACE;GACA;GACA;GACA,yCAAyC,UAAU,QAAQ,MAAM,KAAK,CAAC;GACxE,EACD,EAAE,OAAO,UAAU,CACpB;MAID,mCACE,YACA,CAAC,yBAAyB,EAC1B,EAAE,OAAO,UAAU,CACpB;WAEM,OAAO,CAChB,mCAAU,UAAU,CAAC,UAAU,EAAE,EAAE,OAAO,UAAU,CAAC;6CAG5B,UAAU,CAAC,UAAU,EAAE,EAAE,OAAO,UAAU,CAAC,CACzD,WAAW,EACpB,mCAAU,SAAS,CAAC,UAAU,EAAE,EAAE,OAAO,UAAU,CAAC;SAGlD;;;;;;;;;;;;;AC1DV,SAAS,aAAa,MAA6B;CAGjD,MAAM,aAAa,CACjBC,UAAK,KAAKA,UAAK,QAAQ,QAAQ,KAAK,MAAM,WAAW,EAAE,UAAU,KAAK,EACtEA,UAAK,KAAK,WAAW,UAAU,KAAK,CACrC;AACD,MAAK,MAAM,KAAK,WACd,KAAI;AAGF,UAAQ,UAAU,CAAC,WAAW,EAAE;AAEhC,MAAI,QAAQ,UAAU,CAAC,WAAW,EAAE,CAAE,QAAO;SACvC;AAIV,QAAO;;;AAIT,SAAS,mBAAyB;CAEhC,MAAM,MAAM,aAAa,mBAAmB;AAC5C,KAAI,KAAK;AACP,YAAU,IAAI;AACd;;AAIF,KAAI,WAAW,CAEb,WAAU,iBAAiB;UAClB,OAAO,CAEhB,WAAU,mCAAmC;KAG7C,WAAU,+DAA+D;;AAI7E,eAAkC,WAAW;AAC3C,mBAAkB;AAClB,SAAQ,KAAK,EAAE;EACf"}