maxsimcli 4.7.1 → 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.
Files changed (161) hide show
  1. package/dist/assets/CHANGELOG.md +20 -0
  2. package/dist/assets/hooks/maxsim-check-update.cjs +38 -0
  3. package/dist/assets/hooks/maxsim-check-update.cjs.map +1 -1
  4. package/dist/assets/hooks/maxsim-statusline.cjs +116 -48
  5. package/dist/assets/hooks/maxsim-statusline.cjs.map +1 -1
  6. package/dist/assets/hooks/maxsim-sync-reminder.cjs +117 -0
  7. package/dist/assets/hooks/maxsim-sync-reminder.cjs.map +1 -0
  8. package/dist/assets/templates/agents/AGENTS.md +78 -106
  9. package/dist/assets/templates/agents/executor.md +101 -0
  10. package/dist/assets/templates/agents/planner.md +86 -0
  11. package/dist/assets/templates/agents/researcher.md +71 -0
  12. package/dist/assets/templates/agents/verifier.md +88 -0
  13. package/dist/assets/templates/commands/maxsim/debug.md +7 -7
  14. package/dist/assets/templates/commands/maxsim/execute.md +45 -0
  15. package/dist/assets/templates/commands/maxsim/go.md +29 -0
  16. package/dist/assets/templates/commands/maxsim/help.md +2 -2
  17. package/dist/assets/templates/commands/maxsim/init.md +52 -0
  18. package/dist/assets/templates/commands/maxsim/plan.md +50 -0
  19. package/dist/assets/templates/commands/maxsim/progress.md +4 -3
  20. package/dist/assets/templates/commands/maxsim/quick.md +6 -4
  21. package/dist/assets/templates/commands/maxsim/settings.md +4 -3
  22. package/dist/assets/templates/references/continuation-format.md +16 -16
  23. package/dist/assets/templates/references/model-profile-resolution.md +1 -1
  24. package/dist/assets/templates/references/model-profiles.md +12 -19
  25. package/dist/assets/templates/rules/conventions.md +51 -0
  26. package/dist/assets/templates/rules/verification-protocol.md +57 -0
  27. package/dist/assets/templates/skills/agent-system-map/SKILL.md +92 -0
  28. package/dist/assets/templates/skills/brainstorming/SKILL.md +48 -36
  29. package/dist/assets/templates/skills/code-review/SKILL.md +40 -61
  30. package/dist/assets/templates/skills/commit-conventions/SKILL.md +75 -0
  31. package/dist/assets/templates/skills/evidence-collection/SKILL.md +87 -0
  32. package/dist/assets/templates/skills/handoff-contract/SKILL.md +70 -0
  33. package/dist/assets/templates/skills/input-validation/SKILL.md +51 -0
  34. package/dist/assets/templates/skills/maxsim-batch/SKILL.md +41 -45
  35. package/dist/assets/templates/skills/maxsim-simplify/SKILL.md +37 -90
  36. package/dist/assets/templates/skills/memory-management/SKILL.md +32 -67
  37. package/dist/assets/templates/skills/research-methodology/SKILL.md +137 -0
  38. package/dist/assets/templates/skills/roadmap-writing/SKILL.md +40 -58
  39. package/dist/assets/templates/skills/sdd/SKILL.md +34 -69
  40. package/dist/assets/templates/skills/systematic-debugging/SKILL.md +20 -26
  41. package/dist/assets/templates/skills/tdd/SKILL.md +25 -33
  42. package/dist/assets/templates/skills/tool-priority-guide/SKILL.md +80 -0
  43. package/dist/assets/templates/skills/using-maxsim/SKILL.md +42 -73
  44. package/dist/assets/templates/skills/verification-before-completion/SKILL.md +12 -24
  45. package/dist/assets/templates/skills/verification-gates/SKILL.md +169 -0
  46. package/dist/assets/templates/templates/UAT.md +3 -3
  47. package/dist/assets/templates/templates/VALIDATION.md +1 -1
  48. package/dist/assets/templates/templates/context.md +4 -4
  49. package/dist/assets/templates/templates/debug-subagent-prompt.md +3 -3
  50. package/dist/assets/templates/templates/discovery.md +2 -2
  51. package/dist/assets/templates/templates/phase-prompt.md +2 -2
  52. package/dist/assets/templates/templates/planner-subagent-prompt.md +7 -7
  53. package/dist/assets/templates/templates/project.md +1 -1
  54. package/dist/assets/templates/templates/research.md +1 -1
  55. package/dist/assets/templates/templates/state.md +2 -2
  56. package/dist/assets/templates/templates/summary.md +41 -0
  57. package/dist/assets/templates/workflows/batch.md +5 -5
  58. package/dist/assets/templates/workflows/diagnose-issues.md +2 -2
  59. package/dist/assets/templates/workflows/discovery-phase.md +3 -3
  60. package/dist/assets/templates/workflows/discuss-phase.md +11 -11
  61. package/dist/assets/templates/workflows/execute-phase.md +205 -11
  62. package/dist/assets/templates/workflows/execute-plan.md +299 -34
  63. package/dist/assets/templates/workflows/execute.md +421 -0
  64. package/dist/assets/templates/workflows/go.md +250 -0
  65. package/dist/assets/templates/workflows/health.md +5 -5
  66. package/dist/assets/templates/workflows/help.md +165 -435
  67. package/dist/assets/templates/workflows/init-existing.md +23 -23
  68. package/dist/assets/templates/workflows/init.md +205 -0
  69. package/dist/assets/templates/workflows/new-milestone.md +9 -9
  70. package/dist/assets/templates/workflows/new-project.md +26 -26
  71. package/dist/assets/templates/workflows/plan-create.md +298 -0
  72. package/dist/assets/templates/workflows/plan-discuss.md +347 -0
  73. package/dist/assets/templates/workflows/plan-phase.md +29 -29
  74. package/dist/assets/templates/workflows/plan-research.md +177 -0
  75. package/dist/assets/templates/workflows/plan.md +231 -0
  76. package/dist/assets/templates/workflows/progress.md +46 -42
  77. package/dist/assets/templates/workflows/quick.md +195 -14
  78. package/dist/assets/templates/workflows/research-phase.md +5 -5
  79. package/dist/assets/templates/workflows/sdd.md +20 -12
  80. package/dist/assets/templates/workflows/settings.md +18 -14
  81. package/dist/assets/templates/workflows/verify-phase.md +1 -1
  82. package/dist/assets/templates/workflows/verify-work.md +16 -16
  83. package/dist/cli.cjs +496 -91
  84. package/dist/cli.cjs.map +1 -1
  85. package/dist/core-D5zUr9cb.cjs.map +1 -1
  86. package/dist/install.cjs +274 -355
  87. package/dist/install.cjs.map +1 -1
  88. package/dist/mcp-server.cjs +5213 -2248
  89. package/dist/mcp-server.cjs.map +1 -1
  90. package/dist/skills-CjFWZIGM.cjs.map +1 -1
  91. package/package.json +4 -1
  92. package/dist/assets/hooks/maxsim-context-monitor.cjs +0 -121
  93. package/dist/assets/hooks/maxsim-context-monitor.cjs.map +0 -1
  94. package/dist/assets/templates/agents/maxsim-code-reviewer.md +0 -239
  95. package/dist/assets/templates/agents/maxsim-codebase-mapper.md +0 -214
  96. package/dist/assets/templates/agents/maxsim-debugger.md +0 -572
  97. package/dist/assets/templates/agents/maxsim-drift-checker.md +0 -522
  98. package/dist/assets/templates/agents/maxsim-executor.md +0 -504
  99. package/dist/assets/templates/agents/maxsim-integration-checker.md +0 -273
  100. package/dist/assets/templates/agents/maxsim-phase-researcher.md +0 -305
  101. package/dist/assets/templates/agents/maxsim-plan-checker.md +0 -343
  102. package/dist/assets/templates/agents/maxsim-planner.md +0 -610
  103. package/dist/assets/templates/agents/maxsim-project-researcher.md +0 -359
  104. package/dist/assets/templates/agents/maxsim-research-synthesizer.md +0 -263
  105. package/dist/assets/templates/agents/maxsim-roadmapper.md +0 -324
  106. package/dist/assets/templates/agents/maxsim-spec-reviewer.md +0 -245
  107. package/dist/assets/templates/agents/maxsim-verifier.md +0 -393
  108. package/dist/assets/templates/commands/maxsim/add-phase.md +0 -43
  109. package/dist/assets/templates/commands/maxsim/add-tests.md +0 -41
  110. package/dist/assets/templates/commands/maxsim/add-todo.md +0 -57
  111. package/dist/assets/templates/commands/maxsim/artefakte.md +0 -122
  112. package/dist/assets/templates/commands/maxsim/audit-milestone.md +0 -36
  113. package/dist/assets/templates/commands/maxsim/batch.md +0 -42
  114. package/dist/assets/templates/commands/maxsim/check-drift.md +0 -56
  115. package/dist/assets/templates/commands/maxsim/check-todos.md +0 -46
  116. package/dist/assets/templates/commands/maxsim/cleanup.md +0 -18
  117. package/dist/assets/templates/commands/maxsim/complete-milestone.md +0 -136
  118. package/dist/assets/templates/commands/maxsim/discuss-phase.md +0 -87
  119. package/dist/assets/templates/commands/maxsim/discuss.md +0 -70
  120. package/dist/assets/templates/commands/maxsim/execute-phase.md +0 -41
  121. package/dist/assets/templates/commands/maxsim/health.md +0 -22
  122. package/dist/assets/templates/commands/maxsim/init-existing.md +0 -46
  123. package/dist/assets/templates/commands/maxsim/insert-phase.md +0 -32
  124. package/dist/assets/templates/commands/maxsim/list-phase-assumptions.md +0 -46
  125. package/dist/assets/templates/commands/maxsim/map-codebase.md +0 -71
  126. package/dist/assets/templates/commands/maxsim/new-milestone.md +0 -44
  127. package/dist/assets/templates/commands/maxsim/new-project.md +0 -46
  128. package/dist/assets/templates/commands/maxsim/pause-work.md +0 -38
  129. package/dist/assets/templates/commands/maxsim/plan-milestone-gaps.md +0 -34
  130. package/dist/assets/templates/commands/maxsim/plan-phase.md +0 -44
  131. package/dist/assets/templates/commands/maxsim/realign.md +0 -39
  132. package/dist/assets/templates/commands/maxsim/reapply-patches.md +0 -110
  133. package/dist/assets/templates/commands/maxsim/remove-phase.md +0 -31
  134. package/dist/assets/templates/commands/maxsim/research-phase.md +0 -189
  135. package/dist/assets/templates/commands/maxsim/resume-work.md +0 -40
  136. package/dist/assets/templates/commands/maxsim/roadmap.md +0 -19
  137. package/dist/assets/templates/commands/maxsim/sdd.md +0 -39
  138. package/dist/assets/templates/commands/maxsim/set-profile.md +0 -34
  139. package/dist/assets/templates/commands/maxsim/update.md +0 -37
  140. package/dist/assets/templates/commands/maxsim/verify-work.md +0 -38
  141. package/dist/assets/templates/workflows/add-phase.md +0 -111
  142. package/dist/assets/templates/workflows/add-tests.md +0 -351
  143. package/dist/assets/templates/workflows/add-todo.md +0 -247
  144. package/dist/assets/templates/workflows/audit-milestone.md +0 -297
  145. package/dist/assets/templates/workflows/check-drift.md +0 -248
  146. package/dist/assets/templates/workflows/check-todos.md +0 -261
  147. package/dist/assets/templates/workflows/cleanup.md +0 -153
  148. package/dist/assets/templates/workflows/complete-milestone.md +0 -701
  149. package/dist/assets/templates/workflows/discuss.md +0 -343
  150. package/dist/assets/templates/workflows/insert-phase.md +0 -129
  151. package/dist/assets/templates/workflows/list-phase-assumptions.md +0 -178
  152. package/dist/assets/templates/workflows/map-codebase.md +0 -315
  153. package/dist/assets/templates/workflows/pause-work.md +0 -122
  154. package/dist/assets/templates/workflows/plan-milestone-gaps.md +0 -274
  155. package/dist/assets/templates/workflows/realign.md +0 -288
  156. package/dist/assets/templates/workflows/remove-phase.md +0 -154
  157. package/dist/assets/templates/workflows/resume-project.md +0 -306
  158. package/dist/assets/templates/workflows/roadmap.md +0 -130
  159. package/dist/assets/templates/workflows/set-profile.md +0 -81
  160. package/dist/assets/templates/workflows/transition.md +0 -544
  161. package/dist/assets/templates/workflows/update.md +0 -220
@@ -1,3 +1,23 @@
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
+
14
+ ## [4.7.1](https://github.com/maystudios/maxsimcli/compare/v4.7.0...v4.7.1) (2026-03-09)
15
+
16
+
17
+ ### Bug Fixes
18
+
19
+ * **infra:** remove dashboard package and backend server [INFRA-01, INFRA-02, INFRA-03] ([eb25801](https://github.com/maystudios/maxsimcli/commit/eb25801a251d9b346e0fa3e38d4566efbff1c701))
20
+
1
21
  # [4.7.0](https://github.com/maystudios/maxsimcli/compare/v4.6.0...v4.7.0) (2026-03-09)
2
22
 
3
23
 
@@ -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 context-monitor and statusline 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// 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;;AAIf,IAAI,QAAQ,SAAS,OACnB,gBAAe;CAAE,SAASC,QAAG,SAAS;CAAE,KAAK,QAAQ,KAAK;CAAE,CAAC"}
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 node_os = require("node:os");
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 context-monitor and statusline hooks.
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 | current task | directory | context usage
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 session = data.session_id || "";
70
- const remaining = data.context_window?.remaining_percentage;
71
- let ctx = "";
72
- if (remaining != null) {
73
- const rem = Math.round(remaining);
74
- const rawUsed = Math.max(0, Math.min(100, 100 - rem));
75
- const used = Math.min(100, Math.round(rawUsed / 80 * 100));
76
- if (session) try {
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
- let maxsimUpdate = "";
107
- const cacheFile = node_path.join(homeDir, CLAUDE_DIR, "cache", "maxsim-update-check.json");
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
- if (JSON.parse(node_fs.readFileSync(cacheFile, "utf8")).update_available) maxsimUpdate = "\x1B[33m⬆ /maxsim:update\x1B[0m │ ";
110
- } catch {}
111
- const dirname = node_path.basename(dir);
112
- if (task) return `${maxsimUpdate}\x1b[2m${model}\x1b[0m \u2502 \x1b[1m${task}\x1b[0m \u2502 \x1b[2m${dirname}\x1b[0m${ctx}`;
113
- else return `${maxsimUpdate}\x1b[2m${model}\x1b[0m \u2502 \x1b[2m${dirname}\x1b[0m${ctx}`;
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","os","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 context-monitor and statusline 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: model | current task | directory | context usage\n */\n\nimport * as fs from 'node:fs';\nimport * as path from 'node:path';\nimport * as os from 'node:os';\nimport { readStdinJson, CLAUDE_DIR } from './shared';\n\nexport interface StatuslineInput {\n model?: { display_name?: string };\n workspace?: { current_dir?: string };\n session_id?: string;\n context_window?: { remaining_percentage?: number };\n}\n\nexport function formatStatusline(data: StatuslineInput): string {\n const model = data.model?.display_name || 'Claude';\n const dir = data.workspace?.current_dir || process.cwd();\n const session = data.session_id || '';\n const remaining = data.context_window?.remaining_percentage;\n\n // Context window display (shows USED percentage scaled to 80% limit)\n let ctx = '';\n if (remaining != null) {\n const rem = Math.round(remaining);\n const rawUsed = Math.max(0, Math.min(100, 100 - rem));\n // Scale: 80% real usage = 100% displayed\n const used = Math.min(100, Math.round((rawUsed / 80) * 100));\n\n // Write context metrics to bridge file for the context-monitor PostToolUse hook.\n if (session) {\n try {\n const bridgePath = path.join(os.tmpdir(), `claude-ctx-${session}.json`);\n const bridgeData = JSON.stringify({\n session_id: session,\n remaining_percentage: remaining,\n used_pct: used,\n timestamp: Math.floor(Date.now() / 1000),\n });\n fs.writeFileSync(bridgePath, bridgeData);\n } catch {\n // Silent fail -- bridge is best-effort, don't break statusline\n }\n }\n\n // Build progress bar (10 segments)\n const filled = Math.floor(used / 10);\n const bar = '\\u2588'.repeat(filled) + '\\u2591'.repeat(10 - filled);\n\n // Color based on scaled usage\n if (used < 63) {\n ctx = ` \\x1b[32m${bar} ${used}%\\x1b[0m`;\n } else if (used < 81) {\n ctx = ` \\x1b[33m${bar} ${used}%\\x1b[0m`;\n } else if (used < 95) {\n ctx = ` \\x1b[38;5;208m${bar} ${used}%\\x1b[0m`;\n } else {\n ctx = ` \\x1b[5;31m\\uD83D\\uDC80 ${bar} ${used}%\\x1b[0m`;\n }\n }\n\n // Current task from todos\n let task = '';\n const homeDir = os.homedir();\n const todosDir = path.join(homeDir, CLAUDE_DIR, 'todos');\n if (session && fs.existsSync(todosDir)) {\n try {\n const files = fs.readdirSync(todosDir)\n .filter((f: string) => f.startsWith(session) && f.includes('-agent-') && f.endsWith('.json'))\n .map((f: string) => ({ name: f, mtime: fs.statSync(path.join(todosDir, f)).mtime }))\n .sort((a: { mtime: Date }, b: { mtime: Date }) => b.mtime.getTime() - a.mtime.getTime());\n\n if (files.length > 0) {\n try {\n const todos = JSON.parse(fs.readFileSync(path.join(todosDir, files[0].name), 'utf8'));\n const inProgress = todos.find((t: { status: string; activeForm?: string }) => t.status === 'in_progress');\n if (inProgress) task = inProgress.activeForm || '';\n } catch {\n // ignore\n }\n }\n } catch {\n // Silently fail on file system errors - don't break statusline\n }\n }\n\n // MAXSIM update available?\n let maxsimUpdate = '';\n const cacheFile = path.join(homeDir, CLAUDE_DIR, 'cache', 'maxsim-update-check.json');\n if (fs.existsSync(cacheFile)) {\n try {\n const cache = JSON.parse(fs.readFileSync(cacheFile, 'utf8'));\n if (cache.update_available) {\n maxsimUpdate = '\\x1b[33m\\u2B06 /maxsim:update\\x1b[0m \\u2502 ';\n }\n } catch {\n // ignore\n }\n }\n\n // Output\n const dirname = path.basename(dir);\n if (task) {\n return `${maxsimUpdate}\\x1b[2m${model}\\x1b[0m \\u2502 \\x1b[1m${task}\\x1b[0m \\u2502 \\x1b[2m${dirname}\\x1b[0m${ctx}`;\n } else {\n return `${maxsimUpdate}\\x1b[2m${model}\\x1b[0m \\u2502 \\x1b[2m${dirname}\\x1b[0m${ctx}`;\n }\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;;;;;;;;ACN1B,SAAgB,iBAAiB,MAA+B;CAC9D,MAAM,QAAQ,KAAK,OAAO,gBAAgB;CAC1C,MAAM,MAAM,KAAK,WAAW,eAAe,QAAQ,KAAK;CACxD,MAAM,UAAU,KAAK,cAAc;CACnC,MAAM,YAAY,KAAK,gBAAgB;CAGvC,IAAI,MAAM;AACV,KAAI,aAAa,MAAM;EACrB,MAAM,MAAM,KAAK,MAAM,UAAU;EACjC,MAAM,UAAU,KAAK,IAAI,GAAG,KAAK,IAAI,KAAK,MAAM,IAAI,CAAC;EAErD,MAAM,OAAO,KAAK,IAAI,KAAK,KAAK,MAAO,UAAU,KAAM,IAAI,CAAC;AAG5D,MAAI,QACF,KAAI;GACF,MAAM,aAAaA,UAAK,KAAKC,QAAG,QAAQ,EAAE,cAAc,QAAQ,OAAO;GACvE,MAAM,aAAa,KAAK,UAAU;IAChC,YAAY;IACZ,sBAAsB;IACtB,UAAU;IACV,WAAW,KAAK,MAAM,KAAK,KAAK,GAAG,IAAK;IACzC,CAAC;AACF,WAAG,cAAc,YAAY,WAAW;UAClC;EAMV,MAAM,SAAS,KAAK,MAAM,OAAO,GAAG;EACpC,MAAM,MAAM,IAAS,OAAO,OAAO,GAAG,IAAS,OAAO,KAAK,OAAO;AAGlE,MAAI,OAAO,GACT,OAAM,YAAY,IAAI,GAAG,KAAK;WACrB,OAAO,GAChB,OAAM,YAAY,IAAI,GAAG,KAAK;WACrB,OAAO,GAChB,OAAM,kBAAkB,IAAI,GAAG,KAAK;MAEpC,OAAM,2BAA2B,IAAI,GAAG,KAAK;;CAKjD,IAAI,OAAO;CACX,MAAM,UAAUA,QAAG,SAAS;CAC5B,MAAM,WAAWD,UAAK,KAAK,SAAS,YAAY,QAAQ;AACxD,KAAI,WAAWE,QAAG,WAAW,SAAS,CACpC,KAAI;EACF,MAAM,QAAQA,QAAG,YAAY,SAAS,CACnC,QAAQ,MAAc,EAAE,WAAW,QAAQ,IAAI,EAAE,SAAS,UAAU,IAAI,EAAE,SAAS,QAAQ,CAAC,CAC5F,KAAK,OAAe;GAAE,MAAM;GAAG,OAAOA,QAAG,SAASF,UAAK,KAAK,UAAU,EAAE,CAAC,CAAC;GAAO,EAAE,CACnF,MAAM,GAAoB,MAAuB,EAAE,MAAM,SAAS,GAAG,EAAE,MAAM,SAAS,CAAC;AAE1F,MAAI,MAAM,SAAS,EACjB,KAAI;GAEF,MAAM,aADQ,KAAK,MAAME,QAAG,aAAaF,UAAK,KAAK,UAAU,MAAM,GAAG,KAAK,EAAE,OAAO,CAAC,CAC5D,MAAM,MAA+C,EAAE,WAAW,cAAc;AACzG,OAAI,WAAY,QAAO,WAAW,cAAc;UAC1C;SAIJ;CAMV,IAAI,eAAe;CACnB,MAAM,YAAYA,UAAK,KAAK,SAAS,YAAY,SAAS,2BAA2B;AACrF,KAAIE,QAAG,WAAW,UAAU,CAC1B,KAAI;AAEF,MADc,KAAK,MAAMA,QAAG,aAAa,WAAW,OAAO,CAAC,CAClD,iBACR,gBAAe;SAEX;CAMV,MAAM,UAAUF,UAAK,SAAS,IAAI;AAClC,KAAI,KACF,QAAO,GAAG,aAAa,SAAS,MAAM,wBAAwB,KAAK,wBAAwB,QAAQ,SAAS;KAE5G,QAAO,GAAG,aAAa,SAAS,MAAM,wBAAwB,QAAQ,SAAS;;AAKnF,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","#!/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"}