maxsimcli 5.0.3 → 5.0.5
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/assets/CHANGELOG.md +17 -0
- package/dist/assets/hooks/maxsim-check-update.cjs +2 -1
- package/dist/assets/hooks/maxsim-check-update.cjs.map +1 -1
- package/dist/assets/hooks/maxsim-notification-sound.cjs +3 -2
- package/dist/assets/hooks/maxsim-notification-sound.cjs.map +1 -1
- package/dist/assets/hooks/maxsim-statusline.cjs +6 -4
- package/dist/assets/hooks/maxsim-statusline.cjs.map +1 -1
- package/dist/assets/hooks/maxsim-stop-sound.cjs +3 -2
- package/dist/assets/hooks/maxsim-stop-sound.cjs.map +1 -1
- package/dist/assets/hooks/maxsim-sync-reminder.cjs.map +1 -1
- package/dist/assets/templates/commands/maxsim/quick.md +3 -5
- package/dist/assets/templates/references/thinking-partner.md +0 -2
- package/dist/assets/templates/skills/github-artifact-protocol/SKILL.md +0 -1
- package/dist/assets/templates/skills/github-tools-guide/SKILL.md +2 -9
- package/dist/assets/templates/templates/state.md +0 -10
- package/dist/assets/templates/workflows/execute.md +3 -3
- package/dist/assets/templates/workflows/go.md +1 -1
- package/dist/assets/templates/workflows/help.md +1 -2
- package/dist/assets/templates/workflows/new-milestone.md +1 -1
- package/dist/assets/templates/workflows/plan-create.md +1 -1
- package/dist/assets/templates/workflows/plan-discuss.md +1 -1
- package/dist/assets/templates/workflows/plan.md +4 -4
- package/dist/assets/templates/workflows/progress.md +0 -5
- package/dist/assets/templates/workflows/quick.md +1 -151
- package/dist/assets/templates/workflows/sdd.md +1 -1
- package/dist/assets/templates/workflows/verify-phase.md +2 -2
- package/dist/cli.cjs +8417 -8695
- package/dist/cli.cjs.map +1 -1
- package/dist/install.cjs +2 -1
- package/dist/install.cjs.map +1 -1
- package/package.json +1 -1
package/dist/assets/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,20 @@
|
|
|
1
|
+
## [5.0.4](https://github.com/maystudios/maxsimcli/compare/v5.0.3...v5.0.4) (2026-03-12)
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
### Bug Fixes
|
|
5
|
+
|
|
6
|
+
* **agents:** correct skill path prefix in available_skills frontmatter ([37d4a84](https://github.com/maystudios/maxsimcli/commit/37d4a84255b85990e6aafd1467617f3b0b6398c4))
|
|
7
|
+
* **commands:** use absolute [@path](https://github.com/path) refs so commands resolve after install ([ff86aae](https://github.com/maystudios/maxsimcli/commit/ff86aaee0d02aae064ee1ac82bfba7730f1f2a42))
|
|
8
|
+
* **hooks:** prevent console window flashes on Windows ([35a2591](https://github.com/maystudios/maxsimcli/commit/35a2591e18fe81cce5e0a10ae81595852804dcfa))
|
|
9
|
+
* **workflows:** correct [@path](https://github.com/path) references to use ~/.claude/maxsim/ prefix ([9fdacc0](https://github.com/maystudios/maxsimcli/commit/9fdacc08f0adc6236288f4edcfef7a5845531797))
|
|
10
|
+
|
|
11
|
+
## [5.0.3](https://github.com/maystudios/maxsimcli/compare/v5.0.2...v5.0.3) (2026-03-12)
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
### Bug Fixes
|
|
15
|
+
|
|
16
|
+
* **templates:** rewrite [@path](https://github.com/path) references to resolve correctly after install ([11aeffa](https://github.com/maystudios/maxsimcli/commit/11aeffad7b355ccbbd3c1c9cab58419113360eb3))
|
|
17
|
+
|
|
1
18
|
## [5.0.2](https://github.com/maystudios/maxsimcli/compare/v5.0.1...v5.0.2) (2026-03-12)
|
|
2
19
|
|
|
3
20
|
|
|
@@ -52,6 +52,7 @@ function checkForUpdate(options) {
|
|
|
52
52
|
const projectVersionFile = node_path.join(cwd, CLAUDE_DIR, "maxsim", "VERSION");
|
|
53
53
|
const globalVersionFile = node_path.join(homeDir, CLAUDE_DIR, "maxsim", "VERSION");
|
|
54
54
|
if (!node_fs.existsSync(cacheDir)) node_fs.mkdirSync(cacheDir, { recursive: true });
|
|
55
|
+
const isWindows = process.platform === "win32";
|
|
55
56
|
(0, node_child_process.spawn)(process.execPath, ["-e", `
|
|
56
57
|
const fs = require('fs');
|
|
57
58
|
const { execSync } = require('child_process');
|
|
@@ -86,7 +87,7 @@ function checkForUpdate(options) {
|
|
|
86
87
|
`], {
|
|
87
88
|
stdio: "ignore",
|
|
88
89
|
windowsHide: true,
|
|
89
|
-
detached:
|
|
90
|
+
detached: !isWindows
|
|
90
91
|
}).unref();
|
|
91
92
|
}
|
|
92
93
|
/**
|
|
@@ -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', '-Command', `(New-Object Media.SoundPlayer '${file}').PlaySync()`],\n { stdio: 'ignore', windowsHide: true
|
|
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"}
|
|
@@ -34,12 +34,13 @@ function playSound(type) {
|
|
|
34
34
|
const { spawn } = require("node:child_process");
|
|
35
35
|
spawn("powershell", [
|
|
36
36
|
"-NoProfile",
|
|
37
|
+
"-WindowStyle",
|
|
38
|
+
"Hidden",
|
|
37
39
|
"-Command",
|
|
38
40
|
`(New-Object Media.SoundPlayer '${file}').PlaySync()`
|
|
39
41
|
], {
|
|
40
42
|
stdio: "ignore",
|
|
41
|
-
windowsHide: true
|
|
42
|
-
detached: true
|
|
43
|
+
windowsHide: true
|
|
43
44
|
}).unref();
|
|
44
45
|
} else if (platform === "darwin") {
|
|
45
46
|
const file = type === "question" ? "/System/Library/Sounds/Ping.aiff" : "/System/Library/Sounds/Glass.aiff";
|
|
@@ -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', '-Command', `(New-Object Media.SoundPlayer '${file}').PlaySync()`],\n { stdio: 'ignore', windowsHide: true
|
|
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"}
|
|
@@ -81,6 +81,7 @@ try {
|
|
|
81
81
|
encoding: 'utf8',
|
|
82
82
|
timeout: 10000,
|
|
83
83
|
stdio: ['pipe', 'pipe', 'pipe'],
|
|
84
|
+
windowsHide: true,
|
|
84
85
|
}).trim();
|
|
85
86
|
|
|
86
87
|
if (!nameWithOwner || !nameWithOwner.includes('/')) {
|
|
@@ -95,7 +96,7 @@ try {
|
|
|
95
96
|
try {
|
|
96
97
|
const milestonesRaw = execSync(
|
|
97
98
|
'gh api repos/' + owner + '/' + repo + '/milestones --jq "."',
|
|
98
|
-
{ encoding: 'utf8', timeout: 10000, stdio: ['pipe', 'pipe', 'pipe'] }
|
|
99
|
+
{ encoding: 'utf8', timeout: 10000, stdio: ['pipe', 'pipe', 'pipe'], windowsHide: true }
|
|
99
100
|
).trim();
|
|
100
101
|
if (milestonesRaw) {
|
|
101
102
|
const milestones = JSON.parse(milestonesRaw);
|
|
@@ -118,7 +119,7 @@ try {
|
|
|
118
119
|
try {
|
|
119
120
|
const phaseRaw = execSync(
|
|
120
121
|
'gh api "repos/' + owner + '/' + repo + '/issues?state=open&labels=phase&per_page=1&sort=updated&direction=desc" --jq ".[0] | {number: .number, title: .title}"',
|
|
121
|
-
{ encoding: 'utf8', timeout: 10000, stdio: ['pipe', 'pipe', 'pipe'] }
|
|
122
|
+
{ encoding: 'utf8', timeout: 10000, stdio: ['pipe', 'pipe', 'pipe'], windowsHide: true }
|
|
122
123
|
).trim();
|
|
123
124
|
const phaseData = JSON.parse(phaseRaw || '{}');
|
|
124
125
|
const titleMatch = (phaseData.title || '').match(/^\\[Phase\\s+(\\S+)\\]/);
|
|
@@ -137,7 +138,7 @@ try {
|
|
|
137
138
|
const gqlQuery = '{ repository(owner: "' + owner + '", name: "' + repo + '") { issue(number: ' + issueNumber + ') { projectItems(first: 5, includeArchived: false) { nodes { fieldValueByName(name: "Status") { ... on ProjectV2ItemFieldSingleSelectValue { name } } } } } } }';
|
|
138
139
|
const boardRaw = execSync(
|
|
139
140
|
'gh api graphql -f query=@-',
|
|
140
|
-
{ input: gqlQuery, encoding: 'utf8', timeout: 10000, stdio: ['pipe', 'pipe', 'pipe'] }
|
|
141
|
+
{ input: gqlQuery, encoding: 'utf8', timeout: 10000, stdio: ['pipe', 'pipe', 'pipe'], windowsHide: true }
|
|
141
142
|
).trim();
|
|
142
143
|
const boardData = JSON.parse(boardRaw);
|
|
143
144
|
const nodes = boardData?.data?.repository?.issue?.projectItems?.nodes || [];
|
|
@@ -177,10 +178,11 @@ try {
|
|
|
177
178
|
process.exit(0);
|
|
178
179
|
}
|
|
179
180
|
`;
|
|
181
|
+
const isWindows = process.platform === "win32";
|
|
180
182
|
(0, node_child_process.spawn)(process.execPath, ["-e", script], {
|
|
181
183
|
stdio: "ignore",
|
|
182
184
|
windowsHide: true,
|
|
183
|
-
detached:
|
|
185
|
+
detached: !isWindows
|
|
184
186
|
}).unref();
|
|
185
187
|
} catch {}
|
|
186
188
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"maxsim-statusline.cjs","names":["path","fs"],"sources":["../../../src/hooks/shared.ts","../../../src/hooks/maxsim-statusline.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', '-Command', `(New-Object Media.SoundPlayer '${file}').PlaySync()`],\n { stdio: 'ignore', windowsHide: true, detached: 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 * Claude Code Statusline - MAXSIM Edition\n * Shows: [update] model | P{N} {BoardColumn} | {milestone}: {pct}% | dirname\n */\n\nimport * as fs from 'node:fs';\nimport * as path from 'node:path';\nimport { spawn } from 'node:child_process';\nimport { readStdinJson, CLAUDE_DIR } from './shared';\n\nexport interface StatuslineInput {\n model?: { display_name?: string };\n workspace?: { current_dir?: string; project_dir?: string };\n session_id?: string;\n}\n\nexport interface ProgressCache {\n phase_number: string | null;\n milestone_title: string | null;\n milestone_pct: number;\n board_column: string | null;\n offline?: boolean;\n updated: number;\n}\n\nconst CACHE_TTL_SECONDS = 60;\n\n/**\n * Spawn a detached Node child process to refresh the progress cache in the background.\n * The child runs gh CLI commands to detect owner/repo, find the first open milestone,\n * compute progress, and find the current phase label.\n */\nfunction spawnBackgroundRefresh(cacheDir: string, cacheFile: string): void {\n try {\n const script = `\nconst { execSync } = require('child_process');\nconst fs = require('fs');\nconst path = require('path');\n\ntry {\n // Detect owner/repo\n const nameWithOwner = execSync('gh repo view --json nameWithOwner -q .nameWithOwner', {\n encoding: 'utf8',\n timeout: 10000,\n stdio: ['pipe', 'pipe', 'pipe'],\n }).trim();\n\n if (!nameWithOwner || !nameWithOwner.includes('/')) {\n process.exit(0);\n }\n\n const [owner, repo] = nameWithOwner.split('/');\n\n // Get milestones\n let milestoneTitle = null;\n let milestonePct = 0;\n try {\n const milestonesRaw = execSync(\n 'gh api repos/' + owner + '/' + repo + '/milestones --jq \".\"',\n { encoding: 'utf8', timeout: 10000, stdio: ['pipe', 'pipe', 'pipe'] }\n ).trim();\n if (milestonesRaw) {\n const milestones = JSON.parse(milestonesRaw);\n const openMilestone = milestones.find(function(m) { return m.state === 'open'; });\n if (openMilestone) {\n milestoneTitle = openMilestone.title || null;\n const total = (openMilestone.open_issues || 0) + (openMilestone.closed_issues || 0);\n if (total > 0) {\n milestonePct = Math.round(((openMilestone.closed_issues || 0) / total) * 100);\n }\n }\n }\n } catch (e) {\n // gh api failed for milestones, continue with defaults\n }\n\n // Get current phase from open issues with 'phase' label, parse number from title\n let phaseNumber = null;\n let issueNumber = null;\n try {\n const phaseRaw = execSync(\n 'gh api \"repos/' + owner + '/' + repo + '/issues?state=open&labels=phase&per_page=1&sort=updated&direction=desc\" --jq \".[0] | {number: .number, title: .title}\"',\n { encoding: 'utf8', timeout: 10000, stdio: ['pipe', 'pipe', 'pipe'] }\n ).trim();\n const phaseData = JSON.parse(phaseRaw || '{}');\n const titleMatch = (phaseData.title || '').match(/^\\\\[Phase\\\\s+(\\\\S+)\\\\]/);\n if (titleMatch) {\n phaseNumber = titleMatch[1];\n }\n issueNumber = phaseData.number || null;\n } catch (e) {\n // gh api failed for phase, continue with null\n }\n\n // Get board column via GraphQL\n let boardColumn = null;\n if (issueNumber) {\n try {\n const gqlQuery = '{ repository(owner: \"' + owner + '\", name: \"' + repo + '\") { issue(number: ' + issueNumber + ') { projectItems(first: 5, includeArchived: false) { nodes { fieldValueByName(name: \"Status\") { ... on ProjectV2ItemFieldSingleSelectValue { name } } } } } } }';\n const boardRaw = execSync(\n 'gh api graphql -f query=@-',\n { input: gqlQuery, encoding: 'utf8', timeout: 10000, stdio: ['pipe', 'pipe', 'pipe'] }\n ).trim();\n const boardData = JSON.parse(boardRaw);\n const nodes = boardData?.data?.repository?.issue?.projectItems?.nodes || [];\n if (nodes.length > 0 && nodes[0]?.fieldValueByName?.name) {\n boardColumn = nodes[0].fieldValueByName.name;\n }\n } catch (e) {\n boardColumn = null;\n }\n }\n\n // Write cache\n const cacheData = JSON.stringify({\n phase_number: phaseNumber,\n milestone_title: milestoneTitle,\n milestone_pct: milestonePct,\n board_column: boardColumn,\n updated: Math.floor(Date.now() / 1000),\n });\n\n const dir = ${JSON.stringify(cacheDir)};\n fs.mkdirSync(dir, { recursive: true });\n fs.writeFileSync(${JSON.stringify(cacheFile)}, cacheData);\n} catch (e) {\n try {\n const dir = ${JSON.stringify(cacheDir)};\n fs.mkdirSync(dir, { recursive: true });\n fs.writeFileSync(${JSON.stringify(cacheFile)}, JSON.stringify({\n phase_number: null,\n milestone_title: null,\n milestone_pct: 0,\n board_column: null,\n offline: true,\n updated: Math.floor(Date.now() / 1000),\n }));\n } catch (_) {}\n process.exit(0);\n}\n`;\n\n const child = spawn(process.execPath, ['-e', script], {\n stdio: 'ignore',\n windowsHide: true,\n detached: true,\n });\n child.unref();\n } catch {\n // Silent fail -- never break statusline\n }\n}\n\nexport function formatStatusline(data: StatuslineInput): string {\n const model = data.model?.display_name || 'Claude';\n const dir = data.workspace?.project_dir || data.workspace?.current_dir || process.cwd();\n const dirname = path.basename(dir);\n\n const SEP = ' \\u2502 ';\n const DIM = '\\x1b[2m';\n const RESET = '\\x1b[0m';\n\n // MAXSIM update available?\n let updateIndicator = '';\n const updateCacheFile = path.join(dir, CLAUDE_DIR, 'cache', 'maxsim-update-check.json');\n if (fs.existsSync(updateCacheFile)) {\n try {\n const cache = JSON.parse(fs.readFileSync(updateCacheFile, 'utf8'));\n if (cache.update_available) {\n updateIndicator = '\\x1b[33m\\u2B06\\x1b[0m ';\n }\n } catch {\n // ignore\n }\n }\n\n // Check if this is a MAXSIM project\n const planningDir = path.join(dir, '.planning');\n const isMaxsimProject = fs.existsSync(planningDir);\n\n if (!isMaxsimProject) {\n return `${updateIndicator}${DIM}${model}${RESET}${SEP}${DIM}${dirname}${RESET}`;\n }\n\n // Read progress cache\n const cacheDir = path.join(dir, CLAUDE_DIR, 'cache');\n const cacheFile = path.join(cacheDir, 'maxsim-progress.json');\n let cache: ProgressCache | null = null;\n let cacheAge = Infinity;\n\n if (fs.existsSync(cacheFile)) {\n try {\n cache = JSON.parse(fs.readFileSync(cacheFile, 'utf8')) as ProgressCache;\n cacheAge = Math.floor(Date.now() / 1000) - (cache.updated || 0);\n } catch {\n cache = null;\n }\n }\n\n // Spawn background refresh if cache is stale or missing\n if (cacheAge > CACHE_TTL_SECONDS) {\n spawnBackgroundRefresh(cacheDir, cacheFile);\n }\n\n // Offline fallback\n if (cache?.offline) {\n return `${updateIndicator}${DIM}${model}${RESET}${SEP}${DIM}P? offline${RESET}${SEP}${DIM}${dirname}${RESET}`;\n }\n\n // Build phase segment: P{N} {BoardColumn}\n let phaseSegment = '';\n if (cache?.phase_number) {\n const column = cache.board_column ? ` ${cache.board_column}` : '';\n phaseSegment = `${SEP}${DIM}P${cache.phase_number}${column}${RESET}`;\n }\n\n // Build milestone segment\n let milestoneSegment = '';\n if (cache?.milestone_title) {\n milestoneSegment = `${SEP}${DIM}${cache.milestone_title}: ${cache.milestone_pct}%${RESET}`;\n }\n\n return `${updateIndicator}${DIM}${model}${RESET}${phaseSegment}${milestoneSegment}${SEP}${DIM}${dirname}${RESET}`;\n}\n\n// Standalone entry\nif (require.main === module) {\n readStdinJson<StatuslineInput>((data) => {\n process.stdout.write(formatStatusline(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;;;AAIJ,MAAa,aAAa;;;;;;;;ACE1B,MAAM,oBAAoB;;;;;;AAO1B,SAAS,uBAAuB,UAAkB,WAAyB;AACzE,KAAI;EACF,MAAM,SAAS;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;gBAwFH,KAAK,UAAU,SAAS,CAAC;;qBAEpB,KAAK,UAAU,UAAU,CAAC;;;kBAG7B,KAAK,UAAU,SAAS,CAAC;;uBAEpB,KAAK,UAAU,UAAU,CAAC;;;;;;;;;;;;AAkB7C,gCALoB,QAAQ,UAAU,CAAC,MAAM,OAAO,EAAE;GACpD,OAAO;GACP,aAAa;GACb,UAAU;GACX,CAAC,CACI,OAAO;SACP;;AAKV,SAAgB,iBAAiB,MAA+B;CAC9D,MAAM,QAAQ,KAAK,OAAO,gBAAgB;CAC1C,MAAM,MAAM,KAAK,WAAW,eAAe,KAAK,WAAW,eAAe,QAAQ,KAAK;CACvF,MAAM,UAAUA,UAAK,SAAS,IAAI;CAElC,MAAM,MAAM;CACZ,MAAM,MAAM;CACZ,MAAM,QAAQ;CAGd,IAAI,kBAAkB;CACtB,MAAM,kBAAkBA,UAAK,KAAK,KAAK,YAAY,SAAS,2BAA2B;AACvF,KAAIC,QAAG,WAAW,gBAAgB,CAChC,KAAI;AAEF,MADc,KAAK,MAAMA,QAAG,aAAa,iBAAiB,OAAO,CAAC,CACxD,iBACR,mBAAkB;SAEd;CAMV,MAAM,cAAcD,UAAK,KAAK,KAAK,YAAY;AAG/C,KAAI,CAFoBC,QAAG,WAAW,YAAY,CAGhD,QAAO,GAAG,kBAAkB,MAAM,QAAQ,QAAQ,MAAM,MAAM,UAAU;CAI1E,MAAM,WAAWD,UAAK,KAAK,KAAK,YAAY,QAAQ;CACpD,MAAM,YAAYA,UAAK,KAAK,UAAU,uBAAuB;CAC7D,IAAI,QAA8B;CAClC,IAAI,WAAW;AAEf,KAAIC,QAAG,WAAW,UAAU,CAC1B,KAAI;AACF,UAAQ,KAAK,MAAMA,QAAG,aAAa,WAAW,OAAO,CAAC;AACtD,aAAW,KAAK,MAAM,KAAK,KAAK,GAAG,IAAK,IAAI,MAAM,WAAW;SACvD;AACN,UAAQ;;AAKZ,KAAI,WAAW,kBACb,wBAAuB,UAAU,UAAU;AAI7C,KAAI,OAAO,QACT,QAAO,GAAG,kBAAkB,MAAM,QAAQ,QAAQ,MAAM,IAAI,YAAY,QAAQ,MAAM,MAAM,UAAU;CAIxG,IAAI,eAAe;AACnB,KAAI,OAAO,cAAc;EACvB,MAAM,SAAS,MAAM,eAAe,IAAI,MAAM,iBAAiB;AAC/D,iBAAe,GAAG,MAAM,IAAI,GAAG,MAAM,eAAe,SAAS;;CAI/D,IAAI,mBAAmB;AACvB,KAAI,OAAO,gBACT,oBAAmB,GAAG,MAAM,MAAM,MAAM,gBAAgB,IAAI,MAAM,cAAc,GAAG;AAGrF,QAAO,GAAG,kBAAkB,MAAM,QAAQ,QAAQ,eAAe,mBAAmB,MAAM,MAAM,UAAU;;AAI5G,IAAI,QAAQ,SAAS,OACnB,gBAAgC,SAAS;AACvC,SAAQ,OAAO,MAAM,iBAAiB,KAAK,CAAC;EAC5C"}
|
|
1
|
+
{"version":3,"file":"maxsim-statusline.cjs","names":["path","fs"],"sources":["../../../src/hooks/shared.ts","../../../src/hooks/maxsim-statusline.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 * Claude Code Statusline - MAXSIM Edition\n * Shows: [update] model | P{N} {BoardColumn} | {milestone}: {pct}% | dirname\n */\n\nimport * as fs from 'node:fs';\nimport * as path from 'node:path';\nimport { spawn } from 'node:child_process';\nimport { readStdinJson, CLAUDE_DIR } from './shared';\n\nexport interface StatuslineInput {\n model?: { display_name?: string };\n workspace?: { current_dir?: string; project_dir?: string };\n session_id?: string;\n}\n\nexport interface ProgressCache {\n phase_number: string | null;\n milestone_title: string | null;\n milestone_pct: number;\n board_column: string | null;\n offline?: boolean;\n updated: number;\n}\n\nconst CACHE_TTL_SECONDS = 60;\n\n/**\n * Spawn a detached Node child process to refresh the progress cache in the background.\n * The child runs gh CLI commands to detect owner/repo, find the first open milestone,\n * compute progress, and find the current phase label.\n */\nfunction spawnBackgroundRefresh(cacheDir: string, cacheFile: string): void {\n try {\n const script = `\nconst { execSync } = require('child_process');\nconst fs = require('fs');\nconst path = require('path');\n\ntry {\n // Detect owner/repo\n const nameWithOwner = execSync('gh repo view --json nameWithOwner -q .nameWithOwner', {\n encoding: 'utf8',\n timeout: 10000,\n stdio: ['pipe', 'pipe', 'pipe'],\n windowsHide: true,\n }).trim();\n\n if (!nameWithOwner || !nameWithOwner.includes('/')) {\n process.exit(0);\n }\n\n const [owner, repo] = nameWithOwner.split('/');\n\n // Get milestones\n let milestoneTitle = null;\n let milestonePct = 0;\n try {\n const milestonesRaw = execSync(\n 'gh api repos/' + owner + '/' + repo + '/milestones --jq \".\"',\n { encoding: 'utf8', timeout: 10000, stdio: ['pipe', 'pipe', 'pipe'], windowsHide: true }\n ).trim();\n if (milestonesRaw) {\n const milestones = JSON.parse(milestonesRaw);\n const openMilestone = milestones.find(function(m) { return m.state === 'open'; });\n if (openMilestone) {\n milestoneTitle = openMilestone.title || null;\n const total = (openMilestone.open_issues || 0) + (openMilestone.closed_issues || 0);\n if (total > 0) {\n milestonePct = Math.round(((openMilestone.closed_issues || 0) / total) * 100);\n }\n }\n }\n } catch (e) {\n // gh api failed for milestones, continue with defaults\n }\n\n // Get current phase from open issues with 'phase' label, parse number from title\n let phaseNumber = null;\n let issueNumber = null;\n try {\n const phaseRaw = execSync(\n 'gh api \"repos/' + owner + '/' + repo + '/issues?state=open&labels=phase&per_page=1&sort=updated&direction=desc\" --jq \".[0] | {number: .number, title: .title}\"',\n { encoding: 'utf8', timeout: 10000, stdio: ['pipe', 'pipe', 'pipe'], windowsHide: true }\n ).trim();\n const phaseData = JSON.parse(phaseRaw || '{}');\n const titleMatch = (phaseData.title || '').match(/^\\\\[Phase\\\\s+(\\\\S+)\\\\]/);\n if (titleMatch) {\n phaseNumber = titleMatch[1];\n }\n issueNumber = phaseData.number || null;\n } catch (e) {\n // gh api failed for phase, continue with null\n }\n\n // Get board column via GraphQL\n let boardColumn = null;\n if (issueNumber) {\n try {\n const gqlQuery = '{ repository(owner: \"' + owner + '\", name: \"' + repo + '\") { issue(number: ' + issueNumber + ') { projectItems(first: 5, includeArchived: false) { nodes { fieldValueByName(name: \"Status\") { ... on ProjectV2ItemFieldSingleSelectValue { name } } } } } } }';\n const boardRaw = execSync(\n 'gh api graphql -f query=@-',\n { input: gqlQuery, encoding: 'utf8', timeout: 10000, stdio: ['pipe', 'pipe', 'pipe'], windowsHide: true }\n ).trim();\n const boardData = JSON.parse(boardRaw);\n const nodes = boardData?.data?.repository?.issue?.projectItems?.nodes || [];\n if (nodes.length > 0 && nodes[0]?.fieldValueByName?.name) {\n boardColumn = nodes[0].fieldValueByName.name;\n }\n } catch (e) {\n boardColumn = null;\n }\n }\n\n // Write cache\n const cacheData = JSON.stringify({\n phase_number: phaseNumber,\n milestone_title: milestoneTitle,\n milestone_pct: milestonePct,\n board_column: boardColumn,\n updated: Math.floor(Date.now() / 1000),\n });\n\n const dir = ${JSON.stringify(cacheDir)};\n fs.mkdirSync(dir, { recursive: true });\n fs.writeFileSync(${JSON.stringify(cacheFile)}, cacheData);\n} catch (e) {\n try {\n const dir = ${JSON.stringify(cacheDir)};\n fs.mkdirSync(dir, { recursive: true });\n fs.writeFileSync(${JSON.stringify(cacheFile)}, JSON.stringify({\n phase_number: null,\n milestone_title: null,\n milestone_pct: 0,\n board_column: null,\n offline: true,\n updated: Math.floor(Date.now() / 1000),\n }));\n } catch (_) {}\n process.exit(0);\n}\n`;\n\n const isWindows = process.platform === 'win32';\n const child = spawn(process.execPath, ['-e', script], {\n stdio: 'ignore',\n windowsHide: true,\n detached: !isWindows,\n });\n child.unref();\n } catch {\n // Silent fail -- never break statusline\n }\n}\n\nexport function formatStatusline(data: StatuslineInput): string {\n const model = data.model?.display_name || 'Claude';\n const dir = data.workspace?.project_dir || data.workspace?.current_dir || process.cwd();\n const dirname = path.basename(dir);\n\n const SEP = ' \\u2502 ';\n const DIM = '\\x1b[2m';\n const RESET = '\\x1b[0m';\n\n // MAXSIM update available?\n let updateIndicator = '';\n const updateCacheFile = path.join(dir, CLAUDE_DIR, 'cache', 'maxsim-update-check.json');\n if (fs.existsSync(updateCacheFile)) {\n try {\n const cache = JSON.parse(fs.readFileSync(updateCacheFile, 'utf8'));\n if (cache.update_available) {\n updateIndicator = '\\x1b[33m\\u2B06\\x1b[0m ';\n }\n } catch {\n // ignore\n }\n }\n\n // Check if this is a MAXSIM project\n const planningDir = path.join(dir, '.planning');\n const isMaxsimProject = fs.existsSync(planningDir);\n\n if (!isMaxsimProject) {\n return `${updateIndicator}${DIM}${model}${RESET}${SEP}${DIM}${dirname}${RESET}`;\n }\n\n // Read progress cache\n const cacheDir = path.join(dir, CLAUDE_DIR, 'cache');\n const cacheFile = path.join(cacheDir, 'maxsim-progress.json');\n let cache: ProgressCache | null = null;\n let cacheAge = Infinity;\n\n if (fs.existsSync(cacheFile)) {\n try {\n cache = JSON.parse(fs.readFileSync(cacheFile, 'utf8')) as ProgressCache;\n cacheAge = Math.floor(Date.now() / 1000) - (cache.updated || 0);\n } catch {\n cache = null;\n }\n }\n\n // Spawn background refresh if cache is stale or missing\n if (cacheAge > CACHE_TTL_SECONDS) {\n spawnBackgroundRefresh(cacheDir, cacheFile);\n }\n\n // Offline fallback\n if (cache?.offline) {\n return `${updateIndicator}${DIM}${model}${RESET}${SEP}${DIM}P? offline${RESET}${SEP}${DIM}${dirname}${RESET}`;\n }\n\n // Build phase segment: P{N} {BoardColumn}\n let phaseSegment = '';\n if (cache?.phase_number) {\n const column = cache.board_column ? ` ${cache.board_column}` : '';\n phaseSegment = `${SEP}${DIM}P${cache.phase_number}${column}${RESET}`;\n }\n\n // Build milestone segment\n let milestoneSegment = '';\n if (cache?.milestone_title) {\n milestoneSegment = `${SEP}${DIM}${cache.milestone_title}: ${cache.milestone_pct}%${RESET}`;\n }\n\n return `${updateIndicator}${DIM}${model}${RESET}${phaseSegment}${milestoneSegment}${SEP}${DIM}${dirname}${RESET}`;\n}\n\n// Standalone entry\nif (require.main === module) {\n readStdinJson<StatuslineInput>((data) => {\n process.stdout.write(formatStatusline(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;;;AAIJ,MAAa,aAAa;;;;;;;;ACE1B,MAAM,oBAAoB;;;;;;AAO1B,SAAS,uBAAuB,UAAkB,WAAyB;AACzE,KAAI;EACF,MAAM,SAAS;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;gBAyFH,KAAK,UAAU,SAAS,CAAC;;qBAEpB,KAAK,UAAU,UAAU,CAAC;;;kBAG7B,KAAK,UAAU,SAAS,CAAC;;uBAEpB,KAAK,UAAU,UAAU,CAAC;;;;;;;;;;;;EAa7C,MAAM,YAAY,QAAQ,aAAa;AAMvC,gCALoB,QAAQ,UAAU,CAAC,MAAM,OAAO,EAAE;GACpD,OAAO;GACP,aAAa;GACb,UAAU,CAAC;GACZ,CAAC,CACI,OAAO;SACP;;AAKV,SAAgB,iBAAiB,MAA+B;CAC9D,MAAM,QAAQ,KAAK,OAAO,gBAAgB;CAC1C,MAAM,MAAM,KAAK,WAAW,eAAe,KAAK,WAAW,eAAe,QAAQ,KAAK;CACvF,MAAM,UAAUA,UAAK,SAAS,IAAI;CAElC,MAAM,MAAM;CACZ,MAAM,MAAM;CACZ,MAAM,QAAQ;CAGd,IAAI,kBAAkB;CACtB,MAAM,kBAAkBA,UAAK,KAAK,KAAK,YAAY,SAAS,2BAA2B;AACvF,KAAIC,QAAG,WAAW,gBAAgB,CAChC,KAAI;AAEF,MADc,KAAK,MAAMA,QAAG,aAAa,iBAAiB,OAAO,CAAC,CACxD,iBACR,mBAAkB;SAEd;CAMV,MAAM,cAAcD,UAAK,KAAK,KAAK,YAAY;AAG/C,KAAI,CAFoBC,QAAG,WAAW,YAAY,CAGhD,QAAO,GAAG,kBAAkB,MAAM,QAAQ,QAAQ,MAAM,MAAM,UAAU;CAI1E,MAAM,WAAWD,UAAK,KAAK,KAAK,YAAY,QAAQ;CACpD,MAAM,YAAYA,UAAK,KAAK,UAAU,uBAAuB;CAC7D,IAAI,QAA8B;CAClC,IAAI,WAAW;AAEf,KAAIC,QAAG,WAAW,UAAU,CAC1B,KAAI;AACF,UAAQ,KAAK,MAAMA,QAAG,aAAa,WAAW,OAAO,CAAC;AACtD,aAAW,KAAK,MAAM,KAAK,KAAK,GAAG,IAAK,IAAI,MAAM,WAAW;SACvD;AACN,UAAQ;;AAKZ,KAAI,WAAW,kBACb,wBAAuB,UAAU,UAAU;AAI7C,KAAI,OAAO,QACT,QAAO,GAAG,kBAAkB,MAAM,QAAQ,QAAQ,MAAM,IAAI,YAAY,QAAQ,MAAM,MAAM,UAAU;CAIxG,IAAI,eAAe;AACnB,KAAI,OAAO,cAAc;EACvB,MAAM,SAAS,MAAM,eAAe,IAAI,MAAM,iBAAiB;AAC/D,iBAAe,GAAG,MAAM,IAAI,GAAG,MAAM,eAAe,SAAS;;CAI/D,IAAI,mBAAmB;AACvB,KAAI,OAAO,gBACT,oBAAmB,GAAG,MAAM,MAAM,MAAM,gBAAgB,IAAI,MAAM,cAAc,GAAG;AAGrF,QAAO,GAAG,kBAAkB,MAAM,QAAQ,QAAQ,eAAe,mBAAmB,MAAM,MAAM,UAAU;;AAI5G,IAAI,QAAQ,SAAS,OACnB,gBAAgC,SAAS;AACvC,SAAQ,OAAO,MAAM,iBAAiB,KAAK,CAAC;EAC5C"}
|
|
@@ -34,12 +34,13 @@ function playSound(type) {
|
|
|
34
34
|
const { spawn } = require("node:child_process");
|
|
35
35
|
spawn("powershell", [
|
|
36
36
|
"-NoProfile",
|
|
37
|
+
"-WindowStyle",
|
|
38
|
+
"Hidden",
|
|
37
39
|
"-Command",
|
|
38
40
|
`(New-Object Media.SoundPlayer '${file}').PlaySync()`
|
|
39
41
|
], {
|
|
40
42
|
stdio: "ignore",
|
|
41
|
-
windowsHide: true
|
|
42
|
-
detached: true
|
|
43
|
+
windowsHide: true
|
|
43
44
|
}).unref();
|
|
44
45
|
} else if (platform === "darwin") {
|
|
45
46
|
const file = type === "question" ? "/System/Library/Sounds/Ping.aiff" : "/System/Library/Sounds/Glass.aiff";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"maxsim-stop-sound.cjs","names":[],"sources":["../../../src/hooks/shared.ts","../../../src/hooks/maxsim-stop-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', '-Command', `(New-Object Media.SoundPlayer '${file}').PlaySync()`],\n { stdio: 'ignore', windowsHide: true
|
|
1
|
+
{"version":3,"file":"maxsim-stop-sound.cjs","names":[],"sources":["../../../src/hooks/shared.ts","../../../src/hooks/maxsim-stop-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 * Stop Sound Hook — Stop event hook that plays a sound\n * when Claude finishes working.\n */\n\nimport { readStdinJson, playSound } from './shared';\n\ninterface StopSoundInput {\n stop_hook_active?: boolean;\n}\n\nexport function processStopSound(data: StopSoundInput): void {\n playSound('stop');\n}\n\n// Standalone entry\nif (require.main === module) {\n readStdinJson<StopSoundInput>((data) => {\n processStopSound(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,iBAAiB,MAA4B;AAC3D,WAAU,OAAO;;AAInB,IAAI,QAAQ,SAAS,OACnB,gBAA+B,SAAS;AACtC,kBAAiB,KAAK;EACtB"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"maxsim-sync-reminder.cjs","names":[],"sources":["../../../src/hooks/shared.ts","../../../src/hooks/maxsim-sync-reminder.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', '-Command', `(New-Object Media.SoundPlayer '${file}').PlaySync()`],\n { stdio: 'ignore', windowsHide: true
|
|
1
|
+
{"version":3,"file":"maxsim-sync-reminder.cjs","names":[],"sources":["../../../src/hooks/shared.ts","../../../src/hooks/maxsim-sync-reminder.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 * Sync Reminder Hook — No longer needed.\n * GitHub Issues is the sole source of truth for phase artifacts and todos.\n * Local .planning/ writes no longer need sync reminders.\n */\n\nimport { readStdinJson } from './shared';\n\nexport interface SyncReminderInput {\n session_id?: string;\n cwd?: string;\n tool_input?: { file_path?: string };\n}\n\nexport interface SyncReminderOutput {\n hookSpecificOutput: {\n hookEventName: string;\n additionalContext: string;\n };\n}\n\nexport const DEBOUNCE_CALLS = 10;\n\nexport function processSyncReminder(\n _data: SyncReminderInput,\n): SyncReminderOutput | null {\n // No-op: GitHub Issues is SSOT for phase artifacts and todos.\n return null;\n}\n\n// Standalone entry\nif (require.main === module) {\n readStdinJson<SyncReminderInput>((data) => {\n const result = processSyncReminder(data);\n if (result) {\n process.stdout.write(JSON.stringify(result));\n }\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;;;;;;;;;;ACEJ,MAAa,iBAAiB;AAE9B,SAAgB,oBACd,OAC2B;AAE3B,QAAO;;AAIT,IAAI,QAAQ,SAAS,OACnB,gBAAkC,SAAS;CACzC,MAAM,SAAS,oBAAoB,KAAK;AACxC,KAAI,OACF,SAAQ,OAAO,MAAM,KAAK,UAAU,OAAO,CAAC;EAE9C"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: maxsim:quick
|
|
3
|
-
description: Execute a quick task with MAXSIM guarantees
|
|
4
|
-
argument-hint: "[--full]
|
|
3
|
+
description: Execute a quick task with MAXSIM guarantees
|
|
4
|
+
argument-hint: "[--full]"
|
|
5
5
|
allowed-tools:
|
|
6
6
|
- Read
|
|
7
7
|
- Write
|
|
@@ -13,7 +13,7 @@ allowed-tools:
|
|
|
13
13
|
- AskUserQuestion
|
|
14
14
|
---
|
|
15
15
|
<objective>
|
|
16
|
-
Execute small, ad-hoc tasks with MAXSIM guarantees (atomic commits, STATE.md tracking).
|
|
16
|
+
Execute small, ad-hoc tasks with MAXSIM guarantees (atomic commits, STATE.md tracking).
|
|
17
17
|
|
|
18
18
|
Quick mode is the same system with a shorter path:
|
|
19
19
|
- Spawns planner (quick mode) + executor(s)
|
|
@@ -23,8 +23,6 @@ Quick mode is the same system with a shorter path:
|
|
|
23
23
|
**Default:** Skips research, plan-checker, verifier. Use when you know exactly what to do.
|
|
24
24
|
|
|
25
25
|
**`--full` flag:** Enables plan-checking (max 2 iterations) and post-execution verification. Use when you want quality guarantees without full milestone ceremony.
|
|
26
|
-
|
|
27
|
-
**`--todo` flag:** Enters todo management mode. List, capture, complete, and triage todos without executing tasks. "Save for later" creates a GitHub Issue with 'todo' label (primary) with local file cache.
|
|
28
26
|
</objective>
|
|
29
27
|
|
|
30
28
|
<execution_context>
|
|
@@ -44,8 +44,6 @@ You are a thinking partner, not a task executor. Your role is to help the user a
|
|
|
44
44
|
|
|
45
45
|
**Phase discussion** (discuss-phase): Focus on implementation decisions, gray area resolution, downstream impact. Challenge hand-wavy integration plans. Push for concrete acceptance criteria per deliverable.
|
|
46
46
|
|
|
47
|
-
**Todo/bug triage** (/maxsim:quick --todo triage): Focus on problem definition, scope containment, approach selection. Shorter rounds — 2-3 questions vs 4. Time-boxed to 20-30 min. Don't over-explore — capture enough to unblock, not to solve.
|
|
48
|
-
|
|
49
47
|
**General discussion**: Default behaviors from core_behaviors apply. Read the energy — if the user is exploring, explore with them. If they want a quick answer, give it.
|
|
50
48
|
|
|
51
49
|
</context_modes>
|
|
@@ -37,12 +37,12 @@ Add `--raw` to get machine-readable JSON output (no formatting).
|
|
|
37
37
|
### Issue Operations
|
|
38
38
|
| Command | Description |
|
|
39
39
|
|---------|-------------|
|
|
40
|
-
| `github get-issue N [--comments]` | Get issue details (with optional comments) |
|
|
40
|
+
| `github get-issue N [--include-comments]` | Get issue details (with optional comments) |
|
|
41
41
|
| `github list-sub-issues N` | List sub-issues of a phase issue |
|
|
42
42
|
| `github close-issue N [--reason "..."] [--state-reason completed\|not_planned]` | Close issue |
|
|
43
43
|
| `github reopen-issue N` | Reopen closed issue |
|
|
44
44
|
| `github bounce-issue --issue-number N --reason "feedback"` | Bounce to In Progress with feedback |
|
|
45
|
-
| `github move-issue --
|
|
45
|
+
| `github move-issue --issue-number N --status "STATUS"` | Move to board column (To Do, In Progress, In Review, Done) |
|
|
46
46
|
| `github detect-external-edits --phase-number "01"` | Check body hash mismatch |
|
|
47
47
|
|
|
48
48
|
### Board Operations
|
|
@@ -60,13 +60,6 @@ Add `--raw` to get machine-readable JSON output (no formatting).
|
|
|
60
60
|
| `github all-progress` | All phases progress overview |
|
|
61
61
|
| `github detect-interrupted --phase-issue-number N` | Detect interrupted phase |
|
|
62
62
|
|
|
63
|
-
### Todos
|
|
64
|
-
| Command | Description |
|
|
65
|
-
|---------|-------------|
|
|
66
|
-
| `github add-todo --title "T" [--description "D"] [--area A] [--phase P]` | Create todo issue |
|
|
67
|
-
| `github complete-todo --todo-id "N" [--github-issue-number N]` | Complete todo |
|
|
68
|
-
| `github list-todos [--area A] [--status pending\|completed\|all]` | List todos |
|
|
69
|
-
|
|
70
63
|
### Convenience
|
|
71
64
|
| Command | Description |
|
|
72
65
|
|---------|-------------|
|
|
@@ -54,12 +54,6 @@ Recent decisions affecting current work:
|
|
|
54
54
|
- [Phase X]: [Decision summary]
|
|
55
55
|
- [Phase Y]: [Decision summary]
|
|
56
56
|
|
|
57
|
-
### Pending Todos
|
|
58
|
-
|
|
59
|
-
[From GitHub Issues with label 'todo' — ideas captured during sessions]
|
|
60
|
-
|
|
61
|
-
None yet.
|
|
62
|
-
|
|
63
57
|
### Blockers/Concerns
|
|
64
58
|
|
|
65
59
|
[Issues that affect future work]
|
|
@@ -145,10 +139,6 @@ Updated after each plan completion.
|
|
|
145
139
|
|
|
146
140
|
**Decisions:** Reference to PROJECT.md Key Decisions table, plus recent decisions summary for quick access. Full decision log lives in PROJECT.md.
|
|
147
141
|
|
|
148
|
-
**Pending Todos:** Ideas captured via /maxsim:quick --todo (tracked as GitHub Issues)
|
|
149
|
-
- Count of pending todos
|
|
150
|
-
- Brief list if few, count if many (e.g., "5 pending todos — see /maxsim:quick --todo")
|
|
151
|
-
|
|
152
142
|
**Blockers/Concerns:** From "Next Phase Readiness" sections
|
|
153
143
|
- Issues that affect future work
|
|
154
144
|
- Prefix with originating phase
|
|
@@ -42,7 +42,7 @@ Exit workflow.
|
|
|
42
42
|
When GitHub integration is active (phase_issue_number is set), check for plan comments on the phase issue before reporting no plans:
|
|
43
43
|
|
|
44
44
|
```bash
|
|
45
|
-
node ~/.claude/maxsim/bin/maxsim-tools.cjs github get-issue $PHASE_ISSUE_NUMBER --comments
|
|
45
|
+
node ~/.claude/maxsim/bin/maxsim-tools.cjs github get-issue $PHASE_ISSUE_NUMBER --include-comments
|
|
46
46
|
```
|
|
47
47
|
|
|
48
48
|
Parse the response to find comments with `<!-- maxsim:type=plan -->` markers or `## Plan NN` headings. If no plan comments are found:
|
|
@@ -62,7 +62,7 @@ When GitHub integration is active (`phase_issue_number` is set):
|
|
|
62
62
|
|
|
63
63
|
1. Fetch the phase issue and its comments:
|
|
64
64
|
```bash
|
|
65
|
-
node ~/.claude/maxsim/bin/maxsim-tools.cjs github get-issue $PHASE_ISSUE_NUMBER --comments
|
|
65
|
+
node ~/.claude/maxsim/bin/maxsim-tools.cjs github get-issue $PHASE_ISSUE_NUMBER --include-comments
|
|
66
66
|
```
|
|
67
67
|
|
|
68
68
|
2. Parse issue comments to identify plan comments. A plan comment is one that contains either:
|
|
@@ -330,7 +330,7 @@ Also write VERIFICATION.md to the phase directory for local reference.",
|
|
|
330
330
|
Read verification status from the verification comment on the phase issue:
|
|
331
331
|
|
|
332
332
|
```bash
|
|
333
|
-
node ~/.claude/maxsim/bin/maxsim-tools.cjs github get-issue $PHASE_ISSUE_NUMBER --comments
|
|
333
|
+
node ~/.claude/maxsim/bin/maxsim-tools.cjs github get-issue $PHASE_ISSUE_NUMBER --include-comments
|
|
334
334
|
```
|
|
335
335
|
|
|
336
336
|
Look for the `<!-- maxsim:type=verification -->` comment and parse the `status:` field from its body.
|
|
@@ -112,7 +112,7 @@ Block until user responds.
|
|
|
112
112
|
Check if any phase issue has a verification comment with FAIL status (from live GitHub data via `github all-progress` — look for phases stuck in "In Review" with a known failure, or check recent comments via `github get-issue` for the current phase issue):
|
|
113
113
|
|
|
114
114
|
```bash
|
|
115
|
-
node ~/.claude/maxsim/bin/maxsim-tools.cjs github get-issue N --comments
|
|
115
|
+
node ~/.claude/maxsim/bin/maxsim-tools.cjs github get-issue N --include-comments
|
|
116
116
|
```
|
|
117
117
|
```
|
|
118
118
|
## Problem Detected
|
|
@@ -117,9 +117,8 @@ Usage: `/maxsim:debug login form returns 500` or `/maxsim:debug` (resume)
|
|
|
117
117
|
|
|
118
118
|
Ad-hoc tasks with MAXSIM guarantees.
|
|
119
119
|
|
|
120
|
-
- Atomic commits, state tracking
|
|
120
|
+
- Atomic commits, state tracking
|
|
121
121
|
- `--full` flag enables plan-checking and verification agents
|
|
122
|
-
- "Save for later" captures ideas as todos for future work
|
|
123
122
|
|
|
124
123
|
Usage: `/maxsim:quick` or `/maxsim:quick --full`
|
|
125
124
|
|
|
@@ -16,7 +16,7 @@ Read all files referenced by the invoking prompt's execution_context before star
|
|
|
16
16
|
|
|
17
17
|
- Read PROJECT.md (existing project, validated requirements, decisions)
|
|
18
18
|
- Read MILESTONES.md (what shipped previously)
|
|
19
|
-
- Read STATE.md (
|
|
19
|
+
- Read STATE.md (blockers, decisions)
|
|
20
20
|
- Check for MILESTONE-CONTEXT.md (from /maxsim:plan)
|
|
21
21
|
|
|
22
22
|
## 2. Gather Milestone Goals
|
|
@@ -29,7 +29,7 @@ CHECKER_MODEL=$(node .claude/maxsim/bin/maxsim-tools.cjs resolve-model planner -
|
|
|
29
29
|
|
|
30
30
|
Query the phase GitHub Issue for existing plan comments:
|
|
31
31
|
```bash
|
|
32
|
-
node ~/.claude/maxsim/bin/maxsim-tools.cjs github get-issue $PHASE_ISSUE_NUMBER --comments
|
|
32
|
+
node ~/.claude/maxsim/bin/maxsim-tools.cjs github get-issue $PHASE_ISSUE_NUMBER --include-comments
|
|
33
33
|
```
|
|
34
34
|
|
|
35
35
|
Look for comments that contain `<!-- maxsim:type=plan -->`.
|
|
@@ -141,7 +141,7 @@ Also extract `phase_issue_number` passed from the orchestrator.
|
|
|
141
141
|
|
|
142
142
|
Check if a context comment already exists on the phase GitHub Issue by calling:
|
|
143
143
|
```bash
|
|
144
|
-
node ~/.claude/maxsim/bin/maxsim-tools.cjs github get-issue $PHASE_ISSUE_NUMBER --comments
|
|
144
|
+
node ~/.claude/maxsim/bin/maxsim-tools.cjs github get-issue $PHASE_ISSUE_NUMBER --include-comments
|
|
145
145
|
```
|
|
146
146
|
|
|
147
147
|
Look for a comment that contains `<!-- maxsim:type=context -->`.
|
|
@@ -47,7 +47,7 @@ Detect planning stage by querying the phase GitHub Issue:
|
|
|
47
47
|
2. **If no `phase_issue_number` exists:** The phase has not been set up on GitHub yet.
|
|
48
48
|
- Run `node ~/.claude/maxsim/bin/maxsim-tools.cjs github create-phase --phase-number "$PHASE_NUMBER" --phase-name "$PHASE_NAME" --goal "$GOAL" --requirements "$REQUIREMENTS" --success-criteria "$SUCCESS_CRITERIA"` to create the issue (uses roadmap data).
|
|
49
49
|
- Store the returned issue number as `phase_issue_number`.
|
|
50
|
-
3. Run `node ~/.claude/maxsim/bin/maxsim-tools.cjs github get-issue $PHASE_ISSUE_NUMBER --comments` to read the phase issue body and comments.
|
|
50
|
+
3. Run `node ~/.claude/maxsim/bin/maxsim-tools.cjs github get-issue $PHASE_ISSUE_NUMBER --include-comments` to read the phase issue body and comments.
|
|
51
51
|
4. Check issue comments for existing artifacts using HTML marker comments:
|
|
52
52
|
- Has a comment containing `<!-- maxsim:type=context -->`? → Discussion stage complete
|
|
53
53
|
- Has a comment containing `<!-- maxsim:type=research -->`? → Research stage complete
|
|
@@ -105,7 +105,7 @@ Pass context: `phase_number`, `phase_name`, `phase_dir`, `padded_phase`, `phase_
|
|
|
105
105
|
|
|
106
106
|
Re-query the phase issue to verify the `type=context` comment now exists:
|
|
107
107
|
```bash
|
|
108
|
-
node ~/.claude/maxsim/bin/maxsim-tools.cjs github get-issue $PHASE_ISSUE_NUMBER --comments
|
|
108
|
+
node ~/.claude/maxsim/bin/maxsim-tools.cjs github get-issue $PHASE_ISSUE_NUMBER --include-comments
|
|
109
109
|
```
|
|
110
110
|
|
|
111
111
|
Show gate:
|
|
@@ -140,7 +140,7 @@ Pass context: `phase_number`, `phase_name`, `phase_dir`, `padded_phase`, `phase_
|
|
|
140
140
|
|
|
141
141
|
Re-query the phase issue to verify the `type=research` comment now exists:
|
|
142
142
|
```bash
|
|
143
|
-
node ~/.claude/maxsim/bin/maxsim-tools.cjs github get-issue $PHASE_ISSUE_NUMBER --comments
|
|
143
|
+
node ~/.claude/maxsim/bin/maxsim-tools.cjs github get-issue $PHASE_ISSUE_NUMBER --include-comments
|
|
144
144
|
```
|
|
145
145
|
|
|
146
146
|
Show gate:
|
|
@@ -172,7 +172,7 @@ Pass context: `phase_number`, `phase_name`, `phase_dir`, `padded_phase`, `phase_
|
|
|
172
172
|
|
|
173
173
|
Re-query the phase issue to verify `type=plan` comments exist:
|
|
174
174
|
```bash
|
|
175
|
-
node ~/.claude/maxsim/bin/maxsim-tools.cjs github get-issue $PHASE_ISSUE_NUMBER --comments
|
|
175
|
+
node ~/.claude/maxsim/bin/maxsim-tools.cjs github get-issue $PHASE_ISSUE_NUMBER --include-comments
|
|
176
176
|
```
|
|
177
177
|
|
|
178
178
|
Show final gate:
|
|
@@ -124,11 +124,9 @@ node ~/.claude/maxsim/bin/maxsim-tools.cjs github detect-external-edits --phase-
|
|
|
124
124
|
|
|
125
125
|
- Use `current_phase` and `next_phase` from `$ROADMAP` (local) cross-referenced with GitHub board status
|
|
126
126
|
- Note `paused_at` if work was paused (from `$STATE`)
|
|
127
|
-
- Count pending todos: run `github list-todos` for live todo count
|
|
128
127
|
- Check for interrupted phases via `github detect-interrupted`
|
|
129
128
|
|
|
130
129
|
```bash
|
|
131
|
-
node ~/.claude/maxsim/bin/maxsim-tools.cjs github list-todos --status pending
|
|
132
130
|
node ~/.claude/maxsim/bin/maxsim-tools.cjs github detect-interrupted --phase-issue-number N
|
|
133
131
|
```
|
|
134
132
|
</step>
|
|
@@ -173,9 +171,6 @@ CONTEXT: [✓ if has_context | - if not]
|
|
|
173
171
|
- Phase [N]: [issue description, e.g., "External edit detected — body_hash mismatch"]
|
|
174
172
|
- Phase [M]: [issue description, e.g., "Local: complete, GitHub: In Progress — discrepancy"]
|
|
175
173
|
|
|
176
|
-
## Pending Todos
|
|
177
|
-
- [count] pending — /maxsim:quick --todo to review
|
|
178
|
-
|
|
179
174
|
## What's Next
|
|
180
175
|
[Next phase/plan objective from live GitHub data and local roadmap]
|
|
181
176
|
```
|