kairn-cli 2.6.0 → 2.7.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/dist/cli.js +1799 -165
  2. package/dist/cli.js.map +1 -1
  3. package/package.json +1 -1
package/dist/cli.js CHANGED
@@ -1409,68 +1409,1619 @@ Return ONLY valid JSON.`;
1409
1409
  }
1410
1410
  });
1411
1411
 
1412
+ // src/ir/types.ts
1413
+ function createEmptyIR() {
1414
+ return {
1415
+ meta: {
1416
+ name: "",
1417
+ purpose: "",
1418
+ techStack: { language: "" },
1419
+ autonomyLevel: 2
1420
+ },
1421
+ sections: [],
1422
+ commands: [],
1423
+ rules: [],
1424
+ agents: [],
1425
+ skills: [],
1426
+ docs: [],
1427
+ hooks: [],
1428
+ settings: createEmptySettings(),
1429
+ mcpServers: [],
1430
+ intents: []
1431
+ };
1432
+ }
1433
+ function createEmptySettings() {
1434
+ return { hooks: {}, raw: {} };
1435
+ }
1436
+ function createSection(id, heading, content, order) {
1437
+ return { id, heading, content, order };
1438
+ }
1439
+ function createCommandNode(name, content, description) {
1440
+ return { name, description: description ?? "", content };
1441
+ }
1442
+ function createRuleNode(name, content, paths) {
1443
+ const node = { name, content };
1444
+ if (paths !== void 0) {
1445
+ node.paths = paths;
1446
+ }
1447
+ return node;
1448
+ }
1449
+ function createAgentNode(name, content, model) {
1450
+ const node = { name, content };
1451
+ if (model !== void 0) {
1452
+ node.model = model;
1453
+ }
1454
+ return node;
1455
+ }
1456
+ function createEmptyDiff() {
1457
+ return {
1458
+ sections: {
1459
+ added: [],
1460
+ removed: [],
1461
+ modified: [],
1462
+ reordered: []
1463
+ },
1464
+ commands: {
1465
+ added: [],
1466
+ removed: [],
1467
+ modified: []
1468
+ },
1469
+ rules: {
1470
+ added: [],
1471
+ removed: [],
1472
+ modified: []
1473
+ },
1474
+ agents: {
1475
+ added: [],
1476
+ removed: [],
1477
+ modified: []
1478
+ },
1479
+ mcpServers: {
1480
+ added: [],
1481
+ removed: []
1482
+ },
1483
+ settings: {
1484
+ changes: []
1485
+ }
1486
+ };
1487
+ }
1488
+ var init_types = __esm({
1489
+ "src/ir/types.ts"() {
1490
+ "use strict";
1491
+ }
1492
+ });
1493
+
1494
+ // src/ir/parser.ts
1495
+ import fs21 from "fs/promises";
1496
+ import path21 from "path";
1497
+ function slugify(heading) {
1498
+ return heading.toLowerCase().trim().replace(/\s+/g, "-").replace(/[^a-z0-9-]/g, "").replace(/-+/g, "-").replace(/^-|-$/g, "");
1499
+ }
1500
+ function resolveSectionId(heading) {
1501
+ const trimmed = heading.trim();
1502
+ for (const entry of SECTION_ID_MAP) {
1503
+ if (entry.pattern.test(trimmed)) {
1504
+ return entry.id;
1505
+ }
1506
+ }
1507
+ return `custom-${slugify(trimmed)}`;
1508
+ }
1509
+ function parseYamlFrontmatter(content) {
1510
+ if (!content.startsWith("---\n") && !content.startsWith("---\r\n")) {
1511
+ return { frontmatter: {}, body: content };
1512
+ }
1513
+ const secondDash = content.indexOf("\n---", 3);
1514
+ if (secondDash === -1) {
1515
+ return { frontmatter: {}, body: content };
1516
+ }
1517
+ const yamlBlock = content.slice(4, secondDash);
1518
+ const afterClose = secondDash + 4;
1519
+ const body = content.slice(afterClose).replace(/^\r?\n/, "");
1520
+ const frontmatter = {};
1521
+ const lines = yamlBlock.split("\n");
1522
+ let currentKey = null;
1523
+ let currentList = null;
1524
+ for (const line of lines) {
1525
+ const trimmed = line.trimEnd();
1526
+ if (currentKey !== null && /^\s+-\s+/.test(trimmed)) {
1527
+ if (currentList === null) {
1528
+ currentList = [];
1529
+ }
1530
+ let value = trimmed.replace(/^\s+-\s+/, "").trim();
1531
+ value = stripQuotes(value);
1532
+ currentList.push(value);
1533
+ continue;
1534
+ }
1535
+ if (currentKey !== null && currentList !== null) {
1536
+ frontmatter[currentKey] = currentList;
1537
+ currentKey = null;
1538
+ currentList = null;
1539
+ }
1540
+ const colonIdx = trimmed.indexOf(":");
1541
+ if (colonIdx > 0) {
1542
+ const key = trimmed.slice(0, colonIdx).trim();
1543
+ const rawValue = trimmed.slice(colonIdx + 1).trim();
1544
+ if (rawValue === "") {
1545
+ currentKey = key;
1546
+ currentList = null;
1547
+ } else {
1548
+ frontmatter[key] = stripQuotes(rawValue);
1549
+ currentKey = null;
1550
+ currentList = null;
1551
+ }
1552
+ }
1553
+ }
1554
+ if (currentKey !== null && currentList !== null) {
1555
+ frontmatter[currentKey] = currentList;
1556
+ }
1557
+ return { frontmatter, body };
1558
+ }
1559
+ function stripQuotes(value) {
1560
+ if (value.startsWith('"') && value.endsWith('"') || value.startsWith("'") && value.endsWith("'")) {
1561
+ return value.slice(1, -1);
1562
+ }
1563
+ return value;
1564
+ }
1565
+ function parseClaudeMd(content) {
1566
+ const meta = {
1567
+ techStack: { language: "" },
1568
+ autonomyLevel: 2
1569
+ };
1570
+ const sections = [];
1571
+ const chunks = content.split(/^## /gm);
1572
+ const preamble = chunks[0];
1573
+ const titleMatch = preamble.match(/^# (.+)$/m);
1574
+ if (titleMatch) {
1575
+ meta.name = titleMatch[1].trim();
1576
+ } else {
1577
+ meta.name = "";
1578
+ }
1579
+ const preambleHeading = meta.name ? `# ${meta.name}` : "";
1580
+ const preambleContent = titleMatch ? preamble.slice(preamble.indexOf(titleMatch[0]) + titleMatch[0].length).trim() : preamble.trim();
1581
+ sections.push({
1582
+ id: "preamble",
1583
+ heading: preambleHeading,
1584
+ content: preambleContent,
1585
+ order: 0
1586
+ });
1587
+ for (let i = 1; i < chunks.length; i++) {
1588
+ const chunk = chunks[i];
1589
+ const newlineIdx = chunk.indexOf("\n");
1590
+ const heading = newlineIdx >= 0 ? chunk.slice(0, newlineIdx).trim() : chunk.trim();
1591
+ const sectionContent = newlineIdx >= 0 ? chunk.slice(newlineIdx + 1).trim() : "";
1592
+ const sectionId = resolveSectionId(heading);
1593
+ sections.push({
1594
+ id: sectionId,
1595
+ heading: `## ${heading}`,
1596
+ content: sectionContent,
1597
+ order: i
1598
+ });
1599
+ if (sectionId === "purpose") {
1600
+ const firstParagraph = sectionContent.split(/\n\n/)[0].trim();
1601
+ meta.purpose = firstParagraph;
1602
+ }
1603
+ if (sectionId === "tech-stack") {
1604
+ meta.techStack = extractTechStack(sectionContent);
1605
+ }
1606
+ }
1607
+ return { meta, sections };
1608
+ }
1609
+ function extractTechStack(content) {
1610
+ const stack = { language: "" };
1611
+ const lines = content.split("\n");
1612
+ for (const line of lines) {
1613
+ const trimmed = line.trim();
1614
+ if (!trimmed.startsWith("-")) continue;
1615
+ const bullet = trimmed.slice(1).trim().toLowerCase();
1616
+ if (!stack.language && (bullet.includes("typescript") || bullet.includes("javascript"))) {
1617
+ stack.language = bullet.includes("typescript") ? "TypeScript" : "JavaScript";
1618
+ } else if (!stack.language && bullet.includes("python")) {
1619
+ stack.language = "Python";
1620
+ } else if (!stack.language && bullet.includes("rust")) {
1621
+ stack.language = "Rust";
1622
+ } else if (!stack.language && bullet.includes("go ") || !stack.language && bullet.startsWith("go,")) {
1623
+ stack.language = "Go";
1624
+ }
1625
+ if (!stack.buildTool) {
1626
+ const buildTools = [
1627
+ "tsup",
1628
+ "webpack",
1629
+ "vite",
1630
+ "esbuild",
1631
+ "rollup",
1632
+ "parcel",
1633
+ "turbopack",
1634
+ "swc",
1635
+ "cargo",
1636
+ "make",
1637
+ "cmake"
1638
+ ];
1639
+ for (const tool of buildTools) {
1640
+ if (bullet.includes(tool)) {
1641
+ stack.buildTool = tool;
1642
+ break;
1643
+ }
1644
+ }
1645
+ }
1646
+ if (!stack.testRunner) {
1647
+ const testRunners = [
1648
+ "vitest",
1649
+ "jest",
1650
+ "mocha",
1651
+ "ava",
1652
+ "tap",
1653
+ "pytest",
1654
+ "cargo test"
1655
+ ];
1656
+ for (const runner of testRunners) {
1657
+ if (bullet.includes(runner)) {
1658
+ stack.testRunner = runner;
1659
+ break;
1660
+ }
1661
+ }
1662
+ }
1663
+ if (!stack.framework) {
1664
+ const frameworks = [
1665
+ "commander.js",
1666
+ "commander",
1667
+ "express",
1668
+ "fastify",
1669
+ "next.js",
1670
+ "nextjs",
1671
+ "react",
1672
+ "vue",
1673
+ "angular",
1674
+ "svelte",
1675
+ "django",
1676
+ "flask",
1677
+ "actix",
1678
+ "axum"
1679
+ ];
1680
+ for (const fw of frameworks) {
1681
+ if (bullet.includes(fw)) {
1682
+ stack.framework = fw.charAt(0).toUpperCase() + fw.slice(1);
1683
+ break;
1684
+ }
1685
+ }
1686
+ }
1687
+ if (!stack.packageManager) {
1688
+ const pkgManagers = ["pnpm", "yarn", "bun", "npm", "cargo", "pip"];
1689
+ for (const pm of pkgManagers) {
1690
+ if (bullet.includes(`${pm} `)) {
1691
+ stack.packageManager = pm;
1692
+ break;
1693
+ }
1694
+ }
1695
+ }
1696
+ }
1697
+ return stack;
1698
+ }
1699
+ function parseSettings(content) {
1700
+ const parsed = JSON.parse(content);
1701
+ const settings = createEmptySettings();
1702
+ const permissions = parsed["permissions"];
1703
+ if (permissions) {
1704
+ const deny = permissions["deny"];
1705
+ if (Array.isArray(deny) && deny.length > 0) {
1706
+ settings.denyPatterns = deny;
1707
+ }
1708
+ }
1709
+ const statusLine = parsed["statusLine"];
1710
+ if (statusLine && typeof statusLine.command === "string") {
1711
+ settings.statusLine = { command: statusLine.command };
1712
+ }
1713
+ const hooksRaw = parsed["hooks"];
1714
+ if (hooksRaw && typeof hooksRaw === "object") {
1715
+ const knownEvents = [
1716
+ "PreToolUse",
1717
+ "PostToolUse",
1718
+ "UserPromptSubmit",
1719
+ "SessionStart",
1720
+ "PostCompact"
1721
+ ];
1722
+ for (const event of knownEvents) {
1723
+ const entries = hooksRaw[event];
1724
+ if (Array.isArray(entries) && entries.length > 0) {
1725
+ settings.hooks[event] = entries;
1726
+ }
1727
+ }
1728
+ }
1729
+ const extractedKeys = /* @__PURE__ */ new Set(["permissions", "hooks", "statusLine"]);
1730
+ for (const [key, value] of Object.entries(parsed)) {
1731
+ if (!extractedKeys.has(key)) {
1732
+ settings.raw[key] = value;
1733
+ }
1734
+ }
1735
+ return settings;
1736
+ }
1737
+ function parseMcpConfig(content) {
1738
+ const parsed = JSON.parse(content);
1739
+ const servers = parsed["mcpServers"];
1740
+ if (!servers || typeof servers !== "object") {
1741
+ return [];
1742
+ }
1743
+ const nodes = [];
1744
+ for (const [id, config] of Object.entries(servers)) {
1745
+ const command = config["command"];
1746
+ const args = config["args"] ?? [];
1747
+ const env = config["env"];
1748
+ const node = { id, command, args };
1749
+ if (env && typeof env === "object" && Object.keys(env).length > 0) {
1750
+ node.env = env;
1751
+ }
1752
+ nodes.push(node);
1753
+ }
1754
+ return nodes;
1755
+ }
1756
+ async function readDirSafe(dirPath) {
1757
+ try {
1758
+ return await fs21.readdir(dirPath);
1759
+ } catch {
1760
+ return [];
1761
+ }
1762
+ }
1763
+ async function readFileSafe2(filePath) {
1764
+ try {
1765
+ return await fs21.readFile(filePath, "utf-8");
1766
+ } catch {
1767
+ return null;
1768
+ }
1769
+ }
1770
+ async function isDirectory(filePath) {
1771
+ try {
1772
+ const stat = await fs21.stat(filePath);
1773
+ return stat.isDirectory();
1774
+ } catch {
1775
+ return false;
1776
+ }
1777
+ }
1778
+ async function parseCommands(harnessPath) {
1779
+ const dirPath = path21.join(harnessPath, "commands");
1780
+ const entries = await readDirSafe(dirPath);
1781
+ const nodes = [];
1782
+ for (const entry of entries) {
1783
+ if (!entry.endsWith(".md")) continue;
1784
+ const filePath = path21.join(dirPath, entry);
1785
+ const content = await readFileSafe2(filePath);
1786
+ if (content === null) continue;
1787
+ const name = entry.replace(/\.md$/, "");
1788
+ const firstLine = content.split("\n")[0].trim();
1789
+ const description = firstLine && !firstLine.startsWith("#") && !firstLine.startsWith("```") ? firstLine : "";
1790
+ nodes.push({ name, description, content });
1791
+ }
1792
+ return nodes;
1793
+ }
1794
+ async function parseRules(harnessPath) {
1795
+ const dirPath = path21.join(harnessPath, "rules");
1796
+ const entries = await readDirSafe(dirPath);
1797
+ const nodes = [];
1798
+ for (const entry of entries) {
1799
+ if (!entry.endsWith(".md")) continue;
1800
+ const filePath = path21.join(dirPath, entry);
1801
+ const rawContent = await readFileSafe2(filePath);
1802
+ if (rawContent === null) continue;
1803
+ const name = entry.replace(/\.md$/, "");
1804
+ const { frontmatter, body } = parseYamlFrontmatter(rawContent);
1805
+ const node = { name, content: body };
1806
+ const paths = frontmatter["paths"];
1807
+ if (Array.isArray(paths) && paths.length > 0) {
1808
+ node.paths = paths;
1809
+ }
1810
+ nodes.push(node);
1811
+ }
1812
+ return nodes;
1813
+ }
1814
+ async function parseAgents(harnessPath) {
1815
+ const dirPath = path21.join(harnessPath, "agents");
1816
+ const entries = await readDirSafe(dirPath);
1817
+ const nodes = [];
1818
+ for (const entry of entries) {
1819
+ if (!entry.endsWith(".md")) continue;
1820
+ const filePath = path21.join(dirPath, entry);
1821
+ const rawContent = await readFileSafe2(filePath);
1822
+ if (rawContent === null) continue;
1823
+ const fileBaseName = entry.replace(/\.md$/, "");
1824
+ const { frontmatter, body } = parseYamlFrontmatter(rawContent);
1825
+ const name = typeof frontmatter["name"] === "string" ? frontmatter["name"] : fileBaseName;
1826
+ const node = { name, content: body };
1827
+ if (typeof frontmatter["model"] === "string") {
1828
+ node.model = frontmatter["model"];
1829
+ }
1830
+ const disallowedTools = frontmatter["disallowedTools"];
1831
+ if (Array.isArray(disallowedTools)) {
1832
+ node.disallowedTools = disallowedTools;
1833
+ }
1834
+ const knownKeys = /* @__PURE__ */ new Set(["name", "model", "disallowedTools"]);
1835
+ const extra = {};
1836
+ for (const [key, value] of Object.entries(frontmatter)) {
1837
+ if (!knownKeys.has(key)) {
1838
+ extra[key] = value;
1839
+ }
1840
+ }
1841
+ if (Object.keys(extra).length > 0) {
1842
+ node.extraFrontmatter = extra;
1843
+ }
1844
+ nodes.push(node);
1845
+ }
1846
+ return nodes;
1847
+ }
1848
+ async function parseSkills(harnessPath) {
1849
+ const dirPath = path21.join(harnessPath, "skills");
1850
+ const entries = await readDirSafe(dirPath);
1851
+ const nodes = [];
1852
+ for (const entry of entries) {
1853
+ const entryPath = path21.join(dirPath, entry);
1854
+ if (entry.endsWith(".md")) {
1855
+ const content = await readFileSafe2(entryPath);
1856
+ if (content === null) continue;
1857
+ const name = entry.replace(/\.md$/, "");
1858
+ nodes.push({ name, content });
1859
+ } else if (await isDirectory(entryPath)) {
1860
+ let content = await readFileSafe2(path21.join(entryPath, "skill.md"));
1861
+ if (content === null) {
1862
+ content = await readFileSafe2(path21.join(entryPath, "SKILL.md"));
1863
+ }
1864
+ if (content === null) continue;
1865
+ nodes.push({ name: entry, content });
1866
+ }
1867
+ }
1868
+ return nodes;
1869
+ }
1870
+ async function parseDocs(harnessPath) {
1871
+ const dirPath = path21.join(harnessPath, "docs");
1872
+ const entries = await readDirSafe(dirPath);
1873
+ const nodes = [];
1874
+ for (const entry of entries) {
1875
+ if (!entry.endsWith(".md")) continue;
1876
+ const filePath = path21.join(dirPath, entry);
1877
+ const content = await readFileSafe2(filePath);
1878
+ if (content === null) continue;
1879
+ const name = entry.replace(/\.md$/, "");
1880
+ nodes.push({ name, content });
1881
+ }
1882
+ return nodes;
1883
+ }
1884
+ async function parseHooks(harnessPath) {
1885
+ const dirPath = path21.join(harnessPath, "hooks");
1886
+ const entries = await readDirSafe(dirPath);
1887
+ const nodes = [];
1888
+ for (const entry of entries) {
1889
+ if (!entry.endsWith(".mjs")) continue;
1890
+ const filePath = path21.join(dirPath, entry);
1891
+ const content = await readFileSafe2(filePath);
1892
+ if (content === null) continue;
1893
+ const name = entry.replace(/\.mjs$/, "");
1894
+ nodes.push({ name, content, type: "command" });
1895
+ }
1896
+ return nodes;
1897
+ }
1898
+ async function parseHarness(harnessPath) {
1899
+ const ir = createEmptyIR();
1900
+ const claudeMdContent = await readFileSafe2(
1901
+ path21.join(harnessPath, "CLAUDE.md")
1902
+ );
1903
+ if (claudeMdContent !== null) {
1904
+ const { meta, sections } = parseClaudeMd(claudeMdContent);
1905
+ ir.meta = {
1906
+ ...ir.meta,
1907
+ ...meta,
1908
+ techStack: { ...ir.meta.techStack, ...meta.techStack }
1909
+ };
1910
+ ir.sections = sections;
1911
+ }
1912
+ const settingsContent = await readFileSafe2(
1913
+ path21.join(harnessPath, "settings.json")
1914
+ );
1915
+ if (settingsContent !== null) {
1916
+ ir.settings = parseSettings(settingsContent);
1917
+ }
1918
+ const [commands, rules, agents, skills, docs, hooks] = await Promise.all([
1919
+ parseCommands(harnessPath),
1920
+ parseRules(harnessPath),
1921
+ parseAgents(harnessPath),
1922
+ parseSkills(harnessPath),
1923
+ parseDocs(harnessPath),
1924
+ parseHooks(harnessPath)
1925
+ ]);
1926
+ ir.commands = commands;
1927
+ ir.rules = rules;
1928
+ ir.agents = agents;
1929
+ ir.skills = skills;
1930
+ ir.docs = docs;
1931
+ ir.hooks = hooks;
1932
+ const mcpServers = [];
1933
+ const seenIds = /* @__PURE__ */ new Set();
1934
+ const parentMcpPath = path21.join(path21.dirname(harnessPath), ".mcp.json");
1935
+ const parentMcpContent = await readFileSafe2(parentMcpPath);
1936
+ if (parentMcpContent !== null) {
1937
+ for (const node of parseMcpConfig(parentMcpContent)) {
1938
+ if (!seenIds.has(node.id)) {
1939
+ seenIds.add(node.id);
1940
+ mcpServers.push(node);
1941
+ }
1942
+ }
1943
+ }
1944
+ const innerMcpPath = path21.join(harnessPath, ".mcp.json");
1945
+ const innerMcpContent = await readFileSafe2(innerMcpPath);
1946
+ if (innerMcpContent !== null) {
1947
+ for (const node of parseMcpConfig(innerMcpContent)) {
1948
+ if (!seenIds.has(node.id)) {
1949
+ seenIds.add(node.id);
1950
+ mcpServers.push(node);
1951
+ }
1952
+ }
1953
+ }
1954
+ ir.mcpServers = mcpServers;
1955
+ return ir;
1956
+ }
1957
+ var SECTION_ID_MAP;
1958
+ var init_parser = __esm({
1959
+ "src/ir/parser.ts"() {
1960
+ "use strict";
1961
+ init_types();
1962
+ SECTION_ID_MAP = [
1963
+ { pattern: /^(purpose|about|what)\b/i, id: "purpose" },
1964
+ { pattern: /^(tech\s*stack|technology|stack)\b/i, id: "tech-stack" },
1965
+ { pattern: /^(commands|key\s*commands)\b/i, id: "commands" },
1966
+ { pattern: /^architecture\b/i, id: "architecture" },
1967
+ { pattern: /^conventions?\b/i, id: "conventions" },
1968
+ { pattern: /^verification\b/i, id: "verification" },
1969
+ { pattern: /^(known\s*gotchas|gotchas)\b/i, id: "gotchas" },
1970
+ { pattern: /^output\b/i, id: "output" },
1971
+ { pattern: /^debugging\b/i, id: "debugging" },
1972
+ { pattern: /^git\b/i, id: "git" }
1973
+ ];
1974
+ }
1975
+ });
1976
+
1977
+ // src/ir/translate.ts
1978
+ function extractName(filePath, pattern) {
1979
+ const match = filePath.match(pattern);
1980
+ return match ? match[1] : null;
1981
+ }
1982
+ function findSectionContaining(ir, text) {
1983
+ return ir.sections.find((s) => s.content.includes(text));
1984
+ }
1985
+ function translateClaudeMdMutation(mutation, ir) {
1986
+ switch (mutation.action) {
1987
+ case "replace": {
1988
+ if (mutation.oldText === void 0) {
1989
+ return buildRawText(mutation);
1990
+ }
1991
+ const section = findSectionContaining(ir, mutation.oldText);
1992
+ if (!section) {
1993
+ return buildRawText(mutation);
1994
+ }
1995
+ return {
1996
+ type: "update_section",
1997
+ sectionId: section.id,
1998
+ content: section.content.replace(mutation.oldText, mutation.newText),
1999
+ rationale: mutation.rationale
2000
+ };
2001
+ }
2002
+ case "add_section": {
2003
+ const headingMatch = mutation.newText.match(/^## (.+)/);
2004
+ if (!headingMatch) {
2005
+ return buildRawText(mutation);
2006
+ }
2007
+ const headingText = headingMatch[1].trim();
2008
+ const sectionId = resolveSectionId(headingText);
2009
+ const heading = `## ${headingText}`;
2010
+ const newlineIdx = mutation.newText.indexOf("\n");
2011
+ const content = newlineIdx >= 0 ? mutation.newText.slice(newlineIdx + 1).trim() : "";
2012
+ const nextOrder = ir.sections.length;
2013
+ return {
2014
+ type: "add_section",
2015
+ section: createSection(sectionId, heading, content, nextOrder),
2016
+ rationale: mutation.rationale
2017
+ };
2018
+ }
2019
+ case "delete_section": {
2020
+ if (mutation.oldText === void 0) {
2021
+ return buildRawText(mutation);
2022
+ }
2023
+ const section = findSectionContaining(ir, mutation.oldText);
2024
+ if (!section) {
2025
+ return buildRawText(mutation);
2026
+ }
2027
+ return {
2028
+ type: "remove_section",
2029
+ sectionId: section.id,
2030
+ rationale: mutation.rationale
2031
+ };
2032
+ }
2033
+ default:
2034
+ return buildRawText(mutation);
2035
+ }
2036
+ }
2037
+ function translateCommandMutation(mutation, name) {
2038
+ switch (mutation.action) {
2039
+ case "create_file":
2040
+ return {
2041
+ type: "add_command",
2042
+ command: createCommandNode(name, mutation.newText),
2043
+ rationale: mutation.rationale
2044
+ };
2045
+ case "delete_file":
2046
+ return {
2047
+ type: "remove_command",
2048
+ name,
2049
+ rationale: mutation.rationale
2050
+ };
2051
+ case "replace":
2052
+ return {
2053
+ type: "update_command",
2054
+ name,
2055
+ content: mutation.newText,
2056
+ rationale: mutation.rationale
2057
+ };
2058
+ default:
2059
+ return buildRawText(mutation);
2060
+ }
2061
+ }
2062
+ function translateRuleMutation(mutation, name) {
2063
+ switch (mutation.action) {
2064
+ case "create_file":
2065
+ return {
2066
+ type: "add_rule",
2067
+ rule: createRuleNode(name, mutation.newText),
2068
+ rationale: mutation.rationale
2069
+ };
2070
+ case "delete_file":
2071
+ return {
2072
+ type: "remove_rule",
2073
+ name,
2074
+ rationale: mutation.rationale
2075
+ };
2076
+ case "replace":
2077
+ return {
2078
+ type: "update_rule",
2079
+ name,
2080
+ content: mutation.newText,
2081
+ rationale: mutation.rationale
2082
+ };
2083
+ default:
2084
+ return buildRawText(mutation);
2085
+ }
2086
+ }
2087
+ function translateAgentMutation(mutation, name) {
2088
+ switch (mutation.action) {
2089
+ case "create_file":
2090
+ return {
2091
+ type: "add_agent",
2092
+ agent: createAgentNode(name, mutation.newText),
2093
+ rationale: mutation.rationale
2094
+ };
2095
+ case "delete_file":
2096
+ return {
2097
+ type: "remove_agent",
2098
+ name,
2099
+ rationale: mutation.rationale
2100
+ };
2101
+ case "replace":
2102
+ return {
2103
+ type: "update_agent",
2104
+ name,
2105
+ changes: { content: mutation.newText },
2106
+ rationale: mutation.rationale
2107
+ };
2108
+ default:
2109
+ return buildRawText(mutation);
2110
+ }
2111
+ }
2112
+ function buildRawText(mutation) {
2113
+ return {
2114
+ type: "raw_text",
2115
+ file: mutation.file,
2116
+ action: mutation.action,
2117
+ oldText: mutation.oldText,
2118
+ newText: mutation.newText,
2119
+ rationale: mutation.rationale
2120
+ };
2121
+ }
2122
+ function translateMutation(mutation, ir) {
2123
+ if (mutation.file === "CLAUDE.md") {
2124
+ return translateClaudeMdMutation(mutation, ir);
2125
+ }
2126
+ const commandName = extractName(mutation.file, COMMANDS_PATH_RE);
2127
+ if (commandName !== null) {
2128
+ return translateCommandMutation(mutation, commandName);
2129
+ }
2130
+ const ruleName = extractName(mutation.file, RULES_PATH_RE);
2131
+ if (ruleName !== null) {
2132
+ return translateRuleMutation(mutation, ruleName);
2133
+ }
2134
+ const agentName = extractName(mutation.file, AGENTS_PATH_RE);
2135
+ if (agentName !== null) {
2136
+ return translateAgentMutation(mutation, agentName);
2137
+ }
2138
+ return buildRawText(mutation);
2139
+ }
2140
+ function translateMutations(mutations, ir) {
2141
+ return mutations.map((m) => translateMutation(m, ir));
2142
+ }
2143
+ var COMMANDS_PATH_RE, RULES_PATH_RE, AGENTS_PATH_RE;
2144
+ var init_translate = __esm({
2145
+ "src/ir/translate.ts"() {
2146
+ "use strict";
2147
+ init_types();
2148
+ init_parser();
2149
+ COMMANDS_PATH_RE = /^commands\/([^/]+?)(?:\.md)?$/;
2150
+ RULES_PATH_RE = /^rules\/([^/]+?)(?:\.md)?$/;
2151
+ AGENTS_PATH_RE = /^agents\/([^/]+?)(?:\.md)?$/;
2152
+ }
2153
+ });
2154
+
2155
+ // src/ir/mutations.ts
2156
+ function sectionExists(ir, id) {
2157
+ return ir.sections.some((s) => s.id === id);
2158
+ }
2159
+ function commandExists(ir, name) {
2160
+ return ir.commands.some((c) => c.name === name);
2161
+ }
2162
+ function ruleExists(ir, name) {
2163
+ return ir.rules.some((r) => r.name === name);
2164
+ }
2165
+ function agentExists(ir, name) {
2166
+ return ir.agents.some((a) => a.name === name);
2167
+ }
2168
+ function mcpServerExists(ir, id) {
2169
+ return ir.mcpServers.some((s) => s.id === id);
2170
+ }
2171
+ function deepSet(obj, segments, value) {
2172
+ if (segments.length === 0) return obj;
2173
+ const [head, ...rest] = segments;
2174
+ if (rest.length === 0) {
2175
+ return { ...obj, [head]: value };
2176
+ }
2177
+ const existing = obj[head];
2178
+ const child = existing !== null && typeof existing === "object" && !Array.isArray(existing) ? existing : {};
2179
+ return { ...obj, [head]: deepSet(child, rest, value) };
2180
+ }
2181
+ function applySettingsUpdate(settings, path31, value) {
2182
+ const segments = path31.split(".");
2183
+ const topKey = segments[0];
2184
+ if (STRUCTURED_SETTINGS_KEYS.has(topKey)) {
2185
+ const settingsRecord = { ...settings };
2186
+ const updated = deepSet(settingsRecord, segments, value);
2187
+ return {
2188
+ ...settings,
2189
+ ...updated,
2190
+ // Preserve raw as-is (deepSet may have overwritten it if path happened to be "raw")
2191
+ raw: settings.raw
2192
+ };
2193
+ }
2194
+ const updatedRaw = deepSet({ ...settings.raw }, segments, value);
2195
+ return {
2196
+ ...settings,
2197
+ raw: updatedRaw
2198
+ };
2199
+ }
2200
+ function applyIRMutation(ir, mutation) {
2201
+ switch (mutation.type) {
2202
+ // -- Sections ----------------------------------------------------------
2203
+ case "update_section": {
2204
+ if (!sectionExists(ir, mutation.sectionId)) {
2205
+ throw new Error(`Section '${mutation.sectionId}' not found`);
2206
+ }
2207
+ return {
2208
+ ...ir,
2209
+ sections: ir.sections.map(
2210
+ (s) => s.id === mutation.sectionId ? { ...s, content: mutation.content } : s
2211
+ )
2212
+ };
2213
+ }
2214
+ case "add_section": {
2215
+ if (sectionExists(ir, mutation.section.id)) {
2216
+ throw new Error(`Section '${mutation.section.id}' already exists`);
2217
+ }
2218
+ return {
2219
+ ...ir,
2220
+ sections: [...ir.sections, { ...mutation.section }]
2221
+ };
2222
+ }
2223
+ case "remove_section": {
2224
+ if (!sectionExists(ir, mutation.sectionId)) {
2225
+ throw new Error(`Section '${mutation.sectionId}' not found`);
2226
+ }
2227
+ return {
2228
+ ...ir,
2229
+ sections: ir.sections.filter((s) => s.id !== mutation.sectionId)
2230
+ };
2231
+ }
2232
+ case "reorder_section": {
2233
+ if (!sectionExists(ir, mutation.sectionId)) {
2234
+ throw new Error(`Section '${mutation.sectionId}' not found`);
2235
+ }
2236
+ return {
2237
+ ...ir,
2238
+ sections: ir.sections.map(
2239
+ (s) => s.id === mutation.sectionId ? { ...s, order: mutation.newOrder } : s
2240
+ )
2241
+ };
2242
+ }
2243
+ // -- Commands ----------------------------------------------------------
2244
+ case "add_command": {
2245
+ if (commandExists(ir, mutation.command.name)) {
2246
+ throw new Error(`Command '${mutation.command.name}' already exists`);
2247
+ }
2248
+ return {
2249
+ ...ir,
2250
+ commands: [...ir.commands, { ...mutation.command }]
2251
+ };
2252
+ }
2253
+ case "update_command": {
2254
+ if (!commandExists(ir, mutation.name)) {
2255
+ throw new Error(`Command '${mutation.name}' not found`);
2256
+ }
2257
+ return {
2258
+ ...ir,
2259
+ commands: ir.commands.map(
2260
+ (c) => c.name === mutation.name ? { ...c, content: mutation.content } : c
2261
+ )
2262
+ };
2263
+ }
2264
+ case "remove_command": {
2265
+ if (!commandExists(ir, mutation.name)) {
2266
+ throw new Error(`Command '${mutation.name}' not found`);
2267
+ }
2268
+ return {
2269
+ ...ir,
2270
+ commands: ir.commands.filter((c) => c.name !== mutation.name)
2271
+ };
2272
+ }
2273
+ // -- Rules -------------------------------------------------------------
2274
+ case "add_rule": {
2275
+ if (ruleExists(ir, mutation.rule.name)) {
2276
+ throw new Error(`Rule '${mutation.rule.name}' already exists`);
2277
+ }
2278
+ return {
2279
+ ...ir,
2280
+ rules: [...ir.rules, { ...mutation.rule }]
2281
+ };
2282
+ }
2283
+ case "update_rule": {
2284
+ if (!ruleExists(ir, mutation.name)) {
2285
+ throw new Error(`Rule '${mutation.name}' not found`);
2286
+ }
2287
+ return {
2288
+ ...ir,
2289
+ rules: ir.rules.map(
2290
+ (r) => r.name === mutation.name ? { ...r, content: mutation.content } : r
2291
+ )
2292
+ };
2293
+ }
2294
+ case "remove_rule": {
2295
+ if (!ruleExists(ir, mutation.name)) {
2296
+ throw new Error(`Rule '${mutation.name}' not found`);
2297
+ }
2298
+ return {
2299
+ ...ir,
2300
+ rules: ir.rules.filter((r) => r.name !== mutation.name)
2301
+ };
2302
+ }
2303
+ // -- Agents ------------------------------------------------------------
2304
+ case "add_agent": {
2305
+ if (agentExists(ir, mutation.agent.name)) {
2306
+ throw new Error(`Agent '${mutation.agent.name}' already exists`);
2307
+ }
2308
+ return {
2309
+ ...ir,
2310
+ agents: [...ir.agents, { ...mutation.agent }]
2311
+ };
2312
+ }
2313
+ case "update_agent": {
2314
+ if (!agentExists(ir, mutation.name)) {
2315
+ throw new Error(`Agent '${mutation.name}' not found`);
2316
+ }
2317
+ return {
2318
+ ...ir,
2319
+ agents: ir.agents.map(
2320
+ (a) => a.name === mutation.name ? { ...a, ...mutation.changes } : a
2321
+ )
2322
+ };
2323
+ }
2324
+ case "remove_agent": {
2325
+ if (!agentExists(ir, mutation.name)) {
2326
+ throw new Error(`Agent '${mutation.name}' not found`);
2327
+ }
2328
+ return {
2329
+ ...ir,
2330
+ agents: ir.agents.filter((a) => a.name !== mutation.name)
2331
+ };
2332
+ }
2333
+ // -- MCP Servers -------------------------------------------------------
2334
+ case "add_mcp_server": {
2335
+ if (mcpServerExists(ir, mutation.server.id)) {
2336
+ throw new Error(`MCP server '${mutation.server.id}' already exists`);
2337
+ }
2338
+ return {
2339
+ ...ir,
2340
+ mcpServers: [...ir.mcpServers, { ...mutation.server }]
2341
+ };
2342
+ }
2343
+ case "remove_mcp_server": {
2344
+ if (!mcpServerExists(ir, mutation.id)) {
2345
+ throw new Error(`MCP server '${mutation.id}' not found`);
2346
+ }
2347
+ return {
2348
+ ...ir,
2349
+ mcpServers: ir.mcpServers.filter((s) => s.id !== mutation.id)
2350
+ };
2351
+ }
2352
+ // -- Settings ----------------------------------------------------------
2353
+ case "update_settings": {
2354
+ return {
2355
+ ...ir,
2356
+ settings: applySettingsUpdate(ir.settings, mutation.path, mutation.value)
2357
+ };
2358
+ }
2359
+ // -- Raw text (legacy fallback) ----------------------------------------
2360
+ case "raw_text": {
2361
+ console.warn(
2362
+ "raw_text mutation is a legacy fallback \u2014 the text operation will be applied during rendering"
2363
+ );
2364
+ return { ...ir };
2365
+ }
2366
+ }
2367
+ }
2368
+ var STRUCTURED_SETTINGS_KEYS;
2369
+ var init_mutations = __esm({
2370
+ "src/ir/mutations.ts"() {
2371
+ "use strict";
2372
+ STRUCTURED_SETTINGS_KEYS = /* @__PURE__ */ new Set(["statusLine", "hooks", "denyPatterns"]);
2373
+ }
2374
+ });
2375
+
2376
+ // src/ir/renderer.ts
2377
+ function renderClaudeMd(_meta, sections) {
2378
+ const sorted = [...sections].sort((a, b) => a.order - b.order);
2379
+ const blocks = [];
2380
+ for (const section of sorted) {
2381
+ if (section.heading && section.content) {
2382
+ blocks.push(`${section.heading}
2383
+
2384
+ ${section.content}`);
2385
+ } else if (section.heading) {
2386
+ blocks.push(section.heading);
2387
+ } else if (section.content) {
2388
+ blocks.push(section.content);
2389
+ }
2390
+ }
2391
+ if (blocks.length === 0) {
2392
+ return "\n";
2393
+ }
2394
+ return blocks.join("\n\n") + "\n";
2395
+ }
2396
+ function renderSettings(settings) {
2397
+ const result = JSON.parse(
2398
+ JSON.stringify(settings.raw)
2399
+ );
2400
+ if (settings.denyPatterns && settings.denyPatterns.length > 0) {
2401
+ const permissions = result["permissions"] ?? {};
2402
+ permissions["deny"] = settings.denyPatterns;
2403
+ result["permissions"] = permissions;
2404
+ }
2405
+ if (settings.statusLine) {
2406
+ result["statusLine"] = settings.statusLine;
2407
+ }
2408
+ const hookEvents = [
2409
+ "PreToolUse",
2410
+ "PostToolUse",
2411
+ "UserPromptSubmit",
2412
+ "SessionStart",
2413
+ "PostCompact"
2414
+ ];
2415
+ const hooksObj = {};
2416
+ let hasHooks = false;
2417
+ for (const event of hookEvents) {
2418
+ const entries = settings.hooks[event];
2419
+ if (entries && entries.length > 0) {
2420
+ hooksObj[event] = entries;
2421
+ hasHooks = true;
2422
+ }
2423
+ }
2424
+ if (hasHooks) {
2425
+ result["hooks"] = hooksObj;
2426
+ }
2427
+ return JSON.stringify(result, null, 2) + "\n";
2428
+ }
2429
+ function renderMcpConfig(servers) {
2430
+ if (servers.length === 0) {
2431
+ return "";
2432
+ }
2433
+ const mcpServers = {};
2434
+ for (const server of servers) {
2435
+ const entry = {
2436
+ command: server.command,
2437
+ args: server.args
2438
+ };
2439
+ if (server.env && Object.keys(server.env).length > 0) {
2440
+ entry["env"] = server.env;
2441
+ }
2442
+ mcpServers[server.id] = entry;
2443
+ }
2444
+ return JSON.stringify({ mcpServers }, null, 2) + "\n";
2445
+ }
2446
+ function renderRuleWithFrontmatter(rule) {
2447
+ if (!rule.paths || rule.paths.length === 0) {
2448
+ return rule.content;
2449
+ }
2450
+ const yamlLines = ["---", "paths:"];
2451
+ for (const p of rule.paths) {
2452
+ yamlLines.push(` - ${p}`);
2453
+ }
2454
+ yamlLines.push("---");
2455
+ return yamlLines.join("\n") + "\n\n" + rule.content;
2456
+ }
2457
+ function renderAgentWithFrontmatter(agent) {
2458
+ const hasModel = agent.model !== void 0;
2459
+ const hasDisallowed = agent.disallowedTools !== void 0 && agent.disallowedTools.length > 0;
2460
+ const hasExtra = agent.extraFrontmatter !== void 0 && Object.keys(agent.extraFrontmatter).length > 0;
2461
+ if (!hasModel && !hasDisallowed && !hasExtra) {
2462
+ return agent.content;
2463
+ }
2464
+ const yamlLines = ["---"];
2465
+ if (hasModel) {
2466
+ yamlLines.push(`model: ${agent.model}`);
2467
+ }
2468
+ if (hasDisallowed) {
2469
+ yamlLines.push("disallowedTools:");
2470
+ for (const tool of agent.disallowedTools) {
2471
+ yamlLines.push(` - ${tool}`);
2472
+ }
2473
+ }
2474
+ if (hasExtra) {
2475
+ for (const [key, value] of Object.entries(agent.extraFrontmatter)) {
2476
+ if (Array.isArray(value)) {
2477
+ yamlLines.push(`${key}:`);
2478
+ for (const item of value) {
2479
+ yamlLines.push(` - ${String(item)}`);
2480
+ }
2481
+ } else if (typeof value === "object" && value !== null) {
2482
+ yamlLines.push(`${key}:`);
2483
+ for (const [subKey, subVal] of Object.entries(value)) {
2484
+ yamlLines.push(` ${subKey}: ${String(subVal)}`);
2485
+ }
2486
+ } else {
2487
+ yamlLines.push(`${key}: ${String(value)}`);
2488
+ }
2489
+ }
2490
+ }
2491
+ yamlLines.push("---");
2492
+ return yamlLines.join("\n") + "\n\n" + agent.content;
2493
+ }
2494
+ function settingsHasContent(settings) {
2495
+ if (settings.statusLine) return true;
2496
+ if (settings.denyPatterns && settings.denyPatterns.length > 0) return true;
2497
+ if (Object.keys(settings.raw).length > 0) return true;
2498
+ const hookEvents = [
2499
+ "PreToolUse",
2500
+ "PostToolUse",
2501
+ "UserPromptSubmit",
2502
+ "SessionStart",
2503
+ "PostCompact"
2504
+ ];
2505
+ for (const event of hookEvents) {
2506
+ const entries = settings.hooks[event];
2507
+ if (entries && entries.length > 0) return true;
2508
+ }
2509
+ return false;
2510
+ }
2511
+ function renderHarness(ir) {
2512
+ const files = /* @__PURE__ */ new Map();
2513
+ if (ir.sections.length > 0 || ir.meta.name) {
2514
+ files.set("CLAUDE.md", renderClaudeMd(ir.meta, ir.sections));
2515
+ }
2516
+ if (settingsHasContent(ir.settings)) {
2517
+ files.set("settings.json", renderSettings(ir.settings));
2518
+ }
2519
+ for (const cmd of ir.commands) {
2520
+ files.set(`commands/${cmd.name}.md`, cmd.content);
2521
+ }
2522
+ for (const rule of ir.rules) {
2523
+ files.set(`rules/${rule.name}.md`, renderRuleWithFrontmatter(rule));
2524
+ }
2525
+ for (const agent of ir.agents) {
2526
+ files.set(`agents/${agent.name}.md`, renderAgentWithFrontmatter(agent));
2527
+ }
2528
+ for (const skill of ir.skills) {
2529
+ files.set(`skills/${skill.name}.md`, skill.content);
2530
+ }
2531
+ for (const doc of ir.docs) {
2532
+ files.set(`docs/${doc.name}.md`, doc.content);
2533
+ }
2534
+ for (const hook of ir.hooks) {
2535
+ files.set(`hooks/${hook.name}.mjs`, hook.content);
2536
+ }
2537
+ const mcpContent = renderMcpConfig(ir.mcpServers);
2538
+ if (mcpContent) {
2539
+ files.set(".mcp.json", mcpContent);
2540
+ }
2541
+ return files;
2542
+ }
2543
+ var init_renderer = __esm({
2544
+ "src/ir/renderer.ts"() {
2545
+ "use strict";
2546
+ }
2547
+ });
2548
+
2549
+ // src/ir/diff.ts
2550
+ function diffIR(before, after) {
2551
+ const diff = createEmptyDiff();
2552
+ diffSections(before.sections, after.sections, diff);
2553
+ diffByName(before.commands, after.commands, diff.commands);
2554
+ diffByName(before.rules, after.rules, diff.rules);
2555
+ diffAgents(before.agents, after.agents, diff);
2556
+ diffMcpServers(before.mcpServers, after.mcpServers, diff);
2557
+ diffSettings(before.settings, after.settings, diff);
2558
+ return diff;
2559
+ }
2560
+ function formatIRDiff(diff) {
2561
+ const blocks = [];
2562
+ const sectionLines = formatSectionBlock(diff);
2563
+ if (sectionLines.length > 0) {
2564
+ blocks.push(["Sections:", ...sectionLines].join("\n"));
2565
+ }
2566
+ const commandLines = formatNamedBlock(diff.commands, "commands");
2567
+ if (commandLines.length > 0) {
2568
+ blocks.push(["Commands:", ...commandLines].join("\n"));
2569
+ }
2570
+ const ruleLines = formatNamedBlock(diff.rules, "rules");
2571
+ if (ruleLines.length > 0) {
2572
+ blocks.push(["Rules:", ...ruleLines].join("\n"));
2573
+ }
2574
+ const agentLines = formatAgentBlock(diff);
2575
+ if (agentLines.length > 0) {
2576
+ blocks.push(["Agents:", ...agentLines].join("\n"));
2577
+ }
2578
+ const mcpLines = formatMcpBlock(diff);
2579
+ if (mcpLines.length > 0) {
2580
+ blocks.push(["MCP Servers:", ...mcpLines].join("\n"));
2581
+ }
2582
+ const settingsLines = formatSettingsBlock(diff);
2583
+ if (settingsLines.length > 0) {
2584
+ blocks.push(["Settings:", ...settingsLines].join("\n"));
2585
+ }
2586
+ if (blocks.length === 0) {
2587
+ return "No changes.";
2588
+ }
2589
+ return blocks.join("\n\n");
2590
+ }
2591
+ function diffSections(beforeList, afterList, diff) {
2592
+ const beforeMap = /* @__PURE__ */ new Map();
2593
+ for (const s of beforeList) {
2594
+ beforeMap.set(s.id, s);
2595
+ }
2596
+ const afterMap = /* @__PURE__ */ new Map();
2597
+ for (const s of afterList) {
2598
+ afterMap.set(s.id, s);
2599
+ }
2600
+ for (const s of afterList) {
2601
+ if (!beforeMap.has(s.id)) {
2602
+ diff.sections.added.push(s);
2603
+ }
2604
+ }
2605
+ for (const s of beforeList) {
2606
+ if (!afterMap.has(s.id)) {
2607
+ diff.sections.removed.push(s);
2608
+ }
2609
+ }
2610
+ for (const [id, afterSection] of afterMap) {
2611
+ const beforeSection = beforeMap.get(id);
2612
+ if (beforeSection === void 0) continue;
2613
+ if (beforeSection.content !== afterSection.content) {
2614
+ diff.sections.modified.push({
2615
+ id,
2616
+ before: beforeSection.content,
2617
+ after: afterSection.content
2618
+ });
2619
+ }
2620
+ if (beforeSection.order !== afterSection.order) {
2621
+ diff.sections.reordered.push({
2622
+ id,
2623
+ oldOrder: beforeSection.order,
2624
+ newOrder: afterSection.order
2625
+ });
2626
+ }
2627
+ }
2628
+ }
2629
+ function diffByName(beforeList, afterList, target) {
2630
+ const beforeMap = /* @__PURE__ */ new Map();
2631
+ for (const n of beforeList) {
2632
+ beforeMap.set(n.name, n);
2633
+ }
2634
+ const afterMap = /* @__PURE__ */ new Map();
2635
+ for (const n of afterList) {
2636
+ afterMap.set(n.name, n);
2637
+ }
2638
+ for (const n of afterList) {
2639
+ if (!beforeMap.has(n.name)) {
2640
+ target.added.push(n);
2641
+ }
2642
+ }
2643
+ for (const n of beforeList) {
2644
+ if (!afterMap.has(n.name)) {
2645
+ target.removed.push(n.name);
2646
+ }
2647
+ }
2648
+ for (const [name, afterNode] of afterMap) {
2649
+ const beforeNode = beforeMap.get(name);
2650
+ if (beforeNode === void 0) continue;
2651
+ if (beforeNode.content !== afterNode.content) {
2652
+ target.modified.push({
2653
+ name,
2654
+ before: beforeNode.content,
2655
+ after: afterNode.content
2656
+ });
2657
+ }
2658
+ }
2659
+ }
2660
+ function diffAgents(beforeList, afterList, diff) {
2661
+ const beforeMap = /* @__PURE__ */ new Map();
2662
+ for (const a of beforeList) {
2663
+ beforeMap.set(a.name, a);
2664
+ }
2665
+ const afterMap = /* @__PURE__ */ new Map();
2666
+ for (const a of afterList) {
2667
+ afterMap.set(a.name, a);
2668
+ }
2669
+ for (const a of afterList) {
2670
+ if (!beforeMap.has(a.name)) {
2671
+ diff.agents.added.push(a);
2672
+ }
2673
+ }
2674
+ for (const a of beforeList) {
2675
+ if (!afterMap.has(a.name)) {
2676
+ diff.agents.removed.push(a.name);
2677
+ }
2678
+ }
2679
+ for (const [name, afterAgent] of afterMap) {
2680
+ const beforeAgent = beforeMap.get(name);
2681
+ if (beforeAgent === void 0) continue;
2682
+ const changeParts = [];
2683
+ if (beforeAgent.model !== afterAgent.model) {
2684
+ const from = beforeAgent.model ?? "none";
2685
+ const to = afterAgent.model ?? "none";
2686
+ changeParts.push(`model changed from ${from} to ${to}`);
2687
+ }
2688
+ if (beforeAgent.content !== afterAgent.content) {
2689
+ changeParts.push("content updated");
2690
+ }
2691
+ const beforeTools = JSON.stringify(beforeAgent.disallowedTools ?? []);
2692
+ const afterTools = JSON.stringify(afterAgent.disallowedTools ?? []);
2693
+ if (beforeTools !== afterTools) {
2694
+ changeParts.push("disallowedTools changed");
2695
+ }
2696
+ if (changeParts.length > 0) {
2697
+ diff.agents.modified.push({
2698
+ name,
2699
+ changes: changeParts.join("; ")
2700
+ });
2701
+ }
2702
+ }
2703
+ }
2704
+ function diffMcpServers(beforeList, afterList, diff) {
2705
+ const beforeIds = new Set(beforeList.map((s) => s.id));
2706
+ const afterIds = new Set(afterList.map((s) => s.id));
2707
+ for (const s of afterList) {
2708
+ if (!beforeIds.has(s.id)) {
2709
+ diff.mcpServers.added.push(s);
2710
+ }
2711
+ }
2712
+ for (const s of beforeList) {
2713
+ if (!afterIds.has(s.id)) {
2714
+ diff.mcpServers.removed.push(s.id);
2715
+ }
2716
+ }
2717
+ }
2718
+ function diffSettings(before, after, diff) {
2719
+ if (!deepEqual(before.statusLine, after.statusLine)) {
2720
+ diff.settings.changes.push({
2721
+ path: "statusLine",
2722
+ before: before.statusLine,
2723
+ after: after.statusLine
2724
+ });
2725
+ }
2726
+ if (!deepEqual(before.denyPatterns, after.denyPatterns)) {
2727
+ diff.settings.changes.push({
2728
+ path: "denyPatterns",
2729
+ before: before.denyPatterns,
2730
+ after: after.denyPatterns
2731
+ });
2732
+ }
2733
+ if (!deepEqual(before.hooks, after.hooks)) {
2734
+ diff.settings.changes.push({
2735
+ path: "hooks",
2736
+ before: before.hooks,
2737
+ after: after.hooks
2738
+ });
2739
+ }
2740
+ }
2741
+ function formatSectionBlock(diff) {
2742
+ const lines = [];
2743
+ for (const s of diff.sections.added) {
2744
+ lines.push(` + Added: ${s.heading}`);
2745
+ }
2746
+ for (const s of diff.sections.removed) {
2747
+ lines.push(` - Removed: ${s.heading}`);
2748
+ }
2749
+ for (const m of diff.sections.modified) {
2750
+ lines.push(` ~ Modified: ${m.id} (content changed)`);
2751
+ }
2752
+ for (const r of diff.sections.reordered) {
2753
+ lines.push(
2754
+ ` \u2195 Reordered: ${r.id} (${r.oldOrder} \u2192 ${r.newOrder})`
2755
+ );
2756
+ }
2757
+ return lines;
2758
+ }
2759
+ function formatNamedBlock(bucket, _category) {
2760
+ const lines = [];
2761
+ for (const n of bucket.added) {
2762
+ lines.push(` + Added: ${n.name}`);
2763
+ }
2764
+ for (const name of bucket.removed) {
2765
+ lines.push(` - Removed: ${name}`);
2766
+ }
2767
+ for (const m of bucket.modified) {
2768
+ lines.push(` ~ Modified: ${m.name} (content changed)`);
2769
+ }
2770
+ return lines;
2771
+ }
2772
+ function formatAgentBlock(diff) {
2773
+ const lines = [];
2774
+ for (const a of diff.agents.added) {
2775
+ lines.push(` + Added: ${a.name}`);
2776
+ }
2777
+ for (const name of diff.agents.removed) {
2778
+ lines.push(` - Removed: ${name}`);
2779
+ }
2780
+ for (const m of diff.agents.modified) {
2781
+ lines.push(` ~ Modified: ${m.name} (${m.changes})`);
2782
+ }
2783
+ return lines;
2784
+ }
2785
+ function formatMcpBlock(diff) {
2786
+ const lines = [];
2787
+ for (const s of diff.mcpServers.added) {
2788
+ lines.push(` + Added: ${s.id}`);
2789
+ }
2790
+ for (const id of diff.mcpServers.removed) {
2791
+ lines.push(` - Removed: ${id}`);
2792
+ }
2793
+ return lines;
2794
+ }
2795
+ function formatSettingsBlock(diff) {
2796
+ const lines = [];
2797
+ for (const c of diff.settings.changes) {
2798
+ lines.push(` ~ ${c.path} changed`);
2799
+ }
2800
+ return lines;
2801
+ }
2802
+ function deepEqual(a, b) {
2803
+ return JSON.stringify(a) === JSON.stringify(b);
2804
+ }
2805
+ var init_diff = __esm({
2806
+ "src/ir/diff.ts"() {
2807
+ "use strict";
2808
+ init_types();
2809
+ }
2810
+ });
2811
+
1412
2812
  // src/evolve/mutator.ts
1413
- import fs21 from "fs/promises";
1414
- import path21 from "path";
2813
+ import fs22 from "fs/promises";
2814
+ import path22 from "path";
1415
2815
  async function applyMutations(currentHarnessPath, nextIterationDir, mutations) {
1416
- const newHarnessPath = path21.join(nextIterationDir, "harness");
2816
+ const newHarnessPath = path22.join(nextIterationDir, "harness");
2817
+ let baselineIR = null;
2818
+ try {
2819
+ baselineIR = await parseHarness(currentHarnessPath);
2820
+ } catch {
2821
+ }
2822
+ if (baselineIR !== null) {
2823
+ return applyMutationsViaIR(
2824
+ currentHarnessPath,
2825
+ newHarnessPath,
2826
+ mutations,
2827
+ baselineIR
2828
+ );
2829
+ }
2830
+ return applyMutationsLegacy(currentHarnessPath, newHarnessPath, mutations);
2831
+ }
2832
+ async function applyMutationsViaIR(currentHarnessPath, newHarnessPath, mutations, baselineIR) {
1417
2833
  await copyDir(currentHarnessPath, newHarnessPath);
1418
- for (const mutation of mutations) {
1419
- if (mutation.file.includes("..")) {
2834
+ const irMutations = translateMutations(mutations, baselineIR);
2835
+ let currentIR = baselineIR;
2836
+ const rawTextMutations = [];
2837
+ const touchedCategories = /* @__PURE__ */ new Set();
2838
+ for (let i = 0; i < irMutations.length; i++) {
2839
+ const irMut = irMutations[i];
2840
+ if (irMut.type === "raw_text") {
2841
+ rawTextMutations.push(mutations[i]);
1420
2842
  continue;
1421
2843
  }
1422
- const filePath = path21.join(newHarnessPath, mutation.file);
1423
- if (mutation.action === "replace") {
1424
- if (!mutation.oldText) {
1425
- continue;
1426
- }
1427
- const content = await fs21.readFile(filePath, "utf-8");
1428
- if (!content.includes(mutation.oldText)) {
1429
- continue;
1430
- }
1431
- await fs21.writeFile(
2844
+ try {
2845
+ currentIR = applyIRMutation(currentIR, irMut);
2846
+ touchedCategories.add(getMutationCategory(irMut.type));
2847
+ } catch {
2848
+ continue;
2849
+ }
2850
+ }
2851
+ if (touchedCategories.size > 0) {
2852
+ await renderAffectedFiles(currentIR, newHarnessPath, touchedCategories);
2853
+ }
2854
+ for (const mutation of rawTextMutations) {
2855
+ await applyLegacyMutation(newHarnessPath, mutation);
2856
+ }
2857
+ const irDiff = diffIR(baselineIR, currentIR);
2858
+ let diffPatch = formatIRDiff(irDiff);
2859
+ if (diffPatch === "No changes." && rawTextMutations.length > 0) {
2860
+ diffPatch = await generateDiffLegacy(currentHarnessPath, newHarnessPath);
2861
+ }
2862
+ if (diffPatch === "No changes.") {
2863
+ diffPatch = "";
2864
+ }
2865
+ return { newHarnessPath, diffPatch };
2866
+ }
2867
+ function getMutationCategory(mutationType) {
2868
+ if (mutationType.includes("section") || mutationType.includes("reorder")) {
2869
+ return "claude_md";
2870
+ }
2871
+ if (mutationType.includes("command")) {
2872
+ return "commands";
2873
+ }
2874
+ if (mutationType.includes("rule")) {
2875
+ return "rules";
2876
+ }
2877
+ if (mutationType.includes("agent")) {
2878
+ return "agents";
2879
+ }
2880
+ if (mutationType.includes("mcp")) {
2881
+ return "mcp";
2882
+ }
2883
+ if (mutationType.includes("settings")) {
2884
+ return "settings";
2885
+ }
2886
+ return "unknown";
2887
+ }
2888
+ async function renderAffectedFiles(ir, targetDir, touchedCategories) {
2889
+ const fileMap = renderHarness(ir);
2890
+ for (const [relativePath, content] of fileMap) {
2891
+ const category = getFileCategory(relativePath);
2892
+ if (touchedCategories.has(category)) {
2893
+ const fullPath = path22.join(targetDir, relativePath);
2894
+ await fs22.mkdir(path22.dirname(fullPath), { recursive: true });
2895
+ await fs22.writeFile(fullPath, content, "utf-8");
2896
+ }
2897
+ }
2898
+ if (touchedCategories.has("commands")) {
2899
+ await deleteOrphanedFiles(targetDir, "commands", fileMap);
2900
+ }
2901
+ if (touchedCategories.has("rules")) {
2902
+ await deleteOrphanedFiles(targetDir, "rules", fileMap);
2903
+ }
2904
+ if (touchedCategories.has("agents")) {
2905
+ await deleteOrphanedFiles(targetDir, "agents", fileMap);
2906
+ }
2907
+ }
2908
+ function getFileCategory(relativePath) {
2909
+ if (relativePath === "CLAUDE.md") return "claude_md";
2910
+ if (relativePath.startsWith("commands/")) return "commands";
2911
+ if (relativePath.startsWith("rules/")) return "rules";
2912
+ if (relativePath.startsWith("agents/")) return "agents";
2913
+ if (relativePath.startsWith("skills/")) return "skills";
2914
+ if (relativePath.startsWith("docs/")) return "docs";
2915
+ if (relativePath.startsWith("hooks/")) return "hooks";
2916
+ if (relativePath === "settings.json") return "settings";
2917
+ if (relativePath === ".mcp.json") return "mcp";
2918
+ return "unknown";
2919
+ }
2920
+ async function deleteOrphanedFiles(targetDir, subdir, renderedMap) {
2921
+ const subdirPath = path22.join(targetDir, subdir);
2922
+ let entries;
2923
+ try {
2924
+ entries = await fs22.readdir(subdirPath);
2925
+ } catch {
2926
+ return;
2927
+ }
2928
+ for (const entry of entries) {
2929
+ const relativePath = `${subdir}/${entry}`;
2930
+ if (!renderedMap.has(relativePath)) {
2931
+ await fs22.unlink(path22.join(subdirPath, entry)).catch(() => {
2932
+ });
2933
+ }
2934
+ }
2935
+ }
2936
+ async function applyMutationsLegacy(currentHarnessPath, newHarnessPath, mutations) {
2937
+ await copyDir(currentHarnessPath, newHarnessPath);
2938
+ for (const mutation of mutations) {
2939
+ await applyLegacyMutation(newHarnessPath, mutation);
2940
+ }
2941
+ const diffPatch = await generateDiffLegacy(currentHarnessPath, newHarnessPath);
2942
+ return { newHarnessPath, diffPatch };
2943
+ }
2944
+ async function applyLegacyMutation(harnessPath, mutation) {
2945
+ if (mutation.file.includes("..")) {
2946
+ return;
2947
+ }
2948
+ const filePath = path22.join(harnessPath, mutation.file);
2949
+ if (mutation.action === "replace") {
2950
+ if (!mutation.oldText) {
2951
+ return;
2952
+ }
2953
+ let content;
2954
+ try {
2955
+ content = await fs22.readFile(filePath, "utf-8");
2956
+ } catch {
2957
+ return;
2958
+ }
2959
+ if (!content.includes(mutation.oldText)) {
2960
+ return;
2961
+ }
2962
+ await fs22.writeFile(
2963
+ filePath,
2964
+ content.replace(mutation.oldText, mutation.newText),
2965
+ "utf-8"
2966
+ );
2967
+ } else if (mutation.action === "add_section") {
2968
+ try {
2969
+ const content = await fs22.readFile(filePath, "utf-8");
2970
+ await fs22.writeFile(
1432
2971
  filePath,
1433
- content.replace(mutation.oldText, mutation.newText),
2972
+ content + "\n\n" + mutation.newText,
1434
2973
  "utf-8"
1435
2974
  );
1436
- } else if (mutation.action === "add_section") {
1437
- try {
1438
- const content = await fs21.readFile(filePath, "utf-8");
1439
- await fs21.writeFile(
1440
- filePath,
1441
- content + "\n\n" + mutation.newText,
1442
- "utf-8"
1443
- );
1444
- } catch {
1445
- await fs21.mkdir(path21.dirname(filePath), { recursive: true });
1446
- await fs21.writeFile(filePath, mutation.newText, "utf-8");
1447
- }
1448
- } else if (mutation.action === "create_file") {
1449
- await fs21.mkdir(path21.dirname(filePath), { recursive: true });
1450
- await fs21.writeFile(filePath, mutation.newText, "utf-8");
1451
- } else if (mutation.action === "delete_section") {
1452
- if (!mutation.oldText) {
1453
- continue;
1454
- }
1455
- let sectionContent;
1456
- try {
1457
- sectionContent = await fs21.readFile(filePath, "utf-8");
1458
- } catch {
1459
- continue;
2975
+ } catch {
2976
+ await fs22.mkdir(path22.dirname(filePath), { recursive: true });
2977
+ await fs22.writeFile(filePath, mutation.newText, "utf-8");
2978
+ }
2979
+ } else if (mutation.action === "create_file") {
2980
+ await fs22.mkdir(path22.dirname(filePath), { recursive: true });
2981
+ await fs22.writeFile(filePath, mutation.newText, "utf-8");
2982
+ } else if (mutation.action === "delete_section") {
2983
+ if (!mutation.oldText) {
2984
+ return;
2985
+ }
2986
+ let sectionContent;
2987
+ try {
2988
+ sectionContent = await fs22.readFile(filePath, "utf-8");
2989
+ } catch {
2990
+ return;
2991
+ }
2992
+ if (!sectionContent.includes(mutation.oldText)) {
2993
+ return;
2994
+ }
2995
+ await fs22.writeFile(filePath, sectionContent.replace(mutation.oldText, ""), "utf-8");
2996
+ } else if (mutation.action === "delete_file") {
2997
+ await fs22.unlink(filePath).catch(() => {
2998
+ });
2999
+ }
3000
+ }
3001
+ async function generateDiff2(oldDir, newDir) {
3002
+ try {
3003
+ const oldIR = await parseHarness(oldDir);
3004
+ const newIR = await parseHarness(newDir);
3005
+ const oldHasContent = oldIR.sections.length > 0 || oldIR.commands.length > 0 || oldIR.rules.length > 0 || oldIR.agents.length > 0;
3006
+ const newHasContent = newIR.sections.length > 0 || newIR.commands.length > 0 || newIR.rules.length > 0 || newIR.agents.length > 0;
3007
+ if (oldHasContent || newHasContent) {
3008
+ const irDiff = diffIR(oldIR, newIR);
3009
+ const formatted = formatIRDiff(irDiff);
3010
+ if (formatted === "No changes.") {
3011
+ const legacyDiff2 = await generateDiffLegacy(oldDir, newDir);
3012
+ return legacyDiff2;
1460
3013
  }
1461
- if (!sectionContent.includes(mutation.oldText)) {
1462
- continue;
3014
+ const legacyDiff = await generateDiffLegacy(oldDir, newDir);
3015
+ if (legacyDiff && !formatted.includes(legacyDiff)) {
3016
+ return formatted + "\n\n" + legacyDiff;
1463
3017
  }
1464
- await fs21.writeFile(filePath, sectionContent.replace(mutation.oldText, ""), "utf-8");
1465
- } else if (mutation.action === "delete_file") {
1466
- await fs21.unlink(filePath).catch(() => {
1467
- });
3018
+ return formatted;
1468
3019
  }
3020
+ } catch {
1469
3021
  }
1470
- const diffPatch = await generateDiff2(currentHarnessPath, newHarnessPath);
1471
- return { newHarnessPath, diffPatch };
3022
+ return generateDiffLegacy(oldDir, newDir);
1472
3023
  }
1473
- async function generateDiff2(oldDir, newDir) {
3024
+ async function generateDiffLegacy(oldDir, newDir) {
1474
3025
  const oldFiles = await readAllFiles(oldDir);
1475
3026
  const newFiles = await readAllFiles(newDir);
1476
3027
  const allPaths = /* @__PURE__ */ new Set([
@@ -1511,17 +3062,17 @@ async function readAllFiles(dir) {
1511
3062
  async function walk(current) {
1512
3063
  let entries;
1513
3064
  try {
1514
- entries = await fs21.readdir(current, { withFileTypes: true });
3065
+ entries = await fs22.readdir(current, { withFileTypes: true });
1515
3066
  } catch {
1516
3067
  return;
1517
3068
  }
1518
3069
  for (const entry of entries) {
1519
- const fullPath = path21.join(current, entry.name);
1520
- const relativePath = path21.relative(dir, fullPath);
3070
+ const fullPath = path22.join(current, entry.name);
3071
+ const relativePath = path22.relative(dir, fullPath);
1521
3072
  if (entry.isDirectory()) {
1522
3073
  await walk(fullPath);
1523
3074
  } else {
1524
- result[relativePath] = await fs21.readFile(fullPath, "utf-8");
3075
+ result[relativePath] = await fs22.readFile(fullPath, "utf-8");
1525
3076
  }
1526
3077
  }
1527
3078
  }
@@ -1532,12 +3083,17 @@ var init_mutator = __esm({
1532
3083
  "src/evolve/mutator.ts"() {
1533
3084
  "use strict";
1534
3085
  init_baseline();
3086
+ init_parser();
3087
+ init_translate();
3088
+ init_mutations();
3089
+ init_renderer();
3090
+ init_diff();
1535
3091
  }
1536
3092
  });
1537
3093
 
1538
3094
  // src/evolve/sampling.ts
1539
- import fs22 from "fs/promises";
1540
- import path22 from "path";
3095
+ import fs23 from "fs/promises";
3096
+ import path23 from "path";
1541
3097
  function initBeliefs(tasks) {
1542
3098
  return tasks.map((task) => ({
1543
3099
  taskId: task.id,
@@ -1598,9 +3154,9 @@ function updateBeliefs(beliefs, results) {
1598
3154
  });
1599
3155
  }
1600
3156
  async function loadBeliefs(workspacePath) {
1601
- const beliefsPath = path22.join(workspacePath, "task-beliefs.json");
3157
+ const beliefsPath = path23.join(workspacePath, "task-beliefs.json");
1602
3158
  try {
1603
- const content = await fs22.readFile(beliefsPath, "utf-8");
3159
+ const content = await fs23.readFile(beliefsPath, "utf-8");
1604
3160
  const parsed = JSON.parse(content);
1605
3161
  if (!Array.isArray(parsed)) return null;
1606
3162
  for (const entry of parsed) {
@@ -1614,9 +3170,9 @@ async function loadBeliefs(workspacePath) {
1614
3170
  }
1615
3171
  }
1616
3172
  async function saveBeliefs(workspacePath, beliefs) {
1617
- const beliefsPath = path22.join(workspacePath, "task-beliefs.json");
1618
- await fs22.mkdir(path22.dirname(beliefsPath), { recursive: true });
1619
- await fs22.writeFile(beliefsPath, JSON.stringify(beliefs, null, 2), "utf-8");
3173
+ const beliefsPath = path23.join(workspacePath, "task-beliefs.json");
3174
+ await fs23.mkdir(path23.dirname(beliefsPath), { recursive: true });
3175
+ await fs23.writeFile(beliefsPath, JSON.stringify(beliefs, null, 2), "utf-8");
1620
3176
  }
1621
3177
  var init_sampling = __esm({
1622
3178
  "src/evolve/sampling.ts"() {
@@ -1625,8 +3181,8 @@ var init_sampling = __esm({
1625
3181
  });
1626
3182
 
1627
3183
  // src/evolve/regularization.ts
1628
- import fs23 from "fs/promises";
1629
- import path23 from "path";
3184
+ import fs24 from "fs/promises";
3185
+ import path24 from "path";
1630
3186
  async function measureComplexity(harnessPath) {
1631
3187
  let totalLines = 0;
1632
3188
  let totalFiles = 0;
@@ -1657,6 +3213,64 @@ async function measureComplexity(harnessPath) {
1657
3213
  diffFromBaseline: 0
1658
3214
  };
1659
3215
  }
3216
+ function measureComplexityFromIR(ir) {
3217
+ const totalSections = ir.sections.length;
3218
+ const totalRules = ir.rules.length;
3219
+ const totalCommands = ir.commands.length;
3220
+ let totalFiles = 0;
3221
+ if (ir.sections.length > 0 || ir.meta.name) {
3222
+ totalFiles += 1;
3223
+ }
3224
+ totalFiles += ir.commands.length;
3225
+ totalFiles += ir.rules.length;
3226
+ totalFiles += ir.agents.length;
3227
+ totalFiles += ir.skills.length;
3228
+ totalFiles += ir.docs.length;
3229
+ totalFiles += ir.hooks.length;
3230
+ const hasSettings = ir.settings.statusLine !== void 0 || ir.settings.denyPatterns !== void 0 && ir.settings.denyPatterns.length > 0 || Object.keys(ir.settings.raw).length > 0 || Object.values(ir.settings.hooks).some(
3231
+ (entries) => entries !== void 0 && entries.length > 0
3232
+ );
3233
+ if (hasSettings) {
3234
+ totalFiles += 1;
3235
+ }
3236
+ if (ir.mcpServers.length > 0) {
3237
+ totalFiles += 1;
3238
+ }
3239
+ let totalLines = 0;
3240
+ for (const section of ir.sections) {
3241
+ totalLines += countLines(section.content);
3242
+ }
3243
+ for (const cmd of ir.commands) {
3244
+ totalLines += countLines(cmd.content);
3245
+ }
3246
+ for (const rule of ir.rules) {
3247
+ totalLines += countLines(rule.content);
3248
+ }
3249
+ for (const agent of ir.agents) {
3250
+ totalLines += countLines(agent.content);
3251
+ }
3252
+ for (const skill of ir.skills) {
3253
+ totalLines += countLines(skill.content);
3254
+ }
3255
+ for (const doc of ir.docs) {
3256
+ totalLines += countLines(doc.content);
3257
+ }
3258
+ for (const hook of ir.hooks) {
3259
+ totalLines += countLines(hook.content);
3260
+ }
3261
+ return {
3262
+ totalLines,
3263
+ totalFiles,
3264
+ totalSections,
3265
+ totalRules,
3266
+ totalCommands,
3267
+ diffFromBaseline: 0
3268
+ };
3269
+ }
3270
+ function countLines(content) {
3271
+ if (!content) return 0;
3272
+ return content.split("\n").length;
3273
+ }
1660
3274
  function computeComplexityCost(current, baseline) {
1661
3275
  const baselineLines = Math.max(baseline.totalLines, 1);
1662
3276
  const lineDelta = (current.totalLines - baseline.totalLines) / baselineLines;
@@ -1702,18 +3316,18 @@ async function readAllFilesRecursive(dir) {
1702
3316
  async function walk(current) {
1703
3317
  let entries;
1704
3318
  try {
1705
- entries = await fs23.readdir(current, { withFileTypes: true });
3319
+ entries = await fs24.readdir(current, { withFileTypes: true });
1706
3320
  } catch {
1707
3321
  return;
1708
3322
  }
1709
3323
  for (const entry of entries) {
1710
- const fullPath = path23.join(current, entry.name);
1711
- const relativePath = path23.relative(dir, fullPath);
3324
+ const fullPath = path24.join(current, entry.name);
3325
+ const relativePath = path24.relative(dir, fullPath);
1712
3326
  if (entry.isDirectory()) {
1713
3327
  await walk(fullPath);
1714
3328
  } else {
1715
3329
  try {
1716
- result[relativePath] = await fs23.readFile(fullPath, "utf-8");
3330
+ result[relativePath] = await fs24.readFile(fullPath, "utf-8");
1717
3331
  } catch {
1718
3332
  }
1719
3333
  }
@@ -1729,8 +3343,8 @@ var init_regularization = __esm({
1729
3343
  });
1730
3344
 
1731
3345
  // src/evolve/loop.ts
1732
- import fs24 from "fs/promises";
1733
- import path24 from "path";
3346
+ import fs25 from "fs/promises";
3347
+ import path25 from "path";
1734
3348
  function computeMutationCap(iter, maxIterations, maxMutations) {
1735
3349
  if (maxIterations <= 1) return maxMutations;
1736
3350
  const progress = iter / (maxIterations - 1);
@@ -1747,27 +3361,33 @@ async function evolve(workspacePath, tasks, kairnConfig, evolveConfig, onProgres
1747
3361
  let beliefs = useThompson ? await loadBeliefs(workspacePath) ?? initBeliefs(tasks) : [];
1748
3362
  const useKL = evolveConfig.klLambda > 0;
1749
3363
  let baselineComplexity = null;
3364
+ let baselineIR = null;
1750
3365
  if (useKL) {
1751
- const baselineHarness = path24.join(workspacePath, "iterations", "0", "harness");
3366
+ const baselineHarness = path25.join(workspacePath, "iterations", "0", "harness");
1752
3367
  try {
1753
- baselineComplexity = await measureComplexity(baselineHarness);
3368
+ baselineIR = await parseHarness(baselineHarness);
3369
+ baselineComplexity = measureComplexityFromIR(baselineIR);
1754
3370
  } catch {
3371
+ try {
3372
+ baselineComplexity = await measureComplexity(baselineHarness);
3373
+ } catch {
3374
+ }
1755
3375
  }
1756
3376
  }
1757
- let rngState = 42;
3377
+ let rngState = evolveConfig.rngSeed ?? 42;
1758
3378
  const rng = () => {
1759
3379
  rngState = rngState * 1664525 + 1013904223 & 4294967295;
1760
3380
  return (rngState >>> 0) / 4294967296;
1761
3381
  };
1762
3382
  for (let iter = 0; iter < evolveConfig.maxIterations; iter++) {
1763
- const harnessPath = path24.join(
3383
+ const harnessPath = path25.join(
1764
3384
  workspacePath,
1765
3385
  "iterations",
1766
3386
  iter.toString(),
1767
3387
  "harness"
1768
3388
  );
1769
3389
  try {
1770
- await fs24.access(harnessPath);
3390
+ await fs25.access(harnessPath);
1771
3391
  } catch {
1772
3392
  if (iter === 0) {
1773
3393
  throw new Error(
@@ -1851,10 +3471,16 @@ async function evolve(workspacePath, tasks, kairnConfig, evolveConfig, onProgres
1851
3471
  let aggregate = rawAggregate;
1852
3472
  let iterComplexityCost;
1853
3473
  if (useKL && baselineComplexity) {
1854
- const currentComplexity = await measureComplexity(harnessPath);
3474
+ let currentComplexity;
3475
+ try {
3476
+ const iterIR = await parseHarness(harnessPath);
3477
+ currentComplexity = measureComplexityFromIR(iterIR);
3478
+ } catch {
3479
+ currentComplexity = await measureComplexity(harnessPath);
3480
+ }
1855
3481
  const diffRatio = await computeDiffRatio(
1856
3482
  harnessPath,
1857
- path24.join(workspacePath, "iterations", "0", "harness")
3483
+ path25.join(workspacePath, "iterations", "0", "harness")
1858
3484
  );
1859
3485
  currentComplexity.diffFromBaseline = diffRatio;
1860
3486
  iterComplexityCost = computeComplexityCost(currentComplexity, baselineComplexity);
@@ -1872,7 +3498,12 @@ async function evolve(workspacePath, tasks, kairnConfig, evolveConfig, onProgres
1872
3498
  if (iter === 0) {
1873
3499
  baselineScore = aggregate;
1874
3500
  if (useKL && !baselineComplexity) {
1875
- baselineComplexity = await measureComplexity(harnessPath);
3501
+ try {
3502
+ baselineIR = await parseHarness(harnessPath);
3503
+ baselineComplexity = measureComplexityFromIR(baselineIR);
3504
+ } catch {
3505
+ baselineComplexity = await measureComplexity(harnessPath);
3506
+ }
1876
3507
  }
1877
3508
  }
1878
3509
  let shouldRollback = iter > 0 && aggregate < bestScore;
@@ -1917,7 +3548,7 @@ async function evolve(workspacePath, tasks, kairnConfig, evolveConfig, onProgres
1917
3548
  };
1918
3549
  await writeIterationLog(workspacePath, rollbackLog);
1919
3550
  history.push(rollbackLog);
1920
- const bestHarnessPath = path24.join(
3551
+ const bestHarnessPath = path25.join(
1921
3552
  workspacePath,
1922
3553
  "iterations",
1923
3554
  bestIteration.toString(),
@@ -1942,7 +3573,7 @@ async function evolve(workspacePath, tasks, kairnConfig, evolveConfig, onProgres
1942
3573
  mutations: rollbackProposal.mutations.slice(0, rollbackCap)
1943
3574
  };
1944
3575
  }
1945
- const nextIterDir2 = path24.join(workspacePath, "iterations", (iter + 1).toString());
3576
+ const nextIterDir2 = path25.join(workspacePath, "iterations", (iter + 1).toString());
1946
3577
  await applyMutations(bestHarnessPath, nextIterDir2, rollbackProposal.mutations);
1947
3578
  onProgress?.({
1948
3579
  type: "mutations-applied",
@@ -1950,8 +3581,8 @@ async function evolve(workspacePath, tasks, kairnConfig, evolveConfig, onProgres
1950
3581
  mutationCount: rollbackProposal.mutations.length
1951
3582
  });
1952
3583
  } catch {
1953
- const nextIterDir2 = path24.join(workspacePath, "iterations", (iter + 1).toString());
1954
- await copyDir(bestHarnessPath, path24.join(nextIterDir2, "harness"));
3584
+ const nextIterDir2 = path25.join(workspacePath, "iterations", (iter + 1).toString());
3585
+ await copyDir(bestHarnessPath, path25.join(nextIterDir2, "harness"));
1955
3586
  }
1956
3587
  }
1957
3588
  continue;
@@ -2015,12 +3646,12 @@ async function evolve(workspacePath, tasks, kairnConfig, evolveConfig, onProgres
2015
3646
  iteration: iter,
2016
3647
  message: `Proposer failed: ${errMsg}`
2017
3648
  });
2018
- const nextIterDir2 = path24.join(
3649
+ const nextIterDir2 = path25.join(
2019
3650
  workspacePath,
2020
3651
  "iterations",
2021
3652
  (iter + 1).toString()
2022
3653
  );
2023
- await copyDir(harnessPath, path24.join(nextIterDir2, "harness"));
3654
+ await copyDir(harnessPath, path25.join(nextIterDir2, "harness"));
2024
3655
  const skipLog = {
2025
3656
  iteration: iter,
2026
3657
  score: aggregate,
@@ -2035,7 +3666,7 @@ async function evolve(workspacePath, tasks, kairnConfig, evolveConfig, onProgres
2035
3666
  history.push(skipLog);
2036
3667
  continue;
2037
3668
  }
2038
- const nextIterDir = path24.join(
3669
+ const nextIterDir = path25.join(
2039
3670
  workspacePath,
2040
3671
  "iterations",
2041
3672
  (iter + 1).toString()
@@ -2049,7 +3680,7 @@ async function evolve(workspacePath, tasks, kairnConfig, evolveConfig, onProgres
2049
3680
  );
2050
3681
  diffPatch = mutationResult.diffPatch;
2051
3682
  } catch {
2052
- await copyDir(harnessPath, path24.join(nextIterDir, "harness"));
3683
+ await copyDir(harnessPath, path25.join(nextIterDir, "harness"));
2053
3684
  }
2054
3685
  onProgress?.({
2055
3686
  type: "mutations-applied",
@@ -2071,7 +3702,7 @@ async function evolve(workspacePath, tasks, kairnConfig, evolveConfig, onProgres
2071
3702
  }
2072
3703
  if (evolveConfig.usePrincipal && history.length >= 2) {
2073
3704
  onProgress?.({ type: "proposing", iteration: history.length, message: "Principal Proposer synthesizing final harness" });
2074
- const baselineHarnessPath = path24.join(workspacePath, "iterations", "0", "harness");
3705
+ const baselineHarnessPath = path25.join(workspacePath, "iterations", "0", "harness");
2075
3706
  try {
2076
3707
  const principalProposal = await propose(
2077
3708
  history.length,
@@ -2086,7 +3717,7 @@ async function evolve(workspacePath, tasks, kairnConfig, evolveConfig, onProgres
2086
3717
  principalProposal.mutations = principalProposal.mutations.slice(0, evolveConfig.maxMutationsPerIteration);
2087
3718
  }
2088
3719
  const principalIterNum = history.length;
2089
- const principalIterDir = path24.join(workspacePath, "iterations", principalIterNum.toString());
3720
+ const principalIterDir = path25.join(workspacePath, "iterations", principalIterNum.toString());
2090
3721
  const mutResult = await applyMutations(baselineHarnessPath, principalIterDir, principalProposal.mutations);
2091
3722
  onProgress?.({ type: "iteration-start", iteration: principalIterNum });
2092
3723
  const { results: principalResults, aggregate: principalAggregate } = await evaluateAll(
@@ -2147,11 +3778,12 @@ var init_loop = __esm({
2147
3778
  init_baseline();
2148
3779
  init_sampling();
2149
3780
  init_regularization();
3781
+ init_parser();
2150
3782
  }
2151
3783
  });
2152
3784
 
2153
3785
  // src/evolve/synthesis.ts
2154
- import path27 from "path";
3786
+ import path28 from "path";
2155
3787
  function buildMetaPrincipalSystemPrompt(numBranches) {
2156
3788
  return `You are reviewing the COMPLETE results of ${numBranches} independent evolution runs.
2157
3789
  Each branch explored different mutations and saw different task subsets.
@@ -2306,7 +3938,7 @@ async function runSynthesis(context, kairnConfig, evolveConfig, workspacePath) {
2306
3938
  if (mutations.length === 0) {
2307
3939
  return null;
2308
3940
  }
2309
- const synthesisDir = path27.join(workspacePath, "synthesis");
3941
+ const synthesisDir = path28.join(workspacePath, "synthesis");
2310
3942
  const { newHarnessPath } = await applyMutations(
2311
3943
  context.baselineHarnessPath,
2312
3944
  synthesisDir,
@@ -2340,26 +3972,26 @@ __export(population_exports, {
2340
3972
  initBranches: () => initBranches,
2341
3973
  runPopulation: () => runPopulation
2342
3974
  });
2343
- import fs27 from "fs/promises";
2344
- import path28 from "path";
3975
+ import fs28 from "fs/promises";
3976
+ import path29 from "path";
2345
3977
  async function initBranches(workspacePath, baselinePath, numBranches) {
2346
- const branchesDir = path28.join(workspacePath, "branches");
2347
- await fs27.mkdir(branchesDir, { recursive: true });
3978
+ const branchesDir = path29.join(workspacePath, "branches");
3979
+ await fs28.mkdir(branchesDir, { recursive: true });
2348
3980
  const configs = [];
2349
3981
  for (let i = 0; i < numBranches; i++) {
2350
- const branchPath = path28.join(branchesDir, i.toString());
2351
- const harnessPath = path28.join(branchPath, "iterations", "0", "harness");
3982
+ const branchPath = path29.join(branchesDir, i.toString());
3983
+ const harnessPath = path29.join(branchPath, "iterations", "0", "harness");
2352
3984
  await copyDir(baselinePath, harnessPath);
2353
- const tasksYaml = path28.join(workspacePath, "tasks.yaml");
3985
+ const tasksYaml = path29.join(workspacePath, "tasks.yaml");
2354
3986
  try {
2355
- await fs27.access(tasksYaml);
2356
- await fs27.copyFile(tasksYaml, path28.join(branchPath, "tasks.yaml"));
3987
+ await fs28.access(tasksYaml);
3988
+ await fs28.copyFile(tasksYaml, path29.join(branchPath, "tasks.yaml"));
2357
3989
  } catch {
2358
3990
  }
2359
- const configYaml = path28.join(workspacePath, "config.yaml");
3991
+ const configYaml = path29.join(workspacePath, "config.yaml");
2360
3992
  try {
2361
- await fs27.access(configYaml);
2362
- await fs27.copyFile(configYaml, path28.join(branchPath, "config.yaml"));
3993
+ await fs28.access(configYaml);
3994
+ await fs28.copyFile(configYaml, path29.join(branchPath, "config.yaml"));
2363
3995
  } catch {
2364
3996
  }
2365
3997
  const seed = 42 + i * 1337;
@@ -2373,13 +4005,15 @@ async function initBranches(workspacePath, baselinePath, numBranches) {
2373
4005
  }
2374
4006
  async function runPopulation(workspacePath, tasks, kairnConfig, evolveConfig, numBranches, onProgress) {
2375
4007
  const branches = numBranches ?? evolveConfig.pbtBranches;
2376
- const baselinePath = path28.join(workspacePath, "baseline");
4008
+ const baselinePath = path29.join(workspacePath, "baseline");
2377
4009
  const branchConfigs = await initBranches(workspacePath, baselinePath, branches);
2378
4010
  const branchPromises = branchConfigs.map(async (branchConfig) => {
2379
4011
  const branchEvolveConfig = {
2380
4012
  ...evolveConfig,
2381
4013
  // Disable principal for individual branches — synthesis replaces it
2382
- usePrincipal: false
4014
+ usePrincipal: false,
4015
+ // Each branch gets its own RNG seed for Thompson Sampling diversity
4016
+ rngSeed: branchConfig.seed
2383
4017
  };
2384
4018
  const branchProgress = onProgress ? (event) => {
2385
4019
  onProgress({ ...event, branchId: branchConfig.branchId });
@@ -2391,7 +4025,7 @@ async function runPopulation(workspacePath, tasks, kairnConfig, evolveConfig, nu
2391
4025
  branchEvolveConfig,
2392
4026
  branchProgress
2393
4027
  );
2394
- const finalHarnessPath = path28.join(
4028
+ const finalHarnessPath = path29.join(
2395
4029
  branchConfig.workspacePath,
2396
4030
  "iterations",
2397
4031
  result.bestIteration.toString(),
@@ -2399,8 +4033,8 @@ async function runPopulation(workspacePath, tasks, kairnConfig, evolveConfig, nu
2399
4033
  );
2400
4034
  let beliefs = [];
2401
4035
  try {
2402
- const beliefsPath = path28.join(branchConfig.workspacePath, "task-beliefs.json");
2403
- const beliefsContent = await fs27.readFile(beliefsPath, "utf-8");
4036
+ const beliefsPath = path29.join(branchConfig.workspacePath, "task-beliefs.json");
4037
+ const beliefsContent = await fs28.readFile(beliefsPath, "utf-8");
2404
4038
  beliefs = JSON.parse(beliefsContent);
2405
4039
  } catch {
2406
4040
  }
@@ -2422,7 +4056,7 @@ async function runPopulation(workspacePath, tasks, kairnConfig, evolveConfig, nu
2422
4056
  }
2423
4057
  let synthesizedResult;
2424
4058
  try {
2425
- const baselinePath2 = path28.join(workspacePath, "baseline");
4059
+ const baselinePath2 = path29.join(workspacePath, "baseline");
2426
4060
  const synthesisResult = await runSynthesis(
2427
4061
  { branches: branchResults, tasks, baselineHarnessPath: baselinePath2 },
2428
4062
  kairnConfig,
@@ -2597,7 +4231,7 @@ var ui = {
2597
4231
  // Key-value pairs
2598
4232
  kv: (key, value) => ` ${chalk.cyan(key.padEnd(14))} ${value}`,
2599
4233
  // File list
2600
- file: (path30) => chalk.dim(` ${path30}`),
4234
+ file: (path31) => chalk.dim(` ${path31}`),
2601
4235
  // Tool display
2602
4236
  tool: (name, reason) => ` ${warmStone("\u25CF")} ${chalk.bold(name)}
2603
4237
  ${chalk.dim(reason)}`,
@@ -6449,8 +8083,8 @@ var keysCommand = new Command10("keys").description("Add or update API keys for
6449
8083
  import { Command as Command11 } from "commander";
6450
8084
  import chalk14 from "chalk";
6451
8085
  import ora2 from "ora";
6452
- import fs28 from "fs/promises";
6453
- import path29 from "path";
8086
+ import fs29 from "fs/promises";
8087
+ import path30 from "path";
6454
8088
  import { parse as yamlParse2 } from "yaml";
6455
8089
  import { confirm as confirm4, input as input4, select as select4 } from "@inquirer/prompts";
6456
8090
 
@@ -6780,8 +8414,8 @@ init_loop();
6780
8414
 
6781
8415
  // src/evolve/report.ts
6782
8416
  init_trace();
6783
- import fs25 from "fs/promises";
6784
- import path25 from "path";
8417
+ import fs26 from "fs/promises";
8418
+ import path26 from "path";
6785
8419
 
6786
8420
  // src/evolve/diagnosis.ts
6787
8421
  function numericScore(s) {
@@ -6831,10 +8465,10 @@ function numericScore2(s) {
6831
8465
  return s.score ?? (s.pass ? 100 : 0);
6832
8466
  }
6833
8467
  async function loadAllIterations(workspacePath) {
6834
- const iterDir = path25.join(workspacePath, "iterations");
8468
+ const iterDir = path26.join(workspacePath, "iterations");
6835
8469
  let entries;
6836
8470
  try {
6837
- entries = await fs25.readdir(iterDir);
8471
+ entries = await fs26.readdir(iterDir);
6838
8472
  } catch {
6839
8473
  return [];
6840
8474
  }
@@ -6848,7 +8482,7 @@ async function loadAllIterations(workspacePath) {
6848
8482
  }
6849
8483
  async function loadTasks(workspacePath) {
6850
8484
  try {
6851
- const content = await fs25.readFile(path25.join(workspacePath, "tasks.yaml"), "utf-8");
8485
+ const content = await fs26.readFile(path26.join(workspacePath, "tasks.yaml"), "utf-8");
6852
8486
  const parsed = yamlParse(content);
6853
8487
  return parsed?.tasks ?? [];
6854
8488
  } catch {
@@ -7028,13 +8662,13 @@ init_mutator();
7028
8662
  init_baseline();
7029
8663
  init_mutator();
7030
8664
  init_trace();
7031
- import fs26 from "fs/promises";
7032
- import path26 from "path";
8665
+ import fs27 from "fs/promises";
8666
+ import path27 from "path";
7033
8667
  async function listIterations(workspacePath) {
7034
- const iterationsDir = path26.join(workspacePath, "iterations");
8668
+ const iterationsDir = path27.join(workspacePath, "iterations");
7035
8669
  let entries;
7036
8670
  try {
7037
- entries = await fs26.readdir(iterationsDir);
8671
+ entries = await fs27.readdir(iterationsDir);
7038
8672
  } catch {
7039
8673
  return [];
7040
8674
  }
@@ -7043,7 +8677,7 @@ async function listIterations(workspacePath) {
7043
8677
  const n = parseInt(entry, 10);
7044
8678
  if (!isNaN(n)) {
7045
8679
  try {
7046
- await fs26.access(path26.join(iterationsDir, entry, "harness"));
8680
+ await fs27.access(path27.join(iterationsDir, entry, "harness"));
7047
8681
  nums.push(n);
7048
8682
  } catch {
7049
8683
  }
@@ -7069,16 +8703,16 @@ async function listFilesRecursive(dir) {
7069
8703
  async function walk(current) {
7070
8704
  let entries;
7071
8705
  try {
7072
- entries = await fs26.readdir(current, { withFileTypes: true });
8706
+ entries = await fs27.readdir(current, { withFileTypes: true });
7073
8707
  } catch {
7074
8708
  return;
7075
8709
  }
7076
8710
  for (const entry of entries) {
7077
- const fullPath = path26.join(current, entry.name);
8711
+ const fullPath = path27.join(current, entry.name);
7078
8712
  if (entry.isDirectory()) {
7079
8713
  await walk(fullPath);
7080
8714
  } else {
7081
- results.push(path26.relative(dir, fullPath));
8715
+ results.push(path27.relative(dir, fullPath));
7082
8716
  }
7083
8717
  }
7084
8718
  }
@@ -7101,37 +8735,37 @@ async function applyEvolution(workspacePath, projectRoot, targetIteration) {
7101
8735
  } else {
7102
8736
  iter = await findBestIteration(workspacePath, iterations);
7103
8737
  }
7104
- const harnessPath = path26.join(
8738
+ const harnessPath = path27.join(
7105
8739
  workspacePath,
7106
8740
  "iterations",
7107
8741
  iter.toString(),
7108
8742
  "harness"
7109
8743
  );
7110
- const claudeDir = path26.join(projectRoot, ".claude");
8744
+ const claudeDir = path27.join(projectRoot, ".claude");
7111
8745
  const diffPreview = await generateDiff2(claudeDir, harnessPath);
7112
8746
  const currentFiles = await listFilesRecursive(claudeDir);
7113
8747
  const targetFiles = await listFilesRecursive(harnessPath);
7114
8748
  const allPaths = /* @__PURE__ */ new Set([...currentFiles, ...targetFiles]);
7115
8749
  const filesChanged = [];
7116
8750
  for (const filePath of allPaths) {
7117
- const currentContent = await fs26.readFile(path26.join(claudeDir, filePath), "utf-8").catch(() => null);
7118
- const targetContent = await fs26.readFile(path26.join(harnessPath, filePath), "utf-8").catch(() => null);
8751
+ const currentContent = await fs27.readFile(path27.join(claudeDir, filePath), "utf-8").catch(() => null);
8752
+ const targetContent = await fs27.readFile(path27.join(harnessPath, filePath), "utf-8").catch(() => null);
7119
8753
  if (currentContent !== targetContent) {
7120
8754
  filesChanged.push(filePath);
7121
8755
  }
7122
8756
  }
7123
- await fs26.rm(claudeDir, { recursive: true, force: true });
8757
+ await fs27.rm(claudeDir, { recursive: true, force: true });
7124
8758
  await copyDir(harnessPath, claudeDir);
7125
- const harnessMcpJson = path26.join(harnessPath, ".mcp.json");
7126
- const projectMcpJson = path26.join(projectRoot, ".mcp.json");
8759
+ const harnessMcpJson = path27.join(harnessPath, ".mcp.json");
8760
+ const projectMcpJson = path27.join(projectRoot, ".mcp.json");
7127
8761
  try {
7128
- await fs26.access(harnessMcpJson);
7129
- const currentMcp = await fs26.readFile(projectMcpJson, "utf-8").catch(() => null);
7130
- const targetMcp = await fs26.readFile(harnessMcpJson, "utf-8").catch(() => null);
8762
+ await fs27.access(harnessMcpJson);
8763
+ const currentMcp = await fs27.readFile(projectMcpJson, "utf-8").catch(() => null);
8764
+ const targetMcp = await fs27.readFile(harnessMcpJson, "utf-8").catch(() => null);
7131
8765
  if (currentMcp !== targetMcp) {
7132
8766
  filesChanged.push(".mcp.json");
7133
8767
  }
7134
- await fs26.copyFile(harnessMcpJson, projectMcpJson);
8768
+ await fs27.copyFile(harnessMcpJson, projectMcpJson);
7135
8769
  } catch {
7136
8770
  }
7137
8771
  return {
@@ -7160,7 +8794,7 @@ var DEFAULT_CONFIG = {
7160
8794
  };
7161
8795
  async function loadEvolveConfigFromWorkspace(workspacePath) {
7162
8796
  try {
7163
- const configStr = await fs28.readFile(path29.join(workspacePath, "config.yaml"), "utf-8");
8797
+ const configStr = await fs29.readFile(path30.join(workspacePath, "config.yaml"), "utf-8");
7164
8798
  const parsed = yamlParse2(configStr);
7165
8799
  return {
7166
8800
  model: parsed.model ?? DEFAULT_CONFIG.model,
@@ -7187,9 +8821,9 @@ evolveCommand.command("init").description("Initialize an evolution workspace wit
7187
8821
  try {
7188
8822
  const projectRoot = process.cwd();
7189
8823
  console.log(ui.section("Evolve Init"));
7190
- const claudeDir = path29.join(projectRoot, ".claude");
8824
+ const claudeDir = path30.join(projectRoot, ".claude");
7191
8825
  try {
7192
- await fs28.access(claudeDir);
8826
+ await fs29.access(claudeDir);
7193
8827
  } catch {
7194
8828
  console.log(ui.error("No .claude/ directory found. Run kairn describe first."));
7195
8829
  process.exit(1);
@@ -7239,7 +8873,7 @@ evolveCommand.command("init").description("Initialize an evolution workspace wit
7239
8873
  if (config) {
7240
8874
  let claudeMd = "";
7241
8875
  try {
7242
- claudeMd = await fs28.readFile(path29.join(claudeDir, "CLAUDE.md"), "utf-8");
8876
+ claudeMd = await fs29.readFile(path30.join(claudeDir, "CLAUDE.md"), "utf-8");
7243
8877
  } catch {
7244
8878
  }
7245
8879
  const profile = await buildProjectProfile(projectRoot);
@@ -7270,16 +8904,16 @@ evolveCommand.command("init").description("Initialize an evolution workspace wit
7270
8904
  evolveCommand.command("baseline").description("Snapshot current .claude/ directory as baseline").action(async () => {
7271
8905
  try {
7272
8906
  const projectRoot = process.cwd();
7273
- const workspace = path29.join(projectRoot, ".kairn-evolve");
8907
+ const workspace = path30.join(projectRoot, ".kairn-evolve");
7274
8908
  console.log(ui.section("Evolve Baseline"));
7275
8909
  try {
7276
- await fs28.access(workspace);
8910
+ await fs29.access(workspace);
7277
8911
  } catch {
7278
8912
  console.log(ui.error("No .kairn-evolve/ directory found. Run kairn evolve init first."));
7279
8913
  process.exit(1);
7280
8914
  }
7281
8915
  await snapshotBaseline(projectRoot, workspace);
7282
- const baselineDir = path29.join(workspace, "baseline");
8916
+ const baselineDir = path30.join(workspace, "baseline");
7283
8917
  const fileCount = await countFiles(baselineDir);
7284
8918
  console.log(ui.success(`Baseline snapshot created (${fileCount} files)`));
7285
8919
  } catch (err) {
@@ -7291,18 +8925,18 @@ evolveCommand.command("baseline").description("Snapshot current .claude/ directo
7291
8925
  evolveCommand.command("run").description("Run tasks against the current harness").option("--task <id>", "Run a specific task by ID").option("--iterations <n>", "Number of evolution iterations", "5").option("--runs <n>", "Run each task N times for variance measurement", "1").option("--parallel <n>", "Run up to N tasks concurrently", "1").option("--max-mutations <n>", "Max mutations per iteration", "3").option("--prune-threshold <n>", "Skip tasks scoring above this on middle iterations", "95").option("--max-task-drop <n>", "Roll back if any task drops more than N points", "20").option("--principal", "Run Principal Proposer as final iteration").option("--eval-sample <n>", "Sample N tasks per middle iteration (0 = all)", "0").option("--sampling <strategy>", "Task sampling strategy: thompson or uniform", "thompson").option("--kl-lambda <n>", "KL regularization strength (0 = disabled)", "0.1").option("-i, --interactive", "Configure evolution settings interactively").action(async (options) => {
7292
8926
  try {
7293
8927
  const projectRoot = process.cwd();
7294
- const workspace = path29.join(projectRoot, ".kairn-evolve");
8928
+ const workspace = path30.join(projectRoot, ".kairn-evolve");
7295
8929
  console.log(ui.section("Evolve Run"));
7296
8930
  try {
7297
- await fs28.access(workspace);
8931
+ await fs29.access(workspace);
7298
8932
  } catch {
7299
8933
  console.log(ui.error("No .kairn-evolve/ directory found. Run kairn evolve init first."));
7300
8934
  process.exit(1);
7301
8935
  }
7302
- const tasksPath = path29.join(workspace, "tasks.yaml");
8936
+ const tasksPath = path30.join(workspace, "tasks.yaml");
7303
8937
  let tasksContent;
7304
8938
  try {
7305
- tasksContent = await fs28.readFile(tasksPath, "utf-8");
8939
+ tasksContent = await fs29.readFile(tasksPath, "utf-8");
7306
8940
  } catch {
7307
8941
  console.log(ui.error("No tasks.yaml found. Run kairn evolve init first."));
7308
8942
  process.exit(1);
@@ -7321,15 +8955,15 @@ evolveCommand.command("run").description("Run tasks against the current harness"
7321
8955
  console.log(ui.info(`Running ${tasksToRun.length} task(s)...`));
7322
8956
  console.log("");
7323
8957
  const config = await loadConfig();
7324
- const harnessPath = path29.join(projectRoot, ".claude");
8958
+ const harnessPath = path30.join(projectRoot, ".claude");
7325
8959
  const results = [];
7326
8960
  for (const task of tasksToRun) {
7327
- const traceDir = path29.join(workspace, "traces", "0", task.id);
8961
+ const traceDir = path30.join(workspace, "traces", "0", task.id);
7328
8962
  const spinner = ora2(`Running: ${task.id}`).start();
7329
8963
  const result = await runTask(task, harnessPath, traceDir, 0);
7330
8964
  if (config) {
7331
- const stdout = await fs28.readFile(path29.join(traceDir, "stdout.log"), "utf-8").catch(() => "");
7332
- const stderr = await fs28.readFile(path29.join(traceDir, "stderr.log"), "utf-8").catch(() => "");
8965
+ const stdout = await fs29.readFile(path30.join(traceDir, "stdout.log"), "utf-8").catch(() => "");
8966
+ const stderr = await fs29.readFile(path30.join(traceDir, "stderr.log"), "utf-8").catch(() => "");
7333
8967
  const score = await scoreTask(task, traceDir, stdout, stderr, config);
7334
8968
  result.score = score;
7335
8969
  await writeScore(traceDir, score);
@@ -7477,7 +9111,7 @@ evolveCommand.command("run").description("Run tasks against the current harness"
7477
9111
  evolveConfig.klLambda = klLambda;
7478
9112
  }
7479
9113
  try {
7480
- await fs28.access(path29.join(workspace, "iterations", "0", "harness"));
9114
+ await fs29.access(path30.join(workspace, "iterations", "0", "harness"));
7481
9115
  } catch {
7482
9116
  console.log(ui.error("No baseline harness found. Run kairn evolve baseline first."));
7483
9117
  process.exit(1);
@@ -7571,16 +9205,16 @@ evolveCommand.command("run").description("Run tasks against the current harness"
7571
9205
  evolveCommand.command("pbt").description("Run Population-Based Training with parallel evolution branches").option("--branches <n>", "Number of parallel branches", "3").option("--iterations <n>", "Iterations per branch", "5").option("--parallel <n>", "Tasks per branch concurrently", "2").option("--sampling <strategy>", "Task sampling strategy: thompson or uniform", "thompson").option("--kl-lambda <n>", "KL regularization strength (0 = disabled)", "0.1").option("--eval-sample <n>", "Sample N tasks per middle iteration (0 = all)", "5").action(async (options) => {
7572
9206
  try {
7573
9207
  const projectRoot = process.cwd();
7574
- const workspace = path29.join(projectRoot, ".kairn-evolve");
9208
+ const workspace = path30.join(projectRoot, ".kairn-evolve");
7575
9209
  console.log(ui.section("Evolve PBT"));
7576
9210
  try {
7577
- await fs28.access(workspace);
9211
+ await fs29.access(workspace);
7578
9212
  } catch {
7579
9213
  console.log(ui.error("No .kairn-evolve/ directory found. Run kairn evolve init first."));
7580
9214
  process.exit(1);
7581
9215
  }
7582
9216
  try {
7583
- await fs28.access(path29.join(workspace, "iterations", "0", "harness"));
9217
+ await fs29.access(path30.join(workspace, "iterations", "0", "harness"));
7584
9218
  } catch {
7585
9219
  console.log(ui.error("No baseline harness found. Run kairn evolve baseline first."));
7586
9220
  process.exit(1);
@@ -7600,8 +9234,8 @@ evolveCommand.command("pbt").description("Run Population-Based Training with par
7600
9234
  if (sampling === "thompson" || sampling === "uniform") {
7601
9235
  evolveConfig.samplingStrategy = sampling;
7602
9236
  }
7603
- const tasksPath = path29.join(workspace, "tasks.yaml");
7604
- const tasksContent = await fs28.readFile(tasksPath, "utf-8");
9237
+ const tasksPath = path30.join(workspace, "tasks.yaml");
9238
+ const tasksContent = await fs29.readFile(tasksPath, "utf-8");
7605
9239
  const parsed = yamlParse2(tasksContent);
7606
9240
  if (!parsed?.tasks || parsed.tasks.length === 0) {
7607
9241
  console.log(ui.error("No tasks found in tasks.yaml"));
@@ -7659,10 +9293,10 @@ evolveCommand.command("pbt").description("Run Population-Based Training with par
7659
9293
  evolveCommand.command("apply").description("Apply the best evolved harness to your project").option("--iter <n>", "Apply a specific iteration instead of the best").option("--force", "Apply even if git working tree is dirty").option("--no-commit", "Skip automatic git commit after applying").action(async (options) => {
7660
9294
  try {
7661
9295
  const projectRoot = process.cwd();
7662
- const workspace = path29.join(projectRoot, ".kairn-evolve");
9296
+ const workspace = path30.join(projectRoot, ".kairn-evolve");
7663
9297
  console.log(ui.section("Evolve Apply"));
7664
9298
  try {
7665
- await fs28.access(workspace);
9299
+ await fs29.access(workspace);
7666
9300
  } catch {
7667
9301
  console.log(ui.error("No .kairn-evolve/ directory found. Run kairn evolve init first."));
7668
9302
  process.exit(1);
@@ -7703,9 +9337,9 @@ evolveCommand.command("apply").description("Apply the best evolved harness to yo
7703
9337
  evolveCommand.command("report").description("Generate a summary report of the evolution run").option("--json", "Output machine-readable JSON instead of Markdown").action(async (options) => {
7704
9338
  try {
7705
9339
  const projectRoot = process.cwd();
7706
- const workspace = path29.join(projectRoot, ".kairn-evolve");
9340
+ const workspace = path30.join(projectRoot, ".kairn-evolve");
7707
9341
  try {
7708
- await fs28.access(workspace);
9342
+ await fs29.access(workspace);
7709
9343
  } catch {
7710
9344
  console.log(ui.error("No .kairn-evolve/ directory found. Run kairn evolve init first."));
7711
9345
  process.exit(1);
@@ -7726,23 +9360,23 @@ evolveCommand.command("report").description("Generate a summary report of the ev
7726
9360
  evolveCommand.command("diff <iter1> <iter2>").description("Show harness changes between two iterations").action(async (iter1Str, iter2Str) => {
7727
9361
  try {
7728
9362
  const projectRoot = process.cwd();
7729
- const workspace = path29.join(projectRoot, ".kairn-evolve");
9363
+ const workspace = path30.join(projectRoot, ".kairn-evolve");
7730
9364
  const iter1 = parseInt(iter1Str, 10);
7731
9365
  const iter2 = parseInt(iter2Str, 10);
7732
9366
  if (isNaN(iter1) || isNaN(iter2)) {
7733
9367
  console.log(ui.error("Both arguments must be integers (iteration numbers)"));
7734
9368
  process.exit(1);
7735
9369
  }
7736
- const harness1 = path29.join(workspace, "iterations", iter1.toString(), "harness");
7737
- const harness2 = path29.join(workspace, "iterations", iter2.toString(), "harness");
9370
+ const harness1 = path30.join(workspace, "iterations", iter1.toString(), "harness");
9371
+ const harness2 = path30.join(workspace, "iterations", iter2.toString(), "harness");
7738
9372
  try {
7739
- await fs28.access(harness1);
9373
+ await fs29.access(harness1);
7740
9374
  } catch {
7741
9375
  console.log(ui.error(`Iteration ${iter1} harness not found at ${harness1}`));
7742
9376
  process.exit(1);
7743
9377
  }
7744
9378
  try {
7745
- await fs28.access(harness2);
9379
+ await fs29.access(harness2);
7746
9380
  } catch {
7747
9381
  console.log(ui.error(`Iteration ${iter2} harness not found at ${harness2}`));
7748
9382
  process.exit(1);
@@ -7797,10 +9431,10 @@ evolveCommand.command("diff <iter1> <iter2>").description("Show harness changes
7797
9431
  async function countFiles(dir) {
7798
9432
  let count = 0;
7799
9433
  try {
7800
- const entries = await fs28.readdir(dir, { withFileTypes: true });
9434
+ const entries = await fs29.readdir(dir, { withFileTypes: true });
7801
9435
  for (const entry of entries) {
7802
9436
  if (entry.isDirectory()) {
7803
- count += await countFiles(path29.join(dir, entry.name));
9437
+ count += await countFiles(path30.join(dir, entry.name));
7804
9438
  } else {
7805
9439
  count++;
7806
9440
  }