maxsimcli 3.12.0 → 4.0.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 (178) hide show
  1. package/dist/.tsbuildinfo +1 -1
  2. package/dist/adapters/index.d.ts +0 -11
  3. package/dist/adapters/index.d.ts.map +1 -1
  4. package/dist/adapters/index.js +4 -40
  5. package/dist/adapters/index.js.map +1 -1
  6. package/dist/assets/CHANGELOG.md +17 -0
  7. package/dist/assets/dashboard/client/assets/{index-wtQDvXzr.js → index-C_eAetZJ.js} +60 -60
  8. package/dist/assets/dashboard/client/assets/index-CmiJKqOU.css +32 -0
  9. package/dist/assets/dashboard/client/index.html +2 -2
  10. package/dist/assets/dashboard/server.js +467 -271
  11. package/dist/assets/templates/agents/AGENTS.md +13 -1
  12. package/dist/assets/templates/agents/maxsim-debugger.md +2 -2
  13. package/dist/assets/templates/agents/maxsim-executor.md +5 -5
  14. package/dist/assets/templates/agents/maxsim-phase-researcher.md +2 -2
  15. package/dist/assets/templates/agents/maxsim-plan-checker.md +2 -2
  16. package/dist/assets/templates/agents/maxsim-planner.md +3 -3
  17. package/dist/assets/templates/commands/maxsim/add-todo.md +15 -5
  18. package/dist/assets/templates/commands/maxsim/discuss-phase.md +1 -0
  19. package/dist/assets/templates/commands/maxsim/init-existing.md +4 -0
  20. package/dist/assets/templates/commands/maxsim/new-project.md +4 -0
  21. package/dist/assets/templates/references/thinking-partner.md +41 -0
  22. package/dist/assets/templates/skills/batch-worktree/SKILL.md +137 -0
  23. package/dist/assets/templates/skills/brainstorming/SKILL.md +159 -0
  24. package/dist/assets/templates/skills/roadmap-writing/SKILL.md +198 -0
  25. package/dist/assets/templates/skills/sdd/SKILL.md +175 -0
  26. package/dist/assets/templates/skills/simplify/SKILL.md +48 -0
  27. package/dist/assets/templates/skills/using-maxsim/SKILL.md +6 -1
  28. package/dist/assets/templates/templates/acceptance-criteria.md +10 -0
  29. package/dist/assets/templates/templates/decisions.md +10 -0
  30. package/dist/assets/templates/templates/no-gos.md +9 -0
  31. package/dist/assets/templates/workflows/add-todo.md +89 -0
  32. package/dist/assets/templates/workflows/discuss-phase.md +85 -1
  33. package/dist/assets/templates/workflows/execute-phase.md +22 -2
  34. package/dist/assets/templates/workflows/execute-plan.md +166 -0
  35. package/dist/assets/templates/workflows/init-existing.md +116 -0
  36. package/dist/assets/templates/workflows/new-project.md +105 -1
  37. package/dist/assets/templates/workflows/plan-phase.md +3 -3
  38. package/dist/assets/templates/workflows/quick.md +2 -2
  39. package/dist/cli.cjs +1264 -882
  40. package/dist/cli.cjs.map +1 -1
  41. package/dist/cli.js +97 -74
  42. package/dist/cli.js.map +1 -1
  43. package/dist/core/artefakte.d.ts +12 -0
  44. package/dist/core/artefakte.d.ts.map +1 -0
  45. package/dist/core/artefakte.js +136 -0
  46. package/dist/core/artefakte.js.map +1 -0
  47. package/dist/core/commands.d.ts +13 -13
  48. package/dist/core/commands.d.ts.map +1 -1
  49. package/dist/core/commands.js +44 -57
  50. package/dist/core/commands.js.map +1 -1
  51. package/dist/core/config.d.ts +4 -3
  52. package/dist/core/config.d.ts.map +1 -1
  53. package/dist/core/config.js +14 -18
  54. package/dist/core/config.js.map +1 -1
  55. package/dist/core/context-loader.d.ts +20 -0
  56. package/dist/core/context-loader.d.ts.map +1 -0
  57. package/dist/core/context-loader.js +154 -0
  58. package/dist/core/context-loader.js.map +1 -0
  59. package/dist/core/core.d.ts +8 -2
  60. package/dist/core/core.d.ts.map +1 -1
  61. package/dist/core/core.js +47 -11
  62. package/dist/core/core.js.map +1 -1
  63. package/dist/core/dashboard-launcher.d.ts +1 -1
  64. package/dist/core/dashboard-launcher.d.ts.map +1 -1
  65. package/dist/core/dashboard-launcher.js +18 -15
  66. package/dist/core/dashboard-launcher.js.map +1 -1
  67. package/dist/core/frontmatter.d.ts +5 -5
  68. package/dist/core/frontmatter.d.ts.map +1 -1
  69. package/dist/core/frontmatter.js +21 -26
  70. package/dist/core/frontmatter.js.map +1 -1
  71. package/dist/core/index.d.ts +8 -3
  72. package/dist/core/index.d.ts.map +1 -1
  73. package/dist/core/index.js +23 -3
  74. package/dist/core/index.js.map +1 -1
  75. package/dist/core/init.d.ts +14 -14
  76. package/dist/core/init.d.ts.map +1 -1
  77. package/dist/core/init.js +93 -154
  78. package/dist/core/init.js.map +1 -1
  79. package/dist/core/milestone.d.ts +3 -3
  80. package/dist/core/milestone.d.ts.map +1 -1
  81. package/dist/core/milestone.js +9 -9
  82. package/dist/core/milestone.js.map +1 -1
  83. package/dist/core/phase.d.ts +9 -9
  84. package/dist/core/phase.d.ts.map +1 -1
  85. package/dist/core/phase.js +64 -68
  86. package/dist/core/phase.js.map +1 -1
  87. package/dist/core/roadmap.d.ts +4 -3
  88. package/dist/core/roadmap.d.ts.map +1 -1
  89. package/dist/core/roadmap.js +46 -109
  90. package/dist/core/roadmap.js.map +1 -1
  91. package/dist/core/skills.d.ts +19 -0
  92. package/dist/core/skills.d.ts.map +1 -0
  93. package/dist/core/skills.js +145 -0
  94. package/dist/core/skills.js.map +1 -0
  95. package/dist/core/start.d.ts +15 -0
  96. package/dist/core/start.d.ts.map +1 -0
  97. package/dist/core/start.js +80 -0
  98. package/dist/core/start.js.map +1 -0
  99. package/dist/core/state.d.ts +13 -13
  100. package/dist/core/state.d.ts.map +1 -1
  101. package/dist/core/state.js +119 -126
  102. package/dist/core/state.js.map +1 -1
  103. package/dist/core/template.d.ts +3 -3
  104. package/dist/core/template.d.ts.map +1 -1
  105. package/dist/core/template.js +12 -14
  106. package/dist/core/template.js.map +1 -1
  107. package/dist/core/types.d.ts +14 -2
  108. package/dist/core/types.d.ts.map +1 -1
  109. package/dist/core/types.js +8 -0
  110. package/dist/core/types.js.map +1 -1
  111. package/dist/core/verify.d.ts +10 -9
  112. package/dist/core/verify.d.ts.map +1 -1
  113. package/dist/core/verify.js +38 -48
  114. package/dist/core/verify.js.map +1 -1
  115. package/dist/core-TFSlUjV1.cjs +4312 -0
  116. package/dist/core-TFSlUjV1.cjs.map +1 -0
  117. package/dist/install/adapters.d.ts +2 -11
  118. package/dist/install/adapters.d.ts.map +1 -1
  119. package/dist/install/adapters.js +16 -154
  120. package/dist/install/adapters.js.map +1 -1
  121. package/dist/install/copy.d.ts +1 -10
  122. package/dist/install/copy.d.ts.map +1 -1
  123. package/dist/install/copy.js +5 -125
  124. package/dist/install/copy.js.map +1 -1
  125. package/dist/install/hooks.d.ts +4 -5
  126. package/dist/install/hooks.d.ts.map +1 -1
  127. package/dist/install/hooks.js +46 -71
  128. package/dist/install/hooks.js.map +1 -1
  129. package/dist/install/index.js +163 -226
  130. package/dist/install/index.js.map +1 -1
  131. package/dist/install/manifest.d.ts +5 -2
  132. package/dist/install/manifest.d.ts.map +1 -1
  133. package/dist/install/manifest.js +20 -26
  134. package/dist/install/manifest.js.map +1 -1
  135. package/dist/install/patches.d.ts +1 -2
  136. package/dist/install/patches.d.ts.map +1 -1
  137. package/dist/install/patches.js +4 -16
  138. package/dist/install/patches.js.map +1 -1
  139. package/dist/install/shared.d.ts +24 -18
  140. package/dist/install/shared.d.ts.map +1 -1
  141. package/dist/install/shared.js +65 -35
  142. package/dist/install/shared.js.map +1 -1
  143. package/dist/install/uninstall.d.ts +2 -3
  144. package/dist/install/uninstall.d.ts.map +1 -1
  145. package/dist/install/uninstall.js +24 -82
  146. package/dist/install/uninstall.js.map +1 -1
  147. package/dist/install.cjs +321 -1230
  148. package/dist/install.cjs.map +1 -1
  149. package/dist/mcp-server.cjs +38 -14
  150. package/dist/mcp-server.cjs.map +1 -1
  151. package/dist/skills-BOSxYUzf.cjs +6812 -0
  152. package/dist/skills-BOSxYUzf.cjs.map +1 -0
  153. package/package.json +1 -1
  154. package/dist/adapters/codex.d.ts +0 -19
  155. package/dist/adapters/codex.d.ts.map +0 -1
  156. package/dist/adapters/codex.js +0 -94
  157. package/dist/adapters/codex.js.map +0 -1
  158. package/dist/adapters/gemini.d.ts +0 -19
  159. package/dist/adapters/gemini.d.ts.map +0 -1
  160. package/dist/adapters/gemini.js +0 -96
  161. package/dist/adapters/gemini.js.map +0 -1
  162. package/dist/adapters/opencode.d.ts +0 -17
  163. package/dist/adapters/opencode.d.ts.map +0 -1
  164. package/dist/adapters/opencode.js +0 -111
  165. package/dist/adapters/opencode.js.map +0 -1
  166. package/dist/adapters/transforms/content.d.ts +0 -39
  167. package/dist/adapters/transforms/content.d.ts.map +0 -1
  168. package/dist/adapters/transforms/content.js +0 -125
  169. package/dist/adapters/transforms/content.js.map +0 -1
  170. package/dist/adapters/transforms/frontmatter.d.ts +0 -42
  171. package/dist/adapters/transforms/frontmatter.d.ts.map +0 -1
  172. package/dist/adapters/transforms/frontmatter.js +0 -204
  173. package/dist/adapters/transforms/frontmatter.js.map +0 -1
  174. package/dist/adapters/transforms/tool-maps.d.ts +0 -20
  175. package/dist/adapters/transforms/tool-maps.d.ts.map +0 -1
  176. package/dist/adapters/transforms/tool-maps.js +0 -64
  177. package/dist/adapters/transforms/tool-maps.js.map +0 -1
  178. package/dist/assets/dashboard/client/assets/index-CxFKStBk.css +0 -32
package/dist/install.cjs CHANGED
@@ -1,4 +1,5 @@
1
1
  #!/usr/bin/env node
2
+ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
2
3
  //#region \0rolldown/runtime.js
3
4
  var __create = Object.create;
4
5
  var __defProp = Object.defineProperty;
@@ -5646,7 +5647,6 @@ function ora(options) {
5646
5647
  //#region ../../node_modules/@inquirer/core/dist/lib/key.js
5647
5648
  const isUpKey = (key, keybindings = []) => key.name === "up" || keybindings.includes("vim") && key.name === "k" || keybindings.includes("emacs") && key.ctrl && key.name === "p";
5648
5649
  const isDownKey = (key, keybindings = []) => key.name === "down" || keybindings.includes("vim") && key.name === "j" || keybindings.includes("emacs") && key.ctrl && key.name === "n";
5649
- const isSpaceKey = (key) => key.name === "space";
5650
5650
  const isBackspaceKey = (key) => key.name === "backspace";
5651
5651
  const isTabKey = (key) => key.name === "tab";
5652
5652
  const isNumberKey = (key) => "1234567890".includes(key.name);
@@ -7138,187 +7138,6 @@ var Separator = class {
7138
7138
  }
7139
7139
  };
7140
7140
 
7141
- //#endregion
7142
- //#region ../../node_modules/@inquirer/checkbox/dist/index.js
7143
- const checkboxTheme = {
7144
- icon: {
7145
- checked: (0, node_util.styleText)("green", figures.circleFilled),
7146
- unchecked: figures.circle,
7147
- cursor: figures.pointer,
7148
- disabledChecked: (0, node_util.styleText)("green", figures.circleDouble),
7149
- disabledUnchecked: "-"
7150
- },
7151
- style: {
7152
- disabled: (text) => (0, node_util.styleText)("dim", text),
7153
- renderSelectedChoices: (selectedChoices) => selectedChoices.map((choice) => choice.short).join(", "),
7154
- description: (text) => (0, node_util.styleText)("cyan", text),
7155
- keysHelpTip: (keys) => keys.map(([key, action]) => `${(0, node_util.styleText)("bold", key)} ${(0, node_util.styleText)("dim", action)}`).join((0, node_util.styleText)("dim", " • "))
7156
- },
7157
- i18n: { disabledError: "This option is disabled and cannot be toggled." },
7158
- keybindings: []
7159
- };
7160
- function isSelectable$1(item) {
7161
- return !Separator.isSeparator(item) && !item.disabled;
7162
- }
7163
- function isNavigable$1(item) {
7164
- return !Separator.isSeparator(item);
7165
- }
7166
- function isChecked(item) {
7167
- return !Separator.isSeparator(item) && item.checked;
7168
- }
7169
- function toggle(item) {
7170
- return isSelectable$1(item) ? {
7171
- ...item,
7172
- checked: !item.checked
7173
- } : item;
7174
- }
7175
- function check(checked) {
7176
- return function(item) {
7177
- return isSelectable$1(item) ? {
7178
- ...item,
7179
- checked
7180
- } : item;
7181
- };
7182
- }
7183
- function normalizeChoices$1(choices) {
7184
- return choices.map((choice) => {
7185
- if (Separator.isSeparator(choice)) return choice;
7186
- if (typeof choice === "string") return {
7187
- value: choice,
7188
- name: choice,
7189
- short: choice,
7190
- checkedName: choice,
7191
- disabled: false,
7192
- checked: false
7193
- };
7194
- const name = choice.name ?? String(choice.value);
7195
- const normalizedChoice = {
7196
- value: choice.value,
7197
- name,
7198
- short: choice.short ?? name,
7199
- checkedName: choice.checkedName ?? name,
7200
- disabled: choice.disabled ?? false,
7201
- checked: choice.checked ?? false
7202
- };
7203
- if (choice.description) normalizedChoice.description = choice.description;
7204
- return normalizedChoice;
7205
- });
7206
- }
7207
- var dist_default$2 = createPrompt((config, done) => {
7208
- const { pageSize = 7, loop = true, required, validate = () => true } = config;
7209
- const shortcuts = {
7210
- all: "a",
7211
- invert: "i",
7212
- ...config.shortcuts
7213
- };
7214
- const theme = makeTheme(checkboxTheme, config.theme);
7215
- const { keybindings } = theme;
7216
- const [status, setStatus] = useState("idle");
7217
- const prefix = usePrefix({
7218
- status,
7219
- theme
7220
- });
7221
- const [items, setItems] = useState(normalizeChoices$1(config.choices));
7222
- const bounds = useMemo(() => {
7223
- const first = items.findIndex(isNavigable$1);
7224
- const last = items.findLastIndex(isNavigable$1);
7225
- if (first === -1) throw new ValidationError("[checkbox prompt] No selectable choices. All choices are disabled.");
7226
- return {
7227
- first,
7228
- last
7229
- };
7230
- }, [items]);
7231
- const [active, setActive] = useState(bounds.first);
7232
- const [errorMsg, setError] = useState();
7233
- useKeypress(async (key) => {
7234
- if (isEnterKey(key)) {
7235
- const selection = items.filter(isChecked);
7236
- const isValid = await validate([...selection]);
7237
- if (required && !selection.length) setError("At least one choice must be selected");
7238
- else if (isValid === true) {
7239
- setStatus("done");
7240
- done(selection.map((choice) => choice.value));
7241
- } else setError(isValid || "You must select a valid value");
7242
- } else if (isUpKey(key, keybindings) || isDownKey(key, keybindings)) {
7243
- if (errorMsg) setError(void 0);
7244
- if (loop || isUpKey(key, keybindings) && active !== bounds.first || isDownKey(key, keybindings) && active !== bounds.last) {
7245
- const offset = isUpKey(key, keybindings) ? -1 : 1;
7246
- let next = active;
7247
- do
7248
- next = (next + offset + items.length) % items.length;
7249
- while (!isNavigable$1(items[next]));
7250
- setActive(next);
7251
- }
7252
- } else if (isSpaceKey(key)) {
7253
- const activeItem = items[active];
7254
- if (activeItem && !Separator.isSeparator(activeItem)) if (activeItem.disabled) setError(theme.i18n.disabledError);
7255
- else {
7256
- setError(void 0);
7257
- setItems(items.map((choice, i) => i === active ? toggle(choice) : choice));
7258
- }
7259
- } else if (key.name === shortcuts.all) {
7260
- const selectAll = items.some((choice) => isSelectable$1(choice) && !choice.checked);
7261
- setItems(items.map(check(selectAll)));
7262
- } else if (key.name === shortcuts.invert) setItems(items.map(toggle));
7263
- else if (isNumberKey(key)) {
7264
- const selectedIndex = Number(key.name) - 1;
7265
- let selectableIndex = -1;
7266
- const position = items.findIndex((item) => {
7267
- if (Separator.isSeparator(item)) return false;
7268
- selectableIndex++;
7269
- return selectableIndex === selectedIndex;
7270
- });
7271
- const selectedItem = items[position];
7272
- if (selectedItem && isSelectable$1(selectedItem)) {
7273
- setActive(position);
7274
- setItems(items.map((choice, i) => i === position ? toggle(choice) : choice));
7275
- }
7276
- }
7277
- });
7278
- const message = theme.style.message(config.message, status);
7279
- let description;
7280
- const page = usePagination({
7281
- items,
7282
- active,
7283
- renderItem({ item, isActive }) {
7284
- if (Separator.isSeparator(item)) return ` ${item.separator}`;
7285
- const cursor = isActive ? theme.icon.cursor : " ";
7286
- if (item.disabled) {
7287
- const disabledLabel = typeof item.disabled === "string" ? item.disabled : "(disabled)";
7288
- const checkbox = item.checked ? theme.icon.disabledChecked : theme.icon.disabledUnchecked;
7289
- return theme.style.disabled(`${cursor}${checkbox} ${item.name} ${disabledLabel}`);
7290
- }
7291
- if (isActive) description = item.description;
7292
- const checkbox = item.checked ? theme.icon.checked : theme.icon.unchecked;
7293
- const name = item.checked ? item.checkedName : item.name;
7294
- return (isActive ? theme.style.highlight : (x) => x)(`${cursor}${checkbox} ${name}`);
7295
- },
7296
- pageSize,
7297
- loop
7298
- });
7299
- if (status === "done") {
7300
- const selection = items.filter(isChecked);
7301
- return [
7302
- prefix,
7303
- message,
7304
- theme.style.answer(theme.style.renderSelectedChoices(selection, items))
7305
- ].filter(Boolean).join(" ");
7306
- }
7307
- const keys = [["↑↓", "navigate"], ["space", "select"]];
7308
- if (shortcuts.all) keys.push([shortcuts.all, "all"]);
7309
- if (shortcuts.invert) keys.push([shortcuts.invert, "invert"]);
7310
- keys.push(["⏎", "submit"]);
7311
- const helpLine = theme.style.keysHelpTip(keys);
7312
- return `${[
7313
- [prefix, message].filter(Boolean).join(" "),
7314
- page,
7315
- " ",
7316
- description ? theme.style.description(description) : "",
7317
- errorMsg ? theme.style.error(errorMsg) : "",
7318
- helpLine
7319
- ].filter(Boolean).join("\n").trimEnd()}${cursorHide}`;
7320
- });
7321
-
7322
7141
  //#endregion
7323
7142
  //#region ../../node_modules/@inquirer/confirm/dist/index.js
7324
7143
  function getBooleanValue(value, defaultValue) {
@@ -7701,25 +7520,6 @@ function expandTilde(filePath) {
7701
7520
  return filePath;
7702
7521
  }
7703
7522
  /**
7704
- * Extract YAML frontmatter and body from markdown content.
7705
- * Returns null frontmatter if content doesn't start with ---.
7706
- */
7707
- function extractFrontmatterAndBody(content) {
7708
- if (!content.startsWith("---")) return {
7709
- frontmatter: null,
7710
- body: content
7711
- };
7712
- const endIndex = content.indexOf("---", 3);
7713
- if (endIndex === -1) return {
7714
- frontmatter: null,
7715
- body: content
7716
- };
7717
- return {
7718
- frontmatter: content.substring(3, endIndex).trim(),
7719
- body: content.substring(endIndex + 3)
7720
- };
7721
- }
7722
- /**
7723
7523
  * Process Co-Authored-By lines based on attribution setting.
7724
7524
  * @param content - File content to process
7725
7525
  * @param attribution - null=remove, undefined=keep default, string=replace
@@ -7769,7 +7569,7 @@ function writeSettings(settingsPath, settings) {
7769
7569
  * Get the global config directory for Claude Code.
7770
7570
  * Priority: explicitDir > CLAUDE_CONFIG_DIR env > ~/.claude
7771
7571
  */
7772
- function getGlobalDir$4(explicitDir) {
7572
+ function getGlobalDir$1(explicitDir) {
7773
7573
  if (explicitDir) return expandTilde(explicitDir);
7774
7574
  if (process.env.CLAUDE_CONFIG_DIR) return expandTilde(process.env.CLAUDE_CONFIG_DIR);
7775
7575
  return node_path.join(node_os.homedir(), ".claude");
@@ -7778,7 +7578,7 @@ function getGlobalDir$4(explicitDir) {
7778
7578
  * Get the config directory path relative to home for hook templating.
7779
7579
  * Used for path.join(homeDir, '<configDir>', ...) replacement in hooks.
7780
7580
  */
7781
- function getConfigDirFromHome$4(isGlobal) {
7581
+ function getConfigDirFromHome$1(isGlobal) {
7782
7582
  return "'.claude'";
7783
7583
  }
7784
7584
  /**
@@ -7786,7 +7586,7 @@ function getConfigDirFromHome$4(isGlobal) {
7786
7586
  * For Claude, this is path replacement only — no frontmatter conversion needed.
7787
7587
  * Replaces ~/.claude/ and ./.claude/ references with the actual install path prefix.
7788
7588
  */
7789
- function transformContent$3(content, pathPrefix) {
7589
+ function transformContent(content, pathPrefix) {
7790
7590
  const globalClaudeRegex = /~\/\.claude\//g;
7791
7591
  const localClaudeRegex = /\.\/\.claude\//g;
7792
7592
  let result = content.replace(globalClaudeRegex, pathPrefix);
@@ -7800,525 +7600,44 @@ function transformContent$3(content, pathPrefix) {
7800
7600
  const claudeAdapter = {
7801
7601
  runtime: "claude",
7802
7602
  dirName: ".claude",
7803
- getGlobalDir: getGlobalDir$4,
7804
- getConfigDirFromHome: getConfigDirFromHome$4,
7805
- transformContent: transformContent$3,
7806
- commandStructure: "nested"
7807
- };
7808
-
7809
- //#endregion
7810
- //#region src/adapters/transforms/tool-maps.ts
7811
- /**
7812
- * @maxsim/adapters — Tool name mappings per runtime
7813
- *
7814
- * Ported from bin/install.js lines ~327-390
7815
- */
7816
- /** Tool name mapping from Claude Code to OpenCode */
7817
- const claudeToOpencodeTools = {
7818
- AskUserQuestion: "question",
7819
- SlashCommand: "skill",
7820
- TodoWrite: "todowrite",
7821
- WebFetch: "webfetch",
7822
- WebSearch: "websearch"
7823
- };
7824
- /** Tool name mapping from Claude Code to Gemini CLI */
7825
- const claudeToGeminiTools = {
7826
- Read: "read_file",
7827
- Write: "write_file",
7828
- Edit: "replace",
7829
- Bash: "run_shell_command",
7830
- Glob: "glob",
7831
- Grep: "search_file_content",
7832
- WebSearch: "google_web_search",
7833
- WebFetch: "web_fetch",
7834
- TodoWrite: "write_todos",
7835
- AskUserQuestion: "ask_user"
7836
- };
7837
- /**
7838
- * Convert a Claude Code tool name to OpenCode format.
7839
- * - Applies special mappings (AskUserQuestion -> question, etc.)
7840
- * - Converts to lowercase (except MCP tools which keep their format)
7841
- */
7842
- function convertToolName(claudeTool) {
7843
- if (claudeToOpencodeTools[claudeTool]) return claudeToOpencodeTools[claudeTool];
7844
- if (claudeTool.startsWith("mcp__")) return claudeTool;
7845
- return claudeTool.toLowerCase();
7846
- }
7847
- /**
7848
- * Convert a Claude Code tool name to Gemini CLI format.
7849
- * - Applies Claude->Gemini mapping (Read->read_file, Bash->run_shell_command, etc.)
7850
- * - Filters out MCP tools (mcp__*) -- auto-discovered at runtime in Gemini
7851
- * - Filters out Task -- agents are auto-registered as tools in Gemini
7852
- * @returns Gemini tool name, or null if tool should be excluded
7853
- */
7854
- function convertGeminiToolName(claudeTool) {
7855
- if (claudeTool.startsWith("mcp__")) return null;
7856
- if (claudeTool === "Task") return null;
7857
- if (claudeToGeminiTools[claudeTool]) return claudeToGeminiTools[claudeTool];
7858
- return claudeTool.toLowerCase();
7859
- }
7860
-
7861
- //#endregion
7862
- //#region src/adapters/transforms/content.ts
7863
- /**
7864
- * @maxsim/adapters — Content transformation utilities
7865
- *
7866
- * Ported from bin/install.js lines ~423-564
7867
- */
7868
- /**
7869
- * Convert /maxsim:command-name to $maxsim-command-name for Codex skill mentions.
7870
- * Ported from install.js line ~423
7871
- */
7872
- function convertSlashCommandsToCodexSkillMentions(content) {
7873
- let converted = content.replace(/\/maxsim:([a-z0-9-]+)/gi, (_, commandName) => {
7874
- return `$maxsim-${String(commandName).toLowerCase()}`;
7875
- });
7876
- converted = converted.replace(/\/maxsim-help\b/g, "$maxsim-help");
7877
- return converted;
7878
- }
7879
- /**
7880
- * Convert Claude markdown to Codex markdown format.
7881
- * Replaces slash commands and $ARGUMENTS placeholder.
7882
- * Ported from install.js line ~431
7883
- */
7884
- function convertClaudeToCodexMarkdown(content) {
7885
- let converted = convertSlashCommandsToCodexSkillMentions(content);
7886
- converted = converted.replace(/\$ARGUMENTS\b/g, "{{MAXSIM_ARGS}}");
7887
- return converted;
7888
- }
7889
- /**
7890
- * Strip HTML <sub> tags for Gemini CLI output.
7891
- * Terminals don't support subscript -- converts <sub>text</sub> to italic *(text)*.
7892
- * Ported from install.js line ~474
7893
- */
7894
- function stripSubTags(content) {
7895
- return content.replace(/<sub>(.*?)<\/sub>/g, "*($1)*");
7896
- }
7897
- /**
7898
- * Convert Claude Code agent frontmatter to Gemini CLI format.
7899
- * - tools: must be a YAML array (not comma-separated string)
7900
- * - tool names: must use Gemini built-in names (read_file, not Read)
7901
- * - color: must be removed (causes validation error)
7902
- * - mcp__* tools: must be excluded (auto-discovered at runtime)
7903
- * - ${VAR} patterns: escaped to $VAR for Gemini template compatibility
7904
- *
7905
- * Ported from install.js line ~487
7906
- */
7907
- function convertClaudeToGeminiAgent(content) {
7908
- if (!content.startsWith("---")) return content;
7909
- const endIndex = content.indexOf("---", 3);
7910
- if (endIndex === -1) return content;
7911
- const frontmatter = content.substring(3, endIndex).trim();
7912
- const body = content.substring(endIndex + 3);
7913
- const lines = frontmatter.split("\n");
7914
- const newLines = [];
7915
- let inAllowedTools = false;
7916
- const tools = [];
7917
- for (const line of lines) {
7918
- const trimmed = line.trim();
7919
- if (trimmed.startsWith("allowed-tools:")) {
7920
- inAllowedTools = true;
7921
- continue;
7922
- }
7923
- if (trimmed.startsWith("tools:")) {
7924
- const toolsValue = trimmed.substring(6).trim();
7925
- if (toolsValue) {
7926
- const parsed = toolsValue.split(",").map((t) => t.trim()).filter((t) => t);
7927
- for (const t of parsed) {
7928
- const mapped = convertGeminiToolName(t);
7929
- if (mapped) tools.push(mapped);
7930
- }
7931
- } else inAllowedTools = true;
7932
- continue;
7933
- }
7934
- if (trimmed.startsWith("color:")) continue;
7935
- if (inAllowedTools) {
7936
- if (trimmed.startsWith("- ")) {
7937
- const mapped = convertGeminiToolName(trimmed.substring(2).trim());
7938
- if (mapped) tools.push(mapped);
7939
- continue;
7940
- } else if (trimmed && !trimmed.startsWith("-")) inAllowedTools = false;
7941
- }
7942
- if (!inAllowedTools) newLines.push(line);
7943
- }
7944
- if (tools.length > 0) {
7945
- newLines.push("tools:");
7946
- for (const tool of tools) newLines.push(` - ${tool}`);
7947
- }
7948
- return `---\n${newLines.join("\n").trim()}\n---${stripSubTags(body.replace(/\$\{(\w+)\}/g, "$$$1"))}`;
7949
- }
7950
- /**
7951
- * Replace path references in markdown content for a target runtime.
7952
- * Replaces ~/.claude/ with pathPrefix and ./.claude/ with ./dirName/.
7953
- */
7954
- function replacePathReferences(content, pathPrefix, dirName) {
7955
- const globalClaudeRegex = /~\/\.claude\//g;
7956
- const localClaudeRegex = /\.\/\.claude\//g;
7957
- let result = content.replace(globalClaudeRegex, pathPrefix);
7958
- result = result.replace(localClaudeRegex, `./${dirName}/`);
7959
- return result;
7960
- }
7961
-
7962
- //#endregion
7963
- //#region src/adapters/transforms/frontmatter.ts
7964
- /**
7965
- * @maxsim/adapters — Frontmatter conversion functions for opencode, gemini, codex
7966
- *
7967
- * Ported from bin/install.js lines ~308-711
7968
- */
7969
- /** Color name to hex mapping for opencode compatibility */
7970
- const colorNameToHex = {
7971
- cyan: "#00FFFF",
7972
- red: "#FF0000",
7973
- green: "#00FF00",
7974
- blue: "#0000FF",
7975
- yellow: "#FFFF00",
7976
- magenta: "#FF00FF",
7977
- orange: "#FFA500",
7978
- purple: "#800080",
7979
- pink: "#FFC0CB",
7980
- white: "#FFFFFF",
7981
- black: "#000000",
7982
- gray: "#808080",
7983
- grey: "#808080"
7984
- };
7985
- /** Collapse whitespace to single line */
7986
- function toSingleLine(value) {
7987
- return value.replace(/\s+/g, " ").trim();
7988
- }
7989
- /** Quote a value for YAML using JSON.stringify */
7990
- function yamlQuote(value) {
7991
- return JSON.stringify(value);
7992
- }
7993
- /** Extract a single-line field value from YAML frontmatter text */
7994
- function extractFrontmatterField(frontmatter, fieldName) {
7995
- const regex = new RegExp(`^${fieldName}:\\s*(.+)$`, "m");
7996
- const match = frontmatter.match(regex);
7997
- if (!match) return null;
7998
- return match[1].trim().replace(/^['"]|['"]$/g, "");
7999
- }
8000
- /**
8001
- * Convert Claude Code frontmatter to OpenCode format.
8002
- * - Converts 'allowed-tools:' array to 'tools:' object with tool: true entries
8003
- * - Converts color names to hex
8004
- * - Removes name: field (opencode uses filename)
8005
- * - Replaces tool name references in body content
8006
- * - Replaces /maxsim: with /maxsim- (flat command structure)
8007
- * - Replaces ~/.claude with ~/.config/opencode
8008
- * - Replaces subagent_type="general-purpose" with "general"
8009
- *
8010
- * Ported from install.js line ~566
8011
- */
8012
- function convertClaudeToOpencodeFrontmatter(content) {
8013
- let convertedContent = content;
8014
- convertedContent = convertedContent.replace(/\bAskUserQuestion\b/g, "question");
8015
- convertedContent = convertedContent.replace(/\bSlashCommand\b/g, "skill");
8016
- convertedContent = convertedContent.replace(/\bTodoWrite\b/g, "todowrite");
8017
- convertedContent = convertedContent.replace(/\/maxsim:/g, "/maxsim-");
8018
- convertedContent = convertedContent.replace(/~\/\.claude\b/g, "~/.config/opencode");
8019
- convertedContent = convertedContent.replace(/subagent_type="general-purpose"/g, "subagent_type=\"general\"");
8020
- if (!convertedContent.startsWith("---")) return convertedContent;
8021
- const endIndex = convertedContent.indexOf("---", 3);
8022
- if (endIndex === -1) return convertedContent;
8023
- const frontmatter = convertedContent.substring(3, endIndex).trim();
8024
- const body = convertedContent.substring(endIndex + 3);
8025
- const lines = frontmatter.split("\n");
8026
- const newLines = [];
8027
- let inAllowedTools = false;
8028
- const allowedTools = [];
8029
- for (const line of lines) {
8030
- const trimmed = line.trim();
8031
- if (trimmed.startsWith("allowed-tools:")) {
8032
- inAllowedTools = true;
8033
- continue;
8034
- }
8035
- if (trimmed.startsWith("tools:")) {
8036
- const toolsValue = trimmed.substring(6).trim();
8037
- if (toolsValue) {
8038
- const tools = toolsValue.split(",").map((t) => t.trim()).filter((t) => t);
8039
- allowedTools.push(...tools);
8040
- }
8041
- continue;
8042
- }
8043
- if (trimmed.startsWith("name:")) continue;
8044
- if (trimmed.startsWith("color:")) {
8045
- const colorValue = trimmed.substring(6).trim().toLowerCase();
8046
- const hexColor = colorNameToHex[colorValue];
8047
- if (hexColor) newLines.push(`color: "${hexColor}"`);
8048
- else if (colorValue.startsWith("#")) {
8049
- if (/^#[0-9a-f]{3}$|^#[0-9a-f]{6}$/i.test(colorValue)) newLines.push(line);
8050
- }
8051
- continue;
8052
- }
8053
- if (inAllowedTools) {
8054
- if (trimmed.startsWith("- ")) {
8055
- allowedTools.push(trimmed.substring(2).trim());
8056
- continue;
8057
- } else if (trimmed && !trimmed.startsWith("-")) inAllowedTools = false;
8058
- }
8059
- if (!inAllowedTools) newLines.push(line);
8060
- }
8061
- if (allowedTools.length > 0) {
8062
- newLines.push("tools:");
8063
- for (const tool of allowedTools) newLines.push(` ${convertToolName(tool)}: true`);
8064
- }
8065
- return `---\n${newLines.join("\n").trim()}\n---${body}`;
8066
- }
8067
- /**
8068
- * Convert Claude Code markdown command to Gemini TOML format.
8069
- * Ported from install.js line ~677
8070
- */
8071
- function convertClaudeToGeminiToml(content) {
8072
- if (!content.startsWith("---")) return `prompt = ${JSON.stringify(content)}\n`;
8073
- const endIndex = content.indexOf("---", 3);
8074
- if (endIndex === -1) return `prompt = ${JSON.stringify(content)}\n`;
8075
- const frontmatter = content.substring(3, endIndex).trim();
8076
- const body = content.substring(endIndex + 3).trim();
8077
- let description = "";
8078
- const lines = frontmatter.split("\n");
8079
- for (const line of lines) {
8080
- const trimmed = line.trim();
8081
- if (trimmed.startsWith("description:")) {
8082
- description = trimmed.substring(12).trim();
8083
- break;
8084
- }
8085
- }
8086
- let toml = "";
8087
- if (description) toml += `description = ${JSON.stringify(description)}\n`;
8088
- toml += `prompt = ${JSON.stringify(body)}\n`;
8089
- return toml;
8090
- }
8091
- /**
8092
- * Convert Claude command to Codex skill format with adapter header.
8093
- * Ported from install.js line ~452
8094
- */
8095
- function convertClaudeCommandToCodexSkill(content, skillName) {
8096
- const { frontmatter, body } = extractFrontmatterAndBody(convertClaudeToCodexMarkdown(content));
8097
- let description = `Run MAXSIM workflow ${skillName}.`;
8098
- if (frontmatter) {
8099
- const maybeDescription = extractFrontmatterField(frontmatter, "description");
8100
- if (maybeDescription) description = maybeDescription;
8101
- }
8102
- description = toSingleLine(description);
8103
- const shortDescription = description.length > 180 ? `${description.slice(0, 177)}...` : description;
8104
- const adapter = getCodexSkillAdapterHeader(skillName);
8105
- return `---\nname: ${yamlQuote(skillName)}\ndescription: ${yamlQuote(description)}\nmetadata:\n short-description: ${yamlQuote(shortDescription)}\n---\n\n${adapter}\n\n${body.trimStart()}`;
8106
- }
8107
- /**
8108
- * Generate the Codex skill adapter header block.
8109
- * Ported from install.js line ~437
8110
- */
8111
- function getCodexSkillAdapterHeader(skillName) {
8112
- const invocation = `$${skillName}`;
8113
- return `<codex_skill_adapter>
8114
- Codex skills-first mode:
8115
- - This skill is invoked by mentioning \`${invocation}\`.
8116
- - Treat all user text after \`${invocation}\` as \`{{MAXSIM_ARGS}}\`.
8117
- - If no arguments are present, treat \`{{MAXSIM_ARGS}}\` as empty.
8118
-
8119
- Legacy orchestration compatibility:
8120
- - Any \`Task(...)\` pattern in referenced workflow docs is legacy syntax.
8121
- - Implement equivalent behavior with Codex collaboration tools: \`spawn_agent\`, \`wait\`, \`send_input\`, and \`close_agent\`.
8122
- - Treat legacy \`subagent_type\` names as role hints in the spawned message.
8123
- </codex_skill_adapter>`;
8124
- }
8125
-
8126
- //#endregion
8127
- //#region src/adapters/opencode.ts
8128
- /**
8129
- * @maxsim/adapters — OpenCode adapter
8130
- *
8131
- * Ports the OpenCode-specific logic from bin/install.js:
8132
- * - getOpencodeGlobalDir() (lines 79-97)
8133
- * - getGlobalDir('opencode', ...) (lines 104-111)
8134
- * - getDirName('opencode') (line 46)
8135
- * - getConfigDirFromHome('opencode', isGlobal) (lines 58-68)
8136
- * - convertClaudeToOpencodeFrontmatter + path replacement
8137
- */
8138
- /**
8139
- * Get the global config directory for OpenCode.
8140
- * OpenCode follows XDG Base Directory spec and uses ~/.config/opencode/.
8141
- * Priority: OPENCODE_CONFIG_DIR > dirname(OPENCODE_CONFIG) > XDG_CONFIG_HOME/opencode > ~/.config/opencode
8142
- */
8143
- function getOpencodeGlobalDir$1() {
8144
- if (process.env.OPENCODE_CONFIG_DIR) return expandTilde(process.env.OPENCODE_CONFIG_DIR);
8145
- if (process.env.OPENCODE_CONFIG) return node_path.dirname(expandTilde(process.env.OPENCODE_CONFIG));
8146
- if (process.env.XDG_CONFIG_HOME) return node_path.join(expandTilde(process.env.XDG_CONFIG_HOME), "opencode");
8147
- return node_path.join(node_os.homedir(), ".config", "opencode");
8148
- }
8149
- /**
8150
- * Get the global config directory for OpenCode.
8151
- * Priority: explicitDir > env vars (via getOpencodeGlobalDir)
8152
- */
8153
- function getGlobalDir$3(explicitDir) {
8154
- if (explicitDir) return expandTilde(explicitDir);
8155
- return getOpencodeGlobalDir$1();
8156
- }
8157
- /**
8158
- * Get the config directory path relative to home for hook templating.
8159
- */
8160
- function getConfigDirFromHome$3(isGlobal) {
8161
- if (!isGlobal) return "'.opencode'";
8162
- return "'.config', 'opencode'";
8163
- }
8164
- /**
8165
- * Transform markdown content for OpenCode installation.
8166
- * Applies frontmatter conversion and path replacement.
8167
- */
8168
- function transformContent$2(content, pathPrefix) {
8169
- let result = replacePathReferences(content, pathPrefix, ".opencode");
8170
- result = result.replace(/~\/\.opencode\//g, pathPrefix);
8171
- result = convertClaudeToOpencodeFrontmatter(result);
8172
- return result;
8173
- }
8174
- /**
8175
- * OpenCode adapter configuration.
8176
- * OpenCode uses flat command structure (command/maxsim-*.md).
8177
- */
8178
- const opencodeAdapter = {
8179
- runtime: "opencode",
8180
- dirName: ".opencode",
8181
- getGlobalDir: getGlobalDir$3,
8182
- getConfigDirFromHome: getConfigDirFromHome$3,
8183
- transformContent: transformContent$2,
8184
- commandStructure: "flat"
8185
- };
8186
-
8187
- //#endregion
8188
- //#region src/adapters/gemini.ts
8189
- /**
8190
- * @maxsim/adapters — Gemini adapter
8191
- *
8192
- * Ports the Gemini-specific logic from bin/install.js:
8193
- * - getGlobalDir('gemini', ...) (lines 113-122)
8194
- * - getDirName('gemini') (line 47)
8195
- * - getConfigDirFromHome('gemini', isGlobal) (line 69)
8196
- * - convertClaudeToGeminiToml + convertClaudeToGeminiAgent + stripSubTags
8197
- */
8198
- /**
8199
- * Get the global config directory for Gemini.
8200
- * Priority: explicitDir > GEMINI_CONFIG_DIR env > ~/.gemini
8201
- */
8202
- function getGlobalDir$2(explicitDir) {
8203
- if (explicitDir) return expandTilde(explicitDir);
8204
- if (process.env.GEMINI_CONFIG_DIR) return expandTilde(process.env.GEMINI_CONFIG_DIR);
8205
- return node_path.join(node_os.homedir(), ".gemini");
8206
- }
8207
- /**
8208
- * Get the config directory path relative to home for hook templating.
8209
- */
8210
- function getConfigDirFromHome$2(_isGlobal) {
8211
- return "'.gemini'";
8212
- }
8213
- /**
8214
- * Transform markdown content for Gemini installation.
8215
- * Applies TOML conversion for commands, agent conversion for agents,
8216
- * stripSubTags, and path replacement.
8217
- */
8218
- function transformContent$1(content, pathPrefix) {
8219
- let result = replacePathReferences(content, pathPrefix, ".gemini");
8220
- result = stripSubTags(result);
8221
- result = convertClaudeToGeminiToml(result);
8222
- return result;
8223
- }
8224
- /**
8225
- * Gemini adapter configuration.
8226
- * Gemini uses nested command structure (commands/maxsim/*.toml).
8227
- */
8228
- const geminiAdapter = {
8229
- runtime: "gemini",
8230
- dirName: ".gemini",
8231
- getGlobalDir: getGlobalDir$2,
8232
- getConfigDirFromHome: getConfigDirFromHome$2,
8233
- transformContent: transformContent$1,
8234
- commandStructure: "nested"
8235
- };
8236
-
8237
- //#endregion
8238
- //#region src/adapters/codex.ts
8239
- /**
8240
- * @maxsim/adapters — Codex adapter
8241
- *
8242
- * Ports the Codex-specific logic from bin/install.js:
8243
- * - getGlobalDir('codex', ...) (lines 124-133)
8244
- * - getDirName('codex') (line 48)
8245
- * - getConfigDirFromHome('codex', isGlobal) (line 70)
8246
- * - convertClaudeCommandToCodexSkill + convertClaudeToCodexMarkdown
8247
- */
8248
- /**
8249
- * Get the global config directory for Codex.
8250
- * Priority: explicitDir > CODEX_HOME env > ~/.codex
8251
- */
8252
- function getGlobalDir$1(explicitDir) {
8253
- if (explicitDir) return expandTilde(explicitDir);
8254
- if (process.env.CODEX_HOME) return expandTilde(process.env.CODEX_HOME);
8255
- return node_path.join(node_os.homedir(), ".codex");
8256
- }
8257
- /**
8258
- * Get the config directory path relative to home for hook templating.
8259
- */
8260
- function getConfigDirFromHome$1(_isGlobal) {
8261
- return "'.codex'";
8262
- }
8263
- /**
8264
- * Transform markdown content for Codex installation.
8265
- * Applies Codex markdown conversion and path replacement.
8266
- */
8267
- function transformContent(content, pathPrefix) {
8268
- let result = replacePathReferences(content, pathPrefix, ".codex");
8269
- result = result.replace(/~\/\.codex\//g, pathPrefix);
8270
- result = convertClaudeCommandToCodexSkill(result);
8271
- return result;
8272
- }
8273
- /**
8274
- * Codex adapter configuration.
8275
- * Codex uses skill-based command structure (skills/maxsim-star/SKILL.md).
8276
- */
8277
- const codexAdapter = {
8278
- runtime: "codex",
8279
- dirName: ".codex",
8280
7603
  getGlobalDir: getGlobalDir$1,
8281
7604
  getConfigDirFromHome: getConfigDirFromHome$1,
8282
7605
  transformContent,
8283
- commandStructure: "skills"
7606
+ commandStructure: "nested"
8284
7607
  };
8285
7608
 
8286
7609
  //#endregion
8287
7610
  //#region src/install/shared.ts
8288
7611
  const pkg = JSON.parse(node_fs.readFileSync(node_path.resolve(__dirname, "..", "package.json"), "utf-8"));
8289
7612
  const templatesRoot = node_path.resolve(__dirname, "assets", "templates");
7613
+ const builtInSkills = [
7614
+ "tdd",
7615
+ "systematic-debugging",
7616
+ "verification-before-completion",
7617
+ "simplify",
7618
+ "code-review",
7619
+ "memory-management",
7620
+ "using-maxsim",
7621
+ "brainstorming",
7622
+ "roadmap-writing"
7623
+ ];
8290
7624
  /**
8291
- * Adapter registry keyed by runtime name
8292
- */
8293
- const adapterMap = {
8294
- claude: claudeAdapter,
8295
- opencode: opencodeAdapter,
8296
- gemini: geminiAdapter,
8297
- codex: codexAdapter
8298
- };
8299
- /**
8300
- * Get adapter for a runtime
8301
- */
8302
- function getAdapter(runtime) {
8303
- return adapterMap[runtime];
8304
- }
8305
- /**
8306
- * Get the global config directory for a runtime, using adapter
7625
+ * Get the global config directory, using the Claude adapter
8307
7626
  */
8308
- function getGlobalDir(runtime, explicitDir = null) {
8309
- return getAdapter(runtime).getGlobalDir(explicitDir);
7627
+ function getGlobalDir(explicitDir = null) {
7628
+ return claudeAdapter.getGlobalDir(explicitDir);
8310
7629
  }
8311
7630
  /**
8312
7631
  * Get the config directory path relative to home for hook templating
8313
7632
  */
8314
- function getConfigDirFromHome(runtime, isGlobal) {
8315
- return getAdapter(runtime).getConfigDirFromHome(isGlobal);
7633
+ function getConfigDirFromHome(isGlobal) {
7634
+ return claudeAdapter.getConfigDirFromHome(isGlobal);
8316
7635
  }
8317
7636
  /**
8318
- * Get the local directory name for a runtime
7637
+ * Get the local directory name
8319
7638
  */
8320
- function getDirName(runtime) {
8321
- return getAdapter(runtime).dirName;
7639
+ function getDirName() {
7640
+ return claudeAdapter.dirName;
8322
7641
  }
8323
7642
  /**
8324
7643
  * Recursively remove a directory, handling Windows read-only file attributes.
@@ -8334,16 +7653,10 @@ function copyDirRecursive(src, dest) {
8334
7653
  import_lib.default.copySync(src, dest, { dereference: true });
8335
7654
  }
8336
7655
  /**
8337
- * Get the global config directory for OpenCode (for JSONC permissions)
8338
- * OpenCode follows XDG Base Directory spec
8339
- */
8340
- function getOpencodeGlobalDir() {
8341
- return opencodeAdapter.getGlobalDir();
8342
- }
8343
- /**
8344
- * Verify a directory exists and contains files
7656
+ * Verify a directory exists and contains files.
7657
+ * If expectedFiles is provided, also checks that those specific files exist inside the directory.
8345
7658
  */
8346
- function verifyInstalled(dirPath, description) {
7659
+ function verifyInstalled(dirPath, description, expectedFiles) {
8347
7660
  if (!node_fs.existsSync(dirPath)) {
8348
7661
  console.error(` \u2717 Failed to install ${description}: directory not created`);
8349
7662
  return false;
@@ -8357,6 +7670,13 @@ function verifyInstalled(dirPath, description) {
8357
7670
  console.error(` \u2717 Failed to install ${description}: ${e.message}`);
8358
7671
  return false;
8359
7672
  }
7673
+ if (expectedFiles && expectedFiles.length > 0) {
7674
+ const missing = expectedFiles.filter((f) => !node_fs.existsSync(node_path.join(dirPath, f)));
7675
+ if (missing.length > 0) {
7676
+ console.error(` \u2717 Failed to install ${description}: missing files: ${missing.join(", ")}`);
7677
+ return false;
7678
+ }
7679
+ }
8360
7680
  return true;
8361
7681
  }
8362
7682
  /**
@@ -8369,102 +7689,69 @@ function verifyFileInstalled(filePath, description) {
8369
7689
  }
8370
7690
  return true;
8371
7691
  }
8372
-
8373
- //#endregion
8374
- //#region src/install/adapters.ts
8375
- const attributionCache = /* @__PURE__ */ new Map();
8376
7692
  /**
8377
- * Get commit attribution setting for a runtime
8378
- * @returns null = remove, undefined = keep default, string = custom
8379
- */
8380
- function getCommitAttribution(runtime, explicitConfigDir) {
8381
- if (attributionCache.has(runtime)) return attributionCache.get(runtime);
8382
- let result;
8383
- if (runtime === "opencode") result = readSettings(node_path.join(getGlobalDir("opencode", null), "opencode.json")).disable_ai_attribution === true ? null : void 0;
8384
- else if (runtime === "gemini") {
8385
- const attr = readSettings(node_path.join(getGlobalDir("gemini", explicitConfigDir), "settings.json")).attribution;
8386
- if (!attr || attr.commit === void 0) result = void 0;
8387
- else if (attr.commit === "") result = null;
8388
- else result = attr.commit;
8389
- } else if (runtime === "claude") {
8390
- const attr = readSettings(node_path.join(getGlobalDir("claude", explicitConfigDir), "settings.json")).attribution;
8391
- if (!attr || attr.commit === void 0) result = void 0;
8392
- else if (attr.commit === "") result = null;
8393
- else result = attr.commit;
8394
- } else result = void 0;
8395
- attributionCache.set(runtime, result);
8396
- return result;
8397
- }
8398
- /**
8399
- * Parse JSONC (JSON with Comments) by stripping comments and trailing commas.
7693
+ * Verify that all major install components are present. Uses the manifest
7694
+ * (if available) to check individual files; otherwise falls back to
7695
+ * directory-level checks.
7696
+ *
7697
+ * Returns an object with `complete` (boolean) and `missing` (list of
7698
+ * component names that are absent or incomplete).
8400
7699
  */
8401
- function parseJsonc(content) {
8402
- if (content.charCodeAt(0) === 65279) content = content.slice(1);
8403
- let result = "";
8404
- let inString = false;
8405
- let i = 0;
8406
- while (i < content.length) {
8407
- const char = content[i];
8408
- const next = content[i + 1];
8409
- if (inString) {
8410
- result += char;
8411
- if (char === "\\" && i + 1 < content.length) {
8412
- result += next;
8413
- i += 2;
8414
- continue;
8415
- }
8416
- if (char === "\"") inString = false;
8417
- i++;
8418
- } else if (char === "\"") {
8419
- inString = true;
8420
- result += char;
8421
- i++;
8422
- } else if (char === "/" && next === "/") while (i < content.length && content[i] !== "\n") i++;
8423
- else if (char === "/" && next === "*") {
8424
- i += 2;
8425
- while (i < content.length - 1 && !(content[i] === "*" && content[i + 1] === "/")) i++;
8426
- i += 2;
8427
- } else {
8428
- result += char;
8429
- i++;
7700
+ function verifyInstallComplete(configDir, _runtime, manifest = null) {
7701
+ const missing = [];
7702
+ if (manifest && manifest.files) {
7703
+ for (const relPath of Object.keys(manifest.files)) if (!node_fs.existsSync(node_path.join(configDir, relPath))) missing.push(relPath);
7704
+ return {
7705
+ complete: missing.length === 0,
7706
+ missing
7707
+ };
7708
+ }
7709
+ const components = [
7710
+ {
7711
+ dir: node_path.join(configDir, "maxsim"),
7712
+ label: "maxsim (workflows/templates)"
7713
+ },
7714
+ {
7715
+ dir: node_path.join(configDir, "agents"),
7716
+ label: "agents"
7717
+ },
7718
+ {
7719
+ dir: node_path.join(configDir, "commands", "maxsim"),
7720
+ label: "commands"
7721
+ },
7722
+ {
7723
+ dir: node_path.join(configDir, "hooks"),
7724
+ label: "hooks"
8430
7725
  }
7726
+ ];
7727
+ for (const { dir, label } of components) if (!node_fs.existsSync(dir)) missing.push(label);
7728
+ else try {
7729
+ if (node_fs.readdirSync(dir).length === 0) missing.push(label);
7730
+ } catch {
7731
+ missing.push(label);
8431
7732
  }
8432
- result = result.replace(/,(\s*[}\]])/g, "$1");
8433
- return JSON.parse(result);
7733
+ return {
7734
+ complete: missing.length === 0,
7735
+ missing
7736
+ };
8434
7737
  }
7738
+
7739
+ //#endregion
7740
+ //#region src/install/adapters.ts
7741
+ let attributionCached = false;
7742
+ let attributionValue;
8435
7743
  /**
8436
- * Configure OpenCode permissions to allow reading MAXSIM reference docs
7744
+ * Get commit attribution setting for Claude Code
7745
+ * @returns null = remove, undefined = keep default, string = custom
8437
7746
  */
8438
- function configureOpencodePermissions(isGlobal = true) {
8439
- const opencodeConfigDir = isGlobal ? getOpencodeGlobalDir() : node_path.join(process.cwd(), ".opencode");
8440
- const configPath = node_path.join(opencodeConfigDir, "opencode.json");
8441
- node_fs.mkdirSync(opencodeConfigDir, { recursive: true });
8442
- let config = {};
8443
- if (node_fs.existsSync(configPath)) try {
8444
- config = parseJsonc(node_fs.readFileSync(configPath, "utf8"));
8445
- } catch (e) {
8446
- console.log(` ${chalk.yellow("⚠")} Could not parse opencode.json - skipping permission config`);
8447
- console.log(` ${chalk.dim(`Reason: ${e.message}`)}`);
8448
- console.log(` ${chalk.dim("Your config was NOT modified. Fix the syntax manually if needed.")}`);
8449
- return;
8450
- }
8451
- if (!config.permission) config.permission = {};
8452
- const permission = config.permission;
8453
- const maxsimPath = opencodeConfigDir === node_path.join(node_os.homedir(), ".config", "opencode") ? "~/.config/opencode/maxsim/*" : `${opencodeConfigDir.replace(/\\/g, "/")}/maxsim/*`;
8454
- let modified = false;
8455
- if (!permission.read || typeof permission.read !== "object") permission.read = {};
8456
- if (permission.read[maxsimPath] !== "allow") {
8457
- permission.read[maxsimPath] = "allow";
8458
- modified = true;
8459
- }
8460
- if (!permission.external_directory || typeof permission.external_directory !== "object") permission.external_directory = {};
8461
- if (permission.external_directory[maxsimPath] !== "allow") {
8462
- permission.external_directory[maxsimPath] = "allow";
8463
- modified = true;
8464
- }
8465
- if (!modified) return;
8466
- node_fs.writeFileSync(configPath, JSON.stringify(config, null, 2) + "\n");
8467
- console.log(` ${chalk.green("✓")} Configured read permission for MAXSIM docs`);
7747
+ function getCommitAttribution(explicitConfigDir) {
7748
+ if (attributionCached) return attributionValue;
7749
+ const attr = readSettings(node_path.join(getGlobalDir(explicitConfigDir), "settings.json")).attribution;
7750
+ if (!attr || attr.commit === void 0) attributionValue = void 0;
7751
+ else if (attr.commit === "") attributionValue = null;
7752
+ else attributionValue = attr.commit;
7753
+ attributionCached = true;
7754
+ return attributionValue;
8468
7755
  }
8469
7756
 
8470
7757
  //#endregion
@@ -8663,11 +7950,9 @@ function cleanupOrphanedHooks(settings) {
8663
7950
  return settings;
8664
7951
  }
8665
7952
  /**
8666
- * Install hook files and configure settings.json for a runtime
7953
+ * Install hook files and configure settings.json
8667
7954
  */
8668
- function installHookFiles(targetDir, runtime, isGlobal, failures) {
8669
- getDirName(runtime);
8670
- if (runtime === "codex") return;
7955
+ function installHookFiles(targetDir, isGlobal, failures) {
8671
7956
  let hooksSrc = null;
8672
7957
  const bundledHooksDir = node_path.resolve(__dirname, "assets", "hooks");
8673
7958
  if (node_fs.existsSync(bundledHooksDir)) hooksSrc = bundledHooksDir;
@@ -8680,7 +7965,7 @@ function installHookFiles(targetDir, runtime, isGlobal, failures) {
8680
7965
  const hooksDest = node_path.join(targetDir, "hooks");
8681
7966
  node_fs.mkdirSync(hooksDest, { recursive: true });
8682
7967
  const hookEntries = node_fs.readdirSync(hooksSrc);
8683
- const configDirReplacement = getConfigDirFromHome(runtime, isGlobal);
7968
+ const configDirReplacement = getConfigDirFromHome(isGlobal);
8684
7969
  for (const entry of hookEntries) {
8685
7970
  const srcFile = node_path.join(hooksSrc, entry);
8686
7971
  if (node_fs.statSync(srcFile).isFile() && entry.endsWith(".cjs") && !entry.includes(".d.")) {
@@ -8701,33 +7986,30 @@ function installHookFiles(targetDir, runtime, isGlobal, failures) {
8701
7986
  /**
8702
7987
  * Configure hooks and statusline in settings.json
8703
7988
  */
8704
- function configureSettingsHooks(targetDir, runtime, isGlobal) {
8705
- const dirName = getDirName(runtime);
8706
- const isOpencode = runtime === "opencode";
7989
+ function configureSettingsHooks(targetDir, isGlobal) {
7990
+ const dirName = getDirName();
8707
7991
  const settingsPath = node_path.join(targetDir, "settings.json");
8708
7992
  const settings = cleanupOrphanedHooks(readSettings(settingsPath));
8709
7993
  const statuslineCommand = isGlobal ? buildHookCommand(targetDir, "maxsim-statusline.js") : "node " + dirName + "/hooks/maxsim-statusline.js";
8710
7994
  const updateCheckCommand = isGlobal ? buildHookCommand(targetDir, "maxsim-check-update.js") : "node " + dirName + "/hooks/maxsim-check-update.js";
8711
7995
  const contextMonitorCommand = isGlobal ? buildHookCommand(targetDir, "maxsim-context-monitor.js") : "node " + dirName + "/hooks/maxsim-context-monitor.js";
8712
- if (!isOpencode) {
8713
- if (!settings.hooks) settings.hooks = {};
8714
- const installHooks = settings.hooks;
8715
- if (!installHooks.SessionStart) installHooks.SessionStart = [];
8716
- if (!installHooks.SessionStart.some((entry) => entry.hooks && entry.hooks.some((h) => h.command && h.command.includes("maxsim-check-update")))) {
8717
- installHooks.SessionStart.push({ hooks: [{
8718
- type: "command",
8719
- command: updateCheckCommand
8720
- }] });
8721
- console.log(` ${chalk.green("✓")} Configured update check hook`);
8722
- }
8723
- if (!installHooks.PostToolUse) installHooks.PostToolUse = [];
8724
- if (!installHooks.PostToolUse.some((entry) => entry.hooks && entry.hooks.some((h) => h.command && h.command.includes("maxsim-context-monitor")))) {
8725
- installHooks.PostToolUse.push({ hooks: [{
8726
- type: "command",
8727
- command: contextMonitorCommand
8728
- }] });
8729
- console.log(` ${chalk.green("✓")} Configured context window monitor hook`);
8730
- }
7996
+ if (!settings.hooks) settings.hooks = {};
7997
+ const installHooks = settings.hooks;
7998
+ if (!installHooks.SessionStart) installHooks.SessionStart = [];
7999
+ if (!installHooks.SessionStart.some((entry) => entry.hooks && entry.hooks.some((h) => h.command && h.command.includes("maxsim-check-update")))) {
8000
+ installHooks.SessionStart.push({ hooks: [{
8001
+ type: "command",
8002
+ command: updateCheckCommand
8003
+ }] });
8004
+ console.log(` ${chalk.green("✓")} Configured update check hook`);
8005
+ }
8006
+ if (!installHooks.PostToolUse) installHooks.PostToolUse = [];
8007
+ if (!installHooks.PostToolUse.some((entry) => entry.hooks && entry.hooks.some((h) => h.command && h.command.includes("maxsim-context-monitor")))) {
8008
+ installHooks.PostToolUse.push({ hooks: [{
8009
+ type: "command",
8010
+ command: contextMonitorCommand
8011
+ }] });
8012
+ console.log(` ${chalk.green("✓")} Configured context window monitor hook`);
8731
8013
  }
8732
8014
  return {
8733
8015
  settingsPath,
@@ -8769,138 +8051,22 @@ async function handleStatusline(settings, isInteractive, forceStatusline) {
8769
8051
  /**
8770
8052
  * Apply statusline config, then print completion message
8771
8053
  */
8772
- function finishInstall(settingsPath, settings, statuslineCommand, shouldInstallStatusline, runtime = "claude", isGlobal = true) {
8773
- const isOpencode = runtime === "opencode";
8774
- const isCodex = runtime === "codex";
8775
- if (shouldInstallStatusline && !isOpencode && !isCodex) {
8054
+ function finishInstall(settingsPath, settings, statuslineCommand, shouldInstallStatusline, isGlobal = true) {
8055
+ if (shouldInstallStatusline) {
8776
8056
  settings.statusLine = {
8777
8057
  type: "command",
8778
8058
  command: statuslineCommand
8779
8059
  };
8780
8060
  console.log(` ${chalk.green("✓")} Configured statusline`);
8781
8061
  }
8782
- if (!isCodex && settingsPath && settings) writeSettings(settingsPath, settings);
8783
- if (isOpencode) configureOpencodePermissions(isGlobal);
8784
- let program = "Claude Code";
8785
- if (runtime === "opencode") program = "OpenCode";
8786
- if (runtime === "gemini") program = "Gemini";
8787
- if (runtime === "codex") program = "Codex";
8788
- let command = "/maxsim:help";
8789
- if (runtime === "opencode") command = "/maxsim-help";
8790
- if (runtime === "codex") command = "$maxsim-help";
8062
+ if (settingsPath && settings) writeSettings(settingsPath, settings);
8791
8063
  console.log(`
8792
- ${chalk.green("Done!")} Launch ${program} and run ${chalk.cyan(command)}.
8064
+ ${chalk.green("Done!")} Launch Claude Code and run ${chalk.cyan("/maxsim:help")}.
8793
8065
 
8794
8066
  ${chalk.cyan("Join the community:")} https://discord.gg/5JJgD5svVS
8795
8067
  `);
8796
8068
  }
8797
8069
 
8798
- //#endregion
8799
- //#region src/install/copy.ts
8800
- /**
8801
- * Copy commands to a flat structure for OpenCode
8802
- * OpenCode expects: command/maxsim-help.md (invoked as /maxsim-help)
8803
- * Source structure: commands/maxsim/help.md
8804
- */
8805
- function copyFlattenedCommands(srcDir, destDir, prefix, pathPrefix, runtime, explicitConfigDir) {
8806
- if (!node_fs.existsSync(srcDir)) return;
8807
- if (node_fs.existsSync(destDir)) {
8808
- for (const file of node_fs.readdirSync(destDir)) if (file.startsWith(`${prefix}-`) && file.endsWith(".md")) node_fs.unlinkSync(node_path.join(destDir, file));
8809
- } else node_fs.mkdirSync(destDir, { recursive: true });
8810
- const entries = node_fs.readdirSync(srcDir, { withFileTypes: true });
8811
- for (const entry of entries) {
8812
- const srcPath = node_path.join(srcDir, entry.name);
8813
- if (entry.isDirectory()) copyFlattenedCommands(srcPath, destDir, `${prefix}-${entry.name}`, pathPrefix, runtime, explicitConfigDir);
8814
- else if (entry.name.endsWith(".md")) {
8815
- const destName = `${prefix}-${entry.name.replace(".md", "")}.md`;
8816
- const destPath = node_path.join(destDir, destName);
8817
- let content = node_fs.readFileSync(srcPath, "utf8");
8818
- const globalClaudeRegex = /~\/\.claude\//g;
8819
- const localClaudeRegex = /\.\/\.claude\//g;
8820
- const opencodeDirRegex = /~\/\.opencode\//g;
8821
- content = content.replace(globalClaudeRegex, pathPrefix);
8822
- content = content.replace(localClaudeRegex, `./${getDirName(runtime)}/`);
8823
- content = content.replace(opencodeDirRegex, pathPrefix);
8824
- content = processAttribution(content, getCommitAttribution(runtime, explicitConfigDir));
8825
- content = convertClaudeToOpencodeFrontmatter(content);
8826
- node_fs.writeFileSync(destPath, content);
8827
- }
8828
- }
8829
- }
8830
- function listCodexSkillNames(skillsDir, prefix = "maxsim-") {
8831
- if (!node_fs.existsSync(skillsDir)) return [];
8832
- return node_fs.readdirSync(skillsDir, { withFileTypes: true }).filter((entry) => entry.isDirectory() && entry.name.startsWith(prefix)).filter((entry) => node_fs.existsSync(node_path.join(skillsDir, entry.name, "SKILL.md"))).map((entry) => entry.name).sort();
8833
- }
8834
- function copyCommandsAsCodexSkills(srcDir, skillsDir, prefix, pathPrefix, runtime, explicitConfigDir) {
8835
- if (!node_fs.existsSync(srcDir)) return;
8836
- node_fs.mkdirSync(skillsDir, { recursive: true });
8837
- const existing = node_fs.readdirSync(skillsDir, { withFileTypes: true });
8838
- for (const entry of existing) if (entry.isDirectory() && entry.name.startsWith(`${prefix}-`)) node_fs.rmSync(node_path.join(skillsDir, entry.name), { recursive: true });
8839
- function recurse(currentSrcDir, currentPrefix) {
8840
- const entries = node_fs.readdirSync(currentSrcDir, { withFileTypes: true });
8841
- for (const entry of entries) {
8842
- const srcPath = node_path.join(currentSrcDir, entry.name);
8843
- if (entry.isDirectory()) {
8844
- recurse(srcPath, `${currentPrefix}-${entry.name}`);
8845
- continue;
8846
- }
8847
- if (!entry.name.endsWith(".md")) continue;
8848
- const skillName = `${currentPrefix}-${entry.name.replace(".md", "")}`;
8849
- const skillDir = node_path.join(skillsDir, skillName);
8850
- node_fs.mkdirSync(skillDir, { recursive: true });
8851
- let content = node_fs.readFileSync(srcPath, "utf8");
8852
- const globalClaudeRegex = /~\/\.claude\//g;
8853
- const localClaudeRegex = /\.\/\.claude\//g;
8854
- const codexDirRegex = /~\/\.codex\//g;
8855
- content = content.replace(globalClaudeRegex, pathPrefix);
8856
- content = content.replace(localClaudeRegex, `./${getDirName(runtime)}/`);
8857
- content = content.replace(codexDirRegex, pathPrefix);
8858
- content = processAttribution(content, getCommitAttribution(runtime, explicitConfigDir));
8859
- content = convertClaudeCommandToCodexSkill(content, skillName);
8860
- node_fs.writeFileSync(node_path.join(skillDir, "SKILL.md"), content);
8861
- }
8862
- }
8863
- recurse(srcDir, prefix);
8864
- }
8865
- /**
8866
- * Recursively copy directory, replacing paths in .md files
8867
- * Deletes existing destDir first to remove orphaned files from previous versions
8868
- */
8869
- function copyWithPathReplacement(srcDir, destDir, pathPrefix, runtime, explicitConfigDir, isCommand = false) {
8870
- const isOpencode = runtime === "opencode";
8871
- const isCodex = runtime === "codex";
8872
- const dirName = getDirName(runtime);
8873
- if (node_fs.existsSync(destDir)) node_fs.rmSync(destDir, { recursive: true });
8874
- node_fs.mkdirSync(destDir, { recursive: true });
8875
- const entries = node_fs.readdirSync(srcDir, { withFileTypes: true });
8876
- for (const entry of entries) {
8877
- const srcPath = node_path.join(srcDir, entry.name);
8878
- const destPath = node_path.join(destDir, entry.name);
8879
- if (entry.isDirectory()) copyWithPathReplacement(srcPath, destPath, pathPrefix, runtime, explicitConfigDir, isCommand);
8880
- else if (entry.name.endsWith(".md")) {
8881
- let content = node_fs.readFileSync(srcPath, "utf8");
8882
- const globalClaudeRegex = /~\/\.claude\//g;
8883
- const localClaudeRegex = /\.\/\.claude\//g;
8884
- content = content.replace(globalClaudeRegex, pathPrefix);
8885
- content = content.replace(localClaudeRegex, `./${dirName}/`);
8886
- content = processAttribution(content, getCommitAttribution(runtime, explicitConfigDir));
8887
- if (isOpencode) {
8888
- content = convertClaudeToOpencodeFrontmatter(content);
8889
- node_fs.writeFileSync(destPath, content);
8890
- } else if (runtime === "gemini") if (isCommand) {
8891
- content = stripSubTags(content);
8892
- const tomlContent = convertClaudeToGeminiToml(content);
8893
- const tomlPath = destPath.replace(/\.md$/, ".toml");
8894
- node_fs.writeFileSync(tomlPath, tomlContent);
8895
- } else node_fs.writeFileSync(destPath, content);
8896
- else if (isCodex) {
8897
- content = convertClaudeToCodexMarkdown(content);
8898
- node_fs.writeFileSync(destPath, content);
8899
- } else node_fs.writeFileSync(destPath, content);
8900
- } else node_fs.copyFileSync(srcPath, destPath);
8901
- }
8902
- }
8903
-
8904
8070
  //#endregion
8905
8071
  //#region src/install/manifest.ts
8906
8072
  const MANIFEST_NAME = "maxsim-file-manifest.json";
@@ -8930,13 +8096,9 @@ function generateManifest(dir, baseDir) {
8930
8096
  /**
8931
8097
  * Write file manifest after installation for future modification detection
8932
8098
  */
8933
- function writeManifest(configDir, runtime = "claude") {
8934
- const isOpencode = runtime === "opencode";
8935
- const isCodex = runtime === "codex";
8099
+ function writeManifest(configDir) {
8936
8100
  const maxsimDir = node_path.join(configDir, "maxsim");
8937
8101
  const commandsDir = node_path.join(configDir, "commands", "maxsim");
8938
- const opencodeCommandDir = node_path.join(configDir, "command");
8939
- const codexSkillsDir = node_path.join(configDir, "skills");
8940
8102
  const agentsDir = node_path.join(configDir, "agents");
8941
8103
  const manifest = {
8942
8104
  version: pkg.version,
@@ -8945,28 +8107,33 @@ function writeManifest(configDir, runtime = "claude") {
8945
8107
  };
8946
8108
  const maxsimHashes = generateManifest(maxsimDir);
8947
8109
  for (const [rel, hash] of Object.entries(maxsimHashes)) manifest.files["maxsim/" + rel] = hash;
8948
- if (!isOpencode && !isCodex && node_fs.existsSync(commandsDir)) {
8110
+ if (node_fs.existsSync(commandsDir)) {
8949
8111
  const cmdHashes = generateManifest(commandsDir);
8950
8112
  for (const [rel, hash] of Object.entries(cmdHashes)) manifest.files["commands/maxsim/" + rel] = hash;
8951
8113
  }
8952
- if (isOpencode && node_fs.existsSync(opencodeCommandDir)) {
8953
- for (const file of node_fs.readdirSync(opencodeCommandDir)) if (file.startsWith("maxsim-") && file.endsWith(".md")) manifest.files["command/" + file] = fileHash(node_path.join(opencodeCommandDir, file));
8954
- }
8955
- if (isCodex && node_fs.existsSync(codexSkillsDir)) for (const skillName of listCodexSkillNames(codexSkillsDir)) {
8956
- const skillHashes = generateManifest(node_path.join(codexSkillsDir, skillName));
8957
- for (const [rel, hash] of Object.entries(skillHashes)) manifest.files[`skills/${skillName}/${rel}`] = hash;
8958
- }
8959
8114
  if (node_fs.existsSync(agentsDir)) {
8960
8115
  for (const file of node_fs.readdirSync(agentsDir)) if (file.startsWith("maxsim-") && file.endsWith(".md")) manifest.files["agents/" + file] = fileHash(node_path.join(agentsDir, file));
8961
8116
  }
8962
- const skillsManifestDir = node_path.join(agentsDir, "skills");
8117
+ const skillsManifestDir = node_path.join(configDir, "skills");
8963
8118
  if (node_fs.existsSync(skillsManifestDir)) {
8964
8119
  const skillHashes = generateManifest(skillsManifestDir);
8965
- for (const [rel, hash] of Object.entries(skillHashes)) manifest.files["agents/skills/" + rel] = hash;
8120
+ for (const [rel, hash] of Object.entries(skillHashes)) manifest.files["skills/" + rel] = hash;
8966
8121
  }
8967
8122
  node_fs.writeFileSync(node_path.join(configDir, MANIFEST_NAME), JSON.stringify(manifest, null, 2));
8968
8123
  return manifest;
8969
8124
  }
8125
+ /**
8126
+ * Read an existing manifest from the config directory, or return null if none exists / is invalid
8127
+ */
8128
+ function readManifest(configDir) {
8129
+ const manifestPath = node_path.join(configDir, MANIFEST_NAME);
8130
+ if (!node_fs.existsSync(manifestPath)) return null;
8131
+ try {
8132
+ return JSON.parse(node_fs.readFileSync(manifestPath, "utf8"));
8133
+ } catch {
8134
+ return null;
8135
+ }
8136
+ }
8970
8137
 
8971
8138
  //#endregion
8972
8139
  //#region src/install/patches.ts
@@ -8975,14 +8142,8 @@ const PATCHES_DIR_NAME = "maxsim-local-patches";
8975
8142
  * Detect user-modified MAXSIM files by comparing against install manifest.
8976
8143
  */
8977
8144
  function saveLocalPatches(configDir) {
8978
- const manifestPath = node_path.join(configDir, MANIFEST_NAME);
8979
- if (!node_fs.existsSync(manifestPath)) return [];
8980
- let manifest;
8981
- try {
8982
- manifest = JSON.parse(node_fs.readFileSync(manifestPath, "utf8"));
8983
- } catch {
8984
- return [];
8985
- }
8145
+ const manifest = readManifest(configDir);
8146
+ if (!manifest) return [];
8986
8147
  const patchesDir = node_path.join(configDir, PATCHES_DIR_NAME);
8987
8148
  const modified = [];
8988
8149
  for (const [relPath, originalHash] of Object.entries(manifest.files || {})) {
@@ -9010,7 +8171,7 @@ function saveLocalPatches(configDir) {
9010
8171
  /**
9011
8172
  * After install, report backed-up patches for user to reapply.
9012
8173
  */
9013
- function reportLocalPatches(configDir, runtime = "claude") {
8174
+ function reportLocalPatches(configDir) {
9014
8175
  const patchesDir = node_path.join(configDir, PATCHES_DIR_NAME);
9015
8176
  const metaPath = node_path.join(patchesDir, "backup-meta.json");
9016
8177
  if (!node_fs.existsSync(metaPath)) return [];
@@ -9021,71 +8182,86 @@ function reportLocalPatches(configDir, runtime = "claude") {
9021
8182
  return [];
9022
8183
  }
9023
8184
  if (meta.files && meta.files.length > 0) {
9024
- const reapplyCommand = runtime === "opencode" ? "/maxsim-reapply-patches" : runtime === "codex" ? "$maxsim-reapply-patches" : "/maxsim:reapply-patches";
9025
8185
  console.log("");
9026
8186
  console.log(" " + chalk.yellow("Local patches detected") + " (from v" + meta.from_version + "):");
9027
8187
  for (const f of meta.files) console.log(" " + chalk.cyan(f));
9028
8188
  console.log("");
9029
8189
  console.log(" Your modifications are saved in " + chalk.cyan(PATCHES_DIR_NAME + "/"));
9030
- console.log(" Run " + chalk.cyan(reapplyCommand) + " to merge them into the new version.");
8190
+ console.log(" Run " + chalk.cyan("/maxsim:reapply-patches") + " to merge them into the new version.");
9031
8191
  console.log(" Or manually compare and merge the files.");
9032
8192
  console.log("");
9033
8193
  }
9034
8194
  return meta.files || [];
9035
8195
  }
9036
8196
 
8197
+ //#endregion
8198
+ //#region src/install/copy.ts
8199
+ /**
8200
+ * Recursively copy directory, replacing paths in .md files
8201
+ * Deletes existing destDir first to remove orphaned files from previous versions
8202
+ */
8203
+ function copyWithPathReplacement(srcDir, destDir, pathPrefix, explicitConfigDir, isCommand = false) {
8204
+ if (node_fs.existsSync(destDir)) node_fs.rmSync(destDir, { recursive: true });
8205
+ node_fs.mkdirSync(destDir, { recursive: true });
8206
+ const entries = node_fs.readdirSync(srcDir, { withFileTypes: true });
8207
+ for (const entry of entries) {
8208
+ const srcPath = node_path.join(srcDir, entry.name);
8209
+ const destPath = node_path.join(destDir, entry.name);
8210
+ if (entry.isDirectory()) copyWithPathReplacement(srcPath, destPath, pathPrefix, explicitConfigDir, isCommand);
8211
+ else if (entry.name.endsWith(".md")) {
8212
+ let content = node_fs.readFileSync(srcPath, "utf8");
8213
+ const globalClaudeRegex = /~\/\.claude\//g;
8214
+ const localClaudeRegex = /\.\/\.claude\//g;
8215
+ content = content.replace(globalClaudeRegex, pathPrefix);
8216
+ content = content.replace(localClaudeRegex, "./.claude/");
8217
+ content = processAttribution(content, getCommitAttribution(explicitConfigDir));
8218
+ node_fs.writeFileSync(destPath, content);
8219
+ } else node_fs.copyFileSync(srcPath, destPath);
8220
+ }
8221
+ }
8222
+
9037
8223
  //#endregion
9038
8224
  //#region src/install/uninstall.ts
9039
8225
  /**
9040
- * Uninstall MAXSIM from the specified directory for a specific runtime
8226
+ * Uninstall MAXSIM from the specified directory
9041
8227
  */
9042
- function uninstall(isGlobal, runtime = "claude", explicitConfigDir = null) {
9043
- const isOpencode = runtime === "opencode";
9044
- const isCodex = runtime === "codex";
9045
- const dirName = getDirName(runtime);
9046
- const targetDir = isGlobal ? getGlobalDir(runtime, explicitConfigDir) : node_path.join(process.cwd(), dirName);
8228
+ function uninstall(isGlobal, explicitConfigDir = null) {
8229
+ const dirName = getDirName();
8230
+ const targetDir = isGlobal ? getGlobalDir(explicitConfigDir) : node_path.join(process.cwd(), dirName);
9047
8231
  const locationLabel = isGlobal ? targetDir.replace(node_os.homedir(), "~") : targetDir.replace(process.cwd(), ".");
9048
- let runtimeLabel = "Claude Code";
9049
- if (runtime === "opencode") runtimeLabel = "OpenCode";
9050
- if (runtime === "gemini") runtimeLabel = "Gemini";
9051
- if (runtime === "codex") runtimeLabel = "Codex";
9052
- console.log(` Uninstalling MAXSIM from ${chalk.cyan(runtimeLabel)} at ${chalk.cyan(locationLabel)}\n`);
8232
+ console.log(` Uninstalling MAXSIM from ${chalk.cyan("Claude Code")} at ${chalk.cyan(locationLabel)}\n`);
9053
8233
  if (!node_fs.existsSync(targetDir)) {
9054
8234
  console.log(` ${chalk.yellow("⚠")} Directory does not exist: ${locationLabel}`);
9055
8235
  console.log(` Nothing to uninstall.\n`);
9056
8236
  return;
9057
8237
  }
9058
8238
  let removedCount = 0;
9059
- if (isOpencode) {
9060
- const commandDir = node_path.join(targetDir, "command");
9061
- if (node_fs.existsSync(commandDir)) {
9062
- const files = node_fs.readdirSync(commandDir);
9063
- for (const file of files) if (file.startsWith("maxsim-") && file.endsWith(".md")) {
9064
- node_fs.unlinkSync(node_path.join(commandDir, file));
9065
- removedCount++;
9066
- }
9067
- console.log(` ${chalk.green("✓")} Removed MAXSIM commands from command/`);
9068
- }
9069
- } else if (isCodex) {
8239
+ const maxsimCommandsDir = node_path.join(targetDir, "commands", "maxsim");
8240
+ if (node_fs.existsSync(maxsimCommandsDir)) {
8241
+ node_fs.rmSync(maxsimCommandsDir, { recursive: true });
8242
+ removedCount++;
8243
+ console.log(` ${chalk.green("")} Removed commands/maxsim/`);
8244
+ }
8245
+ {
9070
8246
  const skillsDir = node_path.join(targetDir, "skills");
9071
8247
  if (node_fs.existsSync(skillsDir)) {
9072
8248
  let skillCount = 0;
9073
- const entries = node_fs.readdirSync(skillsDir, { withFileTypes: true });
9074
- for (const entry of entries) if (entry.isDirectory() && entry.name.startsWith("maxsim-")) {
9075
- node_fs.rmSync(node_path.join(skillsDir, entry.name), { recursive: true });
9076
- skillCount++;
8249
+ for (const skill of builtInSkills) {
8250
+ const skillDir = node_path.join(skillsDir, skill);
8251
+ if (node_fs.existsSync(skillDir)) {
8252
+ node_fs.rmSync(skillDir, { recursive: true });
8253
+ skillCount++;
8254
+ }
9077
8255
  }
9078
8256
  if (skillCount > 0) {
9079
8257
  removedCount++;
9080
- console.log(` ${chalk.green("✓")} Removed ${skillCount} Codex skills`);
8258
+ console.log(` ${chalk.green("✓")} Removed ${skillCount} MAXSIM skills`);
9081
8259
  }
9082
8260
  }
9083
- } else {
9084
- const maxsimCommandsDir = node_path.join(targetDir, "commands", "maxsim");
9085
- if (node_fs.existsSync(maxsimCommandsDir)) {
9086
- node_fs.rmSync(maxsimCommandsDir, { recursive: true });
9087
- removedCount++;
9088
- console.log(` ${chalk.green("✓")} Removed commands/maxsim/`);
8261
+ const legacySkillsDir = node_path.join(targetDir, "agents", "skills");
8262
+ if (node_fs.existsSync(legacySkillsDir)) {
8263
+ node_fs.rmSync(legacySkillsDir, { recursive: true });
8264
+ console.log(` ${chalk.green("✓")} Removed legacy agents/skills/ directory`);
9089
8265
  }
9090
8266
  }
9091
8267
  const maxsimDir = node_path.join(targetDir, "maxsim");
@@ -9177,34 +8353,9 @@ function uninstall(isGlobal, runtime = "claude", explicitConfigDir = null) {
9177
8353
  removedCount++;
9178
8354
  }
9179
8355
  }
9180
- if (isOpencode) {
9181
- const opencodeConfigDir = isGlobal ? getOpencodeGlobalDir() : node_path.join(process.cwd(), ".opencode");
9182
- const configPath = node_path.join(opencodeConfigDir, "opencode.json");
9183
- if (node_fs.existsSync(configPath)) try {
9184
- const config = JSON.parse(node_fs.readFileSync(configPath, "utf8"));
9185
- let modified = false;
9186
- const permission = config.permission;
9187
- if (permission) {
9188
- for (const permType of ["read", "external_directory"]) if (permission[permType]) {
9189
- const keys = Object.keys(permission[permType]);
9190
- for (const key of keys) if (key.includes("maxsim")) {
9191
- delete permission[permType][key];
9192
- modified = true;
9193
- }
9194
- if (Object.keys(permission[permType]).length === 0) delete permission[permType];
9195
- }
9196
- if (Object.keys(permission).length === 0) delete config.permission;
9197
- }
9198
- if (modified) {
9199
- node_fs.writeFileSync(configPath, JSON.stringify(config, null, 2) + "\n");
9200
- removedCount++;
9201
- console.log(` ${chalk.green("✓")} Removed MAXSIM permissions from opencode.json`);
9202
- }
9203
- } catch {}
9204
- }
9205
8356
  if (removedCount === 0) console.log(` ${chalk.yellow("⚠")} No MAXSIM files found to remove.`);
9206
8357
  console.log(`
9207
- ${chalk.green("Done!")} MAXSIM has been uninstalled from ${runtimeLabel}.
8358
+ ${chalk.green("Done!")} MAXSIM has been uninstalled from Claude Code.
9208
8359
  Your other files and settings have been preserved.
9209
8360
  `);
9210
8361
  }
@@ -9215,12 +8366,7 @@ const argv = (0, import_minimist.default)(process.argv.slice(2), {
9215
8366
  boolean: [
9216
8367
  "global",
9217
8368
  "local",
9218
- "opencode",
9219
8369
  "claude",
9220
- "gemini",
9221
- "codex",
9222
- "both",
9223
- "all",
9224
8370
  "uninstall",
9225
8371
  "help",
9226
8372
  "version",
@@ -9238,93 +8384,62 @@ const argv = (0, import_minimist.default)(process.argv.slice(2), {
9238
8384
  });
9239
8385
  const hasGlobal = !!argv["global"];
9240
8386
  const hasLocal = !!argv["local"];
9241
- const hasOpencode = !!argv["opencode"];
9242
- const hasClaude = !!argv["claude"];
9243
- const hasGemini = !!argv["gemini"];
9244
- const hasCodex = !!argv["codex"];
9245
- const hasBoth = !!argv["both"];
9246
- const hasAll = !!argv["all"];
9247
8387
  const hasUninstall = !!argv["uninstall"];
9248
- let selectedRuntimes = [];
9249
- if (hasAll) selectedRuntimes = [
9250
- "claude",
9251
- "opencode",
9252
- "gemini",
9253
- "codex"
9254
- ];
9255
- else if (hasBoth) selectedRuntimes = ["claude", "opencode"];
9256
- else {
9257
- if (hasOpencode) selectedRuntimes.push("opencode");
9258
- if (hasClaude) selectedRuntimes.push("claude");
9259
- if (hasGemini) selectedRuntimes.push("gemini");
9260
- if (hasCodex) selectedRuntimes.push("codex");
9261
- }
9262
- const banner = "\n" + chalk.cyan(figlet.default.textSync("MAXSIM", { font: "ANSI Shadow" }).split("\n").map((line) => " " + line).join("\n")) + "\n\n MAXSIM " + chalk.dim("v" + pkg.version) + "\n A meta-prompting, context engineering and spec-driven\n development system for Claude Code, OpenCode, Gemini, and Codex.\n";
8388
+ const banner = "\n" + chalk.cyan(figlet.default.textSync("MAXSIM", { font: "ANSI Shadow" }).split("\n").map((line) => " " + line).join("\n")) + "\n\n MAXSIM " + chalk.dim("v" + pkg.version) + "\n A meta-prompting, context engineering and spec-driven\n development system for Claude Code.\n";
9263
8389
  const explicitConfigDir = argv["config-dir"] || null;
9264
8390
  const hasHelp = !!argv["help"];
9265
8391
  const hasVersion = !!argv["version"];
9266
8392
  const forceStatusline = !!argv["force-statusline"];
8393
+ for (const flag of [
8394
+ "opencode",
8395
+ "gemini",
8396
+ "codex",
8397
+ "both",
8398
+ "all"
8399
+ ]) if (argv[flag]) {
8400
+ console.error(`Error: The --${flag} flag is no longer supported. MAXSIM v2.0 is Claude Code only.`);
8401
+ process.exit(1);
8402
+ }
9267
8403
  if (hasVersion) {
9268
8404
  console.log(pkg.version);
9269
8405
  process.exit(0);
9270
8406
  }
9271
8407
  console.log(banner);
9272
8408
  if (hasHelp) {
9273
- console.log(` ${chalk.yellow("Usage:")} npx maxsimcli [options]\n\n ${chalk.yellow("Options:")}\n ${chalk.cyan("-g, --global")} Install globally (to config directory)\n ${chalk.cyan("-l, --local")} Install locally (to current directory)\n ${chalk.cyan("--claude")} Install for Claude Code only\n ${chalk.cyan("--opencode")} Install for OpenCode only\n ${chalk.cyan("--gemini")} Install for Gemini only\n ${chalk.cyan("--codex")} Install for Codex only\n ${chalk.cyan("--all")} Install for all runtimes\n ${chalk.cyan("-u, --uninstall")} Uninstall MAXSIM (remove all MAXSIM files)\n ${chalk.cyan("-c, --config-dir <path>")} Specify custom config directory\n ${chalk.cyan("-h, --help")} Show this help message\n ${chalk.cyan("--force-statusline")} Replace existing statusline config\n\n ${chalk.yellow("Examples:")}\n ${chalk.dim("# Interactive install (prompts for runtime and location)")}\n npx maxsimcli\n\n ${chalk.dim("# Install for Claude Code globally")}\n npx maxsimcli --claude --global\n\n ${chalk.dim("# Install for Gemini globally")}\n npx maxsimcli --gemini --global\n\n ${chalk.dim("# Install for Codex globally")}\n npx maxsimcli --codex --global\n\n ${chalk.dim("# Install for all runtimes globally")}\n npx maxsimcli --all --global\n\n ${chalk.dim("# Install to custom config directory")}\n npx maxsimcli --codex --global --config-dir ~/.codex-work\n\n ${chalk.dim("# Install to current project only")}\n npx maxsimcli --claude --local\n\n ${chalk.dim("# Uninstall MAXSIM from Codex globally")}\n npx maxsimcli --codex --global --uninstall\n\n ${chalk.yellow("Notes:")}\n The --config-dir option is useful when you have multiple configurations.\n It takes priority over CLAUDE_CONFIG_DIR / GEMINI_CONFIG_DIR / CODEX_HOME environment variables.\n`);
8409
+ console.log(` ${chalk.yellow("Usage:")} npx maxsimcli [options]\n\n ${chalk.yellow("Options:")}\n ${chalk.cyan("-g, --global")} Install globally (to config directory)\n ${chalk.cyan("-l, --local")} Install locally (to current directory)\n ${chalk.cyan("-u, --uninstall")} Uninstall MAXSIM (remove all MAXSIM files)\n ${chalk.cyan("-c, --config-dir <path>")} Specify custom config directory\n ${chalk.cyan("-h, --help")} Show this help message\n ${chalk.cyan("--force-statusline")} Replace existing statusline config\n\n ${chalk.yellow("Examples:")}\n ${chalk.dim("# Interactive install (prompts for location)")}\n npx maxsimcli\n\n ${chalk.dim("# Install globally")}\n npx maxsimcli --global\n\n ${chalk.dim("# Install to current project only")}\n npx maxsimcli --local\n\n ${chalk.dim("# Install to custom config directory")}\n npx maxsimcli --global --config-dir ~/.claude-work\n\n ${chalk.dim("# Uninstall MAXSIM globally")}\n npx maxsimcli --global --uninstall\n\n ${chalk.yellow("Notes:")}\n The --config-dir option is useful when you have multiple configurations.\n It takes priority over the CLAUDE_CONFIG_DIR environment variable.\n`);
9274
8410
  process.exit(0);
9275
8411
  }
9276
- async function install(isGlobal, runtime = "claude") {
9277
- const isOpencode = runtime === "opencode";
9278
- const isGemini = runtime === "gemini";
9279
- const isCodex = runtime === "codex";
9280
- const dirName = getDirName(runtime);
8412
+ async function install(isGlobal) {
8413
+ const runtime = "claude";
8414
+ const dirName = getDirName();
9281
8415
  const src = templatesRoot;
9282
- const targetDir = isGlobal ? getGlobalDir(runtime, explicitConfigDir) : node_path.join(process.cwd(), dirName);
8416
+ const targetDir = isGlobal ? getGlobalDir(explicitConfigDir) : node_path.join(process.cwd(), dirName);
9283
8417
  const locationLabel = isGlobal ? targetDir.replace(node_os.homedir(), "~") : targetDir.replace(process.cwd(), ".");
9284
8418
  const pathPrefix = isGlobal ? `${targetDir.replace(/\\/g, "/")}/` : `./${dirName}/`;
9285
- let runtimeLabel = "Claude Code";
9286
- if (isOpencode) runtimeLabel = "OpenCode";
9287
- if (isGemini) runtimeLabel = "Gemini";
9288
- if (isCodex) runtimeLabel = "Codex";
9289
- console.log(` Installing for ${chalk.cyan(runtimeLabel)} to ${chalk.cyan(locationLabel)}\n`);
8419
+ console.log(` Installing for ${chalk.cyan("Claude Code")} to ${chalk.cyan(locationLabel)}\n`);
9290
8420
  const failures = [];
8421
+ const existingManifest = readManifest(targetDir);
8422
+ const isAlreadyCurrent = existingManifest !== null && existingManifest.version === pkg.version;
8423
+ if (existingManifest !== null) {
8424
+ const { complete, missing } = verifyInstallComplete(targetDir, runtime, existingManifest);
8425
+ if (!complete) console.log(` ${chalk.yellow("!")} Previous install (v${existingManifest.version}) is incomplete — ${missing.length} missing file(s). Re-installing.`);
8426
+ else if (isAlreadyCurrent) console.log(` ${chalk.dim(`Version ${pkg.version} already installed — upgrading in place`)}`);
8427
+ }
9291
8428
  saveLocalPatches(targetDir);
9292
8429
  cleanupOrphanedFiles(targetDir);
9293
8430
  let spinner = ora({
9294
8431
  text: "Installing commands...",
9295
8432
  color: "cyan"
9296
8433
  }).start();
9297
- if (isOpencode) {
9298
- const commandDir = node_path.join(targetDir, "command");
9299
- node_fs.mkdirSync(commandDir, { recursive: true });
9300
- copyFlattenedCommands(node_path.join(src, "commands", "maxsim"), commandDir, "maxsim", pathPrefix, runtime, explicitConfigDir);
9301
- if (verifyInstalled(commandDir, "command/maxsim-*")) {
9302
- const count = node_fs.readdirSync(commandDir).filter((f) => f.startsWith("maxsim-")).length;
9303
- spinner.succeed(chalk.green("✓") + ` Installed ${count} commands to command/`);
9304
- } else {
9305
- spinner.fail("Failed to install commands");
9306
- failures.push("command/maxsim-*");
9307
- }
9308
- } else if (isCodex) {
9309
- const skillsDir = node_path.join(targetDir, "skills");
9310
- copyCommandsAsCodexSkills(node_path.join(src, "commands", "maxsim"), skillsDir, "maxsim", pathPrefix, runtime, explicitConfigDir);
9311
- const installedSkillNames = listCodexSkillNames(skillsDir);
9312
- if (installedSkillNames.length > 0) spinner.succeed(chalk.green("✓") + ` Installed ${installedSkillNames.length} skills to skills/`);
9313
- else {
9314
- spinner.fail("Failed to install skills");
9315
- failures.push("skills/maxsim-*");
9316
- }
9317
- } else {
9318
- const commandsDir = node_path.join(targetDir, "commands");
9319
- node_fs.mkdirSync(commandsDir, { recursive: true });
9320
- const maxsimSrc = node_path.join(src, "commands", "maxsim");
9321
- const maxsimDest = node_path.join(commandsDir, "maxsim");
9322
- copyWithPathReplacement(maxsimSrc, maxsimDest, pathPrefix, runtime, explicitConfigDir, true);
9323
- if (verifyInstalled(maxsimDest, "commands/maxsim")) spinner.succeed(chalk.green("✓") + " Installed commands/maxsim");
9324
- else {
9325
- spinner.fail("Failed to install commands/maxsim");
9326
- failures.push("commands/maxsim");
9327
- }
8434
+ const commandsDir = node_path.join(targetDir, "commands");
8435
+ node_fs.mkdirSync(commandsDir, { recursive: true });
8436
+ const maxsimSrc = node_path.join(src, "commands", "maxsim");
8437
+ const maxsimDest = node_path.join(commandsDir, "maxsim");
8438
+ copyWithPathReplacement(maxsimSrc, maxsimDest, pathPrefix, explicitConfigDir, true);
8439
+ if (verifyInstalled(maxsimDest, "commands/maxsim")) spinner.succeed(chalk.green("✓") + " Installed commands/maxsim");
8440
+ else {
8441
+ spinner.fail("Failed to install commands/maxsim");
8442
+ failures.push("commands/maxsim");
9328
8443
  }
9329
8444
  spinner = ora({
9330
8445
  text: "Installing workflows and templates...",
@@ -9340,7 +8455,7 @@ async function install(isGlobal, runtime = "claude") {
9340
8455
  node_fs.mkdirSync(skillDest, { recursive: true });
9341
8456
  for (const subdir of maxsimSubdirs) {
9342
8457
  const subdirSrc = node_path.join(src, subdir);
9343
- if (node_fs.existsSync(subdirSrc)) copyWithPathReplacement(subdirSrc, node_path.join(skillDest, subdir), pathPrefix, runtime, explicitConfigDir);
8458
+ if (node_fs.existsSync(subdirSrc)) copyWithPathReplacement(subdirSrc, node_path.join(skillDest, subdir), pathPrefix, explicitConfigDir);
9344
8459
  }
9345
8460
  if (verifyInstalled(skillDest, "maxsim")) spinner.succeed(chalk.green("✓") + " Installed maxsim");
9346
8461
  else {
@@ -9362,10 +8477,7 @@ async function install(isGlobal, runtime = "claude") {
9362
8477
  for (const entry of agentEntries) if (entry.isFile() && entry.name.endsWith(".md")) {
9363
8478
  let content = node_fs.readFileSync(node_path.join(agentsSrc, entry.name), "utf8");
9364
8479
  content = content.replace(/~\/\.claude\//g, pathPrefix);
9365
- content = processAttribution(content, getCommitAttribution(runtime, explicitConfigDir));
9366
- if (isOpencode) content = convertClaudeToOpencodeFrontmatter(content);
9367
- else if (isGemini) content = convertClaudeToGeminiAgent(content);
9368
- else if (isCodex) content = convertClaudeToCodexMarkdown(content);
8480
+ content = processAttribution(content, getCommitAttribution(explicitConfigDir));
9369
8481
  node_fs.writeFileSync(node_path.join(agentsDest, entry.name), content);
9370
8482
  }
9371
8483
  if (verifyInstalled(agentsDest, "agents")) spinner.succeed(chalk.green("✓") + " Installed agents");
@@ -9374,18 +8486,19 @@ async function install(isGlobal, runtime = "claude") {
9374
8486
  failures.push("agents");
9375
8487
  }
9376
8488
  }
8489
+ const legacySkillsDir = node_path.join(targetDir, "agents", "skills");
8490
+ if (node_fs.existsSync(legacySkillsDir)) {
8491
+ node_fs.rmSync(legacySkillsDir, { recursive: true });
8492
+ console.log(` ${chalk.green("✓")} Removed legacy agents/skills/ directory`);
8493
+ }
9377
8494
  const skillsSrc = node_path.join(src, "skills");
9378
8495
  if (node_fs.existsSync(skillsSrc)) {
9379
8496
  spinner = ora({
9380
8497
  text: "Installing skills...",
9381
8498
  color: "cyan"
9382
8499
  }).start();
9383
- const skillsDest = node_path.join(targetDir, "agents", "skills");
9384
- if (node_fs.existsSync(skillsDest)) for (const skill of [
9385
- "tdd",
9386
- "systematic-debugging",
9387
- "verification-before-completion"
9388
- ]) {
8500
+ const skillsDest = node_path.join(targetDir, "skills");
8501
+ if (node_fs.existsSync(skillsDest)) for (const skill of builtInSkills) {
9389
8502
  const skillDir = node_path.join(skillsDest, skill);
9390
8503
  if (node_fs.existsSync(skillDir)) node_fs.rmSync(skillDir, { recursive: true });
9391
8504
  }
@@ -9396,15 +8509,15 @@ async function install(isGlobal, runtime = "claude") {
9396
8509
  if (node_fs.existsSync(skillMd)) {
9397
8510
  let content = node_fs.readFileSync(skillMd, "utf8");
9398
8511
  content = content.replace(/~\/\.claude\//g, pathPrefix);
9399
- content = processAttribution(content, getCommitAttribution(runtime, explicitConfigDir));
8512
+ content = processAttribution(content, getCommitAttribution(explicitConfigDir));
9400
8513
  node_fs.writeFileSync(skillMd, content);
9401
8514
  }
9402
8515
  }
9403
8516
  const installedSkillDirs = node_fs.readdirSync(skillsDest, { withFileTypes: true }).filter((e) => e.isDirectory()).length;
9404
- if (installedSkillDirs > 0) spinner.succeed(chalk.green("✓") + ` Installed ${installedSkillDirs} skills to agents/skills/`);
8517
+ if (installedSkillDirs > 0) spinner.succeed(chalk.green("✓") + ` Installed ${installedSkillDirs} skills to skills/`);
9405
8518
  else {
9406
8519
  spinner.fail("Failed to install skills");
9407
- failures.push("agents/skills");
8520
+ failures.push("skills");
9408
8521
  }
9409
8522
  }
9410
8523
  const changelogSrc = node_path.join(src, "..", "CHANGELOG.md");
@@ -9439,30 +8552,28 @@ async function install(isGlobal, runtime = "claude") {
9439
8552
  node_fs.writeFileSync(versionDest, pkg.version);
9440
8553
  if (verifyFileInstalled(versionDest, "VERSION")) console.log(` ${chalk.green("✓")} Wrote VERSION (${pkg.version})`);
9441
8554
  else failures.push("VERSION");
9442
- if (!isCodex) {
9443
- const pkgJsonDest = node_path.join(targetDir, "package.json");
9444
- node_fs.writeFileSync(pkgJsonDest, "{\"type\":\"commonjs\"}\n");
9445
- console.log(` ${chalk.green("✓")} Wrote package.json (CommonJS mode)`);
9446
- const toolSrc = node_path.resolve(__dirname, "cli.cjs");
9447
- const binDir = node_path.join(targetDir, "maxsim", "bin");
9448
- const toolDest = node_path.join(binDir, "maxsim-tools.cjs");
9449
- if (node_fs.existsSync(toolSrc)) {
9450
- node_fs.mkdirSync(binDir, { recursive: true });
9451
- node_fs.copyFileSync(toolSrc, toolDest);
9452
- console.log(` ${chalk.green("✓")} Installed maxsim-tools.cjs`);
9453
- } else {
9454
- console.warn(` ${chalk.yellow("!")} cli.cjs not found at ${toolSrc} — maxsim-tools.cjs not installed`);
9455
- failures.push("maxsim-tools.cjs");
9456
- }
9457
- const mcpSrc = node_path.resolve(__dirname, "mcp-server.cjs");
9458
- const mcpDest = node_path.join(binDir, "mcp-server.cjs");
9459
- if (node_fs.existsSync(mcpSrc)) {
9460
- node_fs.mkdirSync(binDir, { recursive: true });
9461
- node_fs.copyFileSync(mcpSrc, mcpDest);
9462
- console.log(` ${chalk.green("")} Installed mcp-server.cjs`);
9463
- } else console.warn(` ${chalk.yellow("!")} mcp-server.cjs not found — MCP server not installed`);
9464
- installHookFiles(targetDir, runtime, isGlobal, failures);
9465
- }
8555
+ const pkgJsonDest = node_path.join(targetDir, "package.json");
8556
+ node_fs.writeFileSync(pkgJsonDest, "{\"type\":\"commonjs\"}\n");
8557
+ console.log(` ${chalk.green("")} Wrote package.json (CommonJS mode)`);
8558
+ const toolSrc = node_path.resolve(__dirname, "cli.cjs");
8559
+ const binDir = node_path.join(targetDir, "maxsim", "bin");
8560
+ const toolDest = node_path.join(binDir, "maxsim-tools.cjs");
8561
+ if (node_fs.existsSync(toolSrc)) {
8562
+ node_fs.mkdirSync(binDir, { recursive: true });
8563
+ node_fs.copyFileSync(toolSrc, toolDest);
8564
+ console.log(` ${chalk.green("✓")} Installed maxsim-tools.cjs`);
8565
+ } else {
8566
+ console.warn(` ${chalk.yellow("!")} cli.cjs not found at ${toolSrc} — maxsim-tools.cjs not installed`);
8567
+ failures.push("maxsim-tools.cjs");
8568
+ }
8569
+ const mcpSrc = node_path.resolve(__dirname, "mcp-server.cjs");
8570
+ const mcpDest = node_path.join(binDir, "mcp-server.cjs");
8571
+ if (node_fs.existsSync(mcpSrc)) {
8572
+ node_fs.mkdirSync(binDir, { recursive: true });
8573
+ node_fs.copyFileSync(mcpSrc, mcpDest);
8574
+ console.log(` ${chalk.green("✓")} Installed mcp-server.cjs`);
8575
+ } else console.warn(` ${chalk.yellow("!")} mcp-server.cjs not found — MCP server not installed`);
8576
+ installHookFiles(targetDir, isGlobal, failures);
9466
8577
  const dashboardSrc = node_path.resolve(__dirname, "assets", "dashboard");
9467
8578
  if (node_fs.existsSync(dashboardSrc)) {
9468
8579
  let networkMode = false;
@@ -9489,12 +8600,29 @@ async function install(isGlobal, runtime = "claude") {
9489
8600
  else spinner.succeed(chalk.green("✓") + " Installed dashboard (server.js not found in bundle)");
9490
8601
  if (networkMode) applyFirewallRule(3333);
9491
8602
  }
9492
- if (!isOpencode && !isCodex && !isGemini) {
9493
- const mcpJsonPath = isGlobal ? node_path.join(targetDir, "..", ".mcp.json") : node_path.join(process.cwd(), ".mcp.json");
9494
- let mcpConfig = {};
9495
- if (node_fs.existsSync(mcpJsonPath)) try {
8603
+ const mcpJsonPath = isGlobal ? node_path.join(targetDir, "..", ".mcp.json") : node_path.join(process.cwd(), ".mcp.json");
8604
+ let mcpConfig = {};
8605
+ let skipMcpConfig = false;
8606
+ if (node_fs.existsSync(mcpJsonPath)) {
8607
+ node_fs.copyFileSync(mcpJsonPath, mcpJsonPath + ".bak");
8608
+ try {
9496
8609
  mcpConfig = JSON.parse(node_fs.readFileSync(mcpJsonPath, "utf-8"));
9497
- } catch {}
8610
+ } catch {
8611
+ console.warn(` ${chalk.yellow("!")} .mcp.json is corrupted (invalid JSON). Backup saved to .mcp.json.bak`);
8612
+ let startFresh = true;
8613
+ try {
8614
+ startFresh = await dist_default$1({
8615
+ message: ".mcp.json is corrupted. Start with a fresh config? (No = abort MCP setup)",
8616
+ default: true
8617
+ });
8618
+ } catch {}
8619
+ if (!startFresh) {
8620
+ console.log(` ${chalk.yellow("!")} Skipping .mcp.json configuration`);
8621
+ skipMcpConfig = true;
8622
+ }
8623
+ }
8624
+ }
8625
+ if (!skipMcpConfig) {
9498
8626
  const mcpServers = mcpConfig.mcpServers ?? {};
9499
8627
  mcpServers["maxsim"] = {
9500
8628
  command: "node",
@@ -9509,24 +8637,10 @@ async function install(isGlobal, runtime = "claude") {
9509
8637
  console.error(`\n ${chalk.yellow("Installation incomplete!")} Failed: ${failures.join(", ")}`);
9510
8638
  process.exit(1);
9511
8639
  }
9512
- writeManifest(targetDir, runtime);
8640
+ writeManifest(targetDir);
9513
8641
  console.log(` ${chalk.green("✓")} Wrote file manifest (${MANIFEST_NAME})`);
9514
- reportLocalPatches(targetDir, runtime);
9515
- if (isCodex) return {
9516
- settingsPath: null,
9517
- settings: null,
9518
- statuslineCommand: null,
9519
- runtime
9520
- };
9521
- const { settingsPath, settings, statuslineCommand } = configureSettingsHooks(targetDir, runtime, isGlobal);
9522
- if (isGemini) {
9523
- if (!settings.experimental) settings.experimental = {};
9524
- const experimental = settings.experimental;
9525
- if (!experimental.enableAgents) {
9526
- experimental.enableAgents = true;
9527
- console.log(` ${chalk.green("✓")} Enabled experimental agents`);
9528
- }
9529
- }
8642
+ reportLocalPatches(targetDir);
8643
+ const { settingsPath, settings, statuslineCommand } = configureSettingsHooks(targetDir, isGlobal);
9530
8644
  return {
9531
8645
  settingsPath,
9532
8646
  settings,
@@ -9535,56 +8649,27 @@ async function install(isGlobal, runtime = "claude") {
9535
8649
  };
9536
8650
  }
9537
8651
  /**
9538
- * Prompt for runtime selection (multi-select)
9539
- */
9540
- async function promptRuntime() {
9541
- return await dist_default$2({
9542
- message: "Which runtime(s) would you like to install for?",
9543
- choices: [
9544
- {
9545
- name: "Claude Code " + chalk.dim("(~/.claude)"),
9546
- value: "claude",
9547
- checked: true
9548
- },
9549
- {
9550
- name: "OpenCode " + chalk.dim("(~/.config/opencode)") + " — open source, free models",
9551
- value: "opencode"
9552
- },
9553
- {
9554
- name: "Gemini " + chalk.dim("(~/.gemini)"),
9555
- value: "gemini"
9556
- },
9557
- {
9558
- name: "Codex " + chalk.dim("(~/.codex)"),
9559
- value: "codex"
9560
- }
9561
- ],
9562
- validate: (choices) => choices.length > 0 || "Please select at least one runtime"
9563
- });
9564
- }
9565
- /**
9566
8652
  * Prompt for install location
9567
8653
  */
9568
- async function promptLocation(runtimes) {
8654
+ async function promptLocation() {
9569
8655
  if (!process.stdin.isTTY) {
9570
8656
  console.log(chalk.yellow("Non-interactive terminal detected, defaulting to global install") + "\n");
9571
8657
  return true;
9572
8658
  }
9573
- const pathExamples = runtimes.map((r) => getGlobalDir(r, explicitConfigDir).replace(node_os.homedir(), "~")).join(", ");
9574
- const localExamples = runtimes.map((r) => `./${getDirName(r)}`).join(", ");
8659
+ const globalPath = getGlobalDir(explicitConfigDir).replace(node_os.homedir(), "~");
9575
8660
  return await dist_default({
9576
8661
  message: "Where would you like to install?",
9577
8662
  choices: [{
9578
- name: "Global " + chalk.dim(`(${pathExamples})`) + " — available in all projects",
8663
+ name: "Global " + chalk.dim(`(${globalPath})`) + " — available in all projects",
9579
8664
  value: "global"
9580
8665
  }, {
9581
- name: "Local " + chalk.dim(`(${localExamples})`) + " — this project only",
8666
+ name: "Local " + chalk.dim("(./.claude)") + " — this project only",
9582
8667
  value: "local"
9583
8668
  }]
9584
8669
  }) === "global";
9585
8670
  }
9586
8671
  /**
9587
- * Prompt whether to enable Agent Teams (Claude only, experimental feature)
8672
+ * Prompt whether to enable Agent Teams (experimental feature)
9588
8673
  */
9589
8674
  async function promptAgentTeams() {
9590
8675
  console.log();
@@ -9598,29 +8683,20 @@ async function promptAgentTeams() {
9598
8683
  });
9599
8684
  }
9600
8685
  /**
9601
- * Install MAXSIM for all selected runtimes
8686
+ * Install MAXSIM for Claude Code
9602
8687
  */
9603
- async function installAllRuntimes(runtimes, isGlobal, isInteractive) {
9604
- const results = [];
9605
- for (const runtime of runtimes) {
9606
- const result = await install(isGlobal, runtime);
9607
- results.push(result);
9608
- }
9609
- const statuslineRuntimes = ["claude", "gemini"];
9610
- const primaryStatuslineResult = results.find((r) => statuslineRuntimes.includes(r.runtime));
8688
+ async function installForClaude(isGlobal, isInteractive) {
8689
+ const result = await install(isGlobal);
9611
8690
  let shouldInstallStatusline = false;
9612
- if (primaryStatuslineResult && primaryStatuslineResult.settings) shouldInstallStatusline = await handleStatusline(primaryStatuslineResult.settings, isInteractive, forceStatusline);
8691
+ if (result.settings) shouldInstallStatusline = await handleStatusline(result.settings, isInteractive, forceStatusline);
9613
8692
  let enableAgentTeams = false;
9614
- if (isInteractive && runtimes.includes("claude")) enableAgentTeams = await promptAgentTeams();
9615
- for (const result of results) {
9616
- const useStatusline = statuslineRuntimes.includes(result.runtime) && shouldInstallStatusline;
9617
- if (result.runtime === "claude" && enableAgentTeams && result.settings) {
9618
- const env = result.settings.env ?? {};
9619
- env["CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS"] = "1";
9620
- result.settings.env = env;
9621
- }
9622
- finishInstall(result.settingsPath, result.settings, result.statuslineCommand, useStatusline, result.runtime, isGlobal);
8693
+ if (isInteractive) enableAgentTeams = await promptAgentTeams();
8694
+ if (enableAgentTeams && result.settings) {
8695
+ const env = result.settings.env ?? {};
8696
+ env["CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS"] = "1";
8697
+ result.settings.env = env;
9623
8698
  }
8699
+ finishInstall(result.settingsPath, result.settings, result.statuslineCommand, shouldInstallStatusline, isGlobal);
9624
8700
  }
9625
8701
  const subcommand = argv._[0];
9626
8702
  (async () => {
@@ -9628,6 +8704,27 @@ const subcommand = argv._[0];
9628
8704
  await runDashboardSubcommand(argv);
9629
8705
  return;
9630
8706
  }
8707
+ if (subcommand === "skill-list" || subcommand === "skill-install" || subcommand === "skill-update") {
8708
+ const { cmdSkillList, cmdSkillInstall, cmdSkillUpdate } = await Promise.resolve().then(() => require("./skills-BOSxYUzf.cjs"));
8709
+ const { CliOutput, writeOutput, CliError } = await Promise.resolve().then(() => require("./core-TFSlUjV1.cjs"));
8710
+ const cwd = process.cwd();
8711
+ try {
8712
+ if (subcommand === "skill-list") cmdSkillList(cwd, false);
8713
+ else if (subcommand === "skill-install") cmdSkillInstall(cwd, argv._[1], false);
8714
+ else if (subcommand === "skill-update") cmdSkillUpdate(cwd, argv._[1], false);
8715
+ } catch (thrown) {
8716
+ if (thrown instanceof CliOutput) {
8717
+ writeOutput(thrown);
8718
+ process.exit(0);
8719
+ }
8720
+ if (thrown instanceof CliError) {
8721
+ console.error("Error: " + thrown.message);
8722
+ process.exit(1);
8723
+ }
8724
+ throw thrown;
8725
+ }
8726
+ return;
8727
+ }
9631
8728
  if (hasGlobal && hasLocal) {
9632
8729
  console.error(chalk.yellow("Cannot specify both --global and --local"));
9633
8730
  process.exit(1);
@@ -9639,20 +8736,12 @@ const subcommand = argv._[0];
9639
8736
  console.error(chalk.yellow("--uninstall requires --global or --local"));
9640
8737
  process.exit(1);
9641
8738
  }
9642
- const runtimes = selectedRuntimes.length > 0 ? selectedRuntimes : ["claude"];
9643
- for (const runtime of runtimes) uninstall(hasGlobal, runtime, explicitConfigDir);
9644
- } else if (selectedRuntimes.length > 0) if (!hasGlobal && !hasLocal) {
9645
- const isGlobal = await promptLocation(selectedRuntimes);
9646
- await installAllRuntimes(selectedRuntimes, isGlobal, true);
9647
- } else await installAllRuntimes(selectedRuntimes, hasGlobal, false);
9648
- else if (hasGlobal || hasLocal) await installAllRuntimes(["claude"], hasGlobal, false);
8739
+ uninstall(hasGlobal, explicitConfigDir);
8740
+ } else if (hasGlobal || hasLocal) await installForClaude(hasGlobal, false);
9649
8741
  else if (!process.stdin.isTTY) {
9650
- console.log(chalk.yellow("Non-interactive terminal detected, defaulting to Claude Code global install") + "\n");
9651
- await installAllRuntimes(["claude"], true, false);
9652
- } else {
9653
- const runtimes = await promptRuntime();
9654
- await installAllRuntimes(runtimes, await promptLocation(runtimes), true);
9655
- }
8742
+ console.log(chalk.yellow("Non-interactive terminal detected, defaulting to global install") + "\n");
8743
+ await installForClaude(true, false);
8744
+ } else await installForClaude(await promptLocation(), true);
9656
8745
  })().catch((err) => {
9657
8746
  if (err instanceof Error && err.message.includes("User force closed")) {
9658
8747
  console.log("\n" + chalk.yellow("Installation cancelled") + "\n");
@@ -9663,4 +8752,6 @@ const subcommand = argv._[0];
9663
8752
  });
9664
8753
 
9665
8754
  //#endregion
8755
+ exports.__commonJSMin = __commonJSMin;
8756
+ exports.__toESM = __toESM;
9666
8757
  //# sourceMappingURL=install.cjs.map