mancode 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js CHANGED
@@ -8,11 +8,11 @@ import { program } from "commander";
8
8
 
9
9
  // src/commands/init.ts
10
10
  import { promises as fs2 } from "fs";
11
- import path11 from "path";
11
+ import path13 from "path";
12
12
  import process3 from "process";
13
13
 
14
14
  // src/installers/claude-code.ts
15
- import { mkdir as mkdir3, readFile as readFile3, rm, writeFile as writeFile3 } from "fs/promises";
15
+ import { access, mkdir as mkdir3, readFile as readFile2, rm, writeFile as writeFile3 } from "fs/promises";
16
16
  import path3 from "path";
17
17
 
18
18
  // src/templates/agents/scout.ts
@@ -836,6 +836,15 @@ AskUserQuestion({
836
836
  \u6536\u5230 \`/man8\` \u6216\u81EA\u52A8\u89E6\u53D1\u540E\u7ACB\u5373\u5F00\u59CB Step 1\uFF0C\u4E0D\u8981\u7B49\u7528\u6237\u786E\u8BA4\u3002`
837
837
  };
838
838
 
839
+ // src/templates/skills/principles.ts
840
+ var CORE_CODING_PRINCIPLES = `## \u94C1\u5F8B\uFF08\u6C38\u4E0D\u8FDD\u53CD\uFF09
841
+
842
+ 1. **\u4E0D\u505A\u65E0\u5173\u4FEE\u6539** \u2014 \u53EA\u6539 plan \u8303\u56F4\u5185\u7684
843
+ 2. **\u5148\u9A8C\u8BC1\u518D\u58F0\u79F0\u5B8C\u6210** \u2014 build/lint/test \u5FC5\u987B\u5B9E\u9645\u8DD1
844
+ 3. **\u5931\u8D25\u4E24\u6B21\u5FC5\u987B\u505C\u4E0B** \u2014 \u4E0D\u76F2\u8BD5
845
+ 4. **\u4E0D\u53EF\u9006\u64CD\u4F5C\u5148\u95EE** \u2014 \u5220\u9664\u3001force push\u3001worktree \u5408\u5E76
846
+ 5. **\u53EA\u89E3\u51B3\u88AB\u95EE\u5230\u7684\u95EE\u9898** \u2014 \u4E0D\u52A0\u63A8\u6D4B\u6027\u529F\u80FD`;
847
+
839
848
  // src/templates/skills/man.ts
840
849
  var MAN_SKILL = {
841
850
  name: "man",
@@ -987,13 +996,7 @@ metadata.json\uFF1A\`currentStep: 8\`
987
996
  - diff \u5927\u65F6\u53EA\u8D34 hunk \u6458\u8981\uFF0C\u8BA9 agent \u81EA\u5DF1\u7528 Read \u770B\u5B8C\u6574 diff
988
997
  - \u4E0D\u8981 dump \u6574\u4E2A\u6E90\u6587\u4EF6\u5230\u5BF9\u8BDD
989
998
 
990
- ## \u94C1\u5F8B\uFF08\u6C38\u4E0D\u8FDD\u53CD\uFF09
991
-
992
- 1. **\u4E0D\u505A\u65E0\u5173\u4FEE\u6539** \u2014 \u53EA\u6539 plan \u8303\u56F4\u5185\u7684
993
- 2. **\u5148\u9A8C\u8BC1\u518D\u58F0\u79F0\u5B8C\u6210** \u2014 build/lint/test \u5FC5\u987B\u5B9E\u9645\u8DD1
994
- 3. **\u5931\u8D25\u4E24\u6B21\u5FC5\u987B\u505C\u4E0B** \u2014 \u4E0D\u76F2\u8BD5
995
- 4. **\u4E0D\u53EF\u9006\u64CD\u4F5C\u5148\u95EE** \u2014 \u5220\u9664\u3001force push\u3001worktree \u5408\u5E76
996
- 5. **\u53EA\u89E3\u51B3\u88AB\u95EE\u5230\u7684\u95EE\u9898** \u2014 \u4E0D\u52A0\u63A8\u6D4B\u6027\u529F\u80FD
999
+ ${CORE_CODING_PRINCIPLES}
997
1000
 
998
1001
  \u6536\u5230 \`/man\` \u89E6\u53D1\u540E\u7ACB\u5373\u5F00\u59CB Step 1\u3002`
999
1002
  };
@@ -1234,7 +1237,6 @@ var MANTEAM_SKILL = {
1234
1237
  - \`.mancode/memory/spec.md\`
1235
1238
  - \`.mancode/memory/decisions.md\`
1236
1239
  - \`.mancode/team/commit-template.txt\`
1237
- - \`.mancode/team/commit-msg.sh\`\uFF08\u4EC5\u5F53\u56E2\u961F\u542F\u7528 \`mancode install claude-code --commit-hook\`\uFF09
1238
1240
  - \`.github/PULL_REQUEST_TEMPLATE.md\`
1239
1241
  2. \u5982\u679C memory \u6587\u4EF6\u4E0D\u5B58\u5728\uFF0C\u5148\u521B\u5EFA\u4E0A\u8FF0 3 \u4E2A\u6587\u4EF6\uFF0C\u4F7F\u7528\u7B80\u77ED\u6807\u9898\u548C\u7A7A\u6A21\u677F\uFF0C\u4E0D\u8981\u8986\u76D6\u5DF2\u6709\u5185\u5BB9\u3002
1240
1242
  3. \u6536\u96C6\u56E2\u961F\u4E0A\u4E0B\u6587\uFF1A
@@ -1296,16 +1298,16 @@ var MANTEAM_SKILL = {
1296
1298
  - workflow: <taskId>
1297
1299
  - validation: <commands>
1298
1300
  \`\`\`
1299
- 4. \u5982\u679C\u56E2\u961F\u9700\u8981\u5F3A\u5236 commit \u89C4\u8303\uFF0C\u63D0\u9192\u53EF\u8FD0\u884C \`mancode install claude-code --commit-hook\` \u5B89\u88C5\u53EF\u9009 Git hook\uFF1B\u4E0D\u8981\u8986\u76D6\u5DF2\u6709\u81EA\u5B9A\u4E49 hook\u3002
1301
+ 4. \u5982\u679C\u56E2\u961F\u9700\u8981\u5F3A\u5236 commit \u89C4\u8303\uFF0C\u5EFA\u8BAE\u5728\u9879\u76EE\u5C42\u9762\u914D\u7F6E Git hooks\uFF08\u5982 husky \u6216 native git hooks\uFF09\uFF1B\u4E0D\u8981\u8986\u76D6\u5DF2\u6709\u81EA\u5B9A\u4E49 hook\u3002
1300
1302
  5. \u5982\u679C\u7528\u6237\u8981\u5F00 PR\uFF0C\u53C2\u8003 \`.github/PULL_REQUEST_TEMPLATE.md\` \u8F93\u51FA PR \u63CF\u8FF0\u3002
1301
1303
 
1302
- ## \u94C1\u5F8B
1304
+ ${CORE_CODING_PRINCIPLES}
1305
+
1306
+ ## \u56E2\u961F\u6269\u5C55\u7EAA\u5F8B
1303
1307
 
1304
- 1. \u4E0D\u8986\u76D6\u672A\u786E\u8BA4\u7684\u7528\u6237/\u961F\u53CB\u6539\u52A8
1305
- 2. \u4E0D\u6539 plan \u5916\u6587\u4EF6
1306
- 3. \u5171\u4EAB\u63A5\u53E3\u53D8\u66F4\u5FC5\u987B\u5199\u517C\u5BB9\u6027\u8BF4\u660E
1307
- 4. \u5931\u8D25\u4E24\u6B21\u505C\u4E0B\u8BCA\u65AD
1308
- 5. \u4E0D\u81EA\u52A8 commit / merge / push
1308
+ 1. **\u4E0D\u8986\u76D6\u672A\u786E\u8BA4\u7684\u7528\u6237/\u961F\u53CB\u6539\u52A8** \u2014 \u6539\u52A8\u524D\u540E\u90FD\u68C0\u67E5\u5DE5\u4F5C\u533A\u72B6\u6001
1309
+ 2. **\u5171\u4EAB\u63A5\u53E3\u53D8\u66F4\u5FC5\u987B\u5199\u517C\u5BB9\u6027\u8BF4\u660E** \u2014 \u5305\u542B\u8FC1\u79FB\u3001\u56DE\u6EDA\u548C\u8C03\u7528\u65B9\u5F71\u54CD
1310
+ 3. **\u4E0D\u81EA\u52A8 commit / merge / push** \u2014 \u9664\u975E\u7528\u6237\u660E\u786E\u8981\u6C42
1309
1311
 
1310
1312
  \u6536\u5230 \`/manteam\` \u89E6\u53D1\u540E\u7ACB\u5373\u5F00\u59CB Step 0\u3002`
1311
1313
  };
@@ -1329,11 +1331,11 @@ ${spec.body}
1329
1331
  }
1330
1332
 
1331
1333
  // src/installers/common.ts
1332
- import { chmod, mkdir as mkdir2, readFile as readFile2, stat, writeFile as writeFile2 } from "fs/promises";
1334
+ import { chmod, mkdir as mkdir2, readFile, stat, writeFile as writeFile2 } from "fs/promises";
1333
1335
  import path2 from "path";
1334
1336
 
1335
1337
  // src/system/team-memory.ts
1336
- import { mkdir, readFile, writeFile } from "fs/promises";
1338
+ import { appendFile, mkdir, writeFile } from "fs/promises";
1337
1339
  import path from "path";
1338
1340
  var MEMORY_FILES = {
1339
1341
  prd: {
@@ -1384,9 +1386,9 @@ function memoryDir(projectRoot) {
1384
1386
  }
1385
1387
  async function writeIfMissing(file, content) {
1386
1388
  try {
1387
- await readFile(file, "utf-8");
1388
- } catch {
1389
- await writeFile(file, content, "utf-8");
1389
+ await writeFile(file, content, { flag: "wx" });
1390
+ } catch (err) {
1391
+ if (err.code !== "EEXIST") throw err;
1390
1392
  }
1391
1393
  }
1392
1394
  function renderMemoryFile(title, body) {
@@ -1461,7 +1463,7 @@ async function installMancodeCore(projectRoot) {
1461
1463
  }
1462
1464
  async function readTextIfExists(filePath) {
1463
1465
  try {
1464
- return await readFile2(filePath, "utf-8");
1466
+ return await readFile(filePath, "utf-8");
1465
1467
  } catch (err) {
1466
1468
  if (isNodeError(err) && err.code === "ENOENT") return "";
1467
1469
  throw err;
@@ -1494,18 +1496,19 @@ async function validateClaudeCodeSettings(projectRoot) {
1494
1496
  async function installClaudeCode(projectRoot, options) {
1495
1497
  const claudeDir = path3.join(projectRoot, ".claude");
1496
1498
  const settings = await readClaudeSettings(claudeDir);
1499
+ const force = options.force ?? false;
1497
1500
  await installMancodeCore(projectRoot);
1498
1501
  await mkdir3(path3.join(claudeDir, "skills"), { recursive: true });
1499
1502
  await installSoloSkill(path3.join(claudeDir, "skills"));
1500
1503
  if (options.minimal) {
1501
1504
  await uninstallMvp2Skills(path3.join(claudeDir, "skills"));
1502
1505
  } else {
1503
- await installMvp2Skills(path3.join(claudeDir, "skills"));
1506
+ await installMvp2Skills(path3.join(claudeDir, "skills"), force);
1504
1507
  }
1505
1508
  if (options.minimal) {
1506
1509
  await uninstallAgents(path3.join(claudeDir, "agents"));
1507
1510
  } else {
1508
- await installAgents(path3.join(claudeDir, "agents"));
1511
+ await installAgents(path3.join(claudeDir, "agents"), force);
1509
1512
  }
1510
1513
  await updateClaudeSettings(claudeDir, settings);
1511
1514
  }
@@ -1517,9 +1520,9 @@ async function installSoloSkill(skillsDir) {
1517
1520
  });
1518
1521
  await removeLegacyFlatSkill(skillsDir, "mancode-solo");
1519
1522
  }
1520
- async function installMvp2Skills(skillsDir) {
1523
+ async function installMvp2Skills(skillsDir, force) {
1521
1524
  for (const skill of MVP2_SKILLS) {
1522
- await installProjectSkill(skillsDir, skill);
1525
+ await installProjectSkill(skillsDir, skill, force);
1523
1526
  await removeLegacyFlatSkill(skillsDir, `mancode-${skill.name}`);
1524
1527
  }
1525
1528
  }
@@ -1532,19 +1535,35 @@ async function uninstallMvp2Skills(skillsDir) {
1532
1535
  await removeLegacyFlatSkill(skillsDir, `mancode-${skill.name}`);
1533
1536
  }
1534
1537
  }
1535
- async function installProjectSkill(skillsDir, skill) {
1538
+ async function installProjectSkill(skillsDir, skill, force = true) {
1536
1539
  const skillDir = path3.join(skillsDir, skill.name);
1540
+ const skillPath = path3.join(skillDir, "SKILL.md");
1541
+ if (!force) {
1542
+ try {
1543
+ await access(skillPath);
1544
+ return;
1545
+ } catch {
1546
+ }
1547
+ }
1537
1548
  await mkdir3(skillDir, { recursive: true });
1538
- await writeFile3(path3.join(skillDir, "SKILL.md"), renderSkill(skill), "utf-8");
1549
+ await writeFile3(skillPath, renderSkill(skill), "utf-8");
1539
1550
  }
1540
1551
  async function removeLegacyFlatSkill(skillsDir, legacyName) {
1541
1552
  await rm(path3.join(skillsDir, `${legacyName}.md`), { force: true });
1542
1553
  }
1543
- async function installAgents(agentsDir) {
1554
+ async function installAgents(agentsDir, force) {
1544
1555
  await mkdir3(agentsDir, { recursive: true });
1545
1556
  for (const agent of ALL_AGENTS) {
1557
+ const agentPath = path3.join(agentsDir, `${agent.name}.md`);
1558
+ if (!force) {
1559
+ try {
1560
+ await access(agentPath);
1561
+ continue;
1562
+ } catch {
1563
+ }
1564
+ }
1546
1565
  const content = renderAgent(agent);
1547
- await writeFile3(path3.join(agentsDir, `${agent.name}.md`), content, "utf-8");
1566
+ await writeFile3(agentPath, content, "utf-8");
1548
1567
  }
1549
1568
  }
1550
1569
  async function uninstallAgents(agentsDir) {
@@ -1555,7 +1574,7 @@ async function uninstallAgents(agentsDir) {
1555
1574
  async function readClaudeSettings(claudeDir) {
1556
1575
  const settingsPath = path3.join(claudeDir, "settings.json");
1557
1576
  try {
1558
- const raw = await readFile3(settingsPath, "utf-8");
1577
+ const raw = await readFile2(settingsPath, "utf-8");
1559
1578
  return JSON.parse(raw);
1560
1579
  } catch (err) {
1561
1580
  if (isNodeError2(err) && err.code === "ENOENT") {
@@ -1610,19 +1629,20 @@ function createCommandGroup(command) {
1610
1629
  }
1611
1630
  function normalizeHookGroups(value) {
1612
1631
  if (Array.isArray(value)) {
1613
- return value.flatMap((entry) => {
1614
- const group = normalizeMatcherGroup(entry);
1615
- if (group) return [group];
1616
- const hook = normalizeHookItem(entry);
1617
- if (hook && !isMancodeHook(hook)) return [{ hooks: [hook] }];
1618
- return [];
1619
- });
1632
+ return value.flatMap(normalizeHookGroupEntry);
1620
1633
  }
1621
1634
  if (isRecord(value)) {
1622
- return Object.values(value).flatMap((entry) => {
1623
- const group = normalizeMatcherGroup(entry);
1624
- return group ? [group] : [];
1625
- });
1635
+ return Object.values(value).flatMap(normalizeHookGroupEntry);
1636
+ }
1637
+ return [];
1638
+ }
1639
+ function normalizeHookGroupEntry(entry) {
1640
+ const group = normalizeMatcherGroup(entry);
1641
+ if (group) return [group];
1642
+ const hook = normalizeHookItem(entry);
1643
+ if (hook && !isMancodeHook(hook)) return [{ hooks: [hook] }];
1644
+ if (Array.isArray(entry)) {
1645
+ return entry.flatMap(normalizeHookGroupEntry);
1626
1646
  }
1627
1647
  return [];
1628
1648
  }
@@ -1660,8 +1680,8 @@ function isNodeError2(err) {
1660
1680
  }
1661
1681
 
1662
1682
  // src/installers/codex.ts
1663
- import { writeFile as writeFile4 } from "fs/promises";
1664
- import path5 from "path";
1683
+ import { writeFile as writeFile5 } from "fs/promises";
1684
+ import path6 from "path";
1665
1685
 
1666
1686
  // src/installers/managed-block.ts
1667
1687
  var DEFAULT_MANCODE_START_MARKER = "<!-- mancode:start -->";
@@ -1677,6 +1697,11 @@ function removeManagedBlock(existing, startMarker = DEFAULT_MANCODE_START_MARKER
1677
1697
  const merged = `${before}${after}`;
1678
1698
  return cleanUpOrphanedNewlines(merged);
1679
1699
  }
1700
+ function hasManagedBlock(existing, startMarker = DEFAULT_MANCODE_START_MARKER, endMarker = DEFAULT_MANCODE_END_MARKER) {
1701
+ const start = findMarkerLine(existing, startMarker);
1702
+ const end = findMarkerLine(existing, endMarker);
1703
+ return start !== null && end !== null && end.start > start.start;
1704
+ }
1680
1705
  function cleanUpOrphanedNewlines(content) {
1681
1706
  const trimmed = content.replace(/\n{3,}/gu, "\n\n").replace(/\n+$/u, "\n");
1682
1707
  return trimmed || "";
@@ -1748,16 +1773,322 @@ function findMarkerLine(content, marker) {
1748
1773
  return null;
1749
1774
  }
1750
1775
 
1776
+ // src/installers/mode-skills.ts
1777
+ import { mkdir as mkdir4, readFile as readFile3, readdir, rm as rm2, writeFile as writeFile4 } from "fs/promises";
1778
+ import path4 from "path";
1779
+ var MODE_NAMES = [
1780
+ "man8",
1781
+ "man",
1782
+ "manteam",
1783
+ "manps",
1784
+ "mansolo"
1785
+ ];
1786
+ var CODEX_SKILL_MANAGED_MARKER = "<!-- Managed by mancode:codex-skill. Do not edit this file manually. -->";
1787
+ var ZCODE_SKILL_MANAGED_MARKER = "<!-- Managed by mancode:zcode-skill. Do not edit this file manually. -->";
1788
+ function renderModeSkill(mode, commandPrefix = "$") {
1789
+ const meta = MODE_META[mode];
1790
+ const stateMode = mode === "mansolo" ? "solo" : mode;
1791
+ const modesList = MODE_NAMES.map((m) => `${commandPrefix}${m}`).join(", ");
1792
+ return [
1793
+ meta.intro,
1794
+ "",
1795
+ "## Mode Persistence",
1796
+ "",
1797
+ "Continue following these instructions for ALL subsequent tasks in this",
1798
+ `conversation until the user switches modes (${modesList}).`,
1799
+ "",
1800
+ "When entering this mode, update `.mancode/state.json` and set",
1801
+ `"currentMode" to "${stateMode}" so the mode persists across session restarts.`,
1802
+ "If unsure whether a mode is active, read `.mancode/state.json` to verify.",
1803
+ "",
1804
+ "## Practice",
1805
+ "",
1806
+ "Before writing new code, check the YAGNI ladder:",
1807
+ "",
1808
+ "1. Reuse existing project code.",
1809
+ "2. Use the standard library.",
1810
+ "3. Use platform-native behavior.",
1811
+ "4. Use already installed dependencies.",
1812
+ "5. Prefer a one-line fix when it is enough.",
1813
+ "6. Only then write the smallest new implementation.",
1814
+ "",
1815
+ "For every task, consider: why this change, what already exists, and what",
1816
+ "is the smallest useful diff?",
1817
+ "",
1818
+ "For UI tasks, read `.mancode/aesthetics/style-tokens.json` for current",
1819
+ "design tokens before inventing new styles.",
1820
+ "",
1821
+ meta.workflow
1822
+ ].join("\n");
1823
+ }
1824
+ async function installCodexSkills(projectRoot, minimal) {
1825
+ if (minimal) {
1826
+ await removeCodexSkills(projectRoot);
1827
+ return;
1828
+ }
1829
+ const skillsDir = path4.join(projectRoot, ".codex", "skills");
1830
+ for (const mode of MODE_NAMES) {
1831
+ const modeDir = path4.join(skillsDir, mode);
1832
+ await mkdir4(modeDir, { recursive: true });
1833
+ const meta = MODE_META[mode];
1834
+ const content = [
1835
+ "---",
1836
+ `name: ${mode}`,
1837
+ `description: ${JSON.stringify(meta.description)}`,
1838
+ "---",
1839
+ "",
1840
+ CODEX_SKILL_MANAGED_MARKER,
1841
+ "",
1842
+ renderModeSkill(mode, "$"),
1843
+ ""
1844
+ ].join("\n");
1845
+ await writeManagedSkill(
1846
+ path4.join(modeDir, "SKILL.md"),
1847
+ content,
1848
+ CODEX_SKILL_MANAGED_MARKER,
1849
+ "Codex"
1850
+ );
1851
+ }
1852
+ }
1853
+ async function installZcodeSkills(projectRoot, minimal) {
1854
+ if (minimal) {
1855
+ await removeZcodeSkills(projectRoot);
1856
+ return;
1857
+ }
1858
+ const skillsDir = path4.join(projectRoot, ".zcode", "skills");
1859
+ for (const mode of MODE_NAMES) {
1860
+ const modeDir = path4.join(skillsDir, mode);
1861
+ await mkdir4(modeDir, { recursive: true });
1862
+ const meta = MODE_META[mode];
1863
+ const content = [
1864
+ "---",
1865
+ `name: ${mode}`,
1866
+ `description: ${JSON.stringify(meta.description)}`,
1867
+ "---",
1868
+ "",
1869
+ ZCODE_SKILL_MANAGED_MARKER,
1870
+ "",
1871
+ renderModeSkill(mode, "$"),
1872
+ ""
1873
+ ].join("\n");
1874
+ await writeManagedSkill(
1875
+ path4.join(modeDir, "SKILL.md"),
1876
+ content,
1877
+ ZCODE_SKILL_MANAGED_MARKER,
1878
+ "ZCode"
1879
+ );
1880
+ }
1881
+ }
1882
+ async function installCursorCommands(projectRoot, minimal) {
1883
+ if (minimal) {
1884
+ await removeCursorCommands(projectRoot);
1885
+ return;
1886
+ }
1887
+ const commandsDir = path4.join(projectRoot, ".cursor", "commands");
1888
+ await mkdir4(commandsDir, { recursive: true });
1889
+ for (const mode of MODE_NAMES) {
1890
+ const meta = MODE_META[mode];
1891
+ const content = [
1892
+ "---",
1893
+ `description: ${JSON.stringify(meta.description)}`,
1894
+ "---",
1895
+ "",
1896
+ renderModeSkill(mode, "/"),
1897
+ ""
1898
+ ].join("\n");
1899
+ await writeFile4(path4.join(commandsDir, `${mode}.md`), content, "utf-8");
1900
+ }
1901
+ }
1902
+ async function installCopilotPrompts(projectRoot, minimal) {
1903
+ if (minimal) {
1904
+ await removeCopilotPrompts(projectRoot);
1905
+ return;
1906
+ }
1907
+ const promptsDir = path4.join(projectRoot, ".github", "prompts");
1908
+ await mkdir4(promptsDir, { recursive: true });
1909
+ for (const mode of MODE_NAMES) {
1910
+ const meta = MODE_META[mode];
1911
+ const content = [
1912
+ "---",
1913
+ "agent: 'agent'",
1914
+ `description: ${JSON.stringify(meta.description)}`,
1915
+ "---",
1916
+ "",
1917
+ renderModeSkill(mode, ""),
1918
+ ""
1919
+ ].join("\n");
1920
+ await writeFile4(
1921
+ path4.join(promptsDir, `${mode}.prompt.md`),
1922
+ content,
1923
+ "utf-8"
1924
+ );
1925
+ }
1926
+ }
1927
+ async function removeCodexSkills(projectRoot) {
1928
+ const skillsDir = path4.join(projectRoot, ".codex", "skills");
1929
+ for (const mode of MODE_NAMES) {
1930
+ const modeDir = path4.join(skillsDir, mode);
1931
+ const skillPath = path4.join(modeDir, "SKILL.md");
1932
+ if (await isManagedSkillFile(skillPath, CODEX_SKILL_MANAGED_MARKER)) {
1933
+ await rm2(skillPath, { force: true });
1934
+ await removeIfEmpty(modeDir);
1935
+ }
1936
+ }
1937
+ await removeIfEmpty(skillsDir);
1938
+ }
1939
+ async function removeZcodeSkills(projectRoot) {
1940
+ const skillsDir = path4.join(projectRoot, ".zcode", "skills");
1941
+ for (const mode of MODE_NAMES) {
1942
+ const modeDir = path4.join(skillsDir, mode);
1943
+ const skillPath = path4.join(modeDir, "SKILL.md");
1944
+ if (await isManagedSkillFile(skillPath, ZCODE_SKILL_MANAGED_MARKER)) {
1945
+ await rm2(skillPath, { force: true });
1946
+ await removeIfEmpty(modeDir);
1947
+ }
1948
+ }
1949
+ await removeIfEmpty(skillsDir);
1950
+ }
1951
+ async function removeCursorCommands(projectRoot) {
1952
+ for (const mode of MODE_NAMES) {
1953
+ await rm2(path4.join(projectRoot, ".cursor", "commands", `${mode}.md`), {
1954
+ force: true
1955
+ });
1956
+ }
1957
+ }
1958
+ async function removeCopilotPrompts(projectRoot) {
1959
+ for (const mode of MODE_NAMES) {
1960
+ await rm2(
1961
+ path4.join(projectRoot, ".github", "prompts", `${mode}.prompt.md`),
1962
+ { force: true }
1963
+ );
1964
+ }
1965
+ }
1966
+ async function removeIfEmpty(dir) {
1967
+ try {
1968
+ const entries = await readdir(dir);
1969
+ if (entries.length === 0) {
1970
+ await rm2(dir, { recursive: true, force: true });
1971
+ }
1972
+ } catch {
1973
+ }
1974
+ }
1975
+ async function writeManagedSkill(skillPath, content, marker, platformLabel) {
1976
+ const existing = await readTextIfExists2(skillPath);
1977
+ if (existing !== null && !existing.includes(marker)) {
1978
+ throw new Error(
1979
+ `refusing to overwrite user-authored ${platformLabel} skill: ${skillPath}`
1980
+ );
1981
+ }
1982
+ await writeFile4(skillPath, content, "utf-8");
1983
+ }
1984
+ async function isManagedSkillFile(skillPath, marker) {
1985
+ const content = await readTextIfExists2(skillPath);
1986
+ return content?.includes(marker) ?? false;
1987
+ }
1988
+ async function readTextIfExists2(filePath) {
1989
+ try {
1990
+ return await readFile3(filePath, "utf-8");
1991
+ } catch {
1992
+ return null;
1993
+ }
1994
+ }
1995
+ var MODE_META = {
1996
+ man8: {
1997
+ description: "Investigate first, then produce a plan before implementation. Use when the user asks for man8, planning, architecture, migration, integration, or risk assessment.",
1998
+ intro: "# mancode man8 \u2014 Investigate and Plan",
1999
+ workflow: [
2000
+ "## Workflow (simulate in single conversation)",
2001
+ "",
2002
+ "This platform does not provide isolated subagents. Simulate the coaching",
2003
+ "staff in sequence:",
2004
+ "",
2005
+ "1. **Scout**: Investigate the relevant code and summarize constraints,",
2006
+ " existing patterns, and dependencies.",
2007
+ "2. **Head Coach**: Write a concrete implementation plan based on the",
2008
+ " Scout findings.",
2009
+ "3. **Stop for user approval** before implementing. Do not write code",
2010
+ " until the user confirms.",
2011
+ "",
2012
+ "After user approval, switch to solo mode (update state.json currentMode",
2013
+ 'to "solo") and implement the plan.'
2014
+ ].join("\n")
2015
+ },
2016
+ man: {
2017
+ description: "Full high-risk workflow with plan, implementation, verification, and review. Use when the user asks for man or a high-risk production change.",
2018
+ intro: "# mancode man \u2014 Full Review Workflow",
2019
+ workflow: [
2020
+ "## Workflow (simulate in single conversation)",
2021
+ "",
2022
+ "This platform does not provide isolated subagents. Simulate the full",
2023
+ "coaching staff workflow in sequence:",
2024
+ "",
2025
+ "1. **Scout**: Investigate the codebase and summarize constraints.",
2026
+ "2. **Head Coach**: Propose a concrete implementation plan.",
2027
+ "3. **Wait for approval** when the task is high-risk.",
2028
+ "4. **Implement** the plan and self-test.",
2029
+ "5. **Film Analyst Offense**: Review readability, DRY, and YAGNI.",
2030
+ "6. **Fix** review findings.",
2031
+ "7. **Film Analyst Defense**: Review auth, injection, concurrency, and",
2032
+ " resource risks.",
2033
+ "8. **Summarize** verification results and remaining risk."
2034
+ ].join("\n")
2035
+ },
2036
+ manteam: {
2037
+ description: "Team-aware workflow with handoff-friendly summaries. Use when the user asks for manteam or when the task affects shared team context.",
2038
+ intro: "# mancode manteam \u2014 Team Workflow",
2039
+ workflow: [
2040
+ "## Workflow",
2041
+ "",
2042
+ "1. Read `.mancode/memory/prd.md`, `.mancode/memory/spec.md`, and",
2043
+ " `.mancode/memory/decisions.md` when relevant.",
2044
+ "2. Preserve decisions and leave handoff-friendly summaries.",
2045
+ "3. Prefer Conventional Commit style when suggesting commits.",
2046
+ "4. Simulate the coaching staff in sequence (same as man mode) but",
2047
+ " with extra emphasis on documentation and handoff clarity."
2048
+ ].join("\n")
2049
+ },
2050
+ manps: {
2051
+ description: "Run project health and cleanup scans before remediation. Use when the user asks for manps or project health cleanup.",
2052
+ intro: "# mancode manps \u2014 Project Health Scan",
2053
+ workflow: [
2054
+ "## Workflow",
2055
+ "",
2056
+ "1. Run `mancode manps [area]` to scan for tech debt, unused dependencies,",
2057
+ " security issues, dead code, and config drift.",
2058
+ "2. Supported areas: all, deps, security, dead-code, config.",
2059
+ "3. Treat scan output as triage data \u2014 do not remediate without clear",
2060
+ " user approval for each item.",
2061
+ "4. Present findings as a prioritized list with severity and recommendation."
2062
+ ].join("\n")
2063
+ },
2064
+ mansolo: {
2065
+ description: "Exit any active mancode mode and return to default solo behavior.",
2066
+ intro: "# mancode mansolo \u2014 Return to Solo",
2067
+ workflow: [
2068
+ "## Solo Behavior",
2069
+ "",
2070
+ "You are now in solo (default) mode.",
2071
+ "",
2072
+ 'Update `.mancode/state.json` and set `currentMode` to "solo".',
2073
+ "",
2074
+ "- Keep the diff narrow.",
2075
+ "- Reuse existing functions, components, styles, and dependencies.",
2076
+ "- For UI work, read `.mancode/aesthetics/style-tokens.json`.",
2077
+ "- Verify with the narrowest meaningful test, lint, build, or smoke check."
2078
+ ].join("\n")
2079
+ }
2080
+ };
2081
+
1751
2082
  // src/installers/shared-content.ts
1752
2083
  import { readFile as readFile4 } from "fs/promises";
1753
- import path4 from "path";
2084
+ import path5 from "path";
1754
2085
  async function generateSharedContent(projectRoot, options) {
1755
2086
  const [state, tokens] = await Promise.all([
1756
2087
  readJson(
1757
- path4.join(projectRoot, ".mancode", "state.json")
2088
+ path5.join(projectRoot, ".mancode", "state.json")
1758
2089
  ),
1759
2090
  readJson(
1760
- path4.join(projectRoot, ".mancode", "aesthetics", "style-tokens.json")
2091
+ path5.join(projectRoot, ".mancode", "aesthetics", "style-tokens.json")
1761
2092
  )
1762
2093
  ]);
1763
2094
  const sections = [
@@ -1833,7 +2164,14 @@ function renderAesthetics(tokens) {
1833
2164
  return lines.join("\n");
1834
2165
  }
1835
2166
  function renderModes(options) {
1836
- const commandLabel = options.capabilities.slashCommands === "native" ? "Use the named commands directly when available." : "Treat these as prompt conventions when native commands are unavailable.";
2167
+ let commandLabel;
2168
+ if (options.capabilities.skills === "agents-skills") {
2169
+ commandLabel = "Invoke mode skills with `$man8`, `$man`, `$manteam`, `$manps`, `$mansolo`.";
2170
+ } else if (options.capabilities.slashCommands === "native") {
2171
+ commandLabel = "Use the named commands directly when available.";
2172
+ } else {
2173
+ commandLabel = "Treat these as prompt conventions when native commands are unavailable.";
2174
+ }
1837
2175
  return [
1838
2176
  "## mancode Modes",
1839
2177
  "",
@@ -1897,7 +2235,7 @@ function formatValue(value) {
1897
2235
  // src/installers/codex.ts
1898
2236
  async function installCodex(projectRoot, options) {
1899
2237
  await installMancodeCore(projectRoot);
1900
- const agentsPath = path5.join(projectRoot, "AGENTS.md");
2238
+ const agentsPath = path6.join(projectRoot, "AGENTS.md");
1901
2239
  const existing = await readTextIfExists(agentsPath);
1902
2240
  const sharedContent = await generateSharedContent(projectRoot, {
1903
2241
  platform: "codex",
@@ -1906,7 +2244,7 @@ async function installCodex(projectRoot, options) {
1906
2244
  slashCommands: "partial",
1907
2245
  subagents: false,
1908
2246
  hooks: false,
1909
- skills: "single-file"
2247
+ skills: "agents-skills"
1910
2248
  },
1911
2249
  minimal: options.minimal,
1912
2250
  techStack: options.techStack,
@@ -1921,17 +2259,18 @@ async function installCodex(projectRoot, options) {
1921
2259
  sharedContent.trim(),
1922
2260
  DEFAULT_MANCODE_END_MARKER
1923
2261
  ].join("\n");
1924
- await writeFile4(agentsPath, replaceManagedBlock(existing, block), "utf-8");
2262
+ await writeFile5(agentsPath, replaceManagedBlock(existing, block), "utf-8");
2263
+ await installCodexSkills(projectRoot, options.minimal ?? false);
1925
2264
  }
1926
2265
 
1927
2266
  // src/installers/copilot.ts
1928
- import { mkdir as mkdir4, writeFile as writeFile5 } from "fs/promises";
1929
- import path6 from "path";
2267
+ import { mkdir as mkdir5, writeFile as writeFile6 } from "fs/promises";
2268
+ import path7 from "path";
1930
2269
  async function installCopilot(projectRoot, options) {
1931
2270
  await installMancodeCore(projectRoot);
1932
- const githubDir = path6.join(projectRoot, ".github");
1933
- await mkdir4(githubDir, { recursive: true });
1934
- const instructionsPath = path6.join(githubDir, "copilot-instructions.md");
2271
+ const githubDir = path7.join(projectRoot, ".github");
2272
+ await mkdir5(githubDir, { recursive: true });
2273
+ const instructionsPath = path7.join(githubDir, "copilot-instructions.md");
1935
2274
  const existing = await readTextIfExists(instructionsPath);
1936
2275
  const sharedContent = await generateSharedContent(projectRoot, {
1937
2276
  platform: "copilot",
@@ -1958,11 +2297,12 @@ async function installCopilot(projectRoot, options) {
1958
2297
  sections.push("", renderCopilotPromptConventions());
1959
2298
  }
1960
2299
  sections.push(DEFAULT_MANCODE_END_MARKER);
1961
- await writeFile5(
2300
+ await writeFile6(
1962
2301
  instructionsPath,
1963
2302
  replaceManagedBlock(existing, sections.join("\n")),
1964
2303
  "utf-8"
1965
2304
  );
2305
+ await installCopilotPrompts(projectRoot, options.minimal ?? false);
1966
2306
  }
1967
2307
  function renderCopilotPromptConventions() {
1968
2308
  return [
@@ -1973,13 +2313,16 @@ function renderCopilotPromptConventions() {
1973
2313
  "- man8: investigate first and produce a plan before implementation.",
1974
2314
  "- man: use a careful plan, implementation, verification, and review loop.",
1975
2315
  "- manteam: read team memory and write handoff-friendly summaries.",
1976
- "- manps: prefer `mancode manps [area]` before cleanup."
2316
+ "- manps: prefer `mancode manps [area]` before cleanup.",
2317
+ "- mansolo: exit any active mode and return to default solo behavior.",
2318
+ "",
2319
+ "These correspond to prompt files in `.github/prompts/`. Select the matching prompt to activate a mode."
1977
2320
  ].join("\n");
1978
2321
  }
1979
2322
 
1980
2323
  // src/installers/cursor.ts
1981
- import { mkdir as mkdir5, rm as rm2, writeFile as writeFile6 } from "fs/promises";
1982
- import path7 from "path";
2324
+ import { mkdir as mkdir6, rm as rm3, writeFile as writeFile7 } from "fs/promises";
2325
+ import path8 from "path";
1983
2326
  var MANCODE_CURSOR_CORE_RULE_FILES = [
1984
2327
  "mancode-context.mdc",
1985
2328
  "mancode-practice.mdc",
@@ -1997,8 +2340,8 @@ var MANCODE_CURSOR_RULE_FILES = [
1997
2340
  ];
1998
2341
  async function installCursor(projectRoot, options) {
1999
2342
  await installMancodeCore(projectRoot);
2000
- const rulesDir = path7.join(projectRoot, ".cursor", "rules");
2001
- await mkdir5(rulesDir, { recursive: true });
2343
+ const rulesDir = path8.join(projectRoot, ".cursor", "rules");
2344
+ await mkdir6(rulesDir, { recursive: true });
2002
2345
  const sharedContent = await generateSharedContent(projectRoot, {
2003
2346
  platform: "cursor",
2004
2347
  displayName: "Cursor",
@@ -2037,6 +2380,7 @@ async function installCursor(projectRoot, options) {
2037
2380
  );
2038
2381
  if (options.minimal) {
2039
2382
  await removeAdvancedRules(rulesDir);
2383
+ await installCursorCommands(projectRoot, true);
2040
2384
  return;
2041
2385
  }
2042
2386
  await writeRule(
@@ -2067,6 +2411,7 @@ async function installCursor(projectRoot, options) {
2067
2411
  false,
2068
2412
  renderManpsRule()
2069
2413
  );
2414
+ await installCursorCommands(projectRoot, options.minimal ?? false);
2070
2415
  }
2071
2416
  async function writeRule(rulesDir, fileName, description, alwaysApply, body) {
2072
2417
  const frontmatter = [
@@ -2082,11 +2427,11 @@ async function writeRule(rulesDir, fileName, description, alwaysApply, body) {
2082
2427
 
2083
2428
  ${body.trim()}
2084
2429
  `;
2085
- await writeFile6(path7.join(rulesDir, fileName), content, "utf-8");
2430
+ await writeFile7(path8.join(rulesDir, fileName), content, "utf-8");
2086
2431
  }
2087
2432
  async function removeAdvancedRules(rulesDir) {
2088
2433
  for (const fileName of MANCODE_CURSOR_ADVANCED_RULE_FILES) {
2089
- await rm2(path7.join(rulesDir, fileName), { force: true });
2434
+ await rm3(path8.join(rulesDir, fileName), { force: true });
2090
2435
  }
2091
2436
  }
2092
2437
  function renderPracticeRule() {
@@ -2118,57 +2463,60 @@ function renderSoloRule() {
2118
2463
  ].join("\n");
2119
2464
  }
2120
2465
  function renderMan8Rule() {
2121
- return [
2122
- "# mancode man8",
2123
- "",
2124
- "Use this when the user asks for /man8, planning, investigation, architecture, migration, integration, or risk assessment.",
2125
- "",
2126
- "Cursor does not provide isolated mancode subagents. Simulate the flow in sequence:",
2127
- "",
2128
- "1. Scout: inspect the relevant code and summarize constraints.",
2129
- "2. Head Coach: write a concrete implementation plan.",
2130
- "3. Stop for user approval when the user asked for planning only."
2131
- ].join("\n");
2466
+ return renderModeSkill("man8", "/");
2132
2467
  }
2133
2468
  function renderManRule() {
2134
- return [
2135
- "# mancode man",
2136
- "",
2137
- "Use this when the user asks for /man or a high-risk production change.",
2138
- "",
2139
- "Simulate the full workflow in one conversation:",
2140
- "",
2141
- "1. Scout investigates the codebase.",
2142
- "2. Head Coach proposes the plan.",
2143
- "3. Wait for approval when required.",
2144
- "4. Implement and self-test.",
2145
- "5. Film Analyst Offense reviews readability, DRY, and YAGNI.",
2146
- "6. Fix review findings.",
2147
- "7. Film Analyst Defense reviews auth, injection, concurrency, and resource risks.",
2148
- "8. Summarize verification and remaining risk."
2149
- ].join("\n");
2469
+ return renderModeSkill("man", "/");
2150
2470
  }
2151
2471
  function renderManteamRule() {
2152
- return [
2153
- "# mancode manteam",
2154
- "",
2155
- "Use this when the user asks for /manteam or when the task affects shared team context.",
2156
- "",
2157
- "- Read `.mancode/memory/prd.md`, `.mancode/memory/spec.md`, and `.mancode/memory/decisions.md` when relevant.",
2158
- "- Preserve decisions and leave handoff-friendly summaries.",
2159
- "- Prefer Conventional Commit style when suggesting commits."
2160
- ].join("\n");
2472
+ return renderModeSkill("manteam", "/");
2161
2473
  }
2162
2474
  function renderManpsRule() {
2163
- return [
2164
- "# mancode manps",
2475
+ return renderModeSkill("manps", "/");
2476
+ }
2477
+
2478
+ // src/installers/zcode.ts
2479
+ import { writeFile as writeFile8 } from "fs/promises";
2480
+ import path9 from "path";
2481
+ var ZCODE_MANCODE_START_MARKER = "<!-- mancode:zcode:start -->";
2482
+ var ZCODE_MANCODE_END_MARKER = "<!-- mancode:zcode:end -->";
2483
+ async function installZcode(projectRoot, options) {
2484
+ await installMancodeCore(projectRoot);
2485
+ const agentsPath = path9.join(projectRoot, "AGENTS.md");
2486
+ const existing = await readTextIfExists(agentsPath);
2487
+ const sharedContent = await generateSharedContent(projectRoot, {
2488
+ platform: "zcode",
2489
+ displayName: "ZCode",
2490
+ capabilities: {
2491
+ slashCommands: "partial",
2492
+ subagents: false,
2493
+ hooks: false,
2494
+ skills: "agents-skills"
2495
+ },
2496
+ minimal: options.minimal,
2497
+ techStack: options.techStack,
2498
+ uiLibrary: options.uiLibrary
2499
+ });
2500
+ const block = [
2501
+ ZCODE_MANCODE_START_MARKER,
2502
+ "<!-- Managed by mancode. Do not edit this block manually. -->",
2165
2503
  "",
2166
- "Use this when the user asks for /manps or project health cleanup.",
2504
+ "# mancode Configuration",
2167
2505
  "",
2168
- "- Prefer `mancode manps [area]` before manual cleanup.",
2169
- "- Supported areas: all, deps, security, dead-code, config.",
2170
- "- Treat scan output as triage data; do not remediate without clear user approval."
2506
+ sharedContent.trim(),
2507
+ ZCODE_MANCODE_END_MARKER
2171
2508
  ].join("\n");
2509
+ await writeFile8(
2510
+ agentsPath,
2511
+ replaceManagedBlock(
2512
+ existing,
2513
+ block,
2514
+ ZCODE_MANCODE_START_MARKER,
2515
+ ZCODE_MANCODE_END_MARKER
2516
+ ),
2517
+ "utf-8"
2518
+ );
2519
+ await installZcodeSkills(projectRoot, options.minimal ?? false);
2172
2520
  }
2173
2521
 
2174
2522
  // src/installers/registry.ts
@@ -2202,7 +2550,7 @@ var PLATFORM_INSTALLERS = {
2202
2550
  slashCommands: "partial",
2203
2551
  subagents: false,
2204
2552
  hooks: false,
2205
- skills: "single-file"
2553
+ skills: "agents-skills"
2206
2554
  },
2207
2555
  install: installCodex
2208
2556
  },
@@ -2216,6 +2564,17 @@ var PLATFORM_INSTALLERS = {
2216
2564
  skills: "instructions"
2217
2565
  },
2218
2566
  install: installCopilot
2567
+ },
2568
+ zcode: {
2569
+ name: "zcode",
2570
+ displayName: "ZCode",
2571
+ capabilities: {
2572
+ slashCommands: "partial",
2573
+ subagents: false,
2574
+ hooks: false,
2575
+ skills: "agents-skills"
2576
+ },
2577
+ install: installZcode
2219
2578
  }
2220
2579
  };
2221
2580
  function getPlatformInstaller(platform) {
@@ -2234,7 +2593,7 @@ function isPlatformName(platform) {
2234
2593
 
2235
2594
  // src/system/detect-team.ts
2236
2595
  import { exec } from "child_process";
2237
- import path8 from "path";
2596
+ import path10 from "path";
2238
2597
  import process2 from "process";
2239
2598
  import { promisify } from "util";
2240
2599
  var execAsync = promisify(exec);
@@ -2256,7 +2615,7 @@ var EXEC_OPTS = {
2256
2615
  }
2257
2616
  };
2258
2617
  async function detectTeamStatus(projectRoot) {
2259
- const gitDir = path8.join(projectRoot, ".git");
2618
+ const gitDir = path10.join(projectRoot, ".git");
2260
2619
  try {
2261
2620
  await execAsync("git rev-parse --is-inside-work-tree", {
2262
2621
  cwd: projectRoot,
@@ -2318,8 +2677,8 @@ async function checkRemote(cwd) {
2318
2677
 
2319
2678
  // src/system/detect.ts
2320
2679
  import { exec as exec2 } from "child_process";
2321
- import { access, readFile as readFile5 } from "fs/promises";
2322
- import path9 from "path";
2680
+ import { access as access2, readFile as readFile5 } from "fs/promises";
2681
+ import path11 from "path";
2323
2682
  import { promisify as promisify2 } from "util";
2324
2683
  var execAsync2 = promisify2(exec2);
2325
2684
  async function detectSystemDeps() {
@@ -2344,9 +2703,9 @@ async function detectSystemDeps() {
2344
2703
  return { bash, git, jq, node };
2345
2704
  }
2346
2705
  async function detectProjectType(projectRoot) {
2347
- const pkgPath = path9.join(projectRoot, "package.json");
2706
+ const pkgPath = path11.join(projectRoot, "package.json");
2348
2707
  try {
2349
- await access(pkgPath);
2708
+ await access2(pkgPath);
2350
2709
  } catch (err) {
2351
2710
  if (err.code === "ENOENT") {
2352
2711
  return {
@@ -2378,7 +2737,7 @@ async function detectProjectType(projectRoot) {
2378
2737
  if (deps.includes("tailwindcss")) techStack.push("Tailwind CSS");
2379
2738
  let uiLibrary = null;
2380
2739
  const hasRadix = deps.some((d) => d.startsWith("@radix-ui/"));
2381
- const uiDir = path9.join(projectRoot, "src", "components", "ui");
2740
+ const uiDir = path11.join(projectRoot, "src", "components", "ui");
2382
2741
  const hasUiDir = await pathExists2(uiDir);
2383
2742
  if (hasRadix && hasUiDir) {
2384
2743
  uiLibrary = "shadcn/ui";
@@ -2393,7 +2752,7 @@ async function detectProjectType(projectRoot) {
2393
2752
  }
2394
2753
  async function pathExists2(p) {
2395
2754
  try {
2396
- await access(p);
2755
+ await access2(p);
2397
2756
  return true;
2398
2757
  } catch {
2399
2758
  return false;
@@ -2402,7 +2761,7 @@ async function pathExists2(p) {
2402
2761
 
2403
2762
  // src/system/scan-aesthetics.ts
2404
2763
  import { promises as fs } from "fs";
2405
- import path10 from "path";
2764
+ import path12 from "path";
2406
2765
  var MAX_COMPONENT_SCAN_DEPTH = 12;
2407
2766
  var MAX_COMPONENT_FILES = 2e3;
2408
2767
  async function scanAesthetics(projectRoot, uiLibrary = null) {
@@ -2462,7 +2821,7 @@ async function findTailwindConfig(projectRoot) {
2462
2821
  "tailwind.config.mjs"
2463
2822
  ];
2464
2823
  for (const name of candidates) {
2465
- const absPath = path10.join(projectRoot, name);
2824
+ const absPath = path12.join(projectRoot, name);
2466
2825
  if (await pathExists3(absPath)) {
2467
2826
  return { absPath, relPath: name };
2468
2827
  }
@@ -2682,7 +3041,7 @@ async function scanComponents(projectRoot) {
2682
3041
  const names = /* @__PURE__ */ new Set();
2683
3042
  let visitedFiles = 0;
2684
3043
  for (const relRoot of roots) {
2685
- const absRoot = path10.join(projectRoot, relRoot);
3044
+ const absRoot = path12.join(projectRoot, relRoot);
2686
3045
  if (!await pathExists3(absRoot)) continue;
2687
3046
  visitedFiles = await collectComponentNames(absRoot, names, 0, visitedFiles);
2688
3047
  if (visitedFiles >= MAX_COMPONENT_FILES) break;
@@ -2702,7 +3061,7 @@ async function collectComponentNames(dir, names, depth, visitedFiles) {
2702
3061
  }
2703
3062
  for (const entry of entries) {
2704
3063
  if (fileCount >= MAX_COMPONENT_FILES) return fileCount;
2705
- const abs = path10.join(dir, entry);
3064
+ const abs = path12.join(dir, entry);
2706
3065
  let info;
2707
3066
  try {
2708
3067
  info = await fs.lstat(abs);
@@ -2719,7 +3078,7 @@ async function collectComponentNames(dir, names, depth, visitedFiles) {
2719
3078
  if (isNonComponentFile(entry)) continue;
2720
3079
  let base = entry.replace(/\.(tsx|jsx|ts|js|vue|svelte)$/, "");
2721
3080
  if (base === "index") {
2722
- base = path10.basename(dir);
3081
+ base = path12.basename(dir);
2723
3082
  }
2724
3083
  if (base.startsWith(".")) continue;
2725
3084
  const componentName = toPascalCase(base);
@@ -2741,7 +3100,7 @@ async function scanCssVariables(projectRoot) {
2741
3100
  const variables = {};
2742
3101
  const sourceFiles = [];
2743
3102
  for (const relPath of candidates) {
2744
- const absPath = path10.join(projectRoot, relPath);
3103
+ const absPath = path12.join(projectRoot, relPath);
2745
3104
  if (!await pathExists3(absPath)) continue;
2746
3105
  const content = await fs.readFile(absPath, "utf-8");
2747
3106
  const found = extractCssVariables(content);
@@ -2787,7 +3146,7 @@ function toPascalCase(value) {
2787
3146
  async function hasTailwindDep(projectRoot) {
2788
3147
  try {
2789
3148
  const raw = await fs.readFile(
2790
- path10.join(projectRoot, "package.json"),
3149
+ path12.join(projectRoot, "package.json"),
2791
3150
  "utf-8"
2792
3151
  );
2793
3152
  const pkg = JSON.parse(raw);
@@ -2817,8 +3176,8 @@ var EXIT_NOT_A_PROJECT_DIR = 2;
2817
3176
  var EXIT_INIT_FAILED = 5;
2818
3177
  var DEFAULT_INIT_PLATFORM = "claude-code";
2819
3178
  async function init(rootDir = process3.cwd(), options = {}) {
2820
- const mancodeDir = path11.join(rootDir, ".mancode");
2821
- const stateFile = path11.join(mancodeDir, "state.json");
3179
+ const mancodeDir = path13.join(rootDir, ".mancode");
3180
+ const stateFile = path13.join(mancodeDir, "state.json");
2822
3181
  const wasInitialized = await pathExists4(stateFile);
2823
3182
  if (wasInitialized) {
2824
3183
  if (!options.force) {
@@ -2833,8 +3192,8 @@ async function init(rootDir = process3.cwd(), options = {}) {
2833
3192
  console.error(`\u2717 Target directory does not exist: ${rootDir}`);
2834
3193
  return EXIT_NOT_A_PROJECT_DIR;
2835
3194
  }
2836
- const isGitRepo = await pathExists4(path11.join(rootDir, ".git"));
2837
- const hasPackageJson = await pathExists4(path11.join(rootDir, "package.json"));
3195
+ const isGitRepo = await pathExists4(path13.join(rootDir, ".git"));
3196
+ const hasPackageJson = await pathExists4(path13.join(rootDir, "package.json"));
2838
3197
  if (!isGitRepo && !hasPackageJson) {
2839
3198
  console.error(`\u2717 Not a project directory: ${rootDir}`);
2840
3199
  console.error(" (No .git or package.json found)");
@@ -2918,13 +3277,16 @@ async function init(rootDir = process3.cwd(), options = {}) {
2918
3277
  await updateConfigOptions(mancodeDir, {
2919
3278
  forceTeamMode: options.team === true,
2920
3279
  defaultStyle: options.style ?? null,
2921
- platforms: [installer.name]
3280
+ platforms: [installer.name],
3281
+ platformOptions: {
3282
+ [installer.name]: { minimal: false }
3283
+ }
2922
3284
  });
2923
3285
  let styleLine = " .mancode/aesthetics/ # style-tokens.json (\u7A7A)";
2924
3286
  if (project.hasFrontend) {
2925
3287
  console.log("\u2713 \u626B\u63CF\u5BA1\u7F8E token...");
2926
3288
  const tokens = await scanAesthetics(rootDir, project.uiLibrary);
2927
- const tokensPath = path11.join(
3289
+ const tokensPath = path13.join(
2928
3290
  mancodeDir,
2929
3291
  "aesthetics",
2930
3292
  "style-tokens.json"
@@ -2984,7 +3346,7 @@ async function init(rootDir = process3.cwd(), options = {}) {
2984
3346
  }
2985
3347
  }
2986
3348
  async function updateConfigOptions(mancodeDir, patch) {
2987
- const configPath = path11.join(mancodeDir, "config.json");
3349
+ const configPath = path13.join(mancodeDir, "config.json");
2988
3350
  let config = {};
2989
3351
  try {
2990
3352
  config = JSON.parse(await fs2.readFile(configPath, "utf-8"));
@@ -3009,6 +3371,12 @@ function printPlatformCreatedFiles(platform) {
3009
3371
  }
3010
3372
  if (platform === "codex") {
3011
3373
  console.log(" AGENTS.md # Codex managed block");
3374
+ console.log(" .codex/skills/ # Codex mode skills");
3375
+ return;
3376
+ }
3377
+ if (platform === "zcode") {
3378
+ console.log(" AGENTS.md # ZCode managed block");
3379
+ console.log(" .zcode/skills/ # ZCode mode skills");
3012
3380
  return;
3013
3381
  }
3014
3382
  console.log(" .github/copilot-instructions.md # Copilot instructions");
@@ -3024,17 +3392,17 @@ async function pathExists4(p) {
3024
3392
 
3025
3393
  // src/commands/install.ts
3026
3394
  import { promises as fs4 } from "fs";
3027
- import path13 from "path";
3395
+ import path15 from "path";
3028
3396
  import process4 from "process";
3029
3397
 
3030
3398
  // src/installers/platform-status.ts
3031
3399
  import { promises as fs3 } from "fs";
3032
- import path12 from "path";
3400
+ import path14 from "path";
3033
3401
  async function checkPlatformStatus(rootDir, platform, installed) {
3034
3402
  const readiness = await checkPlatformReadiness(rootDir, platform);
3035
3403
  return {
3036
3404
  installed,
3037
- ready: installed && readiness.present,
3405
+ ready: installed && readiness.ready,
3038
3406
  target: readiness.target,
3039
3407
  detail: describeStatus(installed, readiness)
3040
3408
  };
@@ -3042,11 +3410,13 @@ async function checkPlatformStatus(rootDir, platform, installed) {
3042
3410
  async function checkPlatformReadiness(rootDir, platform) {
3043
3411
  if (platform === "claude-code") {
3044
3412
  const hasSoloSkill = await pathExists5(
3045
- path12.join(rootDir, ".claude", "skills", "solo", "SKILL.md")
3413
+ path14.join(rootDir, ".claude", "skills", "solo", "SKILL.md")
3046
3414
  );
3047
3415
  const registered = await claudeHooksRegistered(rootDir);
3416
+ const present = hasSoloSkill && registered;
3048
3417
  return {
3049
- present: hasSoloSkill && registered,
3418
+ present,
3419
+ ready: present,
3050
3420
  target: ".claude/",
3051
3421
  readyDetail: "skills and hooks registered"
3052
3422
  };
@@ -3054,26 +3424,104 @@ async function checkPlatformReadiness(rootDir, platform) {
3054
3424
  if (platform === "cursor") {
3055
3425
  const present = await allPathsExist(
3056
3426
  MANCODE_CURSOR_CORE_RULE_FILES.map(
3057
- (file) => path12.join(rootDir, ".cursor", "rules", file)
3427
+ (file) => path14.join(rootDir, ".cursor", "rules", file)
3058
3428
  )
3059
3429
  );
3060
3430
  return {
3061
3431
  present,
3432
+ ready: present,
3062
3433
  target: ".cursor/rules/",
3063
3434
  readyDetail: "mancode rules present"
3064
3435
  };
3065
3436
  }
3066
3437
  if (platform === "codex") {
3438
+ const hasBlock2 = await fileHasManagedBlock(path14.join(rootDir, "AGENTS.md"));
3439
+ if (!hasBlock2) {
3440
+ return {
3441
+ present: false,
3442
+ ready: false,
3443
+ target: "AGENTS.md",
3444
+ readyDetail: "managed block missing"
3445
+ };
3446
+ }
3447
+ if (await isPlatformMinimal(rootDir, "codex")) {
3448
+ return {
3449
+ present: true,
3450
+ ready: true,
3451
+ target: "AGENTS.md",
3452
+ readyDetail: "managed block present"
3453
+ };
3454
+ }
3455
+ const skillsDir = path14.join(rootDir, ".codex", "skills");
3456
+ const hasSkills = await allManagedSkills(
3457
+ MODE_NAMES.map((mode) => path14.join(skillsDir, mode, "SKILL.md")),
3458
+ CODEX_SKILL_MANAGED_MARKER
3459
+ );
3067
3460
  return {
3068
- present: await fileHasManagedBlock(path12.join(rootDir, "AGENTS.md")),
3069
- target: "AGENTS.md",
3070
- readyDetail: "managed block present"
3461
+ present: true,
3462
+ ready: hasSkills,
3463
+ target: "AGENTS.md + .codex/skills/",
3464
+ readyDetail: hasSkills ? "managed block and skills present" : "mode skills are missing, incomplete, or user-authored"
3465
+ };
3466
+ }
3467
+ if (platform === "zcode") {
3468
+ const hasBlock2 = await fileHasManagedBlock(
3469
+ path14.join(rootDir, "AGENTS.md"),
3470
+ ZCODE_MANCODE_START_MARKER,
3471
+ ZCODE_MANCODE_END_MARKER
3472
+ );
3473
+ if (!hasBlock2) {
3474
+ return {
3475
+ present: false,
3476
+ ready: false,
3477
+ target: "AGENTS.md",
3478
+ readyDetail: "managed block missing"
3479
+ };
3480
+ }
3481
+ if (await isPlatformMinimal(rootDir, "zcode")) {
3482
+ return {
3483
+ present: true,
3484
+ ready: true,
3485
+ target: "AGENTS.md",
3486
+ readyDetail: "managed block present"
3487
+ };
3488
+ }
3489
+ const skillsDir = path14.join(rootDir, ".zcode", "skills");
3490
+ const hasSkills = await allManagedSkills(
3491
+ MODE_NAMES.map((mode) => path14.join(skillsDir, mode, "SKILL.md")),
3492
+ ZCODE_SKILL_MANAGED_MARKER
3493
+ );
3494
+ return {
3495
+ present: true,
3496
+ ready: hasSkills,
3497
+ target: "AGENTS.md + .zcode/skills/",
3498
+ readyDetail: hasSkills ? "managed block and skills present" : "mode skills are missing, incomplete, or user-authored"
3499
+ };
3500
+ }
3501
+ const hasBlock = await fileHasManagedBlock(
3502
+ path14.join(rootDir, ".github", "copilot-instructions.md")
3503
+ );
3504
+ if (!hasBlock) {
3505
+ return {
3506
+ present: false,
3507
+ ready: false,
3508
+ target: ".github/copilot-instructions.md",
3509
+ readyDetail: "managed block missing"
3510
+ };
3511
+ }
3512
+ const promptsDir = path14.join(rootDir, ".github", "prompts");
3513
+ if (await pathExists5(promptsDir)) {
3514
+ const hasPrompt = await pathExists5(path14.join(promptsDir, "man8.prompt.md"));
3515
+ return {
3516
+ present: hasPrompt,
3517
+ ready: hasPrompt,
3518
+ target: ".github/copilot-instructions.md + .github/prompts/",
3519
+ readyDetail: hasPrompt ? "managed block and prompts present" : "prompts directory exists but man8 prompt missing"
3071
3520
  };
3072
3521
  }
3073
3522
  return {
3074
- present: await fileHasManagedBlock(
3075
- path12.join(rootDir, ".github", "copilot-instructions.md")
3076
- ),
3523
+ present: true,
3524
+ ready: true,
3077
3525
  target: ".github/copilot-instructions.md",
3078
3526
  readyDetail: "managed block present"
3079
3527
  };
@@ -3089,10 +3537,38 @@ async function allPathsExist(paths) {
3089
3537
  const results = await Promise.all(paths.map(pathExists5));
3090
3538
  return results.every(Boolean);
3091
3539
  }
3092
- async function fileHasManagedBlock(filePath) {
3540
+ async function allManagedSkills(paths, marker) {
3541
+ const results = await Promise.all(
3542
+ paths.map((filePath) => fileHasText(filePath, marker))
3543
+ );
3544
+ return results.every(Boolean);
3545
+ }
3546
+ async function fileHasText(filePath, needle) {
3093
3547
  try {
3094
3548
  const content = await fs3.readFile(filePath, "utf-8");
3095
- return content.includes(DEFAULT_MANCODE_START_MARKER) && content.includes(DEFAULT_MANCODE_END_MARKER);
3549
+ return content.includes(needle);
3550
+ } catch {
3551
+ return false;
3552
+ }
3553
+ }
3554
+ async function isPlatformMinimal(rootDir, platform) {
3555
+ try {
3556
+ const raw = await fs3.readFile(
3557
+ path14.join(rootDir, ".mancode", "config.json"),
3558
+ "utf-8"
3559
+ );
3560
+ const config = JSON.parse(raw);
3561
+ if (!isRecord2(config.platformOptions)) return false;
3562
+ const options = config.platformOptions[platform];
3563
+ return isRecord2(options) && options.minimal === true;
3564
+ } catch {
3565
+ return false;
3566
+ }
3567
+ }
3568
+ async function fileHasManagedBlock(filePath, startMarker = DEFAULT_MANCODE_START_MARKER, endMarker = DEFAULT_MANCODE_END_MARKER) {
3569
+ try {
3570
+ const content = await fs3.readFile(filePath, "utf-8");
3571
+ return hasManagedBlock(content, startMarker, endMarker);
3096
3572
  } catch {
3097
3573
  return false;
3098
3574
  }
@@ -3100,7 +3576,7 @@ async function fileHasManagedBlock(filePath) {
3100
3576
  async function claudeHooksRegistered(rootDir) {
3101
3577
  try {
3102
3578
  const raw = await fs3.readFile(
3103
- path12.join(rootDir, ".claude", "settings.json"),
3579
+ path14.join(rootDir, ".claude", "settings.json"),
3104
3580
  "utf-8"
3105
3581
  );
3106
3582
  const settings = JSON.parse(raw);
@@ -3146,7 +3622,7 @@ var EXIT_NOT_INITIALIZED = 1;
3146
3622
  var EXIT_UNSUPPORTED_PLATFORM = 2;
3147
3623
  var EXIT_INSTALL_FAILED = 3;
3148
3624
  async function install(rootDir = process4.cwd(), platform = "claude-code", options = {}) {
3149
- const stateFile = path13.join(rootDir, ".mancode", "state.json");
3625
+ const stateFile = path15.join(rootDir, ".mancode", "state.json");
3150
3626
  if (!await pathExists6(stateFile)) {
3151
3627
  console.error("\u2717 mancode not initialized.");
3152
3628
  console.error(" Run `mancode init` first.");
@@ -3177,15 +3653,22 @@ async function install(rootDir = process4.cwd(), platform = "claude-code", optio
3177
3653
  if (alreadyInstalled && !options.force) {
3178
3654
  const status2 = await checkPlatformStatus(rootDir, platform, true);
3179
3655
  if (status2.ready) {
3656
+ if (!options.minimal) {
3657
+ console.log(
3658
+ `\u2139\uFE0F ${formatPlatformName(platform)} adapter already installed.`
3659
+ );
3660
+ console.log(" Run with --force to reinstall.");
3661
+ return EXIT_OK2;
3662
+ }
3663
+ console.log(
3664
+ `\u2139\uFE0F ${formatPlatformName(platform)} adapter already installed; switching to minimal install.`
3665
+ );
3666
+ console.log(" Run with --force to fully reinstall.");
3667
+ } else {
3180
3668
  console.log(
3181
- `\u2139\uFE0F ${formatPlatformName(platform)} adapter already installed.`
3669
+ `\u2139\uFE0F ${formatPlatformName(platform)} is recorded in config but not ready; repairing generated files.`
3182
3670
  );
3183
- console.log(" Run with --force to reinstall.");
3184
- return EXIT_OK2;
3185
3671
  }
3186
- console.log(
3187
- `\u2139\uFE0F ${formatPlatformName(platform)} is recorded in config but not ready; repairing generated files.`
3188
- );
3189
3672
  }
3190
3673
  console.log(`\u2713 Installing ${formatPlatformName(platform)} adapter...`);
3191
3674
  const project = await detectProjectType(rootDir);
@@ -3193,7 +3676,8 @@ async function install(rootDir = process4.cwd(), platform = "claude-code", optio
3193
3676
  await installer.install(rootDir, {
3194
3677
  techStack: project.techStack,
3195
3678
  uiLibrary: project.uiLibrary,
3196
- minimal: options.minimal
3679
+ minimal: options.minimal,
3680
+ force: options.force
3197
3681
  });
3198
3682
  } catch (err) {
3199
3683
  const msg = err instanceof Error ? err.message : String(err);
@@ -3206,12 +3690,20 @@ async function install(rootDir = process4.cwd(), platform = "claude-code", optio
3206
3690
  if (!platforms.includes(platform)) {
3207
3691
  platforms.push(platform);
3208
3692
  }
3209
- await updateConfig(rootDir, { ...config, platforms });
3693
+ await updateConfig(rootDir, {
3694
+ ...config,
3695
+ platforms,
3696
+ platformOptions: updatePlatformOptions(
3697
+ config.platformOptions,
3698
+ platform,
3699
+ options
3700
+ )
3701
+ });
3210
3702
  console.log(`\u2713 ${formatPlatformName(platform)} adapter installed.`);
3211
3703
  return EXIT_OK2;
3212
3704
  }
3213
3705
  async function readConfig(rootDir) {
3214
- const configPath = path13.join(rootDir, ".mancode", "config.json");
3706
+ const configPath = path15.join(rootDir, ".mancode", "config.json");
3215
3707
  try {
3216
3708
  const raw = await fs4.readFile(configPath, "utf-8");
3217
3709
  return { config: JSON.parse(raw), valid: true };
@@ -3226,7 +3718,7 @@ function isNodeError3(err) {
3226
3718
  return err instanceof Error && "code" in err;
3227
3719
  }
3228
3720
  async function updateConfig(rootDir, config) {
3229
- const configPath = path13.join(rootDir, ".mancode", "config.json");
3721
+ const configPath = path15.join(rootDir, ".mancode", "config.json");
3230
3722
  const content = `${JSON.stringify(config, null, 2)}
3231
3723
  `;
3232
3724
  await fs4.writeFile(configPath, content, "utf-8");
@@ -3245,10 +3737,19 @@ function withDefaultConfig(config, fallbackPlatform) {
3245
3737
  logging: isRecord3(config.logging) ? config.logging : DEFAULT_CONFIG.logging
3246
3738
  };
3247
3739
  }
3740
+ function updatePlatformOptions(value, platform, options) {
3741
+ const platformOptions = isRecord3(value) ? { ...value } : {};
3742
+ const existing = platformOptions[platform];
3743
+ platformOptions[platform] = {
3744
+ ...isRecord3(existing) ? existing : {},
3745
+ minimal: options.minimal === true
3746
+ };
3747
+ return platformOptions;
3748
+ }
3248
3749
  async function readStatePlatform(rootDir) {
3249
3750
  try {
3250
3751
  const raw = await fs4.readFile(
3251
- path13.join(rootDir, ".mancode", "state.json"),
3752
+ path15.join(rootDir, ".mancode", "state.json"),
3252
3753
  "utf-8"
3253
3754
  );
3254
3755
  const state = JSON.parse(raw);
@@ -3271,7 +3772,7 @@ function isRecord3(value) {
3271
3772
 
3272
3773
  // src/commands/list-platforms.ts
3273
3774
  import { promises as fs5 } from "fs";
3274
- import path14 from "path";
3775
+ import path16 from "path";
3275
3776
  import process5 from "process";
3276
3777
  var EXIT_OK3 = 0;
3277
3778
  async function listPlatforms(rootDir = process5.cwd()) {
@@ -3288,7 +3789,7 @@ async function listPlatforms(rootDir = process5.cwd()) {
3288
3789
  async function readInstalledPlatforms(rootDir) {
3289
3790
  try {
3290
3791
  const raw = await fs5.readFile(
3291
- path14.join(rootDir, ".mancode", "config.json"),
3792
+ path16.join(rootDir, ".mancode", "config.json"),
3292
3793
  "utf-8"
3293
3794
  );
3294
3795
  const config = JSON.parse(raw);
@@ -3312,22 +3813,24 @@ function describePlatform(platform) {
3312
3813
  }
3313
3814
 
3314
3815
  // src/commands/manps.ts
3315
- import { access as access2 } from "fs/promises";
3316
- import path16 from "path";
3816
+ import { access as access4 } from "fs/promises";
3817
+ import path18 from "path";
3317
3818
  import { createInterface } from "readline/promises";
3318
3819
 
3319
3820
  // src/system/preseason.ts
3821
+ import { execFile } from "child_process";
3320
3822
  import { createHash } from "crypto";
3321
3823
  import { existsSync, readFileSync } from "fs";
3322
3824
  import {
3825
+ access as access3,
3323
3826
  lstat,
3324
- mkdir as mkdir6,
3827
+ mkdir as mkdir7,
3325
3828
  readFile as readFile6,
3326
- readdir,
3829
+ readdir as readdir2,
3327
3830
  rename,
3328
- writeFile as writeFile7
3831
+ writeFile as writeFile9
3329
3832
  } from "fs/promises";
3330
- import path15 from "path";
3833
+ import path17 from "path";
3331
3834
  var PRESEASON_AREAS = [
3332
3835
  "all",
3333
3836
  "deps",
@@ -3356,16 +3859,60 @@ var SOURCE_EXTENSIONS = /* @__PURE__ */ new Set([
3356
3859
  ]);
3357
3860
  var MAX_SCAN_DEPTH = 20;
3358
3861
  var MAX_PROJECT_FILES = 5e3;
3862
+ var DEPCRUISE_TIMEOUT_MS = 15e3;
3863
+ var DEPCRUISE_MAX_BUFFER = 4 * 1024 * 1024;
3864
+ var SCANNERS = [
3865
+ {
3866
+ name: "scripts",
3867
+ areas: ["all", "config"],
3868
+ scan: ({ pkg }) => scanScripts(pkg)
3869
+ },
3870
+ {
3871
+ name: "dependency-overlap",
3872
+ areas: ["all", "deps"],
3873
+ scan: ({ pkg }) => scanDependencyOverlap(pkg)
3874
+ },
3875
+ {
3876
+ name: "security",
3877
+ areas: ["all", "security"],
3878
+ scan: ({ pkg }) => scanSecurity(pkg)
3879
+ },
3880
+ {
3881
+ name: "todos",
3882
+ areas: ["all", "dead-code"],
3883
+ scan: ({ projectRoot, files }) => scanTodos(projectRoot, files)
3884
+ },
3885
+ {
3886
+ name: "test-gaps",
3887
+ areas: ["all", "dead-code"],
3888
+ scan: ({ files }) => scanTestGaps(files)
3889
+ },
3890
+ {
3891
+ name: "config",
3892
+ areas: ["all", "config"],
3893
+ scan: ({ projectRoot, files }) => scanConfig(projectRoot, files)
3894
+ },
3895
+ {
3896
+ name: "aesthetic-drift",
3897
+ areas: ["all"],
3898
+ scan: ({ projectRoot, files }) => scanAestheticDrift(projectRoot, files)
3899
+ },
3900
+ {
3901
+ name: "architecture",
3902
+ areas: ["all"],
3903
+ scan: ({ projectRoot }) => scanArchitecture(projectRoot)
3904
+ }
3905
+ ];
3359
3906
  async function runPreseasonScan(projectRoot, area = "all") {
3360
3907
  const generatedAt = (/* @__PURE__ */ new Date()).toISOString();
3361
3908
  const normalizedArea = normalizeArea(area);
3362
3909
  const pkg = await readPackageJson(projectRoot);
3363
3910
  const needsFiles = normalizedArea === "all" || normalizedArea === "dead-code" || normalizedArea === "config";
3364
3911
  const files = needsFiles ? await listProjectFiles(projectRoot) : [];
3365
- const issues = scanArea(projectRoot, normalizedArea, pkg, files).slice(0, 20);
3366
- const reportDir = path15.join(projectRoot, ".mancode", "preseason-reports");
3367
- await mkdir6(reportDir, { recursive: true });
3368
- const issueDbPath = path15.join(
3912
+ const issues = (await scanArea(projectRoot, normalizedArea, pkg, files)).slice(0, 20);
3913
+ const reportDir = path17.join(projectRoot, ".mancode", "preseason-reports");
3914
+ await mkdir7(reportDir, { recursive: true });
3915
+ const issueDbPath = path17.join(
3369
3916
  projectRoot,
3370
3917
  ".mancode",
3371
3918
  "preseason-issues.json"
@@ -3383,9 +3930,9 @@ async function runPreseasonScan(projectRoot, area = "all") {
3383
3930
  issueDbPath
3384
3931
  };
3385
3932
  const database = await buildIssueDatabase(projectRoot, report);
3386
- await writeFile7(reportPath, renderPreseasonReport(report), "utf-8");
3387
- await writeFile7(
3388
- path15.join(projectRoot, ".mancode", "preseason-report.md"),
3933
+ await writeFile9(reportPath, renderPreseasonReport(report), "utf-8");
3934
+ await writeFile9(
3935
+ path17.join(projectRoot, ".mancode", "preseason-report.md"),
3389
3936
  renderPreseasonReport(report),
3390
3937
  "utf-8"
3391
3938
  );
@@ -3393,7 +3940,7 @@ async function runPreseasonScan(projectRoot, area = "all") {
3393
3940
  return report;
3394
3941
  }
3395
3942
  async function runPreseasonRemediation(projectRoot, issues, options = {}) {
3396
- const issueDbPath = path15.join(
3943
+ const issueDbPath = path17.join(
3397
3944
  projectRoot,
3398
3945
  ".mancode",
3399
3946
  "preseason-issues.json"
@@ -3475,7 +4022,7 @@ async function runPreseasonRemediation(projectRoot, issues, options = {}) {
3475
4022
  write(" Decision: skipped.");
3476
4023
  }
3477
4024
  database.updatedAt = now;
3478
- await writeFile7(
4025
+ await writeFile9(
3479
4026
  issueDbPath,
3480
4027
  `${JSON.stringify(database, null, 2)}
3481
4028
  `,
@@ -3498,32 +4045,16 @@ function normalizeArea(area) {
3498
4045
  `invalid area: ${area}. Supported areas: ${PRESEASON_AREAS.join(", ")}`
3499
4046
  );
3500
4047
  }
3501
- function scanArea(projectRoot, area, pkg, files) {
3502
- switch (area) {
3503
- case "deps":
3504
- return scanDependencyOverlap(pkg);
3505
- case "security":
3506
- return scanSecurity(pkg);
3507
- case "dead-code":
3508
- return [...scanTodos(projectRoot, files), ...scanTestGaps(files)];
3509
- case "config":
3510
- return [...scanScripts(pkg), ...scanConfig(projectRoot, files)];
3511
- case "all":
3512
- return [
3513
- ...scanScripts(pkg),
3514
- ...scanDependencyOverlap(pkg),
3515
- ...scanSecurity(pkg),
3516
- ...scanTodos(projectRoot, files),
3517
- ...scanTestGaps(files),
3518
- ...scanConfig(projectRoot, files),
3519
- ...scanAestheticDrift(projectRoot, files)
3520
- ];
3521
- }
4048
+ async function scanArea(projectRoot, area, pkg, files) {
4049
+ const ctx = { projectRoot, area, pkg, files };
4050
+ const active = SCANNERS.filter((scanner) => scanner.areas.includes(area));
4051
+ const chunks = await Promise.all(active.map((scanner) => scanner.scan(ctx)));
4052
+ return chunks.flat();
3522
4053
  }
3523
4054
  async function allocateReportPath(reportDir, baseName) {
3524
4055
  for (let attempt = 0; attempt < 1e3; attempt++) {
3525
4056
  const suffix = attempt === 0 ? "" : `-${attempt + 1}`;
3526
- const candidate = path15.join(reportDir, `${baseName}${suffix}.md`);
4057
+ const candidate = path17.join(reportDir, `${baseName}${suffix}.md`);
3527
4058
  if (!existsSync(candidate)) return candidate;
3528
4059
  }
3529
4060
  throw new Error(`unable to allocate preseason report path: ${baseName}`);
@@ -3566,14 +4097,14 @@ async function walk(root, current, results, depth) {
3566
4097
  if (depth > MAX_SCAN_DEPTH || results.length >= MAX_PROJECT_FILES) return;
3567
4098
  let entries;
3568
4099
  try {
3569
- entries = await readdir(current);
4100
+ entries = await readdir2(current);
3570
4101
  } catch {
3571
4102
  return;
3572
4103
  }
3573
4104
  for (const entry of entries) {
3574
4105
  if (IGNORE_DIRS.has(entry)) continue;
3575
- const abs = path15.join(current, entry);
3576
- const rel = path15.relative(root, abs);
4106
+ const abs = path17.join(current, entry);
4107
+ const rel = path17.relative(root, abs);
3577
4108
  let info;
3578
4109
  try {
3579
4110
  info = await lstat(abs);
@@ -3583,7 +4114,7 @@ async function walk(root, current, results, depth) {
3583
4114
  if (info.isSymbolicLink()) continue;
3584
4115
  if (info.isDirectory()) {
3585
4116
  await walk(root, abs, results, depth + 1);
3586
- } else if (SOURCE_EXTENSIONS.has(path15.extname(entry)) || entry === "package.json") {
4117
+ } else if (SOURCE_EXTENSIONS.has(path17.extname(entry)) || entry === "package.json") {
3587
4118
  results.push(rel);
3588
4119
  if (results.length >= MAX_PROJECT_FILES) return;
3589
4120
  }
@@ -3591,7 +4122,7 @@ async function walk(root, current, results, depth) {
3591
4122
  }
3592
4123
  async function readPackageJson(projectRoot) {
3593
4124
  try {
3594
- const raw = await readFile6(path15.join(projectRoot, "package.json"), "utf-8");
4125
+ const raw = await readFile6(path17.join(projectRoot, "package.json"), "utf-8");
3595
4126
  return JSON.parse(raw);
3596
4127
  } catch {
3597
4128
  return null;
@@ -3684,7 +4215,7 @@ function scanTodos(projectRoot, files) {
3684
4215
  const matches = [];
3685
4216
  for (const file of files) {
3686
4217
  if (matches.length >= 7) break;
3687
- const abs = path15.join(projectRoot, file);
4218
+ const abs = path17.join(projectRoot, file);
3688
4219
  matches.push(...readTodoIssues(abs, file, matches.length));
3689
4220
  }
3690
4221
  return matches.slice(0, 7);
@@ -3721,7 +4252,7 @@ function scanTestGaps(files) {
3721
4252
  if (sourceFiles.length === 0) return [];
3722
4253
  const tests = new Set(files.filter((file) => file.startsWith("tests/")));
3723
4254
  const missing = sourceFiles.filter((file) => {
3724
- const base = path15.basename(file).replace(/\.(ts|tsx|js|jsx)$/, "");
4255
+ const base = path17.basename(file).replace(/\.(ts|tsx|js|jsx)$/, "");
3725
4256
  return !Array.from(tests).some((test) => test.includes(base));
3726
4257
  }).slice(0, 4);
3727
4258
  return missing.map((file, index) => ({
@@ -3730,13 +4261,13 @@ function scanTestGaps(files) {
3730
4261
  type: "tests",
3731
4262
  title: "Core source file has no obvious test",
3732
4263
  file,
3733
- detail: `No matching test file was found for ${path15.basename(file)}.`,
4264
+ detail: `No matching test file was found for ${path17.basename(file)}.`,
3734
4265
  recommendation: "Add focused coverage for the public behavior or document why this module is exercised indirectly."
3735
4266
  }));
3736
4267
  }
3737
4268
  function scanConfig(projectRoot, files) {
3738
4269
  const issues = [];
3739
- if (!files.includes(".gitignore") && !pathExistsSync(path15.join(projectRoot, ".gitignore"))) {
4270
+ if (!files.includes(".gitignore") && !pathExistsSync(path17.join(projectRoot, ".gitignore"))) {
3740
4271
  issues.push({
3741
4272
  id: "config-gitignore",
3742
4273
  severity: "P1",
@@ -3747,7 +4278,7 @@ function scanConfig(projectRoot, files) {
3747
4278
  recommendation: "Add a .gitignore that excludes dependencies, build output, coverage, and local env files."
3748
4279
  });
3749
4280
  }
3750
- if (!files.includes(".editorconfig") && !pathExistsSync(path15.join(projectRoot, ".editorconfig"))) {
4281
+ if (!files.includes(".editorconfig") && !pathExistsSync(path17.join(projectRoot, ".editorconfig"))) {
3751
4282
  issues.push({
3752
4283
  id: "config-editorconfig",
3753
4284
  severity: "P2",
@@ -3766,7 +4297,7 @@ function scanAestheticDrift(projectRoot, files) {
3766
4297
  for (const file of frontendFiles) {
3767
4298
  let content;
3768
4299
  try {
3769
- content = readFileSyncSafe(path15.join(projectRoot, file));
4300
+ content = readFileSyncSafe(path17.join(projectRoot, file));
3770
4301
  } catch {
3771
4302
  continue;
3772
4303
  }
@@ -3785,6 +4316,151 @@ function scanAestheticDrift(projectRoot, files) {
3785
4316
  }
3786
4317
  return issues;
3787
4318
  }
4319
+ async function scanArchitecture(projectRoot) {
4320
+ const localBinary = path17.join(
4321
+ projectRoot,
4322
+ "node_modules",
4323
+ ".bin",
4324
+ process.platform === "win32" ? "depcruise.cmd" : "depcruise"
4325
+ );
4326
+ const binary = pathExistsSync(localBinary) ? localBinary : "depcruise";
4327
+ const result = await runDepcruise(binary, projectRoot);
4328
+ if (result.status === "not-found") {
4329
+ if (!await hasDependencyCruiserConfig(projectRoot)) return [];
4330
+ return [architectureScannerUnavailableIssue()];
4331
+ }
4332
+ const parsed = parseDepcruiseJson(result.stdout);
4333
+ if (!parsed) {
4334
+ return [
4335
+ {
4336
+ id: "architecture-scan-skipped",
4337
+ severity: "P2",
4338
+ type: "architecture",
4339
+ title: "Architecture scan skipped",
4340
+ detail: summarizeProcessFailure(result),
4341
+ recommendation: "Check the dependency-cruiser configuration or run depcruise manually to inspect the failure."
4342
+ }
4343
+ ];
4344
+ }
4345
+ const violations = extractDepcruiseViolations(parsed);
4346
+ if (violations.length === 0) return [];
4347
+ return violations.slice(0, 5).map((violation, index) => ({
4348
+ id: `architecture-${index + 1}`,
4349
+ severity: depcruiseSeverityToPreseason(violation.severity),
4350
+ type: "architecture",
4351
+ title: `Architecture rule violation: ${violation.ruleName}`,
4352
+ file: violation.from,
4353
+ detail: `${violation.from}${violation.to ? ` -> ${violation.to}` : ""}${violation.comment ? `: ${violation.comment}` : ""}`,
4354
+ recommendation: "Respect the configured dependency boundary or update the dependency-cruiser rule if the architecture decision changed."
4355
+ }));
4356
+ }
4357
+ async function hasDependencyCruiserConfig(projectRoot) {
4358
+ const files = [
4359
+ ".dependency-cruiser.js",
4360
+ ".dependency-cruiser.cjs",
4361
+ ".dependency-cruiser.mjs",
4362
+ ".dependency-cruiser.json",
4363
+ "dependency-cruiser.config.js",
4364
+ "dependency-cruiser.config.cjs",
4365
+ "dependency-cruiser.config.mjs"
4366
+ ];
4367
+ const results = await Promise.all(
4368
+ files.map((file) => pathExists7(path17.join(projectRoot, file)))
4369
+ );
4370
+ return results.some(Boolean);
4371
+ }
4372
+ function architectureScannerUnavailableIssue() {
4373
+ return {
4374
+ id: "architecture-scanner-unavailable",
4375
+ severity: "P2",
4376
+ type: "architecture",
4377
+ title: "Architecture scanner not installed",
4378
+ detail: "dependency-cruiser config was found, but dependency-cruiser was not found in node_modules/.bin or on PATH. Architecture dependency rules cannot be checked.",
4379
+ recommendation: "Install dependency-cruiser (npm i -D dependency-cruiser) to enable architecture scanning."
4380
+ };
4381
+ }
4382
+ function runDepcruise(binary, projectRoot) {
4383
+ return new Promise((resolve) => {
4384
+ const needsShell = process.platform === "win32";
4385
+ execFile(
4386
+ binary,
4387
+ ["--output-type", "json", "."],
4388
+ {
4389
+ cwd: projectRoot,
4390
+ timeout: DEPCRUISE_TIMEOUT_MS,
4391
+ maxBuffer: DEPCRUISE_MAX_BUFFER,
4392
+ shell: needsShell
4393
+ },
4394
+ (error, stdout, stderr) => {
4395
+ if (!error) {
4396
+ resolve({
4397
+ status: "ok",
4398
+ stdout,
4399
+ stderr,
4400
+ exitCode: 0,
4401
+ timedOut: false
4402
+ });
4403
+ return;
4404
+ }
4405
+ const nodeError = error;
4406
+ if (nodeError.code === "ENOENT") {
4407
+ resolve({
4408
+ status: "not-found",
4409
+ stdout: "",
4410
+ stderr: "",
4411
+ exitCode: null,
4412
+ timedOut: false
4413
+ });
4414
+ return;
4415
+ }
4416
+ resolve({
4417
+ status: "failed",
4418
+ stdout: nodeError.stdout ?? stdout,
4419
+ stderr: nodeError.stderr ?? stderr,
4420
+ exitCode: typeof nodeError.code === "number" ? nodeError.code : null,
4421
+ timedOut: Boolean(nodeError.killed || nodeError.signal === "SIGTERM")
4422
+ });
4423
+ }
4424
+ );
4425
+ });
4426
+ }
4427
+ function parseDepcruiseJson(output) {
4428
+ if (!output.trim()) return null;
4429
+ try {
4430
+ return JSON.parse(output);
4431
+ } catch {
4432
+ return null;
4433
+ }
4434
+ }
4435
+ function extractDepcruiseViolations(value) {
4436
+ if (!isRecord4(value)) return [];
4437
+ const rawViolations = Array.isArray(value.violations) ? value.violations : isRecord4(value.summary) && Array.isArray(value.summary.violations) ? value.summary.violations : [];
4438
+ return rawViolations.map((violation) => normalizeDepcruiseViolation(violation)).filter(
4439
+ (violation) => violation !== null
4440
+ );
4441
+ }
4442
+ function normalizeDepcruiseViolation(value) {
4443
+ if (!isRecord4(value)) return null;
4444
+ const rule = isRecord4(value.rule) ? value.rule : {};
4445
+ const from = typeof value.from === "string" ? value.from : "";
4446
+ const to = typeof value.to === "string" ? value.to : void 0;
4447
+ const ruleName = typeof rule.name === "string" ? rule.name : typeof value.name === "string" ? value.name : "unnamed-rule";
4448
+ const severity = typeof rule.severity === "string" ? rule.severity : typeof value.severity === "string" ? value.severity : "warn";
4449
+ const comment = typeof value.comment === "string" ? value.comment : void 0;
4450
+ if (!from) return null;
4451
+ return { ruleName, severity, from, to, comment };
4452
+ }
4453
+ function depcruiseSeverityToPreseason(severity) {
4454
+ return severity.toLowerCase() === "error" ? "P1" : "P2";
4455
+ }
4456
+ function summarizeProcessFailure(result) {
4457
+ if (result.timedOut) {
4458
+ return `dependency-cruiser timed out after ${DEPCRUISE_TIMEOUT_MS}ms.`;
4459
+ }
4460
+ const output = `${result.stderr || result.stdout}`.trim();
4461
+ const summary = output.length > 0 ? output.slice(0, 240) : "No output.";
4462
+ return `dependency-cruiser exited with ${result.exitCode ?? "unknown"}: ${summary}`;
4463
+ }
3788
4464
  function containsHardcodedColor(content) {
3789
4465
  return content.split("\n").some(
3790
4466
  (line) => /#[0-9a-fA-F]{3,8}\b/.test(line) && !/^\s*--[a-zA-Z0-9-_]+\s*:/.test(line)
@@ -3795,9 +4471,9 @@ function inferCommands(pkg) {
3795
4471
  return ["lint", "test", "build"].filter((name) => scripts[name]).map((name) => `npm run ${name}`);
3796
4472
  }
3797
4473
  async function buildIssueDatabase(projectRoot, report) {
3798
- const reportRef = path15.relative(projectRoot, report.reportPath);
4474
+ const reportRef = path17.relative(projectRoot, report.reportPath);
3799
4475
  const run = {
3800
- id: path15.basename(report.reportPath, ".md"),
4476
+ id: path17.basename(report.reportPath, ".md"),
3801
4477
  generatedAt: report.generatedAt,
3802
4478
  area: report.area,
3803
4479
  reportPath: reportRef,
@@ -3860,7 +4536,7 @@ async function readIssueDatabase(issueDbPath) {
3860
4536
  }
3861
4537
  async function writeJsonAtomic(file, value) {
3862
4538
  const tmp = `${file}.tmp-${Date.now()}-${Math.random().toString(36).slice(2)}`;
3863
- await writeFile7(tmp, `${JSON.stringify(value, null, 2)}
4539
+ await writeFile9(tmp, `${JSON.stringify(value, null, 2)}
3864
4540
  `, "utf-8");
3865
4541
  await rename(tmp, file);
3866
4542
  }
@@ -3903,20 +4579,20 @@ function compareIssueRecords(a, b) {
3903
4579
  }
3904
4580
  async function applySafeRemediation(projectRoot, issue) {
3905
4581
  if (issue.id === "config-gitignore" && issue.file === ".gitignore") {
3906
- const gitignorePath = path15.join(projectRoot, ".gitignore");
4582
+ const gitignorePath = path17.join(projectRoot, ".gitignore");
3907
4583
  if (pathExistsSync(gitignorePath)) {
3908
4584
  return { applied: false };
3909
4585
  }
3910
- await writeFile7(gitignorePath, `${DEFAULT_GITIGNORE}
4586
+ await writeFile9(gitignorePath, `${DEFAULT_GITIGNORE}
3911
4587
  `, "utf-8");
3912
4588
  return { applied: true, action: "created .gitignore" };
3913
4589
  }
3914
4590
  if (issue.id === "config-editorconfig" && issue.file === ".editorconfig") {
3915
- const editorconfigPath = path15.join(projectRoot, ".editorconfig");
4591
+ const editorconfigPath = path17.join(projectRoot, ".editorconfig");
3916
4592
  if (pathExistsSync(editorconfigPath)) {
3917
4593
  return { applied: false };
3918
4594
  }
3919
- await writeFile7(editorconfigPath, `${DEFAULT_EDITORCONFIG}
4595
+ await writeFile9(editorconfigPath, `${DEFAULT_EDITORCONFIG}
3920
4596
  `, "utf-8");
3921
4597
  return { applied: true, action: "created .editorconfig" };
3922
4598
  }
@@ -3952,7 +4628,7 @@ async function inferSafePackageScript(projectRoot, scriptName) {
3952
4628
  }
3953
4629
  }
3954
4630
  async function addPackageScript(projectRoot, scriptName, script) {
3955
- const packagePath = path15.join(projectRoot, "package.json");
4631
+ const packagePath = path17.join(projectRoot, "package.json");
3956
4632
  let pkg;
3957
4633
  try {
3958
4634
  pkg = JSON.parse(await readFile6(packagePath, "utf-8"));
@@ -3965,7 +4641,7 @@ async function addPackageScript(projectRoot, scriptName, script) {
3965
4641
  ...scripts,
3966
4642
  [scriptName]: script
3967
4643
  };
3968
- await writeFile7(packagePath, `${JSON.stringify(pkg, null, 2)}
4644
+ await writeFile9(packagePath, `${JSON.stringify(pkg, null, 2)}
3969
4645
  `, "utf-8");
3970
4646
  return true;
3971
4647
  }
@@ -4044,9 +4720,20 @@ function readFileSyncSafe(file) {
4044
4720
  function pathExistsSync(file) {
4045
4721
  return existsSync(file);
4046
4722
  }
4723
+ async function pathExists7(file) {
4724
+ try {
4725
+ await access3(file);
4726
+ return true;
4727
+ } catch {
4728
+ return false;
4729
+ }
4730
+ }
4047
4731
  function isNodeError4(err) {
4048
4732
  return err instanceof Error && "code" in err;
4049
4733
  }
4734
+ function isRecord4(value) {
4735
+ return typeof value === "object" && value !== null;
4736
+ }
4050
4737
 
4051
4738
  // src/commands/manps.ts
4052
4739
  var EXIT_OK4 = 0;
@@ -4054,7 +4741,7 @@ var EXIT_NOT_INITIALIZED2 = 1;
4054
4741
  var EXIT_SCAN_FAILED = 2;
4055
4742
  var EXIT_INVALID_ARG = 3;
4056
4743
  async function manps(rootDir, area = "all", options = {}) {
4057
- if (!await pathExists7(path16.join(rootDir, ".mancode", "state.json"))) {
4744
+ if (!await pathExists8(path18.join(rootDir, ".mancode", "state.json"))) {
4058
4745
  if (options.json) {
4059
4746
  console.log(JSON.stringify({ error: "not initialized" }, null, 2));
4060
4747
  } else {
@@ -4133,8 +4820,8 @@ async function manps(rootDir, area = "all", options = {}) {
4133
4820
  console.log(
4134
4821
  `Issues: ${report.issues.length} total (P0 ${p0}, P1 ${p1}, P2 ${p2})`
4135
4822
  );
4136
- console.log(`Report: ${path16.relative(rootDir, report.reportPath)}`);
4137
- console.log(`Issue DB: ${path16.relative(rootDir, report.issueDbPath)}`);
4823
+ console.log(`Report: ${path18.relative(rootDir, report.reportPath)}`);
4824
+ console.log(`Issue DB: ${path18.relative(rootDir, report.issueDbPath)}`);
4138
4825
  if (report.issues.length > 0) {
4139
4826
  console.log("");
4140
4827
  for (const issue of report.issues.slice(0, 7)) {
@@ -4150,7 +4837,7 @@ async function manps(rootDir, area = "all", options = {}) {
4150
4837
  console.log(` Skipped: ${remediation.skipped}`);
4151
4838
  console.log(` Fixed: ${remediation.fixed}`);
4152
4839
  console.log(
4153
- ` Issue DB: ${path16.relative(rootDir, remediation.issueDbPath)}`
4840
+ ` Issue DB: ${path18.relative(rootDir, remediation.issueDbPath)}`
4154
4841
  );
4155
4842
  }
4156
4843
  return EXIT_OK4;
@@ -4196,9 +4883,9 @@ async function readStdinLines() {
4196
4883
  }
4197
4884
  return Buffer.concat(chunks).toString("utf-8").split(/\r?\n/).filter((line) => line.length > 0);
4198
4885
  }
4199
- async function pathExists7(p) {
4886
+ async function pathExists8(p) {
4200
4887
  try {
4201
- await access2(p);
4888
+ await access4(p);
4202
4889
  return true;
4203
4890
  } catch {
4204
4891
  return false;
@@ -4207,13 +4894,13 @@ async function pathExists7(p) {
4207
4894
 
4208
4895
  // src/commands/refresh-style.ts
4209
4896
  import { promises as fs6 } from "fs";
4210
- import path17 from "path";
4897
+ import path19 from "path";
4211
4898
  import process6 from "process";
4212
4899
  var EXIT_OK5 = 0;
4213
4900
  var EXIT_NOT_INITIALIZED3 = 1;
4214
4901
  async function refreshStyle(rootDir = process6.cwd()) {
4215
- const stateFile = path17.join(rootDir, ".mancode", "state.json");
4216
- if (!await pathExists8(stateFile)) {
4902
+ const stateFile = path19.join(rootDir, ".mancode", "state.json");
4903
+ if (!await pathExists9(stateFile)) {
4217
4904
  console.error("\u2717 mancode not initialized.");
4218
4905
  console.error(" Run `mancode init` first.");
4219
4906
  return EXIT_NOT_INITIALIZED3;
@@ -4225,13 +4912,13 @@ async function refreshStyle(rootDir = process6.cwd()) {
4225
4912
  }
4226
4913
  console.log("\u2713 \u626B\u63CF\u9879\u76EE\u8BBE\u8BA1 token...");
4227
4914
  const tokens = await scanAesthetics(rootDir, project.uiLibrary);
4228
- const tokensPath = path17.join(
4915
+ const tokensPath = path19.join(
4229
4916
  rootDir,
4230
4917
  ".mancode",
4231
4918
  "aesthetics",
4232
4919
  "style-tokens.json"
4233
4920
  );
4234
- await fs6.mkdir(path17.dirname(tokensPath), { recursive: true });
4921
+ await fs6.mkdir(path19.dirname(tokensPath), { recursive: true });
4235
4922
  await fs6.writeFile(
4236
4923
  tokensPath,
4237
4924
  `${JSON.stringify(tokens, null, 2)}
@@ -4295,7 +4982,7 @@ async function printStaticPlatformRefreshHint(rootDir) {
4295
4982
  async function readInstalledPlatforms2(rootDir) {
4296
4983
  try {
4297
4984
  const raw = await fs6.readFile(
4298
- path17.join(rootDir, ".mancode", "config.json"),
4985
+ path19.join(rootDir, ".mancode", "config.json"),
4299
4986
  "utf-8"
4300
4987
  );
4301
4988
  const config = JSON.parse(raw);
@@ -4306,7 +4993,7 @@ async function readInstalledPlatforms2(rootDir) {
4306
4993
  return [];
4307
4994
  }
4308
4995
  }
4309
- async function pathExists8(p) {
4996
+ async function pathExists9(p) {
4310
4997
  try {
4311
4998
  await fs6.access(p);
4312
4999
  return true;
@@ -4318,19 +5005,19 @@ async function pathExists8(p) {
4318
5005
  // src/commands/status.ts
4319
5006
  import { spawn } from "child_process";
4320
5007
  import { promises as fs7 } from "fs";
4321
- import path19 from "path";
5008
+ import path21 from "path";
4322
5009
  import process7 from "process";
4323
5010
 
4324
5011
  // src/system/workflow.ts
4325
5012
  import {
4326
- mkdir as mkdir7,
5013
+ mkdir as mkdir8,
4327
5014
  readFile as readFile7,
4328
- readdir as readdir2,
4329
- rm as rm3,
5015
+ readdir as readdir3,
5016
+ rm as rm4,
4330
5017
  stat as stat2,
4331
- writeFile as writeFile8
5018
+ writeFile as writeFile10
4332
5019
  } from "fs/promises";
4333
- import path18 from "path";
5020
+ import path20 from "path";
4334
5021
  var METADATA_FILE = "metadata.json";
4335
5022
  var SLUG_MAX = 30;
4336
5023
  var TASK_ID_PATTERN = /^[A-Za-z0-9][A-Za-z0-9-]*$/;
@@ -4347,7 +5034,7 @@ function slugify(text) {
4347
5034
  return text.toLowerCase().trim().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
4348
5035
  }
4349
5036
  async function createWorkflow(projectRoot, task, mode) {
4350
- await mkdir7(workflowsRoot(projectRoot), { recursive: true });
5037
+ await mkdir8(workflowsRoot(projectRoot), { recursive: true });
4351
5038
  const taskId = await allocateTaskId(projectRoot, generateTaskId(task));
4352
5039
  const dir = workflowDir(projectRoot, taskId);
4353
5040
  const now = (/* @__PURE__ */ new Date()).toISOString();
@@ -4368,7 +5055,7 @@ async function allocateTaskId(projectRoot, baseTaskId) {
4368
5055
  for (let attempt = 0; attempt < 1e3; attempt++) {
4369
5056
  const taskId = attempt === 0 ? baseTaskId : `${baseTaskId}-${attempt + 1}`;
4370
5057
  try {
4371
- await mkdir7(workflowDir(projectRoot, taskId));
5058
+ await mkdir8(workflowDir(projectRoot, taskId));
4372
5059
  return taskId;
4373
5060
  } catch (err) {
4374
5061
  if (isNodeError5(err) && err.code === "EEXIST") {
@@ -4408,7 +5095,7 @@ async function listWorkflows(projectRoot) {
4408
5095
  const root = workflowsRoot(projectRoot);
4409
5096
  let entries;
4410
5097
  try {
4411
- entries = await readdir2(root);
5098
+ entries = await readdir3(root);
4412
5099
  } catch {
4413
5100
  return [];
4414
5101
  }
@@ -4428,23 +5115,23 @@ async function deleteWorkflow(projectRoot, taskId) {
4428
5115
  } catch {
4429
5116
  return false;
4430
5117
  }
4431
- await rm3(dir, { recursive: true, force: true });
5118
+ await rm4(dir, { recursive: true, force: true });
4432
5119
  return true;
4433
5120
  }
4434
5121
  function workflowsRoot(projectRoot) {
4435
- return path18.join(projectRoot, ".mancode", "workflows");
5122
+ return path20.join(projectRoot, ".mancode", "workflows");
4436
5123
  }
4437
5124
  function workflowDir(projectRoot, taskId) {
4438
5125
  assertValidTaskId(taskId);
4439
- return path18.join(workflowsRoot(projectRoot), taskId);
5126
+ return path20.join(workflowsRoot(projectRoot), taskId);
4440
5127
  }
4441
5128
  function metadataPath(projectRoot, taskId) {
4442
- return path18.join(workflowDir(projectRoot, taskId), METADATA_FILE);
5129
+ return path20.join(workflowDir(projectRoot, taskId), METADATA_FILE);
4443
5130
  }
4444
5131
  async function writeMetadata(dir, meta) {
4445
5132
  const content = `${JSON.stringify(meta, null, 2)}
4446
5133
  `;
4447
- await writeFile8(path18.join(dir, METADATA_FILE), content, "utf-8");
5134
+ await writeFile10(path20.join(dir, METADATA_FILE), content, "utf-8");
4448
5135
  }
4449
5136
  function parseWorkflowMeta(raw, taskId) {
4450
5137
  try {
@@ -4467,12 +5154,13 @@ function assertValidTaskId(taskId) {
4467
5154
  }
4468
5155
 
4469
5156
  // src/commands/status.ts
5157
+ var HOOK_ESTIMATE_TIMEOUT_MS = 2e3;
4470
5158
  var EXIT_OK6 = 0;
4471
5159
  var EXIT_NOT_INITIALIZED4 = 1;
4472
5160
  var EXIT_CORRUPT_STATE = 2;
4473
5161
  async function status(rootDir = process7.cwd(), options = {}) {
4474
- const stateFile = path19.join(rootDir, ".mancode", "state.json");
4475
- if (!await pathExists9(stateFile)) {
5162
+ const stateFile = path21.join(rootDir, ".mancode", "state.json");
5163
+ if (!await pathExists10(stateFile)) {
4476
5164
  console.error("\u2717 mancode not initialized.");
4477
5165
  console.error(" Run `mancode init` to get started.");
4478
5166
  return EXIT_NOT_INITIALIZED4;
@@ -4558,19 +5246,19 @@ async function getCurrentWorkflow(rootDir, taskId) {
4558
5246
  }
4559
5247
  async function getProjectName(rootDir) {
4560
5248
  try {
4561
- const raw = await fs7.readFile(path19.join(rootDir, "package.json"), "utf-8");
5249
+ const raw = await fs7.readFile(path21.join(rootDir, "package.json"), "utf-8");
4562
5250
  const pkg = JSON.parse(raw);
4563
5251
  if (pkg.name && typeof pkg.name === "string") {
4564
5252
  return pkg.name;
4565
5253
  }
4566
5254
  } catch {
4567
5255
  }
4568
- return path19.basename(rootDir);
5256
+ return path21.basename(rootDir);
4569
5257
  }
4570
5258
  async function readConfig2(rootDir) {
4571
5259
  try {
4572
5260
  const raw = await fs7.readFile(
4573
- path19.join(rootDir, ".mancode", "config.json"),
5261
+ path21.join(rootDir, ".mancode", "config.json"),
4574
5262
  "utf-8"
4575
5263
  );
4576
5264
  return JSON.parse(raw);
@@ -4597,9 +5285,9 @@ function getEffectiveTeamStatus(state, config, detected) {
4597
5285
  }
4598
5286
  async function checkHooks(rootDir) {
4599
5287
  const [sessionStart, userPromptSubmit, registered] = await Promise.all([
4600
- pathExists9(path19.join(rootDir, ".mancode", "hooks", "session-start.sh")),
4601
- pathExists9(
4602
- path19.join(rootDir, ".mancode", "hooks", "user-prompt-submit.sh")
5288
+ pathExists10(path21.join(rootDir, ".mancode", "hooks", "session-start.sh")),
5289
+ pathExists10(
5290
+ path21.join(rootDir, ".mancode", "hooks", "user-prompt-submit.sh")
4603
5291
  ),
4604
5292
  isRegistered(rootDir)
4605
5293
  ]);
@@ -4608,7 +5296,7 @@ async function checkHooks(rootDir) {
4608
5296
  async function isRegistered(rootDir) {
4609
5297
  try {
4610
5298
  const raw = await fs7.readFile(
4611
- path19.join(rootDir, ".claude", "settings.json"),
5299
+ path21.join(rootDir, ".claude", "settings.json"),
4612
5300
  "utf-8"
4613
5301
  );
4614
5302
  const settings = JSON.parse(raw);
@@ -4618,9 +5306,9 @@ async function isRegistered(rootDir) {
4618
5306
  }
4619
5307
  }
4620
5308
  function hooksRegistered2(settings) {
4621
- if (!isRecord4(settings)) return false;
5309
+ if (!isRecord5(settings)) return false;
4622
5310
  const hooks = settings.hooks;
4623
- if (!isRecord4(hooks)) return false;
5311
+ if (!isRecord5(hooks)) return false;
4624
5312
  return hasHookCommand2(hooks.SessionStart, ".mancode/hooks/session-start.sh") && hasHookCommand2(
4625
5313
  hooks.UserPromptSubmit,
4626
5314
  ".mancode/hooks/user-prompt-submit.sh"
@@ -4629,21 +5317,21 @@ function hooksRegistered2(settings) {
4629
5317
  function hasHookCommand2(value, needle) {
4630
5318
  if (!Array.isArray(value)) return false;
4631
5319
  return value.some((group) => {
4632
- if (!isRecord4(group) || !Array.isArray(group.hooks)) return false;
5320
+ if (!isRecord5(group) || !Array.isArray(group.hooks)) return false;
4633
5321
  return group.hooks.some((hook) => {
4634
- if (!isRecord4(hook) || typeof hook.command !== "string") return false;
5322
+ if (!isRecord5(hook) || typeof hook.command !== "string") return false;
4635
5323
  return hook.command.includes(needle);
4636
5324
  });
4637
5325
  });
4638
5326
  }
4639
5327
  async function estimateHookInjection(rootDir) {
4640
- const hookPath = path19.join(
5328
+ const hookPath = path21.join(
4641
5329
  rootDir,
4642
5330
  ".mancode",
4643
5331
  "hooks",
4644
5332
  "user-prompt-submit.sh"
4645
5333
  );
4646
- if (!await pathExists9(hookPath)) {
5334
+ if (!await pathExists10(hookPath)) {
4647
5335
  return { tokens: 0, cap: 800 };
4648
5336
  }
4649
5337
  try {
@@ -4662,15 +5350,28 @@ function runHookEstimate(rootDir, hookPath) {
4662
5350
  cwd: rootDir,
4663
5351
  stdio: ["pipe", "pipe", "ignore"]
4664
5352
  });
5353
+ let settled = false;
5354
+ const finish = (fn) => {
5355
+ if (settled) return;
5356
+ settled = true;
5357
+ clearTimeout(timeout);
5358
+ fn();
5359
+ };
5360
+ const timeout = setTimeout(() => {
5361
+ child.kill();
5362
+ finish(() => reject(new Error("hook estimate timed out")));
5363
+ }, HOOK_ESTIMATE_TIMEOUT_MS);
4665
5364
  let stdout = "";
4666
5365
  child.stdout.setEncoding("utf-8");
4667
5366
  child.stdout.on("data", (chunk) => {
4668
5367
  stdout += chunk;
4669
5368
  });
4670
- child.on("error", reject);
5369
+ child.on("error", (err) => finish(() => reject(err)));
4671
5370
  child.on("close", (code) => {
4672
- if (code === 0) resolve(stdout);
4673
- else reject(new Error(`hook exited with ${code}`));
5371
+ finish(() => {
5372
+ if (code === 0) resolve(stdout);
5373
+ else reject(new Error(`hook exited with ${code}`));
5374
+ });
4674
5375
  });
4675
5376
  child.stdin.end(
4676
5377
  JSON.stringify({ prompt: "design a button component with tailwind css" })
@@ -4733,10 +5434,10 @@ function formatTeamStatus(team) {
4733
5434
  function workflowStepMax(mode) {
4734
5435
  return mode === "man8" ? 3 : 8;
4735
5436
  }
4736
- function isRecord4(value) {
5437
+ function isRecord5(value) {
4737
5438
  return typeof value === "object" && value !== null;
4738
5439
  }
4739
- async function pathExists9(p) {
5440
+ async function pathExists10(p) {
4740
5441
  try {
4741
5442
  await fs7.access(p);
4742
5443
  return true;
@@ -4746,8 +5447,8 @@ async function pathExists9(p) {
4746
5447
  }
4747
5448
 
4748
5449
  // src/commands/uninstall.ts
4749
- import { access as access3, readFile as readFile8, rm as rm4, writeFile as writeFile9 } from "fs/promises";
4750
- import path20 from "path";
5450
+ import { access as access5, readFile as readFile8, rm as rm5, writeFile as writeFile11 } from "fs/promises";
5451
+ import path22 from "path";
4751
5452
  import process8 from "process";
4752
5453
  var EXIT_OK7 = 0;
4753
5454
  var EXIT_NOT_INITIALIZED5 = 1;
@@ -4768,8 +5469,8 @@ var CLAUDE_AGENT_FILES = [
4768
5469
  "plan-coach.md"
4769
5470
  ];
4770
5471
  async function uninstall(rootDir = process8.cwd(), platform, options = {}) {
4771
- const stateFile = path20.join(rootDir, ".mancode", "state.json");
4772
- if (!await pathExists10(stateFile)) {
5472
+ const stateFile = path22.join(rootDir, ".mancode", "state.json");
5473
+ if (!await pathExists11(stateFile)) {
4773
5474
  console.error("\u2717 mancode not initialized.");
4774
5475
  console.error(" Run `mancode init` first.");
4775
5476
  return EXIT_NOT_INITIALIZED5;
@@ -4780,7 +5481,7 @@ async function uninstall(rootDir = process8.cwd(), platform, options = {}) {
4780
5481
  if (!installer) {
4781
5482
  console.error(`\u2717 Unsupported platform: ${platform}`);
4782
5483
  console.error(
4783
- " Supported platforms: claude-code, cursor, codex, copilot"
5484
+ ` Supported platforms: ${getPlatformInstallers().map((item) => item.name).join(", ")}`
4784
5485
  );
4785
5486
  return EXIT_UNSUPPORTED_PLATFORM2;
4786
5487
  }
@@ -4809,34 +5510,36 @@ async function uninstallPlatform(rootDir, platform) {
4809
5510
  await uninstallCodex(rootDir);
4810
5511
  } else if (platform === "copilot") {
4811
5512
  await uninstallCopilot(rootDir);
5513
+ } else if (platform === "zcode") {
5514
+ await uninstallZcode(rootDir);
4812
5515
  }
4813
5516
  await removeFromConfig(rootDir, platform);
4814
5517
  }
4815
5518
  async function uninstallAll(rootDir) {
4816
5519
  console.log("\u2713 Removing all platform adapters...");
4817
- for (const p of ["claude-code", "cursor", "codex", "copilot"]) {
5520
+ for (const p of getPlatformInstallers().map((platform) => platform.name)) {
4818
5521
  await uninstallPlatform(rootDir, p);
4819
5522
  }
4820
5523
  console.log("\u2713 Removing .mancode/ directory...");
4821
- await rm4(path20.join(rootDir, ".mancode"), {
5524
+ await rm5(path22.join(rootDir, ".mancode"), {
4822
5525
  recursive: true,
4823
5526
  force: true
4824
5527
  });
4825
5528
  }
4826
5529
  async function uninstallClaudeCode(rootDir) {
4827
- const claudeDir = path20.join(rootDir, ".claude");
4828
- const skillsDir = path20.join(claudeDir, "skills");
4829
- const agentsDir = path20.join(claudeDir, "agents");
5530
+ const claudeDir = path22.join(rootDir, ".claude");
5531
+ const skillsDir = path22.join(claudeDir, "skills");
5532
+ const agentsDir = path22.join(claudeDir, "agents");
4830
5533
  for (const skillDir of CLAUDE_SKILL_DIRS) {
4831
- await rm4(path20.join(skillsDir, skillDir), { recursive: true, force: true });
5534
+ await rm5(path22.join(skillsDir, skillDir), { recursive: true, force: true });
4832
5535
  }
4833
5536
  for (const agentFile of CLAUDE_AGENT_FILES) {
4834
- await rm4(path20.join(agentsDir, agentFile), { force: true });
5537
+ await rm5(path22.join(agentsDir, agentFile), { force: true });
4835
5538
  }
4836
5539
  await cleanClaudeSettings(rootDir);
4837
5540
  }
4838
5541
  async function cleanClaudeSettings(rootDir) {
4839
- const settingsPath = path20.join(rootDir, ".claude", "settings.json");
5542
+ const settingsPath = path22.join(rootDir, ".claude", "settings.json");
4840
5543
  let content;
4841
5544
  try {
4842
5545
  content = await readFile8(settingsPath, "utf-8");
@@ -4849,18 +5552,19 @@ async function cleanClaudeSettings(rootDir) {
4849
5552
  } catch {
4850
5553
  return;
4851
5554
  }
4852
- if (!isRecord5(settings.hooks)) return;
5555
+ if (!isRecord6(settings.hooks)) return;
4853
5556
  const cleanedHooks = {};
4854
5557
  for (const [event, groups] of Object.entries(settings.hooks)) {
4855
5558
  if (!Array.isArray(groups)) {
4856
5559
  cleanedHooks[event] = groups;
4857
5560
  continue;
4858
5561
  }
4859
- const filtered = groups.filter((group) => {
4860
- if (!isRecord5(group) || !Array.isArray(group.hooks)) return true;
4861
- return !group.hooks.some(
4862
- (hook) => isRecord5(hook) && typeof hook.command === "string" && hook.command.includes(".mancode/hooks/")
5562
+ const filtered = groups.flatMap((group) => {
5563
+ if (!isRecord6(group) || !Array.isArray(group.hooks)) return [group];
5564
+ const hooks = group.hooks.filter(
5565
+ (hook) => !(isRecord6(hook) && typeof hook.command === "string" && hook.command.includes(".mancode/hooks/"))
4863
5566
  );
5567
+ return hooks.length > 0 ? [{ ...group, hooks }] : [];
4864
5568
  });
4865
5569
  if (filtered.length > 0) {
4866
5570
  cleanedHooks[event] = filtered;
@@ -4871,7 +5575,7 @@ async function cleanClaudeSettings(rootDir) {
4871
5575
  } else {
4872
5576
  settings.hooks = void 0;
4873
5577
  }
4874
- await writeFile9(
5578
+ await writeFile11(
4875
5579
  settingsPath,
4876
5580
  `${JSON.stringify(settings, null, 2)}
4877
5581
  `,
@@ -4879,27 +5583,29 @@ async function cleanClaudeSettings(rootDir) {
4879
5583
  );
4880
5584
  }
4881
5585
  async function uninstallCursor(rootDir) {
4882
- const rulesDir = path20.join(rootDir, ".cursor", "rules");
5586
+ const rulesDir = path22.join(rootDir, ".cursor", "rules");
4883
5587
  for (const file of MANCODE_CURSOR_RULE_FILES) {
4884
- await rm4(path20.join(rulesDir, file), { force: true });
5588
+ await rm5(path22.join(rulesDir, file), { force: true });
4885
5589
  }
5590
+ await removeCursorCommands(rootDir);
4886
5591
  }
4887
5592
  async function uninstallCodex(rootDir) {
4888
- const agentsPath = path20.join(rootDir, "AGENTS.md");
5593
+ const agentsPath = path22.join(rootDir, "AGENTS.md");
4889
5594
  try {
4890
5595
  const content = await readFile8(agentsPath, "utf-8");
4891
5596
  const cleaned = removeManagedBlock(content);
4892
5597
  if (cleaned.trim()) {
4893
- await writeFile9(agentsPath, `${cleaned}
5598
+ await writeFile11(agentsPath, `${cleaned}
4894
5599
  `, "utf-8");
4895
5600
  } else {
4896
- await rm4(agentsPath, { force: true });
5601
+ await rm5(agentsPath, { force: true });
4897
5602
  }
4898
5603
  } catch {
4899
5604
  }
5605
+ await removeCodexSkills(rootDir);
4900
5606
  }
4901
5607
  async function uninstallCopilot(rootDir) {
4902
- const instructionsPath = path20.join(
5608
+ const instructionsPath = path22.join(
4903
5609
  rootDir,
4904
5610
  ".github",
4905
5611
  "copilot-instructions.md"
@@ -4908,22 +5614,50 @@ async function uninstallCopilot(rootDir) {
4908
5614
  const content = await readFile8(instructionsPath, "utf-8");
4909
5615
  const cleaned = removeManagedBlock(content);
4910
5616
  if (cleaned.trim()) {
4911
- await writeFile9(instructionsPath, `${cleaned}
5617
+ await writeFile11(instructionsPath, `${cleaned}
4912
5618
  `, "utf-8");
4913
5619
  } else {
4914
- await rm4(instructionsPath, { force: true });
5620
+ await rm5(instructionsPath, { force: true });
4915
5621
  }
4916
5622
  } catch {
4917
5623
  }
5624
+ await removeCopilotPrompts(rootDir);
5625
+ }
5626
+ async function uninstallZcode(rootDir) {
5627
+ const agentsPath = path22.join(rootDir, "AGENTS.md");
5628
+ try {
5629
+ const content = await readFile8(agentsPath, "utf-8");
5630
+ const cleaned = removeManagedBlock(
5631
+ content,
5632
+ ZCODE_MANCODE_START_MARKER,
5633
+ ZCODE_MANCODE_END_MARKER
5634
+ );
5635
+ if (cleaned.trim()) {
5636
+ await writeFile11(agentsPath, `${cleaned}
5637
+ `, "utf-8");
5638
+ } else {
5639
+ await rm5(agentsPath, { force: true });
5640
+ }
5641
+ } catch {
5642
+ }
5643
+ await removeZcodeSkills(rootDir);
4918
5644
  }
4919
5645
  async function removeFromConfig(rootDir, platform) {
4920
- const configPath = path20.join(rootDir, ".mancode", "config.json");
5646
+ const configPath = path22.join(rootDir, ".mancode", "config.json");
4921
5647
  try {
4922
5648
  const raw = await readFile8(configPath, "utf-8");
4923
5649
  const config = JSON.parse(raw);
4924
5650
  if (Array.isArray(config.platforms)) {
4925
5651
  config.platforms = config.platforms.filter((p) => p !== platform);
4926
- await writeFile9(
5652
+ if (isRecord6(config.platformOptions)) {
5653
+ const platformOptions = Object.fromEntries(
5654
+ Object.entries(config.platformOptions).filter(
5655
+ ([name]) => name !== platform
5656
+ )
5657
+ );
5658
+ config.platformOptions = Object.keys(platformOptions).length > 0 ? platformOptions : void 0;
5659
+ }
5660
+ await writeFile11(
4927
5661
  configPath,
4928
5662
  `${JSON.stringify(config, null, 2)}
4929
5663
  `,
@@ -4933,12 +5667,12 @@ async function removeFromConfig(rootDir, platform) {
4933
5667
  } catch {
4934
5668
  }
4935
5669
  }
4936
- function isRecord5(value) {
5670
+ function isRecord6(value) {
4937
5671
  return typeof value === "object" && value !== null && !Array.isArray(value);
4938
5672
  }
4939
- async function pathExists10(p) {
5673
+ async function pathExists11(p) {
4940
5674
  try {
4941
- await access3(p);
5675
+ await access5(p);
4942
5676
  return true;
4943
5677
  } catch {
4944
5678
  return false;
@@ -4956,13 +5690,13 @@ function version() {
4956
5690
  }
4957
5691
 
4958
5692
  // src/commands/workflow.ts
4959
- import { access as access4 } from "fs/promises";
4960
- import path21 from "path";
5693
+ import { access as access6 } from "fs/promises";
5694
+ import path23 from "path";
4961
5695
  var EXIT_OK8 = 0;
4962
5696
  var EXIT_NOT_INITIALIZED6 = 1;
4963
5697
  var EXIT_INVALID_ARG2 = 2;
4964
5698
  async function workflow(rootDir, subcommand, args = [], options = {}) {
4965
- if (!await pathExists11(path21.join(rootDir, ".mancode", "state.json"))) {
5699
+ if (!await pathExists12(path23.join(rootDir, ".mancode", "state.json"))) {
4966
5700
  if (options.json) {
4967
5701
  console.log(JSON.stringify({ error: "not initialized" }, null, 2));
4968
5702
  } else {
@@ -5219,9 +5953,9 @@ function ago(iso) {
5219
5953
  if (hours < 24) return `${hours}h ago`;
5220
5954
  return `${Math.floor(hours / 24)}d ago`;
5221
5955
  }
5222
- async function pathExists11(p) {
5956
+ async function pathExists12(p) {
5223
5957
  try {
5224
- await access4(p);
5958
+ await access6(p);
5225
5959
  return true;
5226
5960
  } catch {
5227
5961
  return false;
@@ -5234,10 +5968,9 @@ program.command("init").description("Initialize mancode in the current project")
5234
5968
  const code = await init(process.cwd(), options);
5235
5969
  process.exitCode = code;
5236
5970
  });
5237
- program.command("install [platform]").description("Install platform adapter (claude-code, cursor, codex, copilot)").option("--force", "Reinstall even if already installed").option("--minimal", "Minimal install (MVP-2)").option(
5238
- "--commit-hook",
5239
- "Install optional /manteam Conventional Commit enforcement hook"
5240
- ).action(async (platform, options) => {
5971
+ program.command("install [platform]").description(
5972
+ "Install platform adapter (claude-code, cursor, codex, copilot, zcode)"
5973
+ ).option("--force", "Reinstall even if already installed").option("--minimal", "Minimal install (MVP-2)").action(async (platform, options) => {
5241
5974
  const code = await install(
5242
5975
  process.cwd(),
5243
5976
  platform ?? "claude-code",