maxsimcli 4.8.0 → 4.9.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.
- package/dist/assets/CHANGELOG.md +13 -0
- package/dist/assets/hooks/maxsim-check-update.cjs +38 -0
- package/dist/assets/hooks/maxsim-check-update.cjs.map +1 -1
- package/dist/assets/hooks/maxsim-statusline.cjs +116 -48
- package/dist/assets/hooks/maxsim-statusline.cjs.map +1 -1
- package/dist/assets/hooks/maxsim-sync-reminder.cjs +117 -0
- package/dist/assets/hooks/maxsim-sync-reminder.cjs.map +1 -0
- package/dist/assets/templates/agents/AGENTS.md +78 -106
- package/dist/assets/templates/agents/executor.md +101 -0
- package/dist/assets/templates/agents/planner.md +86 -0
- package/dist/assets/templates/agents/researcher.md +71 -0
- package/dist/assets/templates/agents/verifier.md +88 -0
- package/dist/assets/templates/commands/maxsim/debug.md +7 -7
- package/dist/assets/templates/commands/maxsim/execute.md +45 -0
- package/dist/assets/templates/commands/maxsim/go.md +29 -0
- package/dist/assets/templates/commands/maxsim/help.md +2 -2
- package/dist/assets/templates/commands/maxsim/init.md +52 -0
- package/dist/assets/templates/commands/maxsim/plan.md +50 -0
- package/dist/assets/templates/commands/maxsim/progress.md +4 -3
- package/dist/assets/templates/commands/maxsim/quick.md +6 -4
- package/dist/assets/templates/commands/maxsim/settings.md +4 -3
- package/dist/assets/templates/references/continuation-format.md +16 -16
- package/dist/assets/templates/references/model-profile-resolution.md +1 -1
- package/dist/assets/templates/references/model-profiles.md +12 -19
- package/dist/assets/templates/rules/conventions.md +51 -0
- package/dist/assets/templates/rules/verification-protocol.md +57 -0
- package/dist/assets/templates/skills/agent-system-map/SKILL.md +92 -0
- package/dist/assets/templates/skills/brainstorming/SKILL.md +48 -36
- package/dist/assets/templates/skills/code-review/SKILL.md +40 -61
- package/dist/assets/templates/skills/commit-conventions/SKILL.md +75 -0
- package/dist/assets/templates/skills/evidence-collection/SKILL.md +87 -0
- package/dist/assets/templates/skills/handoff-contract/SKILL.md +70 -0
- package/dist/assets/templates/skills/input-validation/SKILL.md +51 -0
- package/dist/assets/templates/skills/maxsim-batch/SKILL.md +41 -45
- package/dist/assets/templates/skills/maxsim-simplify/SKILL.md +37 -90
- package/dist/assets/templates/skills/memory-management/SKILL.md +32 -67
- package/dist/assets/templates/skills/research-methodology/SKILL.md +137 -0
- package/dist/assets/templates/skills/roadmap-writing/SKILL.md +40 -58
- package/dist/assets/templates/skills/sdd/SKILL.md +34 -69
- package/dist/assets/templates/skills/systematic-debugging/SKILL.md +20 -26
- package/dist/assets/templates/skills/tdd/SKILL.md +25 -33
- package/dist/assets/templates/skills/tool-priority-guide/SKILL.md +80 -0
- package/dist/assets/templates/skills/using-maxsim/SKILL.md +42 -73
- package/dist/assets/templates/skills/verification-before-completion/SKILL.md +12 -24
- package/dist/assets/templates/skills/verification-gates/SKILL.md +169 -0
- package/dist/assets/templates/templates/UAT.md +3 -3
- package/dist/assets/templates/templates/VALIDATION.md +1 -1
- package/dist/assets/templates/templates/context.md +4 -4
- package/dist/assets/templates/templates/debug-subagent-prompt.md +3 -3
- package/dist/assets/templates/templates/discovery.md +2 -2
- package/dist/assets/templates/templates/phase-prompt.md +2 -2
- package/dist/assets/templates/templates/planner-subagent-prompt.md +7 -7
- package/dist/assets/templates/templates/project.md +1 -1
- package/dist/assets/templates/templates/research.md +1 -1
- package/dist/assets/templates/templates/state.md +2 -2
- package/dist/assets/templates/templates/summary.md +41 -0
- package/dist/assets/templates/workflows/batch.md +5 -5
- package/dist/assets/templates/workflows/diagnose-issues.md +2 -2
- package/dist/assets/templates/workflows/discovery-phase.md +3 -3
- package/dist/assets/templates/workflows/discuss-phase.md +11 -11
- package/dist/assets/templates/workflows/execute-phase.md +205 -11
- package/dist/assets/templates/workflows/execute-plan.md +299 -34
- package/dist/assets/templates/workflows/execute.md +421 -0
- package/dist/assets/templates/workflows/go.md +250 -0
- package/dist/assets/templates/workflows/health.md +5 -5
- package/dist/assets/templates/workflows/help.md +165 -435
- package/dist/assets/templates/workflows/init-existing.md +23 -23
- package/dist/assets/templates/workflows/init.md +205 -0
- package/dist/assets/templates/workflows/new-milestone.md +9 -9
- package/dist/assets/templates/workflows/new-project.md +26 -26
- package/dist/assets/templates/workflows/plan-create.md +298 -0
- package/dist/assets/templates/workflows/plan-discuss.md +347 -0
- package/dist/assets/templates/workflows/plan-phase.md +29 -29
- package/dist/assets/templates/workflows/plan-research.md +177 -0
- package/dist/assets/templates/workflows/plan.md +231 -0
- package/dist/assets/templates/workflows/progress.md +46 -42
- package/dist/assets/templates/workflows/quick.md +195 -14
- package/dist/assets/templates/workflows/research-phase.md +5 -5
- package/dist/assets/templates/workflows/sdd.md +20 -12
- package/dist/assets/templates/workflows/settings.md +18 -14
- package/dist/assets/templates/workflows/verify-phase.md +1 -1
- package/dist/assets/templates/workflows/verify-work.md +16 -16
- package/dist/cli.cjs +496 -91
- package/dist/cli.cjs.map +1 -1
- package/dist/core-D5zUr9cb.cjs.map +1 -1
- package/dist/install.cjs +234 -17
- package/dist/install.cjs.map +1 -1
- package/dist/mcp-server.cjs +21 -2
- package/dist/mcp-server.cjs.map +1 -1
- package/dist/skills-CjFWZIGM.cjs.map +1 -1
- package/package.json +1 -1
- package/dist/assets/hooks/maxsim-context-monitor.cjs +0 -121
- package/dist/assets/hooks/maxsim-context-monitor.cjs.map +0 -1
- package/dist/assets/templates/agents/maxsim-code-reviewer.md +0 -239
- package/dist/assets/templates/agents/maxsim-codebase-mapper.md +0 -214
- package/dist/assets/templates/agents/maxsim-debugger.md +0 -572
- package/dist/assets/templates/agents/maxsim-drift-checker.md +0 -522
- package/dist/assets/templates/agents/maxsim-executor.md +0 -504
- package/dist/assets/templates/agents/maxsim-integration-checker.md +0 -273
- package/dist/assets/templates/agents/maxsim-phase-researcher.md +0 -305
- package/dist/assets/templates/agents/maxsim-plan-checker.md +0 -343
- package/dist/assets/templates/agents/maxsim-planner.md +0 -610
- package/dist/assets/templates/agents/maxsim-project-researcher.md +0 -359
- package/dist/assets/templates/agents/maxsim-research-synthesizer.md +0 -263
- package/dist/assets/templates/agents/maxsim-roadmapper.md +0 -324
- package/dist/assets/templates/agents/maxsim-spec-reviewer.md +0 -245
- package/dist/assets/templates/agents/maxsim-verifier.md +0 -393
- package/dist/assets/templates/commands/maxsim/add-phase.md +0 -43
- package/dist/assets/templates/commands/maxsim/add-tests.md +0 -41
- package/dist/assets/templates/commands/maxsim/add-todo.md +0 -57
- package/dist/assets/templates/commands/maxsim/artefakte.md +0 -122
- package/dist/assets/templates/commands/maxsim/audit-milestone.md +0 -36
- package/dist/assets/templates/commands/maxsim/batch.md +0 -42
- package/dist/assets/templates/commands/maxsim/check-drift.md +0 -56
- package/dist/assets/templates/commands/maxsim/check-todos.md +0 -46
- package/dist/assets/templates/commands/maxsim/cleanup.md +0 -18
- package/dist/assets/templates/commands/maxsim/complete-milestone.md +0 -136
- package/dist/assets/templates/commands/maxsim/discuss-phase.md +0 -87
- package/dist/assets/templates/commands/maxsim/discuss.md +0 -70
- package/dist/assets/templates/commands/maxsim/execute-phase.md +0 -41
- package/dist/assets/templates/commands/maxsim/health.md +0 -22
- package/dist/assets/templates/commands/maxsim/init-existing.md +0 -46
- package/dist/assets/templates/commands/maxsim/insert-phase.md +0 -32
- package/dist/assets/templates/commands/maxsim/list-phase-assumptions.md +0 -46
- package/dist/assets/templates/commands/maxsim/map-codebase.md +0 -71
- package/dist/assets/templates/commands/maxsim/new-milestone.md +0 -44
- package/dist/assets/templates/commands/maxsim/new-project.md +0 -46
- package/dist/assets/templates/commands/maxsim/pause-work.md +0 -38
- package/dist/assets/templates/commands/maxsim/plan-milestone-gaps.md +0 -34
- package/dist/assets/templates/commands/maxsim/plan-phase.md +0 -44
- package/dist/assets/templates/commands/maxsim/realign.md +0 -39
- package/dist/assets/templates/commands/maxsim/reapply-patches.md +0 -110
- package/dist/assets/templates/commands/maxsim/remove-phase.md +0 -31
- package/dist/assets/templates/commands/maxsim/research-phase.md +0 -189
- package/dist/assets/templates/commands/maxsim/resume-work.md +0 -40
- package/dist/assets/templates/commands/maxsim/roadmap.md +0 -19
- package/dist/assets/templates/commands/maxsim/sdd.md +0 -39
- package/dist/assets/templates/commands/maxsim/set-profile.md +0 -34
- package/dist/assets/templates/commands/maxsim/update.md +0 -37
- package/dist/assets/templates/commands/maxsim/verify-work.md +0 -38
- package/dist/assets/templates/workflows/add-phase.md +0 -111
- package/dist/assets/templates/workflows/add-tests.md +0 -351
- package/dist/assets/templates/workflows/add-todo.md +0 -247
- package/dist/assets/templates/workflows/audit-milestone.md +0 -297
- package/dist/assets/templates/workflows/check-drift.md +0 -248
- package/dist/assets/templates/workflows/check-todos.md +0 -261
- package/dist/assets/templates/workflows/cleanup.md +0 -153
- package/dist/assets/templates/workflows/complete-milestone.md +0 -701
- package/dist/assets/templates/workflows/discuss.md +0 -343
- package/dist/assets/templates/workflows/insert-phase.md +0 -129
- package/dist/assets/templates/workflows/list-phase-assumptions.md +0 -178
- package/dist/assets/templates/workflows/map-codebase.md +0 -315
- package/dist/assets/templates/workflows/pause-work.md +0 -122
- package/dist/assets/templates/workflows/plan-milestone-gaps.md +0 -274
- package/dist/assets/templates/workflows/realign.md +0 -288
- package/dist/assets/templates/workflows/remove-phase.md +0 -154
- package/dist/assets/templates/workflows/resume-project.md +0 -306
- package/dist/assets/templates/workflows/roadmap.md +0 -130
- package/dist/assets/templates/workflows/set-profile.md +0 -81
- package/dist/assets/templates/workflows/transition.md +0 -544
- package/dist/assets/templates/workflows/update.md +0 -220
package/dist/assets/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,16 @@
|
|
|
1
|
+
# [4.8.0](https://github.com/maystudios/maxsimcli/compare/v4.7.1...v4.8.0) (2026-03-10)
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
### Features
|
|
5
|
+
|
|
6
|
+
* **02-01:** add Octokit client adapter, AuthError class, update types ([8872d8a](https://github.com/maystudios/maxsimcli/commit/8872d8afb55c685c4db1795d965b152d8471543d))
|
|
7
|
+
* **02-01:** enforce local-only installation, reject --global flag ([2ca49e1](https://github.com/maystudios/maxsimcli/commit/2ca49e12b8e21095283439148c158f6c326910c0))
|
|
8
|
+
* **02-02:** rewrite issues.ts with Octokit and native sub-issues ([4897cfe](https://github.com/maystudios/maxsimcli/commit/4897cfe2c1f5ab5bc7731ee2737d7803713ac467)), closes [#legacy](https://github.com/maystudios/maxsimcli/issues/legacy)
|
|
9
|
+
* **02-02:** rewrite labels, milestones, mapping to use Octokit ([a8b830d](https://github.com/maystudios/maxsimcli/commit/a8b830d30acea76f028603b78f2e795fd129d80a)), closes [#legacy](https://github.com/maystudios/maxsimcli/issues/legacy)
|
|
10
|
+
* **02-03:** rewrite projects.ts with Octokit REST API ([06ebc76](https://github.com/maystudios/maxsimcli/commit/06ebc7609a4d2cf08f5b899226b154de61f5a8b1))
|
|
11
|
+
* **02-03:** rewrite sync.ts for GitHub-native state queries ([842a34d](https://github.com/maystudios/maxsimcli/commit/842a34ddab39706921ff98b575098bbc62aa7223))
|
|
12
|
+
* **02-04:** wire MCP tools to Octokit adapter, remove legacy patterns ([429d05c](https://github.com/maystudios/maxsimcli/commit/429d05c2c81718fcc9ee5696bf9339948dee4b93))
|
|
13
|
+
|
|
1
14
|
## [4.7.1](https://github.com/maystudios/maxsimcli/compare/v4.7.0...v4.7.1) (2026-03-09)
|
|
2
15
|
|
|
3
16
|
|
|
@@ -89,6 +89,43 @@ function checkForUpdate(options) {
|
|
|
89
89
|
detached: true
|
|
90
90
|
}).unref();
|
|
91
91
|
}
|
|
92
|
+
/**
|
|
93
|
+
* Create a backup of the current MAXSIM installation before an update.
|
|
94
|
+
* Called by the installer (not by the SessionStart hook).
|
|
95
|
+
*
|
|
96
|
+
* @param cwd - The project working directory containing .claude/
|
|
97
|
+
* @returns The backup directory path on success, null on failure.
|
|
98
|
+
*/
|
|
99
|
+
function createBackupBeforeUpdate(cwd) {
|
|
100
|
+
try {
|
|
101
|
+
const sourceDir = node_path.join(cwd, CLAUDE_DIR);
|
|
102
|
+
const backupDir = node_path.join(sourceDir, "maxsim-backup");
|
|
103
|
+
node_fs.mkdirSync(backupDir, { recursive: true });
|
|
104
|
+
for (const relDir of [
|
|
105
|
+
"commands/maxsim",
|
|
106
|
+
"maxsim",
|
|
107
|
+
"hooks",
|
|
108
|
+
"agents",
|
|
109
|
+
"skills"
|
|
110
|
+
]) {
|
|
111
|
+
const src = node_path.join(sourceDir, relDir);
|
|
112
|
+
if (!node_fs.existsSync(src)) continue;
|
|
113
|
+
const dest = node_path.join(backupDir, relDir);
|
|
114
|
+
node_fs.mkdirSync(node_path.dirname(dest), { recursive: true });
|
|
115
|
+
node_fs.cpSync(src, dest, { recursive: true });
|
|
116
|
+
}
|
|
117
|
+
let version = "unknown";
|
|
118
|
+
const versionFile = node_path.join(sourceDir, "maxsim", "VERSION");
|
|
119
|
+
if (node_fs.existsSync(versionFile)) version = node_fs.readFileSync(versionFile, "utf8").trim();
|
|
120
|
+
node_fs.writeFileSync(node_path.join(backupDir, "backup-meta.json"), JSON.stringify({
|
|
121
|
+
created: (/* @__PURE__ */ new Date()).toISOString(),
|
|
122
|
+
version
|
|
123
|
+
}, null, 2));
|
|
124
|
+
return backupDir;
|
|
125
|
+
} catch {
|
|
126
|
+
return null;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
92
129
|
if (require.main === module) checkForUpdate({
|
|
93
130
|
homeDir: node_os.homedir(),
|
|
94
131
|
cwd: process.cwd()
|
|
@@ -96,4 +133,5 @@ if (require.main === module) checkForUpdate({
|
|
|
96
133
|
|
|
97
134
|
//#endregion
|
|
98
135
|
exports.checkForUpdate = checkForUpdate;
|
|
136
|
+
exports.createBackupBeforeUpdate = createBackupBeforeUpdate;
|
|
99
137
|
//# 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
|
|
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","#!/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 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: true,\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;AAyC7C,+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;EACX,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"}
|
|
@@ -31,8 +31,7 @@ let node_fs = require("node:fs");
|
|
|
31
31
|
node_fs = __toESM(node_fs);
|
|
32
32
|
let node_path = require("node:path");
|
|
33
33
|
node_path = __toESM(node_path);
|
|
34
|
-
let
|
|
35
|
-
node_os = __toESM(node_os);
|
|
34
|
+
let node_child_process = require("node:child_process");
|
|
36
35
|
|
|
37
36
|
//#region src/hooks/shared.ts
|
|
38
37
|
/**
|
|
@@ -40,7 +39,7 @@ node_os = __toESM(node_os);
|
|
|
40
39
|
*/
|
|
41
40
|
/**
|
|
42
41
|
* Read all stdin as a string, then invoke callback with parsed JSON.
|
|
43
|
-
* Used by
|
|
42
|
+
* Used by statusline and sync-reminder hooks.
|
|
44
43
|
*/
|
|
45
44
|
function readStdinJson(callback) {
|
|
46
45
|
let input = "";
|
|
@@ -61,56 +60,125 @@ const CLAUDE_DIR = ".claude";
|
|
|
61
60
|
//#region src/hooks/maxsim-statusline.ts
|
|
62
61
|
/**
|
|
63
62
|
* Claude Code Statusline - MAXSIM Edition
|
|
64
|
-
* Shows: model |
|
|
63
|
+
* Shows: [update] model | P{N} | v{M}: {pct}% | dirname
|
|
65
64
|
*/
|
|
65
|
+
const CACHE_TTL_SECONDS = 60;
|
|
66
|
+
/**
|
|
67
|
+
* Spawn a detached Node child process to refresh the progress cache in the background.
|
|
68
|
+
* The child runs gh CLI commands to detect owner/repo, find the first open milestone,
|
|
69
|
+
* compute progress, and find the current phase label.
|
|
70
|
+
*/
|
|
71
|
+
function spawnBackgroundRefresh(cacheDir, cacheFile) {
|
|
72
|
+
try {
|
|
73
|
+
const script = `
|
|
74
|
+
const { execSync } = require('child_process');
|
|
75
|
+
const fs = require('fs');
|
|
76
|
+
const path = require('path');
|
|
77
|
+
|
|
78
|
+
try {
|
|
79
|
+
// Detect owner/repo
|
|
80
|
+
const nameWithOwner = execSync('gh repo view --json nameWithOwner -q .nameWithOwner', {
|
|
81
|
+
encoding: 'utf8',
|
|
82
|
+
timeout: 10000,
|
|
83
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
84
|
+
}).trim();
|
|
85
|
+
|
|
86
|
+
if (!nameWithOwner || !nameWithOwner.includes('/')) {
|
|
87
|
+
process.exit(0);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const [owner, repo] = nameWithOwner.split('/');
|
|
91
|
+
|
|
92
|
+
// Get milestones
|
|
93
|
+
let milestoneTitle = null;
|
|
94
|
+
let milestonePct = 0;
|
|
95
|
+
try {
|
|
96
|
+
const milestonesRaw = execSync(
|
|
97
|
+
'gh api repos/' + owner + '/' + repo + '/milestones --jq "."',
|
|
98
|
+
{ encoding: 'utf8', timeout: 10000, stdio: ['pipe', 'pipe', 'pipe'] }
|
|
99
|
+
).trim();
|
|
100
|
+
if (milestonesRaw) {
|
|
101
|
+
const milestones = JSON.parse(milestonesRaw);
|
|
102
|
+
const openMilestone = milestones.find(function(m) { return m.state === 'open'; });
|
|
103
|
+
if (openMilestone) {
|
|
104
|
+
milestoneTitle = openMilestone.title || null;
|
|
105
|
+
const total = (openMilestone.open_issues || 0) + (openMilestone.closed_issues || 0);
|
|
106
|
+
if (total > 0) {
|
|
107
|
+
milestonePct = Math.round(((openMilestone.closed_issues || 0) / total) * 100);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
} catch (e) {
|
|
112
|
+
// gh api failed for milestones, continue with defaults
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// Get current phase from labels on open issues
|
|
116
|
+
let phaseNumber = null;
|
|
117
|
+
try {
|
|
118
|
+
const phaseRaw = execSync(
|
|
119
|
+
'gh api "repos/' + owner + '/' + repo + '/issues?state=open&labels=phase:&per_page=1&sort=created&direction=desc" --jq ".[0].labels[] | select(.name | startswith(\\"phase:\\")) | .name"',
|
|
120
|
+
{ encoding: 'utf8', timeout: 10000, stdio: ['pipe', 'pipe', 'pipe'] }
|
|
121
|
+
).trim();
|
|
122
|
+
if (phaseRaw && phaseRaw.startsWith('phase:')) {
|
|
123
|
+
phaseNumber = phaseRaw.replace('phase:', '').trim();
|
|
124
|
+
}
|
|
125
|
+
} catch (e) {
|
|
126
|
+
// gh api failed for phase, continue with null
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// Write cache
|
|
130
|
+
const cacheData = JSON.stringify({
|
|
131
|
+
phase_number: phaseNumber,
|
|
132
|
+
milestone_title: milestoneTitle,
|
|
133
|
+
milestone_pct: milestonePct,
|
|
134
|
+
updated: Math.floor(Date.now() / 1000),
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
const dir = ${JSON.stringify(cacheDir)};
|
|
138
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
139
|
+
fs.writeFileSync(${JSON.stringify(cacheFile)}, cacheData);
|
|
140
|
+
} catch (e) {
|
|
141
|
+
// Silently degrade if gh not available
|
|
142
|
+
process.exit(0);
|
|
143
|
+
}
|
|
144
|
+
`;
|
|
145
|
+
(0, node_child_process.spawn)(process.execPath, ["-e", script], {
|
|
146
|
+
stdio: "ignore",
|
|
147
|
+
windowsHide: true,
|
|
148
|
+
detached: true
|
|
149
|
+
}).unref();
|
|
150
|
+
} catch {}
|
|
151
|
+
}
|
|
66
152
|
function formatStatusline(data) {
|
|
67
153
|
const model = data.model?.display_name || "Claude";
|
|
68
|
-
const dir = data.workspace?.current_dir || process.cwd();
|
|
69
|
-
const
|
|
70
|
-
const
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
if (
|
|
77
|
-
const bridgePath = node_path.join(node_os.tmpdir(), `claude-ctx-${session}.json`);
|
|
78
|
-
const bridgeData = JSON.stringify({
|
|
79
|
-
session_id: session,
|
|
80
|
-
remaining_percentage: remaining,
|
|
81
|
-
used_pct: used,
|
|
82
|
-
timestamp: Math.floor(Date.now() / 1e3)
|
|
83
|
-
});
|
|
84
|
-
node_fs.writeFileSync(bridgePath, bridgeData);
|
|
85
|
-
} catch {}
|
|
86
|
-
const filled = Math.floor(used / 10);
|
|
87
|
-
const bar = "█".repeat(filled) + "░".repeat(10 - filled);
|
|
88
|
-
if (used < 63) ctx = ` \x1b[32m${bar} ${used}%\x1b[0m`;
|
|
89
|
-
else if (used < 81) ctx = ` \x1b[33m${bar} ${used}%\x1b[0m`;
|
|
90
|
-
else if (used < 95) ctx = ` \x1b[38;5;208m${bar} ${used}%\x1b[0m`;
|
|
91
|
-
else ctx = ` \x1b[5;31m\uD83D\uDC80 ${bar} ${used}%\x1b[0m`;
|
|
92
|
-
}
|
|
93
|
-
let task = "";
|
|
94
|
-
const homeDir = node_os.homedir();
|
|
95
|
-
const todosDir = node_path.join(homeDir, CLAUDE_DIR, "todos");
|
|
96
|
-
if (session && node_fs.existsSync(todosDir)) try {
|
|
97
|
-
const files = node_fs.readdirSync(todosDir).filter((f) => f.startsWith(session) && f.includes("-agent-") && f.endsWith(".json")).map((f) => ({
|
|
98
|
-
name: f,
|
|
99
|
-
mtime: node_fs.statSync(node_path.join(todosDir, f)).mtime
|
|
100
|
-
})).sort((a, b) => b.mtime.getTime() - a.mtime.getTime());
|
|
101
|
-
if (files.length > 0) try {
|
|
102
|
-
const inProgress = JSON.parse(node_fs.readFileSync(node_path.join(todosDir, files[0].name), "utf8")).find((t) => t.status === "in_progress");
|
|
103
|
-
if (inProgress) task = inProgress.activeForm || "";
|
|
104
|
-
} catch {}
|
|
154
|
+
const dir = data.workspace?.project_dir || data.workspace?.current_dir || process.cwd();
|
|
155
|
+
const dirname = node_path.basename(dir);
|
|
156
|
+
const SEP = " │ ";
|
|
157
|
+
const DIM = "\x1B[2m";
|
|
158
|
+
const RESET = "\x1B[0m";
|
|
159
|
+
let updateIndicator = "";
|
|
160
|
+
const updateCacheFile = node_path.join(dir, CLAUDE_DIR, "cache", "maxsim-update-check.json");
|
|
161
|
+
if (node_fs.existsSync(updateCacheFile)) try {
|
|
162
|
+
if (JSON.parse(node_fs.readFileSync(updateCacheFile, "utf8")).update_available) updateIndicator = "\x1B[33m⬆\x1B[0m ";
|
|
105
163
|
} catch {}
|
|
106
|
-
|
|
107
|
-
|
|
164
|
+
const planningDir = node_path.join(dir, ".planning");
|
|
165
|
+
if (!node_fs.existsSync(planningDir)) return `${updateIndicator}${DIM}${model}${RESET}${SEP}${DIM}${dirname}${RESET}`;
|
|
166
|
+
const cacheDir = node_path.join(dir, CLAUDE_DIR, "cache");
|
|
167
|
+
const cacheFile = node_path.join(cacheDir, "maxsim-progress.json");
|
|
168
|
+
let cache = null;
|
|
169
|
+
let cacheAge = Infinity;
|
|
108
170
|
if (node_fs.existsSync(cacheFile)) try {
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
171
|
+
cache = JSON.parse(node_fs.readFileSync(cacheFile, "utf8"));
|
|
172
|
+
cacheAge = Math.floor(Date.now() / 1e3) - (cache.updated || 0);
|
|
173
|
+
} catch {
|
|
174
|
+
cache = null;
|
|
175
|
+
}
|
|
176
|
+
if (cacheAge > CACHE_TTL_SECONDS) spawnBackgroundRefresh(cacheDir, cacheFile);
|
|
177
|
+
let phaseSegment = "";
|
|
178
|
+
if (cache?.phase_number) phaseSegment = `${SEP}${DIM}P${cache.phase_number}${RESET}`;
|
|
179
|
+
let milestoneSegment = "";
|
|
180
|
+
if (cache?.milestone_title) milestoneSegment = `${SEP}${DIM}${cache.milestone_title}: ${cache.milestone_pct}%${RESET}`;
|
|
181
|
+
return `${updateIndicator}${DIM}${model}${RESET}${phaseSegment}${milestoneSegment}${SEP}${DIM}${dirname}${RESET}`;
|
|
114
182
|
}
|
|
115
183
|
if (require.main === module) readStdinJson((data) => {
|
|
116
184
|
process.stdout.write(formatStatusline(data));
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"maxsim-statusline.cjs","names":["path","
|
|
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","#!/usr/bin/env node\n/**\n * Claude Code Statusline - MAXSIM Edition\n * Shows: [update] model | P{N} | v{M}: {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 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 labels on open issues\n let phaseNumber = null;\n try {\n const phaseRaw = execSync(\n 'gh api \"repos/' + owner + '/' + repo + '/issues?state=open&labels=phase:&per_page=1&sort=created&direction=desc\" --jq \".[0].labels[] | select(.name | startswith(\\\\\"phase:\\\\\")) | .name\"',\n { encoding: 'utf8', timeout: 10000, stdio: ['pipe', 'pipe', 'pipe'] }\n ).trim();\n if (phaseRaw && phaseRaw.startsWith('phase:')) {\n phaseNumber = phaseRaw.replace('phase:', '').trim();\n }\n } catch (e) {\n // gh api failed for phase, continue with null\n }\n\n // Write cache\n const cacheData = JSON.stringify({\n phase_number: phaseNumber,\n milestone_title: milestoneTitle,\n milestone_pct: milestonePct,\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 // Silently degrade if gh not available\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 // Build phase segment\n let phaseSegment = '';\n if (cache?.phase_number) {\n phaseSegment = `${SEP}${DIM}P${cache.phase_number}${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;;;;;;;;ACA1B,MAAM,oBAAoB;;;;;;AAO1B,SAAS,uBAAuB,UAAkB,WAAyB;AACzE,KAAI;EACF,MAAM,SAAS;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;gBAgEH,KAAK,UAAU,SAAS,CAAC;;qBAEpB,KAAK,UAAU,UAAU,CAAC;;;;;;AAY3C,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;CAI7C,IAAI,eAAe;AACnB,KAAI,OAAO,aACT,gBAAe,GAAG,MAAM,IAAI,GAAG,MAAM,eAAe;CAItD,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"}
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
|
3
|
+
//#region \0rolldown/runtime.js
|
|
4
|
+
var __create = Object.create;
|
|
5
|
+
var __defProp = Object.defineProperty;
|
|
6
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
7
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
8
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
9
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
|
|
13
|
+
key = keys[i];
|
|
14
|
+
if (!__hasOwnProp.call(to, key) && key !== except) {
|
|
15
|
+
__defProp(to, key, {
|
|
16
|
+
get: ((k) => from[k]).bind(null, key),
|
|
17
|
+
enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
return to;
|
|
23
|
+
};
|
|
24
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
|
|
25
|
+
value: mod,
|
|
26
|
+
enumerable: true
|
|
27
|
+
}) : target, mod));
|
|
28
|
+
|
|
29
|
+
//#endregion
|
|
30
|
+
let node_fs = require("node:fs");
|
|
31
|
+
node_fs = __toESM(node_fs);
|
|
32
|
+
let node_os = require("node:os");
|
|
33
|
+
node_os = __toESM(node_os);
|
|
34
|
+
let node_path = require("node:path");
|
|
35
|
+
node_path = __toESM(node_path);
|
|
36
|
+
|
|
37
|
+
//#region src/hooks/shared.ts
|
|
38
|
+
/**
|
|
39
|
+
* Shared utilities for MAXSIM hooks.
|
|
40
|
+
*/
|
|
41
|
+
/**
|
|
42
|
+
* Read all stdin as a string, then invoke callback with parsed JSON.
|
|
43
|
+
* Used by statusline and sync-reminder hooks.
|
|
44
|
+
*/
|
|
45
|
+
function readStdinJson(callback) {
|
|
46
|
+
let input = "";
|
|
47
|
+
process.stdin.setEncoding("utf8");
|
|
48
|
+
process.stdin.on("data", (chunk) => input += chunk);
|
|
49
|
+
process.stdin.on("end", () => {
|
|
50
|
+
try {
|
|
51
|
+
callback(JSON.parse(input));
|
|
52
|
+
} catch {
|
|
53
|
+
process.exit(0);
|
|
54
|
+
}
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
//#endregion
|
|
59
|
+
//#region src/hooks/maxsim-sync-reminder.ts
|
|
60
|
+
/**
|
|
61
|
+
* Sync Reminder Hook — PostToolUse hook that detects .planning/ file writes
|
|
62
|
+
* and gently reminds the user to sync changes to GitHub Issues.
|
|
63
|
+
*
|
|
64
|
+
* Debounces reminders: fires on the first .planning/ write per session,
|
|
65
|
+
* then every DEBOUNCE_CALLS writes thereafter.
|
|
66
|
+
*/
|
|
67
|
+
/** Number of .planning/ writes between repeated reminders. */
|
|
68
|
+
const DEBOUNCE_CALLS = 10;
|
|
69
|
+
const REMINDER_MESSAGE = ".planning/ files changed locally. Consider syncing to GitHub Issues when ready.";
|
|
70
|
+
function processSyncReminder(data) {
|
|
71
|
+
const sessionId = data.session_id;
|
|
72
|
+
const filePath = data.tool_input?.file_path;
|
|
73
|
+
if (!sessionId || !filePath) return null;
|
|
74
|
+
const normalized = node_path.normalize(filePath);
|
|
75
|
+
const planningSegment = `${node_path.sep}.planning${node_path.sep}`;
|
|
76
|
+
const planningEnd = `${node_path.sep}.planning`;
|
|
77
|
+
if (!normalized.includes(planningSegment) && !normalized.endsWith(planningEnd)) return null;
|
|
78
|
+
const stateFile = node_path.join(node_os.tmpdir(), `maxsim-sync-${sessionId}.json`);
|
|
79
|
+
let state;
|
|
80
|
+
try {
|
|
81
|
+
if (node_fs.existsSync(stateFile)) state = JSON.parse(node_fs.readFileSync(stateFile, "utf8"));
|
|
82
|
+
else state = {
|
|
83
|
+
callsSinceRemind: 0,
|
|
84
|
+
reminded: false
|
|
85
|
+
};
|
|
86
|
+
} catch {
|
|
87
|
+
state = {
|
|
88
|
+
callsSinceRemind: 0,
|
|
89
|
+
reminded: false
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
state.callsSinceRemind++;
|
|
93
|
+
if (!state.reminded || state.callsSinceRemind >= DEBOUNCE_CALLS) {
|
|
94
|
+
state.callsSinceRemind = 0;
|
|
95
|
+
state.reminded = true;
|
|
96
|
+
try {
|
|
97
|
+
node_fs.writeFileSync(stateFile, JSON.stringify(state));
|
|
98
|
+
} catch {}
|
|
99
|
+
return { hookSpecificOutput: {
|
|
100
|
+
hookEventName: "PostToolUse",
|
|
101
|
+
additionalContext: REMINDER_MESSAGE
|
|
102
|
+
} };
|
|
103
|
+
}
|
|
104
|
+
try {
|
|
105
|
+
node_fs.writeFileSync(stateFile, JSON.stringify(state));
|
|
106
|
+
} catch {}
|
|
107
|
+
return null;
|
|
108
|
+
}
|
|
109
|
+
if (require.main === module) readStdinJson((data) => {
|
|
110
|
+
const result = processSyncReminder(data);
|
|
111
|
+
if (result) process.stdout.write(JSON.stringify(result));
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
//#endregion
|
|
115
|
+
exports.DEBOUNCE_CALLS = DEBOUNCE_CALLS;
|
|
116
|
+
exports.processSyncReminder = processSyncReminder;
|
|
117
|
+
//# sourceMappingURL=maxsim-sync-reminder.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"maxsim-sync-reminder.cjs","names":["path","os","fs"],"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","#!/usr/bin/env node\n/**\n * Sync Reminder Hook — PostToolUse hook that detects .planning/ file writes\n * and gently reminds the user to sync changes to GitHub Issues.\n *\n * Debounces reminders: fires on the first .planning/ write per session,\n * then every DEBOUNCE_CALLS writes thereafter.\n */\n\nimport * as fs from 'node:fs';\nimport * as os from 'node:os';\nimport * as path from 'node:path';\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\ninterface DebounceState {\n callsSinceRemind: number;\n reminded: boolean;\n}\n\n/** Number of .planning/ writes between repeated reminders. */\nexport const DEBOUNCE_CALLS = 10;\n\nconst REMINDER_MESSAGE =\n '.planning/ files changed locally. Consider syncing to GitHub Issues when ready.';\n\nexport function processSyncReminder(\n data: SyncReminderInput,\n): SyncReminderOutput | null {\n const sessionId = data.session_id;\n const filePath = data.tool_input?.file_path;\n\n if (!sessionId || !filePath) {\n return null;\n }\n\n // Normalize path for cross-platform (Windows backslash handling)\n const normalized = path.normalize(filePath);\n\n // Check if the file is inside a .planning/ directory\n const planningSegment = `${path.sep}.planning${path.sep}`;\n const planningEnd = `${path.sep}.planning`;\n if (\n !normalized.includes(planningSegment) &&\n !normalized.endsWith(planningEnd)\n ) {\n return null;\n }\n\n // Load debounce state from temp file\n const stateFile = path.join(\n os.tmpdir(),\n `maxsim-sync-${sessionId}.json`,\n );\n\n let state: DebounceState;\n try {\n if (fs.existsSync(stateFile)) {\n state = JSON.parse(fs.readFileSync(stateFile, 'utf8')) as DebounceState;\n } else {\n state = { callsSinceRemind: 0, reminded: false };\n }\n } catch {\n state = { callsSinceRemind: 0, reminded: false };\n }\n\n state.callsSinceRemind++;\n\n // Fire reminder on first write OR after debounce interval expires\n if (!state.reminded || state.callsSinceRemind >= DEBOUNCE_CALLS) {\n state.callsSinceRemind = 0;\n state.reminded = true;\n\n try {\n fs.writeFileSync(stateFile, JSON.stringify(state));\n } catch {\n // Silent fail -- never block hook execution\n }\n\n return {\n hookSpecificOutput: {\n hookEventName: 'PostToolUse',\n additionalContext: REMINDER_MESSAGE,\n },\n };\n }\n\n // Not time for a reminder yet\n try {\n fs.writeFileSync(stateFile, JSON.stringify(state));\n } catch {\n // Silent fail -- never block hook execution\n }\n\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;;;;;;;;;;;;;ACaJ,MAAa,iBAAiB;AAE9B,MAAM,mBACJ;AAEF,SAAgB,oBACd,MAC2B;CAC3B,MAAM,YAAY,KAAK;CACvB,MAAM,WAAW,KAAK,YAAY;AAElC,KAAI,CAAC,aAAa,CAAC,SACjB,QAAO;CAIT,MAAM,aAAaA,UAAK,UAAU,SAAS;CAG3C,MAAM,kBAAkB,GAAGA,UAAK,IAAI,WAAWA,UAAK;CACpD,MAAM,cAAc,GAAGA,UAAK,IAAI;AAChC,KACE,CAAC,WAAW,SAAS,gBAAgB,IACrC,CAAC,WAAW,SAAS,YAAY,CAEjC,QAAO;CAIT,MAAM,YAAYA,UAAK,KACrBC,QAAG,QAAQ,EACX,eAAe,UAAU,OAC1B;CAED,IAAI;AACJ,KAAI;AACF,MAAIC,QAAG,WAAW,UAAU,CAC1B,SAAQ,KAAK,MAAMA,QAAG,aAAa,WAAW,OAAO,CAAC;MAEtD,SAAQ;GAAE,kBAAkB;GAAG,UAAU;GAAO;SAE5C;AACN,UAAQ;GAAE,kBAAkB;GAAG,UAAU;GAAO;;AAGlD,OAAM;AAGN,KAAI,CAAC,MAAM,YAAY,MAAM,oBAAoB,gBAAgB;AAC/D,QAAM,mBAAmB;AACzB,QAAM,WAAW;AAEjB,MAAI;AACF,WAAG,cAAc,WAAW,KAAK,UAAU,MAAM,CAAC;UAC5C;AAIR,SAAO,EACL,oBAAoB;GAClB,eAAe;GACf,mBAAmB;GACpB,EACF;;AAIH,KAAI;AACF,UAAG,cAAc,WAAW,KAAK,UAAU,MAAM,CAAC;SAC5C;AAIR,QAAO;;AAIT,IAAI,QAAQ,SAAS,OACnB,gBAAkC,SAAS;CACzC,MAAM,SAAS,oBAAoB,KAAK;AACxC,KAAI,OACF,SAAQ,OAAO,MAAM,KAAK,UAAU,OAAO,CAAC;EAE9C"}
|