mancode 0.3.3 → 0.3.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js CHANGED
@@ -7,9 +7,10 @@ import {
7
7
  import { program } from "commander";
8
8
 
9
9
  // src/commands/init.ts
10
- import { promises as fs2 } from "fs";
11
- import path13 from "path";
12
- import process4 from "process";
10
+ import { promises as fs3 } from "fs";
11
+ import os from "os";
12
+ import path14 from "path";
13
+ import process5 from "process";
13
14
 
14
15
  // src/installers/claude-code.ts
15
16
  import {
@@ -1385,6 +1386,7 @@ var DEFAULT_CONFIG = {
1385
1386
  platforms: ["claude-code"],
1386
1387
  cliCommand: "mancode",
1387
1388
  cliArgs: [],
1389
+ teamMode: "auto",
1388
1390
  forceTeamMode: false,
1389
1391
  defaultStyle: null,
1390
1392
  hooks: {
@@ -1773,100 +1775,10 @@ function isNodeError2(err) {
1773
1775
  return err instanceof Error && "code" in err;
1774
1776
  }
1775
1777
 
1776
- // src/installers/codex.ts
1777
- import { writeFile as writeFile5 } from "fs/promises";
1778
+ // src/installers/cursor.ts
1779
+ import { mkdir as mkdir5, readFile as readFile6, rm as rm3, writeFile as writeFile5 } from "fs/promises";
1778
1780
  import path6 from "path";
1779
1781
 
1780
- // src/installers/managed-block.ts
1781
- var DEFAULT_MANCODE_START_MARKER = "<!-- mancode:start -->";
1782
- var DEFAULT_MANCODE_END_MARKER = "<!-- mancode:end -->";
1783
- function removeManagedBlock(existing, startMarker = DEFAULT_MANCODE_START_MARKER, endMarker = DEFAULT_MANCODE_END_MARKER) {
1784
- const start = findMarkerLine(existing, startMarker);
1785
- const end = findMarkerLine(existing, endMarker);
1786
- if (start === null && end === null) return existing;
1787
- if (start === null || end === null) return existing;
1788
- if (end.start < start.start) return existing;
1789
- const before = existing.slice(0, start.start);
1790
- const after = existing.slice(end.end);
1791
- const merged = `${before}${after}`;
1792
- return cleanUpOrphanedNewlines(merged);
1793
- }
1794
- function hasManagedBlock(existing, startMarker = DEFAULT_MANCODE_START_MARKER, endMarker = DEFAULT_MANCODE_END_MARKER) {
1795
- const start = findMarkerLine(existing, startMarker);
1796
- const end = findMarkerLine(existing, endMarker);
1797
- return start !== null && end !== null && end.start > start.start;
1798
- }
1799
- function cleanUpOrphanedNewlines(content) {
1800
- const trimmed = content.replace(/\n{3,}/gu, "\n\n").replace(/\n+$/u, "\n");
1801
- return trimmed || "";
1802
- }
1803
- function replaceManagedBlock(existing, block, startMarker = DEFAULT_MANCODE_START_MARKER, endMarker = DEFAULT_MANCODE_END_MARKER) {
1804
- const normalizedBlock = normalizeManagedBlock(block, startMarker, endMarker);
1805
- const start = findMarkerLine(existing, startMarker);
1806
- const end = findMarkerLine(existing, endMarker);
1807
- if (start === null !== (end === null)) {
1808
- throw new Error("managed block is malformed: missing start or end marker");
1809
- }
1810
- if (start === null && end === null) {
1811
- const trimmedExisting = trimTrailingNewlines(existing);
1812
- if (!trimmedExisting) return `${normalizedBlock}
1813
- `;
1814
- return `${trimmedExisting}
1815
-
1816
- ${normalizedBlock}
1817
- `;
1818
- }
1819
- if (!start || !end) {
1820
- throw new Error("managed block is malformed: missing start or end marker");
1821
- }
1822
- if (end.start < start.start) {
1823
- throw new Error("managed block is malformed: end marker precedes start");
1824
- }
1825
- return `${existing.slice(0, start.start)}${normalizedBlock}${existing.slice(
1826
- end.end
1827
- )}`;
1828
- }
1829
- function normalizeManagedBlock(block, startMarker, endMarker) {
1830
- const trimmedBlock = block.trim();
1831
- const hasStart = trimmedBlock.startsWith(startMarker);
1832
- const hasEnd = trimmedBlock.endsWith(endMarker);
1833
- if (hasStart && hasEnd) return trimmedBlock;
1834
- if (hasStart || hasEnd) {
1835
- throw new Error("managed block content includes only one marker");
1836
- }
1837
- return `${startMarker}
1838
- ${trimmedBlock}
1839
- ${endMarker}`;
1840
- }
1841
- function trimTrailingNewlines(value) {
1842
- return value.replace(/\n+$/u, "");
1843
- }
1844
- function findMarkerLine(content, marker) {
1845
- let offset = 0;
1846
- let inFence = null;
1847
- for (const lineWithBreak of content.matchAll(/[^\n]*(?:\n|$)/gu)) {
1848
- const rawLine = lineWithBreak[0];
1849
- if (!rawLine) break;
1850
- const line = rawLine.replace(/\n$/u, "").replace(/\r$/u, "");
1851
- const fence = line.match(/^(`{3,}|~{3,})/u)?.[1];
1852
- if (fence) {
1853
- const char = fence[0];
1854
- if (!inFence) {
1855
- inFence = { char, length: fence.length };
1856
- } else if (inFence.char === char && fence.length >= inFence.length) {
1857
- inFence = null;
1858
- }
1859
- } else if (!inFence && line === marker) {
1860
- return {
1861
- start: offset,
1862
- end: offset + marker.length
1863
- };
1864
- }
1865
- offset += rawLine.length;
1866
- }
1867
- return null;
1868
- }
1869
-
1870
1782
  // src/installers/mode-skills.ts
1871
1783
  import { mkdir as mkdir4, readFile as readFile4, readdir as readdir2, rm as rm2, writeFile as writeFile4 } from "fs/promises";
1872
1784
  import path4 from "path";
@@ -2452,99 +2364,7 @@ function formatValue(value) {
2452
2364
  return "";
2453
2365
  }
2454
2366
 
2455
- // src/installers/codex.ts
2456
- async function installCodex(projectRoot, options) {
2457
- await installMancodeCore(projectRoot);
2458
- const agentsPath = path6.join(projectRoot, "AGENTS.md");
2459
- const existing = await readTextIfExists(agentsPath);
2460
- const sharedContent = await generateSharedContent(projectRoot, {
2461
- platform: "codex",
2462
- displayName: "Codex (ChatGPT desktop/CLI)",
2463
- capabilities: {
2464
- slashCommands: "partial",
2465
- subagents: false,
2466
- hooks: false,
2467
- skills: "agents-skills"
2468
- },
2469
- minimal: options.minimal,
2470
- techStack: options.techStack,
2471
- uiLibrary: options.uiLibrary,
2472
- projectProfile: options.projectProfile
2473
- });
2474
- const block = [
2475
- DEFAULT_MANCODE_START_MARKER,
2476
- "<!-- Managed by mancode. Do not edit this block manually. -->",
2477
- "",
2478
- "# mancode Configuration",
2479
- "",
2480
- sharedContent.trim(),
2481
- DEFAULT_MANCODE_END_MARKER
2482
- ].join("\n");
2483
- await writeFile5(agentsPath, replaceManagedBlock(existing, block), "utf-8");
2484
- await installCodexSkills(projectRoot, options.minimal ?? false);
2485
- }
2486
-
2487
- // src/installers/copilot.ts
2488
- import { mkdir as mkdir5, writeFile as writeFile6 } from "fs/promises";
2489
- import path7 from "path";
2490
- async function installCopilot(projectRoot, options) {
2491
- await installMancodeCore(projectRoot);
2492
- const githubDir = path7.join(projectRoot, ".github");
2493
- await mkdir5(githubDir, { recursive: true });
2494
- const instructionsPath = path7.join(githubDir, "copilot-instructions.md");
2495
- const existing = await readTextIfExists(instructionsPath);
2496
- const sharedContent = await generateSharedContent(projectRoot, {
2497
- platform: "copilot",
2498
- displayName: "GitHub Copilot",
2499
- capabilities: {
2500
- slashCommands: "none",
2501
- subagents: false,
2502
- hooks: false,
2503
- skills: "instructions"
2504
- },
2505
- minimal: true,
2506
- techStack: options.techStack,
2507
- uiLibrary: options.uiLibrary,
2508
- projectProfile: options.projectProfile
2509
- });
2510
- const sections = [
2511
- DEFAULT_MANCODE_START_MARKER,
2512
- "<!-- Managed by mancode. Do not edit this block manually. -->",
2513
- "",
2514
- "# mancode for GitHub Copilot",
2515
- "",
2516
- sharedContent.trim()
2517
- ];
2518
- if (!options.minimal) {
2519
- sections.push("", renderCopilotPromptConventions());
2520
- }
2521
- sections.push(DEFAULT_MANCODE_END_MARKER);
2522
- await writeFile6(
2523
- instructionsPath,
2524
- replaceManagedBlock(existing, sections.join("\n")),
2525
- "utf-8"
2526
- );
2527
- await installCopilotPrompts(projectRoot, options.minimal ?? false);
2528
- }
2529
- function renderCopilotPromptConventions() {
2530
- return [
2531
- "## mancode Prompt Conventions",
2532
- "",
2533
- "GitHub Copilot does not provide native mancode slash commands, hooks, or isolated subagents. Treat these names as user prompt conventions:",
2534
- "",
2535
- "- man: use progressive research, planning, implementation, verification, and bounded risk-based review.",
2536
- "- mamba: diagnose bugs and validate real user flows or regressions.",
2537
- "- manteam: read team memory and write handoff-friendly summaries.",
2538
- "- manps: prefer `mancode manps [area]` before cleanup.",
2539
- "- mansolo: exit any active mode and return to default solo behavior.",
2540
- "",
2541
- "These correspond to prompt files in `.github/prompts/`. Select the matching prompt to activate a mode."
2542
- ].join("\n");
2543
- }
2544
-
2545
2367
  // src/installers/cursor.ts
2546
- import { mkdir as mkdir6, readFile as readFile6, rm as rm3, writeFile as writeFile7 } from "fs/promises";
2547
- import path8 from "path";
2548
2368
  var MANCODE_CURSOR_CORE_RULE_FILES = [
2549
2369
  "mancode-context.mdc",
2550
2370
  "mancode-practice.mdc",
@@ -2563,8 +2383,8 @@ var MANCODE_CURSOR_RULE_FILES = [
2563
2383
  ];
2564
2384
  async function installCursor(projectRoot, options) {
2565
2385
  await installMancodeCore(projectRoot);
2566
- const rulesDir = path8.join(projectRoot, ".cursor", "rules");
2567
- await mkdir6(rulesDir, { recursive: true });
2386
+ const rulesDir = path6.join(projectRoot, ".cursor", "rules");
2387
+ await mkdir5(rulesDir, { recursive: true });
2568
2388
  const sharedContent = await generateSharedContent(projectRoot, {
2569
2389
  platform: "cursor",
2570
2390
  displayName: "Cursor",
@@ -2652,14 +2472,14 @@ async function writeRule(rulesDir, fileName, description, alwaysApply, body) {
2652
2472
 
2653
2473
  ${body.trim()}
2654
2474
  `;
2655
- const rulePath = path8.join(rulesDir, fileName);
2475
+ const rulePath = path6.join(rulesDir, fileName);
2656
2476
  const existing = await readTextIfExists4(rulePath);
2657
2477
  if (existing !== null && !isGeneratedCursorRule(existing, fileName)) {
2658
2478
  throw new Error(
2659
2479
  `refusing to overwrite user-authored Cursor rule: ${rulePath}`
2660
2480
  );
2661
2481
  }
2662
- await writeFile7(
2482
+ await writeFile5(
2663
2483
  rulePath,
2664
2484
  content.replace("---\n\n", `---
2665
2485
 
@@ -2673,17 +2493,17 @@ async function removeAdvancedRules(rulesDir) {
2673
2493
  for (const fileName of MANCODE_CURSOR_ADVANCED_RULE_FILES) {
2674
2494
  await removeGeneratedCursorRule(rulesDir, fileName);
2675
2495
  }
2676
- await removeLegacyCursorRules(path8.dirname(path8.dirname(rulesDir)));
2496
+ await removeLegacyCursorRules(path6.dirname(path6.dirname(rulesDir)));
2677
2497
  }
2678
2498
  async function removeCursorGeneratedRules(projectRoot) {
2679
- const rulesDir = path8.join(projectRoot, ".cursor", "rules");
2499
+ const rulesDir = path6.join(projectRoot, ".cursor", "rules");
2680
2500
  for (const fileName of MANCODE_CURSOR_RULE_FILES) {
2681
2501
  await removeGeneratedCursorRule(rulesDir, fileName);
2682
2502
  }
2683
2503
  await removeLegacyCursorRules(projectRoot);
2684
2504
  }
2685
2505
  async function removeLegacyCursorRules(projectRoot) {
2686
- const legacyPath = path8.join(
2506
+ const legacyPath = path6.join(
2687
2507
  projectRoot,
2688
2508
  ".cursor",
2689
2509
  "rules",
@@ -2698,7 +2518,7 @@ async function removeLegacyCursorRules(projectRoot) {
2698
2518
  }
2699
2519
  }
2700
2520
  async function removeGeneratedCursorRule(rulesDir, fileName) {
2701
- const rulePath = path8.join(rulesDir, fileName);
2521
+ const rulePath = path6.join(rulesDir, fileName);
2702
2522
  const content = await readTextIfExists4(rulePath);
2703
2523
  if (content && isGeneratedCursorRule(content, fileName)) {
2704
2524
  await rm3(rulePath, { force: true });
@@ -2768,78 +2588,262 @@ function renderManpsRule() {
2768
2588
  return renderModeSkill("manps", "/");
2769
2589
  }
2770
2590
 
2771
- // src/installers/zcode.ts
2772
- import { writeFile as writeFile8 } from "fs/promises";
2773
- import path9 from "path";
2774
- var ZCODE_MANCODE_START_MARKER = "<!-- mancode:zcode:start -->";
2775
- var ZCODE_MANCODE_END_MARKER = "<!-- mancode:zcode:end -->";
2776
- async function installZcode(projectRoot, options) {
2777
- await installMancodeCore(projectRoot);
2778
- const agentsPath = path9.join(projectRoot, "AGENTS.md");
2779
- const existing = await readTextIfExists(agentsPath);
2780
- const sharedContent = await generateSharedContent(projectRoot, {
2781
- platform: "zcode",
2782
- displayName: "ZCode",
2783
- capabilities: {
2784
- slashCommands: "partial",
2785
- subagents: false,
2786
- hooks: false,
2787
- skills: "agents-skills"
2788
- },
2789
- minimal: options.minimal,
2790
- techStack: options.techStack,
2791
- uiLibrary: options.uiLibrary,
2792
- projectProfile: options.projectProfile
2793
- });
2794
- const block = [
2795
- ZCODE_MANCODE_START_MARKER,
2796
- "<!-- Managed by mancode. Do not edit this block manually. -->",
2797
- "",
2798
- "# mancode Configuration",
2799
- "",
2800
- sharedContent.trim(),
2801
- ZCODE_MANCODE_END_MARKER
2802
- ].join("\n");
2803
- await writeFile8(
2804
- agentsPath,
2805
- replaceManagedBlock(
2806
- existing,
2807
- block,
2808
- ZCODE_MANCODE_START_MARKER,
2809
- ZCODE_MANCODE_END_MARKER
2810
- ),
2811
- "utf-8"
2812
- );
2813
- await installZcodeSkills(projectRoot, options.minimal ?? false);
2814
- }
2591
+ // src/installers/codex.ts
2592
+ import { writeFile as writeFile6 } from "fs/promises";
2593
+ import path7 from "path";
2815
2594
 
2816
- // src/installers/registry.ts
2817
- var PLATFORM_INSTALLERS = {
2818
- "claude-code": {
2819
- name: "claude-code",
2820
- displayName: "Claude Code",
2821
- capabilities: {
2822
- slashCommands: "native",
2823
- subagents: true,
2824
- hooks: true,
2825
- skills: "native"
2826
- },
2827
- install: installClaudeCode
2828
- },
2829
- cursor: {
2830
- name: "cursor",
2831
- displayName: "Cursor",
2832
- capabilities: {
2833
- slashCommands: "native",
2834
- subagents: false,
2835
- hooks: false,
2836
- skills: "rules"
2837
- },
2838
- install: installCursor
2839
- },
2840
- codex: {
2841
- name: "codex",
2842
- displayName: "Codex (ChatGPT desktop/CLI)",
2595
+ // src/installers/managed-block.ts
2596
+ var DEFAULT_MANCODE_START_MARKER = "<!-- mancode:start -->";
2597
+ var DEFAULT_MANCODE_END_MARKER = "<!-- mancode:end -->";
2598
+ function removeManagedBlock(existing, startMarker = DEFAULT_MANCODE_START_MARKER, endMarker = DEFAULT_MANCODE_END_MARKER) {
2599
+ const start = findMarkerLine(existing, startMarker);
2600
+ const end = findMarkerLine(existing, endMarker);
2601
+ if (start === null && end === null) return existing;
2602
+ if (start === null || end === null) return existing;
2603
+ if (end.start < start.start) return existing;
2604
+ const before = existing.slice(0, start.start);
2605
+ const after = existing.slice(end.end);
2606
+ const merged = `${before}${after}`;
2607
+ return cleanUpOrphanedNewlines(merged);
2608
+ }
2609
+ function hasManagedBlock(existing, startMarker = DEFAULT_MANCODE_START_MARKER, endMarker = DEFAULT_MANCODE_END_MARKER) {
2610
+ const start = findMarkerLine(existing, startMarker);
2611
+ const end = findMarkerLine(existing, endMarker);
2612
+ return start !== null && end !== null && end.start > start.start;
2613
+ }
2614
+ function cleanUpOrphanedNewlines(content) {
2615
+ const trimmed = content.replace(/\n{3,}/gu, "\n\n").replace(/\n+$/u, "\n");
2616
+ return trimmed || "";
2617
+ }
2618
+ function replaceManagedBlock(existing, block, startMarker = DEFAULT_MANCODE_START_MARKER, endMarker = DEFAULT_MANCODE_END_MARKER) {
2619
+ const normalizedBlock = normalizeManagedBlock(block, startMarker, endMarker);
2620
+ const start = findMarkerLine(existing, startMarker);
2621
+ const end = findMarkerLine(existing, endMarker);
2622
+ if (start === null !== (end === null)) {
2623
+ throw new Error("managed block is malformed: missing start or end marker");
2624
+ }
2625
+ if (start === null && end === null) {
2626
+ const trimmedExisting = trimTrailingNewlines(existing);
2627
+ if (!trimmedExisting) return `${normalizedBlock}
2628
+ `;
2629
+ return `${trimmedExisting}
2630
+
2631
+ ${normalizedBlock}
2632
+ `;
2633
+ }
2634
+ if (!start || !end) {
2635
+ throw new Error("managed block is malformed: missing start or end marker");
2636
+ }
2637
+ if (end.start < start.start) {
2638
+ throw new Error("managed block is malformed: end marker precedes start");
2639
+ }
2640
+ return `${existing.slice(0, start.start)}${normalizedBlock}${existing.slice(
2641
+ end.end
2642
+ )}`;
2643
+ }
2644
+ function normalizeManagedBlock(block, startMarker, endMarker) {
2645
+ const trimmedBlock = block.trim();
2646
+ const hasStart = trimmedBlock.startsWith(startMarker);
2647
+ const hasEnd = trimmedBlock.endsWith(endMarker);
2648
+ if (hasStart && hasEnd) return trimmedBlock;
2649
+ if (hasStart || hasEnd) {
2650
+ throw new Error("managed block content includes only one marker");
2651
+ }
2652
+ return `${startMarker}
2653
+ ${trimmedBlock}
2654
+ ${endMarker}`;
2655
+ }
2656
+ function trimTrailingNewlines(value) {
2657
+ return value.replace(/\n+$/u, "");
2658
+ }
2659
+ function findMarkerLine(content, marker) {
2660
+ let offset = 0;
2661
+ let inFence = null;
2662
+ for (const lineWithBreak of content.matchAll(/[^\n]*(?:\n|$)/gu)) {
2663
+ const rawLine = lineWithBreak[0];
2664
+ if (!rawLine) break;
2665
+ const line = rawLine.replace(/\n$/u, "").replace(/\r$/u, "");
2666
+ const fence = line.match(/^(`{3,}|~{3,})/u)?.[1];
2667
+ if (fence) {
2668
+ const char = fence[0];
2669
+ if (!inFence) {
2670
+ inFence = { char, length: fence.length };
2671
+ } else if (inFence.char === char && fence.length >= inFence.length) {
2672
+ inFence = null;
2673
+ }
2674
+ } else if (!inFence && line === marker) {
2675
+ return {
2676
+ start: offset,
2677
+ end: offset + marker.length
2678
+ };
2679
+ }
2680
+ offset += rawLine.length;
2681
+ }
2682
+ return null;
2683
+ }
2684
+
2685
+ // src/installers/codex.ts
2686
+ async function installCodex(projectRoot, options) {
2687
+ await installMancodeCore(projectRoot);
2688
+ const agentsPath = path7.join(projectRoot, "AGENTS.md");
2689
+ const existing = await readTextIfExists(agentsPath);
2690
+ const sharedContent = await generateSharedContent(projectRoot, {
2691
+ platform: "codex",
2692
+ displayName: "Codex (ChatGPT desktop/CLI)",
2693
+ capabilities: {
2694
+ slashCommands: "partial",
2695
+ subagents: false,
2696
+ hooks: false,
2697
+ skills: "agents-skills"
2698
+ },
2699
+ minimal: options.minimal,
2700
+ techStack: options.techStack,
2701
+ uiLibrary: options.uiLibrary,
2702
+ projectProfile: options.projectProfile
2703
+ });
2704
+ const block = [
2705
+ DEFAULT_MANCODE_START_MARKER,
2706
+ "<!-- Managed by mancode. Do not edit this block manually. -->",
2707
+ "",
2708
+ "# mancode Configuration",
2709
+ "",
2710
+ sharedContent.trim(),
2711
+ DEFAULT_MANCODE_END_MARKER
2712
+ ].join("\n");
2713
+ await writeFile6(agentsPath, replaceManagedBlock(existing, block), "utf-8");
2714
+ await installCodexSkills(projectRoot, options.minimal ?? false);
2715
+ }
2716
+
2717
+ // src/installers/copilot.ts
2718
+ import { mkdir as mkdir6, writeFile as writeFile7 } from "fs/promises";
2719
+ import path8 from "path";
2720
+ async function installCopilot(projectRoot, options) {
2721
+ await installMancodeCore(projectRoot);
2722
+ const githubDir = path8.join(projectRoot, ".github");
2723
+ await mkdir6(githubDir, { recursive: true });
2724
+ const instructionsPath = path8.join(githubDir, "copilot-instructions.md");
2725
+ const existing = await readTextIfExists(instructionsPath);
2726
+ const sharedContent = await generateSharedContent(projectRoot, {
2727
+ platform: "copilot",
2728
+ displayName: "GitHub Copilot",
2729
+ capabilities: {
2730
+ slashCommands: "none",
2731
+ subagents: false,
2732
+ hooks: false,
2733
+ skills: "instructions"
2734
+ },
2735
+ minimal: true,
2736
+ techStack: options.techStack,
2737
+ uiLibrary: options.uiLibrary,
2738
+ projectProfile: options.projectProfile
2739
+ });
2740
+ const sections = [
2741
+ DEFAULT_MANCODE_START_MARKER,
2742
+ "<!-- Managed by mancode. Do not edit this block manually. -->",
2743
+ "",
2744
+ "# mancode for GitHub Copilot",
2745
+ "",
2746
+ sharedContent.trim()
2747
+ ];
2748
+ if (!options.minimal) {
2749
+ sections.push("", renderCopilotPromptConventions());
2750
+ }
2751
+ sections.push(DEFAULT_MANCODE_END_MARKER);
2752
+ await writeFile7(
2753
+ instructionsPath,
2754
+ replaceManagedBlock(existing, sections.join("\n")),
2755
+ "utf-8"
2756
+ );
2757
+ await installCopilotPrompts(projectRoot, options.minimal ?? false);
2758
+ }
2759
+ function renderCopilotPromptConventions() {
2760
+ return [
2761
+ "## mancode Prompt Conventions",
2762
+ "",
2763
+ "GitHub Copilot does not provide native mancode slash commands, hooks, or isolated subagents. Treat these names as user prompt conventions:",
2764
+ "",
2765
+ "- man: use progressive research, planning, implementation, verification, and bounded risk-based review.",
2766
+ "- mamba: diagnose bugs and validate real user flows or regressions.",
2767
+ "- manteam: read team memory and write handoff-friendly summaries.",
2768
+ "- manps: prefer `mancode manps [area]` before cleanup.",
2769
+ "- mansolo: exit any active mode and return to default solo behavior.",
2770
+ "",
2771
+ "These correspond to prompt files in `.github/prompts/`. Select the matching prompt to activate a mode."
2772
+ ].join("\n");
2773
+ }
2774
+
2775
+ // src/installers/zcode.ts
2776
+ import { writeFile as writeFile8 } from "fs/promises";
2777
+ import path9 from "path";
2778
+ var ZCODE_MANCODE_START_MARKER = "<!-- mancode:zcode:start -->";
2779
+ var ZCODE_MANCODE_END_MARKER = "<!-- mancode:zcode:end -->";
2780
+ async function installZcode(projectRoot, options) {
2781
+ await installMancodeCore(projectRoot);
2782
+ const agentsPath = path9.join(projectRoot, "AGENTS.md");
2783
+ const existing = await readTextIfExists(agentsPath);
2784
+ const sharedContent = await generateSharedContent(projectRoot, {
2785
+ platform: "zcode",
2786
+ displayName: "ZCode",
2787
+ capabilities: {
2788
+ slashCommands: "partial",
2789
+ subagents: false,
2790
+ hooks: false,
2791
+ skills: "agents-skills"
2792
+ },
2793
+ minimal: options.minimal,
2794
+ techStack: options.techStack,
2795
+ uiLibrary: options.uiLibrary,
2796
+ projectProfile: options.projectProfile
2797
+ });
2798
+ const block = [
2799
+ ZCODE_MANCODE_START_MARKER,
2800
+ "<!-- Managed by mancode. Do not edit this block manually. -->",
2801
+ "",
2802
+ "# mancode Configuration",
2803
+ "",
2804
+ sharedContent.trim(),
2805
+ ZCODE_MANCODE_END_MARKER
2806
+ ].join("\n");
2807
+ await writeFile8(
2808
+ agentsPath,
2809
+ replaceManagedBlock(
2810
+ existing,
2811
+ block,
2812
+ ZCODE_MANCODE_START_MARKER,
2813
+ ZCODE_MANCODE_END_MARKER
2814
+ ),
2815
+ "utf-8"
2816
+ );
2817
+ await installZcodeSkills(projectRoot, options.minimal ?? false);
2818
+ }
2819
+
2820
+ // src/installers/registry.ts
2821
+ var PLATFORM_INSTALLERS = {
2822
+ "claude-code": {
2823
+ name: "claude-code",
2824
+ displayName: "Claude Code",
2825
+ capabilities: {
2826
+ slashCommands: "native",
2827
+ subagents: true,
2828
+ hooks: true,
2829
+ skills: "native"
2830
+ },
2831
+ install: installClaudeCode
2832
+ },
2833
+ cursor: {
2834
+ name: "cursor",
2835
+ displayName: "Cursor",
2836
+ capabilities: {
2837
+ slashCommands: "native",
2838
+ subagents: false,
2839
+ hooks: false,
2840
+ skills: "rules"
2841
+ },
2842
+ install: installCursor
2843
+ },
2844
+ codex: {
2845
+ name: "codex",
2846
+ displayName: "Codex (ChatGPT desktop/CLI)",
2843
2847
  capabilities: {
2844
2848
  slashCommands: "partial",
2845
2849
  subagents: false,
@@ -2932,13 +2936,13 @@ async function detectTeamStatus(projectRoot) {
2932
2936
  }
2933
2937
  }
2934
2938
  async function runGit(cwd, args) {
2935
- const { stdout } = await execFileAsync("git", args, {
2939
+ const { stdout: stdout2 } = await execFileAsync("git", args, {
2936
2940
  cwd,
2937
2941
  encoding: "utf-8",
2938
2942
  env: GIT_ENV,
2939
2943
  windowsHide: true
2940
2944
  });
2941
- return stdout;
2945
+ return stdout2;
2942
2946
  }
2943
2947
  function countUniqueLines(output) {
2944
2948
  return new Set(
@@ -2963,9 +2967,191 @@ async function detectSystemDeps(env = process3.env) {
2963
2967
  }
2964
2968
  }
2965
2969
 
2970
+ // src/system/init-onboarding.ts
2971
+ import { execFileSync } from "child_process";
2972
+ import { promises as fs } from "fs";
2973
+ import path11 from "path";
2974
+ import process4 from "process";
2975
+ import { stdin, stdout } from "process";
2976
+ import { createInterface } from "readline/promises";
2977
+ var ALL_PLATFORMS = Object.keys(PLATFORM_INSTALLERS);
2978
+ var cachedNativeSystemLocale;
2979
+ function detectInitLocale(override, environment = process4.env, systemLocale = Intl.DateTimeFormat().resolvedOptions().locale, nativeLocale) {
2980
+ if (override) return parseLocale(override);
2981
+ const resolvedNativeLocale = nativeLocale === void 0 ? getNativeSystemLocale() : nativeLocale;
2982
+ return parseLocale(resolvedNativeLocale) ?? detectEnvironmentLocale(environment) ?? parseLocale(systemLocale) ?? "en";
2983
+ }
2984
+ function detectNativeSystemLocale(platform = process4.platform, runCommand = runLocaleCommand) {
2985
+ if (platform === "darwin") {
2986
+ const languages = runCommand("defaults", ["read", "-g", "AppleLanguages"]);
2987
+ const primaryLanguage = parsePrimaryAppleLanguage(languages);
2988
+ if (primaryLanguage) return primaryLanguage;
2989
+ return cleanLocaleOutput(
2990
+ runCommand("defaults", ["read", "-g", "AppleLocale"])
2991
+ );
2992
+ }
2993
+ if (platform === "win32") {
2994
+ return cleanLocaleOutput(
2995
+ runCommand("powershell.exe", [
2996
+ "-NoLogo",
2997
+ "-NoProfile",
2998
+ "-NonInteractive",
2999
+ "-Command",
3000
+ "[System.Globalization.CultureInfo]::CurrentUICulture.Name"
3001
+ ])
3002
+ );
3003
+ }
3004
+ return null;
3005
+ }
3006
+ function getNativeSystemLocale() {
3007
+ if (cachedNativeSystemLocale === void 0) {
3008
+ cachedNativeSystemLocale = detectNativeSystemLocale();
3009
+ }
3010
+ return cachedNativeSystemLocale;
3011
+ }
3012
+ function detectEnvironmentLocale(environment) {
3013
+ const candidates = [
3014
+ environment.LANGUAGE,
3015
+ environment.LC_ALL,
3016
+ environment.LC_MESSAGES,
3017
+ environment.LANG
3018
+ ];
3019
+ for (const candidate of candidates) {
3020
+ for (const locale of candidate?.split(":") ?? []) {
3021
+ const parsed = parseLocale(locale);
3022
+ if (parsed) return parsed;
3023
+ }
3024
+ }
3025
+ return null;
3026
+ }
3027
+ function runLocaleCommand(command, args) {
3028
+ try {
3029
+ const output = execFileSync(command, [...args], {
3030
+ encoding: "utf8",
3031
+ stdio: ["ignore", "pipe", "ignore"],
3032
+ timeout: 2e3,
3033
+ windowsHide: true
3034
+ });
3035
+ return cleanLocaleOutput(output);
3036
+ } catch {
3037
+ return null;
3038
+ }
3039
+ }
3040
+ function parsePrimaryAppleLanguage(output) {
3041
+ const cleaned = cleanLocaleOutput(output);
3042
+ if (!cleaned) return null;
3043
+ const quoted = cleaned.match(/"([^"]+)"/)?.[1];
3044
+ if (quoted) return quoted;
3045
+ return cleaned.split(/[\s(),]+/).find((value) => /^[a-z]{2,3}(?:[-_][a-z0-9]+)*$/i.test(value)) ?? null;
3046
+ }
3047
+ function cleanLocaleOutput(output) {
3048
+ const cleaned = output?.replace(/^\uFEFF/, "").trim();
3049
+ return cleaned || null;
3050
+ }
3051
+ function parseLocale(value) {
3052
+ if (!value) return null;
3053
+ const normalized = value.toLowerCase().replace("_", "-");
3054
+ if (normalized === "zh" || normalized.startsWith("zh-")) return "zh-CN";
3055
+ if (normalized === "en" || normalized.startsWith("en-")) return "en";
3056
+ return null;
3057
+ }
3058
+ function parsePlatformSelection(value) {
3059
+ const normalized = value.trim().toLowerCase();
3060
+ if (normalized === "all" || normalized === "\u5168\u90E8") return [...ALL_PLATFORMS];
3061
+ const choices = normalized.split(",").map((item) => item.trim()).filter(Boolean);
3062
+ if (choices.length === 0) return null;
3063
+ if (!choices.every((item) => item in PLATFORM_INSTALLERS)) {
3064
+ return null;
3065
+ }
3066
+ return [...new Set(choices)];
3067
+ }
3068
+ async function detectPlatformHints(rootDir, environment = process4.env) {
3069
+ const hints = /* @__PURE__ */ new Set();
3070
+ if (environment.CLAUDECODE || environment.CLAUDE_CODE)
3071
+ hints.add("claude-code");
3072
+ if (environment.CODEX_HOME) hints.add("codex");
3073
+ if (environment.CURSOR_TRACE_ID) hints.add("cursor");
3074
+ if (environment.COPILOT_AGENT || environment.GITHUB_COPILOT)
3075
+ hints.add("copilot");
3076
+ const exists = async (relative) => {
3077
+ try {
3078
+ await fs.access(path11.join(rootDir, relative));
3079
+ return true;
3080
+ } catch {
3081
+ return false;
3082
+ }
3083
+ };
3084
+ if (await exists(".claude")) hints.add("claude-code");
3085
+ if (await exists(".cursor")) hints.add("cursor");
3086
+ if (await exists(".github/copilot-instructions.md")) hints.add("copilot");
3087
+ return ALL_PLATFORMS.filter((platform) => hints.has(platform));
3088
+ }
3089
+ function createTerminalPrompter() {
3090
+ return {
3091
+ async confirmGenericProject({ rootDir, locale }) {
3092
+ const rl = createInterface({ input: stdin, output: stdout });
3093
+ try {
3094
+ if (locale === "zh-CN") {
3095
+ console.log("\u5F53\u524D\u76EE\u5F55\u6CA1\u6709\u8BC6\u522B\u5230\u9879\u76EE\u6587\u4EF6\u3002");
3096
+ console.log(`\u76EE\u5F55\uFF1A${rootDir}`);
3097
+ console.log("\u8FD9\u662F\u4E00\u4E2A\u65B0\u9879\u76EE\u5417\uFF1F");
3098
+ console.log("[y] \u521D\u59CB\u5316\u4E3A\u901A\u7528\u9879\u76EE");
3099
+ console.log("[n] \u9000\u51FA");
3100
+ const answer2 = await rl.question("\u8BF7\u9009\u62E9 [y/N]: ");
3101
+ return ["y", "yes", "\u662F"].includes(answer2.trim().toLowerCase());
3102
+ }
3103
+ console.log("No project files were detected in the current directory.");
3104
+ console.log(`Directory: ${rootDir}`);
3105
+ console.log("Is this a new project?");
3106
+ console.log("[y] Initialize as a generic project");
3107
+ console.log("[n] Exit");
3108
+ const answer = await rl.question("Choose [y/N]: ");
3109
+ return ["y", "yes"].includes(answer.trim().toLowerCase());
3110
+ } finally {
3111
+ rl.close();
3112
+ }
3113
+ },
3114
+ async selectPlatforms({ locale, detected }) {
3115
+ const rl = createInterface({ input: stdin, output: stdout });
3116
+ try {
3117
+ const names = ALL_PLATFORMS.map((platform, index) => {
3118
+ const suffix = detected.includes(platform) ? locale === "zh-CN" ? "\uFF08\u5DF2\u68C0\u6D4B\u5230\uFF09" : " (detected)" : "";
3119
+ return `${index + 1}. ${PLATFORM_INSTALLERS[platform].displayName}${suffix}`;
3120
+ });
3121
+ console.log(
3122
+ locale === "zh-CN" ? "\n\u9009\u62E9\u8981\u521D\u59CB\u5316\u7684\u5E73\u53F0\uFF1A" : "\nChoose platforms to initialize:"
3123
+ );
3124
+ console.log(names.join("\n"));
3125
+ console.log(locale === "zh-CN" ? "a. \u5168\u90E8\u5E73\u53F0" : "a. All platforms");
3126
+ console.log(
3127
+ locale === "zh-CN" ? "\u53EF\u8F93\u5165\u7F16\u53F7\uFF08\u4F8B\u5982 1,3\uFF09\u6216 a\u3002" : "Enter numbers (for example 1,3) or a."
3128
+ );
3129
+ const answer = (await rl.question(locale === "zh-CN" ? "\u9009\u62E9: " : "Selection: ")).trim().toLowerCase();
3130
+ if (answer === "a" || answer === "all" || answer === "\u5168\u90E8")
3131
+ return [...ALL_PLATFORMS];
3132
+ const indexes = answer.split(",").map((item) => Number(item.trim()));
3133
+ if (!indexes.length || indexes.some(
3134
+ (index) => !Number.isInteger(index) || index < 1 || index > ALL_PLATFORMS.length
3135
+ )) {
3136
+ return null;
3137
+ }
3138
+ const selected = [];
3139
+ for (const index of indexes) {
3140
+ const platform = ALL_PLATFORMS[index - 1];
3141
+ if (!platform) return null;
3142
+ selected.push(platform);
3143
+ }
3144
+ return [...new Set(selected)];
3145
+ } finally {
3146
+ rl.close();
3147
+ }
3148
+ }
3149
+ };
3150
+ }
3151
+
2966
3152
  // src/system/project-profile.ts
2967
3153
  import { readFile as readFile7, readdir as readdir3, stat as stat2 } from "fs/promises";
2968
- import path11 from "path";
3154
+ import path12 from "path";
2969
3155
  var PROJECT_MANIFESTS = [
2970
3156
  "package.json",
2971
3157
  "pyproject.toml",
@@ -2998,12 +3184,12 @@ async function detectProjectProfile(projectRoot) {
2998
3184
  "data",
2999
3185
  "notebooks"
3000
3186
  ]);
3001
- const packageJson = await readJson2(path11.join(projectRoot, "package.json"));
3187
+ const packageJson = await readJson2(path12.join(projectRoot, "package.json"));
3002
3188
  const [pubspec, pythonManifests] = await Promise.all([
3003
- readText(path11.join(projectRoot, "pubspec.yaml")),
3189
+ readText(path12.join(projectRoot, "pubspec.yaml")),
3004
3190
  Promise.all([
3005
- readText(path11.join(projectRoot, "pyproject.toml")),
3006
- readText(path11.join(projectRoot, "requirements.txt"))
3191
+ readText(path12.join(projectRoot, "pyproject.toml")),
3192
+ readText(path12.join(projectRoot, "requirements.txt"))
3007
3193
  ]).then((parts) => parts.filter(Boolean).join("\n"))
3008
3194
  ]);
3009
3195
  const deps = Object.keys({
@@ -3012,7 +3198,7 @@ async function detectProjectProfile(projectRoot) {
3012
3198
  ...packageJson?.peerDependencies
3013
3199
  });
3014
3200
  const flutter = isFlutterProject(pubspec);
3015
- const shadcn = entries.includes("components.json") || deps.some((dep) => dep.startsWith("@radix-ui/")) && await isDirectory(path11.join(projectRoot, "src", "components", "ui"));
3201
+ const shadcn = entries.includes("components.json") || deps.some((dep) => dep.startsWith("@radix-ui/")) && await isDirectory(path12.join(projectRoot, "src", "components", "ui"));
3016
3202
  const languages = inferLanguages(manifests, entries);
3017
3203
  const frameworks = inferFrameworks(
3018
3204
  deps,
@@ -3055,7 +3241,7 @@ async function existingDirs(root, candidates) {
3055
3241
  const found = [];
3056
3242
  for (const candidate of candidates) {
3057
3243
  try {
3058
- if ((await stat2(path11.join(root, candidate))).isDirectory())
3244
+ if ((await stat2(path12.join(root, candidate))).isDirectory())
3059
3245
  found.push(candidate);
3060
3246
  } catch {
3061
3247
  }
@@ -3229,8 +3415,8 @@ function isFlutterProject(pubspec) {
3229
3415
  }
3230
3416
 
3231
3417
  // src/system/scan-aesthetics.ts
3232
- import { promises as fs } from "fs";
3233
- import path12 from "path";
3418
+ import { promises as fs2 } from "fs";
3419
+ import path13 from "path";
3234
3420
  var MAX_COMPONENT_SCAN_DEPTH = 12;
3235
3421
  var MAX_COMPONENT_FILES = 2e3;
3236
3422
  async function scanAesthetics(projectRoot, uiLibrary = null) {
@@ -3243,7 +3429,7 @@ async function scanAesthetics(projectRoot, uiLibrary = null) {
3243
3429
  const cssScan = await scanCssVariables(projectRoot);
3244
3430
  if (configResult) {
3245
3431
  sourceFiles.push(configResult.relPath);
3246
- const content = await fs.readFile(configResult.absPath, "utf-8");
3432
+ const content = await fs2.readFile(configResult.absPath, "utf-8");
3247
3433
  const inspectableContent = stripJsComments(content);
3248
3434
  const themeBlock = findKeyBlock(inspectableContent, "theme");
3249
3435
  if (themeBlock) {
@@ -3266,7 +3452,7 @@ async function scanAesthetics(projectRoot, uiLibrary = null) {
3266
3452
  } else if (await hasTailwindDep(projectRoot) || uiLibrary) {
3267
3453
  matchLevel = "low";
3268
3454
  }
3269
- if (uiLibrary && await pathExists2(path12.join(projectRoot, "package.json"))) {
3455
+ if (uiLibrary && await pathExists2(path13.join(projectRoot, "package.json"))) {
3270
3456
  sourceFiles.push("package.json");
3271
3457
  }
3272
3458
  sourceFiles.push(...cssScan.sourceFiles);
@@ -3291,7 +3477,7 @@ async function findTailwindConfig(projectRoot) {
3291
3477
  "tailwind.config.mjs"
3292
3478
  ];
3293
3479
  for (const name of candidates) {
3294
- const absPath = path12.join(projectRoot, name);
3480
+ const absPath = path13.join(projectRoot, name);
3295
3481
  if (await pathExists2(absPath)) {
3296
3482
  return { absPath, relPath: name };
3297
3483
  }
@@ -3518,7 +3704,7 @@ async function scanComponents(projectRoot) {
3518
3704
  const names = /* @__PURE__ */ new Set();
3519
3705
  let visitedFiles = 0;
3520
3706
  for (const relRoot of roots) {
3521
- const absRoot = path12.join(projectRoot, relRoot);
3707
+ const absRoot = path13.join(projectRoot, relRoot);
3522
3708
  if (!await pathExists2(absRoot)) continue;
3523
3709
  visitedFiles = await collectComponentNames(absRoot, names, 0, visitedFiles);
3524
3710
  if (visitedFiles >= MAX_COMPONENT_FILES) break;
@@ -3532,16 +3718,16 @@ async function collectComponentNames(dir, names, depth, visitedFiles) {
3532
3718
  let fileCount = visitedFiles;
3533
3719
  let entries;
3534
3720
  try {
3535
- entries = await fs.readdir(dir);
3721
+ entries = await fs2.readdir(dir);
3536
3722
  } catch {
3537
3723
  return fileCount;
3538
3724
  }
3539
3725
  for (const entry of entries) {
3540
3726
  if (fileCount >= MAX_COMPONENT_FILES) return fileCount;
3541
- const abs = path12.join(dir, entry);
3727
+ const abs = path13.join(dir, entry);
3542
3728
  let info;
3543
3729
  try {
3544
- info = await fs.lstat(abs);
3730
+ info = await fs2.lstat(abs);
3545
3731
  } catch {
3546
3732
  continue;
3547
3733
  }
@@ -3559,7 +3745,7 @@ async function collectComponentNames(dir, names, depth, visitedFiles) {
3559
3745
  ""
3560
3746
  );
3561
3747
  if (base === "index") {
3562
- base = path12.basename(dir);
3748
+ base = path13.basename(dir);
3563
3749
  }
3564
3750
  if (base.startsWith(".")) continue;
3565
3751
  const componentName = toPascalCase(base);
@@ -3581,9 +3767,9 @@ async function scanCssVariables(projectRoot) {
3581
3767
  const variables = {};
3582
3768
  const sourceFiles = [];
3583
3769
  for (const relPath of candidates) {
3584
- const absPath = path12.join(projectRoot, relPath);
3770
+ const absPath = path13.join(projectRoot, relPath);
3585
3771
  if (!await pathExists2(absPath)) continue;
3586
- const content = await fs.readFile(absPath, "utf-8");
3772
+ const content = await fs2.readFile(absPath, "utf-8");
3587
3773
  const found = extractCssVariables(content);
3588
3774
  if (Object.keys(found).length === 0) continue;
3589
3775
  Object.assign(variables, found);
@@ -3626,8 +3812,8 @@ function toPascalCase(value) {
3626
3812
  }
3627
3813
  async function hasTailwindDep(projectRoot) {
3628
3814
  try {
3629
- const raw = await fs.readFile(
3630
- path12.join(projectRoot, "package.json"),
3815
+ const raw = await fs2.readFile(
3816
+ path13.join(projectRoot, "package.json"),
3631
3817
  "utf-8"
3632
3818
  );
3633
3819
  const pkg = JSON.parse(raw);
@@ -3643,7 +3829,7 @@ function escapeRegex(s) {
3643
3829
  }
3644
3830
  async function pathExists2(p) {
3645
3831
  try {
3646
- await fs.access(p);
3832
+ await fs2.access(p);
3647
3833
  return true;
3648
3834
  } catch {
3649
3835
  return false;
@@ -3654,65 +3840,104 @@ async function pathExists2(p) {
3654
3840
  var EXIT_OK = 0;
3655
3841
  var EXIT_ALREADY_INITIALIZED = 1;
3656
3842
  var EXIT_NOT_A_PROJECT_DIR = 2;
3843
+ var EXIT_USER_CANCEL = 3;
3657
3844
  var EXIT_INIT_FAILED = 5;
3658
3845
  var DEFAULT_INIT_PLATFORM = "claude-code";
3659
- async function init(rootDir = process4.cwd(), options = {}) {
3660
- const mancodeDir = path13.join(rootDir, ".mancode");
3661
- const stateFile = path13.join(mancodeDir, "state.json");
3846
+ async function init(rootDir = process5.cwd(), options = {}) {
3847
+ const mancodeDir = path14.join(rootDir, ".mancode");
3848
+ const stateFile = path14.join(mancodeDir, "state.json");
3662
3849
  const wasInitialized = await pathExists3(stateFile);
3663
- let reinstallSnapshots = [];
3850
+ let mutationSnapshots = [];
3851
+ let directorySnapshots = [];
3852
+ const locale = detectInitLocale(options.lang);
3853
+ if (!locale) {
3854
+ console.error(`\u2717 Unsupported init language: ${options.lang}`);
3855
+ console.error(" Supported values: zh-CN, en");
3856
+ return EXIT_INIT_FAILED;
3857
+ }
3664
3858
  if (wasInitialized) {
3665
3859
  if (!options.force) {
3666
- console.log("\u2139\uFE0F mancode already initialized.");
3860
+ console.log(
3861
+ localize(
3862
+ locale,
3863
+ "\u2139\uFE0F mancode \u5DF2\u7ECF\u521D\u59CB\u5316\u3002",
3864
+ "\u2139\uFE0F mancode already initialized."
3865
+ )
3866
+ );
3667
3867
  console.log(` ${stateFile}`);
3668
- console.log(" Run `mancode init --force` to reinstall.");
3868
+ console.log(
3869
+ localize(
3870
+ locale,
3871
+ " \u8FD0\u884C `mancode init --force` \u91CD\u65B0\u5B89\u88C5\u3002",
3872
+ " Run `mancode init --force` to reinstall."
3873
+ )
3874
+ );
3669
3875
  return EXIT_ALREADY_INITIALIZED;
3670
3876
  }
3671
- try {
3672
- reinstallSnapshots = await snapshotFiles([
3673
- stateFile,
3674
- path13.join(mancodeDir, "config.json"),
3675
- path13.join(mancodeDir, "project-profile.json"),
3676
- path13.join(mancodeDir, "aesthetics", "style-tokens.json")
3677
- ]);
3678
- } catch (error) {
3679
- const message = error instanceof Error ? error.message : String(error);
3680
- console.error(`\u2717 Cannot snapshot existing mancode state: ${message}`);
3681
- return EXIT_INIT_FAILED;
3682
- }
3683
- console.log("\u26A0\uFE0F Reinstalling with --force...");
3877
+ console.log(
3878
+ localize(
3879
+ locale,
3880
+ "\u26A0\uFE0F \u6B63\u5728\u4F7F\u7528 --force \u91CD\u65B0\u5B89\u88C5...",
3881
+ "\u26A0\uFE0F Reinstalling with --force..."
3882
+ )
3883
+ );
3684
3884
  }
3685
3885
  if (!await pathExists3(rootDir)) {
3686
- console.error(`\u2717 Target directory does not exist: ${rootDir}`);
3687
- return EXIT_NOT_A_PROJECT_DIR;
3688
- }
3689
- const isGitRepo = await pathExists3(path13.join(rootDir, ".git"));
3690
- const hasManifest = await Promise.any(
3691
- PROJECT_MANIFESTS.map(async (name) => {
3692
- if (await pathExists3(path13.join(rootDir, name))) return true;
3693
- throw new Error("manifest missing");
3694
- })
3695
- ).catch(() => false);
3696
- if (!isGitRepo && !hasManifest) {
3697
- console.error(`\u2717 Not a project directory: ${rootDir}`);
3698
- console.error(" (No .git or recognized project manifest found)");
3699
- console.error("");
3700
3886
  console.error(
3701
- " Run mancode init in a git repository or a recognized project directory."
3887
+ localize(
3888
+ locale,
3889
+ `\u2717 \u76EE\u6807\u76EE\u5F55\u4E0D\u5B58\u5728\uFF1A${rootDir}`,
3890
+ `\u2717 Target directory does not exist: ${rootDir}`
3891
+ )
3702
3892
  );
3703
3893
  return EXIT_NOT_A_PROJECT_DIR;
3704
3894
  }
3895
+ const isGitRepo = await pathExists3(path14.join(rootDir, ".git"));
3896
+ const hasManifest = await hasProjectManifest(rootDir);
3897
+ let isGenericProject = false;
3898
+ if (!isGitRepo && !hasManifest) {
3899
+ const genericSafety = await canInitializeGenericProject(rootDir);
3900
+ if (!genericSafety.ok) {
3901
+ printNotProjectDirectory(rootDir, locale, genericSafety.reason);
3902
+ return EXIT_NOT_A_PROJECT_DIR;
3903
+ }
3904
+ const prompter = options.prompter ?? (options.interactive ? createTerminalPrompter() : null);
3905
+ const confirmed = options.empty || options.yes ? true : prompter ? await prompter.confirmGenericProject({ rootDir, locale }) : false;
3906
+ if (!confirmed) {
3907
+ if (options.interactive) {
3908
+ console.log(
3909
+ locale === "zh-CN" ? "\u5DF2\u53D6\u6D88\u521D\u59CB\u5316\u3002" : "Initialization cancelled."
3910
+ );
3911
+ return EXIT_USER_CANCEL;
3912
+ }
3913
+ printNotProjectDirectory(rootDir, locale, "empty");
3914
+ return EXIT_NOT_A_PROJECT_DIR;
3915
+ }
3916
+ isGenericProject = true;
3917
+ }
3705
3918
  try {
3706
- console.log("\u2713 \u68C0\u6D4B\u7CFB\u7EDF\u4F9D\u8D56...");
3919
+ console.log(
3920
+ localize(
3921
+ locale,
3922
+ "\u2713 \u68C0\u6D4B\u7CFB\u7EDF\u4F9D\u8D56...",
3923
+ "\u2713 Checking system dependencies..."
3924
+ )
3925
+ );
3707
3926
  const deps = await detectSystemDeps();
3708
3927
  if (!deps.git) {
3709
3928
  console.log(
3710
- "\u26A0\uFE0F Git not found (optional). Team auto-detection will use solo defaults."
3929
+ localize(
3930
+ locale,
3931
+ "\u26A0\uFE0F \u672A\u627E\u5230 Git\uFF08\u53EF\u9009\uFF09\u3002\u56E2\u961F\u81EA\u52A8\u68C0\u6D4B\u5C06\u4F7F\u7528 solo \u9ED8\u8BA4\u503C\u3002",
3932
+ "\u26A0\uFE0F Git not found (optional). Team auto-detection will use solo defaults."
3933
+ )
3711
3934
  );
3712
3935
  } else {
3713
3936
  console.log(" git \u2713");
3714
3937
  }
3715
- console.log("\u2713 \u68C0\u6D4B\u9879\u76EE\u7C7B\u578B...");
3938
+ console.log(
3939
+ localize(locale, "\u2713 \u68C0\u6D4B\u9879\u76EE\u7C7B\u578B...", "\u2713 Detecting project type...")
3940
+ );
3716
3941
  const profile = await detectProjectProfile(rootDir);
3717
3942
  const profileStack = [...profile.languages, ...profile.frameworks];
3718
3943
  const techStackStr = profileStack.join(" + ") || "Unknown";
@@ -3724,37 +3949,100 @@ async function init(rootDir = process4.cwd(), options = {}) {
3724
3949
  console.log(` UI: ${uiLibraryStr}`);
3725
3950
  }
3726
3951
  } else if (hasManifest) {
3727
- console.log(" project manifest found, no known framework dependencies");
3952
+ console.log(
3953
+ localize(
3954
+ locale,
3955
+ " \u5DF2\u53D1\u73B0\u9879\u76EE manifest\uFF0C\u672A\u8BC6\u522B\u5230\u6846\u67B6\u4F9D\u8D56",
3956
+ " Project manifest found; no known framework dependencies"
3957
+ )
3958
+ );
3728
3959
  } else {
3729
3960
  console.log(
3730
- " (No recognized manifest found, profile confidence is low)"
3961
+ localize(
3962
+ locale,
3963
+ " \uFF08\u672A\u53D1\u73B0\u5DF2\u8BC6\u522B\u7684 manifest\uFF0C\u9879\u76EE\u753B\u50CF\u7F6E\u4FE1\u5EA6\u8F83\u4F4E\uFF09",
3964
+ " (No recognized manifest found; profile confidence is low)"
3965
+ )
3731
3966
  );
3732
3967
  }
3733
3968
  const existingPlatform = wasInitialized ? await readExistingInitPlatform(stateFile) : null;
3734
- const initPlatformName = options.platform ?? existingPlatform ?? DEFAULT_INIT_PLATFORM;
3735
- const installer = getPlatformInstaller(initPlatformName);
3736
- if (!installer) {
3737
- console.error(`\u2717 Unsupported platform: ${initPlatformName}`);
3969
+ const platformHints = await detectPlatformHints(rootDir);
3970
+ const selectedPlatforms = await selectInitPlatforms({
3971
+ option: options.platform,
3972
+ existingPlatform,
3973
+ hints: platformHints,
3974
+ interactive: options.interactive,
3975
+ prompter: options.prompter,
3976
+ locale,
3977
+ yes: options.yes
3978
+ });
3979
+ if (!selectedPlatforms) {
3980
+ console.error(
3981
+ locale === "zh-CN" ? "\u2717 \u672A\u9009\u62E9\u5E73\u53F0\u3002\u8BF7\u91CD\u65B0\u8FD0\u884C\u5E76\u9009\u62E9\u5E73\u53F0\uFF0C\u6216\u4F7F\u7528 --platform codex,cursor / --platform all\u3002" : "\u2717 No platform selected. Choose one interactively or pass --platform codex,cursor / --platform all."
3982
+ );
3983
+ return EXIT_INIT_FAILED;
3984
+ }
3985
+ if (selectedPlatforms.length === 0) {
3986
+ console.error("\u2717 No platform selected.");
3987
+ return EXIT_INIT_FAILED;
3988
+ }
3989
+ const firstPlatform = selectedPlatforms[0];
3990
+ if (!firstPlatform) {
3991
+ console.error("\u2717 No platform selected.");
3992
+ return EXIT_INIT_FAILED;
3993
+ }
3994
+ const installers = selectedPlatforms.map(getPlatformInstaller);
3995
+ if (installers.some((installer) => !installer)) {
3996
+ console.error(`\u2717 Unsupported platform: ${options.platform}`);
3738
3997
  return EXIT_INIT_FAILED;
3739
3998
  }
3999
+ const typedInstallers = installers.filter(
4000
+ (installer) => installer !== null
4001
+ );
4002
+ const onlyPlatformHint = platformHints.length === 1 ? platformHints[0] : null;
4003
+ const detectedPrimary = onlyPlatformHint && selectedPlatforms.includes(onlyPlatformHint) ? onlyPlatformHint : null;
4004
+ const primaryPlatform = existingPlatform && selectedPlatforms.includes(existingPlatform) ? existingPlatform : detectedPrimary ?? firstPlatform;
4005
+ const managedFiles = getInitManagedFilePaths(rootDir, selectedPlatforms);
4006
+ mutationSnapshots = await snapshotFiles(managedFiles);
4007
+ directorySnapshots = await snapshotDirectories(managedFiles, [
4008
+ path14.join(mancodeDir, "workflows"),
4009
+ path14.join(mancodeDir, "preseason-reports")
4010
+ ]);
3740
4011
  const team = await detectTeamStatus(rootDir);
3741
- const existingPreferences = wasInitialized ? await readExistingInitPreferences(mancodeDir, installer.name) : {};
4012
+ const existingPreferences = wasInitialized ? await readExistingInitPreferences(mancodeDir, primaryPlatform) : {};
3742
4013
  const initialMinimal = existingPreferences.minimal ?? false;
3743
- const teamModeEnabled = options.team ?? existingPreferences.forceTeamMode ?? team.isTeam;
4014
+ const existingTeamMode = existingPreferences.forceTeamMode === true ? "on" : existingPreferences.teamMode ?? "auto";
4015
+ const teamModeEnabled = options.team ?? (existingTeamMode === "on" ? true : existingTeamMode === "off" ? false : team.isTeam);
3744
4016
  if (options.team === true) {
3745
- console.log(" team: forced on (--team)");
4017
+ console.log(
4018
+ localize(
4019
+ locale,
4020
+ " \u56E2\u961F\u6A21\u5F0F\uFF1A\u5F3A\u5236\u5F00\u542F\uFF08--team\uFF09",
4021
+ " team: forced on (--team)"
4022
+ )
4023
+ );
3746
4024
  } else if (options.team === false) {
3747
- console.log(" team: forced off (--no-team)");
4025
+ console.log(
4026
+ localize(
4027
+ locale,
4028
+ " \u56E2\u961F\u6A21\u5F0F\uFF1A\u5F3A\u5236\u5173\u95ED\uFF08--no-team\uFF09",
4029
+ " team: forced off (--no-team)"
4030
+ )
4031
+ );
3748
4032
  } else if (team.isTeam) {
3749
4033
  console.log(
3750
- ` team: ${team.contributors} contributors (/manteam available)`
4034
+ localize(
4035
+ locale,
4036
+ ` \u56E2\u961F\u6A21\u5F0F\uFF1A${team.contributors} \u4F4D\u8D21\u732E\u8005\uFF08\u53EF\u4F7F\u7528 /manteam\uFF09`,
4037
+ ` team: ${team.contributors} contributors (/manteam available)`
4038
+ )
3751
4039
  );
3752
4040
  }
3753
4041
  const state = {
3754
4042
  version: VERSION,
3755
4043
  currentMode: "solo",
3756
4044
  lastMode: "solo",
3757
- platform: installer.name,
4045
+ platform: primaryPlatform,
3758
4046
  initializedAt: (/* @__PURE__ */ new Date()).toISOString(),
3759
4047
  techStack: techStackStr,
3760
4048
  uiLibrary: uiLibraryStr,
@@ -3762,18 +4050,19 @@ async function init(rootDir = process4.cwd(), options = {}) {
3762
4050
  currentWorkflowMode: null,
3763
4051
  skippedSteps: [],
3764
4052
  teamModeAutoDetected: teamModeEnabled,
3765
- contributors: team.contributors
4053
+ contributors: team.contributors,
4054
+ projectMode: isGenericProject ? "generic" : "detected"
3766
4055
  };
3767
- if (installer.name === "claude-code") {
4056
+ if (selectedPlatforms.includes("claude-code")) {
3768
4057
  await validateClaudeCodeSettings(rootDir);
3769
4058
  }
3770
4059
  await installMancodeCore(rootDir);
3771
- await fs2.mkdir(mancodeDir, { recursive: true });
4060
+ await fs3.mkdir(mancodeDir, { recursive: true });
3772
4061
  const stateContent = `${JSON.stringify(state, null, 2)}
3773
4062
  `;
3774
- await fs2.writeFile(stateFile, stateContent, "utf-8");
3775
- await fs2.writeFile(
3776
- path13.join(mancodeDir, "project-profile.json"),
4063
+ await fs3.writeFile(stateFile, stateContent, "utf-8");
4064
+ await fs3.writeFile(
4065
+ path14.join(mancodeDir, "project-profile.json"),
3777
4066
  `${JSON.stringify(profile, null, 2)}
3778
4067
  `,
3779
4068
  "utf-8"
@@ -3782,24 +4071,40 @@ async function init(rootDir = process4.cwd(), options = {}) {
3782
4071
  mancodeDir,
3783
4072
  {
3784
4073
  forceTeamMode: options.team === void 0 && wasInitialized ? void 0 : options.team === true,
4074
+ teamMode: options.team === void 0 && wasInitialized ? void 0 : options.team === true ? "on" : options.team === false ? "off" : "auto",
3785
4075
  defaultStyle: options.style === void 0 && wasInitialized ? void 0 : options.style ?? null,
3786
- platforms: [installer.name],
4076
+ platforms: selectedPlatforms,
3787
4077
  platformOptions: {
3788
- [installer.name]: { minimal: initialMinimal }
4078
+ ...Object.fromEntries(
4079
+ selectedPlatforms.map((platform) => [
4080
+ platform,
4081
+ { minimal: initialMinimal }
4082
+ ])
4083
+ )
3789
4084
  }
3790
4085
  },
3791
4086
  wasInitialized
3792
4087
  );
3793
- let styleLine = " .mancode/aesthetics/ # style-tokens.json (\u7A7A)";
4088
+ let styleLine = localize(
4089
+ locale,
4090
+ " .mancode/aesthetics/ # style-tokens.json\uFF08\u7A7A\uFF09",
4091
+ " .mancode/aesthetics/ # style-tokens.json (empty)"
4092
+ );
3794
4093
  if (profile.uiAssets === "detected") {
3795
- console.log("\u2713 \u626B\u63CF\u5BA1\u7F8E token...");
4094
+ console.log(
4095
+ localize(
4096
+ locale,
4097
+ "\u2713 \u626B\u63CF\u5BA1\u7F8E token...",
4098
+ "\u2713 Scanning design tokens..."
4099
+ )
4100
+ );
3796
4101
  const tokens = await scanAesthetics(rootDir, uiLibrary);
3797
- const tokensPath = path13.join(
4102
+ const tokensPath = path14.join(
3798
4103
  mancodeDir,
3799
4104
  "aesthetics",
3800
4105
  "style-tokens.json"
3801
4106
  );
3802
- await fs2.writeFile(
4107
+ await fs3.writeFile(
3803
4108
  tokensPath,
3804
4109
  `${JSON.stringify(tokens, null, 2)}
3805
4110
  `,
@@ -3809,70 +4114,146 @@ async function init(rootDir = process4.cwd(), options = {}) {
3809
4114
  const colorCount = Object.keys(tokens.colors).length;
3810
4115
  const fontCount = Object.keys(tokens.fonts).length;
3811
4116
  console.log(
3812
- ` ${colorCount} colors, ${fontCount} fonts (match: high)`
4117
+ localize(
4118
+ locale,
4119
+ ` ${colorCount} \u4E2A\u989C\u8272\uFF0C${fontCount} \u4E2A\u5B57\u4F53\uFF08\u5339\u914D\u5EA6\uFF1A\u9AD8\uFF09`,
4120
+ ` ${colorCount} colors, ${fontCount} fonts (match: high)`
4121
+ )
4122
+ );
4123
+ styleLine = localize(
4124
+ locale,
4125
+ ` .mancode/aesthetics/ # style-tokens.json\uFF08${colorCount} \u4E2A\u989C\u8272\uFF09`,
4126
+ ` .mancode/aesthetics/ # style-tokens.json (${colorCount} colors)`
3813
4127
  );
3814
- styleLine = ` .mancode/aesthetics/ # style-tokens.json (${colorCount} colors)`;
3815
4128
  } else if (tokens.matchLevel === "low") {
3816
4129
  console.log(
3817
- " UI assets detected, no reusable tokens found (match: low)"
4130
+ localize(
4131
+ locale,
4132
+ " \u5DF2\u68C0\u6D4B\u5230 UI \u8D44\u6E90\uFF0C\u672A\u627E\u5230\u53EF\u590D\u7528 token\uFF08\u5339\u914D\u5EA6\uFF1A\u4F4E\uFF09",
4133
+ " UI assets detected; no reusable tokens found (match: low)"
4134
+ )
4135
+ );
4136
+ styleLine = localize(
4137
+ locale,
4138
+ " .mancode/aesthetics/ # style-tokens.json\uFF08\u4F4E\u5339\u914D\uFF09",
4139
+ " .mancode/aesthetics/ # style-tokens.json (low match)"
3818
4140
  );
3819
- styleLine = " .mancode/aesthetics/ # style-tokens.json (low match)";
3820
4141
  } else {
3821
- console.log(" No design tokens found (match: none)");
3822
- styleLine = " .mancode/aesthetics/ # style-tokens.json (no tokens)";
4142
+ console.log(
4143
+ localize(
4144
+ locale,
4145
+ " \u672A\u627E\u5230\u8BBE\u8BA1 token\uFF08\u65E0\u5339\u914D\uFF09",
4146
+ " No design tokens found (match: none)"
4147
+ )
4148
+ );
4149
+ styleLine = localize(
4150
+ locale,
4151
+ " .mancode/aesthetics/ # style-tokens.json\uFF08\u65E0 token\uFF09",
4152
+ " .mancode/aesthetics/ # style-tokens.json (no tokens)"
4153
+ );
3823
4154
  }
3824
4155
  }
3825
- console.log(`\u2713 \u5B89\u88C5 ${installer.displayName} \u9002\u914D...`);
3826
- await installer.install(rootDir, {
3827
- techStack: profileStack,
3828
- uiLibrary,
3829
- projectProfile: profile,
3830
- minimal: initialMinimal,
3831
- force: options.force
3832
- });
4156
+ for (const installer of typedInstallers) {
4157
+ console.log(
4158
+ localize(
4159
+ locale,
4160
+ `\u2713 \u5B89\u88C5 ${installer.displayName} \u9002\u914D\u5668...`,
4161
+ `\u2713 Installing ${installer.displayName} adapter...`
4162
+ )
4163
+ );
4164
+ await installer.install(rootDir, {
4165
+ techStack: profileStack,
4166
+ uiLibrary,
4167
+ projectProfile: profile,
4168
+ minimal: initialMinimal,
4169
+ force: options.force
4170
+ });
4171
+ }
3833
4172
  console.log("");
3834
- console.log("\u2713 mancode initialized.");
4173
+ console.log(
4174
+ localize(locale, "\u2713 mancode \u521D\u59CB\u5316\u5B8C\u6210\u3002", "\u2713 mancode initialized.")
4175
+ );
3835
4176
  console.log("");
3836
- console.log("Created:");
3837
- console.log(" .mancode/state.json # \u9879\u76EE\u72B6\u6001");
3838
- console.log(" .mancode/project-profile.json # \u68C0\u6D4B\u5230\u7684\u9879\u76EE\u4E8B\u5B9E");
3839
- console.log(" .mancode/config.json # \u914D\u7F6E");
4177
+ console.log(localize(locale, "\u5DF2\u521B\u5EFA\uFF1A", "Created:"));
3840
4178
  console.log(
3841
- " .mancode/hooks/ # SessionStart + UserPromptSubmit"
4179
+ localize(
4180
+ locale,
4181
+ " .mancode/state.json # \u9879\u76EE\u72B6\u6001",
4182
+ " .mancode/state.json # project state"
4183
+ )
4184
+ );
4185
+ console.log(
4186
+ localize(
4187
+ locale,
4188
+ " .mancode/project-profile.json # \u68C0\u6D4B\u5230\u7684\u9879\u76EE\u4E8B\u5B9E",
4189
+ " .mancode/project-profile.json # detected project facts"
4190
+ )
4191
+ );
4192
+ console.log(
4193
+ localize(
4194
+ locale,
4195
+ " .mancode/config.json # \u914D\u7F6E",
4196
+ " .mancode/config.json # configuration"
4197
+ )
4198
+ );
4199
+ console.log(
4200
+ localize(
4201
+ locale,
4202
+ " .mancode/hooks/ # SessionStart + UserPromptSubmit",
4203
+ " .mancode/hooks/ # SessionStart + UserPromptSubmit"
4204
+ )
3842
4205
  );
3843
4206
  console.log(styleLine);
3844
4207
  console.log(" .mancode/logs/ # hooks.log");
3845
- printPlatformCreatedFiles(installer.name);
4208
+ for (const platform of selectedPlatforms)
4209
+ printPlatformCreatedFiles(platform, locale);
3846
4210
  console.log("");
3847
- console.log("Next:");
3848
- console.log(" mancode status # Show project state");
3849
- if (installer.name === "claude-code") {
3850
- console.log(" (Restart Claude Code to load hooks)");
3851
- } else if (installer.name === "codex") {
4211
+ console.log(localize(locale, "\u4E0B\u4E00\u6B65\uFF1A", "Next:"));
4212
+ console.log(
4213
+ localize(
4214
+ locale,
4215
+ " mancode status # \u663E\u793A\u9879\u76EE\u72B6\u6001",
4216
+ " mancode status # Show project state"
4217
+ )
4218
+ );
4219
+ if (selectedPlatforms.includes("claude-code")) {
3852
4220
  console.log(
3853
- " (If skills do not appear, restart the ChatGPT desktop app or Codex session)"
4221
+ localize(
4222
+ locale,
4223
+ " \uFF08\u91CD\u542F Claude Code \u4EE5\u52A0\u8F7D hooks\uFF09",
4224
+ " (Restart Claude Code to load hooks)"
4225
+ )
4226
+ );
4227
+ }
4228
+ if (selectedPlatforms.includes("codex")) {
4229
+ console.log(
4230
+ localize(
4231
+ locale,
4232
+ " \uFF08\u5982\u679C skills \u672A\u51FA\u73B0\uFF0C\u8BF7\u91CD\u542F ChatGPT \u684C\u9762\u5E94\u7528\u6216 Codex \u4F1A\u8BDD\uFF09",
4233
+ " (If skills do not appear, restart the ChatGPT desktop app or Codex session)"
4234
+ )
3854
4235
  );
3855
4236
  }
3856
4237
  return EXIT_OK;
3857
4238
  } catch (err) {
3858
- if (wasInitialized) {
3859
- await restoreFiles(reinstallSnapshots);
3860
- } else {
3861
- await fs2.rm(stateFile, { force: true });
3862
- await fs2.rm(path13.join(mancodeDir, "project-profile.json"), {
3863
- force: true
3864
- });
3865
- }
4239
+ await restoreFiles(mutationSnapshots);
4240
+ await removeNewEmptyDirectories(directorySnapshots);
3866
4241
  const msg = err instanceof Error ? err.message : String(err);
3867
- console.error(`\u2717 mancode init failed: ${msg}`);
4242
+ console.error(
4243
+ localize(
4244
+ locale,
4245
+ `\u2717 mancode \u521D\u59CB\u5316\u5931\u8D25\uFF1A${msg}`,
4246
+ `\u2717 mancode init failed: ${msg}`
4247
+ )
4248
+ );
3868
4249
  return EXIT_INIT_FAILED;
3869
4250
  }
3870
4251
  }
3871
4252
  async function updateConfigOptions(mancodeDir, patch, preserveInstalledPlatforms) {
3872
- const configPath = path13.join(mancodeDir, "config.json");
4253
+ const configPath = path14.join(mancodeDir, "config.json");
3873
4254
  let config = {};
3874
4255
  try {
3875
- config = JSON.parse(await fs2.readFile(configPath, "utf-8"));
4256
+ config = JSON.parse(await fs3.readFile(configPath, "utf-8"));
3876
4257
  } catch {
3877
4258
  }
3878
4259
  const existingPlatforms = Array.isArray(config.platforms) ? config.platforms.filter(
@@ -3892,7 +4273,7 @@ async function updateConfigOptions(mancodeDir, patch, preserveInstalledPlatforms
3892
4273
  ...patch.platformOptions
3893
4274
  }
3894
4275
  } : definedPatch;
3895
- await fs2.writeFile(
4276
+ await fs3.writeFile(
3896
4277
  configPath,
3897
4278
  `${JSON.stringify({ ...config, ...mergedPatch }, null, 2)}
3898
4279
  `,
@@ -3902,36 +4283,182 @@ async function updateConfigOptions(mancodeDir, patch, preserveInstalledPlatforms
3902
4283
  async function readExistingInitPreferences(mancodeDir, platform) {
3903
4284
  try {
3904
4285
  const config = JSON.parse(
3905
- await fs2.readFile(path13.join(mancodeDir, "config.json"), "utf-8")
4286
+ await fs3.readFile(path14.join(mancodeDir, "config.json"), "utf-8")
3906
4287
  );
3907
4288
  const preferences = {};
3908
4289
  if (typeof config.forceTeamMode === "boolean") {
3909
4290
  preferences.forceTeamMode = config.forceTeamMode;
3910
4291
  }
3911
- if (Array.isArray(config.platforms) && config.platforms.includes(platform) && isRecord3(config.platformOptions)) {
3912
- const platformOptions = config.platformOptions[platform];
3913
- if (isRecord3(platformOptions) && platformOptions.minimal === true) {
3914
- preferences.minimal = true;
3915
- }
4292
+ if (config.teamMode === "auto" || config.teamMode === "on" || config.teamMode === "off") {
4293
+ preferences.teamMode = config.teamMode;
4294
+ }
4295
+ if (Array.isArray(config.platforms) && config.platforms.includes(platform) && isRecord3(config.platformOptions)) {
4296
+ const platformOptions = config.platformOptions[platform];
4297
+ if (isRecord3(platformOptions) && platformOptions.minimal === true) {
4298
+ preferences.minimal = true;
4299
+ }
4300
+ }
4301
+ return preferences;
4302
+ } catch {
4303
+ return {};
4304
+ }
4305
+ }
4306
+ async function readExistingInitPlatform(stateFile) {
4307
+ try {
4308
+ const state = JSON.parse(await fs3.readFile(stateFile, "utf-8"));
4309
+ return typeof state.platform === "string" && getPlatformInstaller(state.platform) ? state.platform : null;
4310
+ } catch {
4311
+ return null;
4312
+ }
4313
+ }
4314
+ async function selectInitPlatforms(input) {
4315
+ if (input.option !== void 0) {
4316
+ return parsePlatformSelection(input.option);
4317
+ }
4318
+ if (input.existingPlatform) return [input.existingPlatform];
4319
+ if (input.yes && input.interactive !== void 0) {
4320
+ return input.hints.length === 1 ? input.hints : null;
4321
+ }
4322
+ if (input.interactive) {
4323
+ const prompter = input.prompter ?? createTerminalPrompter();
4324
+ return prompter.selectPlatforms({
4325
+ locale: input.locale,
4326
+ detected: input.hints
4327
+ });
4328
+ }
4329
+ if (input.interactive === false) {
4330
+ return input.hints.length === 1 ? input.hints : null;
4331
+ }
4332
+ return [DEFAULT_INIT_PLATFORM];
4333
+ }
4334
+ async function hasProjectManifest(rootDir) {
4335
+ for (const name of PROJECT_MANIFESTS) {
4336
+ if (await pathExists3(path14.join(rootDir, name))) return true;
4337
+ }
4338
+ return false;
4339
+ }
4340
+ async function canInitializeGenericProject(rootDir) {
4341
+ const resolved = path14.resolve(rootDir);
4342
+ if (resolved === path14.parse(resolved).root || resolved === path14.resolve(os.homedir())) {
4343
+ return { ok: false, reason: "unsafe" };
4344
+ }
4345
+ try {
4346
+ const entries = await fs3.readdir(resolved);
4347
+ const meaningfulEntries = entries.filter(
4348
+ (entry) => ![".DS_Store", "Thumbs.db", ".gitkeep"].includes(entry)
4349
+ );
4350
+ return meaningfulEntries.length === 0 ? { ok: true } : { ok: false, reason: "nonempty" };
4351
+ } catch {
4352
+ return { ok: false, reason: "unsafe" };
4353
+ }
4354
+ }
4355
+ function printNotProjectDirectory(rootDir, locale, reason) {
4356
+ if (locale === "zh-CN") {
4357
+ console.error(`\u2717 \u5F53\u524D\u76EE\u5F55\u4E0D\u662F\u53EF\u521D\u59CB\u5316\u7684\u9879\u76EE\u76EE\u5F55\uFF1A${rootDir}`);
4358
+ if (reason === "unsafe") {
4359
+ console.error(" \u4E3A\u907F\u514D\u8BEF\u5199\uFF0C\u4E0D\u80FD\u5728 Home \u76EE\u5F55\u6216\u78C1\u76D8\u6839\u76EE\u5F55\u521D\u59CB\u5316\u3002");
4360
+ } else if (reason === "nonempty") {
4361
+ console.error(
4362
+ " \u672A\u8BC6\u522B\u5230\u9879\u76EE\u6587\u4EF6\uFF0C\u4E14\u76EE\u5F55\u4E2D\u5DF2\u6709\u6587\u4EF6\u3002\u8BF7\u5148\u8FDB\u5165\u9879\u76EE\u76EE\u5F55\u3002"
4363
+ );
4364
+ } else {
4365
+ console.error(
4366
+ " \u672A\u53D1\u73B0 .git \u6216\u9879\u76EE\u6587\u4EF6\u3002\u4EA4\u4E92\u7EC8\u7AEF\u4E2D\u53EF\u786E\u8BA4\u901A\u7528\u9879\u76EE\uFF0C\u6216\u4F7F\u7528 --empty\u3002"
4367
+ );
4368
+ }
4369
+ return;
4370
+ }
4371
+ console.error(`\u2717 Not a project directory: ${rootDir}`);
4372
+ if (reason === "unsafe") {
4373
+ console.error(
4374
+ " Refusing to initialize a home directory or filesystem root."
4375
+ );
4376
+ } else if (reason === "nonempty") {
4377
+ console.error(
4378
+ " No project files were detected and the directory is not empty."
4379
+ );
4380
+ } else {
4381
+ console.error(
4382
+ " No .git or recognized project manifest found. Use an interactive terminal or --empty for a new project."
4383
+ );
4384
+ }
4385
+ }
4386
+ function getInitManagedFilePaths(rootDir, platforms) {
4387
+ const files = [
4388
+ ".mancode/state.json",
4389
+ ".mancode/config.json",
4390
+ ".mancode/project-profile.json",
4391
+ ".mancode/aesthetics/style-tokens.json",
4392
+ ".mancode/hooks/session-start.mjs",
4393
+ ".mancode/hooks/user-prompt-submit.mjs",
4394
+ ".mancode/logs/hooks.log",
4395
+ ".mancode/memory/prd.md",
4396
+ ".mancode/memory/spec.md",
4397
+ ".mancode/memory/decisions.md"
4398
+ ];
4399
+ if (platforms.includes("claude-code")) {
4400
+ files.push(
4401
+ ".mancode/hooks/session-start.sh",
4402
+ ".mancode/hooks/user-prompt-submit.sh",
4403
+ ".claude/settings.json",
4404
+ ".claude/skills/man8/SKILL.md",
4405
+ ".claude/skills/solo/SKILL.md",
4406
+ ".claude/skills/mancode-solo.md",
4407
+ ".claude/skills/mancode-man8.md"
4408
+ );
4409
+ for (const mode of MODE_NAMES) {
4410
+ files.push(
4411
+ `.claude/skills/${mode}/SKILL.md`,
4412
+ `.claude/skills/mancode-${mode}.md`
4413
+ );
4414
+ }
4415
+ for (const agent of ALL_AGENTS) {
4416
+ files.push(`.claude/agents/${agent.name}.md`);
3916
4417
  }
3917
- return preferences;
3918
- } catch {
3919
- return {};
3920
4418
  }
3921
- }
3922
- async function readExistingInitPlatform(stateFile) {
3923
- try {
3924
- const state = JSON.parse(await fs2.readFile(stateFile, "utf-8"));
3925
- return typeof state.platform === "string" && getPlatformInstaller(state.platform) ? state.platform : null;
3926
- } catch {
3927
- return null;
4419
+ if (platforms.includes("cursor")) {
4420
+ files.push(".cursor/rules/mancode-man8.mdc", ".cursor/commands/man8.md");
4421
+ for (const fileName of MANCODE_CURSOR_RULE_FILES) {
4422
+ files.push(`.cursor/rules/${fileName}`);
4423
+ }
4424
+ for (const mode of MODE_NAMES) {
4425
+ files.push(`.cursor/commands/${mode}.md`);
4426
+ }
4427
+ }
4428
+ if (platforms.includes("codex") || platforms.includes("zcode")) {
4429
+ files.push("AGENTS.md", ".agents/skills/man8/SKILL.md");
4430
+ for (const mode of MODE_NAMES) {
4431
+ files.push(`.agents/skills/${mode}/SKILL.md`);
4432
+ }
4433
+ }
4434
+ if (platforms.includes("codex")) {
4435
+ files.push(".codex/skills/man8/SKILL.md");
4436
+ for (const mode of MODE_NAMES) {
4437
+ files.push(`.codex/skills/${mode}/SKILL.md`);
4438
+ }
4439
+ }
4440
+ if (platforms.includes("zcode")) {
4441
+ files.push(".zcode/skills/man8/SKILL.md");
4442
+ for (const mode of MODE_NAMES) {
4443
+ files.push(`.zcode/skills/${mode}/SKILL.md`);
4444
+ }
4445
+ }
4446
+ if (platforms.includes("copilot")) {
4447
+ files.push(
4448
+ ".github/copilot-instructions.md",
4449
+ ".github/prompts/man8.prompt.md"
4450
+ );
4451
+ for (const mode of MODE_NAMES) {
4452
+ files.push(`.github/prompts/${mode}.prompt.md`);
4453
+ }
3928
4454
  }
4455
+ return [...new Set(files.map((file) => path14.join(rootDir, file)))];
3929
4456
  }
3930
4457
  async function snapshotFiles(filePaths) {
3931
4458
  return Promise.all(
3932
4459
  filePaths.map(async (filePath) => {
3933
4460
  try {
3934
- return { filePath, content: await fs2.readFile(filePath, "utf-8") };
4461
+ return { filePath, content: await fs3.readFile(filePath, "utf-8") };
3935
4462
  } catch (error) {
3936
4463
  if (isNodeError3(error) && error.code === "ENOENT") {
3937
4464
  return { filePath, content: null };
@@ -3941,14 +4468,39 @@ async function snapshotFiles(filePaths) {
3941
4468
  })
3942
4469
  );
3943
4470
  }
4471
+ async function snapshotDirectories(filePaths, additionalDirectories = []) {
4472
+ const directories = new Set(additionalDirectories);
4473
+ for (const filePath of filePaths) {
4474
+ let current = path14.dirname(filePath);
4475
+ while (current !== path14.dirname(current)) {
4476
+ directories.add(current);
4477
+ current = path14.dirname(current);
4478
+ }
4479
+ }
4480
+ return Promise.all(
4481
+ [...directories].map(async (dirPath) => ({
4482
+ dirPath,
4483
+ existed: await pathExists3(dirPath)
4484
+ }))
4485
+ );
4486
+ }
3944
4487
  async function restoreFiles(snapshots) {
3945
4488
  for (const snapshot of snapshots) {
3946
4489
  if (snapshot.content === null) {
3947
- await fs2.rm(snapshot.filePath, { force: true });
4490
+ await fs3.rm(snapshot.filePath, { force: true });
3948
4491
  continue;
3949
4492
  }
3950
- await fs2.mkdir(path13.dirname(snapshot.filePath), { recursive: true });
3951
- await fs2.writeFile(snapshot.filePath, snapshot.content, "utf-8");
4493
+ await fs3.mkdir(path14.dirname(snapshot.filePath), { recursive: true });
4494
+ await fs3.writeFile(snapshot.filePath, snapshot.content, "utf-8");
4495
+ }
4496
+ }
4497
+ async function removeNewEmptyDirectories(snapshots) {
4498
+ const candidates = snapshots.filter((snapshot) => !snapshot.existed).sort((a, b) => b.dirPath.length - a.dirPath.length);
4499
+ for (const { dirPath } of candidates) {
4500
+ try {
4501
+ await fs3.rmdir(dirPath);
4502
+ } catch {
4503
+ }
3952
4504
  }
3953
4505
  }
3954
4506
  function isRecord3(value) {
@@ -3957,10 +4509,22 @@ function isRecord3(value) {
3957
4509
  function isNodeError3(error) {
3958
4510
  return error instanceof Error && "code" in error;
3959
4511
  }
3960
- function printPlatformCreatedFiles(platform) {
4512
+ function printPlatformCreatedFiles(platform, locale) {
3961
4513
  if (platform === "claude-code") {
3962
- console.log(" .claude/settings.json # hook \u6CE8\u518C");
3963
- console.log(" .claude/skills/ # solo + MVP-2 slash skills");
4514
+ console.log(
4515
+ localize(
4516
+ locale,
4517
+ " .claude/settings.json # hook \u6CE8\u518C",
4518
+ " .claude/settings.json # hook registration"
4519
+ )
4520
+ );
4521
+ console.log(
4522
+ localize(
4523
+ locale,
4524
+ " .claude/skills/ # solo + MVP-2 slash skills",
4525
+ " .claude/skills/ # solo + MVP-2 slash skills"
4526
+ )
4527
+ );
3964
4528
  return;
3965
4529
  }
3966
4530
  if (platform === "cursor") {
@@ -3979,9 +4543,12 @@ function printPlatformCreatedFiles(platform) {
3979
4543
  }
3980
4544
  console.log(" .github/copilot-instructions.md # Copilot instructions");
3981
4545
  }
4546
+ function localize(locale, chinese, english) {
4547
+ return locale === "zh-CN" ? chinese : english;
4548
+ }
3982
4549
  async function pathExists3(p) {
3983
4550
  try {
3984
- await fs2.access(p);
4551
+ await fs3.access(p);
3985
4552
  return true;
3986
4553
  } catch {
3987
4554
  return false;
@@ -3989,13 +4556,13 @@ async function pathExists3(p) {
3989
4556
  }
3990
4557
 
3991
4558
  // src/commands/install.ts
3992
- import { promises as fs4 } from "fs";
3993
- import path15 from "path";
3994
- import process5 from "process";
4559
+ import { promises as fs5 } from "fs";
4560
+ import path16 from "path";
4561
+ import process6 from "process";
3995
4562
 
3996
4563
  // src/installers/platform-status.ts
3997
- import { promises as fs3 } from "fs";
3998
- import path14 from "path";
4564
+ import { promises as fs4 } from "fs";
4565
+ import path15 from "path";
3999
4566
  async function checkPlatformStatus(rootDir, platform, installed) {
4000
4567
  const readiness = await checkPlatformReadiness(rootDir, platform);
4001
4568
  return {
@@ -4009,13 +4576,13 @@ async function checkPlatformReadiness(rootDir, platform) {
4009
4576
  if (platform === "claude-code") {
4010
4577
  const [hasSoloSkill, registered, hasHookFiles] = await Promise.all([
4011
4578
  fileMatches(
4012
- path14.join(rootDir, ".claude", "skills", "solo", "SKILL.md"),
4579
+ path15.join(rootDir, ".claude", "skills", "solo", "SKILL.md"),
4013
4580
  (content) => isGeneratedClaudeSkill(content, "solo")
4014
4581
  ),
4015
4582
  claudeHooksRegistered(rootDir),
4016
4583
  pathsExist([
4017
- path14.join(rootDir, ".mancode", "hooks", "session-start.mjs"),
4018
- path14.join(rootDir, ".mancode", "hooks", "user-prompt-submit.mjs")
4584
+ path15.join(rootDir, ".mancode", "hooks", "session-start.mjs"),
4585
+ path15.join(rootDir, ".mancode", "hooks", "user-prompt-submit.mjs")
4019
4586
  ])
4020
4587
  ]);
4021
4588
  const present = hasSoloSkill && registered && hasHookFiles;
@@ -4031,7 +4598,7 @@ async function checkPlatformReadiness(rootDir, platform) {
4031
4598
  if (platform === "cursor") {
4032
4599
  const hasCoreRules = await allManagedSkills(
4033
4600
  MANCODE_CURSOR_CORE_RULE_FILES.map(
4034
- (file) => path14.join(rootDir, ".cursor", "rules", file)
4601
+ (file) => path15.join(rootDir, ".cursor", "rules", file)
4035
4602
  ),
4036
4603
  [CURSOR_RULE_MANAGED_MARKER]
4037
4604
  );
@@ -4046,13 +4613,13 @@ async function checkPlatformReadiness(rootDir, platform) {
4046
4613
  const [hasRules, hasCommands] = await Promise.all([
4047
4614
  allManagedSkills(
4048
4615
  MANCODE_CURSOR_RULE_FILES.map(
4049
- (file) => path14.join(rootDir, ".cursor", "rules", file)
4616
+ (file) => path15.join(rootDir, ".cursor", "rules", file)
4050
4617
  ),
4051
4618
  [CURSOR_RULE_MANAGED_MARKER]
4052
4619
  ),
4053
4620
  allManagedSkills(
4054
4621
  MODE_NAMES.map(
4055
- (mode) => path14.join(rootDir, ".cursor", "commands", `${mode}.md`)
4622
+ (mode) => path15.join(rootDir, ".cursor", "commands", `${mode}.md`)
4056
4623
  ),
4057
4624
  [MODE_FILE_MANAGED_MARKER]
4058
4625
  )
@@ -4065,7 +4632,7 @@ async function checkPlatformReadiness(rootDir, platform) {
4065
4632
  };
4066
4633
  }
4067
4634
  if (platform === "codex") {
4068
- const hasBlock2 = await fileHasManagedBlock(path14.join(rootDir, "AGENTS.md"));
4635
+ const hasBlock2 = await fileHasManagedBlock(path15.join(rootDir, "AGENTS.md"));
4069
4636
  if (!hasBlock2) {
4070
4637
  return {
4071
4638
  present: false,
@@ -4082,9 +4649,9 @@ async function checkPlatformReadiness(rootDir, platform) {
4082
4649
  readyDetail: "managed block present"
4083
4650
  };
4084
4651
  }
4085
- const skillsDir = path14.join(rootDir, ".agents", "skills");
4652
+ const skillsDir = path15.join(rootDir, ".agents", "skills");
4086
4653
  const hasSkills = await allManagedSkills(
4087
- MODE_NAMES.map((mode) => path14.join(skillsDir, mode, "SKILL.md")),
4654
+ MODE_NAMES.map((mode) => path15.join(skillsDir, mode, "SKILL.md")),
4088
4655
  MANCODE_AGENT_SKILL_MARKERS
4089
4656
  );
4090
4657
  return {
@@ -4096,7 +4663,7 @@ async function checkPlatformReadiness(rootDir, platform) {
4096
4663
  }
4097
4664
  if (platform === "zcode") {
4098
4665
  const hasBlock2 = await fileHasManagedBlock(
4099
- path14.join(rootDir, "AGENTS.md"),
4666
+ path15.join(rootDir, "AGENTS.md"),
4100
4667
  ZCODE_MANCODE_START_MARKER,
4101
4668
  ZCODE_MANCODE_END_MARKER
4102
4669
  );
@@ -4116,9 +4683,9 @@ async function checkPlatformReadiness(rootDir, platform) {
4116
4683
  readyDetail: "managed block present"
4117
4684
  };
4118
4685
  }
4119
- const skillsDir = path14.join(rootDir, ".agents", "skills");
4686
+ const skillsDir = path15.join(rootDir, ".agents", "skills");
4120
4687
  const hasSkills = await allManagedSkills(
4121
- MODE_NAMES.map((mode) => path14.join(skillsDir, mode, "SKILL.md")),
4688
+ MODE_NAMES.map((mode) => path15.join(skillsDir, mode, "SKILL.md")),
4122
4689
  MANCODE_AGENT_SKILL_MARKERS
4123
4690
  );
4124
4691
  return {
@@ -4129,7 +4696,7 @@ async function checkPlatformReadiness(rootDir, platform) {
4129
4696
  };
4130
4697
  }
4131
4698
  const hasBlock = await fileHasManagedBlock(
4132
- path14.join(rootDir, ".github", "copilot-instructions.md")
4699
+ path15.join(rootDir, ".github", "copilot-instructions.md")
4133
4700
  );
4134
4701
  if (!hasBlock) {
4135
4702
  return {
@@ -4140,9 +4707,9 @@ async function checkPlatformReadiness(rootDir, platform) {
4140
4707
  };
4141
4708
  }
4142
4709
  if (!await isPlatformMinimal(rootDir, "copilot")) {
4143
- const promptsDir = path14.join(rootDir, ".github", "prompts");
4710
+ const promptsDir = path15.join(rootDir, ".github", "prompts");
4144
4711
  const hasPrompts = await allManagedSkills(
4145
- MODE_NAMES.map((mode) => path14.join(promptsDir, `${mode}.prompt.md`)),
4712
+ MODE_NAMES.map((mode) => path15.join(promptsDir, `${mode}.prompt.md`)),
4146
4713
  [MODE_FILE_MANAGED_MARKER]
4147
4714
  );
4148
4715
  return {
@@ -4175,13 +4742,13 @@ async function allManagedSkills(paths, markers) {
4175
4742
  async function claudeFullContentReady(rootDir) {
4176
4743
  const skillChecks = MVP2_SKILLS.map(
4177
4744
  (skill) => fileMatches(
4178
- path14.join(rootDir, ".claude", "skills", skill.name, "SKILL.md"),
4745
+ path15.join(rootDir, ".claude", "skills", skill.name, "SKILL.md"),
4179
4746
  (content) => isGeneratedClaudeSkill(content, skill.name)
4180
4747
  )
4181
4748
  );
4182
4749
  const agentChecks = ALL_AGENTS.map(
4183
4750
  (agent) => fileMatches(
4184
- path14.join(rootDir, ".claude", "agents", `${agent.name}.md`),
4751
+ path15.join(rootDir, ".claude", "agents", `${agent.name}.md`),
4185
4752
  (content) => isGeneratedClaudeAgent(content, agent.name)
4186
4753
  )
4187
4754
  );
@@ -4190,7 +4757,7 @@ async function claudeFullContentReady(rootDir) {
4190
4757
  }
4191
4758
  async function fileMatches(filePath, predicate) {
4192
4759
  try {
4193
- return predicate(await fs3.readFile(filePath, "utf-8"));
4760
+ return predicate(await fs4.readFile(filePath, "utf-8"));
4194
4761
  } catch {
4195
4762
  return false;
4196
4763
  }
@@ -4200,7 +4767,7 @@ async function pathsExist(paths) {
4200
4767
  }
4201
4768
  async function fileHasAnyMarker(filePath, needles) {
4202
4769
  try {
4203
- const content = await fs3.readFile(filePath, "utf-8");
4770
+ const content = await fs4.readFile(filePath, "utf-8");
4204
4771
  return needles.some((needle) => content.includes(needle));
4205
4772
  } catch {
4206
4773
  return false;
@@ -4208,8 +4775,8 @@ async function fileHasAnyMarker(filePath, needles) {
4208
4775
  }
4209
4776
  async function isPlatformMinimal(rootDir, platform) {
4210
4777
  try {
4211
- const raw = await fs3.readFile(
4212
- path14.join(rootDir, ".mancode", "config.json"),
4778
+ const raw = await fs4.readFile(
4779
+ path15.join(rootDir, ".mancode", "config.json"),
4213
4780
  "utf-8"
4214
4781
  );
4215
4782
  const config = JSON.parse(raw);
@@ -4222,7 +4789,7 @@ async function isPlatformMinimal(rootDir, platform) {
4222
4789
  }
4223
4790
  async function fileHasManagedBlock(filePath, startMarker = DEFAULT_MANCODE_START_MARKER, endMarker = DEFAULT_MANCODE_END_MARKER) {
4224
4791
  try {
4225
- const content = await fs3.readFile(filePath, "utf-8");
4792
+ const content = await fs4.readFile(filePath, "utf-8");
4226
4793
  return hasManagedBlock(content, startMarker, endMarker);
4227
4794
  } catch {
4228
4795
  return false;
@@ -4230,8 +4797,8 @@ async function fileHasManagedBlock(filePath, startMarker = DEFAULT_MANCODE_START
4230
4797
  }
4231
4798
  async function claudeHooksRegistered(rootDir) {
4232
4799
  try {
4233
- const raw = await fs3.readFile(
4234
- path14.join(rootDir, ".claude", "settings.json"),
4800
+ const raw = await fs4.readFile(
4801
+ path15.join(rootDir, ".claude", "settings.json"),
4235
4802
  "utf-8"
4236
4803
  );
4237
4804
  const settings = JSON.parse(raw);
@@ -4261,7 +4828,7 @@ function hasHookCommand(value, needle) {
4261
4828
  }
4262
4829
  async function pathExists4(p) {
4263
4830
  try {
4264
- await fs3.access(p);
4831
+ await fs4.access(p);
4265
4832
  return true;
4266
4833
  } catch {
4267
4834
  return false;
@@ -4276,8 +4843,8 @@ var EXIT_OK2 = 0;
4276
4843
  var EXIT_NOT_INITIALIZED = 1;
4277
4844
  var EXIT_UNSUPPORTED_PLATFORM = 2;
4278
4845
  var EXIT_INSTALL_FAILED = 3;
4279
- async function install(rootDir = process5.cwd(), platform = "claude-code", options = {}) {
4280
- const stateFile = path15.join(rootDir, ".mancode", "state.json");
4846
+ async function install(rootDir = process6.cwd(), platform = "claude-code", options = {}) {
4847
+ const stateFile = path16.join(rootDir, ".mancode", "state.json");
4281
4848
  if (!await pathExists5(stateFile)) {
4282
4849
  console.error("\u2717 mancode not initialized.");
4283
4850
  console.error(" Run `mancode init` first.");
@@ -4363,9 +4930,9 @@ async function install(rootDir = process5.cwd(), platform = "claude-code", optio
4363
4930
  return EXIT_OK2;
4364
4931
  }
4365
4932
  async function readConfig(rootDir) {
4366
- const configPath = path15.join(rootDir, ".mancode", "config.json");
4933
+ const configPath = path16.join(rootDir, ".mancode", "config.json");
4367
4934
  try {
4368
- const raw = await fs4.readFile(configPath, "utf-8");
4935
+ const raw = await fs5.readFile(configPath, "utf-8");
4369
4936
  return { config: JSON.parse(raw), valid: true };
4370
4937
  } catch (err) {
4371
4938
  if (isNodeError4(err) && err.code === "ENOENT") {
@@ -4378,10 +4945,10 @@ function isNodeError4(err) {
4378
4945
  return err instanceof Error && "code" in err;
4379
4946
  }
4380
4947
  async function updateConfig(rootDir, config) {
4381
- const configPath = path15.join(rootDir, ".mancode", "config.json");
4948
+ const configPath = path16.join(rootDir, ".mancode", "config.json");
4382
4949
  const content = `${JSON.stringify(config, null, 2)}
4383
4950
  `;
4384
- await fs4.writeFile(configPath, content, "utf-8");
4951
+ await fs5.writeFile(configPath, content, "utf-8");
4385
4952
  }
4386
4953
  function withDefaultConfig(config, fallbackPlatform) {
4387
4954
  const fallback = fallbackPlatform ?? DEFAULT_CONFIG.platforms[0];
@@ -4413,8 +4980,8 @@ function readConfiguredMinimal(value, platform) {
4413
4980
  }
4414
4981
  async function readStatePlatform(rootDir) {
4415
4982
  try {
4416
- const raw = await fs4.readFile(
4417
- path15.join(rootDir, ".mancode", "state.json"),
4983
+ const raw = await fs5.readFile(
4984
+ path16.join(rootDir, ".mancode", "state.json"),
4418
4985
  "utf-8"
4419
4986
  );
4420
4987
  const state = JSON.parse(raw);
@@ -4425,7 +4992,7 @@ async function readStatePlatform(rootDir) {
4425
4992
  }
4426
4993
  async function pathExists5(p) {
4427
4994
  try {
4428
- await fs4.access(p);
4995
+ await fs5.access(p);
4429
4996
  return true;
4430
4997
  } catch {
4431
4998
  return false;
@@ -4436,11 +5003,11 @@ function isRecord5(value) {
4436
5003
  }
4437
5004
 
4438
5005
  // src/commands/list-platforms.ts
4439
- import { promises as fs5 } from "fs";
4440
- import path16 from "path";
4441
- import process6 from "process";
5006
+ import { promises as fs6 } from "fs";
5007
+ import path17 from "path";
5008
+ import process7 from "process";
4442
5009
  var EXIT_OK3 = 0;
4443
- async function listPlatforms(rootDir = process6.cwd()) {
5010
+ async function listPlatforms(rootDir = process7.cwd()) {
4444
5011
  const installed = new Set(await readInstalledPlatforms(rootDir));
4445
5012
  const platforms = getPlatformInstallers();
4446
5013
  console.log("");
@@ -4453,8 +5020,8 @@ async function listPlatforms(rootDir = process6.cwd()) {
4453
5020
  }
4454
5021
  async function readInstalledPlatforms(rootDir) {
4455
5022
  try {
4456
- const raw = await fs5.readFile(
4457
- path16.join(rootDir, ".mancode", "config.json"),
5023
+ const raw = await fs6.readFile(
5024
+ path17.join(rootDir, ".mancode", "config.json"),
4458
5025
  "utf-8"
4459
5026
  );
4460
5027
  const config = JSON.parse(raw);
@@ -4479,8 +5046,8 @@ function describePlatform(platform) {
4479
5046
 
4480
5047
  // src/commands/manps.ts
4481
5048
  import { access as access4 } from "fs/promises";
4482
- import path18 from "path";
4483
- import { createInterface } from "readline/promises";
5049
+ import path19 from "path";
5050
+ import { createInterface as createInterface2 } from "readline/promises";
4484
5051
 
4485
5052
  // src/system/preseason.ts
4486
5053
  import { execFile as execFile3 } from "child_process";
@@ -4495,7 +5062,7 @@ import {
4495
5062
  rename,
4496
5063
  writeFile as writeFile9
4497
5064
  } from "fs/promises";
4498
- import path17 from "path";
5065
+ import path18 from "path";
4499
5066
  var PRESEASON_AREAS = [
4500
5067
  "all",
4501
5068
  "deps",
@@ -4575,9 +5142,9 @@ async function runPreseasonScan(projectRoot, area = "all") {
4575
5142
  const needsFiles = normalizedArea === "all" || normalizedArea === "dead-code" || normalizedArea === "config";
4576
5143
  const files = needsFiles ? await listProjectFiles(projectRoot) : [];
4577
5144
  const issues = (await scanArea(projectRoot, normalizedArea, pkg, files)).slice(0, 20);
4578
- const reportDir = path17.join(projectRoot, ".mancode", "preseason-reports");
5145
+ const reportDir = path18.join(projectRoot, ".mancode", "preseason-reports");
4579
5146
  await mkdir7(reportDir, { recursive: true });
4580
- const issueDbPath = path17.join(
5147
+ const issueDbPath = path18.join(
4581
5148
  projectRoot,
4582
5149
  ".mancode",
4583
5150
  "preseason-issues.json"
@@ -4597,7 +5164,7 @@ async function runPreseasonScan(projectRoot, area = "all") {
4597
5164
  const database = await buildIssueDatabase(projectRoot, report);
4598
5165
  await writeFile9(reportPath, renderPreseasonReport(report), "utf-8");
4599
5166
  await writeFile9(
4600
- path17.join(projectRoot, ".mancode", "preseason-report.md"),
5167
+ path18.join(projectRoot, ".mancode", "preseason-report.md"),
4601
5168
  renderPreseasonReport(report),
4602
5169
  "utf-8"
4603
5170
  );
@@ -4605,7 +5172,7 @@ async function runPreseasonScan(projectRoot, area = "all") {
4605
5172
  return report;
4606
5173
  }
4607
5174
  async function runPreseasonRemediation(projectRoot, issues, options = {}) {
4608
- const issueDbPath = path17.join(
5175
+ const issueDbPath = path18.join(
4609
5176
  projectRoot,
4610
5177
  ".mancode",
4611
5178
  "preseason-issues.json"
@@ -4719,7 +5286,7 @@ async function scanArea(projectRoot, area, pkg, files) {
4719
5286
  async function allocateReportPath(reportDir, baseName) {
4720
5287
  for (let attempt = 0; attempt < 1e3; attempt++) {
4721
5288
  const suffix = attempt === 0 ? "" : `-${attempt + 1}`;
4722
- const candidate = path17.join(reportDir, `${baseName}${suffix}.md`);
5289
+ const candidate = path18.join(reportDir, `${baseName}${suffix}.md`);
4723
5290
  if (!existsSync(candidate)) return candidate;
4724
5291
  }
4725
5292
  throw new Error(`unable to allocate preseason report path: ${baseName}`);
@@ -4768,8 +5335,8 @@ async function walk(root, current, results, depth) {
4768
5335
  }
4769
5336
  for (const entry of entries) {
4770
5337
  if (IGNORE_DIRS.has(entry)) continue;
4771
- const abs = path17.join(current, entry);
4772
- const rel = path17.relative(root, abs);
5338
+ const abs = path18.join(current, entry);
5339
+ const rel = path18.relative(root, abs);
4773
5340
  let info;
4774
5341
  try {
4775
5342
  info = await lstat(abs);
@@ -4779,7 +5346,7 @@ async function walk(root, current, results, depth) {
4779
5346
  if (info.isSymbolicLink()) continue;
4780
5347
  if (info.isDirectory()) {
4781
5348
  await walk(root, abs, results, depth + 1);
4782
- } else if (SOURCE_EXTENSIONS.has(path17.extname(entry)) || entry === "package.json") {
5349
+ } else if (SOURCE_EXTENSIONS.has(path18.extname(entry)) || entry === "package.json") {
4783
5350
  results.push(rel);
4784
5351
  if (results.length >= MAX_PROJECT_FILES) return;
4785
5352
  }
@@ -4787,7 +5354,7 @@ async function walk(root, current, results, depth) {
4787
5354
  }
4788
5355
  async function readPackageJson(projectRoot) {
4789
5356
  try {
4790
- const raw = await readFile8(path17.join(projectRoot, "package.json"), "utf-8");
5357
+ const raw = await readFile8(path18.join(projectRoot, "package.json"), "utf-8");
4791
5358
  return JSON.parse(raw);
4792
5359
  } catch {
4793
5360
  return null;
@@ -4880,7 +5447,7 @@ function scanTodos(projectRoot, files) {
4880
5447
  const matches = [];
4881
5448
  for (const file of files) {
4882
5449
  if (matches.length >= 7) break;
4883
- const abs = path17.join(projectRoot, file);
5450
+ const abs = path18.join(projectRoot, file);
4884
5451
  matches.push(...readTodoIssues(abs, file, matches.length));
4885
5452
  }
4886
5453
  return matches.slice(0, 7);
@@ -4917,7 +5484,7 @@ function scanTestGaps(files) {
4917
5484
  if (sourceFiles.length === 0) return [];
4918
5485
  const tests = new Set(files.filter((file) => file.startsWith("tests/")));
4919
5486
  const missing = sourceFiles.filter((file) => {
4920
- const base = path17.basename(file).replace(/\.(ts|tsx|js|jsx)$/, "");
5487
+ const base = path18.basename(file).replace(/\.(ts|tsx|js|jsx)$/, "");
4921
5488
  return !Array.from(tests).some((test) => test.includes(base));
4922
5489
  }).slice(0, 4);
4923
5490
  return missing.map((file, index) => ({
@@ -4926,13 +5493,13 @@ function scanTestGaps(files) {
4926
5493
  type: "tests",
4927
5494
  title: "Core source file has no obvious test",
4928
5495
  file,
4929
- detail: `No matching test file was found for ${path17.basename(file)}.`,
5496
+ detail: `No matching test file was found for ${path18.basename(file)}.`,
4930
5497
  recommendation: "Add focused coverage for the public behavior or document why this module is exercised indirectly."
4931
5498
  }));
4932
5499
  }
4933
5500
  function scanConfig(projectRoot, files) {
4934
5501
  const issues = [];
4935
- if (!files.includes(".gitignore") && !pathExistsSync(path17.join(projectRoot, ".gitignore"))) {
5502
+ if (!files.includes(".gitignore") && !pathExistsSync(path18.join(projectRoot, ".gitignore"))) {
4936
5503
  issues.push({
4937
5504
  id: "config-gitignore",
4938
5505
  severity: "P1",
@@ -4943,7 +5510,7 @@ function scanConfig(projectRoot, files) {
4943
5510
  recommendation: "Add a .gitignore that excludes dependencies, build output, coverage, and local env files."
4944
5511
  });
4945
5512
  }
4946
- if (!files.includes(".editorconfig") && !pathExistsSync(path17.join(projectRoot, ".editorconfig"))) {
5513
+ if (!files.includes(".editorconfig") && !pathExistsSync(path18.join(projectRoot, ".editorconfig"))) {
4947
5514
  issues.push({
4948
5515
  id: "config-editorconfig",
4949
5516
  severity: "P2",
@@ -4962,7 +5529,7 @@ function scanAestheticDrift(projectRoot, files) {
4962
5529
  for (const file of frontendFiles) {
4963
5530
  let content;
4964
5531
  try {
4965
- content = readFileSyncSafe(path17.join(projectRoot, file));
5532
+ content = readFileSyncSafe(path18.join(projectRoot, file));
4966
5533
  } catch {
4967
5534
  continue;
4968
5535
  }
@@ -4982,7 +5549,7 @@ function scanAestheticDrift(projectRoot, files) {
4982
5549
  return issues;
4983
5550
  }
4984
5551
  async function scanArchitecture(projectRoot) {
4985
- const localBinary = path17.join(
5552
+ const localBinary = path18.join(
4986
5553
  projectRoot,
4987
5554
  "node_modules",
4988
5555
  ".bin",
@@ -5030,7 +5597,7 @@ async function hasDependencyCruiserConfig(projectRoot) {
5030
5597
  "dependency-cruiser.config.mjs"
5031
5598
  ];
5032
5599
  const results = await Promise.all(
5033
- files.map((file) => pathExists6(path17.join(projectRoot, file)))
5600
+ files.map((file) => pathExists6(path18.join(projectRoot, file)))
5034
5601
  );
5035
5602
  return results.some(Boolean);
5036
5603
  }
@@ -5056,11 +5623,11 @@ function runDepcruise(binary, projectRoot) {
5056
5623
  maxBuffer: DEPCRUISE_MAX_BUFFER,
5057
5624
  shell: needsShell
5058
5625
  },
5059
- (error, stdout, stderr) => {
5626
+ (error, stdout2, stderr) => {
5060
5627
  if (!error) {
5061
5628
  resolve({
5062
5629
  status: "ok",
5063
- stdout,
5630
+ stdout: stdout2,
5064
5631
  stderr,
5065
5632
  exitCode: 0,
5066
5633
  timedOut: false
@@ -5080,7 +5647,7 @@ function runDepcruise(binary, projectRoot) {
5080
5647
  }
5081
5648
  resolve({
5082
5649
  status: "failed",
5083
- stdout: nodeError.stdout ?? stdout,
5650
+ stdout: nodeError.stdout ?? stdout2,
5084
5651
  stderr: nodeError.stderr ?? stderr,
5085
5652
  exitCode: typeof nodeError.code === "number" ? nodeError.code : null,
5086
5653
  timedOut: Boolean(nodeError.killed || nodeError.signal === "SIGTERM")
@@ -5136,9 +5703,9 @@ function inferCommands(pkg) {
5136
5703
  return ["lint", "test", "build"].filter((name) => scripts[name]).map((name) => `npm run ${name}`);
5137
5704
  }
5138
5705
  async function buildIssueDatabase(projectRoot, report) {
5139
- const reportRef = path17.relative(projectRoot, report.reportPath);
5706
+ const reportRef = path18.relative(projectRoot, report.reportPath);
5140
5707
  const run = {
5141
- id: path17.basename(report.reportPath, ".md"),
5708
+ id: path18.basename(report.reportPath, ".md"),
5142
5709
  generatedAt: report.generatedAt,
5143
5710
  area: report.area,
5144
5711
  reportPath: reportRef,
@@ -5244,7 +5811,7 @@ function compareIssueRecords(a, b) {
5244
5811
  }
5245
5812
  async function applySafeRemediation(projectRoot, issue) {
5246
5813
  if (issue.id === "config-gitignore" && issue.file === ".gitignore") {
5247
- const gitignorePath = path17.join(projectRoot, ".gitignore");
5814
+ const gitignorePath = path18.join(projectRoot, ".gitignore");
5248
5815
  if (pathExistsSync(gitignorePath)) {
5249
5816
  return { applied: false };
5250
5817
  }
@@ -5253,7 +5820,7 @@ async function applySafeRemediation(projectRoot, issue) {
5253
5820
  return { applied: true, action: "created .gitignore" };
5254
5821
  }
5255
5822
  if (issue.id === "config-editorconfig" && issue.file === ".editorconfig") {
5256
- const editorconfigPath = path17.join(projectRoot, ".editorconfig");
5823
+ const editorconfigPath = path18.join(projectRoot, ".editorconfig");
5257
5824
  if (pathExistsSync(editorconfigPath)) {
5258
5825
  return { applied: false };
5259
5826
  }
@@ -5293,7 +5860,7 @@ async function inferSafePackageScript(projectRoot, scriptName) {
5293
5860
  }
5294
5861
  }
5295
5862
  async function addPackageScript(projectRoot, scriptName, script) {
5296
- const packagePath = path17.join(projectRoot, "package.json");
5863
+ const packagePath = path18.join(projectRoot, "package.json");
5297
5864
  let pkg;
5298
5865
  try {
5299
5866
  pkg = JSON.parse(await readFile8(packagePath, "utf-8"));
@@ -5406,7 +5973,7 @@ var EXIT_NOT_INITIALIZED2 = 1;
5406
5973
  var EXIT_SCAN_FAILED = 2;
5407
5974
  var EXIT_INVALID_ARG = 3;
5408
5975
  async function manps(rootDir, area = "all", options = {}) {
5409
- if (!await pathExists7(path18.join(rootDir, ".mancode", "state.json"))) {
5976
+ if (!await pathExists7(path19.join(rootDir, ".mancode", "state.json"))) {
5410
5977
  if (options.json) {
5411
5978
  console.log(JSON.stringify({ error: "not initialized" }, null, 2));
5412
5979
  } else {
@@ -5485,8 +6052,8 @@ async function manps(rootDir, area = "all", options = {}) {
5485
6052
  console.log(
5486
6053
  `Issues: ${report.issues.length} total (P0 ${p0}, P1 ${p1}, P2 ${p2})`
5487
6054
  );
5488
- console.log(`Report: ${path18.relative(rootDir, report.reportPath)}`);
5489
- console.log(`Issue DB: ${path18.relative(rootDir, report.issueDbPath)}`);
6055
+ console.log(`Report: ${path19.relative(rootDir, report.reportPath)}`);
6056
+ console.log(`Issue DB: ${path19.relative(rootDir, report.issueDbPath)}`);
5490
6057
  if (report.issues.length > 0) {
5491
6058
  console.log("");
5492
6059
  for (const issue of report.issues.slice(0, 7)) {
@@ -5502,7 +6069,7 @@ async function manps(rootDir, area = "all", options = {}) {
5502
6069
  console.log(` Skipped: ${remediation.skipped}`);
5503
6070
  console.log(` Fixed: ${remediation.fixed}`);
5504
6071
  console.log(
5505
- ` Issue DB: ${path18.relative(rootDir, remediation.issueDbPath)}`
6072
+ ` Issue DB: ${path19.relative(rootDir, remediation.issueDbPath)}`
5506
6073
  );
5507
6074
  }
5508
6075
  return EXIT_OK4;
@@ -5528,7 +6095,7 @@ async function runRemediation(rootDir, report, options, silent = false) {
5528
6095
  write
5529
6096
  });
5530
6097
  }
5531
- const rl = createInterface({
6098
+ const rl = createInterface2({
5532
6099
  input: process.stdin,
5533
6100
  output: process.stdout
5534
6101
  });
@@ -5557,23 +6124,246 @@ async function pathExists7(p) {
5557
6124
  }
5558
6125
  }
5559
6126
 
5560
- // src/commands/refresh-style.ts
5561
- import { promises as fs6 } from "fs";
5562
- import path19 from "path";
5563
- import process7 from "process";
6127
+ // src/commands/refresh-project.ts
6128
+ import { randomUUID } from "crypto";
6129
+ import { promises as fs7 } from "fs";
6130
+ import path20 from "path";
6131
+ import process8 from "process";
5564
6132
  var EXIT_OK5 = 0;
5565
6133
  var EXIT_NOT_INITIALIZED3 = 1;
5566
- async function refreshStyle(rootDir = process7.cwd()) {
5567
- const stateFile = path19.join(rootDir, ".mancode", "state.json");
5568
- if (!await pathExists8(stateFile)) {
6134
+ var EXIT_CORRUPT_STATE = 2;
6135
+ var EXIT_REFRESH_FAILED = 3;
6136
+ async function refreshProject(rootDir = process8.cwd()) {
6137
+ const mancodeDir = path20.join(rootDir, ".mancode");
6138
+ const statePath = path20.join(mancodeDir, "state.json");
6139
+ if (!await pathExists8(statePath)) {
5569
6140
  console.error("\u2717 mancode not initialized.");
5570
6141
  console.error(" Run `mancode init` first.");
5571
6142
  return EXIT_NOT_INITIALIZED3;
5572
6143
  }
6144
+ const state = await readRequiredState(statePath);
6145
+ if (!state) {
6146
+ console.error("\u2717 .mancode/state.json is corrupt or incomplete.");
6147
+ console.error(" Run `mancode init --force` to repair it.");
6148
+ return EXIT_CORRUPT_STATE;
6149
+ }
6150
+ let factsWritten = false;
6151
+ try {
6152
+ const [profile, team, hasGit, hasManifest] = await Promise.all([
6153
+ detectProjectProfile(rootDir),
6154
+ detectTeamStatus(rootDir),
6155
+ pathExists8(path20.join(rootDir, ".git")),
6156
+ hasProjectManifest2(rootDir)
6157
+ ]);
6158
+ const uiLibrary = primaryUiLibrary(profile);
6159
+ const stack = [...profile.languages, ...profile.frameworks];
6160
+ const config = await readJson3(path20.join(mancodeDir, "config.json"));
6161
+ const configuredTeam = config.forceTeamMode === true ? true : config.teamMode === "on" ? true : config.teamMode === "off" ? false : team.isTeam;
6162
+ const nextState = {
6163
+ ...state,
6164
+ techStack: stack.join(" + ") || profile.projectKind,
6165
+ uiLibrary: uiLibrary ?? "None",
6166
+ projectMode: hasGit || hasManifest ? "detected" : "generic",
6167
+ teamModeAutoDetected: configuredTeam,
6168
+ contributors: team.contributors
6169
+ };
6170
+ await writeProjectFacts(
6171
+ statePath,
6172
+ path20.join(mancodeDir, "project-profile.json"),
6173
+ `${JSON.stringify(nextState, null, 2)}
6174
+ `,
6175
+ `${JSON.stringify(profile, null, 2)}
6176
+ `
6177
+ );
6178
+ factsWritten = true;
6179
+ const refreshedPlatforms = await refreshStaticPlatforms(
6180
+ rootDir,
6181
+ config,
6182
+ state.platform,
6183
+ stack,
6184
+ uiLibrary,
6185
+ profile
6186
+ );
6187
+ console.log("\u2713 Project facts refreshed.");
6188
+ console.log(
6189
+ ` ${hasGit ? "Git detected" : "No Git repository"} | ${hasManifest ? "project manifest detected" : "generic project"}`
6190
+ );
6191
+ console.log(
6192
+ ` Stack: ${nextState.techStack} | UI: ${nextState.uiLibrary}`
6193
+ );
6194
+ if (refreshedPlatforms.length > 0) {
6195
+ console.log(` Refreshed adapters: ${refreshedPlatforms.join(", ")}`);
6196
+ }
6197
+ console.log(
6198
+ " Run `mancode refresh-style` if UI files or dependencies changed."
6199
+ );
6200
+ return EXIT_OK5;
6201
+ } catch (error) {
6202
+ const message = error instanceof Error ? error.message : String(error);
6203
+ console.error(`\u2717 Project refresh failed: ${message}`);
6204
+ if (factsWritten) {
6205
+ console.error(
6206
+ " Project facts were saved, but one or more static adapters remain stale."
6207
+ );
6208
+ }
6209
+ return EXIT_REFRESH_FAILED;
6210
+ }
6211
+ }
6212
+ async function hasProjectManifest2(rootDir) {
6213
+ for (const manifest of PROJECT_MANIFESTS) {
6214
+ if (await pathExists8(path20.join(rootDir, manifest))) return true;
6215
+ }
6216
+ return false;
6217
+ }
6218
+ async function writeProjectFacts(statePath, profilePath, stateContent, profileContent) {
6219
+ await Promise.all([
6220
+ ensureReplaceableFile(statePath),
6221
+ ensureReplaceableFile(profilePath)
6222
+ ]);
6223
+ const previousProfile = await readOptionalText(profilePath);
6224
+ const stateTemp = temporaryPath(statePath);
6225
+ const profileTemp = temporaryPath(profilePath);
6226
+ let profileReplaced = false;
6227
+ try {
6228
+ await Promise.all([
6229
+ fs7.writeFile(stateTemp, stateContent, "utf-8"),
6230
+ fs7.writeFile(profileTemp, profileContent, "utf-8")
6231
+ ]);
6232
+ await fs7.rename(profileTemp, profilePath);
6233
+ profileReplaced = true;
6234
+ await fs7.rename(stateTemp, statePath);
6235
+ } catch (error) {
6236
+ if (profileReplaced) {
6237
+ if (previousProfile === null) {
6238
+ await fs7.rm(profilePath, { force: true });
6239
+ } else {
6240
+ await replaceTextFile(profilePath, previousProfile);
6241
+ }
6242
+ }
6243
+ throw error;
6244
+ } finally {
6245
+ await Promise.all([
6246
+ fs7.rm(stateTemp, { force: true }),
6247
+ fs7.rm(profileTemp, { force: true })
6248
+ ]);
6249
+ }
6250
+ }
6251
+ async function ensureReplaceableFile(filePath) {
6252
+ try {
6253
+ const entry = await fs7.lstat(filePath);
6254
+ if (!entry.isFile()) {
6255
+ throw new Error(`cannot replace non-file path: ${filePath}`);
6256
+ }
6257
+ } catch (error) {
6258
+ if (isNodeError6(error) && error.code === "ENOENT") return;
6259
+ throw error;
6260
+ }
6261
+ }
6262
+ async function replaceTextFile(filePath, content) {
6263
+ const tempPath = temporaryPath(filePath);
6264
+ try {
6265
+ await fs7.writeFile(tempPath, content, "utf-8");
6266
+ await fs7.rename(tempPath, filePath);
6267
+ } finally {
6268
+ await fs7.rm(tempPath, { force: true });
6269
+ }
6270
+ }
6271
+ async function readOptionalText(filePath) {
6272
+ try {
6273
+ return await fs7.readFile(filePath, "utf-8");
6274
+ } catch (error) {
6275
+ if (isNodeError6(error) && error.code === "ENOENT") return null;
6276
+ throw error;
6277
+ }
6278
+ }
6279
+ function temporaryPath(filePath) {
6280
+ return path20.join(
6281
+ path20.dirname(filePath),
6282
+ `.${path20.basename(filePath)}.${process8.pid}.${randomUUID()}.tmp`
6283
+ );
6284
+ }
6285
+ async function refreshStaticPlatforms(rootDir, config, fallbackPlatform, stack, uiLibrary, profile) {
6286
+ const platforms = configuredPlatforms(config, fallbackPlatform).filter(
6287
+ (platform) => platform !== "claude-code"
6288
+ );
6289
+ const refreshed = [];
6290
+ for (const platform of platforms) {
6291
+ const installer = getPlatformInstaller(platform);
6292
+ if (!installer) continue;
6293
+ await installer.install(rootDir, {
6294
+ techStack: stack,
6295
+ uiLibrary,
6296
+ projectProfile: profile,
6297
+ minimal: platformIsMinimal(config, platform),
6298
+ force: true
6299
+ });
6300
+ refreshed.push(installer.displayName);
6301
+ }
6302
+ return refreshed;
6303
+ }
6304
+ function configuredPlatforms(config, fallbackPlatform) {
6305
+ const configured = Array.isArray(config.platforms) ? config.platforms : [fallbackPlatform];
6306
+ return configured.filter(
6307
+ (platform) => typeof platform === "string" && getPlatformInstaller(platform) !== null
6308
+ );
6309
+ }
6310
+ function platformIsMinimal(config, platform) {
6311
+ if (!isRecord7(config.platformOptions)) return false;
6312
+ const options = config.platformOptions[platform];
6313
+ return isRecord7(options) && options.minimal === true;
6314
+ }
6315
+ async function readJson3(filePath) {
6316
+ try {
6317
+ const value = JSON.parse(await fs7.readFile(filePath, "utf-8"));
6318
+ return isRecord7(value) ? value : {};
6319
+ } catch {
6320
+ return {};
6321
+ }
6322
+ }
6323
+ async function readRequiredState(filePath) {
6324
+ let state;
6325
+ try {
6326
+ const raw = await fs7.readFile(filePath, "utf-8");
6327
+ const parsed = JSON.parse(raw);
6328
+ if (!isRecord7(parsed)) return null;
6329
+ state = parsed;
6330
+ } catch {
6331
+ return null;
6332
+ }
6333
+ return typeof state.version === "string" && typeof state.currentMode === "string" && typeof state.platform === "string" ? state : null;
6334
+ }
6335
+ function isRecord7(value) {
6336
+ return typeof value === "object" && value !== null && !Array.isArray(value);
6337
+ }
6338
+ function isNodeError6(error) {
6339
+ return error instanceof Error && "code" in error;
6340
+ }
6341
+ async function pathExists8(filePath) {
6342
+ try {
6343
+ await fs7.access(filePath);
6344
+ return true;
6345
+ } catch {
6346
+ return false;
6347
+ }
6348
+ }
6349
+
6350
+ // src/commands/refresh-style.ts
6351
+ import { promises as fs8 } from "fs";
6352
+ import path21 from "path";
6353
+ import process9 from "process";
6354
+ var EXIT_OK6 = 0;
6355
+ var EXIT_NOT_INITIALIZED4 = 1;
6356
+ async function refreshStyle(rootDir = process9.cwd()) {
6357
+ const stateFile = path21.join(rootDir, ".mancode", "state.json");
6358
+ if (!await pathExists9(stateFile)) {
6359
+ console.error("\u2717 mancode not initialized.");
6360
+ console.error(" Run `mancode init` first.");
6361
+ return EXIT_NOT_INITIALIZED4;
6362
+ }
5573
6363
  console.log("\u2713 \u5237\u65B0\u9879\u76EE profile...");
5574
6364
  const profile = await detectProjectProfile(rootDir);
5575
- const profilePath = path19.join(rootDir, ".mancode", "project-profile.json");
5576
- await fs6.writeFile(
6365
+ const profilePath = path21.join(rootDir, ".mancode", "project-profile.json");
6366
+ await fs8.writeFile(
5577
6367
  profilePath,
5578
6368
  `${JSON.stringify(profile, null, 2)}
5579
6369
  `,
@@ -5590,18 +6380,18 @@ async function refreshStyle(rootDir = process7.cwd()) {
5590
6380
  );
5591
6381
  console.log(" Updated .mancode/project-profile.json.");
5592
6382
  await printStaticPlatformRefreshHint(rootDir);
5593
- return EXIT_OK5;
6383
+ return EXIT_OK6;
5594
6384
  }
5595
6385
  console.log("\u2713 \u626B\u63CF\u9879\u76EE\u8BBE\u8BA1 token...");
5596
6386
  const tokens = await scanAesthetics(rootDir, uiLibraryHint);
5597
- const tokensPath = path19.join(
6387
+ const tokensPath = path21.join(
5598
6388
  rootDir,
5599
6389
  ".mancode",
5600
6390
  "aesthetics",
5601
6391
  "style-tokens.json"
5602
6392
  );
5603
- await fs6.mkdir(path19.dirname(tokensPath), { recursive: true });
5604
- await fs6.writeFile(
6393
+ await fs8.mkdir(path21.dirname(tokensPath), { recursive: true });
6394
+ await fs8.writeFile(
5605
6395
  tokensPath,
5606
6396
  `${JSON.stringify(tokens, null, 2)}
5607
6397
  `,
@@ -5649,7 +6439,7 @@ async function refreshStyle(rootDir = process7.cwd()) {
5649
6439
  "\u5DF2\u66F4\u65B0 .mancode/project-profile.json \u548C .mancode/aesthetics/style-tokens.json"
5650
6440
  );
5651
6441
  await printStaticPlatformRefreshHint(rootDir);
5652
- return EXIT_OK5;
6442
+ return EXIT_OK6;
5653
6443
  }
5654
6444
  async function printStaticPlatformRefreshHint(rootDir) {
5655
6445
  const platforms = await readInstalledPlatforms2(rootDir);
@@ -5666,11 +6456,11 @@ async function printStaticPlatformRefreshHint(rootDir) {
5666
6456
  );
5667
6457
  }
5668
6458
  async function refreshLegacyStateContext(rootDir, profile, uiLibrary) {
5669
- const statePath = path19.join(rootDir, ".mancode", "state.json");
6459
+ const statePath = path21.join(rootDir, ".mancode", "state.json");
5670
6460
  try {
5671
- const state = JSON.parse(await fs6.readFile(statePath, "utf-8"));
6461
+ const state = JSON.parse(await fs8.readFile(statePath, "utf-8"));
5672
6462
  const stack = [...profile.languages, ...profile.frameworks];
5673
- await fs6.writeFile(
6463
+ await fs8.writeFile(
5674
6464
  statePath,
5675
6465
  `${JSON.stringify(
5676
6466
  {
@@ -5689,8 +6479,8 @@ async function refreshLegacyStateContext(rootDir, profile, uiLibrary) {
5689
6479
  }
5690
6480
  async function readInstalledPlatforms2(rootDir) {
5691
6481
  try {
5692
- const raw = await fs6.readFile(
5693
- path19.join(rootDir, ".mancode", "config.json"),
6482
+ const raw = await fs8.readFile(
6483
+ path21.join(rootDir, ".mancode", "config.json"),
5694
6484
  "utf-8"
5695
6485
  );
5696
6486
  const config = JSON.parse(raw);
@@ -5701,9 +6491,9 @@ async function readInstalledPlatforms2(rootDir) {
5701
6491
  return [];
5702
6492
  }
5703
6493
  }
5704
- async function pathExists8(p) {
6494
+ async function pathExists9(p) {
5705
6495
  try {
5706
- await fs6.access(p);
6496
+ await fs8.access(p);
5707
6497
  return true;
5708
6498
  } catch {
5709
6499
  return false;
@@ -5712,9 +6502,9 @@ async function pathExists8(p) {
5712
6502
 
5713
6503
  // src/commands/status.ts
5714
6504
  import { spawn } from "child_process";
5715
- import { promises as fs7 } from "fs";
5716
- import path22 from "path";
5717
- import process8 from "process";
6505
+ import { promises as fs9 } from "fs";
6506
+ import path24 from "path";
6507
+ import process10 from "process";
5718
6508
 
5719
6509
  // src/system/workflow.ts
5720
6510
  import {
@@ -5725,11 +6515,11 @@ import {
5725
6515
  stat as stat4,
5726
6516
  writeFile as writeFile11
5727
6517
  } from "fs/promises";
5728
- import path21 from "path";
6518
+ import path23 from "path";
5729
6519
 
5730
6520
  // src/system/review-ledger.ts
5731
6521
  import { mkdir as mkdir8, readFile as readFile9, stat as stat3, writeFile as writeFile10 } from "fs/promises";
5732
- import path20 from "path";
6522
+ import path22 from "path";
5733
6523
  var REVIEW_FILE = "review-ledger.json";
5734
6524
  var TASK_ID_PATTERN = /^[A-Za-z0-9][A-Za-z0-9-]*$/;
5735
6525
  var BLOCKER_ID_PATTERN = /^[A-Z][A-Z0-9-]{0,31}$/;
@@ -5845,8 +6635,8 @@ async function readReviewLedger(projectRoot, taskId) {
5845
6635
  }
5846
6636
  }
5847
6637
  function isReviewLedger(value) {
5848
- if (!isRecord7(value)) return false;
5849
- if (value.version !== "1.0" || !isReviewDepth(value.depth) || !isDomainArray(value.requiredDomains) || !isDomainArray(value.completedDomains) || !isRecord7(value.reports) || !Array.isArray(value.blockers) || value.remediationRounds !== 0 && value.remediationRounds !== 1) {
6638
+ if (!isRecord8(value)) return false;
6639
+ if (value.version !== "1.0" || !isReviewDepth(value.depth) || !isDomainArray(value.requiredDomains) || !isDomainArray(value.completedDomains) || !isRecord8(value.reports) || !Array.isArray(value.blockers) || value.remediationRounds !== 0 && value.remediationRounds !== 1) {
5850
6640
  return false;
5851
6641
  }
5852
6642
  const requiredDomains = value.requiredDomains;
@@ -5862,13 +6652,13 @@ function isReviewLedger(value) {
5862
6652
  }
5863
6653
  const blockerIds = /* @__PURE__ */ new Set();
5864
6654
  return blockers.every(
5865
- (blocker) => isRecord7(blocker) && typeof blocker.id === "string" && BLOCKER_ID_PATTERN.test(blocker.id) && !blockerIds.has(blocker.id) && blockerIds.add(blocker.id) && isReviewDomain(blocker.domain) && completedDomains.includes(blocker.domain) && (blocker.status === "open" || blocker.status === "resolved")
6655
+ (blocker) => isRecord8(blocker) && typeof blocker.id === "string" && BLOCKER_ID_PATTERN.test(blocker.id) && !blockerIds.has(blocker.id) && blockerIds.add(blocker.id) && isReviewDomain(blocker.domain) && completedDomains.includes(blocker.domain) && (blocker.status === "open" || blocker.status === "resolved")
5866
6656
  );
5867
6657
  }
5868
6658
  function isDomainArray(value) {
5869
6659
  return Array.isArray(value) && value.every(isReviewDomain);
5870
6660
  }
5871
- function isRecord7(value) {
6661
+ function isRecord8(value) {
5872
6662
  return typeof value === "object" && value !== null && !Array.isArray(value);
5873
6663
  }
5874
6664
  async function requireReviewLedger(projectRoot, taskId) {
@@ -5878,17 +6668,17 @@ async function requireReviewLedger(projectRoot, taskId) {
5878
6668
  return ledger;
5879
6669
  }
5880
6670
  async function writeReviewLedger(projectRoot, taskId, ledger) {
5881
- const dir = path20.join(projectRoot, ".mancode", "workflows", taskId);
6671
+ const dir = path22.join(projectRoot, ".mancode", "workflows", taskId);
5882
6672
  await mkdir8(dir, { recursive: true });
5883
6673
  await writeFile10(
5884
- path20.join(dir, REVIEW_FILE),
6674
+ path22.join(dir, REVIEW_FILE),
5885
6675
  `${JSON.stringify(ledger, null, 2)}
5886
6676
  `,
5887
6677
  "utf-8"
5888
6678
  );
5889
6679
  }
5890
6680
  function reviewPath(projectRoot, taskId) {
5891
- return path20.join(projectRoot, ".mancode", "workflows", taskId, REVIEW_FILE);
6681
+ return path22.join(projectRoot, ".mancode", "workflows", taskId, REVIEW_FILE);
5892
6682
  }
5893
6683
  function assertValidTaskId(taskId) {
5894
6684
  if (!TASK_ID_PATTERN.test(taskId)) {
@@ -5901,7 +6691,7 @@ function assertSafeReportPath(report) {
5901
6691
  }
5902
6692
  }
5903
6693
  async function assertReportExists(projectRoot, taskId, report) {
5904
- const reportPath = path20.join(
6694
+ const reportPath = path22.join(
5905
6695
  projectRoot,
5906
6696
  ".mancode",
5907
6697
  "workflows",
@@ -5915,9 +6705,9 @@ async function assertReportExists(projectRoot, taskId, report) {
5915
6705
  throw new Error(`review report not found: ${report}`);
5916
6706
  }
5917
6707
  function isSafeReportPath(report) {
5918
- const normalized = path20.posix.normalize(report.replaceAll("\\", "/"));
6708
+ const normalized = path22.posix.normalize(report.replaceAll("\\", "/"));
5919
6709
  return Boolean(
5920
- report.trim() && !path20.isAbsolute(report) && normalized !== ".." && !normalized.startsWith("../") && normalized.endsWith(".md")
6710
+ report.trim() && !path22.isAbsolute(report) && normalized !== ".." && !normalized.startsWith("../") && normalized.endsWith(".md")
5921
6711
  );
5922
6712
  }
5923
6713
 
@@ -5965,7 +6755,7 @@ async function allocateTaskId(projectRoot, baseTaskId) {
5965
6755
  await mkdir9(workflowDir(projectRoot, taskId));
5966
6756
  return taskId;
5967
6757
  } catch (err) {
5968
- if (isNodeError6(err) && err.code === "EEXIST") {
6758
+ if (isNodeError7(err) && err.code === "EEXIST") {
5969
6759
  continue;
5970
6760
  }
5971
6761
  throw err;
@@ -6041,19 +6831,19 @@ async function deleteWorkflow(projectRoot, taskId) {
6041
6831
  return true;
6042
6832
  }
6043
6833
  function workflowsRoot(projectRoot) {
6044
- return path21.join(projectRoot, ".mancode", "workflows");
6834
+ return path23.join(projectRoot, ".mancode", "workflows");
6045
6835
  }
6046
6836
  function workflowDir(projectRoot, taskId) {
6047
6837
  assertValidTaskId2(taskId);
6048
- return path21.join(workflowsRoot(projectRoot), taskId);
6838
+ return path23.join(workflowsRoot(projectRoot), taskId);
6049
6839
  }
6050
6840
  function metadataPath(projectRoot, taskId) {
6051
- return path21.join(workflowDir(projectRoot, taskId), METADATA_FILE);
6841
+ return path23.join(workflowDir(projectRoot, taskId), METADATA_FILE);
6052
6842
  }
6053
6843
  async function writeMetadata(dir, meta) {
6054
6844
  const content = `${JSON.stringify(meta, null, 2)}
6055
6845
  `;
6056
- await writeFile11(path21.join(dir, METADATA_FILE), content, "utf-8");
6846
+ await writeFile11(path23.join(dir, METADATA_FILE), content, "utf-8");
6057
6847
  }
6058
6848
  function parseWorkflowMeta(raw, taskId) {
6059
6849
  try {
@@ -6259,7 +7049,7 @@ async function propagateChildStatus(projectRoot, child) {
6259
7049
  });
6260
7050
  }
6261
7051
  }
6262
- function isNodeError6(err) {
7052
+ function isNodeError7(err) {
6263
7053
  return err instanceof Error && "code" in err;
6264
7054
  }
6265
7055
  function assertValidTaskId2(taskId) {
@@ -6270,19 +7060,19 @@ function assertValidTaskId2(taskId) {
6270
7060
 
6271
7061
  // src/commands/status.ts
6272
7062
  var HOOK_ESTIMATE_TIMEOUT_MS = 2e3;
6273
- var EXIT_OK6 = 0;
6274
- var EXIT_NOT_INITIALIZED4 = 1;
6275
- var EXIT_CORRUPT_STATE = 2;
6276
- async function status(rootDir = process8.cwd(), options = {}) {
6277
- const stateFile = path22.join(rootDir, ".mancode", "state.json");
6278
- if (!await pathExists9(stateFile)) {
7063
+ var EXIT_OK7 = 0;
7064
+ var EXIT_NOT_INITIALIZED5 = 1;
7065
+ var EXIT_CORRUPT_STATE2 = 2;
7066
+ async function status(rootDir = process10.cwd(), options = {}) {
7067
+ const stateFile = path24.join(rootDir, ".mancode", "state.json");
7068
+ if (!await pathExists10(stateFile)) {
6279
7069
  console.error("\u2717 mancode not initialized.");
6280
7070
  console.error(" Run `mancode init` to get started.");
6281
- return EXIT_NOT_INITIALIZED4;
7071
+ return EXIT_NOT_INITIALIZED5;
6282
7072
  }
6283
7073
  let state;
6284
7074
  try {
6285
- const raw = await fs7.readFile(stateFile, "utf-8");
7075
+ const raw = await fs9.readFile(stateFile, "utf-8");
6286
7076
  state = JSON.parse(raw);
6287
7077
  } catch (err) {
6288
7078
  if (err instanceof SyntaxError) {
@@ -6293,7 +7083,7 @@ async function status(rootDir = process8.cwd(), options = {}) {
6293
7083
  const msg = err instanceof Error ? err.message : String(err);
6294
7084
  console.error(`\u2717 Failed to read .mancode/state.json: ${msg}`);
6295
7085
  }
6296
- return EXIT_CORRUPT_STATE;
7086
+ return EXIT_CORRUPT_STATE2;
6297
7087
  }
6298
7088
  const [
6299
7089
  project,
@@ -6301,14 +7091,16 @@ async function status(rootDir = process8.cwd(), options = {}) {
6301
7091
  config,
6302
7092
  hookInjection,
6303
7093
  teamStatus,
6304
- currentWorkflow
7094
+ currentWorkflow,
7095
+ projectRefreshRecommended
6305
7096
  ] = await Promise.all([
6306
7097
  getProjectName(rootDir),
6307
7098
  checkHooks(rootDir),
6308
7099
  readConfig2(rootDir),
6309
7100
  estimateHookInjection(rootDir),
6310
7101
  detectTeamStatus(rootDir),
6311
- getCurrentWorkflow(rootDir, state.currentTask ?? null)
7102
+ getCurrentWorkflow(rootDir, state.currentTask ?? null),
7103
+ shouldRefreshProject(rootDir, state)
6312
7104
  ]);
6313
7105
  const effectiveTeam = getEffectiveTeamStatus(state, config, teamStatus);
6314
7106
  const result = {
@@ -6323,7 +7115,8 @@ async function status(rootDir = process8.cwd(), options = {}) {
6323
7115
  hookInjection,
6324
7116
  team: effectiveTeam,
6325
7117
  currentWorkflow,
6326
- platformStatus: {}
7118
+ platformStatus: {},
7119
+ projectRefreshRecommended
6327
7120
  };
6328
7121
  result.platformStatus = await checkPlatformStatus2(rootDir, result.platforms);
6329
7122
  if (options.json) {
@@ -6331,7 +7124,16 @@ async function status(rootDir = process8.cwd(), options = {}) {
6331
7124
  } else {
6332
7125
  printText(result);
6333
7126
  }
6334
- return EXIT_OK6;
7127
+ return EXIT_OK7;
7128
+ }
7129
+ async function shouldRefreshProject(rootDir, state) {
7130
+ if (state.projectMode !== "generic") return false;
7131
+ const hasGit = await pathExists10(path24.join(rootDir, ".git"));
7132
+ if (hasGit) return true;
7133
+ for (const manifest of PROJECT_MANIFESTS) {
7134
+ if (await pathExists10(path24.join(rootDir, manifest))) return true;
7135
+ }
7136
+ return false;
6335
7137
  }
6336
7138
  async function checkPlatformStatus2(rootDir, installedPlatforms) {
6337
7139
  const installed = new Set(installedPlatforms);
@@ -6373,19 +7175,19 @@ async function getCurrentWorkflow(rootDir, taskId) {
6373
7175
  }
6374
7176
  async function getProjectName(rootDir) {
6375
7177
  try {
6376
- const raw = await fs7.readFile(path22.join(rootDir, "package.json"), "utf-8");
7178
+ const raw = await fs9.readFile(path24.join(rootDir, "package.json"), "utf-8");
6377
7179
  const pkg = JSON.parse(raw);
6378
7180
  if (pkg.name && typeof pkg.name === "string") {
6379
7181
  return pkg.name;
6380
7182
  }
6381
7183
  } catch {
6382
7184
  }
6383
- return path22.basename(rootDir);
7185
+ return path24.basename(rootDir);
6384
7186
  }
6385
7187
  async function readConfig2(rootDir) {
6386
7188
  try {
6387
- const raw = await fs7.readFile(
6388
- path22.join(rootDir, ".mancode", "config.json"),
7189
+ const raw = await fs9.readFile(
7190
+ path24.join(rootDir, ".mancode", "config.json"),
6389
7191
  "utf-8"
6390
7192
  );
6391
7193
  return JSON.parse(raw);
@@ -6400,21 +7202,22 @@ function getInstalledPlatforms(config, fallback) {
6400
7202
  return fallback ? [fallback] : [];
6401
7203
  }
6402
7204
  function getEffectiveTeamStatus(state, config, detected) {
6403
- const configuredTeam = config.forceTeamMode === true ? true : state.teamModeAutoDetected ?? detected.isTeam;
6404
- const forced = config.forceTeamMode === true;
7205
+ const configuredTeam = config.forceTeamMode === true ? true : config.teamMode === "on" ? true : config.teamMode === "off" ? false : state.teamModeAutoDetected ?? detected.isTeam;
7206
+ const forced = config.teamMode === "on" || config.forceTeamMode === true;
7207
+ const autoDetected = config.forceTeamMode === true || config.teamMode === "on" || config.teamMode === "off" ? false : state.teamModeAutoDetected ?? detected.isTeam;
6405
7208
  return {
6406
7209
  ...detected,
6407
7210
  isTeam: configuredTeam,
6408
7211
  contributors: Math.max(detected.contributors, state.contributors ?? 0, 1),
6409
- autoDetected: forced ? false : state.teamModeAutoDetected ?? detected.isTeam,
7212
+ autoDetected,
6410
7213
  forced
6411
7214
  };
6412
7215
  }
6413
7216
  async function checkHooks(rootDir) {
6414
7217
  const [sessionStart, userPromptSubmit, registered] = await Promise.all([
6415
- pathExists9(path22.join(rootDir, ".mancode", "hooks", "session-start.mjs")),
6416
- pathExists9(
6417
- path22.join(rootDir, ".mancode", "hooks", "user-prompt-submit.mjs")
7218
+ pathExists10(path24.join(rootDir, ".mancode", "hooks", "session-start.mjs")),
7219
+ pathExists10(
7220
+ path24.join(rootDir, ".mancode", "hooks", "user-prompt-submit.mjs")
6418
7221
  ),
6419
7222
  isRegistered(rootDir)
6420
7223
  ]);
@@ -6422,8 +7225,8 @@ async function checkHooks(rootDir) {
6422
7225
  }
6423
7226
  async function isRegistered(rootDir) {
6424
7227
  try {
6425
- const raw = await fs7.readFile(
6426
- path22.join(rootDir, ".claude", "settings.json"),
7228
+ const raw = await fs9.readFile(
7229
+ path24.join(rootDir, ".claude", "settings.json"),
6427
7230
  "utf-8"
6428
7231
  );
6429
7232
  const settings = JSON.parse(raw);
@@ -6433,9 +7236,9 @@ async function isRegistered(rootDir) {
6433
7236
  }
6434
7237
  }
6435
7238
  function hooksRegistered2(settings) {
6436
- if (!isRecord8(settings)) return false;
7239
+ if (!isRecord9(settings)) return false;
6437
7240
  const hooks = settings.hooks;
6438
- if (!isRecord8(hooks)) return false;
7241
+ if (!isRecord9(hooks)) return false;
6439
7242
  return hasHookCommand2(hooks.SessionStart, ".mancode/hooks/session-start.mjs") && hasHookCommand2(
6440
7243
  hooks.UserPromptSubmit,
6441
7244
  ".mancode/hooks/user-prompt-submit.mjs"
@@ -6444,21 +7247,21 @@ function hooksRegistered2(settings) {
6444
7247
  function hasHookCommand2(value, needle) {
6445
7248
  if (!Array.isArray(value)) return false;
6446
7249
  return value.some((group) => {
6447
- if (!isRecord8(group) || !Array.isArray(group.hooks)) return false;
7250
+ if (!isRecord9(group) || !Array.isArray(group.hooks)) return false;
6448
7251
  return group.hooks.some((hook) => {
6449
- if (!isRecord8(hook) || typeof hook.command !== "string") return false;
7252
+ if (!isRecord9(hook) || typeof hook.command !== "string") return false;
6450
7253
  return hook.command.includes(needle);
6451
7254
  });
6452
7255
  });
6453
7256
  }
6454
7257
  async function estimateHookInjection(rootDir) {
6455
- const hookPath = path22.join(
7258
+ const hookPath = path24.join(
6456
7259
  rootDir,
6457
7260
  ".mancode",
6458
7261
  "hooks",
6459
7262
  "user-prompt-submit.mjs"
6460
7263
  );
6461
- if (!await pathExists9(hookPath)) {
7264
+ if (!await pathExists10(hookPath)) {
6462
7265
  return { tokens: 0, cap: 800 };
6463
7266
  }
6464
7267
  try {
@@ -6473,7 +7276,7 @@ async function estimateHookInjection(rootDir) {
6473
7276
  }
6474
7277
  function runHookEstimate(rootDir, hookPath) {
6475
7278
  return new Promise((resolve, reject) => {
6476
- const child = spawn(process8.execPath, [hookPath], {
7279
+ const child = spawn(process10.execPath, [hookPath], {
6477
7280
  cwd: rootDir,
6478
7281
  stdio: ["pipe", "pipe", "ignore"]
6479
7282
  });
@@ -6488,15 +7291,15 @@ function runHookEstimate(rootDir, hookPath) {
6488
7291
  child.kill();
6489
7292
  finish(() => reject(new Error("hook estimate timed out")));
6490
7293
  }, HOOK_ESTIMATE_TIMEOUT_MS);
6491
- let stdout = "";
7294
+ let stdout2 = "";
6492
7295
  child.stdout.setEncoding("utf-8");
6493
7296
  child.stdout.on("data", (chunk) => {
6494
- stdout += chunk;
7297
+ stdout2 += chunk;
6495
7298
  });
6496
7299
  child.on("error", (err) => finish(() => reject(err)));
6497
7300
  child.on("close", (code) => {
6498
7301
  finish(() => {
6499
- if (code === 0) resolve(stdout);
7302
+ if (code === 0) resolve(stdout2);
6500
7303
  else reject(new Error(`hook exited with ${code}`));
6501
7304
  });
6502
7305
  });
@@ -6515,6 +7318,11 @@ function printText(r) {
6515
7318
  console.log(`Style: ${r.uiLibrary}`);
6516
7319
  console.log(`Initialized: ${r.initializedAt}`);
6517
7320
  console.log(`Team: ${formatTeamStatus(r.team)}`);
7321
+ if (r.projectRefreshRecommended) {
7322
+ console.log(
7323
+ "Project: new Git or project files detected; run `mancode refresh-project`."
7324
+ );
7325
+ }
6518
7326
  if (r.currentWorkflow) {
6519
7327
  const stepMax = workflowStepMax(r.currentWorkflow.mode);
6520
7328
  console.log(
@@ -6578,12 +7386,12 @@ function formatTeamStatus(team) {
6578
7386
  function workflowStepMax(mode) {
6579
7387
  return maxWorkflowStep(mode);
6580
7388
  }
6581
- function isRecord8(value) {
7389
+ function isRecord9(value) {
6582
7390
  return typeof value === "object" && value !== null;
6583
7391
  }
6584
- async function pathExists9(p) {
7392
+ async function pathExists10(p) {
6585
7393
  try {
6586
- await fs7.access(p);
7394
+ await fs9.access(p);
6587
7395
  return true;
6588
7396
  } catch {
6589
7397
  return false;
@@ -6592,17 +7400,17 @@ async function pathExists9(p) {
6592
7400
 
6593
7401
  // src/commands/uninstall.ts
6594
7402
  import { access as access5, readFile as readFile11, rm as rm5, writeFile as writeFile12 } from "fs/promises";
6595
- import path23 from "path";
6596
- import process9 from "process";
6597
- var EXIT_OK7 = 0;
6598
- var EXIT_NOT_INITIALIZED5 = 1;
7403
+ import path25 from "path";
7404
+ import process11 from "process";
7405
+ var EXIT_OK8 = 0;
7406
+ var EXIT_NOT_INITIALIZED6 = 1;
6599
7407
  var EXIT_UNSUPPORTED_PLATFORM2 = 2;
6600
- async function uninstall(rootDir = process9.cwd(), platform, options = {}) {
6601
- const stateFile = path23.join(rootDir, ".mancode", "state.json");
6602
- if (!await pathExists10(stateFile)) {
7408
+ async function uninstall(rootDir = process11.cwd(), platform, options = {}) {
7409
+ const stateFile = path25.join(rootDir, ".mancode", "state.json");
7410
+ if (!await pathExists11(stateFile)) {
6603
7411
  console.error("\u2717 mancode not initialized.");
6604
7412
  console.error(" Run `mancode init` first.");
6605
- return EXIT_NOT_INITIALIZED5;
7413
+ return EXIT_NOT_INITIALIZED6;
6606
7414
  }
6607
7415
  const removeAll = !platform || options.all;
6608
7416
  if (platform) {
@@ -6627,7 +7435,7 @@ async function uninstall(rootDir = process9.cwd(), platform, options = {}) {
6627
7435
  await uninstallPlatform(rootDir, platform ?? "claude-code");
6628
7436
  }
6629
7437
  console.log("\u2713 Uninstall complete.");
6630
- return EXIT_OK7;
7438
+ return EXIT_OK8;
6631
7439
  }
6632
7440
  async function uninstallPlatform(rootDir, platform) {
6633
7441
  console.log(`\u2713 Removing ${formatPlatformName(platform)} adapter...`);
@@ -6650,7 +7458,7 @@ async function uninstallAll(rootDir) {
6650
7458
  await uninstallPlatform(rootDir, p);
6651
7459
  }
6652
7460
  console.log("\u2713 Removing .mancode/ directory...");
6653
- await rm5(path23.join(rootDir, ".mancode"), {
7461
+ await rm5(path25.join(rootDir, ".mancode"), {
6654
7462
  recursive: true,
6655
7463
  force: true
6656
7464
  });
@@ -6660,7 +7468,7 @@ async function uninstallClaudeCode(rootDir) {
6660
7468
  await cleanClaudeSettings(rootDir);
6661
7469
  }
6662
7470
  async function cleanClaudeSettings(rootDir) {
6663
- const settingsPath = path23.join(rootDir, ".claude", "settings.json");
7471
+ const settingsPath = path25.join(rootDir, ".claude", "settings.json");
6664
7472
  let content;
6665
7473
  try {
6666
7474
  content = await readFile11(settingsPath, "utf-8");
@@ -6674,14 +7482,14 @@ async function cleanClaudeSettings(rootDir) {
6674
7482
  return;
6675
7483
  }
6676
7484
  const cleanedHooks = {};
6677
- if (isRecord9(settings.hooks)) {
7485
+ if (isRecord10(settings.hooks)) {
6678
7486
  for (const [event, value] of Object.entries(settings.hooks)) {
6679
7487
  const cleaned = cleanClaudeHookValue(value);
6680
7488
  if (cleaned !== void 0) cleanedHooks[event] = cleaned;
6681
7489
  }
6682
7490
  settings.hooks = Object.keys(cleanedHooks).length > 0 ? cleanedHooks : void 0;
6683
7491
  }
6684
- if (isRecord9(settings.skills)) {
7492
+ if (isRecord10(settings.skills)) {
6685
7493
  const retainedSkills = Object.fromEntries(
6686
7494
  Object.entries(settings.skills).filter(
6687
7495
  ([name, value]) => LEGACY_CLAUDE_SKILL_SETTINGS[name] !== value
@@ -6701,7 +7509,7 @@ function cleanClaudeHookValue(value) {
6701
7509
  const entries2 = value.flatMap(cleanClaudeHookEntry);
6702
7510
  return entries2.length > 0 ? entries2 : void 0;
6703
7511
  }
6704
- if (!isRecord9(value)) return value;
7512
+ if (!isRecord10(value)) return value;
6705
7513
  if (Array.isArray(value.hooks) || typeof value.command === "string") {
6706
7514
  const entries2 = cleanClaudeHookEntry(value);
6707
7515
  return entries2.length > 0 ? entries2 : void 0;
@@ -6715,7 +7523,7 @@ function cleanClaudeHookValue(value) {
6715
7523
  }
6716
7524
  function cleanClaudeHookEntry(entry) {
6717
7525
  if (Array.isArray(entry)) return entry.flatMap(cleanClaudeHookEntry);
6718
- if (!isRecord9(entry)) return [entry];
7526
+ if (!isRecord10(entry)) return [entry];
6719
7527
  if (Array.isArray(entry.hooks)) {
6720
7528
  const hooks = entry.hooks.filter((hook) => !isMancodeHookEntry(hook));
6721
7529
  return hooks.length > 0 ? [{ ...entry, hooks }] : [];
@@ -6723,11 +7531,11 @@ function cleanClaudeHookEntry(entry) {
6723
7531
  return isMancodeHookEntry(entry) ? [] : [entry];
6724
7532
  }
6725
7533
  function isMancodeHookEntry(value) {
6726
- return isRecord9(value) && typeof value.command === "string" && isGeneratedMancodeHookCommand(value.command);
7534
+ return isRecord10(value) && typeof value.command === "string" && isGeneratedMancodeHookCommand(value.command);
6727
7535
  }
6728
7536
  function containsLegacyMancodeHookPath2(value) {
6729
7537
  if (Array.isArray(value)) return value.some(containsLegacyMancodeHookPath2);
6730
- if (!isRecord9(value)) return false;
7538
+ if (!isRecord10(value)) return false;
6731
7539
  if (typeof value.command === "string" && value.command.includes(".mancode/hooks/")) {
6732
7540
  return true;
6733
7541
  }
@@ -6738,7 +7546,7 @@ async function uninstallCursor(rootDir) {
6738
7546
  await removeCursorCommands(rootDir);
6739
7547
  }
6740
7548
  async function uninstallCodex(rootDir) {
6741
- const agentsPath = path23.join(rootDir, "AGENTS.md");
7549
+ const agentsPath = path25.join(rootDir, "AGENTS.md");
6742
7550
  try {
6743
7551
  const content = await readFile11(agentsPath, "utf-8");
6744
7552
  const cleaned = removeManagedBlock(content);
@@ -6753,7 +7561,7 @@ async function uninstallCodex(rootDir) {
6753
7561
  await removeCodexSkills(rootDir);
6754
7562
  }
6755
7563
  async function uninstallCopilot(rootDir) {
6756
- const instructionsPath = path23.join(
7564
+ const instructionsPath = path25.join(
6757
7565
  rootDir,
6758
7566
  ".github",
6759
7567
  "copilot-instructions.md"
@@ -6772,7 +7580,7 @@ async function uninstallCopilot(rootDir) {
6772
7580
  await removeCopilotPrompts(rootDir);
6773
7581
  }
6774
7582
  async function uninstallZcode(rootDir) {
6775
- const agentsPath = path23.join(rootDir, "AGENTS.md");
7583
+ const agentsPath = path25.join(rootDir, "AGENTS.md");
6776
7584
  try {
6777
7585
  const content = await readFile11(agentsPath, "utf-8");
6778
7586
  const cleaned = removeManagedBlock(
@@ -6791,13 +7599,13 @@ async function uninstallZcode(rootDir) {
6791
7599
  await removeZcodeSkills(rootDir);
6792
7600
  }
6793
7601
  async function removeFromConfig(rootDir, platform) {
6794
- const configPath = path23.join(rootDir, ".mancode", "config.json");
7602
+ const configPath = path25.join(rootDir, ".mancode", "config.json");
6795
7603
  try {
6796
7604
  const raw = await readFile11(configPath, "utf-8");
6797
7605
  const config = JSON.parse(raw);
6798
7606
  if (Array.isArray(config.platforms)) {
6799
7607
  config.platforms = config.platforms.filter((p) => p !== platform);
6800
- if (isRecord9(config.platformOptions)) {
7608
+ if (isRecord10(config.platformOptions)) {
6801
7609
  const platformOptions = Object.fromEntries(
6802
7610
  Object.entries(config.platformOptions).filter(
6803
7611
  ([name]) => name !== platform
@@ -6815,10 +7623,10 @@ async function removeFromConfig(rootDir, platform) {
6815
7623
  } catch {
6816
7624
  }
6817
7625
  }
6818
- function isRecord9(value) {
7626
+ function isRecord10(value) {
6819
7627
  return typeof value === "object" && value !== null && !Array.isArray(value);
6820
7628
  }
6821
- async function pathExists10(p) {
7629
+ async function pathExists11(p) {
6822
7630
  try {
6823
7631
  await access5(p);
6824
7632
  return true;
@@ -6828,10 +7636,10 @@ async function pathExists10(p) {
6828
7636
  }
6829
7637
 
6830
7638
  // src/commands/version.ts
6831
- import process10 from "process";
7639
+ import process12 from "process";
6832
7640
  function version() {
6833
- const nodeVersion = process10.version;
6834
- const platform = `${process10.platform}/${process10.arch}`;
7641
+ const nodeVersion = process12.version;
7642
+ const platform = `${process12.platform}/${process12.arch}`;
6835
7643
  console.log(`mancode/${VERSION}`);
6836
7644
  console.log(`node/${nodeVersion.replace("v", "")}`);
6837
7645
  console.log(platform);
@@ -6839,19 +7647,19 @@ function version() {
6839
7647
 
6840
7648
  // src/commands/workflow.ts
6841
7649
  import { access as access6 } from "fs/promises";
6842
- import path24 from "path";
6843
- var EXIT_OK8 = 0;
6844
- var EXIT_NOT_INITIALIZED6 = 1;
7650
+ import path26 from "path";
7651
+ var EXIT_OK9 = 0;
7652
+ var EXIT_NOT_INITIALIZED7 = 1;
6845
7653
  var EXIT_INVALID_ARG2 = 2;
6846
7654
  async function workflow(rootDir, subcommand, args = [], options = {}) {
6847
- if (!await pathExists11(path24.join(rootDir, ".mancode", "state.json"))) {
7655
+ if (!await pathExists12(path26.join(rootDir, ".mancode", "state.json"))) {
6848
7656
  if (options.json) {
6849
7657
  console.log(JSON.stringify({ error: "not initialized" }, null, 2));
6850
7658
  } else {
6851
7659
  console.error("\u2717 mancode not initialized.");
6852
7660
  console.error(" Run `mancode init` to get started.");
6853
7661
  }
6854
- return EXIT_NOT_INITIALIZED6;
7662
+ return EXIT_NOT_INITIALIZED7;
6855
7663
  }
6856
7664
  switch (subcommand) {
6857
7665
  case "create":
@@ -6900,7 +7708,7 @@ async function workflowReview(rootDir, args, options) {
6900
7708
  if (!ledger)
6901
7709
  return invalidArg(options, `review not initialized: ${taskId}`);
6902
7710
  outputReviewLedger(ledger, options);
6903
- return EXIT_OK8;
7711
+ return EXIT_OK9;
6904
7712
  }
6905
7713
  if (meta.status !== "in_progress" || meta.currentStep < 6) {
6906
7714
  return invalidArg(
@@ -6957,7 +7765,7 @@ async function workflowReview(rootDir, args, options) {
6957
7765
  );
6958
7766
  }
6959
7767
  outputReviewLedger(ledger, options);
6960
- return EXIT_OK8;
7768
+ return EXIT_OK9;
6961
7769
  } catch (error) {
6962
7770
  return invalidArg(
6963
7771
  options,
@@ -7013,7 +7821,7 @@ async function workflowCreate(rootDir, args, options) {
7013
7821
  error instanceof Error ? error.message : "unable to create workflow"
7014
7822
  );
7015
7823
  }
7016
- return EXIT_OK8;
7824
+ return EXIT_OK9;
7017
7825
  }
7018
7826
  async function workflowUpdate(rootDir, taskId, options) {
7019
7827
  if (!taskId) {
@@ -7111,18 +7919,18 @@ async function workflowUpdate(rootDir, taskId, options) {
7111
7919
  } else {
7112
7920
  console.log(`Updated workflow: ${taskId}`);
7113
7921
  }
7114
- return EXIT_OK8;
7922
+ return EXIT_OK9;
7115
7923
  }
7116
7924
  async function workflowList(rootDir, options) {
7117
7925
  const workflows = await listWorkflows(rootDir);
7118
7926
  const views = attachActiveChildren(workflows);
7119
7927
  if (options.json) {
7120
7928
  console.log(JSON.stringify(views, null, 2));
7121
- return EXIT_OK8;
7929
+ return EXIT_OK9;
7122
7930
  }
7123
7931
  if (workflows.length === 0) {
7124
7932
  console.log("No workflows.");
7125
- return EXIT_OK8;
7933
+ return EXIT_OK9;
7126
7934
  }
7127
7935
  const inProgress = workflows.filter((w) => w.status === "in_progress").length;
7128
7936
  console.log(
@@ -7132,7 +7940,7 @@ async function workflowList(rootDir, options) {
7132
7940
  for (const meta of views) {
7133
7941
  console.log(formatWorkflowRow(meta));
7134
7942
  }
7135
- return EXIT_OK8;
7943
+ return EXIT_OK9;
7136
7944
  }
7137
7945
  async function workflowShow(rootDir, taskId, options) {
7138
7946
  if (!taskId) {
@@ -7169,7 +7977,7 @@ async function workflowShow(rootDir, taskId, options) {
7169
7977
  2
7170
7978
  )
7171
7979
  );
7172
- return EXIT_OK8;
7980
+ return EXIT_OK9;
7173
7981
  }
7174
7982
  console.log(`Workflow: ${meta.taskId}`);
7175
7983
  console.log(`Task: ${meta.task}`);
@@ -7196,7 +8004,7 @@ async function workflowShow(rootDir, taskId, options) {
7196
8004
  }
7197
8005
  }
7198
8006
  }
7199
- return EXIT_OK8;
8007
+ return EXIT_OK9;
7200
8008
  }
7201
8009
  async function workflowClean(rootDir, options) {
7202
8010
  const workflows = await listWorkflows(rootDir);
@@ -7264,7 +8072,7 @@ async function workflowClean(rootDir, options) {
7264
8072
  } else {
7265
8073
  console.log(`Removed ${removed.length} workflow(s).`);
7266
8074
  }
7267
- return EXIT_OK8;
8075
+ return EXIT_OK9;
7268
8076
  }
7269
8077
  function formatWorkflowRow(meta) {
7270
8078
  const stepMax = maxWorkflowStep(meta.mode);
@@ -7323,7 +8131,7 @@ function ago(iso) {
7323
8131
  if (hours < 24) return `${hours}h ago`;
7324
8132
  return `${Math.floor(hours / 24)}d ago`;
7325
8133
  }
7326
- async function pathExists11(p) {
8134
+ async function pathExists12(p) {
7327
8135
  try {
7328
8136
  await access6(p);
7329
8137
  return true;
@@ -7336,8 +8144,11 @@ async function pathExists11(p) {
7336
8144
  program.name("mancode").description(
7337
8145
  "AI coding agent harness. Modes: solo, man, mamba, manteam, manps."
7338
8146
  ).version(VERSION);
7339
- program.command("init").description("Initialize mancode in the current project").option("--force", "Reinstall even if already initialized").option("--yes", "Skip all confirmations (CI mode)").option("--team", "Force enable team mode (MVP-2)").option("--no-team", "Force disable team mode (MVP-2)").option("--style <name>", "Specify aesthetic style (MVP-2)").option("--platform <platform>", "Initial adapter platform (MVP-3)").action(async (options) => {
7340
- const code = await init(process.cwd(), options);
8147
+ program.command("init").description("Initialize mancode in the current project").option("--force", "Reinstall even if already initialized").option("--yes", "Skip all confirmations (CI mode)").option("--team", "Force enable team mode (MVP-2)").option("--no-team", "Force disable team mode (MVP-2)").option("--style <name>", "Specify aesthetic style (MVP-2)").option("--platform <platforms>", "Adapters: comma-separated names or all").option("--empty", "Initialize a safe empty directory as a generic project").option("--lang <locale>", "Initialization language: zh-CN or en").action(async (options) => {
8148
+ const code = await init(process.cwd(), {
8149
+ ...options,
8150
+ interactive: Boolean(process.stdin.isTTY && process.stdout.isTTY)
8151
+ });
7341
8152
  process.exitCode = code;
7342
8153
  });
7343
8154
  program.command("install [platform]").description(
@@ -7383,6 +8194,12 @@ program.command("refresh-style").description("Refresh project profile and rescan
7383
8194
  const code = await refreshStyle(process.cwd());
7384
8195
  process.exitCode = code;
7385
8196
  });
8197
+ program.command("refresh-project").description(
8198
+ "Refresh detected project facts after adding Git or project files"
8199
+ ).action(async () => {
8200
+ const code = await refreshProject(process.cwd());
8201
+ process.exitCode = code;
8202
+ });
7386
8203
  program.command("version").description("Show version, node version, and platform").action(() => {
7387
8204
  version();
7388
8205
  });