kairn-cli 2.7.0 → 2.7.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +123 -9
- package/dist/cli.js.map +1 -1
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -1831,6 +1831,16 @@ async function parseAgents(harnessPath) {
|
|
|
1831
1831
|
if (Array.isArray(disallowedTools)) {
|
|
1832
1832
|
node.disallowedTools = disallowedTools;
|
|
1833
1833
|
}
|
|
1834
|
+
const knownKeys = /* @__PURE__ */ new Set(["name", "model", "disallowedTools"]);
|
|
1835
|
+
const extra = {};
|
|
1836
|
+
for (const [key, value] of Object.entries(frontmatter)) {
|
|
1837
|
+
if (!knownKeys.has(key)) {
|
|
1838
|
+
extra[key] = value;
|
|
1839
|
+
}
|
|
1840
|
+
}
|
|
1841
|
+
if (Object.keys(extra).length > 0) {
|
|
1842
|
+
node.extraFrontmatter = extra;
|
|
1843
|
+
}
|
|
1834
1844
|
nodes.push(node);
|
|
1835
1845
|
}
|
|
1836
1846
|
return nodes;
|
|
@@ -1847,8 +1857,10 @@ async function parseSkills(harnessPath) {
|
|
|
1847
1857
|
const name = entry.replace(/\.md$/, "");
|
|
1848
1858
|
nodes.push({ name, content });
|
|
1849
1859
|
} else if (await isDirectory(entryPath)) {
|
|
1850
|
-
|
|
1851
|
-
|
|
1860
|
+
let content = await readFileSafe2(path21.join(entryPath, "skill.md"));
|
|
1861
|
+
if (content === null) {
|
|
1862
|
+
content = await readFileSafe2(path21.join(entryPath, "SKILL.md"));
|
|
1863
|
+
}
|
|
1852
1864
|
if (content === null) continue;
|
|
1853
1865
|
nodes.push({ name: entry, content });
|
|
1854
1866
|
}
|
|
@@ -2445,7 +2457,8 @@ function renderRuleWithFrontmatter(rule) {
|
|
|
2445
2457
|
function renderAgentWithFrontmatter(agent) {
|
|
2446
2458
|
const hasModel = agent.model !== void 0;
|
|
2447
2459
|
const hasDisallowed = agent.disallowedTools !== void 0 && agent.disallowedTools.length > 0;
|
|
2448
|
-
|
|
2460
|
+
const hasExtra = agent.extraFrontmatter !== void 0 && Object.keys(agent.extraFrontmatter).length > 0;
|
|
2461
|
+
if (!hasModel && !hasDisallowed && !hasExtra) {
|
|
2449
2462
|
return agent.content;
|
|
2450
2463
|
}
|
|
2451
2464
|
const yamlLines = ["---"];
|
|
@@ -2458,6 +2471,23 @@ function renderAgentWithFrontmatter(agent) {
|
|
|
2458
2471
|
yamlLines.push(` - ${tool}`);
|
|
2459
2472
|
}
|
|
2460
2473
|
}
|
|
2474
|
+
if (hasExtra) {
|
|
2475
|
+
for (const [key, value] of Object.entries(agent.extraFrontmatter)) {
|
|
2476
|
+
if (Array.isArray(value)) {
|
|
2477
|
+
yamlLines.push(`${key}:`);
|
|
2478
|
+
for (const item of value) {
|
|
2479
|
+
yamlLines.push(` - ${String(item)}`);
|
|
2480
|
+
}
|
|
2481
|
+
} else if (typeof value === "object" && value !== null) {
|
|
2482
|
+
yamlLines.push(`${key}:`);
|
|
2483
|
+
for (const [subKey, subVal] of Object.entries(value)) {
|
|
2484
|
+
yamlLines.push(` ${subKey}: ${String(subVal)}`);
|
|
2485
|
+
}
|
|
2486
|
+
} else {
|
|
2487
|
+
yamlLines.push(`${key}: ${String(value)}`);
|
|
2488
|
+
}
|
|
2489
|
+
}
|
|
2490
|
+
}
|
|
2461
2491
|
yamlLines.push("---");
|
|
2462
2492
|
return yamlLines.join("\n") + "\n\n" + agent.content;
|
|
2463
2493
|
}
|
|
@@ -2874,6 +2904,14 @@ async function renderAffectedFiles(ir, targetDir, touchedCategories) {
|
|
|
2874
2904
|
if (touchedCategories.has("agents")) {
|
|
2875
2905
|
await deleteOrphanedFiles(targetDir, "agents", fileMap);
|
|
2876
2906
|
}
|
|
2907
|
+
if (touchedCategories.has("mcp") && !fileMap.has(".mcp.json")) {
|
|
2908
|
+
await fs22.unlink(path22.join(targetDir, ".mcp.json")).catch(() => {
|
|
2909
|
+
});
|
|
2910
|
+
}
|
|
2911
|
+
if (touchedCategories.has("settings") && !fileMap.has("settings.json")) {
|
|
2912
|
+
await fs22.unlink(path22.join(targetDir, "settings.json")).catch(() => {
|
|
2913
|
+
});
|
|
2914
|
+
}
|
|
2877
2915
|
}
|
|
2878
2916
|
function getFileCategory(relativePath) {
|
|
2879
2917
|
if (relativePath === "CLAUDE.md") return "claude_md";
|
|
@@ -3344,7 +3382,7 @@ async function evolve(workspacePath, tasks, kairnConfig, evolveConfig, onProgres
|
|
|
3344
3382
|
}
|
|
3345
3383
|
}
|
|
3346
3384
|
}
|
|
3347
|
-
let rngState = 42;
|
|
3385
|
+
let rngState = evolveConfig.rngSeed ?? 42;
|
|
3348
3386
|
const rng = () => {
|
|
3349
3387
|
rngState = rngState * 1664525 + 1013904223 & 4294967295;
|
|
3350
3388
|
return (rngState >>> 0) / 4294967296;
|
|
@@ -3458,7 +3496,7 @@ async function evolve(workspacePath, tasks, kairnConfig, evolveConfig, onProgres
|
|
|
3458
3496
|
}
|
|
3459
3497
|
if (useThompson) {
|
|
3460
3498
|
const scoreMap = {};
|
|
3461
|
-
for (const [taskId, score] of Object.entries(
|
|
3499
|
+
for (const [taskId, score] of Object.entries(evalResults)) {
|
|
3462
3500
|
scoreMap[taskId] = score.score ?? (score.pass ? 100 : 0);
|
|
3463
3501
|
}
|
|
3464
3502
|
beliefs = updateBeliefs(beliefs, scoreMap);
|
|
@@ -3981,7 +4019,9 @@ async function runPopulation(workspacePath, tasks, kairnConfig, evolveConfig, nu
|
|
|
3981
4019
|
const branchEvolveConfig = {
|
|
3982
4020
|
...evolveConfig,
|
|
3983
4021
|
// Disable principal for individual branches — synthesis replaces it
|
|
3984
|
-
usePrincipal: false
|
|
4022
|
+
usePrincipal: false,
|
|
4023
|
+
// Each branch gets its own RNG seed for Thompson Sampling diversity
|
|
4024
|
+
rngSeed: branchConfig.seed
|
|
3985
4025
|
};
|
|
3986
4026
|
const branchProgress = onProgress ? (event) => {
|
|
3987
4027
|
onProgress({ ...event, branchId: branchConfig.branchId });
|
|
@@ -8687,7 +8727,81 @@ async function listFilesRecursive(dir) {
|
|
|
8687
8727
|
await walk(dir);
|
|
8688
8728
|
return results;
|
|
8689
8729
|
}
|
|
8690
|
-
async function
|
|
8730
|
+
async function findBestPBTHarness(workspacePath) {
|
|
8731
|
+
const branchesDir = path27.join(workspacePath, "branches");
|
|
8732
|
+
let branchEntries;
|
|
8733
|
+
try {
|
|
8734
|
+
branchEntries = await fs27.readdir(branchesDir);
|
|
8735
|
+
} catch {
|
|
8736
|
+
return null;
|
|
8737
|
+
}
|
|
8738
|
+
let bestScore = -Infinity;
|
|
8739
|
+
let bestPath = "";
|
|
8740
|
+
let bestLabel = "";
|
|
8741
|
+
for (const branchId of branchEntries) {
|
|
8742
|
+
const branchPath = path27.join(branchesDir, branchId);
|
|
8743
|
+
const branchIterations = await listIterations(branchPath);
|
|
8744
|
+
if (branchIterations.length === 0) continue;
|
|
8745
|
+
const bestIter = await findBestIteration(branchPath, branchIterations);
|
|
8746
|
+
const log = await loadIterationLog(branchPath, bestIter);
|
|
8747
|
+
const score = log?.score ?? 0;
|
|
8748
|
+
if (score > bestScore) {
|
|
8749
|
+
bestScore = score;
|
|
8750
|
+
bestPath = path27.join(branchPath, "iterations", bestIter.toString(), "harness");
|
|
8751
|
+
bestLabel = `branch ${branchId}, iteration ${bestIter} (${score.toFixed(1)}%)`;
|
|
8752
|
+
}
|
|
8753
|
+
}
|
|
8754
|
+
const synthesisHarness = path27.join(workspacePath, "synthesis", "harness");
|
|
8755
|
+
try {
|
|
8756
|
+
await fs27.access(synthesisHarness);
|
|
8757
|
+
const synthesisLog = await loadIterationLog(workspacePath, 999);
|
|
8758
|
+
const synthScore = synthesisLog?.score ?? 0;
|
|
8759
|
+
if (synthScore > bestScore) {
|
|
8760
|
+
bestScore = synthScore;
|
|
8761
|
+
bestPath = synthesisHarness;
|
|
8762
|
+
bestLabel = `Meta-Principal synthesis (${synthScore.toFixed(1)}%)`;
|
|
8763
|
+
}
|
|
8764
|
+
} catch {
|
|
8765
|
+
}
|
|
8766
|
+
if (!bestPath) return null;
|
|
8767
|
+
return { harnessPath: bestPath, label: bestLabel };
|
|
8768
|
+
}
|
|
8769
|
+
async function applyEvolution(workspacePath, projectRoot, targetIteration, pbt) {
|
|
8770
|
+
if (pbt) {
|
|
8771
|
+
const pbtResult = await findBestPBTHarness(workspacePath);
|
|
8772
|
+
if (!pbtResult) {
|
|
8773
|
+
throw new Error("No PBT results found. Run `kairn evolve pbt` first.");
|
|
8774
|
+
}
|
|
8775
|
+
const claudeDir2 = path27.join(projectRoot, ".claude");
|
|
8776
|
+
const diffPreview2 = await generateDiff2(claudeDir2, pbtResult.harnessPath);
|
|
8777
|
+
const currentFiles2 = await listFilesRecursive(claudeDir2);
|
|
8778
|
+
const targetFiles2 = await listFilesRecursive(pbtResult.harnessPath);
|
|
8779
|
+
const allPaths2 = /* @__PURE__ */ new Set([...currentFiles2, ...targetFiles2]);
|
|
8780
|
+
const filesChanged2 = [];
|
|
8781
|
+
for (const filePath of allPaths2) {
|
|
8782
|
+
const currentContent = await fs27.readFile(path27.join(claudeDir2, filePath), "utf-8").catch(() => null);
|
|
8783
|
+
const targetContent = await fs27.readFile(path27.join(pbtResult.harnessPath, filePath), "utf-8").catch(() => null);
|
|
8784
|
+
if (currentContent !== targetContent) {
|
|
8785
|
+
filesChanged2.push(filePath);
|
|
8786
|
+
}
|
|
8787
|
+
}
|
|
8788
|
+
await fs27.rm(claudeDir2, { recursive: true, force: true });
|
|
8789
|
+
await copyDir(pbtResult.harnessPath, claudeDir2);
|
|
8790
|
+
const harnessMcpJson2 = path27.join(pbtResult.harnessPath, ".mcp.json");
|
|
8791
|
+
const projectMcpJson2 = path27.join(projectRoot, ".mcp.json");
|
|
8792
|
+
try {
|
|
8793
|
+
await fs27.access(harnessMcpJson2);
|
|
8794
|
+
await fs27.copyFile(harnessMcpJson2, projectMcpJson2);
|
|
8795
|
+
if (!filesChanged2.includes(".mcp.json")) filesChanged2.push(".mcp.json");
|
|
8796
|
+
} catch {
|
|
8797
|
+
}
|
|
8798
|
+
return {
|
|
8799
|
+
iteration: -1,
|
|
8800
|
+
// signals PBT source
|
|
8801
|
+
filesChanged: filesChanged2,
|
|
8802
|
+
diffPreview: diffPreview2
|
|
8803
|
+
};
|
|
8804
|
+
}
|
|
8691
8805
|
const iterations = await listIterations(workspacePath);
|
|
8692
8806
|
if (iterations.length === 0) {
|
|
8693
8807
|
throw new Error("No iterations found in workspace. Run `kairn evolve run` first.");
|
|
@@ -9258,7 +9372,7 @@ evolveCommand.command("pbt").description("Run Population-Based Training with par
|
|
|
9258
9372
|
process.exit(1);
|
|
9259
9373
|
}
|
|
9260
9374
|
});
|
|
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) => {
|
|
9375
|
+
evolveCommand.command("apply").description("Apply the best evolved harness to your project").option("--iter <n>", "Apply a specific iteration instead of the best").option("--pbt", "Apply best PBT result (branch winner or synthesis)").option("--force", "Apply even if git working tree is dirty").option("--no-commit", "Skip automatic git commit after applying").action(async (options) => {
|
|
9262
9376
|
try {
|
|
9263
9377
|
const projectRoot = process.cwd();
|
|
9264
9378
|
const workspace = path30.join(projectRoot, ".kairn-evolve");
|
|
@@ -9277,7 +9391,7 @@ evolveCommand.command("apply").description("Apply the best evolved harness to yo
|
|
|
9277
9391
|
process.exit(1);
|
|
9278
9392
|
}
|
|
9279
9393
|
}
|
|
9280
|
-
const result = await applyEvolution(workspace, projectRoot, targetIteration);
|
|
9394
|
+
const result = await applyEvolution(workspace, projectRoot, targetIteration, options.pbt);
|
|
9281
9395
|
if (result.diffPreview) {
|
|
9282
9396
|
console.log(ui.section("Changes"));
|
|
9283
9397
|
for (const line of result.diffPreview.split("\n")) {
|