kairn-cli 2.6.0 → 2.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/dist/cli.js +1765 -163
  2. package/dist/cli.js.map +1 -1
  3. package/package.json +1 -1
package/dist/cli.js CHANGED
@@ -1409,68 +1409,1589 @@ 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
+ nodes.push(node);
1835
+ }
1836
+ return nodes;
1837
+ }
1838
+ async function parseSkills(harnessPath) {
1839
+ const dirPath = path21.join(harnessPath, "skills");
1840
+ const entries = await readDirSafe(dirPath);
1841
+ const nodes = [];
1842
+ for (const entry of entries) {
1843
+ const entryPath = path21.join(dirPath, entry);
1844
+ if (entry.endsWith(".md")) {
1845
+ const content = await readFileSafe2(entryPath);
1846
+ if (content === null) continue;
1847
+ const name = entry.replace(/\.md$/, "");
1848
+ nodes.push({ name, content });
1849
+ } else if (await isDirectory(entryPath)) {
1850
+ const skillMdPath = path21.join(entryPath, "skill.md");
1851
+ const content = await readFileSafe2(skillMdPath);
1852
+ if (content === null) continue;
1853
+ nodes.push({ name: entry, content });
1854
+ }
1855
+ }
1856
+ return nodes;
1857
+ }
1858
+ async function parseDocs(harnessPath) {
1859
+ const dirPath = path21.join(harnessPath, "docs");
1860
+ const entries = await readDirSafe(dirPath);
1861
+ const nodes = [];
1862
+ for (const entry of entries) {
1863
+ if (!entry.endsWith(".md")) continue;
1864
+ const filePath = path21.join(dirPath, entry);
1865
+ const content = await readFileSafe2(filePath);
1866
+ if (content === null) continue;
1867
+ const name = entry.replace(/\.md$/, "");
1868
+ nodes.push({ name, content });
1869
+ }
1870
+ return nodes;
1871
+ }
1872
+ async function parseHooks(harnessPath) {
1873
+ const dirPath = path21.join(harnessPath, "hooks");
1874
+ const entries = await readDirSafe(dirPath);
1875
+ const nodes = [];
1876
+ for (const entry of entries) {
1877
+ if (!entry.endsWith(".mjs")) continue;
1878
+ const filePath = path21.join(dirPath, entry);
1879
+ const content = await readFileSafe2(filePath);
1880
+ if (content === null) continue;
1881
+ const name = entry.replace(/\.mjs$/, "");
1882
+ nodes.push({ name, content, type: "command" });
1883
+ }
1884
+ return nodes;
1885
+ }
1886
+ async function parseHarness(harnessPath) {
1887
+ const ir = createEmptyIR();
1888
+ const claudeMdContent = await readFileSafe2(
1889
+ path21.join(harnessPath, "CLAUDE.md")
1890
+ );
1891
+ if (claudeMdContent !== null) {
1892
+ const { meta, sections } = parseClaudeMd(claudeMdContent);
1893
+ ir.meta = {
1894
+ ...ir.meta,
1895
+ ...meta,
1896
+ techStack: { ...ir.meta.techStack, ...meta.techStack }
1897
+ };
1898
+ ir.sections = sections;
1899
+ }
1900
+ const settingsContent = await readFileSafe2(
1901
+ path21.join(harnessPath, "settings.json")
1902
+ );
1903
+ if (settingsContent !== null) {
1904
+ ir.settings = parseSettings(settingsContent);
1905
+ }
1906
+ const [commands, rules, agents, skills, docs, hooks] = await Promise.all([
1907
+ parseCommands(harnessPath),
1908
+ parseRules(harnessPath),
1909
+ parseAgents(harnessPath),
1910
+ parseSkills(harnessPath),
1911
+ parseDocs(harnessPath),
1912
+ parseHooks(harnessPath)
1913
+ ]);
1914
+ ir.commands = commands;
1915
+ ir.rules = rules;
1916
+ ir.agents = agents;
1917
+ ir.skills = skills;
1918
+ ir.docs = docs;
1919
+ ir.hooks = hooks;
1920
+ const mcpServers = [];
1921
+ const seenIds = /* @__PURE__ */ new Set();
1922
+ const parentMcpPath = path21.join(path21.dirname(harnessPath), ".mcp.json");
1923
+ const parentMcpContent = await readFileSafe2(parentMcpPath);
1924
+ if (parentMcpContent !== null) {
1925
+ for (const node of parseMcpConfig(parentMcpContent)) {
1926
+ if (!seenIds.has(node.id)) {
1927
+ seenIds.add(node.id);
1928
+ mcpServers.push(node);
1929
+ }
1930
+ }
1931
+ }
1932
+ const innerMcpPath = path21.join(harnessPath, ".mcp.json");
1933
+ const innerMcpContent = await readFileSafe2(innerMcpPath);
1934
+ if (innerMcpContent !== null) {
1935
+ for (const node of parseMcpConfig(innerMcpContent)) {
1936
+ if (!seenIds.has(node.id)) {
1937
+ seenIds.add(node.id);
1938
+ mcpServers.push(node);
1939
+ }
1940
+ }
1941
+ }
1942
+ ir.mcpServers = mcpServers;
1943
+ return ir;
1944
+ }
1945
+ var SECTION_ID_MAP;
1946
+ var init_parser = __esm({
1947
+ "src/ir/parser.ts"() {
1948
+ "use strict";
1949
+ init_types();
1950
+ SECTION_ID_MAP = [
1951
+ { pattern: /^(purpose|about|what)\b/i, id: "purpose" },
1952
+ { pattern: /^(tech\s*stack|technology|stack)\b/i, id: "tech-stack" },
1953
+ { pattern: /^(commands|key\s*commands)\b/i, id: "commands" },
1954
+ { pattern: /^architecture\b/i, id: "architecture" },
1955
+ { pattern: /^conventions?\b/i, id: "conventions" },
1956
+ { pattern: /^verification\b/i, id: "verification" },
1957
+ { pattern: /^(known\s*gotchas|gotchas)\b/i, id: "gotchas" },
1958
+ { pattern: /^output\b/i, id: "output" },
1959
+ { pattern: /^debugging\b/i, id: "debugging" },
1960
+ { pattern: /^git\b/i, id: "git" }
1961
+ ];
1962
+ }
1963
+ });
1964
+
1965
+ // src/ir/translate.ts
1966
+ function extractName(filePath, pattern) {
1967
+ const match = filePath.match(pattern);
1968
+ return match ? match[1] : null;
1969
+ }
1970
+ function findSectionContaining(ir, text) {
1971
+ return ir.sections.find((s) => s.content.includes(text));
1972
+ }
1973
+ function translateClaudeMdMutation(mutation, ir) {
1974
+ switch (mutation.action) {
1975
+ case "replace": {
1976
+ if (mutation.oldText === void 0) {
1977
+ return buildRawText(mutation);
1978
+ }
1979
+ const section = findSectionContaining(ir, mutation.oldText);
1980
+ if (!section) {
1981
+ return buildRawText(mutation);
1982
+ }
1983
+ return {
1984
+ type: "update_section",
1985
+ sectionId: section.id,
1986
+ content: section.content.replace(mutation.oldText, mutation.newText),
1987
+ rationale: mutation.rationale
1988
+ };
1989
+ }
1990
+ case "add_section": {
1991
+ const headingMatch = mutation.newText.match(/^## (.+)/);
1992
+ if (!headingMatch) {
1993
+ return buildRawText(mutation);
1994
+ }
1995
+ const headingText = headingMatch[1].trim();
1996
+ const sectionId = resolveSectionId(headingText);
1997
+ const heading = `## ${headingText}`;
1998
+ const newlineIdx = mutation.newText.indexOf("\n");
1999
+ const content = newlineIdx >= 0 ? mutation.newText.slice(newlineIdx + 1).trim() : "";
2000
+ const nextOrder = ir.sections.length;
2001
+ return {
2002
+ type: "add_section",
2003
+ section: createSection(sectionId, heading, content, nextOrder),
2004
+ rationale: mutation.rationale
2005
+ };
2006
+ }
2007
+ case "delete_section": {
2008
+ if (mutation.oldText === void 0) {
2009
+ return buildRawText(mutation);
2010
+ }
2011
+ const section = findSectionContaining(ir, mutation.oldText);
2012
+ if (!section) {
2013
+ return buildRawText(mutation);
2014
+ }
2015
+ return {
2016
+ type: "remove_section",
2017
+ sectionId: section.id,
2018
+ rationale: mutation.rationale
2019
+ };
2020
+ }
2021
+ default:
2022
+ return buildRawText(mutation);
2023
+ }
2024
+ }
2025
+ function translateCommandMutation(mutation, name) {
2026
+ switch (mutation.action) {
2027
+ case "create_file":
2028
+ return {
2029
+ type: "add_command",
2030
+ command: createCommandNode(name, mutation.newText),
2031
+ rationale: mutation.rationale
2032
+ };
2033
+ case "delete_file":
2034
+ return {
2035
+ type: "remove_command",
2036
+ name,
2037
+ rationale: mutation.rationale
2038
+ };
2039
+ case "replace":
2040
+ return {
2041
+ type: "update_command",
2042
+ name,
2043
+ content: mutation.newText,
2044
+ rationale: mutation.rationale
2045
+ };
2046
+ default:
2047
+ return buildRawText(mutation);
2048
+ }
2049
+ }
2050
+ function translateRuleMutation(mutation, name) {
2051
+ switch (mutation.action) {
2052
+ case "create_file":
2053
+ return {
2054
+ type: "add_rule",
2055
+ rule: createRuleNode(name, mutation.newText),
2056
+ rationale: mutation.rationale
2057
+ };
2058
+ case "delete_file":
2059
+ return {
2060
+ type: "remove_rule",
2061
+ name,
2062
+ rationale: mutation.rationale
2063
+ };
2064
+ case "replace":
2065
+ return {
2066
+ type: "update_rule",
2067
+ name,
2068
+ content: mutation.newText,
2069
+ rationale: mutation.rationale
2070
+ };
2071
+ default:
2072
+ return buildRawText(mutation);
2073
+ }
2074
+ }
2075
+ function translateAgentMutation(mutation, name) {
2076
+ switch (mutation.action) {
2077
+ case "create_file":
2078
+ return {
2079
+ type: "add_agent",
2080
+ agent: createAgentNode(name, mutation.newText),
2081
+ rationale: mutation.rationale
2082
+ };
2083
+ case "delete_file":
2084
+ return {
2085
+ type: "remove_agent",
2086
+ name,
2087
+ rationale: mutation.rationale
2088
+ };
2089
+ case "replace":
2090
+ return {
2091
+ type: "update_agent",
2092
+ name,
2093
+ changes: { content: mutation.newText },
2094
+ rationale: mutation.rationale
2095
+ };
2096
+ default:
2097
+ return buildRawText(mutation);
2098
+ }
2099
+ }
2100
+ function buildRawText(mutation) {
2101
+ return {
2102
+ type: "raw_text",
2103
+ file: mutation.file,
2104
+ action: mutation.action,
2105
+ oldText: mutation.oldText,
2106
+ newText: mutation.newText,
2107
+ rationale: mutation.rationale
2108
+ };
2109
+ }
2110
+ function translateMutation(mutation, ir) {
2111
+ if (mutation.file === "CLAUDE.md") {
2112
+ return translateClaudeMdMutation(mutation, ir);
2113
+ }
2114
+ const commandName = extractName(mutation.file, COMMANDS_PATH_RE);
2115
+ if (commandName !== null) {
2116
+ return translateCommandMutation(mutation, commandName);
2117
+ }
2118
+ const ruleName = extractName(mutation.file, RULES_PATH_RE);
2119
+ if (ruleName !== null) {
2120
+ return translateRuleMutation(mutation, ruleName);
2121
+ }
2122
+ const agentName = extractName(mutation.file, AGENTS_PATH_RE);
2123
+ if (agentName !== null) {
2124
+ return translateAgentMutation(mutation, agentName);
2125
+ }
2126
+ return buildRawText(mutation);
2127
+ }
2128
+ function translateMutations(mutations, ir) {
2129
+ return mutations.map((m) => translateMutation(m, ir));
2130
+ }
2131
+ var COMMANDS_PATH_RE, RULES_PATH_RE, AGENTS_PATH_RE;
2132
+ var init_translate = __esm({
2133
+ "src/ir/translate.ts"() {
2134
+ "use strict";
2135
+ init_types();
2136
+ init_parser();
2137
+ COMMANDS_PATH_RE = /^commands\/([^/]+?)(?:\.md)?$/;
2138
+ RULES_PATH_RE = /^rules\/([^/]+?)(?:\.md)?$/;
2139
+ AGENTS_PATH_RE = /^agents\/([^/]+?)(?:\.md)?$/;
2140
+ }
2141
+ });
2142
+
2143
+ // src/ir/mutations.ts
2144
+ function sectionExists(ir, id) {
2145
+ return ir.sections.some((s) => s.id === id);
2146
+ }
2147
+ function commandExists(ir, name) {
2148
+ return ir.commands.some((c) => c.name === name);
2149
+ }
2150
+ function ruleExists(ir, name) {
2151
+ return ir.rules.some((r) => r.name === name);
2152
+ }
2153
+ function agentExists(ir, name) {
2154
+ return ir.agents.some((a) => a.name === name);
2155
+ }
2156
+ function mcpServerExists(ir, id) {
2157
+ return ir.mcpServers.some((s) => s.id === id);
2158
+ }
2159
+ function deepSet(obj, segments, value) {
2160
+ if (segments.length === 0) return obj;
2161
+ const [head, ...rest] = segments;
2162
+ if (rest.length === 0) {
2163
+ return { ...obj, [head]: value };
2164
+ }
2165
+ const existing = obj[head];
2166
+ const child = existing !== null && typeof existing === "object" && !Array.isArray(existing) ? existing : {};
2167
+ return { ...obj, [head]: deepSet(child, rest, value) };
2168
+ }
2169
+ function applySettingsUpdate(settings, path31, value) {
2170
+ const segments = path31.split(".");
2171
+ const topKey = segments[0];
2172
+ if (STRUCTURED_SETTINGS_KEYS.has(topKey)) {
2173
+ const settingsRecord = { ...settings };
2174
+ const updated = deepSet(settingsRecord, segments, value);
2175
+ return {
2176
+ ...settings,
2177
+ ...updated,
2178
+ // Preserve raw as-is (deepSet may have overwritten it if path happened to be "raw")
2179
+ raw: settings.raw
2180
+ };
2181
+ }
2182
+ const updatedRaw = deepSet({ ...settings.raw }, segments, value);
2183
+ return {
2184
+ ...settings,
2185
+ raw: updatedRaw
2186
+ };
2187
+ }
2188
+ function applyIRMutation(ir, mutation) {
2189
+ switch (mutation.type) {
2190
+ // -- Sections ----------------------------------------------------------
2191
+ case "update_section": {
2192
+ if (!sectionExists(ir, mutation.sectionId)) {
2193
+ throw new Error(`Section '${mutation.sectionId}' not found`);
2194
+ }
2195
+ return {
2196
+ ...ir,
2197
+ sections: ir.sections.map(
2198
+ (s) => s.id === mutation.sectionId ? { ...s, content: mutation.content } : s
2199
+ )
2200
+ };
2201
+ }
2202
+ case "add_section": {
2203
+ if (sectionExists(ir, mutation.section.id)) {
2204
+ throw new Error(`Section '${mutation.section.id}' already exists`);
2205
+ }
2206
+ return {
2207
+ ...ir,
2208
+ sections: [...ir.sections, { ...mutation.section }]
2209
+ };
2210
+ }
2211
+ case "remove_section": {
2212
+ if (!sectionExists(ir, mutation.sectionId)) {
2213
+ throw new Error(`Section '${mutation.sectionId}' not found`);
2214
+ }
2215
+ return {
2216
+ ...ir,
2217
+ sections: ir.sections.filter((s) => s.id !== mutation.sectionId)
2218
+ };
2219
+ }
2220
+ case "reorder_section": {
2221
+ if (!sectionExists(ir, mutation.sectionId)) {
2222
+ throw new Error(`Section '${mutation.sectionId}' not found`);
2223
+ }
2224
+ return {
2225
+ ...ir,
2226
+ sections: ir.sections.map(
2227
+ (s) => s.id === mutation.sectionId ? { ...s, order: mutation.newOrder } : s
2228
+ )
2229
+ };
2230
+ }
2231
+ // -- Commands ----------------------------------------------------------
2232
+ case "add_command": {
2233
+ if (commandExists(ir, mutation.command.name)) {
2234
+ throw new Error(`Command '${mutation.command.name}' already exists`);
2235
+ }
2236
+ return {
2237
+ ...ir,
2238
+ commands: [...ir.commands, { ...mutation.command }]
2239
+ };
2240
+ }
2241
+ case "update_command": {
2242
+ if (!commandExists(ir, mutation.name)) {
2243
+ throw new Error(`Command '${mutation.name}' not found`);
2244
+ }
2245
+ return {
2246
+ ...ir,
2247
+ commands: ir.commands.map(
2248
+ (c) => c.name === mutation.name ? { ...c, content: mutation.content } : c
2249
+ )
2250
+ };
2251
+ }
2252
+ case "remove_command": {
2253
+ if (!commandExists(ir, mutation.name)) {
2254
+ throw new Error(`Command '${mutation.name}' not found`);
2255
+ }
2256
+ return {
2257
+ ...ir,
2258
+ commands: ir.commands.filter((c) => c.name !== mutation.name)
2259
+ };
2260
+ }
2261
+ // -- Rules -------------------------------------------------------------
2262
+ case "add_rule": {
2263
+ if (ruleExists(ir, mutation.rule.name)) {
2264
+ throw new Error(`Rule '${mutation.rule.name}' already exists`);
2265
+ }
2266
+ return {
2267
+ ...ir,
2268
+ rules: [...ir.rules, { ...mutation.rule }]
2269
+ };
2270
+ }
2271
+ case "update_rule": {
2272
+ if (!ruleExists(ir, mutation.name)) {
2273
+ throw new Error(`Rule '${mutation.name}' not found`);
2274
+ }
2275
+ return {
2276
+ ...ir,
2277
+ rules: ir.rules.map(
2278
+ (r) => r.name === mutation.name ? { ...r, content: mutation.content } : r
2279
+ )
2280
+ };
2281
+ }
2282
+ case "remove_rule": {
2283
+ if (!ruleExists(ir, mutation.name)) {
2284
+ throw new Error(`Rule '${mutation.name}' not found`);
2285
+ }
2286
+ return {
2287
+ ...ir,
2288
+ rules: ir.rules.filter((r) => r.name !== mutation.name)
2289
+ };
2290
+ }
2291
+ // -- Agents ------------------------------------------------------------
2292
+ case "add_agent": {
2293
+ if (agentExists(ir, mutation.agent.name)) {
2294
+ throw new Error(`Agent '${mutation.agent.name}' already exists`);
2295
+ }
2296
+ return {
2297
+ ...ir,
2298
+ agents: [...ir.agents, { ...mutation.agent }]
2299
+ };
2300
+ }
2301
+ case "update_agent": {
2302
+ if (!agentExists(ir, mutation.name)) {
2303
+ throw new Error(`Agent '${mutation.name}' not found`);
2304
+ }
2305
+ return {
2306
+ ...ir,
2307
+ agents: ir.agents.map(
2308
+ (a) => a.name === mutation.name ? { ...a, ...mutation.changes } : a
2309
+ )
2310
+ };
2311
+ }
2312
+ case "remove_agent": {
2313
+ if (!agentExists(ir, mutation.name)) {
2314
+ throw new Error(`Agent '${mutation.name}' not found`);
2315
+ }
2316
+ return {
2317
+ ...ir,
2318
+ agents: ir.agents.filter((a) => a.name !== mutation.name)
2319
+ };
2320
+ }
2321
+ // -- MCP Servers -------------------------------------------------------
2322
+ case "add_mcp_server": {
2323
+ if (mcpServerExists(ir, mutation.server.id)) {
2324
+ throw new Error(`MCP server '${mutation.server.id}' already exists`);
2325
+ }
2326
+ return {
2327
+ ...ir,
2328
+ mcpServers: [...ir.mcpServers, { ...mutation.server }]
2329
+ };
2330
+ }
2331
+ case "remove_mcp_server": {
2332
+ if (!mcpServerExists(ir, mutation.id)) {
2333
+ throw new Error(`MCP server '${mutation.id}' not found`);
2334
+ }
2335
+ return {
2336
+ ...ir,
2337
+ mcpServers: ir.mcpServers.filter((s) => s.id !== mutation.id)
2338
+ };
2339
+ }
2340
+ // -- Settings ----------------------------------------------------------
2341
+ case "update_settings": {
2342
+ return {
2343
+ ...ir,
2344
+ settings: applySettingsUpdate(ir.settings, mutation.path, mutation.value)
2345
+ };
2346
+ }
2347
+ // -- Raw text (legacy fallback) ----------------------------------------
2348
+ case "raw_text": {
2349
+ console.warn(
2350
+ "raw_text mutation is a legacy fallback \u2014 the text operation will be applied during rendering"
2351
+ );
2352
+ return { ...ir };
2353
+ }
2354
+ }
2355
+ }
2356
+ var STRUCTURED_SETTINGS_KEYS;
2357
+ var init_mutations = __esm({
2358
+ "src/ir/mutations.ts"() {
2359
+ "use strict";
2360
+ STRUCTURED_SETTINGS_KEYS = /* @__PURE__ */ new Set(["statusLine", "hooks", "denyPatterns"]);
2361
+ }
2362
+ });
2363
+
2364
+ // src/ir/renderer.ts
2365
+ function renderClaudeMd(_meta, sections) {
2366
+ const sorted = [...sections].sort((a, b) => a.order - b.order);
2367
+ const blocks = [];
2368
+ for (const section of sorted) {
2369
+ if (section.heading && section.content) {
2370
+ blocks.push(`${section.heading}
2371
+
2372
+ ${section.content}`);
2373
+ } else if (section.heading) {
2374
+ blocks.push(section.heading);
2375
+ } else if (section.content) {
2376
+ blocks.push(section.content);
2377
+ }
2378
+ }
2379
+ if (blocks.length === 0) {
2380
+ return "\n";
2381
+ }
2382
+ return blocks.join("\n\n") + "\n";
2383
+ }
2384
+ function renderSettings(settings) {
2385
+ const result = JSON.parse(
2386
+ JSON.stringify(settings.raw)
2387
+ );
2388
+ if (settings.denyPatterns && settings.denyPatterns.length > 0) {
2389
+ const permissions = result["permissions"] ?? {};
2390
+ permissions["deny"] = settings.denyPatterns;
2391
+ result["permissions"] = permissions;
2392
+ }
2393
+ if (settings.statusLine) {
2394
+ result["statusLine"] = settings.statusLine;
2395
+ }
2396
+ const hookEvents = [
2397
+ "PreToolUse",
2398
+ "PostToolUse",
2399
+ "UserPromptSubmit",
2400
+ "SessionStart",
2401
+ "PostCompact"
2402
+ ];
2403
+ const hooksObj = {};
2404
+ let hasHooks = false;
2405
+ for (const event of hookEvents) {
2406
+ const entries = settings.hooks[event];
2407
+ if (entries && entries.length > 0) {
2408
+ hooksObj[event] = entries;
2409
+ hasHooks = true;
2410
+ }
2411
+ }
2412
+ if (hasHooks) {
2413
+ result["hooks"] = hooksObj;
2414
+ }
2415
+ return JSON.stringify(result, null, 2) + "\n";
2416
+ }
2417
+ function renderMcpConfig(servers) {
2418
+ if (servers.length === 0) {
2419
+ return "";
2420
+ }
2421
+ const mcpServers = {};
2422
+ for (const server of servers) {
2423
+ const entry = {
2424
+ command: server.command,
2425
+ args: server.args
2426
+ };
2427
+ if (server.env && Object.keys(server.env).length > 0) {
2428
+ entry["env"] = server.env;
2429
+ }
2430
+ mcpServers[server.id] = entry;
2431
+ }
2432
+ return JSON.stringify({ mcpServers }, null, 2) + "\n";
2433
+ }
2434
+ function renderRuleWithFrontmatter(rule) {
2435
+ if (!rule.paths || rule.paths.length === 0) {
2436
+ return rule.content;
2437
+ }
2438
+ const yamlLines = ["---", "paths:"];
2439
+ for (const p of rule.paths) {
2440
+ yamlLines.push(` - ${p}`);
2441
+ }
2442
+ yamlLines.push("---");
2443
+ return yamlLines.join("\n") + "\n\n" + rule.content;
2444
+ }
2445
+ function renderAgentWithFrontmatter(agent) {
2446
+ const hasModel = agent.model !== void 0;
2447
+ const hasDisallowed = agent.disallowedTools !== void 0 && agent.disallowedTools.length > 0;
2448
+ if (!hasModel && !hasDisallowed) {
2449
+ return agent.content;
2450
+ }
2451
+ const yamlLines = ["---"];
2452
+ if (hasModel) {
2453
+ yamlLines.push(`model: ${agent.model}`);
2454
+ }
2455
+ if (hasDisallowed) {
2456
+ yamlLines.push("disallowedTools:");
2457
+ for (const tool of agent.disallowedTools) {
2458
+ yamlLines.push(` - ${tool}`);
2459
+ }
2460
+ }
2461
+ yamlLines.push("---");
2462
+ return yamlLines.join("\n") + "\n\n" + agent.content;
2463
+ }
2464
+ function settingsHasContent(settings) {
2465
+ if (settings.statusLine) return true;
2466
+ if (settings.denyPatterns && settings.denyPatterns.length > 0) return true;
2467
+ if (Object.keys(settings.raw).length > 0) return true;
2468
+ const hookEvents = [
2469
+ "PreToolUse",
2470
+ "PostToolUse",
2471
+ "UserPromptSubmit",
2472
+ "SessionStart",
2473
+ "PostCompact"
2474
+ ];
2475
+ for (const event of hookEvents) {
2476
+ const entries = settings.hooks[event];
2477
+ if (entries && entries.length > 0) return true;
2478
+ }
2479
+ return false;
2480
+ }
2481
+ function renderHarness(ir) {
2482
+ const files = /* @__PURE__ */ new Map();
2483
+ if (ir.sections.length > 0 || ir.meta.name) {
2484
+ files.set("CLAUDE.md", renderClaudeMd(ir.meta, ir.sections));
2485
+ }
2486
+ if (settingsHasContent(ir.settings)) {
2487
+ files.set("settings.json", renderSettings(ir.settings));
2488
+ }
2489
+ for (const cmd of ir.commands) {
2490
+ files.set(`commands/${cmd.name}.md`, cmd.content);
2491
+ }
2492
+ for (const rule of ir.rules) {
2493
+ files.set(`rules/${rule.name}.md`, renderRuleWithFrontmatter(rule));
2494
+ }
2495
+ for (const agent of ir.agents) {
2496
+ files.set(`agents/${agent.name}.md`, renderAgentWithFrontmatter(agent));
2497
+ }
2498
+ for (const skill of ir.skills) {
2499
+ files.set(`skills/${skill.name}.md`, skill.content);
2500
+ }
2501
+ for (const doc of ir.docs) {
2502
+ files.set(`docs/${doc.name}.md`, doc.content);
2503
+ }
2504
+ for (const hook of ir.hooks) {
2505
+ files.set(`hooks/${hook.name}.mjs`, hook.content);
2506
+ }
2507
+ const mcpContent = renderMcpConfig(ir.mcpServers);
2508
+ if (mcpContent) {
2509
+ files.set(".mcp.json", mcpContent);
2510
+ }
2511
+ return files;
2512
+ }
2513
+ var init_renderer = __esm({
2514
+ "src/ir/renderer.ts"() {
2515
+ "use strict";
2516
+ }
2517
+ });
2518
+
2519
+ // src/ir/diff.ts
2520
+ function diffIR(before, after) {
2521
+ const diff = createEmptyDiff();
2522
+ diffSections(before.sections, after.sections, diff);
2523
+ diffByName(before.commands, after.commands, diff.commands);
2524
+ diffByName(before.rules, after.rules, diff.rules);
2525
+ diffAgents(before.agents, after.agents, diff);
2526
+ diffMcpServers(before.mcpServers, after.mcpServers, diff);
2527
+ diffSettings(before.settings, after.settings, diff);
2528
+ return diff;
2529
+ }
2530
+ function formatIRDiff(diff) {
2531
+ const blocks = [];
2532
+ const sectionLines = formatSectionBlock(diff);
2533
+ if (sectionLines.length > 0) {
2534
+ blocks.push(["Sections:", ...sectionLines].join("\n"));
2535
+ }
2536
+ const commandLines = formatNamedBlock(diff.commands, "commands");
2537
+ if (commandLines.length > 0) {
2538
+ blocks.push(["Commands:", ...commandLines].join("\n"));
2539
+ }
2540
+ const ruleLines = formatNamedBlock(diff.rules, "rules");
2541
+ if (ruleLines.length > 0) {
2542
+ blocks.push(["Rules:", ...ruleLines].join("\n"));
2543
+ }
2544
+ const agentLines = formatAgentBlock(diff);
2545
+ if (agentLines.length > 0) {
2546
+ blocks.push(["Agents:", ...agentLines].join("\n"));
2547
+ }
2548
+ const mcpLines = formatMcpBlock(diff);
2549
+ if (mcpLines.length > 0) {
2550
+ blocks.push(["MCP Servers:", ...mcpLines].join("\n"));
2551
+ }
2552
+ const settingsLines = formatSettingsBlock(diff);
2553
+ if (settingsLines.length > 0) {
2554
+ blocks.push(["Settings:", ...settingsLines].join("\n"));
2555
+ }
2556
+ if (blocks.length === 0) {
2557
+ return "No changes.";
2558
+ }
2559
+ return blocks.join("\n\n");
2560
+ }
2561
+ function diffSections(beforeList, afterList, diff) {
2562
+ const beforeMap = /* @__PURE__ */ new Map();
2563
+ for (const s of beforeList) {
2564
+ beforeMap.set(s.id, s);
2565
+ }
2566
+ const afterMap = /* @__PURE__ */ new Map();
2567
+ for (const s of afterList) {
2568
+ afterMap.set(s.id, s);
2569
+ }
2570
+ for (const s of afterList) {
2571
+ if (!beforeMap.has(s.id)) {
2572
+ diff.sections.added.push(s);
2573
+ }
2574
+ }
2575
+ for (const s of beforeList) {
2576
+ if (!afterMap.has(s.id)) {
2577
+ diff.sections.removed.push(s);
2578
+ }
2579
+ }
2580
+ for (const [id, afterSection] of afterMap) {
2581
+ const beforeSection = beforeMap.get(id);
2582
+ if (beforeSection === void 0) continue;
2583
+ if (beforeSection.content !== afterSection.content) {
2584
+ diff.sections.modified.push({
2585
+ id,
2586
+ before: beforeSection.content,
2587
+ after: afterSection.content
2588
+ });
2589
+ }
2590
+ if (beforeSection.order !== afterSection.order) {
2591
+ diff.sections.reordered.push({
2592
+ id,
2593
+ oldOrder: beforeSection.order,
2594
+ newOrder: afterSection.order
2595
+ });
2596
+ }
2597
+ }
2598
+ }
2599
+ function diffByName(beforeList, afterList, target) {
2600
+ const beforeMap = /* @__PURE__ */ new Map();
2601
+ for (const n of beforeList) {
2602
+ beforeMap.set(n.name, n);
2603
+ }
2604
+ const afterMap = /* @__PURE__ */ new Map();
2605
+ for (const n of afterList) {
2606
+ afterMap.set(n.name, n);
2607
+ }
2608
+ for (const n of afterList) {
2609
+ if (!beforeMap.has(n.name)) {
2610
+ target.added.push(n);
2611
+ }
2612
+ }
2613
+ for (const n of beforeList) {
2614
+ if (!afterMap.has(n.name)) {
2615
+ target.removed.push(n.name);
2616
+ }
2617
+ }
2618
+ for (const [name, afterNode] of afterMap) {
2619
+ const beforeNode = beforeMap.get(name);
2620
+ if (beforeNode === void 0) continue;
2621
+ if (beforeNode.content !== afterNode.content) {
2622
+ target.modified.push({
2623
+ name,
2624
+ before: beforeNode.content,
2625
+ after: afterNode.content
2626
+ });
2627
+ }
2628
+ }
2629
+ }
2630
+ function diffAgents(beforeList, afterList, diff) {
2631
+ const beforeMap = /* @__PURE__ */ new Map();
2632
+ for (const a of beforeList) {
2633
+ beforeMap.set(a.name, a);
2634
+ }
2635
+ const afterMap = /* @__PURE__ */ new Map();
2636
+ for (const a of afterList) {
2637
+ afterMap.set(a.name, a);
2638
+ }
2639
+ for (const a of afterList) {
2640
+ if (!beforeMap.has(a.name)) {
2641
+ diff.agents.added.push(a);
2642
+ }
2643
+ }
2644
+ for (const a of beforeList) {
2645
+ if (!afterMap.has(a.name)) {
2646
+ diff.agents.removed.push(a.name);
2647
+ }
2648
+ }
2649
+ for (const [name, afterAgent] of afterMap) {
2650
+ const beforeAgent = beforeMap.get(name);
2651
+ if (beforeAgent === void 0) continue;
2652
+ const changeParts = [];
2653
+ if (beforeAgent.model !== afterAgent.model) {
2654
+ const from = beforeAgent.model ?? "none";
2655
+ const to = afterAgent.model ?? "none";
2656
+ changeParts.push(`model changed from ${from} to ${to}`);
2657
+ }
2658
+ if (beforeAgent.content !== afterAgent.content) {
2659
+ changeParts.push("content updated");
2660
+ }
2661
+ const beforeTools = JSON.stringify(beforeAgent.disallowedTools ?? []);
2662
+ const afterTools = JSON.stringify(afterAgent.disallowedTools ?? []);
2663
+ if (beforeTools !== afterTools) {
2664
+ changeParts.push("disallowedTools changed");
2665
+ }
2666
+ if (changeParts.length > 0) {
2667
+ diff.agents.modified.push({
2668
+ name,
2669
+ changes: changeParts.join("; ")
2670
+ });
2671
+ }
2672
+ }
2673
+ }
2674
+ function diffMcpServers(beforeList, afterList, diff) {
2675
+ const beforeIds = new Set(beforeList.map((s) => s.id));
2676
+ const afterIds = new Set(afterList.map((s) => s.id));
2677
+ for (const s of afterList) {
2678
+ if (!beforeIds.has(s.id)) {
2679
+ diff.mcpServers.added.push(s);
2680
+ }
2681
+ }
2682
+ for (const s of beforeList) {
2683
+ if (!afterIds.has(s.id)) {
2684
+ diff.mcpServers.removed.push(s.id);
2685
+ }
2686
+ }
2687
+ }
2688
+ function diffSettings(before, after, diff) {
2689
+ if (!deepEqual(before.statusLine, after.statusLine)) {
2690
+ diff.settings.changes.push({
2691
+ path: "statusLine",
2692
+ before: before.statusLine,
2693
+ after: after.statusLine
2694
+ });
2695
+ }
2696
+ if (!deepEqual(before.denyPatterns, after.denyPatterns)) {
2697
+ diff.settings.changes.push({
2698
+ path: "denyPatterns",
2699
+ before: before.denyPatterns,
2700
+ after: after.denyPatterns
2701
+ });
2702
+ }
2703
+ if (!deepEqual(before.hooks, after.hooks)) {
2704
+ diff.settings.changes.push({
2705
+ path: "hooks",
2706
+ before: before.hooks,
2707
+ after: after.hooks
2708
+ });
2709
+ }
2710
+ }
2711
+ function formatSectionBlock(diff) {
2712
+ const lines = [];
2713
+ for (const s of diff.sections.added) {
2714
+ lines.push(` + Added: ${s.heading}`);
2715
+ }
2716
+ for (const s of diff.sections.removed) {
2717
+ lines.push(` - Removed: ${s.heading}`);
2718
+ }
2719
+ for (const m of diff.sections.modified) {
2720
+ lines.push(` ~ Modified: ${m.id} (content changed)`);
2721
+ }
2722
+ for (const r of diff.sections.reordered) {
2723
+ lines.push(
2724
+ ` \u2195 Reordered: ${r.id} (${r.oldOrder} \u2192 ${r.newOrder})`
2725
+ );
2726
+ }
2727
+ return lines;
2728
+ }
2729
+ function formatNamedBlock(bucket, _category) {
2730
+ const lines = [];
2731
+ for (const n of bucket.added) {
2732
+ lines.push(` + Added: ${n.name}`);
2733
+ }
2734
+ for (const name of bucket.removed) {
2735
+ lines.push(` - Removed: ${name}`);
2736
+ }
2737
+ for (const m of bucket.modified) {
2738
+ lines.push(` ~ Modified: ${m.name} (content changed)`);
2739
+ }
2740
+ return lines;
2741
+ }
2742
+ function formatAgentBlock(diff) {
2743
+ const lines = [];
2744
+ for (const a of diff.agents.added) {
2745
+ lines.push(` + Added: ${a.name}`);
2746
+ }
2747
+ for (const name of diff.agents.removed) {
2748
+ lines.push(` - Removed: ${name}`);
2749
+ }
2750
+ for (const m of diff.agents.modified) {
2751
+ lines.push(` ~ Modified: ${m.name} (${m.changes})`);
2752
+ }
2753
+ return lines;
2754
+ }
2755
+ function formatMcpBlock(diff) {
2756
+ const lines = [];
2757
+ for (const s of diff.mcpServers.added) {
2758
+ lines.push(` + Added: ${s.id}`);
2759
+ }
2760
+ for (const id of diff.mcpServers.removed) {
2761
+ lines.push(` - Removed: ${id}`);
2762
+ }
2763
+ return lines;
2764
+ }
2765
+ function formatSettingsBlock(diff) {
2766
+ const lines = [];
2767
+ for (const c of diff.settings.changes) {
2768
+ lines.push(` ~ ${c.path} changed`);
2769
+ }
2770
+ return lines;
2771
+ }
2772
+ function deepEqual(a, b) {
2773
+ return JSON.stringify(a) === JSON.stringify(b);
2774
+ }
2775
+ var init_diff = __esm({
2776
+ "src/ir/diff.ts"() {
2777
+ "use strict";
2778
+ init_types();
2779
+ }
2780
+ });
2781
+
1412
2782
  // src/evolve/mutator.ts
1413
- import fs21 from "fs/promises";
1414
- import path21 from "path";
2783
+ import fs22 from "fs/promises";
2784
+ import path22 from "path";
1415
2785
  async function applyMutations(currentHarnessPath, nextIterationDir, mutations) {
1416
- const newHarnessPath = path21.join(nextIterationDir, "harness");
2786
+ const newHarnessPath = path22.join(nextIterationDir, "harness");
2787
+ let baselineIR = null;
2788
+ try {
2789
+ baselineIR = await parseHarness(currentHarnessPath);
2790
+ } catch {
2791
+ }
2792
+ if (baselineIR !== null) {
2793
+ return applyMutationsViaIR(
2794
+ currentHarnessPath,
2795
+ newHarnessPath,
2796
+ mutations,
2797
+ baselineIR
2798
+ );
2799
+ }
2800
+ return applyMutationsLegacy(currentHarnessPath, newHarnessPath, mutations);
2801
+ }
2802
+ async function applyMutationsViaIR(currentHarnessPath, newHarnessPath, mutations, baselineIR) {
1417
2803
  await copyDir(currentHarnessPath, newHarnessPath);
1418
- for (const mutation of mutations) {
1419
- if (mutation.file.includes("..")) {
2804
+ const irMutations = translateMutations(mutations, baselineIR);
2805
+ let currentIR = baselineIR;
2806
+ const rawTextMutations = [];
2807
+ const touchedCategories = /* @__PURE__ */ new Set();
2808
+ for (let i = 0; i < irMutations.length; i++) {
2809
+ const irMut = irMutations[i];
2810
+ if (irMut.type === "raw_text") {
2811
+ rawTextMutations.push(mutations[i]);
1420
2812
  continue;
1421
2813
  }
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(
2814
+ try {
2815
+ currentIR = applyIRMutation(currentIR, irMut);
2816
+ touchedCategories.add(getMutationCategory(irMut.type));
2817
+ } catch {
2818
+ continue;
2819
+ }
2820
+ }
2821
+ if (touchedCategories.size > 0) {
2822
+ await renderAffectedFiles(currentIR, newHarnessPath, touchedCategories);
2823
+ }
2824
+ for (const mutation of rawTextMutations) {
2825
+ await applyLegacyMutation(newHarnessPath, mutation);
2826
+ }
2827
+ const irDiff = diffIR(baselineIR, currentIR);
2828
+ let diffPatch = formatIRDiff(irDiff);
2829
+ if (diffPatch === "No changes." && rawTextMutations.length > 0) {
2830
+ diffPatch = await generateDiffLegacy(currentHarnessPath, newHarnessPath);
2831
+ }
2832
+ if (diffPatch === "No changes.") {
2833
+ diffPatch = "";
2834
+ }
2835
+ return { newHarnessPath, diffPatch };
2836
+ }
2837
+ function getMutationCategory(mutationType) {
2838
+ if (mutationType.includes("section") || mutationType.includes("reorder")) {
2839
+ return "claude_md";
2840
+ }
2841
+ if (mutationType.includes("command")) {
2842
+ return "commands";
2843
+ }
2844
+ if (mutationType.includes("rule")) {
2845
+ return "rules";
2846
+ }
2847
+ if (mutationType.includes("agent")) {
2848
+ return "agents";
2849
+ }
2850
+ if (mutationType.includes("mcp")) {
2851
+ return "mcp";
2852
+ }
2853
+ if (mutationType.includes("settings")) {
2854
+ return "settings";
2855
+ }
2856
+ return "unknown";
2857
+ }
2858
+ async function renderAffectedFiles(ir, targetDir, touchedCategories) {
2859
+ const fileMap = renderHarness(ir);
2860
+ for (const [relativePath, content] of fileMap) {
2861
+ const category = getFileCategory(relativePath);
2862
+ if (touchedCategories.has(category)) {
2863
+ const fullPath = path22.join(targetDir, relativePath);
2864
+ await fs22.mkdir(path22.dirname(fullPath), { recursive: true });
2865
+ await fs22.writeFile(fullPath, content, "utf-8");
2866
+ }
2867
+ }
2868
+ if (touchedCategories.has("commands")) {
2869
+ await deleteOrphanedFiles(targetDir, "commands", fileMap);
2870
+ }
2871
+ if (touchedCategories.has("rules")) {
2872
+ await deleteOrphanedFiles(targetDir, "rules", fileMap);
2873
+ }
2874
+ if (touchedCategories.has("agents")) {
2875
+ await deleteOrphanedFiles(targetDir, "agents", fileMap);
2876
+ }
2877
+ }
2878
+ function getFileCategory(relativePath) {
2879
+ if (relativePath === "CLAUDE.md") return "claude_md";
2880
+ if (relativePath.startsWith("commands/")) return "commands";
2881
+ if (relativePath.startsWith("rules/")) return "rules";
2882
+ if (relativePath.startsWith("agents/")) return "agents";
2883
+ if (relativePath.startsWith("skills/")) return "skills";
2884
+ if (relativePath.startsWith("docs/")) return "docs";
2885
+ if (relativePath.startsWith("hooks/")) return "hooks";
2886
+ if (relativePath === "settings.json") return "settings";
2887
+ if (relativePath === ".mcp.json") return "mcp";
2888
+ return "unknown";
2889
+ }
2890
+ async function deleteOrphanedFiles(targetDir, subdir, renderedMap) {
2891
+ const subdirPath = path22.join(targetDir, subdir);
2892
+ let entries;
2893
+ try {
2894
+ entries = await fs22.readdir(subdirPath);
2895
+ } catch {
2896
+ return;
2897
+ }
2898
+ for (const entry of entries) {
2899
+ const relativePath = `${subdir}/${entry}`;
2900
+ if (!renderedMap.has(relativePath)) {
2901
+ await fs22.unlink(path22.join(subdirPath, entry)).catch(() => {
2902
+ });
2903
+ }
2904
+ }
2905
+ }
2906
+ async function applyMutationsLegacy(currentHarnessPath, newHarnessPath, mutations) {
2907
+ await copyDir(currentHarnessPath, newHarnessPath);
2908
+ for (const mutation of mutations) {
2909
+ await applyLegacyMutation(newHarnessPath, mutation);
2910
+ }
2911
+ const diffPatch = await generateDiffLegacy(currentHarnessPath, newHarnessPath);
2912
+ return { newHarnessPath, diffPatch };
2913
+ }
2914
+ async function applyLegacyMutation(harnessPath, mutation) {
2915
+ if (mutation.file.includes("..")) {
2916
+ return;
2917
+ }
2918
+ const filePath = path22.join(harnessPath, mutation.file);
2919
+ if (mutation.action === "replace") {
2920
+ if (!mutation.oldText) {
2921
+ return;
2922
+ }
2923
+ let content;
2924
+ try {
2925
+ content = await fs22.readFile(filePath, "utf-8");
2926
+ } catch {
2927
+ return;
2928
+ }
2929
+ if (!content.includes(mutation.oldText)) {
2930
+ return;
2931
+ }
2932
+ await fs22.writeFile(
2933
+ filePath,
2934
+ content.replace(mutation.oldText, mutation.newText),
2935
+ "utf-8"
2936
+ );
2937
+ } else if (mutation.action === "add_section") {
2938
+ try {
2939
+ const content = await fs22.readFile(filePath, "utf-8");
2940
+ await fs22.writeFile(
1432
2941
  filePath,
1433
- content.replace(mutation.oldText, mutation.newText),
2942
+ content + "\n\n" + mutation.newText,
1434
2943
  "utf-8"
1435
2944
  );
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;
2945
+ } catch {
2946
+ await fs22.mkdir(path22.dirname(filePath), { recursive: true });
2947
+ await fs22.writeFile(filePath, mutation.newText, "utf-8");
2948
+ }
2949
+ } else if (mutation.action === "create_file") {
2950
+ await fs22.mkdir(path22.dirname(filePath), { recursive: true });
2951
+ await fs22.writeFile(filePath, mutation.newText, "utf-8");
2952
+ } else if (mutation.action === "delete_section") {
2953
+ if (!mutation.oldText) {
2954
+ return;
2955
+ }
2956
+ let sectionContent;
2957
+ try {
2958
+ sectionContent = await fs22.readFile(filePath, "utf-8");
2959
+ } catch {
2960
+ return;
2961
+ }
2962
+ if (!sectionContent.includes(mutation.oldText)) {
2963
+ return;
2964
+ }
2965
+ await fs22.writeFile(filePath, sectionContent.replace(mutation.oldText, ""), "utf-8");
2966
+ } else if (mutation.action === "delete_file") {
2967
+ await fs22.unlink(filePath).catch(() => {
2968
+ });
2969
+ }
2970
+ }
2971
+ async function generateDiff2(oldDir, newDir) {
2972
+ try {
2973
+ const oldIR = await parseHarness(oldDir);
2974
+ const newIR = await parseHarness(newDir);
2975
+ const oldHasContent = oldIR.sections.length > 0 || oldIR.commands.length > 0 || oldIR.rules.length > 0 || oldIR.agents.length > 0;
2976
+ const newHasContent = newIR.sections.length > 0 || newIR.commands.length > 0 || newIR.rules.length > 0 || newIR.agents.length > 0;
2977
+ if (oldHasContent || newHasContent) {
2978
+ const irDiff = diffIR(oldIR, newIR);
2979
+ const formatted = formatIRDiff(irDiff);
2980
+ if (formatted === "No changes.") {
2981
+ const legacyDiff2 = await generateDiffLegacy(oldDir, newDir);
2982
+ return legacyDiff2;
1460
2983
  }
1461
- if (!sectionContent.includes(mutation.oldText)) {
1462
- continue;
2984
+ const legacyDiff = await generateDiffLegacy(oldDir, newDir);
2985
+ if (legacyDiff && !formatted.includes(legacyDiff)) {
2986
+ return formatted + "\n\n" + legacyDiff;
1463
2987
  }
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
- });
2988
+ return formatted;
1468
2989
  }
2990
+ } catch {
1469
2991
  }
1470
- const diffPatch = await generateDiff2(currentHarnessPath, newHarnessPath);
1471
- return { newHarnessPath, diffPatch };
2992
+ return generateDiffLegacy(oldDir, newDir);
1472
2993
  }
1473
- async function generateDiff2(oldDir, newDir) {
2994
+ async function generateDiffLegacy(oldDir, newDir) {
1474
2995
  const oldFiles = await readAllFiles(oldDir);
1475
2996
  const newFiles = await readAllFiles(newDir);
1476
2997
  const allPaths = /* @__PURE__ */ new Set([
@@ -1511,17 +3032,17 @@ async function readAllFiles(dir) {
1511
3032
  async function walk(current) {
1512
3033
  let entries;
1513
3034
  try {
1514
- entries = await fs21.readdir(current, { withFileTypes: true });
3035
+ entries = await fs22.readdir(current, { withFileTypes: true });
1515
3036
  } catch {
1516
3037
  return;
1517
3038
  }
1518
3039
  for (const entry of entries) {
1519
- const fullPath = path21.join(current, entry.name);
1520
- const relativePath = path21.relative(dir, fullPath);
3040
+ const fullPath = path22.join(current, entry.name);
3041
+ const relativePath = path22.relative(dir, fullPath);
1521
3042
  if (entry.isDirectory()) {
1522
3043
  await walk(fullPath);
1523
3044
  } else {
1524
- result[relativePath] = await fs21.readFile(fullPath, "utf-8");
3045
+ result[relativePath] = await fs22.readFile(fullPath, "utf-8");
1525
3046
  }
1526
3047
  }
1527
3048
  }
@@ -1532,12 +3053,17 @@ var init_mutator = __esm({
1532
3053
  "src/evolve/mutator.ts"() {
1533
3054
  "use strict";
1534
3055
  init_baseline();
3056
+ init_parser();
3057
+ init_translate();
3058
+ init_mutations();
3059
+ init_renderer();
3060
+ init_diff();
1535
3061
  }
1536
3062
  });
1537
3063
 
1538
3064
  // src/evolve/sampling.ts
1539
- import fs22 from "fs/promises";
1540
- import path22 from "path";
3065
+ import fs23 from "fs/promises";
3066
+ import path23 from "path";
1541
3067
  function initBeliefs(tasks) {
1542
3068
  return tasks.map((task) => ({
1543
3069
  taskId: task.id,
@@ -1598,9 +3124,9 @@ function updateBeliefs(beliefs, results) {
1598
3124
  });
1599
3125
  }
1600
3126
  async function loadBeliefs(workspacePath) {
1601
- const beliefsPath = path22.join(workspacePath, "task-beliefs.json");
3127
+ const beliefsPath = path23.join(workspacePath, "task-beliefs.json");
1602
3128
  try {
1603
- const content = await fs22.readFile(beliefsPath, "utf-8");
3129
+ const content = await fs23.readFile(beliefsPath, "utf-8");
1604
3130
  const parsed = JSON.parse(content);
1605
3131
  if (!Array.isArray(parsed)) return null;
1606
3132
  for (const entry of parsed) {
@@ -1614,9 +3140,9 @@ async function loadBeliefs(workspacePath) {
1614
3140
  }
1615
3141
  }
1616
3142
  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");
3143
+ const beliefsPath = path23.join(workspacePath, "task-beliefs.json");
3144
+ await fs23.mkdir(path23.dirname(beliefsPath), { recursive: true });
3145
+ await fs23.writeFile(beliefsPath, JSON.stringify(beliefs, null, 2), "utf-8");
1620
3146
  }
1621
3147
  var init_sampling = __esm({
1622
3148
  "src/evolve/sampling.ts"() {
@@ -1625,8 +3151,8 @@ var init_sampling = __esm({
1625
3151
  });
1626
3152
 
1627
3153
  // src/evolve/regularization.ts
1628
- import fs23 from "fs/promises";
1629
- import path23 from "path";
3154
+ import fs24 from "fs/promises";
3155
+ import path24 from "path";
1630
3156
  async function measureComplexity(harnessPath) {
1631
3157
  let totalLines = 0;
1632
3158
  let totalFiles = 0;
@@ -1657,6 +3183,64 @@ async function measureComplexity(harnessPath) {
1657
3183
  diffFromBaseline: 0
1658
3184
  };
1659
3185
  }
3186
+ function measureComplexityFromIR(ir) {
3187
+ const totalSections = ir.sections.length;
3188
+ const totalRules = ir.rules.length;
3189
+ const totalCommands = ir.commands.length;
3190
+ let totalFiles = 0;
3191
+ if (ir.sections.length > 0 || ir.meta.name) {
3192
+ totalFiles += 1;
3193
+ }
3194
+ totalFiles += ir.commands.length;
3195
+ totalFiles += ir.rules.length;
3196
+ totalFiles += ir.agents.length;
3197
+ totalFiles += ir.skills.length;
3198
+ totalFiles += ir.docs.length;
3199
+ totalFiles += ir.hooks.length;
3200
+ 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(
3201
+ (entries) => entries !== void 0 && entries.length > 0
3202
+ );
3203
+ if (hasSettings) {
3204
+ totalFiles += 1;
3205
+ }
3206
+ if (ir.mcpServers.length > 0) {
3207
+ totalFiles += 1;
3208
+ }
3209
+ let totalLines = 0;
3210
+ for (const section of ir.sections) {
3211
+ totalLines += countLines(section.content);
3212
+ }
3213
+ for (const cmd of ir.commands) {
3214
+ totalLines += countLines(cmd.content);
3215
+ }
3216
+ for (const rule of ir.rules) {
3217
+ totalLines += countLines(rule.content);
3218
+ }
3219
+ for (const agent of ir.agents) {
3220
+ totalLines += countLines(agent.content);
3221
+ }
3222
+ for (const skill of ir.skills) {
3223
+ totalLines += countLines(skill.content);
3224
+ }
3225
+ for (const doc of ir.docs) {
3226
+ totalLines += countLines(doc.content);
3227
+ }
3228
+ for (const hook of ir.hooks) {
3229
+ totalLines += countLines(hook.content);
3230
+ }
3231
+ return {
3232
+ totalLines,
3233
+ totalFiles,
3234
+ totalSections,
3235
+ totalRules,
3236
+ totalCommands,
3237
+ diffFromBaseline: 0
3238
+ };
3239
+ }
3240
+ function countLines(content) {
3241
+ if (!content) return 0;
3242
+ return content.split("\n").length;
3243
+ }
1660
3244
  function computeComplexityCost(current, baseline) {
1661
3245
  const baselineLines = Math.max(baseline.totalLines, 1);
1662
3246
  const lineDelta = (current.totalLines - baseline.totalLines) / baselineLines;
@@ -1702,18 +3286,18 @@ async function readAllFilesRecursive(dir) {
1702
3286
  async function walk(current) {
1703
3287
  let entries;
1704
3288
  try {
1705
- entries = await fs23.readdir(current, { withFileTypes: true });
3289
+ entries = await fs24.readdir(current, { withFileTypes: true });
1706
3290
  } catch {
1707
3291
  return;
1708
3292
  }
1709
3293
  for (const entry of entries) {
1710
- const fullPath = path23.join(current, entry.name);
1711
- const relativePath = path23.relative(dir, fullPath);
3294
+ const fullPath = path24.join(current, entry.name);
3295
+ const relativePath = path24.relative(dir, fullPath);
1712
3296
  if (entry.isDirectory()) {
1713
3297
  await walk(fullPath);
1714
3298
  } else {
1715
3299
  try {
1716
- result[relativePath] = await fs23.readFile(fullPath, "utf-8");
3300
+ result[relativePath] = await fs24.readFile(fullPath, "utf-8");
1717
3301
  } catch {
1718
3302
  }
1719
3303
  }
@@ -1729,8 +3313,8 @@ var init_regularization = __esm({
1729
3313
  });
1730
3314
 
1731
3315
  // src/evolve/loop.ts
1732
- import fs24 from "fs/promises";
1733
- import path24 from "path";
3316
+ import fs25 from "fs/promises";
3317
+ import path25 from "path";
1734
3318
  function computeMutationCap(iter, maxIterations, maxMutations) {
1735
3319
  if (maxIterations <= 1) return maxMutations;
1736
3320
  const progress = iter / (maxIterations - 1);
@@ -1747,11 +3331,17 @@ async function evolve(workspacePath, tasks, kairnConfig, evolveConfig, onProgres
1747
3331
  let beliefs = useThompson ? await loadBeliefs(workspacePath) ?? initBeliefs(tasks) : [];
1748
3332
  const useKL = evolveConfig.klLambda > 0;
1749
3333
  let baselineComplexity = null;
3334
+ let baselineIR = null;
1750
3335
  if (useKL) {
1751
- const baselineHarness = path24.join(workspacePath, "iterations", "0", "harness");
3336
+ const baselineHarness = path25.join(workspacePath, "iterations", "0", "harness");
1752
3337
  try {
1753
- baselineComplexity = await measureComplexity(baselineHarness);
3338
+ baselineIR = await parseHarness(baselineHarness);
3339
+ baselineComplexity = measureComplexityFromIR(baselineIR);
1754
3340
  } catch {
3341
+ try {
3342
+ baselineComplexity = await measureComplexity(baselineHarness);
3343
+ } catch {
3344
+ }
1755
3345
  }
1756
3346
  }
1757
3347
  let rngState = 42;
@@ -1760,14 +3350,14 @@ async function evolve(workspacePath, tasks, kairnConfig, evolveConfig, onProgres
1760
3350
  return (rngState >>> 0) / 4294967296;
1761
3351
  };
1762
3352
  for (let iter = 0; iter < evolveConfig.maxIterations; iter++) {
1763
- const harnessPath = path24.join(
3353
+ const harnessPath = path25.join(
1764
3354
  workspacePath,
1765
3355
  "iterations",
1766
3356
  iter.toString(),
1767
3357
  "harness"
1768
3358
  );
1769
3359
  try {
1770
- await fs24.access(harnessPath);
3360
+ await fs25.access(harnessPath);
1771
3361
  } catch {
1772
3362
  if (iter === 0) {
1773
3363
  throw new Error(
@@ -1851,10 +3441,16 @@ async function evolve(workspacePath, tasks, kairnConfig, evolveConfig, onProgres
1851
3441
  let aggregate = rawAggregate;
1852
3442
  let iterComplexityCost;
1853
3443
  if (useKL && baselineComplexity) {
1854
- const currentComplexity = await measureComplexity(harnessPath);
3444
+ let currentComplexity;
3445
+ try {
3446
+ const iterIR = await parseHarness(harnessPath);
3447
+ currentComplexity = measureComplexityFromIR(iterIR);
3448
+ } catch {
3449
+ currentComplexity = await measureComplexity(harnessPath);
3450
+ }
1855
3451
  const diffRatio = await computeDiffRatio(
1856
3452
  harnessPath,
1857
- path24.join(workspacePath, "iterations", "0", "harness")
3453
+ path25.join(workspacePath, "iterations", "0", "harness")
1858
3454
  );
1859
3455
  currentComplexity.diffFromBaseline = diffRatio;
1860
3456
  iterComplexityCost = computeComplexityCost(currentComplexity, baselineComplexity);
@@ -1872,7 +3468,12 @@ async function evolve(workspacePath, tasks, kairnConfig, evolveConfig, onProgres
1872
3468
  if (iter === 0) {
1873
3469
  baselineScore = aggregate;
1874
3470
  if (useKL && !baselineComplexity) {
1875
- baselineComplexity = await measureComplexity(harnessPath);
3471
+ try {
3472
+ baselineIR = await parseHarness(harnessPath);
3473
+ baselineComplexity = measureComplexityFromIR(baselineIR);
3474
+ } catch {
3475
+ baselineComplexity = await measureComplexity(harnessPath);
3476
+ }
1876
3477
  }
1877
3478
  }
1878
3479
  let shouldRollback = iter > 0 && aggregate < bestScore;
@@ -1917,7 +3518,7 @@ async function evolve(workspacePath, tasks, kairnConfig, evolveConfig, onProgres
1917
3518
  };
1918
3519
  await writeIterationLog(workspacePath, rollbackLog);
1919
3520
  history.push(rollbackLog);
1920
- const bestHarnessPath = path24.join(
3521
+ const bestHarnessPath = path25.join(
1921
3522
  workspacePath,
1922
3523
  "iterations",
1923
3524
  bestIteration.toString(),
@@ -1942,7 +3543,7 @@ async function evolve(workspacePath, tasks, kairnConfig, evolveConfig, onProgres
1942
3543
  mutations: rollbackProposal.mutations.slice(0, rollbackCap)
1943
3544
  };
1944
3545
  }
1945
- const nextIterDir2 = path24.join(workspacePath, "iterations", (iter + 1).toString());
3546
+ const nextIterDir2 = path25.join(workspacePath, "iterations", (iter + 1).toString());
1946
3547
  await applyMutations(bestHarnessPath, nextIterDir2, rollbackProposal.mutations);
1947
3548
  onProgress?.({
1948
3549
  type: "mutations-applied",
@@ -1950,8 +3551,8 @@ async function evolve(workspacePath, tasks, kairnConfig, evolveConfig, onProgres
1950
3551
  mutationCount: rollbackProposal.mutations.length
1951
3552
  });
1952
3553
  } catch {
1953
- const nextIterDir2 = path24.join(workspacePath, "iterations", (iter + 1).toString());
1954
- await copyDir(bestHarnessPath, path24.join(nextIterDir2, "harness"));
3554
+ const nextIterDir2 = path25.join(workspacePath, "iterations", (iter + 1).toString());
3555
+ await copyDir(bestHarnessPath, path25.join(nextIterDir2, "harness"));
1955
3556
  }
1956
3557
  }
1957
3558
  continue;
@@ -2015,12 +3616,12 @@ async function evolve(workspacePath, tasks, kairnConfig, evolveConfig, onProgres
2015
3616
  iteration: iter,
2016
3617
  message: `Proposer failed: ${errMsg}`
2017
3618
  });
2018
- const nextIterDir2 = path24.join(
3619
+ const nextIterDir2 = path25.join(
2019
3620
  workspacePath,
2020
3621
  "iterations",
2021
3622
  (iter + 1).toString()
2022
3623
  );
2023
- await copyDir(harnessPath, path24.join(nextIterDir2, "harness"));
3624
+ await copyDir(harnessPath, path25.join(nextIterDir2, "harness"));
2024
3625
  const skipLog = {
2025
3626
  iteration: iter,
2026
3627
  score: aggregate,
@@ -2035,7 +3636,7 @@ async function evolve(workspacePath, tasks, kairnConfig, evolveConfig, onProgres
2035
3636
  history.push(skipLog);
2036
3637
  continue;
2037
3638
  }
2038
- const nextIterDir = path24.join(
3639
+ const nextIterDir = path25.join(
2039
3640
  workspacePath,
2040
3641
  "iterations",
2041
3642
  (iter + 1).toString()
@@ -2049,7 +3650,7 @@ async function evolve(workspacePath, tasks, kairnConfig, evolveConfig, onProgres
2049
3650
  );
2050
3651
  diffPatch = mutationResult.diffPatch;
2051
3652
  } catch {
2052
- await copyDir(harnessPath, path24.join(nextIterDir, "harness"));
3653
+ await copyDir(harnessPath, path25.join(nextIterDir, "harness"));
2053
3654
  }
2054
3655
  onProgress?.({
2055
3656
  type: "mutations-applied",
@@ -2071,7 +3672,7 @@ async function evolve(workspacePath, tasks, kairnConfig, evolveConfig, onProgres
2071
3672
  }
2072
3673
  if (evolveConfig.usePrincipal && history.length >= 2) {
2073
3674
  onProgress?.({ type: "proposing", iteration: history.length, message: "Principal Proposer synthesizing final harness" });
2074
- const baselineHarnessPath = path24.join(workspacePath, "iterations", "0", "harness");
3675
+ const baselineHarnessPath = path25.join(workspacePath, "iterations", "0", "harness");
2075
3676
  try {
2076
3677
  const principalProposal = await propose(
2077
3678
  history.length,
@@ -2086,7 +3687,7 @@ async function evolve(workspacePath, tasks, kairnConfig, evolveConfig, onProgres
2086
3687
  principalProposal.mutations = principalProposal.mutations.slice(0, evolveConfig.maxMutationsPerIteration);
2087
3688
  }
2088
3689
  const principalIterNum = history.length;
2089
- const principalIterDir = path24.join(workspacePath, "iterations", principalIterNum.toString());
3690
+ const principalIterDir = path25.join(workspacePath, "iterations", principalIterNum.toString());
2090
3691
  const mutResult = await applyMutations(baselineHarnessPath, principalIterDir, principalProposal.mutations);
2091
3692
  onProgress?.({ type: "iteration-start", iteration: principalIterNum });
2092
3693
  const { results: principalResults, aggregate: principalAggregate } = await evaluateAll(
@@ -2147,11 +3748,12 @@ var init_loop = __esm({
2147
3748
  init_baseline();
2148
3749
  init_sampling();
2149
3750
  init_regularization();
3751
+ init_parser();
2150
3752
  }
2151
3753
  });
2152
3754
 
2153
3755
  // src/evolve/synthesis.ts
2154
- import path27 from "path";
3756
+ import path28 from "path";
2155
3757
  function buildMetaPrincipalSystemPrompt(numBranches) {
2156
3758
  return `You are reviewing the COMPLETE results of ${numBranches} independent evolution runs.
2157
3759
  Each branch explored different mutations and saw different task subsets.
@@ -2306,7 +3908,7 @@ async function runSynthesis(context, kairnConfig, evolveConfig, workspacePath) {
2306
3908
  if (mutations.length === 0) {
2307
3909
  return null;
2308
3910
  }
2309
- const synthesisDir = path27.join(workspacePath, "synthesis");
3911
+ const synthesisDir = path28.join(workspacePath, "synthesis");
2310
3912
  const { newHarnessPath } = await applyMutations(
2311
3913
  context.baselineHarnessPath,
2312
3914
  synthesisDir,
@@ -2340,26 +3942,26 @@ __export(population_exports, {
2340
3942
  initBranches: () => initBranches,
2341
3943
  runPopulation: () => runPopulation
2342
3944
  });
2343
- import fs27 from "fs/promises";
2344
- import path28 from "path";
3945
+ import fs28 from "fs/promises";
3946
+ import path29 from "path";
2345
3947
  async function initBranches(workspacePath, baselinePath, numBranches) {
2346
- const branchesDir = path28.join(workspacePath, "branches");
2347
- await fs27.mkdir(branchesDir, { recursive: true });
3948
+ const branchesDir = path29.join(workspacePath, "branches");
3949
+ await fs28.mkdir(branchesDir, { recursive: true });
2348
3950
  const configs = [];
2349
3951
  for (let i = 0; i < numBranches; i++) {
2350
- const branchPath = path28.join(branchesDir, i.toString());
2351
- const harnessPath = path28.join(branchPath, "iterations", "0", "harness");
3952
+ const branchPath = path29.join(branchesDir, i.toString());
3953
+ const harnessPath = path29.join(branchPath, "iterations", "0", "harness");
2352
3954
  await copyDir(baselinePath, harnessPath);
2353
- const tasksYaml = path28.join(workspacePath, "tasks.yaml");
3955
+ const tasksYaml = path29.join(workspacePath, "tasks.yaml");
2354
3956
  try {
2355
- await fs27.access(tasksYaml);
2356
- await fs27.copyFile(tasksYaml, path28.join(branchPath, "tasks.yaml"));
3957
+ await fs28.access(tasksYaml);
3958
+ await fs28.copyFile(tasksYaml, path29.join(branchPath, "tasks.yaml"));
2357
3959
  } catch {
2358
3960
  }
2359
- const configYaml = path28.join(workspacePath, "config.yaml");
3961
+ const configYaml = path29.join(workspacePath, "config.yaml");
2360
3962
  try {
2361
- await fs27.access(configYaml);
2362
- await fs27.copyFile(configYaml, path28.join(branchPath, "config.yaml"));
3963
+ await fs28.access(configYaml);
3964
+ await fs28.copyFile(configYaml, path29.join(branchPath, "config.yaml"));
2363
3965
  } catch {
2364
3966
  }
2365
3967
  const seed = 42 + i * 1337;
@@ -2373,7 +3975,7 @@ async function initBranches(workspacePath, baselinePath, numBranches) {
2373
3975
  }
2374
3976
  async function runPopulation(workspacePath, tasks, kairnConfig, evolveConfig, numBranches, onProgress) {
2375
3977
  const branches = numBranches ?? evolveConfig.pbtBranches;
2376
- const baselinePath = path28.join(workspacePath, "baseline");
3978
+ const baselinePath = path29.join(workspacePath, "baseline");
2377
3979
  const branchConfigs = await initBranches(workspacePath, baselinePath, branches);
2378
3980
  const branchPromises = branchConfigs.map(async (branchConfig) => {
2379
3981
  const branchEvolveConfig = {
@@ -2391,7 +3993,7 @@ async function runPopulation(workspacePath, tasks, kairnConfig, evolveConfig, nu
2391
3993
  branchEvolveConfig,
2392
3994
  branchProgress
2393
3995
  );
2394
- const finalHarnessPath = path28.join(
3996
+ const finalHarnessPath = path29.join(
2395
3997
  branchConfig.workspacePath,
2396
3998
  "iterations",
2397
3999
  result.bestIteration.toString(),
@@ -2399,8 +4001,8 @@ async function runPopulation(workspacePath, tasks, kairnConfig, evolveConfig, nu
2399
4001
  );
2400
4002
  let beliefs = [];
2401
4003
  try {
2402
- const beliefsPath = path28.join(branchConfig.workspacePath, "task-beliefs.json");
2403
- const beliefsContent = await fs27.readFile(beliefsPath, "utf-8");
4004
+ const beliefsPath = path29.join(branchConfig.workspacePath, "task-beliefs.json");
4005
+ const beliefsContent = await fs28.readFile(beliefsPath, "utf-8");
2404
4006
  beliefs = JSON.parse(beliefsContent);
2405
4007
  } catch {
2406
4008
  }
@@ -2422,7 +4024,7 @@ async function runPopulation(workspacePath, tasks, kairnConfig, evolveConfig, nu
2422
4024
  }
2423
4025
  let synthesizedResult;
2424
4026
  try {
2425
- const baselinePath2 = path28.join(workspacePath, "baseline");
4027
+ const baselinePath2 = path29.join(workspacePath, "baseline");
2426
4028
  const synthesisResult = await runSynthesis(
2427
4029
  { branches: branchResults, tasks, baselineHarnessPath: baselinePath2 },
2428
4030
  kairnConfig,
@@ -2597,7 +4199,7 @@ var ui = {
2597
4199
  // Key-value pairs
2598
4200
  kv: (key, value) => ` ${chalk.cyan(key.padEnd(14))} ${value}`,
2599
4201
  // File list
2600
- file: (path30) => chalk.dim(` ${path30}`),
4202
+ file: (path31) => chalk.dim(` ${path31}`),
2601
4203
  // Tool display
2602
4204
  tool: (name, reason) => ` ${warmStone("\u25CF")} ${chalk.bold(name)}
2603
4205
  ${chalk.dim(reason)}`,
@@ -6449,8 +8051,8 @@ var keysCommand = new Command10("keys").description("Add or update API keys for
6449
8051
  import { Command as Command11 } from "commander";
6450
8052
  import chalk14 from "chalk";
6451
8053
  import ora2 from "ora";
6452
- import fs28 from "fs/promises";
6453
- import path29 from "path";
8054
+ import fs29 from "fs/promises";
8055
+ import path30 from "path";
6454
8056
  import { parse as yamlParse2 } from "yaml";
6455
8057
  import { confirm as confirm4, input as input4, select as select4 } from "@inquirer/prompts";
6456
8058
 
@@ -6780,8 +8382,8 @@ init_loop();
6780
8382
 
6781
8383
  // src/evolve/report.ts
6782
8384
  init_trace();
6783
- import fs25 from "fs/promises";
6784
- import path25 from "path";
8385
+ import fs26 from "fs/promises";
8386
+ import path26 from "path";
6785
8387
 
6786
8388
  // src/evolve/diagnosis.ts
6787
8389
  function numericScore(s) {
@@ -6831,10 +8433,10 @@ function numericScore2(s) {
6831
8433
  return s.score ?? (s.pass ? 100 : 0);
6832
8434
  }
6833
8435
  async function loadAllIterations(workspacePath) {
6834
- const iterDir = path25.join(workspacePath, "iterations");
8436
+ const iterDir = path26.join(workspacePath, "iterations");
6835
8437
  let entries;
6836
8438
  try {
6837
- entries = await fs25.readdir(iterDir);
8439
+ entries = await fs26.readdir(iterDir);
6838
8440
  } catch {
6839
8441
  return [];
6840
8442
  }
@@ -6848,7 +8450,7 @@ async function loadAllIterations(workspacePath) {
6848
8450
  }
6849
8451
  async function loadTasks(workspacePath) {
6850
8452
  try {
6851
- const content = await fs25.readFile(path25.join(workspacePath, "tasks.yaml"), "utf-8");
8453
+ const content = await fs26.readFile(path26.join(workspacePath, "tasks.yaml"), "utf-8");
6852
8454
  const parsed = yamlParse(content);
6853
8455
  return parsed?.tasks ?? [];
6854
8456
  } catch {
@@ -7028,13 +8630,13 @@ init_mutator();
7028
8630
  init_baseline();
7029
8631
  init_mutator();
7030
8632
  init_trace();
7031
- import fs26 from "fs/promises";
7032
- import path26 from "path";
8633
+ import fs27 from "fs/promises";
8634
+ import path27 from "path";
7033
8635
  async function listIterations(workspacePath) {
7034
- const iterationsDir = path26.join(workspacePath, "iterations");
8636
+ const iterationsDir = path27.join(workspacePath, "iterations");
7035
8637
  let entries;
7036
8638
  try {
7037
- entries = await fs26.readdir(iterationsDir);
8639
+ entries = await fs27.readdir(iterationsDir);
7038
8640
  } catch {
7039
8641
  return [];
7040
8642
  }
@@ -7043,7 +8645,7 @@ async function listIterations(workspacePath) {
7043
8645
  const n = parseInt(entry, 10);
7044
8646
  if (!isNaN(n)) {
7045
8647
  try {
7046
- await fs26.access(path26.join(iterationsDir, entry, "harness"));
8648
+ await fs27.access(path27.join(iterationsDir, entry, "harness"));
7047
8649
  nums.push(n);
7048
8650
  } catch {
7049
8651
  }
@@ -7069,16 +8671,16 @@ async function listFilesRecursive(dir) {
7069
8671
  async function walk(current) {
7070
8672
  let entries;
7071
8673
  try {
7072
- entries = await fs26.readdir(current, { withFileTypes: true });
8674
+ entries = await fs27.readdir(current, { withFileTypes: true });
7073
8675
  } catch {
7074
8676
  return;
7075
8677
  }
7076
8678
  for (const entry of entries) {
7077
- const fullPath = path26.join(current, entry.name);
8679
+ const fullPath = path27.join(current, entry.name);
7078
8680
  if (entry.isDirectory()) {
7079
8681
  await walk(fullPath);
7080
8682
  } else {
7081
- results.push(path26.relative(dir, fullPath));
8683
+ results.push(path27.relative(dir, fullPath));
7082
8684
  }
7083
8685
  }
7084
8686
  }
@@ -7101,37 +8703,37 @@ async function applyEvolution(workspacePath, projectRoot, targetIteration) {
7101
8703
  } else {
7102
8704
  iter = await findBestIteration(workspacePath, iterations);
7103
8705
  }
7104
- const harnessPath = path26.join(
8706
+ const harnessPath = path27.join(
7105
8707
  workspacePath,
7106
8708
  "iterations",
7107
8709
  iter.toString(),
7108
8710
  "harness"
7109
8711
  );
7110
- const claudeDir = path26.join(projectRoot, ".claude");
8712
+ const claudeDir = path27.join(projectRoot, ".claude");
7111
8713
  const diffPreview = await generateDiff2(claudeDir, harnessPath);
7112
8714
  const currentFiles = await listFilesRecursive(claudeDir);
7113
8715
  const targetFiles = await listFilesRecursive(harnessPath);
7114
8716
  const allPaths = /* @__PURE__ */ new Set([...currentFiles, ...targetFiles]);
7115
8717
  const filesChanged = [];
7116
8718
  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);
8719
+ const currentContent = await fs27.readFile(path27.join(claudeDir, filePath), "utf-8").catch(() => null);
8720
+ const targetContent = await fs27.readFile(path27.join(harnessPath, filePath), "utf-8").catch(() => null);
7119
8721
  if (currentContent !== targetContent) {
7120
8722
  filesChanged.push(filePath);
7121
8723
  }
7122
8724
  }
7123
- await fs26.rm(claudeDir, { recursive: true, force: true });
8725
+ await fs27.rm(claudeDir, { recursive: true, force: true });
7124
8726
  await copyDir(harnessPath, claudeDir);
7125
- const harnessMcpJson = path26.join(harnessPath, ".mcp.json");
7126
- const projectMcpJson = path26.join(projectRoot, ".mcp.json");
8727
+ const harnessMcpJson = path27.join(harnessPath, ".mcp.json");
8728
+ const projectMcpJson = path27.join(projectRoot, ".mcp.json");
7127
8729
  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);
8730
+ await fs27.access(harnessMcpJson);
8731
+ const currentMcp = await fs27.readFile(projectMcpJson, "utf-8").catch(() => null);
8732
+ const targetMcp = await fs27.readFile(harnessMcpJson, "utf-8").catch(() => null);
7131
8733
  if (currentMcp !== targetMcp) {
7132
8734
  filesChanged.push(".mcp.json");
7133
8735
  }
7134
- await fs26.copyFile(harnessMcpJson, projectMcpJson);
8736
+ await fs27.copyFile(harnessMcpJson, projectMcpJson);
7135
8737
  } catch {
7136
8738
  }
7137
8739
  return {
@@ -7160,7 +8762,7 @@ var DEFAULT_CONFIG = {
7160
8762
  };
7161
8763
  async function loadEvolveConfigFromWorkspace(workspacePath) {
7162
8764
  try {
7163
- const configStr = await fs28.readFile(path29.join(workspacePath, "config.yaml"), "utf-8");
8765
+ const configStr = await fs29.readFile(path30.join(workspacePath, "config.yaml"), "utf-8");
7164
8766
  const parsed = yamlParse2(configStr);
7165
8767
  return {
7166
8768
  model: parsed.model ?? DEFAULT_CONFIG.model,
@@ -7187,9 +8789,9 @@ evolveCommand.command("init").description("Initialize an evolution workspace wit
7187
8789
  try {
7188
8790
  const projectRoot = process.cwd();
7189
8791
  console.log(ui.section("Evolve Init"));
7190
- const claudeDir = path29.join(projectRoot, ".claude");
8792
+ const claudeDir = path30.join(projectRoot, ".claude");
7191
8793
  try {
7192
- await fs28.access(claudeDir);
8794
+ await fs29.access(claudeDir);
7193
8795
  } catch {
7194
8796
  console.log(ui.error("No .claude/ directory found. Run kairn describe first."));
7195
8797
  process.exit(1);
@@ -7239,7 +8841,7 @@ evolveCommand.command("init").description("Initialize an evolution workspace wit
7239
8841
  if (config) {
7240
8842
  let claudeMd = "";
7241
8843
  try {
7242
- claudeMd = await fs28.readFile(path29.join(claudeDir, "CLAUDE.md"), "utf-8");
8844
+ claudeMd = await fs29.readFile(path30.join(claudeDir, "CLAUDE.md"), "utf-8");
7243
8845
  } catch {
7244
8846
  }
7245
8847
  const profile = await buildProjectProfile(projectRoot);
@@ -7270,16 +8872,16 @@ evolveCommand.command("init").description("Initialize an evolution workspace wit
7270
8872
  evolveCommand.command("baseline").description("Snapshot current .claude/ directory as baseline").action(async () => {
7271
8873
  try {
7272
8874
  const projectRoot = process.cwd();
7273
- const workspace = path29.join(projectRoot, ".kairn-evolve");
8875
+ const workspace = path30.join(projectRoot, ".kairn-evolve");
7274
8876
  console.log(ui.section("Evolve Baseline"));
7275
8877
  try {
7276
- await fs28.access(workspace);
8878
+ await fs29.access(workspace);
7277
8879
  } catch {
7278
8880
  console.log(ui.error("No .kairn-evolve/ directory found. Run kairn evolve init first."));
7279
8881
  process.exit(1);
7280
8882
  }
7281
8883
  await snapshotBaseline(projectRoot, workspace);
7282
- const baselineDir = path29.join(workspace, "baseline");
8884
+ const baselineDir = path30.join(workspace, "baseline");
7283
8885
  const fileCount = await countFiles(baselineDir);
7284
8886
  console.log(ui.success(`Baseline snapshot created (${fileCount} files)`));
7285
8887
  } catch (err) {
@@ -7291,18 +8893,18 @@ evolveCommand.command("baseline").description("Snapshot current .claude/ directo
7291
8893
  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
8894
  try {
7293
8895
  const projectRoot = process.cwd();
7294
- const workspace = path29.join(projectRoot, ".kairn-evolve");
8896
+ const workspace = path30.join(projectRoot, ".kairn-evolve");
7295
8897
  console.log(ui.section("Evolve Run"));
7296
8898
  try {
7297
- await fs28.access(workspace);
8899
+ await fs29.access(workspace);
7298
8900
  } catch {
7299
8901
  console.log(ui.error("No .kairn-evolve/ directory found. Run kairn evolve init first."));
7300
8902
  process.exit(1);
7301
8903
  }
7302
- const tasksPath = path29.join(workspace, "tasks.yaml");
8904
+ const tasksPath = path30.join(workspace, "tasks.yaml");
7303
8905
  let tasksContent;
7304
8906
  try {
7305
- tasksContent = await fs28.readFile(tasksPath, "utf-8");
8907
+ tasksContent = await fs29.readFile(tasksPath, "utf-8");
7306
8908
  } catch {
7307
8909
  console.log(ui.error("No tasks.yaml found. Run kairn evolve init first."));
7308
8910
  process.exit(1);
@@ -7321,15 +8923,15 @@ evolveCommand.command("run").description("Run tasks against the current harness"
7321
8923
  console.log(ui.info(`Running ${tasksToRun.length} task(s)...`));
7322
8924
  console.log("");
7323
8925
  const config = await loadConfig();
7324
- const harnessPath = path29.join(projectRoot, ".claude");
8926
+ const harnessPath = path30.join(projectRoot, ".claude");
7325
8927
  const results = [];
7326
8928
  for (const task of tasksToRun) {
7327
- const traceDir = path29.join(workspace, "traces", "0", task.id);
8929
+ const traceDir = path30.join(workspace, "traces", "0", task.id);
7328
8930
  const spinner = ora2(`Running: ${task.id}`).start();
7329
8931
  const result = await runTask(task, harnessPath, traceDir, 0);
7330
8932
  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(() => "");
8933
+ const stdout = await fs29.readFile(path30.join(traceDir, "stdout.log"), "utf-8").catch(() => "");
8934
+ const stderr = await fs29.readFile(path30.join(traceDir, "stderr.log"), "utf-8").catch(() => "");
7333
8935
  const score = await scoreTask(task, traceDir, stdout, stderr, config);
7334
8936
  result.score = score;
7335
8937
  await writeScore(traceDir, score);
@@ -7477,7 +9079,7 @@ evolveCommand.command("run").description("Run tasks against the current harness"
7477
9079
  evolveConfig.klLambda = klLambda;
7478
9080
  }
7479
9081
  try {
7480
- await fs28.access(path29.join(workspace, "iterations", "0", "harness"));
9082
+ await fs29.access(path30.join(workspace, "iterations", "0", "harness"));
7481
9083
  } catch {
7482
9084
  console.log(ui.error("No baseline harness found. Run kairn evolve baseline first."));
7483
9085
  process.exit(1);
@@ -7571,16 +9173,16 @@ evolveCommand.command("run").description("Run tasks against the current harness"
7571
9173
  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
9174
  try {
7573
9175
  const projectRoot = process.cwd();
7574
- const workspace = path29.join(projectRoot, ".kairn-evolve");
9176
+ const workspace = path30.join(projectRoot, ".kairn-evolve");
7575
9177
  console.log(ui.section("Evolve PBT"));
7576
9178
  try {
7577
- await fs28.access(workspace);
9179
+ await fs29.access(workspace);
7578
9180
  } catch {
7579
9181
  console.log(ui.error("No .kairn-evolve/ directory found. Run kairn evolve init first."));
7580
9182
  process.exit(1);
7581
9183
  }
7582
9184
  try {
7583
- await fs28.access(path29.join(workspace, "iterations", "0", "harness"));
9185
+ await fs29.access(path30.join(workspace, "iterations", "0", "harness"));
7584
9186
  } catch {
7585
9187
  console.log(ui.error("No baseline harness found. Run kairn evolve baseline first."));
7586
9188
  process.exit(1);
@@ -7600,8 +9202,8 @@ evolveCommand.command("pbt").description("Run Population-Based Training with par
7600
9202
  if (sampling === "thompson" || sampling === "uniform") {
7601
9203
  evolveConfig.samplingStrategy = sampling;
7602
9204
  }
7603
- const tasksPath = path29.join(workspace, "tasks.yaml");
7604
- const tasksContent = await fs28.readFile(tasksPath, "utf-8");
9205
+ const tasksPath = path30.join(workspace, "tasks.yaml");
9206
+ const tasksContent = await fs29.readFile(tasksPath, "utf-8");
7605
9207
  const parsed = yamlParse2(tasksContent);
7606
9208
  if (!parsed?.tasks || parsed.tasks.length === 0) {
7607
9209
  console.log(ui.error("No tasks found in tasks.yaml"));
@@ -7659,10 +9261,10 @@ evolveCommand.command("pbt").description("Run Population-Based Training with par
7659
9261
  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
9262
  try {
7661
9263
  const projectRoot = process.cwd();
7662
- const workspace = path29.join(projectRoot, ".kairn-evolve");
9264
+ const workspace = path30.join(projectRoot, ".kairn-evolve");
7663
9265
  console.log(ui.section("Evolve Apply"));
7664
9266
  try {
7665
- await fs28.access(workspace);
9267
+ await fs29.access(workspace);
7666
9268
  } catch {
7667
9269
  console.log(ui.error("No .kairn-evolve/ directory found. Run kairn evolve init first."));
7668
9270
  process.exit(1);
@@ -7703,9 +9305,9 @@ evolveCommand.command("apply").description("Apply the best evolved harness to yo
7703
9305
  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
9306
  try {
7705
9307
  const projectRoot = process.cwd();
7706
- const workspace = path29.join(projectRoot, ".kairn-evolve");
9308
+ const workspace = path30.join(projectRoot, ".kairn-evolve");
7707
9309
  try {
7708
- await fs28.access(workspace);
9310
+ await fs29.access(workspace);
7709
9311
  } catch {
7710
9312
  console.log(ui.error("No .kairn-evolve/ directory found. Run kairn evolve init first."));
7711
9313
  process.exit(1);
@@ -7726,23 +9328,23 @@ evolveCommand.command("report").description("Generate a summary report of the ev
7726
9328
  evolveCommand.command("diff <iter1> <iter2>").description("Show harness changes between two iterations").action(async (iter1Str, iter2Str) => {
7727
9329
  try {
7728
9330
  const projectRoot = process.cwd();
7729
- const workspace = path29.join(projectRoot, ".kairn-evolve");
9331
+ const workspace = path30.join(projectRoot, ".kairn-evolve");
7730
9332
  const iter1 = parseInt(iter1Str, 10);
7731
9333
  const iter2 = parseInt(iter2Str, 10);
7732
9334
  if (isNaN(iter1) || isNaN(iter2)) {
7733
9335
  console.log(ui.error("Both arguments must be integers (iteration numbers)"));
7734
9336
  process.exit(1);
7735
9337
  }
7736
- const harness1 = path29.join(workspace, "iterations", iter1.toString(), "harness");
7737
- const harness2 = path29.join(workspace, "iterations", iter2.toString(), "harness");
9338
+ const harness1 = path30.join(workspace, "iterations", iter1.toString(), "harness");
9339
+ const harness2 = path30.join(workspace, "iterations", iter2.toString(), "harness");
7738
9340
  try {
7739
- await fs28.access(harness1);
9341
+ await fs29.access(harness1);
7740
9342
  } catch {
7741
9343
  console.log(ui.error(`Iteration ${iter1} harness not found at ${harness1}`));
7742
9344
  process.exit(1);
7743
9345
  }
7744
9346
  try {
7745
- await fs28.access(harness2);
9347
+ await fs29.access(harness2);
7746
9348
  } catch {
7747
9349
  console.log(ui.error(`Iteration ${iter2} harness not found at ${harness2}`));
7748
9350
  process.exit(1);
@@ -7797,10 +9399,10 @@ evolveCommand.command("diff <iter1> <iter2>").description("Show harness changes
7797
9399
  async function countFiles(dir) {
7798
9400
  let count = 0;
7799
9401
  try {
7800
- const entries = await fs28.readdir(dir, { withFileTypes: true });
9402
+ const entries = await fs29.readdir(dir, { withFileTypes: true });
7801
9403
  for (const entry of entries) {
7802
9404
  if (entry.isDirectory()) {
7803
- count += await countFiles(path29.join(dir, entry.name));
9405
+ count += await countFiles(path30.join(dir, entry.name));
7804
9406
  } else {
7805
9407
  count++;
7806
9408
  }