kairn-cli 2.14.0 → 2.16.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +1036 -474
- package/dist/cli.js.map +1 -1
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -411,7 +411,7 @@ var init_ui = __esm({
|
|
|
411
411
|
// Key-value pairs
|
|
412
412
|
kv: (key, value) => ` ${chalk.cyan(key.padEnd(14))} ${value}`,
|
|
413
413
|
// File list
|
|
414
|
-
file: (
|
|
414
|
+
file: (path39) => chalk.dim(` ${path39}`),
|
|
415
415
|
// Tool display
|
|
416
416
|
tool: (name, reason) => ` ${warmStone("\u25CF")} ${chalk.bold(name)}
|
|
417
417
|
${chalk.dim(reason)}`,
|
|
@@ -3811,12 +3811,31 @@ var init_secrets = __esm({
|
|
|
3811
3811
|
}
|
|
3812
3812
|
});
|
|
3813
3813
|
|
|
3814
|
-
// src/
|
|
3814
|
+
// src/compiler/persist.ts
|
|
3815
3815
|
import fs9 from "fs/promises";
|
|
3816
3816
|
import path9 from "path";
|
|
3817
|
+
async function persistHarnessIR(targetDir, ir) {
|
|
3818
|
+
const kairnDir = path9.join(targetDir, KAIRN_DIR2);
|
|
3819
|
+
await fs9.mkdir(kairnDir, { recursive: true });
|
|
3820
|
+
const filePath = path9.join(kairnDir, HARNESS_IR_FILENAME);
|
|
3821
|
+
await fs9.writeFile(filePath, JSON.stringify(ir, null, 2), "utf-8");
|
|
3822
|
+
return filePath;
|
|
3823
|
+
}
|
|
3824
|
+
var KAIRN_DIR2, HARNESS_IR_FILENAME;
|
|
3825
|
+
var init_persist = __esm({
|
|
3826
|
+
"src/compiler/persist.ts"() {
|
|
3827
|
+
"use strict";
|
|
3828
|
+
KAIRN_DIR2 = ".kairn";
|
|
3829
|
+
HARNESS_IR_FILENAME = "harness-ir.json";
|
|
3830
|
+
}
|
|
3831
|
+
});
|
|
3832
|
+
|
|
3833
|
+
// src/scanner/scan.ts
|
|
3834
|
+
import fs10 from "fs/promises";
|
|
3835
|
+
import path10 from "path";
|
|
3817
3836
|
async function fileExists(p) {
|
|
3818
3837
|
try {
|
|
3819
|
-
await
|
|
3838
|
+
await fs10.access(p);
|
|
3820
3839
|
return true;
|
|
3821
3840
|
} catch {
|
|
3822
3841
|
return false;
|
|
@@ -3824,7 +3843,7 @@ async function fileExists(p) {
|
|
|
3824
3843
|
}
|
|
3825
3844
|
async function readJsonSafe(p) {
|
|
3826
3845
|
try {
|
|
3827
|
-
const data = await
|
|
3846
|
+
const data = await fs10.readFile(p, "utf-8");
|
|
3828
3847
|
return JSON.parse(data);
|
|
3829
3848
|
} catch {
|
|
3830
3849
|
return null;
|
|
@@ -3832,14 +3851,14 @@ async function readJsonSafe(p) {
|
|
|
3832
3851
|
}
|
|
3833
3852
|
async function readFileSafe(p) {
|
|
3834
3853
|
try {
|
|
3835
|
-
return await
|
|
3854
|
+
return await fs10.readFile(p, "utf-8");
|
|
3836
3855
|
} catch {
|
|
3837
3856
|
return null;
|
|
3838
3857
|
}
|
|
3839
3858
|
}
|
|
3840
3859
|
async function listDirSafe(p) {
|
|
3841
3860
|
try {
|
|
3842
|
-
const entries = await
|
|
3861
|
+
const entries = await fs10.readdir(p);
|
|
3843
3862
|
return entries.filter((e) => !e.startsWith("."));
|
|
3844
3863
|
} catch {
|
|
3845
3864
|
return [];
|
|
@@ -3873,14 +3892,56 @@ function detectFramework(deps) {
|
|
|
3873
3892
|
}
|
|
3874
3893
|
return detected.length > 0 ? detected.join(" + ") : null;
|
|
3875
3894
|
}
|
|
3876
|
-
function
|
|
3877
|
-
|
|
3878
|
-
|
|
3879
|
-
|
|
3880
|
-
|
|
3881
|
-
|
|
3882
|
-
|
|
3883
|
-
return
|
|
3895
|
+
function detectLanguagesFromFiles(files) {
|
|
3896
|
+
const matched = [];
|
|
3897
|
+
for (const signal of LANGUAGE_SIGNALS) {
|
|
3898
|
+
if (files.some((f) => signal.files.includes(f))) {
|
|
3899
|
+
matched.push(signal.language);
|
|
3900
|
+
}
|
|
3901
|
+
}
|
|
3902
|
+
return matched;
|
|
3903
|
+
}
|
|
3904
|
+
async function detectLanguageLocations(dir, keyFiles) {
|
|
3905
|
+
const rootHits = detectLanguagesFromFiles(keyFiles);
|
|
3906
|
+
if (rootHits.length > 0) {
|
|
3907
|
+
return rootHits.map((language) => ({ language, subdirs: [] }));
|
|
3908
|
+
}
|
|
3909
|
+
const entries = await listDirSafe(dir);
|
|
3910
|
+
const langSubdirs = /* @__PURE__ */ new Map();
|
|
3911
|
+
const counts = /* @__PURE__ */ new Map();
|
|
3912
|
+
for (const entry of entries) {
|
|
3913
|
+
const subPath = path10.join(dir, entry);
|
|
3914
|
+
try {
|
|
3915
|
+
const stat = await fs10.stat(subPath);
|
|
3916
|
+
if (!stat.isDirectory()) continue;
|
|
3917
|
+
} catch {
|
|
3918
|
+
continue;
|
|
3919
|
+
}
|
|
3920
|
+
const subFiles = await listDirSafe(subPath);
|
|
3921
|
+
const subLangs = detectLanguagesFromFiles(subFiles);
|
|
3922
|
+
for (const lang of subLangs) {
|
|
3923
|
+
counts.set(lang, (counts.get(lang) ?? 0) + 1);
|
|
3924
|
+
const existing = langSubdirs.get(lang);
|
|
3925
|
+
if (existing) {
|
|
3926
|
+
existing.push(entry);
|
|
3927
|
+
} else {
|
|
3928
|
+
langSubdirs.set(lang, [entry]);
|
|
3929
|
+
}
|
|
3930
|
+
}
|
|
3931
|
+
}
|
|
3932
|
+
if (counts.size === 0) return [];
|
|
3933
|
+
const precedence = /* @__PURE__ */ new Map();
|
|
3934
|
+
for (let i = 0; i < LANGUAGE_SIGNALS.length; i++) {
|
|
3935
|
+
precedence.set(LANGUAGE_SIGNALS[i].language, i);
|
|
3936
|
+
}
|
|
3937
|
+
return [...counts.entries()].sort((a, b) => {
|
|
3938
|
+
const freqDiff = b[1] - a[1];
|
|
3939
|
+
if (freqDiff !== 0) return freqDiff;
|
|
3940
|
+
return (precedence.get(a[0]) ?? 999) - (precedence.get(b[0]) ?? 999);
|
|
3941
|
+
}).map(([lang]) => ({
|
|
3942
|
+
language: lang,
|
|
3943
|
+
subdirs: langSubdirs.get(lang) ?? []
|
|
3944
|
+
}));
|
|
3884
3945
|
}
|
|
3885
3946
|
function extractEnvKeys(content) {
|
|
3886
3947
|
const keys = [];
|
|
@@ -3891,7 +3952,7 @@ function extractEnvKeys(content) {
|
|
|
3891
3952
|
return keys;
|
|
3892
3953
|
}
|
|
3893
3954
|
async function scanProject(dir) {
|
|
3894
|
-
const pkg2 = await readJsonSafe(
|
|
3955
|
+
const pkg2 = await readJsonSafe(path10.join(dir, "package.json"));
|
|
3895
3956
|
const deps = pkg2?.dependencies ? Object.keys(pkg2.dependencies) : [];
|
|
3896
3957
|
const devDeps = pkg2?.devDependencies ? Object.keys(pkg2.devDependencies) : [];
|
|
3897
3958
|
const allDeps = [...deps, ...devDeps];
|
|
@@ -3915,23 +3976,25 @@ async function scanProject(dir) {
|
|
|
3915
3976
|
"CLAUDE.md"
|
|
3916
3977
|
].includes(f)
|
|
3917
3978
|
);
|
|
3918
|
-
const
|
|
3979
|
+
const languageLocations = await detectLanguageLocations(dir, keyFiles);
|
|
3980
|
+
const detectedLanguages = languageLocations.map((d) => d.language);
|
|
3981
|
+
const language = detectedLanguages[0] ?? null;
|
|
3919
3982
|
const framework = detectFramework(allDeps);
|
|
3920
3983
|
const typescript = keyFiles.includes("tsconfig.json") || allDeps.includes("typescript");
|
|
3921
3984
|
const testCommand = scripts.test && scripts.test !== 'echo "Error: no test specified" && exit 1' ? scripts.test : null;
|
|
3922
|
-
const hasTests = testCommand !== null || await fileExists(
|
|
3985
|
+
const hasTests = testCommand !== null || await fileExists(path10.join(dir, "tests")) || await fileExists(path10.join(dir, "__tests__")) || await fileExists(path10.join(dir, "test"));
|
|
3923
3986
|
const buildCommand = scripts.build || null;
|
|
3924
3987
|
const lintCommand = scripts.lint || null;
|
|
3925
|
-
const hasSrc = await fileExists(
|
|
3926
|
-
const hasDocker = await fileExists(
|
|
3927
|
-
const hasCi = await fileExists(
|
|
3928
|
-
const hasEnvFile = await fileExists(
|
|
3988
|
+
const hasSrc = await fileExists(path10.join(dir, "src"));
|
|
3989
|
+
const hasDocker = await fileExists(path10.join(dir, "docker-compose.yml")) || await fileExists(path10.join(dir, "Dockerfile"));
|
|
3990
|
+
const hasCi = await fileExists(path10.join(dir, ".github/workflows"));
|
|
3991
|
+
const hasEnvFile = await fileExists(path10.join(dir, ".env")) || await fileExists(path10.join(dir, ".env.example"));
|
|
3929
3992
|
let envKeys = [];
|
|
3930
|
-
const envExample = await readFileSafe(
|
|
3993
|
+
const envExample = await readFileSafe(path10.join(dir, ".env.example"));
|
|
3931
3994
|
if (envExample) {
|
|
3932
3995
|
envKeys = extractEnvKeys(envExample);
|
|
3933
3996
|
}
|
|
3934
|
-
const claudeDir =
|
|
3997
|
+
const claudeDir = path10.join(dir, ".claude");
|
|
3935
3998
|
const hasClaudeDir = await fileExists(claudeDir);
|
|
3936
3999
|
let existingClaudeMd = null;
|
|
3937
4000
|
let existingSettings = null;
|
|
@@ -3943,27 +4006,29 @@ async function scanProject(dir) {
|
|
|
3943
4006
|
let mcpServerCount = 0;
|
|
3944
4007
|
let claudeMdLineCount = 0;
|
|
3945
4008
|
if (hasClaudeDir) {
|
|
3946
|
-
existingClaudeMd = await readFileSafe(
|
|
4009
|
+
existingClaudeMd = await readFileSafe(path10.join(claudeDir, "CLAUDE.md"));
|
|
3947
4010
|
if (existingClaudeMd) {
|
|
3948
4011
|
claudeMdLineCount = existingClaudeMd.split("\n").length;
|
|
3949
4012
|
}
|
|
3950
|
-
existingSettings = await readJsonSafe(
|
|
3951
|
-
existingMcpConfig = await readJsonSafe(
|
|
4013
|
+
existingSettings = await readJsonSafe(path10.join(claudeDir, "settings.json"));
|
|
4014
|
+
existingMcpConfig = await readJsonSafe(path10.join(dir, ".mcp.json"));
|
|
3952
4015
|
if (existingMcpConfig?.mcpServers) {
|
|
3953
4016
|
mcpServerCount = Object.keys(existingMcpConfig.mcpServers).length;
|
|
3954
4017
|
}
|
|
3955
|
-
existingCommands = (await listDirSafe(
|
|
3956
|
-
existingRules = (await listDirSafe(
|
|
3957
|
-
existingSkills = await listDirSafe(
|
|
3958
|
-
existingAgents = (await listDirSafe(
|
|
4018
|
+
existingCommands = (await listDirSafe(path10.join(claudeDir, "commands"))).filter((f) => f.endsWith(".md")).map((f) => f.replace(".md", ""));
|
|
4019
|
+
existingRules = (await listDirSafe(path10.join(claudeDir, "rules"))).filter((f) => f.endsWith(".md")).map((f) => f.replace(".md", ""));
|
|
4020
|
+
existingSkills = await listDirSafe(path10.join(claudeDir, "skills"));
|
|
4021
|
+
existingAgents = (await listDirSafe(path10.join(claudeDir, "agents"))).filter((f) => f.endsWith(".md")).map((f) => f.replace(".md", ""));
|
|
3959
4022
|
}
|
|
3960
|
-
const name = pkg2?.name ||
|
|
4023
|
+
const name = pkg2?.name || path10.basename(dir);
|
|
3961
4024
|
const description = pkg2?.description || "";
|
|
3962
4025
|
return {
|
|
3963
4026
|
name,
|
|
3964
4027
|
description,
|
|
3965
4028
|
directory: dir,
|
|
3966
4029
|
language,
|
|
4030
|
+
languages: detectedLanguages,
|
|
4031
|
+
languageLocations,
|
|
3967
4032
|
framework,
|
|
3968
4033
|
typescript,
|
|
3969
4034
|
dependencies: deps,
|
|
@@ -3991,9 +4056,18 @@ async function scanProject(dir) {
|
|
|
3991
4056
|
keyFiles
|
|
3992
4057
|
};
|
|
3993
4058
|
}
|
|
4059
|
+
var LANGUAGE_SIGNALS;
|
|
3994
4060
|
var init_scan = __esm({
|
|
3995
4061
|
"src/scanner/scan.ts"() {
|
|
3996
4062
|
"use strict";
|
|
4063
|
+
LANGUAGE_SIGNALS = [
|
|
4064
|
+
{ files: ["tsconfig.json"], language: "TypeScript" },
|
|
4065
|
+
{ files: ["package.json"], language: "JavaScript" },
|
|
4066
|
+
{ files: ["pyproject.toml", "setup.py", "requirements.txt"], language: "Python" },
|
|
4067
|
+
{ files: ["Cargo.toml"], language: "Rust" },
|
|
4068
|
+
{ files: ["go.mod"], language: "Go" },
|
|
4069
|
+
{ files: ["Gemfile"], language: "Ruby" }
|
|
4070
|
+
];
|
|
3997
4071
|
}
|
|
3998
4072
|
});
|
|
3999
4073
|
|
|
@@ -4014,13 +4088,14 @@ var init_types3 = __esm({
|
|
|
4014
4088
|
});
|
|
4015
4089
|
|
|
4016
4090
|
// src/analyzer/patterns.ts
|
|
4017
|
-
import
|
|
4018
|
-
import
|
|
4091
|
+
import fs11 from "fs/promises";
|
|
4092
|
+
import path11 from "path";
|
|
4019
4093
|
function getStrategy(language) {
|
|
4020
4094
|
if (language === null) {
|
|
4021
4095
|
return null;
|
|
4022
4096
|
}
|
|
4023
4097
|
const key = language.toLowerCase();
|
|
4098
|
+
if (key === "javascript") return STRATEGIES["typescript"] ?? null;
|
|
4024
4099
|
return STRATEGIES[key] ?? null;
|
|
4025
4100
|
}
|
|
4026
4101
|
function getAlwaysInclude() {
|
|
@@ -4051,6 +4126,26 @@ function classifyFilePriority(filePath, strategy) {
|
|
|
4051
4126
|
}
|
|
4052
4127
|
return 3 /* OTHER */;
|
|
4053
4128
|
}
|
|
4129
|
+
function dedupe(arr) {
|
|
4130
|
+
return Array.from(new Set(arr));
|
|
4131
|
+
}
|
|
4132
|
+
function mergeStrategies(strategies) {
|
|
4133
|
+
if (strategies.length === 0) {
|
|
4134
|
+
throw new Error("mergeStrategies requires at least one strategy");
|
|
4135
|
+
}
|
|
4136
|
+
if (strategies.length === 1) {
|
|
4137
|
+
return strategies[0];
|
|
4138
|
+
}
|
|
4139
|
+
return {
|
|
4140
|
+
language: strategies.map((s) => s.language).join("/"),
|
|
4141
|
+
extensions: dedupe(strategies.flatMap((s) => s.extensions)),
|
|
4142
|
+
entryPoints: dedupe(strategies.flatMap((s) => s.entryPoints)),
|
|
4143
|
+
domainPatterns: dedupe(strategies.flatMap((s) => s.domainPatterns)),
|
|
4144
|
+
configPatterns: dedupe(strategies.flatMap((s) => s.configPatterns)),
|
|
4145
|
+
excludePatterns: dedupe(strategies.flatMap((s) => s.excludePatterns)),
|
|
4146
|
+
maxFilesPerCategory: Math.max(...strategies.map((s) => s.maxFilesPerCategory))
|
|
4147
|
+
};
|
|
4148
|
+
}
|
|
4054
4149
|
function extractPathsFromScripts(scripts) {
|
|
4055
4150
|
const paths = [];
|
|
4056
4151
|
const interestingScripts = ["start", "dev", "serve", "main", "server"];
|
|
@@ -4080,7 +4175,7 @@ function extractPathsFromScripts(scripts) {
|
|
|
4080
4175
|
async function resolveNodeEntryPoints(dir) {
|
|
4081
4176
|
const paths = [];
|
|
4082
4177
|
try {
|
|
4083
|
-
const raw = await
|
|
4178
|
+
const raw = await fs11.readFile(path11.join(dir, "package.json"), "utf-8");
|
|
4084
4179
|
const pkg2 = JSON.parse(raw);
|
|
4085
4180
|
if (typeof pkg2.main === "string") {
|
|
4086
4181
|
paths.push(pkg2.main);
|
|
@@ -4119,7 +4214,7 @@ async function resolveNodeEntryPoints(dir) {
|
|
|
4119
4214
|
async function resolvePythonEntryPoints(dir) {
|
|
4120
4215
|
const paths = [];
|
|
4121
4216
|
try {
|
|
4122
|
-
const raw = await
|
|
4217
|
+
const raw = await fs11.readFile(path11.join(dir, "pyproject.toml"), "utf-8");
|
|
4123
4218
|
const scriptMatches = raw.matchAll(/^\s*\w+\s*=\s*"([^":]+)/gm);
|
|
4124
4219
|
for (const m of scriptMatches) {
|
|
4125
4220
|
const modulePath = m[1].replace(/\./g, "/") + ".py";
|
|
@@ -4130,17 +4225,17 @@ async function resolvePythonEntryPoints(dir) {
|
|
|
4130
4225
|
}
|
|
4131
4226
|
for (const f of ["manage.py", "wsgi.py", "asgi.py"]) {
|
|
4132
4227
|
try {
|
|
4133
|
-
await
|
|
4228
|
+
await fs11.access(path11.join(dir, f));
|
|
4134
4229
|
paths.push(f);
|
|
4135
4230
|
} catch {
|
|
4136
4231
|
}
|
|
4137
4232
|
}
|
|
4138
4233
|
try {
|
|
4139
|
-
const entries = await
|
|
4234
|
+
const entries = await fs11.readdir(path11.join(dir, "src"), { withFileTypes: true });
|
|
4140
4235
|
for (const entry of entries) {
|
|
4141
4236
|
if (entry.isDirectory()) {
|
|
4142
4237
|
try {
|
|
4143
|
-
await
|
|
4238
|
+
await fs11.access(path11.join(dir, "src", entry.name, "__main__.py"));
|
|
4144
4239
|
paths.push(`src/${entry.name}/__main__.py`);
|
|
4145
4240
|
paths.push(`src/${entry.name}/__init__.py`);
|
|
4146
4241
|
} catch {
|
|
@@ -4154,7 +4249,7 @@ async function resolvePythonEntryPoints(dir) {
|
|
|
4154
4249
|
async function resolveRustEntryPoints(dir) {
|
|
4155
4250
|
const paths = [];
|
|
4156
4251
|
try {
|
|
4157
|
-
const raw = await
|
|
4252
|
+
const raw = await fs11.readFile(path11.join(dir, "Cargo.toml"), "utf-8");
|
|
4158
4253
|
const binPaths = raw.matchAll(/path\s*=\s*"([^"]+\.rs)"/g);
|
|
4159
4254
|
for (const m of binPaths) paths.push(m[1]);
|
|
4160
4255
|
} catch {
|
|
@@ -4164,7 +4259,7 @@ async function resolveRustEntryPoints(dir) {
|
|
|
4164
4259
|
async function resolveGoEntryPoints(dir) {
|
|
4165
4260
|
const paths = [];
|
|
4166
4261
|
try {
|
|
4167
|
-
const cmdEntries = await
|
|
4262
|
+
const cmdEntries = await fs11.readdir(path11.join(dir, "cmd"), { withFileTypes: true });
|
|
4168
4263
|
for (const entry of cmdEntries) {
|
|
4169
4264
|
if (entry.isDirectory()) {
|
|
4170
4265
|
paths.push(`cmd/${entry.name}/main.go`);
|
|
@@ -4330,9 +4425,9 @@ var init_patterns = __esm({
|
|
|
4330
4425
|
});
|
|
4331
4426
|
|
|
4332
4427
|
// src/analyzer/repomix-adapter.ts
|
|
4333
|
-
import
|
|
4428
|
+
import path12 from "path";
|
|
4334
4429
|
import os3 from "os";
|
|
4335
|
-
import
|
|
4430
|
+
import fs12 from "fs/promises";
|
|
4336
4431
|
import {
|
|
4337
4432
|
pack,
|
|
4338
4433
|
mergeConfigs,
|
|
@@ -4342,7 +4437,7 @@ import {
|
|
|
4342
4437
|
} from "repomix";
|
|
4343
4438
|
async function packCodebase(dir, options) {
|
|
4344
4439
|
setLogLevel(0);
|
|
4345
|
-
const tempOutputFile =
|
|
4440
|
+
const tempOutputFile = path12.join(
|
|
4346
4441
|
os3.tmpdir(),
|
|
4347
4442
|
`kairn-repomix-${Date.now()}-${Math.random().toString(36).slice(2)}.txt`
|
|
4348
4443
|
);
|
|
@@ -4413,7 +4508,7 @@ ${f.content}`).join("\n\n");
|
|
|
4413
4508
|
message
|
|
4414
4509
|
);
|
|
4415
4510
|
} finally {
|
|
4416
|
-
await
|
|
4511
|
+
await fs12.rm(tempOutputFile, { force: true }).catch(() => {
|
|
4417
4512
|
});
|
|
4418
4513
|
}
|
|
4419
4514
|
}
|
|
@@ -4425,50 +4520,64 @@ var init_repomix_adapter = __esm({
|
|
|
4425
4520
|
});
|
|
4426
4521
|
|
|
4427
4522
|
// src/analyzer/cache.ts
|
|
4428
|
-
import
|
|
4523
|
+
import fs13 from "fs/promises";
|
|
4429
4524
|
import fsSync from "fs";
|
|
4430
|
-
import
|
|
4525
|
+
import path13 from "path";
|
|
4431
4526
|
import { createHash } from "crypto";
|
|
4432
4527
|
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
4433
4528
|
function getKairnVersion() {
|
|
4434
|
-
let dir =
|
|
4529
|
+
let dir = path13.dirname(fileURLToPath3(import.meta.url));
|
|
4435
4530
|
for (let i = 0; i < 5; i++) {
|
|
4436
4531
|
try {
|
|
4437
|
-
const pkgPath =
|
|
4532
|
+
const pkgPath = path13.join(dir, "package.json");
|
|
4438
4533
|
const content = fsSync.readFileSync(pkgPath, "utf-8");
|
|
4439
4534
|
const parsed = JSON.parse(content);
|
|
4440
4535
|
if (parsed.name === "kairn-cli") return parsed.version ?? "0.0.0";
|
|
4441
4536
|
} catch {
|
|
4442
4537
|
}
|
|
4443
|
-
dir =
|
|
4538
|
+
dir = path13.dirname(dir);
|
|
4444
4539
|
}
|
|
4445
4540
|
return "0.0.0";
|
|
4446
4541
|
}
|
|
4447
4542
|
async function readCache(dir) {
|
|
4448
|
-
const filePath =
|
|
4543
|
+
const filePath = path13.join(dir, CACHE_FILENAME);
|
|
4449
4544
|
try {
|
|
4450
|
-
const raw = await
|
|
4545
|
+
const raw = await fs13.readFile(filePath, "utf-8");
|
|
4451
4546
|
const parsed = JSON.parse(raw);
|
|
4452
|
-
|
|
4547
|
+
if (typeof parsed !== "object" || parsed === null || typeof parsed.analysis !== "object" || parsed.analysis === null) {
|
|
4548
|
+
return null;
|
|
4549
|
+
}
|
|
4550
|
+
const cache = parsed;
|
|
4551
|
+
let packedSource = null;
|
|
4552
|
+
try {
|
|
4553
|
+
const packedPath = path13.join(dir, PACKED_SOURCE_FILENAME);
|
|
4554
|
+
packedSource = await fs13.readFile(packedPath, "utf-8");
|
|
4555
|
+
} catch {
|
|
4556
|
+
}
|
|
4557
|
+
return { ...cache, packedSource };
|
|
4453
4558
|
} catch {
|
|
4454
4559
|
return null;
|
|
4455
4560
|
}
|
|
4456
4561
|
}
|
|
4457
|
-
async function writeCache(dir, analysis) {
|
|
4458
|
-
const filePath =
|
|
4562
|
+
async function writeCache(dir, analysis, packedSource) {
|
|
4563
|
+
const filePath = path13.join(dir, CACHE_FILENAME);
|
|
4459
4564
|
const cache = {
|
|
4460
4565
|
analysis,
|
|
4461
4566
|
content_hash: analysis.content_hash,
|
|
4462
4567
|
kairn_version: KAIRN_VERSION
|
|
4463
4568
|
};
|
|
4464
|
-
await
|
|
4569
|
+
await fs13.writeFile(filePath, JSON.stringify(cache, null, 2), "utf-8");
|
|
4570
|
+
if (packedSource !== void 0) {
|
|
4571
|
+
const packedPath = path13.join(dir, PACKED_SOURCE_FILENAME);
|
|
4572
|
+
await fs13.writeFile(packedPath, packedSource, "utf-8");
|
|
4573
|
+
}
|
|
4465
4574
|
}
|
|
4466
4575
|
async function computeContentHash(filePaths, dir) {
|
|
4467
4576
|
const hash = createHash("sha256");
|
|
4468
4577
|
for (const filePath of filePaths) {
|
|
4469
4578
|
try {
|
|
4470
|
-
const fullPath =
|
|
4471
|
-
const content = await
|
|
4579
|
+
const fullPath = path13.join(dir, filePath);
|
|
4580
|
+
const content = await fs13.readFile(fullPath, "utf-8");
|
|
4472
4581
|
hash.update(content);
|
|
4473
4582
|
} catch {
|
|
4474
4583
|
}
|
|
@@ -4478,12 +4587,13 @@ async function computeContentHash(filePaths, dir) {
|
|
|
4478
4587
|
function isCacheValid(cache, currentHash) {
|
|
4479
4588
|
return cache.content_hash === currentHash && cache.kairn_version === KAIRN_VERSION;
|
|
4480
4589
|
}
|
|
4481
|
-
var KAIRN_VERSION, CACHE_FILENAME;
|
|
4590
|
+
var KAIRN_VERSION, CACHE_FILENAME, PACKED_SOURCE_FILENAME;
|
|
4482
4591
|
var init_cache = __esm({
|
|
4483
4592
|
"src/analyzer/cache.ts"() {
|
|
4484
4593
|
"use strict";
|
|
4485
4594
|
KAIRN_VERSION = getKairnVersion();
|
|
4486
4595
|
CACHE_FILENAME = ".kairn-analysis.json";
|
|
4596
|
+
PACKED_SOURCE_FILENAME = ".kairn-packed-source.txt";
|
|
4487
4597
|
}
|
|
4488
4598
|
});
|
|
4489
4599
|
|
|
@@ -4507,6 +4617,39 @@ function validateArray(raw, guard) {
|
|
|
4507
4617
|
if (!Array.isArray(raw)) return [];
|
|
4508
4618
|
return raw.filter(guard);
|
|
4509
4619
|
}
|
|
4620
|
+
function scopeStrategyToSubdirs(strategy, subdirs) {
|
|
4621
|
+
if (subdirs.length === 0) return strategy;
|
|
4622
|
+
return {
|
|
4623
|
+
...strategy,
|
|
4624
|
+
entryPoints: subdirs.flatMap(
|
|
4625
|
+
(dir) => strategy.entryPoints.map((ep) => `${dir}/${ep}`)
|
|
4626
|
+
),
|
|
4627
|
+
domainPatterns: subdirs.flatMap(
|
|
4628
|
+
(dir) => strategy.domainPatterns.map((dp) => `${dir}/${dp}`)
|
|
4629
|
+
),
|
|
4630
|
+
configPatterns: subdirs.flatMap(
|
|
4631
|
+
(dir) => strategy.configPatterns.map((cp) => `${dir}/${cp}`)
|
|
4632
|
+
)
|
|
4633
|
+
// excludePatterns: keep global (they use ** globs)
|
|
4634
|
+
};
|
|
4635
|
+
}
|
|
4636
|
+
function getLanguageWeight(filePath, languageLocations) {
|
|
4637
|
+
if (languageLocations.length <= 1) return 0;
|
|
4638
|
+
const subdirToRank = /* @__PURE__ */ new Map();
|
|
4639
|
+
for (let rank2 = 0; rank2 < languageLocations.length; rank2++) {
|
|
4640
|
+
for (const subdir of languageLocations[rank2].subdirs) {
|
|
4641
|
+
subdirToRank.set(subdir, rank2);
|
|
4642
|
+
}
|
|
4643
|
+
}
|
|
4644
|
+
if (subdirToRank.size === 0) return 0;
|
|
4645
|
+
const firstSlash = filePath.indexOf("/");
|
|
4646
|
+
if (firstSlash === -1) {
|
|
4647
|
+
return 0;
|
|
4648
|
+
}
|
|
4649
|
+
const firstSegment = filePath.slice(0, firstSlash);
|
|
4650
|
+
const rank = subdirToRank.get(firstSegment);
|
|
4651
|
+
return rank ?? 0;
|
|
4652
|
+
}
|
|
4510
4653
|
async function analyzeProject(dir, profile, config, options) {
|
|
4511
4654
|
if (!options?.refresh) {
|
|
4512
4655
|
const cache = await readCache(dir);
|
|
@@ -4516,24 +4659,31 @@ async function analyzeProject(dir, profile, config, options) {
|
|
|
4516
4659
|
dir
|
|
4517
4660
|
);
|
|
4518
4661
|
if (isCacheValid(cache, currentHash)) {
|
|
4519
|
-
return
|
|
4662
|
+
return {
|
|
4663
|
+
analysis: cache.analysis,
|
|
4664
|
+
packedSource: cache.packedSource ?? ""
|
|
4665
|
+
};
|
|
4520
4666
|
}
|
|
4521
4667
|
}
|
|
4522
4668
|
}
|
|
4523
|
-
const
|
|
4524
|
-
|
|
4669
|
+
const languageLocations = profile.languageLocations ?? profile.languages.map((lang) => ({ language: lang, subdirs: [] }));
|
|
4670
|
+
const resolvedStrategies = await Promise.all(
|
|
4671
|
+
languageLocations.map(async ({ language, subdirs }) => {
|
|
4672
|
+
const base = getStrategy(language);
|
|
4673
|
+
if (!base) return null;
|
|
4674
|
+
const enriched = await resolveStrategy(dir, base, profile.framework, profile.scripts);
|
|
4675
|
+
return scopeStrategyToSubdirs(enriched, subdirs);
|
|
4676
|
+
})
|
|
4677
|
+
);
|
|
4678
|
+
const validStrategies = resolvedStrategies.filter((s) => s !== null);
|
|
4679
|
+
if (validStrategies.length === 0) {
|
|
4525
4680
|
throw new AnalysisError(
|
|
4526
|
-
`No sampling strategy for
|
|
4681
|
+
`No sampling strategy for languages: ${profile.languages.join(", ") || "none detected"}`,
|
|
4527
4682
|
"no_entry_point",
|
|
4528
4683
|
"Supported: Python, TypeScript, Go, Rust"
|
|
4529
4684
|
);
|
|
4530
4685
|
}
|
|
4531
|
-
const strategy =
|
|
4532
|
-
dir,
|
|
4533
|
-
baseStrategy,
|
|
4534
|
-
profile.framework,
|
|
4535
|
-
profile.scripts
|
|
4536
|
-
);
|
|
4686
|
+
const strategy = mergeStrategies(validStrategies);
|
|
4537
4687
|
const include = [
|
|
4538
4688
|
...strategy.entryPoints,
|
|
4539
4689
|
...strategy.domainPatterns.map((p) => p + "**/*"),
|
|
@@ -4544,7 +4694,11 @@ async function analyzeProject(dir, profile, config, options) {
|
|
|
4544
4694
|
include,
|
|
4545
4695
|
exclude: strategy.excludePatterns,
|
|
4546
4696
|
maxTokens: options?.tokenBudget ?? DEFAULT_TOKEN_BUDGET,
|
|
4547
|
-
prioritize: (filePath) =>
|
|
4697
|
+
prioritize: (filePath) => {
|
|
4698
|
+
const baseTier = classifyFilePriority(filePath, strategy);
|
|
4699
|
+
const langWeight = getLanguageWeight(filePath, languageLocations);
|
|
4700
|
+
return baseTier + langWeight * 0.1;
|
|
4701
|
+
}
|
|
4548
4702
|
});
|
|
4549
4703
|
if (packed.fileCount === 0) {
|
|
4550
4704
|
throw new AnalysisError(
|
|
@@ -4582,8 +4736,8 @@ async function analyzeProject(dir, profile, config, options) {
|
|
|
4582
4736
|
content_hash: contentHash,
|
|
4583
4737
|
analyzed_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
4584
4738
|
};
|
|
4585
|
-
await writeCache(dir, analysis);
|
|
4586
|
-
return analysis;
|
|
4739
|
+
await writeCache(dir, analysis, packed.content);
|
|
4740
|
+
return { analysis, packedSource: packed.content };
|
|
4587
4741
|
}
|
|
4588
4742
|
function buildUserMessage6(language, profile, packedContent) {
|
|
4589
4743
|
return [
|
|
@@ -4656,8 +4810,8 @@ import { Command as Command2 } from "commander";
|
|
|
4656
4810
|
import { confirm as confirm2 } from "@inquirer/prompts";
|
|
4657
4811
|
import chalk5 from "chalk";
|
|
4658
4812
|
import ora from "ora";
|
|
4659
|
-
import
|
|
4660
|
-
import
|
|
4813
|
+
import fs14 from "fs/promises";
|
|
4814
|
+
import path14 from "path";
|
|
4661
4815
|
function simpleDiff(oldContent, newContent) {
|
|
4662
4816
|
const oldLines = oldContent.split("\n");
|
|
4663
4817
|
const newLines = newContent.split("\n");
|
|
@@ -4681,10 +4835,10 @@ async function generateDiff(spec, targetDir) {
|
|
|
4681
4835
|
const fileMap = buildFileMap(spec);
|
|
4682
4836
|
const results = [];
|
|
4683
4837
|
for (const [relativePath, newContent] of fileMap) {
|
|
4684
|
-
const absolutePath =
|
|
4838
|
+
const absolutePath = path14.join(targetDir, relativePath);
|
|
4685
4839
|
let oldContent = null;
|
|
4686
4840
|
try {
|
|
4687
|
-
oldContent = await
|
|
4841
|
+
oldContent = await fs14.readFile(absolutePath, "utf-8");
|
|
4688
4842
|
} catch {
|
|
4689
4843
|
}
|
|
4690
4844
|
if (oldContent === null) {
|
|
@@ -4714,7 +4868,7 @@ function buildProfileSummary(profile) {
|
|
|
4714
4868
|
const lines = [];
|
|
4715
4869
|
lines.push(`Project: ${profile.name}`);
|
|
4716
4870
|
if (profile.description) lines.push(`Description: ${profile.description}`);
|
|
4717
|
-
if (profile.
|
|
4871
|
+
if (profile.languages.length > 0) lines.push(`Languages: ${profile.languages.join(", ")}`);
|
|
4718
4872
|
if (profile.framework) lines.push(`Framework: ${profile.framework}`);
|
|
4719
4873
|
if (profile.dependencies.length > 0) {
|
|
4720
4874
|
lines.push(`Dependencies: ${profile.dependencies.join(", ")}`);
|
|
@@ -4741,7 +4895,7 @@ Existing .claude/ harness found:`);
|
|
|
4741
4895
|
lines.push(` Agents: ${profile.existingAgents.length > 0 ? profile.existingAgents.join(", ") : "none"}`);
|
|
4742
4896
|
return lines.join("\n");
|
|
4743
4897
|
}
|
|
4744
|
-
function buildOptimizeIntent(profile, analysis) {
|
|
4898
|
+
function buildOptimizeIntent(profile, analysis, packedSource) {
|
|
4745
4899
|
const parts = [];
|
|
4746
4900
|
parts.push("## Project Profile (scanned from actual codebase)\n");
|
|
4747
4901
|
parts.push(buildProfileSummary(profile));
|
|
@@ -4826,6 +4980,13 @@ ${profile.existingClaudeMd}`);
|
|
|
4826
4980
|
}
|
|
4827
4981
|
}
|
|
4828
4982
|
}
|
|
4983
|
+
if (packedSource) {
|
|
4984
|
+
parts.push(`
|
|
4985
|
+
|
|
4986
|
+
## Sampled Source Code (reference for project-specific content)
|
|
4987
|
+
|
|
4988
|
+
${packedSource}`);
|
|
4989
|
+
}
|
|
4829
4990
|
return parts.join("\n");
|
|
4830
4991
|
}
|
|
4831
4992
|
var optimizeCommand;
|
|
@@ -4843,6 +5004,7 @@ var init_optimize = __esm({
|
|
|
4843
5004
|
init_logo();
|
|
4844
5005
|
init_analyze();
|
|
4845
5006
|
init_types3();
|
|
5007
|
+
init_persist();
|
|
4846
5008
|
optimizeCommand = new Command2("optimize").description("Scan an existing project and generate or optimize its Claude Code environment").option("-y, --yes", "Skip confirmation prompts").option("--audit-only", "Only audit the existing harness, don't generate changes").option("--diff", "Preview changes as a diff without writing").option("--runtime <runtime>", "Target runtime (claude-code or hermes)", "claude-code").action(async (options) => {
|
|
4847
5009
|
printCompactBanner();
|
|
4848
5010
|
const config = await loadConfig();
|
|
@@ -4855,7 +5017,7 @@ var init_optimize = __esm({
|
|
|
4855
5017
|
const scanSpinner = ora({ text: "Scanning project...", indent: 2 }).start();
|
|
4856
5018
|
const profile = await scanProject(targetDir);
|
|
4857
5019
|
scanSpinner.stop();
|
|
4858
|
-
if (profile.
|
|
5020
|
+
if (profile.languages.length > 0) console.log(ui.kv("Languages:", profile.languages.join(", ")));
|
|
4859
5021
|
if (profile.framework) console.log(ui.kv("Framework:", profile.framework));
|
|
4860
5022
|
console.log(ui.kv("Dependencies:", String(profile.dependencies.length)));
|
|
4861
5023
|
if (profile.testCommand) console.log(ui.kv("Tests:", profile.testCommand));
|
|
@@ -4866,13 +5028,19 @@ var init_optimize = __esm({
|
|
|
4866
5028
|
console.log(ui.section("Codebase Analysis"));
|
|
4867
5029
|
const analysisSpinner = ora({ text: "Analyzing source code...", indent: 2 }).start();
|
|
4868
5030
|
let analysis = null;
|
|
5031
|
+
let packedSource = "";
|
|
4869
5032
|
try {
|
|
4870
|
-
|
|
5033
|
+
const result = await analyzeProject(targetDir, profile, config);
|
|
5034
|
+
analysis = result.analysis;
|
|
5035
|
+
packedSource = result.packedSource;
|
|
4871
5036
|
analysisSpinner.succeed("Codebase analyzed");
|
|
4872
5037
|
console.log(ui.kv("Purpose:", analysis.purpose));
|
|
4873
5038
|
console.log(ui.kv("Domain:", analysis.domain));
|
|
4874
5039
|
console.log(ui.kv("Modules:", analysis.key_modules.map((m) => m.name).join(", ") || "none detected"));
|
|
4875
5040
|
console.log(ui.kv("Workflows:", analysis.workflows.map((w) => w.name).join(", ") || "none detected"));
|
|
5041
|
+
if (packedSource) {
|
|
5042
|
+
console.log(ui.kv("Source:", `${packedSource.length.toLocaleString()} chars sampled`));
|
|
5043
|
+
}
|
|
4876
5044
|
} catch (err) {
|
|
4877
5045
|
if (err instanceof AnalysisError) {
|
|
4878
5046
|
analysisSpinner.fail("Analysis failed");
|
|
@@ -4940,7 +5108,7 @@ Run \`kairn analyze\` for details.`));
|
|
|
4940
5108
|
}
|
|
4941
5109
|
}
|
|
4942
5110
|
}
|
|
4943
|
-
const intent = buildOptimizeIntent(profile, analysis);
|
|
5111
|
+
const intent = buildOptimizeIntent(profile, analysis, packedSource);
|
|
4944
5112
|
let spec;
|
|
4945
5113
|
const spinner = ora({ text: "Compiling optimized environment...", indent: 2 }).start();
|
|
4946
5114
|
try {
|
|
@@ -4954,6 +5122,13 @@ Run \`kairn analyze\` for details.`));
|
|
|
4954
5122
|
console.log(ui.errorBox("KAIRN \u2014 Error", `Optimization failed: ${msg}`));
|
|
4955
5123
|
process.exit(1);
|
|
4956
5124
|
}
|
|
5125
|
+
if (spec.ir) {
|
|
5126
|
+
try {
|
|
5127
|
+
await persistHarnessIR(targetDir, spec.ir);
|
|
5128
|
+
} catch {
|
|
5129
|
+
console.log(ui.warn("Could not persist harness IR to .kairn/harness-ir.json"));
|
|
5130
|
+
}
|
|
5131
|
+
}
|
|
4957
5132
|
const registry = await loadRegistry();
|
|
4958
5133
|
const summary = summarizeSpec(spec, registry);
|
|
4959
5134
|
console.log("");
|
|
@@ -5034,37 +5209,37 @@ Run \`kairn analyze\` for details.`));
|
|
|
5034
5209
|
});
|
|
5035
5210
|
|
|
5036
5211
|
// src/evolve/baseline.ts
|
|
5037
|
-
import
|
|
5038
|
-
import
|
|
5212
|
+
import fs21 from "fs/promises";
|
|
5213
|
+
import path21 from "path";
|
|
5039
5214
|
async function snapshotBaseline(projectRoot, workspacePath) {
|
|
5040
|
-
const claudeDir =
|
|
5041
|
-
const baselineDir =
|
|
5042
|
-
const iter0Dir =
|
|
5215
|
+
const claudeDir = path21.join(projectRoot, ".claude");
|
|
5216
|
+
const baselineDir = path21.join(workspacePath, "baseline");
|
|
5217
|
+
const iter0Dir = path21.join(workspacePath, "iterations", "0", "harness");
|
|
5043
5218
|
try {
|
|
5044
|
-
await
|
|
5219
|
+
await fs21.access(claudeDir);
|
|
5045
5220
|
} catch {
|
|
5046
5221
|
throw new Error(`.claude/ directory not found in ${projectRoot}`);
|
|
5047
5222
|
}
|
|
5048
5223
|
await copyDir(claudeDir, baselineDir);
|
|
5049
5224
|
await copyDir(claudeDir, iter0Dir);
|
|
5050
|
-
const mcpJsonPath =
|
|
5225
|
+
const mcpJsonPath = path21.join(projectRoot, ".mcp.json");
|
|
5051
5226
|
try {
|
|
5052
|
-
await
|
|
5053
|
-
await
|
|
5054
|
-
await
|
|
5227
|
+
await fs21.access(mcpJsonPath);
|
|
5228
|
+
await fs21.copyFile(mcpJsonPath, path21.join(baselineDir, ".mcp.json"));
|
|
5229
|
+
await fs21.copyFile(mcpJsonPath, path21.join(iter0Dir, ".mcp.json"));
|
|
5055
5230
|
} catch {
|
|
5056
5231
|
}
|
|
5057
5232
|
}
|
|
5058
5233
|
async function copyDir(src, dest) {
|
|
5059
|
-
await
|
|
5060
|
-
const entries = await
|
|
5234
|
+
await fs21.mkdir(dest, { recursive: true });
|
|
5235
|
+
const entries = await fs21.readdir(src, { withFileTypes: true });
|
|
5061
5236
|
for (const entry of entries) {
|
|
5062
|
-
const srcPath =
|
|
5063
|
-
const destPath =
|
|
5237
|
+
const srcPath = path21.join(src, entry.name);
|
|
5238
|
+
const destPath = path21.join(dest, entry.name);
|
|
5064
5239
|
if (entry.isDirectory()) {
|
|
5065
5240
|
await copyDir(srcPath, destPath);
|
|
5066
5241
|
} else {
|
|
5067
|
-
await
|
|
5242
|
+
await fs21.copyFile(srcPath, destPath);
|
|
5068
5243
|
}
|
|
5069
5244
|
}
|
|
5070
5245
|
}
|
|
@@ -5075,32 +5250,32 @@ var init_baseline = __esm({
|
|
|
5075
5250
|
});
|
|
5076
5251
|
|
|
5077
5252
|
// src/evolve/trace.ts
|
|
5078
|
-
import
|
|
5079
|
-
import
|
|
5253
|
+
import fs22 from "fs/promises";
|
|
5254
|
+
import path22 from "path";
|
|
5080
5255
|
async function loadTrace(traceDir) {
|
|
5081
|
-
const stdout = await
|
|
5082
|
-
const stderr = await
|
|
5083
|
-
const filesChangedStr = await
|
|
5084
|
-
|
|
5256
|
+
const stdout = await fs22.readFile(path22.join(traceDir, "stdout.log"), "utf-8").catch(() => "");
|
|
5257
|
+
const stderr = await fs22.readFile(path22.join(traceDir, "stderr.log"), "utf-8").catch(() => "");
|
|
5258
|
+
const filesChangedStr = await fs22.readFile(
|
|
5259
|
+
path22.join(traceDir, "files_changed.json"),
|
|
5085
5260
|
"utf-8"
|
|
5086
5261
|
).catch(() => "{}");
|
|
5087
|
-
const timingStr = await
|
|
5088
|
-
|
|
5262
|
+
const timingStr = await fs22.readFile(
|
|
5263
|
+
path22.join(traceDir, "timing.json"),
|
|
5089
5264
|
"utf-8"
|
|
5090
5265
|
).catch(() => "{}");
|
|
5091
|
-
const scoreStr = await
|
|
5092
|
-
|
|
5266
|
+
const scoreStr = await fs22.readFile(
|
|
5267
|
+
path22.join(traceDir, "score.json"),
|
|
5093
5268
|
"utf-8"
|
|
5094
5269
|
).catch(() => '{"pass": false}');
|
|
5095
|
-
const toolCallsStr = await
|
|
5096
|
-
|
|
5270
|
+
const toolCallsStr = await fs22.readFile(
|
|
5271
|
+
path22.join(traceDir, "tool_calls.jsonl"),
|
|
5097
5272
|
"utf-8"
|
|
5098
5273
|
).catch(() => "");
|
|
5099
5274
|
const toolCalls = toolCallsStr.split("\n").filter((line) => line.trim()).map((line) => JSON.parse(line));
|
|
5100
|
-
const parentDir =
|
|
5275
|
+
const parentDir = path22.basename(path22.dirname(traceDir));
|
|
5101
5276
|
const iteration = parseInt(parentDir, 10) || 0;
|
|
5102
5277
|
return {
|
|
5103
|
-
taskId:
|
|
5278
|
+
taskId: path22.basename(traceDir),
|
|
5104
5279
|
iteration,
|
|
5105
5280
|
stdout,
|
|
5106
5281
|
stderr,
|
|
@@ -5111,12 +5286,12 @@ async function loadTrace(traceDir) {
|
|
|
5111
5286
|
};
|
|
5112
5287
|
}
|
|
5113
5288
|
async function loadIterationTraces(workspacePath, iteration) {
|
|
5114
|
-
const tracesDir =
|
|
5289
|
+
const tracesDir = path22.join(workspacePath, "traces", iteration.toString());
|
|
5115
5290
|
const traces = [];
|
|
5116
5291
|
try {
|
|
5117
|
-
const taskDirs = await
|
|
5292
|
+
const taskDirs = await fs22.readdir(tracesDir);
|
|
5118
5293
|
for (const taskId of taskDirs) {
|
|
5119
|
-
const trace = await loadTrace(
|
|
5294
|
+
const trace = await loadTrace(path22.join(tracesDir, taskId));
|
|
5120
5295
|
traces.push(trace);
|
|
5121
5296
|
}
|
|
5122
5297
|
} catch {
|
|
@@ -5124,39 +5299,39 @@ async function loadIterationTraces(workspacePath, iteration) {
|
|
|
5124
5299
|
return traces;
|
|
5125
5300
|
}
|
|
5126
5301
|
async function writeTrace(traceDir, trace) {
|
|
5127
|
-
await
|
|
5128
|
-
await
|
|
5129
|
-
await
|
|
5302
|
+
await fs22.mkdir(traceDir, { recursive: true });
|
|
5303
|
+
await fs22.writeFile(path22.join(traceDir, "stdout.log"), trace.stdout, "utf-8");
|
|
5304
|
+
await fs22.writeFile(path22.join(traceDir, "stderr.log"), trace.stderr, "utf-8");
|
|
5130
5305
|
const toolCallsLines = trace.toolCalls.map((tc) => JSON.stringify(tc)).join("\n");
|
|
5131
|
-
await
|
|
5132
|
-
await
|
|
5133
|
-
|
|
5306
|
+
await fs22.writeFile(path22.join(traceDir, "tool_calls.jsonl"), toolCallsLines, "utf-8");
|
|
5307
|
+
await fs22.writeFile(
|
|
5308
|
+
path22.join(traceDir, "files_changed.json"),
|
|
5134
5309
|
JSON.stringify(trace.filesChanged, null, 2),
|
|
5135
5310
|
"utf-8"
|
|
5136
5311
|
);
|
|
5137
|
-
await
|
|
5138
|
-
|
|
5312
|
+
await fs22.writeFile(
|
|
5313
|
+
path22.join(traceDir, "timing.json"),
|
|
5139
5314
|
JSON.stringify(trace.timing, null, 2),
|
|
5140
5315
|
"utf-8"
|
|
5141
5316
|
);
|
|
5142
|
-
await
|
|
5143
|
-
|
|
5317
|
+
await fs22.writeFile(
|
|
5318
|
+
path22.join(traceDir, "score.json"),
|
|
5144
5319
|
JSON.stringify(trace.score, null, 2),
|
|
5145
5320
|
"utf-8"
|
|
5146
5321
|
);
|
|
5147
5322
|
}
|
|
5148
5323
|
async function writeScore(traceDir, score) {
|
|
5149
|
-
await
|
|
5150
|
-
|
|
5324
|
+
await fs22.writeFile(
|
|
5325
|
+
path22.join(traceDir, "score.json"),
|
|
5151
5326
|
JSON.stringify(score, null, 2),
|
|
5152
5327
|
"utf-8"
|
|
5153
5328
|
);
|
|
5154
5329
|
}
|
|
5155
5330
|
async function writeIterationLog(workspacePath, log) {
|
|
5156
|
-
const iterDir =
|
|
5157
|
-
await
|
|
5158
|
-
await
|
|
5159
|
-
|
|
5331
|
+
const iterDir = path22.join(workspacePath, "iterations", log.iteration.toString());
|
|
5332
|
+
await fs22.mkdir(iterDir, { recursive: true });
|
|
5333
|
+
await fs22.writeFile(
|
|
5334
|
+
path22.join(iterDir, "scores.json"),
|
|
5160
5335
|
JSON.stringify({
|
|
5161
5336
|
score: log.score,
|
|
5162
5337
|
taskResults: log.taskResults,
|
|
@@ -5164,27 +5339,27 @@ async function writeIterationLog(workspacePath, log) {
|
|
|
5164
5339
|
}, null, 2),
|
|
5165
5340
|
"utf-8"
|
|
5166
5341
|
);
|
|
5167
|
-
await
|
|
5168
|
-
|
|
5342
|
+
await fs22.writeFile(
|
|
5343
|
+
path22.join(iterDir, "proposer_reasoning.md"),
|
|
5169
5344
|
log.proposal?.reasoning ?? "Baseline evaluation (no proposal)",
|
|
5170
5345
|
"utf-8"
|
|
5171
5346
|
);
|
|
5172
|
-
await
|
|
5173
|
-
|
|
5347
|
+
await fs22.writeFile(
|
|
5348
|
+
path22.join(iterDir, "mutation_diff.patch"),
|
|
5174
5349
|
log.diffPatch ?? "",
|
|
5175
5350
|
"utf-8"
|
|
5176
5351
|
);
|
|
5177
5352
|
}
|
|
5178
5353
|
async function loadIterationLog(workspacePath, iteration) {
|
|
5179
|
-
const iterDir =
|
|
5354
|
+
const iterDir = path22.join(workspacePath, "iterations", iteration.toString());
|
|
5180
5355
|
try {
|
|
5181
|
-
await
|
|
5356
|
+
await fs22.access(iterDir);
|
|
5182
5357
|
} catch {
|
|
5183
5358
|
return null;
|
|
5184
5359
|
}
|
|
5185
|
-
const scoresStr = await
|
|
5186
|
-
const reasoning = await
|
|
5187
|
-
const diffPatch = await
|
|
5360
|
+
const scoresStr = await fs22.readFile(path22.join(iterDir, "scores.json"), "utf-8").catch(() => "{}");
|
|
5361
|
+
const reasoning = await fs22.readFile(path22.join(iterDir, "proposer_reasoning.md"), "utf-8").catch(() => "");
|
|
5362
|
+
const diffPatch = await fs22.readFile(path22.join(iterDir, "mutation_diff.patch"), "utf-8").catch(() => "");
|
|
5188
5363
|
const scoresData = JSON.parse(scoresStr);
|
|
5189
5364
|
const proposal = reasoning ? { reasoning, mutations: [], expectedImpact: {} } : null;
|
|
5190
5365
|
return {
|
|
@@ -5555,12 +5730,12 @@ Return ONLY valid JSON:
|
|
|
5555
5730
|
// src/evolve/runner.ts
|
|
5556
5731
|
import { exec as exec3, spawn } from "child_process";
|
|
5557
5732
|
import { promisify as promisify3 } from "util";
|
|
5558
|
-
import
|
|
5733
|
+
import fs23 from "fs/promises";
|
|
5559
5734
|
import os4 from "os";
|
|
5560
|
-
import
|
|
5735
|
+
import path23 from "path";
|
|
5561
5736
|
async function deployMcpJson(harnessPath, workDir) {
|
|
5562
|
-
const src =
|
|
5563
|
-
await
|
|
5737
|
+
const src = path23.join(harnessPath, ".mcp.json");
|
|
5738
|
+
await fs23.copyFile(src, path23.join(workDir, ".mcp.json")).catch(() => {
|
|
5564
5739
|
});
|
|
5565
5740
|
}
|
|
5566
5741
|
async function createIsolatedWorkspace(projectRoot, harnessPath) {
|
|
@@ -5570,40 +5745,40 @@ async function createIsolatedWorkspace(projectRoot, harnessPath) {
|
|
|
5570
5745
|
cwd: projectRoot,
|
|
5571
5746
|
timeout: 5e3
|
|
5572
5747
|
});
|
|
5573
|
-
const tmpDir2 =
|
|
5748
|
+
const tmpDir2 = path23.join(os4.tmpdir(), `kairn-evolve-wt-${suffix}`);
|
|
5574
5749
|
await execAsync3(`git worktree add --detach "${tmpDir2}" HEAD`, {
|
|
5575
5750
|
cwd: projectRoot,
|
|
5576
5751
|
timeout: 3e4
|
|
5577
5752
|
});
|
|
5578
|
-
await
|
|
5579
|
-
await copyDir(harnessPath,
|
|
5753
|
+
await fs23.rm(path23.join(tmpDir2, ".claude"), { recursive: true, force: true });
|
|
5754
|
+
await copyDir(harnessPath, path23.join(tmpDir2, ".claude"));
|
|
5580
5755
|
await deployMcpJson(harnessPath, tmpDir2);
|
|
5581
5756
|
return { workDir: tmpDir2, isWorktree: true };
|
|
5582
5757
|
} catch {
|
|
5583
5758
|
}
|
|
5584
|
-
const tmpDir = await
|
|
5759
|
+
const tmpDir = await fs23.mkdtemp(path23.join(os4.tmpdir(), `kairn-evolve-cp-`));
|
|
5585
5760
|
await copyProjectDir(projectRoot, tmpDir);
|
|
5586
|
-
await
|
|
5587
|
-
await copyDir(harnessPath,
|
|
5761
|
+
await fs23.rm(path23.join(tmpDir, ".claude"), { recursive: true, force: true });
|
|
5762
|
+
await copyDir(harnessPath, path23.join(tmpDir, ".claude"));
|
|
5588
5763
|
await deployMcpJson(harnessPath, tmpDir);
|
|
5589
5764
|
return { workDir: tmpDir, isWorktree: false };
|
|
5590
5765
|
}
|
|
5591
5766
|
async function copyProjectDir(src, dest) {
|
|
5592
|
-
await
|
|
5767
|
+
await fs23.mkdir(dest, { recursive: true });
|
|
5593
5768
|
let entries;
|
|
5594
5769
|
try {
|
|
5595
|
-
entries = await
|
|
5770
|
+
entries = await fs23.readdir(src, { withFileTypes: true });
|
|
5596
5771
|
} catch {
|
|
5597
5772
|
return;
|
|
5598
5773
|
}
|
|
5599
5774
|
for (const entry of entries) {
|
|
5600
5775
|
if (COPY_SKIP_DIRS.has(entry.name)) continue;
|
|
5601
|
-
const srcPath =
|
|
5602
|
-
const destPath =
|
|
5776
|
+
const srcPath = path23.join(src, entry.name);
|
|
5777
|
+
const destPath = path23.join(dest, entry.name);
|
|
5603
5778
|
if (entry.isDirectory()) {
|
|
5604
5779
|
await copyDir(srcPath, destPath);
|
|
5605
5780
|
} else {
|
|
5606
|
-
await
|
|
5781
|
+
await fs23.copyFile(srcPath, destPath);
|
|
5607
5782
|
}
|
|
5608
5783
|
}
|
|
5609
5784
|
}
|
|
@@ -5615,7 +5790,7 @@ async function cleanupIsolatedWorkspace(workDir, isWorktree, projectRoot) {
|
|
|
5615
5790
|
timeout: 1e4
|
|
5616
5791
|
});
|
|
5617
5792
|
} catch {
|
|
5618
|
-
await
|
|
5793
|
+
await fs23.rm(workDir, { recursive: true, force: true }).catch(() => {
|
|
5619
5794
|
});
|
|
5620
5795
|
await execAsync3("git worktree prune", {
|
|
5621
5796
|
cwd: projectRoot,
|
|
@@ -5624,12 +5799,12 @@ async function cleanupIsolatedWorkspace(workDir, isWorktree, projectRoot) {
|
|
|
5624
5799
|
});
|
|
5625
5800
|
}
|
|
5626
5801
|
} else {
|
|
5627
|
-
await
|
|
5802
|
+
await fs23.rm(workDir, { recursive: true, force: true }).catch(() => {
|
|
5628
5803
|
});
|
|
5629
5804
|
}
|
|
5630
5805
|
}
|
|
5631
5806
|
async function runTask(task, harnessPath, traceDir, iteration, projectRoot) {
|
|
5632
|
-
await
|
|
5807
|
+
await fs23.mkdir(traceDir, { recursive: true });
|
|
5633
5808
|
const startedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
5634
5809
|
const startMs = Date.now();
|
|
5635
5810
|
const root = projectRoot ?? process.cwd();
|
|
@@ -5709,13 +5884,13 @@ async function snapshotFileList(dir) {
|
|
|
5709
5884
|
async function walk(current) {
|
|
5710
5885
|
let entries;
|
|
5711
5886
|
try {
|
|
5712
|
-
entries = await
|
|
5887
|
+
entries = await fs23.readdir(current, { withFileTypes: true });
|
|
5713
5888
|
} catch {
|
|
5714
5889
|
return;
|
|
5715
5890
|
}
|
|
5716
5891
|
for (const entry of entries) {
|
|
5717
|
-
const fullPath =
|
|
5718
|
-
const relativePath =
|
|
5892
|
+
const fullPath = path23.join(current, entry.name);
|
|
5893
|
+
const relativePath = path23.relative(dir, fullPath);
|
|
5719
5894
|
if (relativePath.startsWith(".claude")) continue;
|
|
5720
5895
|
if (relativePath.startsWith("node_modules")) continue;
|
|
5721
5896
|
if (relativePath.startsWith(".git")) continue;
|
|
@@ -5723,7 +5898,7 @@ async function snapshotFileList(dir) {
|
|
|
5723
5898
|
await walk(fullPath);
|
|
5724
5899
|
} else {
|
|
5725
5900
|
try {
|
|
5726
|
-
const stat = await
|
|
5901
|
+
const stat = await fs23.stat(fullPath);
|
|
5727
5902
|
result[relativePath] = stat.mtimeMs;
|
|
5728
5903
|
} catch {
|
|
5729
5904
|
}
|
|
@@ -5802,7 +5977,7 @@ function computeStddev(values, mean) {
|
|
|
5802
5977
|
}
|
|
5803
5978
|
async function evaluateAll(tasks, harnessPath, workspacePath, iteration, config, onProgress, runsPerTask = 1, parallelTasks = 1) {
|
|
5804
5979
|
const results = {};
|
|
5805
|
-
const projectRoot =
|
|
5980
|
+
const projectRoot = path23.resolve(workspacePath, "..");
|
|
5806
5981
|
const effectiveRuns = Math.max(1, runsPerTask);
|
|
5807
5982
|
const concurrency = Math.max(1, parallelTasks);
|
|
5808
5983
|
const evaluateTask = async (task) => {
|
|
@@ -5812,7 +5987,7 @@ async function evaluateAll(tasks, harnessPath, workspacePath, iteration, config,
|
|
|
5812
5987
|
const runScores = [];
|
|
5813
5988
|
let passCount = 0;
|
|
5814
5989
|
for (let run = 0; run < effectiveRuns; run++) {
|
|
5815
|
-
const traceDir =
|
|
5990
|
+
const traceDir = path23.join(
|
|
5816
5991
|
workspacePath,
|
|
5817
5992
|
"traces",
|
|
5818
5993
|
iteration.toString(),
|
|
@@ -5825,8 +6000,8 @@ async function evaluateAll(tasks, harnessPath, workspacePath, iteration, config,
|
|
|
5825
6000
|
message: `Run ${run + 1}/${effectiveRuns} of ${task.id}`
|
|
5826
6001
|
});
|
|
5827
6002
|
await runTask(task, harnessPath, traceDir, iteration, projectRoot);
|
|
5828
|
-
const stdout = await
|
|
5829
|
-
const stderr = await
|
|
6003
|
+
const stdout = await fs23.readFile(path23.join(traceDir, "stdout.log"), "utf-8").catch(() => "");
|
|
6004
|
+
const stderr = await fs23.readFile(path23.join(traceDir, "stderr.log"), "utf-8").catch(() => "");
|
|
5830
6005
|
const score = await scoreTask(task, traceDir, stdout, stderr, config);
|
|
5831
6006
|
await writeScore(traceDir, score);
|
|
5832
6007
|
runScores.push(score.score ?? (score.pass ? 100 : 0));
|
|
@@ -5846,7 +6021,7 @@ async function evaluateAll(tasks, harnessPath, workspacePath, iteration, config,
|
|
|
5846
6021
|
}
|
|
5847
6022
|
};
|
|
5848
6023
|
} else {
|
|
5849
|
-
const traceDir =
|
|
6024
|
+
const traceDir = path23.join(
|
|
5850
6025
|
workspacePath,
|
|
5851
6026
|
"traces",
|
|
5852
6027
|
iteration.toString(),
|
|
@@ -5855,8 +6030,8 @@ async function evaluateAll(tasks, harnessPath, workspacePath, iteration, config,
|
|
|
5855
6030
|
const taskResult = await runTask(task, harnessPath, traceDir, iteration, projectRoot);
|
|
5856
6031
|
finalScore = taskResult.score;
|
|
5857
6032
|
if (config) {
|
|
5858
|
-
const stdout = await
|
|
5859
|
-
const stderr = await
|
|
6033
|
+
const stdout = await fs23.readFile(path23.join(traceDir, "stdout.log"), "utf-8").catch(() => "");
|
|
6034
|
+
const stderr = await fs23.readFile(path23.join(traceDir, "stderr.log"), "utf-8").catch(() => "");
|
|
5860
6035
|
finalScore = await scoreTask(task, traceDir, stdout, stderr, config);
|
|
5861
6036
|
await writeScore(traceDir, finalScore);
|
|
5862
6037
|
}
|
|
@@ -5904,12 +6079,12 @@ __export(memory_exports, {
|
|
|
5904
6079
|
loadProposerMemory: () => loadProposerMemory,
|
|
5905
6080
|
saveRunSummary: () => saveRunSummary
|
|
5906
6081
|
});
|
|
5907
|
-
import
|
|
5908
|
-
import
|
|
6082
|
+
import fs24 from "fs/promises";
|
|
6083
|
+
import path24 from "path";
|
|
5909
6084
|
async function loadProposerMemory(workspacePath) {
|
|
5910
|
-
const memoryPath =
|
|
6085
|
+
const memoryPath = path24.join(workspacePath, MEMORY_FILE);
|
|
5911
6086
|
try {
|
|
5912
|
-
const raw = await
|
|
6087
|
+
const raw = await fs24.readFile(memoryPath, "utf-8");
|
|
5913
6088
|
const parsed = JSON.parse(raw);
|
|
5914
6089
|
if (Array.isArray(parsed)) return parsed;
|
|
5915
6090
|
return [];
|
|
@@ -5948,8 +6123,8 @@ async function saveRunSummary(workspacePath, summary) {
|
|
|
5948
6123
|
const existing = await loadProposerMemory(workspacePath);
|
|
5949
6124
|
existing.push(summary);
|
|
5950
6125
|
const trimmed = existing.slice(-MAX_ENTRIES);
|
|
5951
|
-
const memoryPath =
|
|
5952
|
-
await
|
|
6126
|
+
const memoryPath = path24.join(workspacePath, MEMORY_FILE);
|
|
6127
|
+
await fs24.writeFile(memoryPath, JSON.stringify(trimmed, null, 2), "utf-8");
|
|
5953
6128
|
}
|
|
5954
6129
|
function formatMemoryForProposer(memory) {
|
|
5955
6130
|
if (memory.length === 0) return "";
|
|
@@ -5994,26 +6169,26 @@ __export(knowledge_exports, {
|
|
|
5994
6169
|
savePattern: () => savePattern,
|
|
5995
6170
|
saveProjectHistory: () => saveProjectHistory
|
|
5996
6171
|
});
|
|
5997
|
-
import
|
|
5998
|
-
import
|
|
6172
|
+
import fs25 from "fs/promises";
|
|
6173
|
+
import path25 from "path";
|
|
5999
6174
|
import crypto2 from "crypto";
|
|
6000
6175
|
function getKnowledgeDir() {
|
|
6001
|
-
return
|
|
6176
|
+
return path25.join(getKairnDir(), "knowledge");
|
|
6002
6177
|
}
|
|
6003
6178
|
function getPatternsPath() {
|
|
6004
|
-
return
|
|
6179
|
+
return path25.join(getKnowledgeDir(), "patterns.jsonl");
|
|
6005
6180
|
}
|
|
6006
6181
|
function getProjectsDir() {
|
|
6007
|
-
return
|
|
6182
|
+
return path25.join(getKnowledgeDir(), "projects");
|
|
6008
6183
|
}
|
|
6009
6184
|
function getConvergencePath() {
|
|
6010
|
-
return
|
|
6185
|
+
return path25.join(getKnowledgeDir(), "convergence.json");
|
|
6011
6186
|
}
|
|
6012
6187
|
async function loadKnowledgeBase(filter) {
|
|
6013
6188
|
const patternsPath = getPatternsPath();
|
|
6014
6189
|
let content;
|
|
6015
6190
|
try {
|
|
6016
|
-
content = await
|
|
6191
|
+
content = await fs25.readFile(patternsPath, "utf-8");
|
|
6017
6192
|
} catch {
|
|
6018
6193
|
return [];
|
|
6019
6194
|
}
|
|
@@ -6033,9 +6208,9 @@ async function loadKnowledgeBase(filter) {
|
|
|
6033
6208
|
}
|
|
6034
6209
|
async function savePattern(pattern) {
|
|
6035
6210
|
const dir = getKnowledgeDir();
|
|
6036
|
-
await
|
|
6211
|
+
await fs25.mkdir(dir, { recursive: true });
|
|
6037
6212
|
const patternsPath = getPatternsPath();
|
|
6038
|
-
await
|
|
6213
|
+
await fs25.appendFile(patternsPath, JSON.stringify(pattern) + "\n", "utf-8");
|
|
6039
6214
|
}
|
|
6040
6215
|
async function extractAndSavePatterns(history, projectName, language) {
|
|
6041
6216
|
const patterns = [];
|
|
@@ -6107,13 +6282,13 @@ function formatKnowledgeForArchitect(patterns, language) {
|
|
|
6107
6282
|
}
|
|
6108
6283
|
async function saveProjectHistory(projectName, summary) {
|
|
6109
6284
|
const projectsDir = getProjectsDir();
|
|
6110
|
-
await
|
|
6111
|
-
const filePath =
|
|
6112
|
-
await
|
|
6285
|
+
await fs25.mkdir(projectsDir, { recursive: true });
|
|
6286
|
+
const filePath = path25.join(projectsDir, `${projectName}.json`);
|
|
6287
|
+
await fs25.writeFile(filePath, JSON.stringify(summary, null, 2), "utf-8");
|
|
6113
6288
|
}
|
|
6114
6289
|
async function loadConvergence() {
|
|
6115
6290
|
try {
|
|
6116
|
-
const content = await
|
|
6291
|
+
const content = await fs25.readFile(getConvergencePath(), "utf-8");
|
|
6117
6292
|
return JSON.parse(content);
|
|
6118
6293
|
} catch {
|
|
6119
6294
|
return null;
|
|
@@ -6121,8 +6296,8 @@ async function loadConvergence() {
|
|
|
6121
6296
|
}
|
|
6122
6297
|
async function saveConvergence(convergence) {
|
|
6123
6298
|
const dir = getKnowledgeDir();
|
|
6124
|
-
await
|
|
6125
|
-
await
|
|
6299
|
+
await fs25.mkdir(dir, { recursive: true });
|
|
6300
|
+
await fs25.writeFile(
|
|
6126
6301
|
getConvergencePath(),
|
|
6127
6302
|
JSON.stringify(convergence, null, 2),
|
|
6128
6303
|
"utf-8"
|
|
@@ -6136,25 +6311,112 @@ var init_knowledge = __esm({
|
|
|
6136
6311
|
});
|
|
6137
6312
|
|
|
6138
6313
|
// src/evolve/proposer.ts
|
|
6139
|
-
import
|
|
6140
|
-
import
|
|
6314
|
+
import fs26 from "fs/promises";
|
|
6315
|
+
import path26 from "path";
|
|
6316
|
+
function countSettingsHooks(settings) {
|
|
6317
|
+
let count = 0;
|
|
6318
|
+
const categories = settings.hooks;
|
|
6319
|
+
for (const key of Object.keys(categories)) {
|
|
6320
|
+
const entries = categories[key];
|
|
6321
|
+
if (entries) {
|
|
6322
|
+
count += entries.length;
|
|
6323
|
+
}
|
|
6324
|
+
}
|
|
6325
|
+
return count;
|
|
6326
|
+
}
|
|
6327
|
+
function buildIRSummary(ir) {
|
|
6328
|
+
const lines = [];
|
|
6329
|
+
lines.push("## Harness Structure (IR)");
|
|
6330
|
+
const meta = ir.meta;
|
|
6331
|
+
const techParts = [meta.techStack.language];
|
|
6332
|
+
if (meta.techStack.framework) techParts.push(meta.techStack.framework);
|
|
6333
|
+
if (meta.techStack.buildTool) techParts.push(meta.techStack.buildTool);
|
|
6334
|
+
lines.push(`Project: ${meta.name || "(unnamed)"} \u2014 ${techParts.join(", ")}`);
|
|
6335
|
+
const sectionIds = ir.sections.map((s) => s.id);
|
|
6336
|
+
lines.push(`Sections (${ir.sections.length}): ${sectionIds.join(", ") || "none"}`);
|
|
6337
|
+
const commandSummaries = ir.commands.map((c) => c.name);
|
|
6338
|
+
lines.push(`Commands (${ir.commands.length}): ${commandSummaries.join(", ") || "none"}`);
|
|
6339
|
+
const ruleSummaries = ir.rules.map((r) => {
|
|
6340
|
+
const pathHint = r.paths && r.paths.length > 0 ? ` (${r.paths.join(", ")})` : "";
|
|
6341
|
+
return `${r.name}${pathHint}`;
|
|
6342
|
+
});
|
|
6343
|
+
lines.push(`Rules (${ir.rules.length}): ${ruleSummaries.join(", ") || "none"}`);
|
|
6344
|
+
const agentNames = ir.agents.map((a) => a.name);
|
|
6345
|
+
lines.push(`Agents (${ir.agents.length}): ${agentNames.join(", ") || "none"}`);
|
|
6346
|
+
const serverNames = ir.mcpServers.map((s) => s.id);
|
|
6347
|
+
lines.push(`MCP Servers (${ir.mcpServers.length}): ${serverNames.join(", ") || "none"}`);
|
|
6348
|
+
if (ir.skills.length > 0) {
|
|
6349
|
+
const skillNames = ir.skills.map((s) => s.name);
|
|
6350
|
+
lines.push(`Skills (${ir.skills.length}): ${skillNames.join(", ")}`);
|
|
6351
|
+
}
|
|
6352
|
+
if (ir.docs.length > 0) {
|
|
6353
|
+
const docNames = ir.docs.map((d) => d.name);
|
|
6354
|
+
lines.push(`Docs (${ir.docs.length}): ${docNames.join(", ")}`);
|
|
6355
|
+
}
|
|
6356
|
+
if (ir.hooks.length > 0) {
|
|
6357
|
+
const hookNames = ir.hooks.map((h) => h.name);
|
|
6358
|
+
lines.push(`Hooks (${ir.hooks.length}): ${hookNames.join(", ")}`);
|
|
6359
|
+
}
|
|
6360
|
+
const settingsParts = [];
|
|
6361
|
+
if (ir.settings.statusLine) {
|
|
6362
|
+
settingsParts.push("statusLine=enabled");
|
|
6363
|
+
}
|
|
6364
|
+
if (ir.settings.denyPatterns && ir.settings.denyPatterns.length > 0) {
|
|
6365
|
+
settingsParts.push(`denyPatterns=${ir.settings.denyPatterns.length}`);
|
|
6366
|
+
}
|
|
6367
|
+
const hooksCount = countSettingsHooks(ir.settings);
|
|
6368
|
+
if (hooksCount > 0) {
|
|
6369
|
+
settingsParts.push(`hooks=${hooksCount}`);
|
|
6370
|
+
}
|
|
6371
|
+
lines.push(`Settings: ${settingsParts.join(", ") || "default"}`);
|
|
6372
|
+
return lines.join("\n");
|
|
6373
|
+
}
|
|
6374
|
+
function formatAnalysisForProposer(analysis) {
|
|
6375
|
+
const lines = [];
|
|
6376
|
+
lines.push(`Purpose: ${analysis.purpose}`);
|
|
6377
|
+
lines.push(`Domain: ${analysis.domain}`);
|
|
6378
|
+
lines.push(`Architecture: ${analysis.architecture_style}`);
|
|
6379
|
+
lines.push(`Deployment: ${analysis.deployment_model}`);
|
|
6380
|
+
if (analysis.key_modules.length > 0) {
|
|
6381
|
+
lines.push("");
|
|
6382
|
+
lines.push("Key Modules:");
|
|
6383
|
+
for (const mod of analysis.key_modules) {
|
|
6384
|
+
lines.push(`- ${mod.name} (${mod.path}): ${mod.description}`);
|
|
6385
|
+
}
|
|
6386
|
+
}
|
|
6387
|
+
if (analysis.workflows.length > 0) {
|
|
6388
|
+
lines.push("");
|
|
6389
|
+
lines.push("Workflows:");
|
|
6390
|
+
for (const wf of analysis.workflows) {
|
|
6391
|
+
lines.push(`- ${wf.name}: ${wf.description} (trigger: ${wf.trigger})`);
|
|
6392
|
+
}
|
|
6393
|
+
}
|
|
6394
|
+
if (analysis.config_keys.length > 0) {
|
|
6395
|
+
lines.push("");
|
|
6396
|
+
lines.push("Config Keys:");
|
|
6397
|
+
for (const key of analysis.config_keys) {
|
|
6398
|
+
lines.push(`- ${key.name}: ${key.purpose}`);
|
|
6399
|
+
}
|
|
6400
|
+
}
|
|
6401
|
+
return lines.join("\n");
|
|
6402
|
+
}
|
|
6141
6403
|
async function readHarnessFiles(harnessPath) {
|
|
6142
6404
|
const result = {};
|
|
6143
6405
|
async function walk(dir, prefix) {
|
|
6144
6406
|
let entries;
|
|
6145
6407
|
try {
|
|
6146
|
-
entries = await
|
|
6408
|
+
entries = await fs26.readdir(dir, { withFileTypes: true });
|
|
6147
6409
|
} catch {
|
|
6148
6410
|
return;
|
|
6149
6411
|
}
|
|
6150
6412
|
for (const entry of entries) {
|
|
6151
|
-
const relativePath = prefix ?
|
|
6152
|
-
const fullPath =
|
|
6413
|
+
const relativePath = prefix ? path26.join(prefix, entry.name) : entry.name;
|
|
6414
|
+
const fullPath = path26.join(dir, entry.name);
|
|
6153
6415
|
if (entry.isDirectory()) {
|
|
6154
6416
|
await walk(fullPath, relativePath);
|
|
6155
6417
|
} else if (entry.isFile()) {
|
|
6156
6418
|
try {
|
|
6157
|
-
result[relativePath] = await
|
|
6419
|
+
result[relativePath] = await fs26.readFile(fullPath, "utf-8");
|
|
6158
6420
|
} catch {
|
|
6159
6421
|
}
|
|
6160
6422
|
}
|
|
@@ -6170,7 +6432,7 @@ function truncateStdout(stdout, limit) {
|
|
|
6170
6432
|
return `[...truncated, showing last ${limit} chars...]
|
|
6171
6433
|
${stdout.slice(-limit)}`;
|
|
6172
6434
|
}
|
|
6173
|
-
function buildProposerUserMessage(harnessFiles, traces, tasks, history, memorySection) {
|
|
6435
|
+
function buildProposerUserMessage(harnessFiles, traces, tasks, history, memorySection, projectContext) {
|
|
6174
6436
|
const harnessSection = ["## Current Harness Files\n"];
|
|
6175
6437
|
const fileEntries = Object.entries(harnessFiles);
|
|
6176
6438
|
if (fileEntries.length === 0) {
|
|
@@ -6197,7 +6459,24 @@ ${content}
|
|
|
6197
6459
|
);
|
|
6198
6460
|
}
|
|
6199
6461
|
}
|
|
6200
|
-
|
|
6462
|
+
let projectSection = "";
|
|
6463
|
+
if (projectContext) {
|
|
6464
|
+
const parts = ["## Project Understanding\n"];
|
|
6465
|
+
parts.push("### Analysis Summary");
|
|
6466
|
+
parts.push(formatAnalysisForProposer(projectContext.analysis));
|
|
6467
|
+
parts.push("");
|
|
6468
|
+
parts.push("### Harness Structure");
|
|
6469
|
+
parts.push(projectContext.irSummary);
|
|
6470
|
+
if (projectContext.keySourceFiles) {
|
|
6471
|
+
parts.push("");
|
|
6472
|
+
parts.push("### Key Source Files");
|
|
6473
|
+
parts.push("```");
|
|
6474
|
+
parts.push(projectContext.keySourceFiles);
|
|
6475
|
+
parts.push("```");
|
|
6476
|
+
}
|
|
6477
|
+
projectSection = "\n" + parts.join("\n") + "\n";
|
|
6478
|
+
}
|
|
6479
|
+
const fixedContent = harnessSection.join("\n") + "\n" + taskSection.join("\n") + projectSection;
|
|
6201
6480
|
const remainingBudget = MAX_CONTEXT_CHARS - fixedContent.length;
|
|
6202
6481
|
if (remainingBudget <= 0) {
|
|
6203
6482
|
return fixedContent + "\n\n[...traces and history omitted \u2014 harness + tasks fill context budget...]";
|
|
@@ -6359,7 +6638,7 @@ function parseProposerResponse(raw) {
|
|
|
6359
6638
|
expectedImpact
|
|
6360
6639
|
};
|
|
6361
6640
|
}
|
|
6362
|
-
async function propose(iteration, workspacePath, harnessPath, history, tasks, config, proposerModel) {
|
|
6641
|
+
async function propose(iteration, workspacePath, harnessPath, history, tasks, config, proposerModel, projectContext) {
|
|
6363
6642
|
const harnessFiles = await readHarnessFiles(harnessPath);
|
|
6364
6643
|
const traces = await loadIterationTraces(workspacePath, iteration);
|
|
6365
6644
|
const { loadProposerMemory: loadProposerMemory2, formatMemoryForProposer: formatMemoryForProposer2 } = await Promise.resolve().then(() => (init_memory(), memory_exports));
|
|
@@ -6373,7 +6652,7 @@ async function propose(iteration, workspacePath, harnessPath, history, tasks, co
|
|
|
6373
6652
|
} catch {
|
|
6374
6653
|
}
|
|
6375
6654
|
const combinedMemory = memorySection + (knowledgeSection ? "\n" + knowledgeSection : "");
|
|
6376
|
-
const userMessage = buildProposerUserMessage(harnessFiles, traces, tasks, history, combinedMemory || void 0);
|
|
6655
|
+
const userMessage = buildProposerUserMessage(harnessFiles, traces, tasks, history, combinedMemory || void 0, projectContext);
|
|
6377
6656
|
const proposerConfig = { ...config, model: proposerModel };
|
|
6378
6657
|
const response = await callLLM(proposerConfig, userMessage, {
|
|
6379
6658
|
systemPrompt: PROPOSER_SYSTEM_PROMPT,
|
|
@@ -6460,8 +6739,8 @@ Return ONLY valid JSON.`;
|
|
|
6460
6739
|
});
|
|
6461
6740
|
|
|
6462
6741
|
// src/ir/parser.ts
|
|
6463
|
-
import
|
|
6464
|
-
import
|
|
6742
|
+
import fs27 from "fs/promises";
|
|
6743
|
+
import path27 from "path";
|
|
6465
6744
|
function slugify(heading) {
|
|
6466
6745
|
return heading.toLowerCase().trim().replace(/\s+/g, "-").replace(/[^a-z0-9-]/g, "").replace(/-+/g, "-").replace(/^-|-$/g, "");
|
|
6467
6746
|
}
|
|
@@ -6723,33 +7002,33 @@ function parseMcpConfig(content) {
|
|
|
6723
7002
|
}
|
|
6724
7003
|
async function readDirSafe(dirPath) {
|
|
6725
7004
|
try {
|
|
6726
|
-
return await
|
|
7005
|
+
return await fs27.readdir(dirPath);
|
|
6727
7006
|
} catch {
|
|
6728
7007
|
return [];
|
|
6729
7008
|
}
|
|
6730
7009
|
}
|
|
6731
7010
|
async function readFileSafe2(filePath) {
|
|
6732
7011
|
try {
|
|
6733
|
-
return await
|
|
7012
|
+
return await fs27.readFile(filePath, "utf-8");
|
|
6734
7013
|
} catch {
|
|
6735
7014
|
return null;
|
|
6736
7015
|
}
|
|
6737
7016
|
}
|
|
6738
7017
|
async function isDirectory(filePath) {
|
|
6739
7018
|
try {
|
|
6740
|
-
const stat = await
|
|
7019
|
+
const stat = await fs27.stat(filePath);
|
|
6741
7020
|
return stat.isDirectory();
|
|
6742
7021
|
} catch {
|
|
6743
7022
|
return false;
|
|
6744
7023
|
}
|
|
6745
7024
|
}
|
|
6746
7025
|
async function parseCommands(harnessPath) {
|
|
6747
|
-
const dirPath =
|
|
7026
|
+
const dirPath = path27.join(harnessPath, "commands");
|
|
6748
7027
|
const entries = await readDirSafe(dirPath);
|
|
6749
7028
|
const nodes = [];
|
|
6750
7029
|
for (const entry of entries) {
|
|
6751
7030
|
if (!entry.endsWith(".md")) continue;
|
|
6752
|
-
const filePath =
|
|
7031
|
+
const filePath = path27.join(dirPath, entry);
|
|
6753
7032
|
const content = await readFileSafe2(filePath);
|
|
6754
7033
|
if (content === null) continue;
|
|
6755
7034
|
const name = entry.replace(/\.md$/, "");
|
|
@@ -6760,12 +7039,12 @@ async function parseCommands(harnessPath) {
|
|
|
6760
7039
|
return nodes;
|
|
6761
7040
|
}
|
|
6762
7041
|
async function parseRules(harnessPath) {
|
|
6763
|
-
const dirPath =
|
|
7042
|
+
const dirPath = path27.join(harnessPath, "rules");
|
|
6764
7043
|
const entries = await readDirSafe(dirPath);
|
|
6765
7044
|
const nodes = [];
|
|
6766
7045
|
for (const entry of entries) {
|
|
6767
7046
|
if (!entry.endsWith(".md")) continue;
|
|
6768
|
-
const filePath =
|
|
7047
|
+
const filePath = path27.join(dirPath, entry);
|
|
6769
7048
|
const rawContent = await readFileSafe2(filePath);
|
|
6770
7049
|
if (rawContent === null) continue;
|
|
6771
7050
|
const name = entry.replace(/\.md$/, "");
|
|
@@ -6780,12 +7059,12 @@ async function parseRules(harnessPath) {
|
|
|
6780
7059
|
return nodes;
|
|
6781
7060
|
}
|
|
6782
7061
|
async function parseAgents(harnessPath) {
|
|
6783
|
-
const dirPath =
|
|
7062
|
+
const dirPath = path27.join(harnessPath, "agents");
|
|
6784
7063
|
const entries = await readDirSafe(dirPath);
|
|
6785
7064
|
const nodes = [];
|
|
6786
7065
|
for (const entry of entries) {
|
|
6787
7066
|
if (!entry.endsWith(".md")) continue;
|
|
6788
|
-
const filePath =
|
|
7067
|
+
const filePath = path27.join(dirPath, entry);
|
|
6789
7068
|
const rawContent = await readFileSafe2(filePath);
|
|
6790
7069
|
if (rawContent === null) continue;
|
|
6791
7070
|
const fileBaseName = entry.replace(/\.md$/, "");
|
|
@@ -6829,20 +7108,20 @@ async function parseAgents(harnessPath) {
|
|
|
6829
7108
|
return nodes;
|
|
6830
7109
|
}
|
|
6831
7110
|
async function parseSkills(harnessPath) {
|
|
6832
|
-
const dirPath =
|
|
7111
|
+
const dirPath = path27.join(harnessPath, "skills");
|
|
6833
7112
|
const entries = await readDirSafe(dirPath);
|
|
6834
7113
|
const nodes = [];
|
|
6835
7114
|
for (const entry of entries) {
|
|
6836
|
-
const entryPath =
|
|
7115
|
+
const entryPath = path27.join(dirPath, entry);
|
|
6837
7116
|
if (entry.endsWith(".md")) {
|
|
6838
7117
|
const content = await readFileSafe2(entryPath);
|
|
6839
7118
|
if (content === null) continue;
|
|
6840
7119
|
const name = entry.replace(/\.md$/, "");
|
|
6841
7120
|
nodes.push({ name, content });
|
|
6842
7121
|
} else if (await isDirectory(entryPath)) {
|
|
6843
|
-
let content = await readFileSafe2(
|
|
7122
|
+
let content = await readFileSafe2(path27.join(entryPath, "skill.md"));
|
|
6844
7123
|
if (content === null) {
|
|
6845
|
-
content = await readFileSafe2(
|
|
7124
|
+
content = await readFileSafe2(path27.join(entryPath, "SKILL.md"));
|
|
6846
7125
|
}
|
|
6847
7126
|
if (content === null) continue;
|
|
6848
7127
|
nodes.push({ name: entry, content });
|
|
@@ -6851,12 +7130,12 @@ async function parseSkills(harnessPath) {
|
|
|
6851
7130
|
return nodes;
|
|
6852
7131
|
}
|
|
6853
7132
|
async function parseDocs(harnessPath) {
|
|
6854
|
-
const dirPath =
|
|
7133
|
+
const dirPath = path27.join(harnessPath, "docs");
|
|
6855
7134
|
const entries = await readDirSafe(dirPath);
|
|
6856
7135
|
const nodes = [];
|
|
6857
7136
|
for (const entry of entries) {
|
|
6858
7137
|
if (!entry.endsWith(".md")) continue;
|
|
6859
|
-
const filePath =
|
|
7138
|
+
const filePath = path27.join(dirPath, entry);
|
|
6860
7139
|
const content = await readFileSafe2(filePath);
|
|
6861
7140
|
if (content === null) continue;
|
|
6862
7141
|
const name = entry.replace(/\.md$/, "");
|
|
@@ -6865,12 +7144,12 @@ async function parseDocs(harnessPath) {
|
|
|
6865
7144
|
return nodes;
|
|
6866
7145
|
}
|
|
6867
7146
|
async function parseHooks(harnessPath) {
|
|
6868
|
-
const dirPath =
|
|
7147
|
+
const dirPath = path27.join(harnessPath, "hooks");
|
|
6869
7148
|
const entries = await readDirSafe(dirPath);
|
|
6870
7149
|
const nodes = [];
|
|
6871
7150
|
for (const entry of entries) {
|
|
6872
7151
|
if (!entry.endsWith(".mjs")) continue;
|
|
6873
|
-
const filePath =
|
|
7152
|
+
const filePath = path27.join(dirPath, entry);
|
|
6874
7153
|
const content = await readFileSafe2(filePath);
|
|
6875
7154
|
if (content === null) continue;
|
|
6876
7155
|
const name = entry.replace(/\.mjs$/, "");
|
|
@@ -6881,7 +7160,7 @@ async function parseHooks(harnessPath) {
|
|
|
6881
7160
|
async function parseHarness(harnessPath) {
|
|
6882
7161
|
const ir = createEmptyIR();
|
|
6883
7162
|
const claudeMdContent = await readFileSafe2(
|
|
6884
|
-
|
|
7163
|
+
path27.join(harnessPath, "CLAUDE.md")
|
|
6885
7164
|
);
|
|
6886
7165
|
if (claudeMdContent !== null) {
|
|
6887
7166
|
const { meta, sections } = parseClaudeMd(claudeMdContent);
|
|
@@ -6893,7 +7172,7 @@ async function parseHarness(harnessPath) {
|
|
|
6893
7172
|
ir.sections = sections;
|
|
6894
7173
|
}
|
|
6895
7174
|
const settingsContent = await readFileSafe2(
|
|
6896
|
-
|
|
7175
|
+
path27.join(harnessPath, "settings.json")
|
|
6897
7176
|
);
|
|
6898
7177
|
if (settingsContent !== null) {
|
|
6899
7178
|
ir.settings = parseSettings(settingsContent);
|
|
@@ -6914,7 +7193,7 @@ async function parseHarness(harnessPath) {
|
|
|
6914
7193
|
ir.hooks = hooks;
|
|
6915
7194
|
const mcpServers = [];
|
|
6916
7195
|
const seenIds = /* @__PURE__ */ new Set();
|
|
6917
|
-
const parentMcpPath =
|
|
7196
|
+
const parentMcpPath = path27.join(path27.dirname(harnessPath), ".mcp.json");
|
|
6918
7197
|
const parentMcpContent = await readFileSafe2(parentMcpPath);
|
|
6919
7198
|
if (parentMcpContent !== null) {
|
|
6920
7199
|
for (const node of parseMcpConfig(parentMcpContent)) {
|
|
@@ -6924,7 +7203,7 @@ async function parseHarness(harnessPath) {
|
|
|
6924
7203
|
}
|
|
6925
7204
|
}
|
|
6926
7205
|
}
|
|
6927
|
-
const innerMcpPath =
|
|
7206
|
+
const innerMcpPath = path27.join(harnessPath, ".mcp.json");
|
|
6928
7207
|
const innerMcpContent = await readFileSafe2(innerMcpPath);
|
|
6929
7208
|
if (innerMcpContent !== null) {
|
|
6930
7209
|
for (const node of parseMcpConfig(innerMcpContent)) {
|
|
@@ -7161,8 +7440,8 @@ function deepSet(obj, segments, value) {
|
|
|
7161
7440
|
const child = existing !== null && typeof existing === "object" && !Array.isArray(existing) ? existing : {};
|
|
7162
7441
|
return { ...obj, [head]: deepSet(child, rest, value) };
|
|
7163
7442
|
}
|
|
7164
|
-
function applySettingsUpdate(settings,
|
|
7165
|
-
const segments =
|
|
7443
|
+
function applySettingsUpdate(settings, path39, value) {
|
|
7444
|
+
const segments = path39.split(".");
|
|
7166
7445
|
const topKey = segments[0];
|
|
7167
7446
|
if (STRUCTURED_SETTINGS_KEYS.has(topKey)) {
|
|
7168
7447
|
const settingsRecord = { ...settings };
|
|
@@ -7620,10 +7899,10 @@ var init_diff = __esm({
|
|
|
7620
7899
|
});
|
|
7621
7900
|
|
|
7622
7901
|
// src/evolve/mutator.ts
|
|
7623
|
-
import
|
|
7624
|
-
import
|
|
7902
|
+
import fs28 from "fs/promises";
|
|
7903
|
+
import path28 from "path";
|
|
7625
7904
|
async function applyMutations(currentHarnessPath, nextIterationDir, mutations) {
|
|
7626
|
-
const newHarnessPath =
|
|
7905
|
+
const newHarnessPath = path28.join(nextIterationDir, "harness");
|
|
7627
7906
|
let baselineIR = null;
|
|
7628
7907
|
try {
|
|
7629
7908
|
baselineIR = await parseHarness(currentHarnessPath);
|
|
@@ -7664,6 +7943,11 @@ async function applyMutationsViaIR(currentHarnessPath, newHarnessPath, mutations
|
|
|
7664
7943
|
for (const mutation of rawTextMutations) {
|
|
7665
7944
|
await applyLegacyMutation(newHarnessPath, mutation);
|
|
7666
7945
|
}
|
|
7946
|
+
await fs28.writeFile(
|
|
7947
|
+
path28.join(newHarnessPath, "..", "harness-ir.json"),
|
|
7948
|
+
JSON.stringify(currentIR, null, 2),
|
|
7949
|
+
"utf-8"
|
|
7950
|
+
);
|
|
7667
7951
|
const irDiff = diffIR(baselineIR, currentIR);
|
|
7668
7952
|
let diffPatch = formatIRDiff(irDiff);
|
|
7669
7953
|
if (diffPatch === "No changes." && rawTextMutations.length > 0) {
|
|
@@ -7700,9 +7984,9 @@ async function renderAffectedFiles(ir, targetDir, touchedCategories) {
|
|
|
7700
7984
|
for (const [relativePath, content] of fileMap) {
|
|
7701
7985
|
const category = getFileCategory(relativePath);
|
|
7702
7986
|
if (touchedCategories.has(category)) {
|
|
7703
|
-
const fullPath =
|
|
7704
|
-
await
|
|
7705
|
-
await
|
|
7987
|
+
const fullPath = path28.join(targetDir, relativePath);
|
|
7988
|
+
await fs28.mkdir(path28.dirname(fullPath), { recursive: true });
|
|
7989
|
+
await fs28.writeFile(fullPath, content, "utf-8");
|
|
7706
7990
|
}
|
|
7707
7991
|
}
|
|
7708
7992
|
if (touchedCategories.has("commands")) {
|
|
@@ -7715,11 +7999,11 @@ async function renderAffectedFiles(ir, targetDir, touchedCategories) {
|
|
|
7715
7999
|
await deleteOrphanedFiles(targetDir, "agents", fileMap);
|
|
7716
8000
|
}
|
|
7717
8001
|
if (touchedCategories.has("mcp") && !fileMap.has(".mcp.json")) {
|
|
7718
|
-
await
|
|
8002
|
+
await fs28.unlink(path28.join(targetDir, ".mcp.json")).catch(() => {
|
|
7719
8003
|
});
|
|
7720
8004
|
}
|
|
7721
8005
|
if (touchedCategories.has("settings") && !fileMap.has("settings.json")) {
|
|
7722
|
-
await
|
|
8006
|
+
await fs28.unlink(path28.join(targetDir, "settings.json")).catch(() => {
|
|
7723
8007
|
});
|
|
7724
8008
|
}
|
|
7725
8009
|
}
|
|
@@ -7736,17 +8020,17 @@ function getFileCategory(relativePath) {
|
|
|
7736
8020
|
return "unknown";
|
|
7737
8021
|
}
|
|
7738
8022
|
async function deleteOrphanedFiles(targetDir, subdir, renderedMap) {
|
|
7739
|
-
const subdirPath =
|
|
8023
|
+
const subdirPath = path28.join(targetDir, subdir);
|
|
7740
8024
|
let entries;
|
|
7741
8025
|
try {
|
|
7742
|
-
entries = await
|
|
8026
|
+
entries = await fs28.readdir(subdirPath);
|
|
7743
8027
|
} catch {
|
|
7744
8028
|
return;
|
|
7745
8029
|
}
|
|
7746
8030
|
for (const entry of entries) {
|
|
7747
8031
|
const relativePath = `${subdir}/${entry}`;
|
|
7748
8032
|
if (!renderedMap.has(relativePath)) {
|
|
7749
|
-
await
|
|
8033
|
+
await fs28.unlink(path28.join(subdirPath, entry)).catch(() => {
|
|
7750
8034
|
});
|
|
7751
8035
|
}
|
|
7752
8036
|
}
|
|
@@ -7763,56 +8047,56 @@ async function applyLegacyMutation(harnessPath, mutation) {
|
|
|
7763
8047
|
if (mutation.file.includes("..")) {
|
|
7764
8048
|
return;
|
|
7765
8049
|
}
|
|
7766
|
-
const filePath =
|
|
8050
|
+
const filePath = path28.join(harnessPath, mutation.file);
|
|
7767
8051
|
if (mutation.action === "replace") {
|
|
7768
8052
|
if (!mutation.oldText) {
|
|
7769
8053
|
return;
|
|
7770
8054
|
}
|
|
7771
8055
|
let content;
|
|
7772
8056
|
try {
|
|
7773
|
-
content = await
|
|
8057
|
+
content = await fs28.readFile(filePath, "utf-8");
|
|
7774
8058
|
} catch {
|
|
7775
8059
|
return;
|
|
7776
8060
|
}
|
|
7777
8061
|
if (!content.includes(mutation.oldText)) {
|
|
7778
8062
|
return;
|
|
7779
8063
|
}
|
|
7780
|
-
await
|
|
8064
|
+
await fs28.writeFile(
|
|
7781
8065
|
filePath,
|
|
7782
8066
|
content.replace(mutation.oldText, mutation.newText),
|
|
7783
8067
|
"utf-8"
|
|
7784
8068
|
);
|
|
7785
8069
|
} else if (mutation.action === "add_section") {
|
|
7786
8070
|
try {
|
|
7787
|
-
const content = await
|
|
7788
|
-
await
|
|
8071
|
+
const content = await fs28.readFile(filePath, "utf-8");
|
|
8072
|
+
await fs28.writeFile(
|
|
7789
8073
|
filePath,
|
|
7790
8074
|
content + "\n\n" + mutation.newText,
|
|
7791
8075
|
"utf-8"
|
|
7792
8076
|
);
|
|
7793
8077
|
} catch {
|
|
7794
|
-
await
|
|
7795
|
-
await
|
|
8078
|
+
await fs28.mkdir(path28.dirname(filePath), { recursive: true });
|
|
8079
|
+
await fs28.writeFile(filePath, mutation.newText, "utf-8");
|
|
7796
8080
|
}
|
|
7797
8081
|
} else if (mutation.action === "create_file") {
|
|
7798
|
-
await
|
|
7799
|
-
await
|
|
8082
|
+
await fs28.mkdir(path28.dirname(filePath), { recursive: true });
|
|
8083
|
+
await fs28.writeFile(filePath, mutation.newText, "utf-8");
|
|
7800
8084
|
} else if (mutation.action === "delete_section") {
|
|
7801
8085
|
if (!mutation.oldText) {
|
|
7802
8086
|
return;
|
|
7803
8087
|
}
|
|
7804
8088
|
let sectionContent;
|
|
7805
8089
|
try {
|
|
7806
|
-
sectionContent = await
|
|
8090
|
+
sectionContent = await fs28.readFile(filePath, "utf-8");
|
|
7807
8091
|
} catch {
|
|
7808
8092
|
return;
|
|
7809
8093
|
}
|
|
7810
8094
|
if (!sectionContent.includes(mutation.oldText)) {
|
|
7811
8095
|
return;
|
|
7812
8096
|
}
|
|
7813
|
-
await
|
|
8097
|
+
await fs28.writeFile(filePath, sectionContent.replace(mutation.oldText, ""), "utf-8");
|
|
7814
8098
|
} else if (mutation.action === "delete_file") {
|
|
7815
|
-
await
|
|
8099
|
+
await fs28.unlink(filePath).catch(() => {
|
|
7816
8100
|
});
|
|
7817
8101
|
}
|
|
7818
8102
|
}
|
|
@@ -7880,17 +8164,17 @@ async function readAllFiles(dir) {
|
|
|
7880
8164
|
async function walk(current) {
|
|
7881
8165
|
let entries;
|
|
7882
8166
|
try {
|
|
7883
|
-
entries = await
|
|
8167
|
+
entries = await fs28.readdir(current, { withFileTypes: true });
|
|
7884
8168
|
} catch {
|
|
7885
8169
|
return;
|
|
7886
8170
|
}
|
|
7887
8171
|
for (const entry of entries) {
|
|
7888
|
-
const fullPath =
|
|
7889
|
-
const relativePath =
|
|
8172
|
+
const fullPath = path28.join(current, entry.name);
|
|
8173
|
+
const relativePath = path28.relative(dir, fullPath);
|
|
7890
8174
|
if (entry.isDirectory()) {
|
|
7891
8175
|
await walk(fullPath);
|
|
7892
8176
|
} else {
|
|
7893
|
-
result[relativePath] = await
|
|
8177
|
+
result[relativePath] = await fs28.readFile(fullPath, "utf-8");
|
|
7894
8178
|
}
|
|
7895
8179
|
}
|
|
7896
8180
|
}
|
|
@@ -8030,7 +8314,7 @@ ${taskScores}
|
|
|
8030
8314
|
}
|
|
8031
8315
|
return "## Iteration History\n\n(History omitted to fit context budget)\n";
|
|
8032
8316
|
}
|
|
8033
|
-
function buildArchitectUserMessage(harnessFiles, traces, tasks, history, knowledgeContext) {
|
|
8317
|
+
function buildArchitectUserMessage(harnessFiles, traces, tasks, history, knowledgeContext, projectContext) {
|
|
8034
8318
|
const harnessSection = ["## Current Harness Files\n"];
|
|
8035
8319
|
const fileEntries = Object.entries(harnessFiles);
|
|
8036
8320
|
if (fileEntries.length === 0) {
|
|
@@ -8059,11 +8343,28 @@ ${content}
|
|
|
8059
8343
|
}
|
|
8060
8344
|
const summarySection = buildEvolutionSummary(history);
|
|
8061
8345
|
const workingSection = buildWhatsWorking(history, tasks);
|
|
8346
|
+
let projectSection = "";
|
|
8347
|
+
if (projectContext) {
|
|
8348
|
+
const parts = ["## Project Understanding\n"];
|
|
8349
|
+
parts.push("### Analysis Summary");
|
|
8350
|
+
parts.push(formatAnalysisForProposer(projectContext.analysis));
|
|
8351
|
+
parts.push("");
|
|
8352
|
+
parts.push("### Harness Structure");
|
|
8353
|
+
parts.push(projectContext.irSummary);
|
|
8354
|
+
if (projectContext.keySourceFiles) {
|
|
8355
|
+
parts.push("");
|
|
8356
|
+
parts.push("### Key Source Files");
|
|
8357
|
+
parts.push("```");
|
|
8358
|
+
parts.push(projectContext.keySourceFiles);
|
|
8359
|
+
parts.push("```");
|
|
8360
|
+
}
|
|
8361
|
+
projectSection = "\n" + parts.join("\n") + "\n";
|
|
8362
|
+
}
|
|
8062
8363
|
const knowledgeSection = knowledgeContext ? `## Knowledge Base (patterns from other projects)
|
|
8063
8364
|
|
|
8064
8365
|
${knowledgeContext}
|
|
8065
8366
|
` : "";
|
|
8066
|
-
const fixedContent = harnessSection.join("\n") + "\n" + taskSection.join("\n") + "\n" + summarySection + (summarySection ? "\n" : "") + workingSection + (workingSection ? "\n" : "") + knowledgeSection;
|
|
8367
|
+
const fixedContent = harnessSection.join("\n") + "\n" + taskSection.join("\n") + "\n" + summarySection + (summarySection ? "\n" : "") + workingSection + (workingSection ? "\n" : "") + projectSection + knowledgeSection;
|
|
8067
8368
|
const remainingBudget = MAX_CONTEXT_CHARS2 - fixedContent.length;
|
|
8068
8369
|
if (remainingBudget <= 0) {
|
|
8069
8370
|
return fixedContent + "\n\n[...traces and history omitted \u2014 context budget exceeded...]";
|
|
@@ -8074,7 +8375,7 @@ ${knowledgeContext}
|
|
|
8074
8375
|
const historySection = buildHistorySection2(history, historyBudget);
|
|
8075
8376
|
return fixedContent + "\n" + traceSection + "\n" + historySection;
|
|
8076
8377
|
}
|
|
8077
|
-
async function proposeArchitecture(iteration, workspacePath, harnessPath, history, tasks, config, architectModel, knowledgeContext) {
|
|
8378
|
+
async function proposeArchitecture(iteration, workspacePath, harnessPath, history, tasks, config, architectModel, knowledgeContext, projectContext) {
|
|
8078
8379
|
const harnessFiles = await readHarnessFiles(harnessPath);
|
|
8079
8380
|
const traces = await loadIterationTraces(workspacePath, iteration);
|
|
8080
8381
|
let effectiveKnowledge = knowledgeContext;
|
|
@@ -8091,7 +8392,8 @@ async function proposeArchitecture(iteration, workspacePath, harnessPath, histor
|
|
|
8091
8392
|
traces,
|
|
8092
8393
|
tasks,
|
|
8093
8394
|
history,
|
|
8094
|
-
effectiveKnowledge
|
|
8395
|
+
effectiveKnowledge,
|
|
8396
|
+
projectContext
|
|
8095
8397
|
);
|
|
8096
8398
|
const architectConfig = { ...config, model: architectModel };
|
|
8097
8399
|
const response = await callLLM(architectConfig, userMessage, {
|
|
@@ -8206,8 +8508,8 @@ var init_schedule = __esm({
|
|
|
8206
8508
|
});
|
|
8207
8509
|
|
|
8208
8510
|
// src/evolve/sampling.ts
|
|
8209
|
-
import
|
|
8210
|
-
import
|
|
8511
|
+
import fs29 from "fs/promises";
|
|
8512
|
+
import path29 from "path";
|
|
8211
8513
|
function initBeliefs(tasks) {
|
|
8212
8514
|
return tasks.map((task) => ({
|
|
8213
8515
|
taskId: task.id,
|
|
@@ -8268,9 +8570,9 @@ function updateBeliefs(beliefs, results) {
|
|
|
8268
8570
|
});
|
|
8269
8571
|
}
|
|
8270
8572
|
async function loadBeliefs(workspacePath) {
|
|
8271
|
-
const beliefsPath =
|
|
8573
|
+
const beliefsPath = path29.join(workspacePath, "task-beliefs.json");
|
|
8272
8574
|
try {
|
|
8273
|
-
const content = await
|
|
8575
|
+
const content = await fs29.readFile(beliefsPath, "utf-8");
|
|
8274
8576
|
const parsed = JSON.parse(content);
|
|
8275
8577
|
if (!Array.isArray(parsed)) return null;
|
|
8276
8578
|
for (const entry of parsed) {
|
|
@@ -8284,9 +8586,9 @@ async function loadBeliefs(workspacePath) {
|
|
|
8284
8586
|
}
|
|
8285
8587
|
}
|
|
8286
8588
|
async function saveBeliefs(workspacePath, beliefs) {
|
|
8287
|
-
const beliefsPath =
|
|
8288
|
-
await
|
|
8289
|
-
await
|
|
8589
|
+
const beliefsPath = path29.join(workspacePath, "task-beliefs.json");
|
|
8590
|
+
await fs29.mkdir(path29.dirname(beliefsPath), { recursive: true });
|
|
8591
|
+
await fs29.writeFile(beliefsPath, JSON.stringify(beliefs, null, 2), "utf-8");
|
|
8290
8592
|
}
|
|
8291
8593
|
var init_sampling = __esm({
|
|
8292
8594
|
"src/evolve/sampling.ts"() {
|
|
@@ -8295,8 +8597,8 @@ var init_sampling = __esm({
|
|
|
8295
8597
|
});
|
|
8296
8598
|
|
|
8297
8599
|
// src/evolve/regularization.ts
|
|
8298
|
-
import
|
|
8299
|
-
import
|
|
8600
|
+
import fs30 from "fs/promises";
|
|
8601
|
+
import path30 from "path";
|
|
8300
8602
|
async function measureComplexity(harnessPath) {
|
|
8301
8603
|
let totalLines = 0;
|
|
8302
8604
|
let totalFiles = 0;
|
|
@@ -8430,18 +8732,18 @@ async function readAllFilesRecursive(dir) {
|
|
|
8430
8732
|
async function walk(current) {
|
|
8431
8733
|
let entries;
|
|
8432
8734
|
try {
|
|
8433
|
-
entries = await
|
|
8735
|
+
entries = await fs30.readdir(current, { withFileTypes: true });
|
|
8434
8736
|
} catch {
|
|
8435
8737
|
return;
|
|
8436
8738
|
}
|
|
8437
8739
|
for (const entry of entries) {
|
|
8438
|
-
const fullPath =
|
|
8439
|
-
const relativePath =
|
|
8740
|
+
const fullPath = path30.join(current, entry.name);
|
|
8741
|
+
const relativePath = path30.relative(dir, fullPath);
|
|
8440
8742
|
if (entry.isDirectory()) {
|
|
8441
8743
|
await walk(fullPath);
|
|
8442
8744
|
} else {
|
|
8443
8745
|
try {
|
|
8444
|
-
result[relativePath] = await
|
|
8746
|
+
result[relativePath] = await fs30.readFile(fullPath, "utf-8");
|
|
8445
8747
|
} catch {
|
|
8446
8748
|
}
|
|
8447
8749
|
}
|
|
@@ -8538,14 +8840,56 @@ var init_targeting = __esm({
|
|
|
8538
8840
|
"test-writing": ["verification", "commands"],
|
|
8539
8841
|
"config-change": ["settings", "mcp"],
|
|
8540
8842
|
"documentation": ["general"],
|
|
8541
|
-
"persistence-completion": ["commands", "verification"]
|
|
8843
|
+
"persistence-completion": ["commands", "verification"],
|
|
8844
|
+
"real-bug-fix": ["general"],
|
|
8845
|
+
"real-feature-add": ["general"],
|
|
8846
|
+
"codebase-question": ["general"]
|
|
8542
8847
|
};
|
|
8543
8848
|
}
|
|
8544
8849
|
});
|
|
8545
8850
|
|
|
8546
8851
|
// src/evolve/loop.ts
|
|
8547
|
-
import
|
|
8548
|
-
import
|
|
8852
|
+
import fs31 from "fs/promises";
|
|
8853
|
+
import path31 from "path";
|
|
8854
|
+
async function loadProjectContext(projectDir) {
|
|
8855
|
+
try {
|
|
8856
|
+
const analysisPath = path31.join(projectDir, CACHE_FILENAME);
|
|
8857
|
+
const analysisRaw = await fs31.readFile(analysisPath, "utf-8");
|
|
8858
|
+
const analysisCache = JSON.parse(analysisRaw);
|
|
8859
|
+
const analysis = analysisCache.analysis;
|
|
8860
|
+
let keySourceFiles = "";
|
|
8861
|
+
try {
|
|
8862
|
+
const packedPath = path31.join(projectDir, PACKED_SOURCE_FILENAME);
|
|
8863
|
+
const packedSource = await fs31.readFile(packedPath, "utf-8");
|
|
8864
|
+
keySourceFiles = packedSource.slice(0, KEY_SOURCE_CHARS_LIMIT);
|
|
8865
|
+
} catch {
|
|
8866
|
+
}
|
|
8867
|
+
return { analysis, keySourceFiles };
|
|
8868
|
+
} catch {
|
|
8869
|
+
return null;
|
|
8870
|
+
}
|
|
8871
|
+
}
|
|
8872
|
+
async function buildProjectContext(projectData, harnessPath) {
|
|
8873
|
+
let irSummary = "";
|
|
8874
|
+
try {
|
|
8875
|
+
const irPath = path31.join(harnessPath, "..", "harness-ir.json");
|
|
8876
|
+
const irRaw = await fs31.readFile(irPath, "utf-8");
|
|
8877
|
+
const ir = JSON.parse(irRaw);
|
|
8878
|
+
irSummary = buildIRSummary(ir);
|
|
8879
|
+
} catch {
|
|
8880
|
+
try {
|
|
8881
|
+
const ir = await parseHarness(harnessPath);
|
|
8882
|
+
irSummary = buildIRSummary(ir);
|
|
8883
|
+
} catch {
|
|
8884
|
+
irSummary = "(IR not available)";
|
|
8885
|
+
}
|
|
8886
|
+
}
|
|
8887
|
+
return {
|
|
8888
|
+
analysis: projectData.analysis,
|
|
8889
|
+
irSummary,
|
|
8890
|
+
keySourceFiles: projectData.keySourceFiles || void 0
|
|
8891
|
+
};
|
|
8892
|
+
}
|
|
8549
8893
|
function computeMutationCap(iter, maxIterations, maxMutations) {
|
|
8550
8894
|
if (maxIterations <= 1) return maxMutations;
|
|
8551
8895
|
const progress = iter / (maxIterations - 1);
|
|
@@ -8564,7 +8908,7 @@ async function evolve(workspacePath, tasks, kairnConfig, evolveConfig, onProgres
|
|
|
8564
8908
|
let baselineComplexity = null;
|
|
8565
8909
|
let baselineIR = null;
|
|
8566
8910
|
if (useKL) {
|
|
8567
|
-
const baselineHarness =
|
|
8911
|
+
const baselineHarness = path31.join(workspacePath, "iterations", "0", "harness");
|
|
8568
8912
|
try {
|
|
8569
8913
|
baselineIR = await parseHarness(baselineHarness);
|
|
8570
8914
|
baselineComplexity = measureComplexityFromIR(baselineIR);
|
|
@@ -8576,20 +8920,22 @@ async function evolve(workspacePath, tasks, kairnConfig, evolveConfig, onProgres
|
|
|
8576
8920
|
}
|
|
8577
8921
|
}
|
|
8578
8922
|
let lastChangedAspects = null;
|
|
8923
|
+
const projectDir = path31.resolve(workspacePath, "..");
|
|
8924
|
+
const projectData = await loadProjectContext(projectDir);
|
|
8579
8925
|
let rngState = evolveConfig.rngSeed ?? 42;
|
|
8580
8926
|
const rng = () => {
|
|
8581
8927
|
rngState = rngState * 1664525 + 1013904223 & 4294967295;
|
|
8582
8928
|
return (rngState >>> 0) / 4294967296;
|
|
8583
8929
|
};
|
|
8584
8930
|
for (let iter = 0; iter < evolveConfig.maxIterations; iter++) {
|
|
8585
|
-
const harnessPath =
|
|
8931
|
+
const harnessPath = path31.join(
|
|
8586
8932
|
workspacePath,
|
|
8587
8933
|
"iterations",
|
|
8588
8934
|
iter.toString(),
|
|
8589
8935
|
"harness"
|
|
8590
8936
|
);
|
|
8591
8937
|
try {
|
|
8592
|
-
await
|
|
8938
|
+
await fs31.access(harnessPath);
|
|
8593
8939
|
} catch {
|
|
8594
8940
|
if (iter === 0) {
|
|
8595
8941
|
throw new Error(
|
|
@@ -8698,7 +9044,7 @@ async function evolve(workspacePath, tasks, kairnConfig, evolveConfig, onProgres
|
|
|
8698
9044
|
}
|
|
8699
9045
|
const diffRatio = await computeDiffRatio(
|
|
8700
9046
|
harnessPath,
|
|
8701
|
-
|
|
9047
|
+
path31.join(workspacePath, "iterations", "0", "harness")
|
|
8702
9048
|
);
|
|
8703
9049
|
currentComplexity.diffFromBaseline = diffRatio;
|
|
8704
9050
|
iterComplexityCost = computeComplexityCost(currentComplexity, baselineComplexity);
|
|
@@ -8767,7 +9113,7 @@ async function evolve(workspacePath, tasks, kairnConfig, evolveConfig, onProgres
|
|
|
8767
9113
|
};
|
|
8768
9114
|
await writeIterationLog(workspacePath, rollbackLog);
|
|
8769
9115
|
history.push(rollbackLog);
|
|
8770
|
-
const bestHarnessPath =
|
|
9116
|
+
const bestHarnessPath = path31.join(
|
|
8771
9117
|
workspacePath,
|
|
8772
9118
|
"iterations",
|
|
8773
9119
|
bestIteration.toString(),
|
|
@@ -8776,6 +9122,7 @@ async function evolve(workspacePath, tasks, kairnConfig, evolveConfig, onProgres
|
|
|
8776
9122
|
if (iter + 1 < evolveConfig.maxIterations) {
|
|
8777
9123
|
onProgress?.({ type: "proposing", iteration: iter, message: "Proposing new mutations after rollback" });
|
|
8778
9124
|
try {
|
|
9125
|
+
const rollbackProjectCtx = projectData ? await buildProjectContext(projectData, bestHarnessPath) : void 0;
|
|
8779
9126
|
let rollbackProposal = await propose(
|
|
8780
9127
|
iter,
|
|
8781
9128
|
workspacePath,
|
|
@@ -8783,7 +9130,8 @@ async function evolve(workspacePath, tasks, kairnConfig, evolveConfig, onProgres
|
|
|
8783
9130
|
history,
|
|
8784
9131
|
tasks,
|
|
8785
9132
|
kairnConfig,
|
|
8786
|
-
evolveConfig.proposerModel
|
|
9133
|
+
evolveConfig.proposerModel,
|
|
9134
|
+
rollbackProjectCtx
|
|
8787
9135
|
);
|
|
8788
9136
|
const rollbackCap = computeMutationCap(iter, evolveConfig.maxIterations, evolveConfig.maxMutationsPerIteration);
|
|
8789
9137
|
if (rollbackProposal.mutations.length > rollbackCap) {
|
|
@@ -8792,7 +9140,7 @@ async function evolve(workspacePath, tasks, kairnConfig, evolveConfig, onProgres
|
|
|
8792
9140
|
mutations: rollbackProposal.mutations.slice(0, rollbackCap)
|
|
8793
9141
|
};
|
|
8794
9142
|
}
|
|
8795
|
-
const nextIterDir2 =
|
|
9143
|
+
const nextIterDir2 = path31.join(workspacePath, "iterations", (iter + 1).toString());
|
|
8796
9144
|
await applyMutations(bestHarnessPath, nextIterDir2, rollbackProposal.mutations);
|
|
8797
9145
|
try {
|
|
8798
9146
|
const rollbackIR = await parseHarness(bestHarnessPath);
|
|
@@ -8807,8 +9155,8 @@ async function evolve(workspacePath, tasks, kairnConfig, evolveConfig, onProgres
|
|
|
8807
9155
|
mutationCount: rollbackProposal.mutations.length
|
|
8808
9156
|
});
|
|
8809
9157
|
} catch {
|
|
8810
|
-
const nextIterDir2 =
|
|
8811
|
-
await copyDir(bestHarnessPath,
|
|
9158
|
+
const nextIterDir2 = path31.join(workspacePath, "iterations", (iter + 1).toString());
|
|
9159
|
+
await copyDir(bestHarnessPath, path31.join(nextIterDir2, "harness"));
|
|
8812
9160
|
}
|
|
8813
9161
|
}
|
|
8814
9162
|
continue;
|
|
@@ -8859,6 +9207,7 @@ async function evolve(workspacePath, tasks, kairnConfig, evolveConfig, onProgres
|
|
|
8859
9207
|
onProgress?.({ type: "architect-start", iteration: iter });
|
|
8860
9208
|
let architectProposal;
|
|
8861
9209
|
try {
|
|
9210
|
+
const architectProjectCtx = projectData ? await buildProjectContext(projectData, harnessPath) : void 0;
|
|
8862
9211
|
architectProposal = await proposeArchitecture(
|
|
8863
9212
|
iter,
|
|
8864
9213
|
workspacePath,
|
|
@@ -8866,7 +9215,9 @@ async function evolve(workspacePath, tasks, kairnConfig, evolveConfig, onProgres
|
|
|
8866
9215
|
history,
|
|
8867
9216
|
tasks,
|
|
8868
9217
|
kairnConfig,
|
|
8869
|
-
evolveConfig.architectModel
|
|
9218
|
+
evolveConfig.architectModel,
|
|
9219
|
+
void 0,
|
|
9220
|
+
architectProjectCtx
|
|
8870
9221
|
);
|
|
8871
9222
|
const architectCap = computeArchitectMutationBudget(iter, evolveConfig.maxIterations);
|
|
8872
9223
|
if (architectProposal.mutations.length > architectCap) {
|
|
@@ -8882,8 +9233,8 @@ async function evolve(workspacePath, tasks, kairnConfig, evolveConfig, onProgres
|
|
|
8882
9233
|
iteration: iter,
|
|
8883
9234
|
message: `Architect failed: ${errMsg}. Falling back to reactive proposer.`
|
|
8884
9235
|
});
|
|
8885
|
-
const nextIterDir2 =
|
|
8886
|
-
await copyDir(harnessPath,
|
|
9236
|
+
const nextIterDir2 = path31.join(workspacePath, "iterations", (iter + 1).toString());
|
|
9237
|
+
await copyDir(harnessPath, path31.join(nextIterDir2, "harness"));
|
|
8887
9238
|
const skipLog = {
|
|
8888
9239
|
iteration: iter,
|
|
8889
9240
|
score: aggregate,
|
|
@@ -8900,7 +9251,7 @@ async function evolve(workspacePath, tasks, kairnConfig, evolveConfig, onProgres
|
|
|
8900
9251
|
continue;
|
|
8901
9252
|
}
|
|
8902
9253
|
onProgress?.({ type: "architect-staging", iteration: iter });
|
|
8903
|
-
const stagingDir =
|
|
9254
|
+
const stagingDir = path31.join(workspacePath, "staging", iter.toString());
|
|
8904
9255
|
let stagingHarnessPath;
|
|
8905
9256
|
try {
|
|
8906
9257
|
const stagingResult = await applyMutations(
|
|
@@ -8911,8 +9262,8 @@ async function evolve(workspacePath, tasks, kairnConfig, evolveConfig, onProgres
|
|
|
8911
9262
|
stagingHarnessPath = stagingResult.newHarnessPath;
|
|
8912
9263
|
} catch {
|
|
8913
9264
|
onProgress?.({ type: "architect-rejected", iteration: iter, score: 0, message: "Failed to apply mutations to staging" });
|
|
8914
|
-
const nextIterDir2 =
|
|
8915
|
-
await copyDir(harnessPath,
|
|
9265
|
+
const nextIterDir2 = path31.join(workspacePath, "iterations", (iter + 1).toString());
|
|
9266
|
+
await copyDir(harnessPath, path31.join(nextIterDir2, "harness"));
|
|
8916
9267
|
const rejectLog = {
|
|
8917
9268
|
iteration: iter,
|
|
8918
9269
|
score: aggregate,
|
|
@@ -8926,7 +9277,7 @@ async function evolve(workspacePath, tasks, kairnConfig, evolveConfig, onProgres
|
|
|
8926
9277
|
};
|
|
8927
9278
|
await writeIterationLog(workspacePath, rejectLog);
|
|
8928
9279
|
history.push(rejectLog);
|
|
8929
|
-
await
|
|
9280
|
+
await fs31.rm(stagingDir, { recursive: true, force: true }).catch(() => {
|
|
8930
9281
|
});
|
|
8931
9282
|
continue;
|
|
8932
9283
|
}
|
|
@@ -8943,12 +9294,12 @@ async function evolve(workspacePath, tasks, kairnConfig, evolveConfig, onProgres
|
|
|
8943
9294
|
);
|
|
8944
9295
|
if (stagingScore >= bestScore) {
|
|
8945
9296
|
onProgress?.({ type: "architect-accepted", iteration: iter, score: stagingScore });
|
|
8946
|
-
const nextIterDir2 =
|
|
8947
|
-
await copyDir(stagingHarnessPath,
|
|
9297
|
+
const nextIterDir2 = path31.join(workspacePath, "iterations", (iter + 1).toString());
|
|
9298
|
+
await copyDir(stagingHarnessPath, path31.join(nextIterDir2, "harness"));
|
|
8948
9299
|
} else {
|
|
8949
9300
|
onProgress?.({ type: "architect-rejected", iteration: iter, score: stagingScore });
|
|
8950
|
-
const nextIterDir2 =
|
|
8951
|
-
await copyDir(harnessPath,
|
|
9301
|
+
const nextIterDir2 = path31.join(workspacePath, "iterations", (iter + 1).toString());
|
|
9302
|
+
await copyDir(harnessPath, path31.join(nextIterDir2, "harness"));
|
|
8952
9303
|
}
|
|
8953
9304
|
const architectLog = {
|
|
8954
9305
|
iteration: iter,
|
|
@@ -8963,13 +9314,14 @@ async function evolve(workspacePath, tasks, kairnConfig, evolveConfig, onProgres
|
|
|
8963
9314
|
};
|
|
8964
9315
|
await writeIterationLog(workspacePath, architectLog);
|
|
8965
9316
|
history.push(architectLog);
|
|
8966
|
-
await
|
|
9317
|
+
await fs31.rm(stagingDir, { recursive: true, force: true }).catch(() => {
|
|
8967
9318
|
});
|
|
8968
9319
|
continue;
|
|
8969
9320
|
}
|
|
8970
9321
|
onProgress?.({ type: "proposing", iteration: iter });
|
|
8971
9322
|
let proposal;
|
|
8972
9323
|
try {
|
|
9324
|
+
const iterProjectCtx = projectData ? await buildProjectContext(projectData, harnessPath) : void 0;
|
|
8973
9325
|
proposal = await propose(
|
|
8974
9326
|
iter,
|
|
8975
9327
|
workspacePath,
|
|
@@ -8977,7 +9329,8 @@ async function evolve(workspacePath, tasks, kairnConfig, evolveConfig, onProgres
|
|
|
8977
9329
|
history,
|
|
8978
9330
|
tasks,
|
|
8979
9331
|
kairnConfig,
|
|
8980
|
-
evolveConfig.proposerModel
|
|
9332
|
+
evolveConfig.proposerModel,
|
|
9333
|
+
iterProjectCtx
|
|
8981
9334
|
);
|
|
8982
9335
|
const iterCap = computeMutationCap(iter, evolveConfig.maxIterations, evolveConfig.maxMutationsPerIteration);
|
|
8983
9336
|
if (proposal.mutations.length > iterCap) {
|
|
@@ -8993,12 +9346,12 @@ async function evolve(workspacePath, tasks, kairnConfig, evolveConfig, onProgres
|
|
|
8993
9346
|
iteration: iter,
|
|
8994
9347
|
message: `Proposer failed: ${errMsg}`
|
|
8995
9348
|
});
|
|
8996
|
-
const nextIterDir2 =
|
|
9349
|
+
const nextIterDir2 = path31.join(
|
|
8997
9350
|
workspacePath,
|
|
8998
9351
|
"iterations",
|
|
8999
9352
|
(iter + 1).toString()
|
|
9000
9353
|
);
|
|
9001
|
-
await copyDir(harnessPath,
|
|
9354
|
+
await copyDir(harnessPath, path31.join(nextIterDir2, "harness"));
|
|
9002
9355
|
const skipLog = {
|
|
9003
9356
|
iteration: iter,
|
|
9004
9357
|
score: aggregate,
|
|
@@ -9014,7 +9367,7 @@ async function evolve(workspacePath, tasks, kairnConfig, evolveConfig, onProgres
|
|
|
9014
9367
|
history.push(skipLog);
|
|
9015
9368
|
continue;
|
|
9016
9369
|
}
|
|
9017
|
-
const nextIterDir =
|
|
9370
|
+
const nextIterDir = path31.join(
|
|
9018
9371
|
workspacePath,
|
|
9019
9372
|
"iterations",
|
|
9020
9373
|
(iter + 1).toString()
|
|
@@ -9035,7 +9388,7 @@ async function evolve(workspacePath, tasks, kairnConfig, evolveConfig, onProgres
|
|
|
9035
9388
|
lastChangedAspects = null;
|
|
9036
9389
|
}
|
|
9037
9390
|
} catch {
|
|
9038
|
-
await copyDir(harnessPath,
|
|
9391
|
+
await copyDir(harnessPath, path31.join(nextIterDir, "harness"));
|
|
9039
9392
|
lastChangedAspects = null;
|
|
9040
9393
|
}
|
|
9041
9394
|
onProgress?.({
|
|
@@ -9059,8 +9412,9 @@ async function evolve(workspacePath, tasks, kairnConfig, evolveConfig, onProgres
|
|
|
9059
9412
|
}
|
|
9060
9413
|
if (evolveConfig.usePrincipal && history.length >= 2) {
|
|
9061
9414
|
onProgress?.({ type: "proposing", iteration: history.length, message: "Principal Proposer synthesizing final harness" });
|
|
9062
|
-
const baselineHarnessPath =
|
|
9415
|
+
const baselineHarnessPath = path31.join(workspacePath, "iterations", "0", "harness");
|
|
9063
9416
|
try {
|
|
9417
|
+
const principalProjectCtx = projectData ? await buildProjectContext(projectData, baselineHarnessPath) : void 0;
|
|
9064
9418
|
let principalProposal = await propose(
|
|
9065
9419
|
history.length,
|
|
9066
9420
|
workspacePath,
|
|
@@ -9068,7 +9422,8 @@ async function evolve(workspacePath, tasks, kairnConfig, evolveConfig, onProgres
|
|
|
9068
9422
|
history,
|
|
9069
9423
|
tasks,
|
|
9070
9424
|
kairnConfig,
|
|
9071
|
-
evolveConfig.proposerModel
|
|
9425
|
+
evolveConfig.proposerModel,
|
|
9426
|
+
principalProjectCtx
|
|
9072
9427
|
);
|
|
9073
9428
|
if (principalProposal.mutations.length > evolveConfig.maxMutationsPerIteration) {
|
|
9074
9429
|
principalProposal = {
|
|
@@ -9077,7 +9432,7 @@ async function evolve(workspacePath, tasks, kairnConfig, evolveConfig, onProgres
|
|
|
9077
9432
|
};
|
|
9078
9433
|
}
|
|
9079
9434
|
const principalIterNum = history.length;
|
|
9080
|
-
const principalIterDir =
|
|
9435
|
+
const principalIterDir = path31.join(workspacePath, "iterations", principalIterNum.toString());
|
|
9081
9436
|
const mutResult = await applyMutations(baselineHarnessPath, principalIterDir, principalProposal.mutations);
|
|
9082
9437
|
onProgress?.({ type: "iteration-start", iteration: principalIterNum });
|
|
9083
9438
|
const { results: principalResults, aggregate: principalAggregate } = await evaluateAll(
|
|
@@ -9118,7 +9473,7 @@ async function evolve(workspacePath, tasks, kairnConfig, evolveConfig, onProgres
|
|
|
9118
9473
|
}
|
|
9119
9474
|
try {
|
|
9120
9475
|
const { extractAndSavePatterns: extractAndSavePatterns2 } = await Promise.resolve().then(() => (init_knowledge(), knowledge_exports));
|
|
9121
|
-
const projectName =
|
|
9476
|
+
const projectName = path31.basename(path31.resolve(workspacePath, ".."));
|
|
9122
9477
|
await extractAndSavePatterns2(history, projectName, null);
|
|
9123
9478
|
} catch {
|
|
9124
9479
|
}
|
|
@@ -9134,6 +9489,7 @@ async function evolve(workspacePath, tasks, kairnConfig, evolveConfig, onProgres
|
|
|
9134
9489
|
baselineScore
|
|
9135
9490
|
};
|
|
9136
9491
|
}
|
|
9492
|
+
var KEY_SOURCE_CHARS_LIMIT;
|
|
9137
9493
|
var init_loop = __esm({
|
|
9138
9494
|
"src/evolve/loop.ts"() {
|
|
9139
9495
|
"use strict";
|
|
@@ -9149,11 +9505,13 @@ var init_loop = __esm({
|
|
|
9149
9505
|
init_parser();
|
|
9150
9506
|
init_translate();
|
|
9151
9507
|
init_targeting();
|
|
9508
|
+
init_cache();
|
|
9509
|
+
KEY_SOURCE_CHARS_LIMIT = 1e4;
|
|
9152
9510
|
}
|
|
9153
9511
|
});
|
|
9154
9512
|
|
|
9155
9513
|
// src/evolve/synthesis.ts
|
|
9156
|
-
import
|
|
9514
|
+
import path34 from "path";
|
|
9157
9515
|
function buildMetaPrincipalSystemPrompt(numBranches) {
|
|
9158
9516
|
return `You are reviewing the COMPLETE results of ${numBranches} independent evolution runs.
|
|
9159
9517
|
Each branch explored different mutations and saw different task subsets.
|
|
@@ -9309,7 +9667,7 @@ async function runSynthesis(context, kairnConfig, evolveConfig, workspacePath) {
|
|
|
9309
9667
|
if (mutations.length === 0) {
|
|
9310
9668
|
return null;
|
|
9311
9669
|
}
|
|
9312
|
-
const synthesisDir =
|
|
9670
|
+
const synthesisDir = path34.join(workspacePath, "synthesis");
|
|
9313
9671
|
const { newHarnessPath } = await applyMutations(
|
|
9314
9672
|
context.baselineHarnessPath,
|
|
9315
9673
|
synthesisDir,
|
|
@@ -9343,26 +9701,26 @@ __export(population_exports, {
|
|
|
9343
9701
|
initBranches: () => initBranches,
|
|
9344
9702
|
runPopulation: () => runPopulation
|
|
9345
9703
|
});
|
|
9346
|
-
import
|
|
9347
|
-
import
|
|
9704
|
+
import fs34 from "fs/promises";
|
|
9705
|
+
import path35 from "path";
|
|
9348
9706
|
async function initBranches(workspacePath, baselinePath, numBranches) {
|
|
9349
|
-
const branchesDir =
|
|
9350
|
-
await
|
|
9707
|
+
const branchesDir = path35.join(workspacePath, "branches");
|
|
9708
|
+
await fs34.mkdir(branchesDir, { recursive: true });
|
|
9351
9709
|
const configs = [];
|
|
9352
9710
|
for (let i = 0; i < numBranches; i++) {
|
|
9353
|
-
const branchPath =
|
|
9354
|
-
const harnessPath =
|
|
9711
|
+
const branchPath = path35.join(branchesDir, i.toString());
|
|
9712
|
+
const harnessPath = path35.join(branchPath, "iterations", "0", "harness");
|
|
9355
9713
|
await copyDir(baselinePath, harnessPath);
|
|
9356
|
-
const tasksYaml =
|
|
9714
|
+
const tasksYaml = path35.join(workspacePath, "tasks.yaml");
|
|
9357
9715
|
try {
|
|
9358
|
-
await
|
|
9359
|
-
await
|
|
9716
|
+
await fs34.access(tasksYaml);
|
|
9717
|
+
await fs34.copyFile(tasksYaml, path35.join(branchPath, "tasks.yaml"));
|
|
9360
9718
|
} catch {
|
|
9361
9719
|
}
|
|
9362
|
-
const configYaml =
|
|
9720
|
+
const configYaml = path35.join(workspacePath, "config.yaml");
|
|
9363
9721
|
try {
|
|
9364
|
-
await
|
|
9365
|
-
await
|
|
9722
|
+
await fs34.access(configYaml);
|
|
9723
|
+
await fs34.copyFile(configYaml, path35.join(branchPath, "config.yaml"));
|
|
9366
9724
|
} catch {
|
|
9367
9725
|
}
|
|
9368
9726
|
const seed = 42 + i * 1337;
|
|
@@ -9376,7 +9734,7 @@ async function initBranches(workspacePath, baselinePath, numBranches) {
|
|
|
9376
9734
|
}
|
|
9377
9735
|
async function runPopulation(workspacePath, tasks, kairnConfig, evolveConfig, numBranches, onProgress) {
|
|
9378
9736
|
const branches = numBranches ?? evolveConfig.pbtBranches;
|
|
9379
|
-
const baselinePath =
|
|
9737
|
+
const baselinePath = path35.join(workspacePath, "baseline");
|
|
9380
9738
|
const branchConfigs = await initBranches(workspacePath, baselinePath, branches);
|
|
9381
9739
|
const branchPromises = branchConfigs.map(async (branchConfig) => {
|
|
9382
9740
|
const branchEvolveConfig = {
|
|
@@ -9396,7 +9754,7 @@ async function runPopulation(workspacePath, tasks, kairnConfig, evolveConfig, nu
|
|
|
9396
9754
|
branchEvolveConfig,
|
|
9397
9755
|
branchProgress
|
|
9398
9756
|
);
|
|
9399
|
-
const finalHarnessPath =
|
|
9757
|
+
const finalHarnessPath = path35.join(
|
|
9400
9758
|
branchConfig.workspacePath,
|
|
9401
9759
|
"iterations",
|
|
9402
9760
|
result.bestIteration.toString(),
|
|
@@ -9404,8 +9762,8 @@ async function runPopulation(workspacePath, tasks, kairnConfig, evolveConfig, nu
|
|
|
9404
9762
|
);
|
|
9405
9763
|
let beliefs = [];
|
|
9406
9764
|
try {
|
|
9407
|
-
const beliefsPath =
|
|
9408
|
-
const beliefsContent = await
|
|
9765
|
+
const beliefsPath = path35.join(branchConfig.workspacePath, "task-beliefs.json");
|
|
9766
|
+
const beliefsContent = await fs34.readFile(beliefsPath, "utf-8");
|
|
9409
9767
|
beliefs = JSON.parse(beliefsContent);
|
|
9410
9768
|
} catch {
|
|
9411
9769
|
}
|
|
@@ -9427,7 +9785,7 @@ async function runPopulation(workspacePath, tasks, kairnConfig, evolveConfig, nu
|
|
|
9427
9785
|
}
|
|
9428
9786
|
let synthesizedResult;
|
|
9429
9787
|
try {
|
|
9430
|
-
const baselinePath2 =
|
|
9788
|
+
const baselinePath2 = path35.join(workspacePath, "baseline");
|
|
9431
9789
|
const synthesisResult = await runSynthesis(
|
|
9432
9790
|
{ branches: branchResults, tasks, baselineHarnessPath: baselinePath2 },
|
|
9433
9791
|
kairnConfig,
|
|
@@ -9489,8 +9847,8 @@ __export(research_exports, {
|
|
|
9489
9847
|
formatResearchReport: () => formatResearchReport,
|
|
9490
9848
|
runResearch: () => runResearch
|
|
9491
9849
|
});
|
|
9492
|
-
import
|
|
9493
|
-
import
|
|
9850
|
+
import fs35 from "fs/promises";
|
|
9851
|
+
import path36 from "path";
|
|
9494
9852
|
import os5 from "os";
|
|
9495
9853
|
import { exec as exec4 } from "child_process";
|
|
9496
9854
|
import { promisify as promisify4 } from "util";
|
|
@@ -9509,8 +9867,8 @@ async function runResearch(config, _kairnConfig, _evolveConfig, onProgress) {
|
|
|
9509
9867
|
}
|
|
9510
9868
|
const allPatterns = [];
|
|
9511
9869
|
const repoResults = [];
|
|
9512
|
-
const tempBase =
|
|
9513
|
-
await
|
|
9870
|
+
const tempBase = path36.join(os5.tmpdir(), `kairn-research-${Date.now()}`);
|
|
9871
|
+
await fs35.mkdir(tempBase, { recursive: true });
|
|
9514
9872
|
try {
|
|
9515
9873
|
for (let i = 0; i < config.repos.length; i++) {
|
|
9516
9874
|
const url = config.repos[i];
|
|
@@ -9522,7 +9880,7 @@ async function runResearch(config, _kairnConfig, _evolveConfig, onProgress) {
|
|
|
9522
9880
|
totalRepos: config.repos.length,
|
|
9523
9881
|
message: `Cloning ${name}...`
|
|
9524
9882
|
});
|
|
9525
|
-
const repoDir =
|
|
9883
|
+
const repoDir = path36.join(tempBase, name);
|
|
9526
9884
|
try {
|
|
9527
9885
|
await execAsync4(`git clone --depth 1 '${url.replace(/'/g, "'\\''")}' '${repoDir.replace(/'/g, "'\\''")}' 2>/dev/null`, { timeout: 6e4 });
|
|
9528
9886
|
} catch (err) {
|
|
@@ -9536,10 +9894,10 @@ async function runResearch(config, _kairnConfig, _evolveConfig, onProgress) {
|
|
|
9536
9894
|
repoResults.push({ repo: name, bestScore: 0, patternsFound: 0 });
|
|
9537
9895
|
continue;
|
|
9538
9896
|
}
|
|
9539
|
-
const claudeDir =
|
|
9897
|
+
const claudeDir = path36.join(repoDir, ".claude");
|
|
9540
9898
|
let hasHarness = false;
|
|
9541
9899
|
try {
|
|
9542
|
-
await
|
|
9900
|
+
await fs35.access(claudeDir);
|
|
9543
9901
|
hasHarness = true;
|
|
9544
9902
|
} catch {
|
|
9545
9903
|
onProgress?.({
|
|
@@ -9589,7 +9947,7 @@ async function runResearch(config, _kairnConfig, _evolveConfig, onProgress) {
|
|
|
9589
9947
|
onProgress?.({ type: "research-complete", message: "Research complete" });
|
|
9590
9948
|
return report;
|
|
9591
9949
|
} finally {
|
|
9592
|
-
await
|
|
9950
|
+
await fs35.rm(tempBase, { recursive: true, force: true }).catch(() => {
|
|
9593
9951
|
});
|
|
9594
9952
|
}
|
|
9595
9953
|
}
|
|
@@ -9960,6 +10318,7 @@ async function detectExistingRepo(dir) {
|
|
|
9960
10318
|
}
|
|
9961
10319
|
|
|
9962
10320
|
// src/commands/describe.ts
|
|
10321
|
+
init_persist();
|
|
9963
10322
|
var describeCommand = new Command3("describe").description("Describe your workflow and generate a Claude Code environment").argument("[intent]", "What you want your agent to do").option("-y, --yes", "Skip confirmation prompt").option("-q, --quick", "Skip clarification questions").option("--runtime <runtime>", "Target runtime (claude-code or hermes)", "claude-code").action(async (intentArg, options) => {
|
|
9964
10323
|
printFullBanner("The Agent Environment Compiler");
|
|
9965
10324
|
const config = await loadConfig();
|
|
@@ -10059,6 +10418,13 @@ Autonomy level: ${autonomyLevel} (${autonomyLabel(autonomyLevel)})`;
|
|
|
10059
10418
|
`));
|
|
10060
10419
|
process.exit(1);
|
|
10061
10420
|
}
|
|
10421
|
+
if (spec.ir) {
|
|
10422
|
+
try {
|
|
10423
|
+
await persistHarnessIR(process.cwd(), spec.ir);
|
|
10424
|
+
} catch {
|
|
10425
|
+
console.log(ui.warn("Could not persist harness IR to .kairn/harness-ir.json"));
|
|
10426
|
+
}
|
|
10427
|
+
}
|
|
10062
10428
|
const registry = await loadRegistry();
|
|
10063
10429
|
const summary = summarizeSpec(spec, registry);
|
|
10064
10430
|
console.log("");
|
|
@@ -10133,14 +10499,14 @@ init_ui();
|
|
|
10133
10499
|
init_logo();
|
|
10134
10500
|
import { Command as Command4 } from "commander";
|
|
10135
10501
|
import chalk7 from "chalk";
|
|
10136
|
-
import
|
|
10137
|
-
import
|
|
10502
|
+
import fs15 from "fs/promises";
|
|
10503
|
+
import path15 from "path";
|
|
10138
10504
|
var listCommand = new Command4("list").description("Show saved environments").action(async () => {
|
|
10139
10505
|
printCompactBanner();
|
|
10140
10506
|
const envsDir = getEnvsDir();
|
|
10141
10507
|
let files;
|
|
10142
10508
|
try {
|
|
10143
|
-
files = await
|
|
10509
|
+
files = await fs15.readdir(envsDir);
|
|
10144
10510
|
} catch {
|
|
10145
10511
|
console.log(chalk7.dim(" No environments yet. Run ") + chalk7.bold("kairn describe") + chalk7.dim(" to create one.\n"));
|
|
10146
10512
|
return;
|
|
@@ -10153,7 +10519,7 @@ var listCommand = new Command4("list").description("Show saved environments").ac
|
|
|
10153
10519
|
let first = true;
|
|
10154
10520
|
for (const file of jsonFiles) {
|
|
10155
10521
|
try {
|
|
10156
|
-
const data = await
|
|
10522
|
+
const data = await fs15.readFile(path15.join(envsDir, file), "utf-8");
|
|
10157
10523
|
const spec = JSON.parse(data);
|
|
10158
10524
|
const date = new Date(spec.created_at).toLocaleDateString();
|
|
10159
10525
|
const toolCount = spec.tools?.length ?? 0;
|
|
@@ -10178,8 +10544,8 @@ init_ui();
|
|
|
10178
10544
|
init_logo();
|
|
10179
10545
|
import { Command as Command5 } from "commander";
|
|
10180
10546
|
import chalk8 from "chalk";
|
|
10181
|
-
import
|
|
10182
|
-
import
|
|
10547
|
+
import fs16 from "fs/promises";
|
|
10548
|
+
import path16 from "path";
|
|
10183
10549
|
var activateCommand = new Command5("activate").description("Re-deploy a saved environment to the current directory").argument("<env_id>", "Environment ID (from kairn list)").action(async (envId) => {
|
|
10184
10550
|
printCompactBanner();
|
|
10185
10551
|
const envsDir = getEnvsDir();
|
|
@@ -10189,7 +10555,7 @@ var activateCommand = new Command5("activate").description("Re-deploy a saved en
|
|
|
10189
10555
|
let fromTemplate = false;
|
|
10190
10556
|
let envFiles = [];
|
|
10191
10557
|
try {
|
|
10192
|
-
envFiles = await
|
|
10558
|
+
envFiles = await fs16.readdir(envsDir);
|
|
10193
10559
|
} catch {
|
|
10194
10560
|
}
|
|
10195
10561
|
match = envFiles.find(
|
|
@@ -10200,7 +10566,7 @@ var activateCommand = new Command5("activate").description("Re-deploy a saved en
|
|
|
10200
10566
|
} else {
|
|
10201
10567
|
let templateFiles = [];
|
|
10202
10568
|
try {
|
|
10203
|
-
templateFiles = await
|
|
10569
|
+
templateFiles = await fs16.readdir(templatesDir);
|
|
10204
10570
|
} catch {
|
|
10205
10571
|
}
|
|
10206
10572
|
match = templateFiles.find(
|
|
@@ -10216,7 +10582,7 @@ var activateCommand = new Command5("activate").description("Re-deploy a saved en
|
|
|
10216
10582
|
process.exit(1);
|
|
10217
10583
|
}
|
|
10218
10584
|
}
|
|
10219
|
-
const data = await
|
|
10585
|
+
const data = await fs16.readFile(path16.join(sourceDir, match), "utf-8");
|
|
10220
10586
|
const spec = JSON.parse(data);
|
|
10221
10587
|
const label = fromTemplate ? chalk8.dim(" (template)") : "";
|
|
10222
10588
|
console.log(chalk8.cyan(` Activating: ${spec.name}`) + label);
|
|
@@ -10236,21 +10602,21 @@ init_ui();
|
|
|
10236
10602
|
init_logo();
|
|
10237
10603
|
import { Command as Command6 } from "commander";
|
|
10238
10604
|
import chalk9 from "chalk";
|
|
10239
|
-
import
|
|
10240
|
-
import
|
|
10605
|
+
import fs17 from "fs/promises";
|
|
10606
|
+
import path17 from "path";
|
|
10241
10607
|
import { fileURLToPath as fileURLToPath4 } from "url";
|
|
10242
10608
|
var REGISTRY_URL = "https://raw.githubusercontent.com/ashtonperlroth/kairn/main/src/registry/tools.json";
|
|
10243
10609
|
async function getLocalRegistryPath() {
|
|
10244
10610
|
const __filename3 = fileURLToPath4(import.meta.url);
|
|
10245
|
-
const __dirname3 =
|
|
10611
|
+
const __dirname3 = path17.dirname(__filename3);
|
|
10246
10612
|
const candidates = [
|
|
10247
|
-
|
|
10248
|
-
|
|
10249
|
-
|
|
10613
|
+
path17.resolve(__dirname3, "../registry/tools.json"),
|
|
10614
|
+
path17.resolve(__dirname3, "../src/registry/tools.json"),
|
|
10615
|
+
path17.resolve(__dirname3, "../../src/registry/tools.json")
|
|
10250
10616
|
];
|
|
10251
10617
|
for (const candidate of candidates) {
|
|
10252
10618
|
try {
|
|
10253
|
-
await
|
|
10619
|
+
await fs17.access(candidate);
|
|
10254
10620
|
return candidate;
|
|
10255
10621
|
} catch {
|
|
10256
10622
|
continue;
|
|
@@ -10287,10 +10653,10 @@ var updateRegistryCommand = new Command6("update-registry").description("Fetch t
|
|
|
10287
10653
|
const registryPath = await getLocalRegistryPath();
|
|
10288
10654
|
const backupPath = registryPath + ".bak";
|
|
10289
10655
|
try {
|
|
10290
|
-
await
|
|
10656
|
+
await fs17.copyFile(registryPath, backupPath);
|
|
10291
10657
|
} catch {
|
|
10292
10658
|
}
|
|
10293
|
-
await
|
|
10659
|
+
await fs17.writeFile(registryPath, JSON.stringify(tools, null, 2), "utf-8");
|
|
10294
10660
|
console.log(ui.success(`Registry updated: ${tools.length} tools`));
|
|
10295
10661
|
console.log(chalk9.dim(` Saved to: ${registryPath}`));
|
|
10296
10662
|
console.log(chalk9.dim(` Backup: ${backupPath}
|
|
@@ -10669,14 +11035,14 @@ init_ui();
|
|
|
10669
11035
|
init_logo();
|
|
10670
11036
|
import { Command as Command9 } from "commander";
|
|
10671
11037
|
import chalk12 from "chalk";
|
|
10672
|
-
import
|
|
10673
|
-
import
|
|
11038
|
+
import fs18 from "fs/promises";
|
|
11039
|
+
import path18 from "path";
|
|
10674
11040
|
var templatesCommand = new Command9("templates").description("Browse available templates").option("--category <cat>", "filter templates by category keyword").option("--json", "output raw JSON array").action(async (options) => {
|
|
10675
11041
|
printCompactBanner();
|
|
10676
11042
|
const templatesDir = getTemplatesDir();
|
|
10677
11043
|
let files;
|
|
10678
11044
|
try {
|
|
10679
|
-
files = await
|
|
11045
|
+
files = await fs18.readdir(templatesDir);
|
|
10680
11046
|
} catch {
|
|
10681
11047
|
console.log(
|
|
10682
11048
|
chalk12.dim(
|
|
@@ -10701,8 +11067,8 @@ var templatesCommand = new Command9("templates").description("Browse available t
|
|
|
10701
11067
|
const templates = [];
|
|
10702
11068
|
for (const file of jsonFiles) {
|
|
10703
11069
|
try {
|
|
10704
|
-
const data = await
|
|
10705
|
-
|
|
11070
|
+
const data = await fs18.readFile(
|
|
11071
|
+
path18.join(templatesDir, file),
|
|
10706
11072
|
"utf-8"
|
|
10707
11073
|
);
|
|
10708
11074
|
const spec = JSON.parse(data);
|
|
@@ -10753,8 +11119,8 @@ init_loader();
|
|
|
10753
11119
|
import { Command as Command10 } from "commander";
|
|
10754
11120
|
import { password as password3 } from "@inquirer/prompts";
|
|
10755
11121
|
import chalk13 from "chalk";
|
|
10756
|
-
import
|
|
10757
|
-
import
|
|
11122
|
+
import fs19 from "fs/promises";
|
|
11123
|
+
import path19 from "path";
|
|
10758
11124
|
var keysCommand = new Command10("keys").description("Add or update API keys for the current environment").option("--show", "Show which keys are set vs missing").action(async (options) => {
|
|
10759
11125
|
printCompactBanner();
|
|
10760
11126
|
const targetDir = process.cwd();
|
|
@@ -10866,8 +11232,8 @@ var keysCommand = new Command10("keys").description("Add or update API keys for
|
|
|
10866
11232
|
for (const [key, value] of envEntries) {
|
|
10867
11233
|
envLines.push(`${key}=${value}`);
|
|
10868
11234
|
}
|
|
10869
|
-
const envPath =
|
|
10870
|
-
await
|
|
11235
|
+
const envPath = path19.join(targetDir, ".env");
|
|
11236
|
+
await fs19.writeFile(envPath, envLines.join("\n") + "\n", "utf-8");
|
|
10871
11237
|
console.log(chalk13.green(` \u2713 ${keysEntered} key(s) saved to .env`));
|
|
10872
11238
|
console.log("");
|
|
10873
11239
|
});
|
|
@@ -10877,90 +11243,131 @@ init_ui();
|
|
|
10877
11243
|
import { Command as Command11 } from "commander";
|
|
10878
11244
|
import chalk14 from "chalk";
|
|
10879
11245
|
import ora2 from "ora";
|
|
10880
|
-
import
|
|
10881
|
-
import
|
|
11246
|
+
import fs36 from "fs/promises";
|
|
11247
|
+
import path37 from "path";
|
|
10882
11248
|
import { parse as yamlParse2 } from "yaml";
|
|
10883
11249
|
import { confirm as confirm4, input as input4, select as select4 } from "@inquirer/prompts";
|
|
10884
11250
|
|
|
10885
11251
|
// src/evolve/init.ts
|
|
10886
11252
|
init_config();
|
|
10887
|
-
|
|
10888
|
-
import
|
|
11253
|
+
init_cache();
|
|
11254
|
+
import fs20 from "fs/promises";
|
|
11255
|
+
import path20 from "path";
|
|
10889
11256
|
import { stringify as yamlStringify } from "yaml";
|
|
10890
11257
|
|
|
10891
11258
|
// src/evolve/templates.ts
|
|
10892
11259
|
init_llm();
|
|
10893
11260
|
var EVAL_TEMPLATES = {
|
|
11261
|
+
// --- Harness-sensitivity templates (probe whether agent follows .claude/ harness) ---
|
|
10894
11262
|
"add-feature": {
|
|
10895
11263
|
id: "add-feature",
|
|
10896
11264
|
name: "Add Feature",
|
|
10897
11265
|
description: "Can the agent add a new capability?",
|
|
10898
|
-
bestFor: ["feature-development", "api-building", "full-stack"]
|
|
11266
|
+
bestFor: ["feature-development", "api-building", "full-stack"],
|
|
11267
|
+
category: "harness-sensitivity"
|
|
10899
11268
|
},
|
|
10900
11269
|
"fix-bug": {
|
|
10901
11270
|
id: "fix-bug",
|
|
10902
11271
|
name: "Fix Bug",
|
|
10903
11272
|
description: "Can the agent diagnose and fix a problem?",
|
|
10904
|
-
bestFor: ["maintenance", "debugging", "qa"]
|
|
11273
|
+
bestFor: ["maintenance", "debugging", "qa"],
|
|
11274
|
+
category: "harness-sensitivity"
|
|
10905
11275
|
},
|
|
10906
11276
|
"refactor": {
|
|
10907
11277
|
id: "refactor",
|
|
10908
11278
|
name: "Refactor",
|
|
10909
11279
|
description: "Can the agent restructure code?",
|
|
10910
|
-
bestFor: ["maintenance", "architecture", "backend"]
|
|
11280
|
+
bestFor: ["maintenance", "architecture", "backend"],
|
|
11281
|
+
category: "harness-sensitivity"
|
|
10911
11282
|
},
|
|
10912
11283
|
"test-writing": {
|
|
10913
11284
|
id: "test-writing",
|
|
10914
11285
|
name: "Test Writing",
|
|
10915
11286
|
description: "Can the agent write tests?",
|
|
10916
|
-
bestFor: ["tdd", "qa", "backend"]
|
|
11287
|
+
bestFor: ["tdd", "qa", "backend"],
|
|
11288
|
+
category: "harness-sensitivity"
|
|
10917
11289
|
},
|
|
10918
11290
|
"config-change": {
|
|
10919
11291
|
id: "config-change",
|
|
10920
11292
|
name: "Config Change",
|
|
10921
11293
|
description: "Can the agent update configuration?",
|
|
10922
|
-
bestFor: ["devops", "infrastructure", "backend"]
|
|
11294
|
+
bestFor: ["devops", "infrastructure", "backend"],
|
|
11295
|
+
category: "harness-sensitivity"
|
|
10923
11296
|
},
|
|
10924
11297
|
"documentation": {
|
|
10925
11298
|
id: "documentation",
|
|
10926
11299
|
name: "Documentation",
|
|
10927
11300
|
description: "Can the agent write and update docs?",
|
|
10928
|
-
bestFor: ["content", "api-building", "full-stack"]
|
|
11301
|
+
bestFor: ["content", "api-building", "full-stack"],
|
|
11302
|
+
category: "harness-sensitivity"
|
|
10929
11303
|
},
|
|
10930
11304
|
"convention-adherence": {
|
|
10931
11305
|
id: "convention-adherence",
|
|
10932
11306
|
name: "Convention Adherence",
|
|
10933
11307
|
description: "Does the agent follow all project conventions defined in CLAUDE.md?",
|
|
10934
|
-
bestFor: ["feature-development", "full-stack", "backend", "maintenance"]
|
|
11308
|
+
bestFor: ["feature-development", "full-stack", "backend", "maintenance"],
|
|
11309
|
+
category: "harness-sensitivity"
|
|
10935
11310
|
},
|
|
10936
11311
|
"workflow-compliance": {
|
|
10937
11312
|
id: "workflow-compliance",
|
|
10938
11313
|
name: "Workflow Compliance",
|
|
10939
11314
|
description: "Does the agent use the project workflow commands and skills?",
|
|
10940
|
-
bestFor: ["feature-development", "full-stack", "tdd", "qa"]
|
|
11315
|
+
bestFor: ["feature-development", "full-stack", "tdd", "qa"],
|
|
11316
|
+
category: "harness-sensitivity"
|
|
10941
11317
|
},
|
|
10942
11318
|
"rule-compliance": {
|
|
10943
11319
|
id: "rule-compliance",
|
|
10944
11320
|
name: "Rule Compliance",
|
|
10945
11321
|
description: "Does the agent follow all project rules without violations?",
|
|
10946
|
-
bestFor: ["feature-development", "backend", "maintenance", "architecture"]
|
|
11322
|
+
bestFor: ["feature-development", "backend", "maintenance", "architecture"],
|
|
11323
|
+
category: "harness-sensitivity"
|
|
10947
11324
|
},
|
|
10948
11325
|
"intent-routing": {
|
|
10949
11326
|
id: "intent-routing",
|
|
10950
11327
|
name: "Intent Routing",
|
|
10951
11328
|
description: "Test that natural language prompts route to the correct workflow command via intent hooks",
|
|
10952
|
-
bestFor: ["feature-development", "full-stack", "api-building"]
|
|
11329
|
+
bestFor: ["feature-development", "full-stack", "api-building"],
|
|
11330
|
+
category: "harness-sensitivity"
|
|
10953
11331
|
},
|
|
10954
11332
|
"persistence-completion": {
|
|
10955
11333
|
id: "persistence-completion",
|
|
10956
11334
|
name: "Persistence Completion",
|
|
10957
11335
|
description: "Can the agent complete a multi-criterion task using the persistence loop?",
|
|
10958
|
-
bestFor: ["feature-development", "full-stack", "api-building", "maintenance"]
|
|
11336
|
+
bestFor: ["feature-development", "full-stack", "api-building", "maintenance"],
|
|
11337
|
+
category: "harness-sensitivity"
|
|
11338
|
+
},
|
|
11339
|
+
// --- Substantive SWE-bench-style templates (test real coding ability) ---
|
|
11340
|
+
"real-bug-fix": {
|
|
11341
|
+
id: "real-bug-fix",
|
|
11342
|
+
name: "Real Bug Fix",
|
|
11343
|
+
description: "Injects a known bug into a source file and asks the agent to diagnose and fix it, mimicking a real GitHub issue",
|
|
11344
|
+
bestFor: ["debugging", "maintenance", "qa", "backend"],
|
|
11345
|
+
category: "substantive"
|
|
11346
|
+
},
|
|
11347
|
+
"real-feature-add": {
|
|
11348
|
+
id: "real-feature-add",
|
|
11349
|
+
name: "Real Feature Add",
|
|
11350
|
+
description: "Describes a concrete feature with clear acceptance criteria and verifies the agent implements it correctly",
|
|
11351
|
+
bestFor: ["feature-development", "full-stack", "api-building", "backend"],
|
|
11352
|
+
category: "substantive"
|
|
11353
|
+
},
|
|
11354
|
+
"codebase-question": {
|
|
11355
|
+
id: "codebase-question",
|
|
11356
|
+
name: "Codebase Question",
|
|
11357
|
+
description: "Asks a factual question about codebase knowledge and checks the answer via LLM-as-judge against a known-correct answer",
|
|
11358
|
+
bestFor: ["research", "architecture", "maintenance", "debugging"],
|
|
11359
|
+
category: "substantive"
|
|
10959
11360
|
}
|
|
10960
11361
|
};
|
|
11362
|
+
var SUBSTANTIVE_FLOOR = [
|
|
11363
|
+
"real-bug-fix",
|
|
11364
|
+
"real-feature-add",
|
|
11365
|
+
"codebase-question"
|
|
11366
|
+
];
|
|
11367
|
+
var MAX_HARNESS_TEMPLATES = 4;
|
|
10961
11368
|
function selectTemplatesForWorkflow(workflowType) {
|
|
10962
|
-
const
|
|
10963
|
-
"feature-development": ["add-feature", "test-writing", "
|
|
11369
|
+
const harnessMapping = {
|
|
11370
|
+
"feature-development": ["add-feature", "test-writing", "intent-routing", "convention-adherence", "workflow-compliance", "persistence-completion"],
|
|
10964
11371
|
"api-building": ["add-feature", "fix-bug", "test-writing", "convention-adherence", "persistence-completion"],
|
|
10965
11372
|
"full-stack": ["add-feature", "fix-bug", "test-writing", "convention-adherence", "persistence-completion"],
|
|
10966
11373
|
"maintenance": ["fix-bug", "refactor", "test-writing", "rule-compliance", "persistence-completion"],
|
|
@@ -10974,7 +11381,8 @@ function selectTemplatesForWorkflow(workflowType) {
|
|
|
10974
11381
|
"content": ["documentation", "add-feature", "convention-adherence"],
|
|
10975
11382
|
"research": ["documentation", "add-feature", "convention-adherence"]
|
|
10976
11383
|
};
|
|
10977
|
-
|
|
11384
|
+
const harness = (harnessMapping[workflowType] ?? ["add-feature", "fix-bug", "test-writing", "convention-adherence"]).slice(0, MAX_HARNESS_TEMPLATES);
|
|
11385
|
+
return [...SUBSTANTIVE_FLOOR, ...harness];
|
|
10978
11386
|
}
|
|
10979
11387
|
var TASK_GENERATION_PROMPT = `You are an eval task generator for Claude Code agent environments. Given a project's CLAUDE.md, project structure, and selected eval templates, generate concrete, project-specific tasks.
|
|
10980
11388
|
|
|
@@ -10988,6 +11396,15 @@ IMPORTANT: For harness-aware templates (convention-adherence, workflow-complianc
|
|
|
10988
11396
|
|
|
10989
11397
|
These harness-aware tasks are critical \u2014 they test whether the .claude/ environment actually improves agent behavior.
|
|
10990
11398
|
|
|
11399
|
+
SUBSTANTIVE SWE-bench-style templates test real coding ability beyond harness adherence:
|
|
11400
|
+
- real-bug-fix: Inject a known bug into a source file (e.g., swap variable names, remove an import, introduce an off-by-one error). Write the task description like a real GitHub issue: "When X happens, Y is broken." The setup command should apply the bug. Scorer: run the test suite or check the specific file was fixed correctly. Use scoring "pass-fail".
|
|
11401
|
+
- real-feature-add: Describe a concrete feature with clear acceptance criteria (e.g., "Add a --verbose flag that prints debug output"). The feature should be small, self-contained, and testable. Scorer: verify the feature exists, tests pass, and no regressions. Use scoring "pass-fail" or "rubric".
|
|
11402
|
+
- codebase-question: Ask a factual question about the codebase that requires reading and understanding source code (e.g., "What function handles authentication?" or "What environment variables does this project need?"). Include the known-correct answer in expected_outcome. Scorer: LLM-as-judge checks answer accuracy against the known-correct answer. Use scoring "llm-judge".
|
|
11403
|
+
|
|
11404
|
+
Each task MUST include a "category" field:
|
|
11405
|
+
- "harness-sensitivity" for templates that test .claude/ harness adherence
|
|
11406
|
+
- "substantive" for SWE-bench-style templates that test real coding ability
|
|
11407
|
+
|
|
10991
11408
|
Return a JSON object with a "tasks" array. Each task has:
|
|
10992
11409
|
- id: kebab-case identifier (e.g., "add-health-endpoint")
|
|
10993
11410
|
- template: which eval template this instantiates
|
|
@@ -10995,8 +11412,11 @@ Return a JSON object with a "tasks" array. Each task has:
|
|
|
10995
11412
|
- setup: shell commands to prepare the workspace (e.g., "npm install")
|
|
10996
11413
|
- expected_outcome: multi-line string describing what success looks like
|
|
10997
11414
|
- scoring: "pass-fail", "llm-judge", or "rubric"
|
|
11415
|
+
- category: "harness-sensitivity" or "substantive"
|
|
10998
11416
|
- timeout: seconds (300 for features/bugs, 600 for refactors, 180 for config/docs/tests)
|
|
10999
11417
|
|
|
11418
|
+
BALANCE REQUIREMENT: Generate an equal number of harness-sensitivity tasks and substantive tasks. If you are given N templates total, aim for ceil(N/2) harness-sensitivity tasks and floor(N/2) substantive tasks. Do not skew toward one category.
|
|
11419
|
+
|
|
11000
11420
|
Return ONLY valid JSON, no markdown fences.`;
|
|
11001
11421
|
function parseJsonResponse(raw) {
|
|
11002
11422
|
let cleaned = raw.trim();
|
|
@@ -11038,9 +11458,58 @@ function validateTask(obj, index) {
|
|
|
11038
11458
|
}
|
|
11039
11459
|
return record;
|
|
11040
11460
|
}
|
|
11041
|
-
function
|
|
11461
|
+
function buildAnalysisContext(analysis) {
|
|
11462
|
+
const lines = ["## Project Analysis", ""];
|
|
11463
|
+
lines.push(`Purpose: ${analysis.purpose}`);
|
|
11464
|
+
lines.push(`Domain: ${analysis.domain}`);
|
|
11465
|
+
lines.push(`Architecture: ${analysis.architecture_style}`);
|
|
11466
|
+
lines.push(`Deployment: ${analysis.deployment_model}`);
|
|
11467
|
+
lines.push("");
|
|
11468
|
+
if (analysis.key_modules.length > 0) {
|
|
11469
|
+
lines.push("### Key Modules");
|
|
11470
|
+
lines.push("");
|
|
11471
|
+
for (const mod of analysis.key_modules) {
|
|
11472
|
+
lines.push(`- **${mod.name}** (${mod.path}): ${mod.description}`);
|
|
11473
|
+
if (mod.responsibilities.length > 0) {
|
|
11474
|
+
lines.push(` Responsibilities: ${mod.responsibilities.join(", ")}`);
|
|
11475
|
+
}
|
|
11476
|
+
}
|
|
11477
|
+
lines.push("");
|
|
11478
|
+
}
|
|
11479
|
+
if (analysis.workflows.length > 0) {
|
|
11480
|
+
lines.push("### Workflows");
|
|
11481
|
+
lines.push("");
|
|
11482
|
+
for (const wf of analysis.workflows) {
|
|
11483
|
+
lines.push(`- **${wf.name}**: ${wf.description} (trigger: ${wf.trigger})`);
|
|
11484
|
+
lines.push(` Steps: ${wf.steps.join(" -> ")}`);
|
|
11485
|
+
}
|
|
11486
|
+
lines.push("");
|
|
11487
|
+
}
|
|
11488
|
+
if (analysis.config_keys.length > 0) {
|
|
11489
|
+
lines.push("### Config Keys");
|
|
11490
|
+
lines.push("");
|
|
11491
|
+
for (const key of analysis.config_keys) {
|
|
11492
|
+
lines.push(`- ${key.name}: ${key.purpose}`);
|
|
11493
|
+
}
|
|
11494
|
+
lines.push("");
|
|
11495
|
+
}
|
|
11496
|
+
if (analysis.dataflow.length > 0) {
|
|
11497
|
+
lines.push("### Data Flow");
|
|
11498
|
+
lines.push("");
|
|
11499
|
+
for (const edge of analysis.dataflow) {
|
|
11500
|
+
lines.push(`- ${edge.from} -> ${edge.to}: ${edge.data}`);
|
|
11501
|
+
}
|
|
11502
|
+
lines.push("");
|
|
11503
|
+
}
|
|
11504
|
+
lines.push("IMPORTANT: Use this analysis to generate domain-specific tasks:");
|
|
11505
|
+
lines.push("- real-bug-fix tasks should reference actual module names and paths listed above");
|
|
11506
|
+
lines.push("- codebase-question tasks should ask about actual workflows, modules, and config keys");
|
|
11507
|
+
lines.push("- real-feature-add tasks should extend actual functionality in the modules listed above");
|
|
11508
|
+
return lines.join("\n");
|
|
11509
|
+
}
|
|
11510
|
+
function buildTaskGenerationMessage(claudeMd, projectProfile, templates, analysis) {
|
|
11042
11511
|
const profileLines = [
|
|
11043
|
-
`
|
|
11512
|
+
`Languages: ${projectProfile.languages.length > 0 ? projectProfile.languages.join(", ") : "unknown"}`,
|
|
11044
11513
|
`Framework: ${projectProfile.framework ?? "none"}`,
|
|
11045
11514
|
`Scripts: ${Object.entries(projectProfile.scripts).map(([k, v]) => `${k}=${v}`).join(", ") || "none"}`,
|
|
11046
11515
|
`Key files: ${projectProfile.keyFiles.join(", ") || "none"}`
|
|
@@ -11049,7 +11518,7 @@ function buildTaskGenerationMessage(claudeMd, projectProfile, templates) {
|
|
|
11049
11518
|
const meta = EVAL_TEMPLATES[t];
|
|
11050
11519
|
return `- ${t}: ${meta.description}`;
|
|
11051
11520
|
}).join("\n");
|
|
11052
|
-
|
|
11521
|
+
const sections = [
|
|
11053
11522
|
"## CLAUDE.md",
|
|
11054
11523
|
"",
|
|
11055
11524
|
claudeMd,
|
|
@@ -11057,16 +11526,23 @@ function buildTaskGenerationMessage(claudeMd, projectProfile, templates) {
|
|
|
11057
11526
|
"## Project Profile",
|
|
11058
11527
|
"",
|
|
11059
11528
|
...profileLines,
|
|
11060
|
-
""
|
|
11529
|
+
""
|
|
11530
|
+
];
|
|
11531
|
+
if (analysis) {
|
|
11532
|
+
sections.push(buildAnalysisContext(analysis));
|
|
11533
|
+
sections.push("");
|
|
11534
|
+
}
|
|
11535
|
+
sections.push(
|
|
11061
11536
|
"## Selected Eval Templates",
|
|
11062
11537
|
"",
|
|
11063
11538
|
templateDescriptions,
|
|
11064
11539
|
"",
|
|
11065
11540
|
"Generate concrete, project-specific tasks for each template above."
|
|
11066
|
-
|
|
11541
|
+
);
|
|
11542
|
+
return sections.join("\n");
|
|
11067
11543
|
}
|
|
11068
|
-
async function generateTasksFromTemplates(claudeMd, projectProfile, templates, config) {
|
|
11069
|
-
const userMessage = buildTaskGenerationMessage(claudeMd, projectProfile, templates);
|
|
11544
|
+
async function generateTasksFromTemplates(claudeMd, projectProfile, templates, config, analysis) {
|
|
11545
|
+
const userMessage = buildTaskGenerationMessage(claudeMd, projectProfile, templates, analysis);
|
|
11070
11546
|
const rawResponse = await callLLM(config, userMessage, {
|
|
11071
11547
|
systemPrompt: TASK_GENERATION_PROMPT,
|
|
11072
11548
|
maxTokens: 4096
|
|
@@ -11088,10 +11564,10 @@ async function generateTasksFromTemplates(claudeMd, projectProfile, templates, c
|
|
|
11088
11564
|
|
|
11089
11565
|
// src/evolve/init.ts
|
|
11090
11566
|
async function createEvolveWorkspace(projectRoot, config) {
|
|
11091
|
-
const workspace =
|
|
11092
|
-
await
|
|
11093
|
-
await
|
|
11094
|
-
await
|
|
11567
|
+
const workspace = path20.join(projectRoot, ".kairn-evolve");
|
|
11568
|
+
await fs20.mkdir(path20.join(workspace, "baseline"), { recursive: true });
|
|
11569
|
+
await fs20.mkdir(path20.join(workspace, "traces"), { recursive: true });
|
|
11570
|
+
await fs20.mkdir(path20.join(workspace, "iterations"), { recursive: true });
|
|
11095
11571
|
const configObj = {
|
|
11096
11572
|
model: config.model,
|
|
11097
11573
|
proposer_model: config.proposerModel,
|
|
@@ -11099,8 +11575,8 @@ async function createEvolveWorkspace(projectRoot, config) {
|
|
|
11099
11575
|
max_iterations: config.maxIterations,
|
|
11100
11576
|
parallel_tasks: config.parallelTasks
|
|
11101
11577
|
};
|
|
11102
|
-
await
|
|
11103
|
-
|
|
11578
|
+
await fs20.writeFile(
|
|
11579
|
+
path20.join(workspace, "config.yaml"),
|
|
11104
11580
|
yamlStringify(configObj),
|
|
11105
11581
|
"utf-8"
|
|
11106
11582
|
);
|
|
@@ -11116,12 +11592,13 @@ async function writeTasksFile(workspacePath, tasks) {
|
|
|
11116
11592
|
expected_outcome: t.expected_outcome,
|
|
11117
11593
|
scoring: t.scoring,
|
|
11118
11594
|
...t.rubric ? { rubric: t.rubric } : {},
|
|
11119
|
-
timeout: t.timeout
|
|
11595
|
+
timeout: t.timeout,
|
|
11596
|
+
...t.category ? { category: t.category } : {}
|
|
11120
11597
|
}))
|
|
11121
11598
|
};
|
|
11122
11599
|
const header = "# .kairn-evolve/tasks.yaml\n# Auto-generated by kairn evolve init \u2014 edit freely\n";
|
|
11123
|
-
await
|
|
11124
|
-
|
|
11600
|
+
await fs20.writeFile(
|
|
11601
|
+
path20.join(workspacePath, "tasks.yaml"),
|
|
11125
11602
|
header + yamlStringify(doc),
|
|
11126
11603
|
"utf-8"
|
|
11127
11604
|
);
|
|
@@ -11129,17 +11606,19 @@ async function writeTasksFile(workspacePath, tasks) {
|
|
|
11129
11606
|
async function buildProjectProfile(projectRoot) {
|
|
11130
11607
|
const profile = {
|
|
11131
11608
|
language: null,
|
|
11609
|
+
languages: [],
|
|
11132
11610
|
framework: null,
|
|
11133
11611
|
scripts: {},
|
|
11134
11612
|
keyFiles: []
|
|
11135
11613
|
};
|
|
11136
11614
|
try {
|
|
11137
|
-
const pkgStr = await
|
|
11138
|
-
|
|
11615
|
+
const pkgStr = await fs20.readFile(
|
|
11616
|
+
path20.join(projectRoot, "package.json"),
|
|
11139
11617
|
"utf-8"
|
|
11140
11618
|
);
|
|
11141
11619
|
const pkg2 = JSON.parse(pkgStr);
|
|
11142
11620
|
profile.language = "typescript";
|
|
11621
|
+
profile.languages = ["typescript"];
|
|
11143
11622
|
if (pkg2.scripts && typeof pkg2.scripts === "object") {
|
|
11144
11623
|
profile.scripts = pkg2.scripts;
|
|
11145
11624
|
}
|
|
@@ -11162,18 +11641,20 @@ async function buildProjectProfile(projectRoot) {
|
|
|
11162
11641
|
}
|
|
11163
11642
|
if (!profile.language) {
|
|
11164
11643
|
try {
|
|
11165
|
-
await
|
|
11644
|
+
await fs20.access(path20.join(projectRoot, "pyproject.toml"));
|
|
11166
11645
|
profile.language = "python";
|
|
11646
|
+
profile.languages = ["python"];
|
|
11167
11647
|
} catch {
|
|
11168
11648
|
try {
|
|
11169
|
-
await
|
|
11649
|
+
await fs20.access(path20.join(projectRoot, "requirements.txt"));
|
|
11170
11650
|
profile.language = "python";
|
|
11651
|
+
profile.languages = ["python"];
|
|
11171
11652
|
} catch {
|
|
11172
11653
|
}
|
|
11173
11654
|
}
|
|
11174
11655
|
}
|
|
11175
11656
|
try {
|
|
11176
|
-
const entries = await
|
|
11657
|
+
const entries = await fs20.readdir(projectRoot);
|
|
11177
11658
|
const keyPatterns = [
|
|
11178
11659
|
"README.md",
|
|
11179
11660
|
"package.json",
|
|
@@ -11189,6 +11670,16 @@ async function buildProjectProfile(projectRoot) {
|
|
|
11189
11670
|
}
|
|
11190
11671
|
return profile;
|
|
11191
11672
|
}
|
|
11673
|
+
async function loadProjectAnalysis(projectRoot) {
|
|
11674
|
+
try {
|
|
11675
|
+
const cache = await readCache(projectRoot);
|
|
11676
|
+
if (cache?.analysis) {
|
|
11677
|
+
return cache.analysis;
|
|
11678
|
+
}
|
|
11679
|
+
} catch {
|
|
11680
|
+
}
|
|
11681
|
+
return void 0;
|
|
11682
|
+
}
|
|
11192
11683
|
async function autoGenerateTasks(projectRoot, workflowType) {
|
|
11193
11684
|
const config = await loadConfig();
|
|
11194
11685
|
if (!config) {
|
|
@@ -11196,15 +11687,16 @@ async function autoGenerateTasks(projectRoot, workflowType) {
|
|
|
11196
11687
|
}
|
|
11197
11688
|
let claudeMd = "";
|
|
11198
11689
|
try {
|
|
11199
|
-
claudeMd = await
|
|
11200
|
-
|
|
11690
|
+
claudeMd = await fs20.readFile(
|
|
11691
|
+
path20.join(projectRoot, ".claude", "CLAUDE.md"),
|
|
11201
11692
|
"utf-8"
|
|
11202
11693
|
);
|
|
11203
11694
|
} catch {
|
|
11204
11695
|
}
|
|
11205
11696
|
const profile = await buildProjectProfile(projectRoot);
|
|
11697
|
+
const analysis = await loadProjectAnalysis(projectRoot);
|
|
11206
11698
|
const templates = selectTemplatesForWorkflow(workflowType);
|
|
11207
|
-
return generateTasksFromTemplates(claudeMd, profile, templates, config);
|
|
11699
|
+
return generateTasksFromTemplates(claudeMd, profile, templates, config, analysis);
|
|
11208
11700
|
}
|
|
11209
11701
|
|
|
11210
11702
|
// src/commands/evolve.ts
|
|
@@ -11216,8 +11708,8 @@ init_loop();
|
|
|
11216
11708
|
|
|
11217
11709
|
// src/evolve/report.ts
|
|
11218
11710
|
init_trace();
|
|
11219
|
-
import
|
|
11220
|
-
import
|
|
11711
|
+
import fs32 from "fs/promises";
|
|
11712
|
+
import path32 from "path";
|
|
11221
11713
|
|
|
11222
11714
|
// src/evolve/diagnosis.ts
|
|
11223
11715
|
function numericScore(s) {
|
|
@@ -11267,10 +11759,10 @@ function numericScore2(s) {
|
|
|
11267
11759
|
return s.score ?? (s.pass ? 100 : 0);
|
|
11268
11760
|
}
|
|
11269
11761
|
async function loadAllIterations(workspacePath) {
|
|
11270
|
-
const iterDir =
|
|
11762
|
+
const iterDir = path32.join(workspacePath, "iterations");
|
|
11271
11763
|
let entries;
|
|
11272
11764
|
try {
|
|
11273
|
-
entries = await
|
|
11765
|
+
entries = await fs32.readdir(iterDir);
|
|
11274
11766
|
} catch {
|
|
11275
11767
|
return [];
|
|
11276
11768
|
}
|
|
@@ -11284,13 +11776,41 @@ async function loadAllIterations(workspacePath) {
|
|
|
11284
11776
|
}
|
|
11285
11777
|
async function loadTasks(workspacePath) {
|
|
11286
11778
|
try {
|
|
11287
|
-
const content = await
|
|
11779
|
+
const content = await fs32.readFile(path32.join(workspacePath, "tasks.yaml"), "utf-8");
|
|
11288
11780
|
const parsed = yamlParse(content);
|
|
11289
|
-
|
|
11781
|
+
const tasks = parsed?.tasks ?? [];
|
|
11782
|
+
return tasks.map((t) => ({
|
|
11783
|
+
...t,
|
|
11784
|
+
category: t.category ?? "harness-sensitivity"
|
|
11785
|
+
}));
|
|
11290
11786
|
} catch {
|
|
11291
11787
|
return [];
|
|
11292
11788
|
}
|
|
11293
11789
|
}
|
|
11790
|
+
function computeCategoryBreakdown(tasks, taskResults) {
|
|
11791
|
+
const harnessTasks = [];
|
|
11792
|
+
const substantiveTasks = [];
|
|
11793
|
+
for (const task of tasks) {
|
|
11794
|
+
const result = taskResults[task.id];
|
|
11795
|
+
if (!result) continue;
|
|
11796
|
+
const score = numericScore2(result);
|
|
11797
|
+
const category = task.category ?? "harness-sensitivity";
|
|
11798
|
+
if (category === "harness-sensitivity") {
|
|
11799
|
+
harnessTasks.push(score);
|
|
11800
|
+
} else {
|
|
11801
|
+
substantiveTasks.push(score);
|
|
11802
|
+
}
|
|
11803
|
+
}
|
|
11804
|
+
if (harnessTasks.length === 0 || substantiveTasks.length === 0) {
|
|
11805
|
+
return void 0;
|
|
11806
|
+
}
|
|
11807
|
+
const harnessAvg = harnessTasks.reduce((a, b) => a + b, 0) / harnessTasks.length;
|
|
11808
|
+
const substantiveAvg = substantiveTasks.reduce((a, b) => a + b, 0) / substantiveTasks.length;
|
|
11809
|
+
return {
|
|
11810
|
+
harnessAdherence: { score: harnessAvg, count: harnessTasks.length },
|
|
11811
|
+
substantiveTasks: { score: substantiveAvg, count: substantiveTasks.length }
|
|
11812
|
+
};
|
|
11813
|
+
}
|
|
11294
11814
|
function buildLeaderboard(iterations, tasks) {
|
|
11295
11815
|
const taskIds = tasks.map((t) => t.id);
|
|
11296
11816
|
return taskIds.map((taskId) => {
|
|
@@ -11350,6 +11870,11 @@ async function generateMarkdownReport(workspacePath) {
|
|
|
11350
11870
|
lines.push(`| Best score | ${bestIter.score.toFixed(1)}% |`);
|
|
11351
11871
|
lines.push(`| Best iteration | ${bestIter.iteration} |`);
|
|
11352
11872
|
lines.push(`| Improvement | ${improvement >= 0 ? "+" : ""}${improvement.toFixed(1)} points |`);
|
|
11873
|
+
const categoryBreakdown = computeCategoryBreakdown(tasks, bestIter.taskResults);
|
|
11874
|
+
if (categoryBreakdown) {
|
|
11875
|
+
lines.push(`| Harness adherence | ${categoryBreakdown.harnessAdherence.score.toFixed(1)}% (${categoryBreakdown.harnessAdherence.count} tasks) |`);
|
|
11876
|
+
lines.push(`| Substantive tasks | ${categoryBreakdown.substantiveTasks.score.toFixed(1)}% (${categoryBreakdown.substantiveTasks.count} tasks) |`);
|
|
11877
|
+
}
|
|
11353
11878
|
lines.push("");
|
|
11354
11879
|
lines.push("## Iterations");
|
|
11355
11880
|
lines.push("");
|
|
@@ -11433,10 +11958,12 @@ async function generateJsonReport(workspacePath) {
|
|
|
11433
11958
|
const iterations = await loadAllIterations(workspacePath);
|
|
11434
11959
|
const tasks = await loadTasks(workspacePath);
|
|
11435
11960
|
const baselineScore = iterations.length > 0 ? iterations[0].score : 0;
|
|
11436
|
-
const
|
|
11961
|
+
const bestIterLog = iterations.length > 0 ? iterations.reduce((best, curr) => curr.score > best.score ? curr : best, iterations[0]) : void 0;
|
|
11962
|
+
const bestIter = bestIterLog ?? { score: 0, iteration: 0 };
|
|
11437
11963
|
const improvement = bestIter.score - baselineScore;
|
|
11438
11964
|
const counterfactuals = diagnoseCounterfactuals(iterations, tasks);
|
|
11439
11965
|
const leaderboard = buildLeaderboard(iterations, tasks);
|
|
11966
|
+
const categoryBreakdown = bestIterLog ? computeCategoryBreakdown(tasks, bestIterLog.taskResults) : void 0;
|
|
11440
11967
|
return {
|
|
11441
11968
|
overview: {
|
|
11442
11969
|
title: "Evolution Report",
|
|
@@ -11444,7 +11971,8 @@ async function generateJsonReport(workspacePath) {
|
|
|
11444
11971
|
baselineScore,
|
|
11445
11972
|
bestScore: bestIter.score,
|
|
11446
11973
|
bestIteration: bestIter.iteration,
|
|
11447
|
-
improvement
|
|
11974
|
+
improvement,
|
|
11975
|
+
...categoryBreakdown ? { categoryBreakdown } : {}
|
|
11448
11976
|
},
|
|
11449
11977
|
iterations: iterations.map((iter) => {
|
|
11450
11978
|
const stddevs = Object.values(iter.taskResults).map((s) => s.variance?.stddev).filter((v) => v !== void 0);
|
|
@@ -11470,13 +11998,13 @@ init_mutator();
|
|
|
11470
11998
|
init_baseline();
|
|
11471
11999
|
init_mutator();
|
|
11472
12000
|
init_trace();
|
|
11473
|
-
import
|
|
11474
|
-
import
|
|
12001
|
+
import fs33 from "fs/promises";
|
|
12002
|
+
import path33 from "path";
|
|
11475
12003
|
async function listIterations(workspacePath) {
|
|
11476
|
-
const iterationsDir =
|
|
12004
|
+
const iterationsDir = path33.join(workspacePath, "iterations");
|
|
11477
12005
|
let entries;
|
|
11478
12006
|
try {
|
|
11479
|
-
entries = await
|
|
12007
|
+
entries = await fs33.readdir(iterationsDir);
|
|
11480
12008
|
} catch {
|
|
11481
12009
|
return [];
|
|
11482
12010
|
}
|
|
@@ -11485,7 +12013,7 @@ async function listIterations(workspacePath) {
|
|
|
11485
12013
|
const n = parseInt(entry, 10);
|
|
11486
12014
|
if (!isNaN(n)) {
|
|
11487
12015
|
try {
|
|
11488
|
-
await
|
|
12016
|
+
await fs33.access(path33.join(iterationsDir, entry, "harness"));
|
|
11489
12017
|
nums.push(n);
|
|
11490
12018
|
} catch {
|
|
11491
12019
|
}
|
|
@@ -11511,16 +12039,16 @@ async function listFilesRecursive(dir) {
|
|
|
11511
12039
|
async function walk(current) {
|
|
11512
12040
|
let entries;
|
|
11513
12041
|
try {
|
|
11514
|
-
entries = await
|
|
12042
|
+
entries = await fs33.readdir(current, { withFileTypes: true });
|
|
11515
12043
|
} catch {
|
|
11516
12044
|
return;
|
|
11517
12045
|
}
|
|
11518
12046
|
for (const entry of entries) {
|
|
11519
|
-
const fullPath =
|
|
12047
|
+
const fullPath = path33.join(current, entry.name);
|
|
11520
12048
|
if (entry.isDirectory()) {
|
|
11521
12049
|
await walk(fullPath);
|
|
11522
12050
|
} else {
|
|
11523
|
-
results.push(
|
|
12051
|
+
results.push(path33.relative(dir, fullPath));
|
|
11524
12052
|
}
|
|
11525
12053
|
}
|
|
11526
12054
|
}
|
|
@@ -11528,10 +12056,10 @@ async function listFilesRecursive(dir) {
|
|
|
11528
12056
|
return results;
|
|
11529
12057
|
}
|
|
11530
12058
|
async function findBestPBTHarness(workspacePath) {
|
|
11531
|
-
const branchesDir =
|
|
12059
|
+
const branchesDir = path33.join(workspacePath, "branches");
|
|
11532
12060
|
let branchEntries;
|
|
11533
12061
|
try {
|
|
11534
|
-
branchEntries = await
|
|
12062
|
+
branchEntries = await fs33.readdir(branchesDir);
|
|
11535
12063
|
} catch {
|
|
11536
12064
|
return null;
|
|
11537
12065
|
}
|
|
@@ -11539,7 +12067,7 @@ async function findBestPBTHarness(workspacePath) {
|
|
|
11539
12067
|
let bestPath = "";
|
|
11540
12068
|
let bestLabel = "";
|
|
11541
12069
|
for (const branchId of branchEntries) {
|
|
11542
|
-
const branchPath =
|
|
12070
|
+
const branchPath = path33.join(branchesDir, branchId);
|
|
11543
12071
|
const branchIterations = await listIterations(branchPath);
|
|
11544
12072
|
if (branchIterations.length === 0) continue;
|
|
11545
12073
|
const bestIter = await findBestIteration(branchPath, branchIterations);
|
|
@@ -11547,13 +12075,13 @@ async function findBestPBTHarness(workspacePath) {
|
|
|
11547
12075
|
const score = log?.score ?? 0;
|
|
11548
12076
|
if (score > bestScore) {
|
|
11549
12077
|
bestScore = score;
|
|
11550
|
-
bestPath =
|
|
12078
|
+
bestPath = path33.join(branchPath, "iterations", bestIter.toString(), "harness");
|
|
11551
12079
|
bestLabel = `branch ${branchId}, iteration ${bestIter} (${score.toFixed(1)}%)`;
|
|
11552
12080
|
}
|
|
11553
12081
|
}
|
|
11554
|
-
const synthesisHarness =
|
|
12082
|
+
const synthesisHarness = path33.join(workspacePath, "synthesis", "harness");
|
|
11555
12083
|
try {
|
|
11556
|
-
await
|
|
12084
|
+
await fs33.access(synthesisHarness);
|
|
11557
12085
|
const synthesisLog = await loadIterationLog(workspacePath, 999);
|
|
11558
12086
|
const synthScore = synthesisLog?.score ?? 0;
|
|
11559
12087
|
if (synthScore > bestScore) {
|
|
@@ -11572,26 +12100,26 @@ async function applyEvolution(workspacePath, projectRoot, targetIteration, pbt)
|
|
|
11572
12100
|
if (!pbtResult) {
|
|
11573
12101
|
throw new Error("No PBT results found. Run `kairn evolve pbt` first.");
|
|
11574
12102
|
}
|
|
11575
|
-
const claudeDir2 =
|
|
12103
|
+
const claudeDir2 = path33.join(projectRoot, ".claude");
|
|
11576
12104
|
const diffPreview2 = await generateDiff2(claudeDir2, pbtResult.harnessPath);
|
|
11577
12105
|
const currentFiles2 = await listFilesRecursive(claudeDir2);
|
|
11578
12106
|
const targetFiles2 = await listFilesRecursive(pbtResult.harnessPath);
|
|
11579
12107
|
const allPaths2 = /* @__PURE__ */ new Set([...currentFiles2, ...targetFiles2]);
|
|
11580
12108
|
const filesChanged2 = [];
|
|
11581
12109
|
for (const filePath of allPaths2) {
|
|
11582
|
-
const currentContent = await
|
|
11583
|
-
const targetContent = await
|
|
12110
|
+
const currentContent = await fs33.readFile(path33.join(claudeDir2, filePath), "utf-8").catch(() => null);
|
|
12111
|
+
const targetContent = await fs33.readFile(path33.join(pbtResult.harnessPath, filePath), "utf-8").catch(() => null);
|
|
11584
12112
|
if (currentContent !== targetContent) {
|
|
11585
12113
|
filesChanged2.push(filePath);
|
|
11586
12114
|
}
|
|
11587
12115
|
}
|
|
11588
|
-
await
|
|
12116
|
+
await fs33.rm(claudeDir2, { recursive: true, force: true });
|
|
11589
12117
|
await copyDir(pbtResult.harnessPath, claudeDir2);
|
|
11590
|
-
const harnessMcpJson2 =
|
|
11591
|
-
const projectMcpJson2 =
|
|
12118
|
+
const harnessMcpJson2 = path33.join(pbtResult.harnessPath, ".mcp.json");
|
|
12119
|
+
const projectMcpJson2 = path33.join(projectRoot, ".mcp.json");
|
|
11592
12120
|
try {
|
|
11593
|
-
await
|
|
11594
|
-
await
|
|
12121
|
+
await fs33.access(harnessMcpJson2);
|
|
12122
|
+
await fs33.copyFile(harnessMcpJson2, projectMcpJson2);
|
|
11595
12123
|
if (!filesChanged2.includes(".mcp.json")) filesChanged2.push(".mcp.json");
|
|
11596
12124
|
} catch {
|
|
11597
12125
|
}
|
|
@@ -11617,37 +12145,37 @@ async function applyEvolution(workspacePath, projectRoot, targetIteration, pbt)
|
|
|
11617
12145
|
} else {
|
|
11618
12146
|
iter = await findBestIteration(workspacePath, iterations);
|
|
11619
12147
|
}
|
|
11620
|
-
const harnessPath =
|
|
12148
|
+
const harnessPath = path33.join(
|
|
11621
12149
|
workspacePath,
|
|
11622
12150
|
"iterations",
|
|
11623
12151
|
iter.toString(),
|
|
11624
12152
|
"harness"
|
|
11625
12153
|
);
|
|
11626
|
-
const claudeDir =
|
|
12154
|
+
const claudeDir = path33.join(projectRoot, ".claude");
|
|
11627
12155
|
const diffPreview = await generateDiff2(claudeDir, harnessPath);
|
|
11628
12156
|
const currentFiles = await listFilesRecursive(claudeDir);
|
|
11629
12157
|
const targetFiles = await listFilesRecursive(harnessPath);
|
|
11630
12158
|
const allPaths = /* @__PURE__ */ new Set([...currentFiles, ...targetFiles]);
|
|
11631
12159
|
const filesChanged = [];
|
|
11632
12160
|
for (const filePath of allPaths) {
|
|
11633
|
-
const currentContent = await
|
|
11634
|
-
const targetContent = await
|
|
12161
|
+
const currentContent = await fs33.readFile(path33.join(claudeDir, filePath), "utf-8").catch(() => null);
|
|
12162
|
+
const targetContent = await fs33.readFile(path33.join(harnessPath, filePath), "utf-8").catch(() => null);
|
|
11635
12163
|
if (currentContent !== targetContent) {
|
|
11636
12164
|
filesChanged.push(filePath);
|
|
11637
12165
|
}
|
|
11638
12166
|
}
|
|
11639
|
-
await
|
|
12167
|
+
await fs33.rm(claudeDir, { recursive: true, force: true });
|
|
11640
12168
|
await copyDir(harnessPath, claudeDir);
|
|
11641
|
-
const harnessMcpJson =
|
|
11642
|
-
const projectMcpJson =
|
|
12169
|
+
const harnessMcpJson = path33.join(harnessPath, ".mcp.json");
|
|
12170
|
+
const projectMcpJson = path33.join(projectRoot, ".mcp.json");
|
|
11643
12171
|
try {
|
|
11644
|
-
await
|
|
11645
|
-
const currentMcp = await
|
|
11646
|
-
const targetMcp = await
|
|
12172
|
+
await fs33.access(harnessMcpJson);
|
|
12173
|
+
const currentMcp = await fs33.readFile(projectMcpJson, "utf-8").catch(() => null);
|
|
12174
|
+
const targetMcp = await fs33.readFile(harnessMcpJson, "utf-8").catch(() => null);
|
|
11647
12175
|
if (currentMcp !== targetMcp) {
|
|
11648
12176
|
filesChanged.push(".mcp.json");
|
|
11649
12177
|
}
|
|
11650
|
-
await
|
|
12178
|
+
await fs33.copyFile(harnessMcpJson, projectMcpJson);
|
|
11651
12179
|
} catch {
|
|
11652
12180
|
}
|
|
11653
12181
|
return {
|
|
@@ -11680,7 +12208,7 @@ var DEFAULT_CONFIG = {
|
|
|
11680
12208
|
};
|
|
11681
12209
|
async function loadEvolveConfigFromWorkspace(workspacePath) {
|
|
11682
12210
|
try {
|
|
11683
|
-
const configStr = await
|
|
12211
|
+
const configStr = await fs36.readFile(path37.join(workspacePath, "config.yaml"), "utf-8");
|
|
11684
12212
|
const parsed = yamlParse2(configStr);
|
|
11685
12213
|
return {
|
|
11686
12214
|
model: parsed.model ?? DEFAULT_CONFIG.model,
|
|
@@ -11710,9 +12238,9 @@ evolveCommand.command("init").description("Initialize an evolution workspace wit
|
|
|
11710
12238
|
try {
|
|
11711
12239
|
const projectRoot = process.cwd();
|
|
11712
12240
|
console.log(ui.section("Evolve Init"));
|
|
11713
|
-
const claudeDir =
|
|
12241
|
+
const claudeDir = path37.join(projectRoot, ".claude");
|
|
11714
12242
|
try {
|
|
11715
|
-
await
|
|
12243
|
+
await fs36.access(claudeDir);
|
|
11716
12244
|
} catch {
|
|
11717
12245
|
console.log(ui.error("No .claude/ directory found. Run kairn describe first."));
|
|
11718
12246
|
process.exit(1);
|
|
@@ -11762,7 +12290,7 @@ evolveCommand.command("init").description("Initialize an evolution workspace wit
|
|
|
11762
12290
|
if (config) {
|
|
11763
12291
|
let claudeMd = "";
|
|
11764
12292
|
try {
|
|
11765
|
-
claudeMd = await
|
|
12293
|
+
claudeMd = await fs36.readFile(path37.join(claudeDir, "CLAUDE.md"), "utf-8");
|
|
11766
12294
|
} catch {
|
|
11767
12295
|
}
|
|
11768
12296
|
const profile = await buildProjectProfile(projectRoot);
|
|
@@ -11793,16 +12321,16 @@ evolveCommand.command("init").description("Initialize an evolution workspace wit
|
|
|
11793
12321
|
evolveCommand.command("baseline").description("Snapshot current .claude/ directory as baseline").action(async () => {
|
|
11794
12322
|
try {
|
|
11795
12323
|
const projectRoot = process.cwd();
|
|
11796
|
-
const workspace =
|
|
12324
|
+
const workspace = path37.join(projectRoot, ".kairn-evolve");
|
|
11797
12325
|
console.log(ui.section("Evolve Baseline"));
|
|
11798
12326
|
try {
|
|
11799
|
-
await
|
|
12327
|
+
await fs36.access(workspace);
|
|
11800
12328
|
} catch {
|
|
11801
12329
|
console.log(ui.error("No .kairn-evolve/ directory found. Run kairn evolve init first."));
|
|
11802
12330
|
process.exit(1);
|
|
11803
12331
|
}
|
|
11804
12332
|
await snapshotBaseline(projectRoot, workspace);
|
|
11805
|
-
const baselineDir =
|
|
12333
|
+
const baselineDir = path37.join(workspace, "baseline");
|
|
11806
12334
|
const fileCount = await countFiles(baselineDir);
|
|
11807
12335
|
console.log(ui.success(`Baseline snapshot created (${fileCount} files)`));
|
|
11808
12336
|
} catch (err) {
|
|
@@ -11814,18 +12342,18 @@ evolveCommand.command("baseline").description("Snapshot current .claude/ directo
|
|
|
11814
12342
|
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("--architect-every <n>", "Run architect proposer every N iterations (default: 3)").option("--schedule <type>", "Architect schedule: explore-exploit, constant, or adaptive (default: explore-exploit)").option("--architect-model <model>", "Model for architect proposer (defaults to proposer model)").option("-i, --interactive", "Configure evolution settings interactively").action(async (options) => {
|
|
11815
12343
|
try {
|
|
11816
12344
|
const projectRoot = process.cwd();
|
|
11817
|
-
const workspace =
|
|
12345
|
+
const workspace = path37.join(projectRoot, ".kairn-evolve");
|
|
11818
12346
|
console.log(ui.section("Evolve Run"));
|
|
11819
12347
|
try {
|
|
11820
|
-
await
|
|
12348
|
+
await fs36.access(workspace);
|
|
11821
12349
|
} catch {
|
|
11822
12350
|
console.log(ui.error("No .kairn-evolve/ directory found. Run kairn evolve init first."));
|
|
11823
12351
|
process.exit(1);
|
|
11824
12352
|
}
|
|
11825
|
-
const tasksPath =
|
|
12353
|
+
const tasksPath = path37.join(workspace, "tasks.yaml");
|
|
11826
12354
|
let tasksContent;
|
|
11827
12355
|
try {
|
|
11828
|
-
tasksContent = await
|
|
12356
|
+
tasksContent = await fs36.readFile(tasksPath, "utf-8");
|
|
11829
12357
|
} catch {
|
|
11830
12358
|
console.log(ui.error("No tasks.yaml found. Run kairn evolve init first."));
|
|
11831
12359
|
process.exit(1);
|
|
@@ -11844,15 +12372,15 @@ evolveCommand.command("run").description("Run tasks against the current harness"
|
|
|
11844
12372
|
console.log(ui.info(`Running ${tasksToRun.length} task(s)...`));
|
|
11845
12373
|
console.log("");
|
|
11846
12374
|
const config = await loadConfig();
|
|
11847
|
-
const harnessPath =
|
|
12375
|
+
const harnessPath = path37.join(projectRoot, ".claude");
|
|
11848
12376
|
const results = [];
|
|
11849
12377
|
for (const task of tasksToRun) {
|
|
11850
|
-
const traceDir =
|
|
12378
|
+
const traceDir = path37.join(workspace, "traces", "0", task.id);
|
|
11851
12379
|
const spinner = ora2(`Running: ${task.id}`).start();
|
|
11852
12380
|
const result = await runTask(task, harnessPath, traceDir, 0);
|
|
11853
12381
|
if (config) {
|
|
11854
|
-
const stdout = await
|
|
11855
|
-
const stderr = await
|
|
12382
|
+
const stdout = await fs36.readFile(path37.join(traceDir, "stdout.log"), "utf-8").catch(() => "");
|
|
12383
|
+
const stderr = await fs36.readFile(path37.join(traceDir, "stderr.log"), "utf-8").catch(() => "");
|
|
11856
12384
|
const score = await scoreTask(task, traceDir, stdout, stderr, config);
|
|
11857
12385
|
result.score = score;
|
|
11858
12386
|
await writeScore(traceDir, score);
|
|
@@ -12019,7 +12547,7 @@ evolveCommand.command("run").description("Run tasks against the current harness"
|
|
|
12019
12547
|
}
|
|
12020
12548
|
}
|
|
12021
12549
|
try {
|
|
12022
|
-
await
|
|
12550
|
+
await fs36.access(path37.join(workspace, "iterations", "0", "harness"));
|
|
12023
12551
|
} catch {
|
|
12024
12552
|
console.log(ui.error("No baseline harness found. Run kairn evolve baseline first."));
|
|
12025
12553
|
process.exit(1);
|
|
@@ -12126,16 +12654,16 @@ evolveCommand.command("run").description("Run tasks against the current harness"
|
|
|
12126
12654
|
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) => {
|
|
12127
12655
|
try {
|
|
12128
12656
|
const projectRoot = process.cwd();
|
|
12129
|
-
const workspace =
|
|
12657
|
+
const workspace = path37.join(projectRoot, ".kairn-evolve");
|
|
12130
12658
|
console.log(ui.section("Evolve PBT"));
|
|
12131
12659
|
try {
|
|
12132
|
-
await
|
|
12660
|
+
await fs36.access(workspace);
|
|
12133
12661
|
} catch {
|
|
12134
12662
|
console.log(ui.error("No .kairn-evolve/ directory found. Run kairn evolve init first."));
|
|
12135
12663
|
process.exit(1);
|
|
12136
12664
|
}
|
|
12137
12665
|
try {
|
|
12138
|
-
await
|
|
12666
|
+
await fs36.access(path37.join(workspace, "iterations", "0", "harness"));
|
|
12139
12667
|
} catch {
|
|
12140
12668
|
console.log(ui.error("No baseline harness found. Run kairn evolve baseline first."));
|
|
12141
12669
|
process.exit(1);
|
|
@@ -12155,8 +12683,8 @@ evolveCommand.command("pbt").description("Run Population-Based Training with par
|
|
|
12155
12683
|
if (sampling === "thompson" || sampling === "uniform") {
|
|
12156
12684
|
evolveConfig.samplingStrategy = sampling;
|
|
12157
12685
|
}
|
|
12158
|
-
const tasksPath =
|
|
12159
|
-
const tasksContent = await
|
|
12686
|
+
const tasksPath = path37.join(workspace, "tasks.yaml");
|
|
12687
|
+
const tasksContent = await fs36.readFile(tasksPath, "utf-8");
|
|
12160
12688
|
const parsed = yamlParse2(tasksContent);
|
|
12161
12689
|
if (!parsed?.tasks || parsed.tasks.length === 0) {
|
|
12162
12690
|
console.log(ui.error("No tasks found in tasks.yaml"));
|
|
@@ -12214,10 +12742,10 @@ evolveCommand.command("pbt").description("Run Population-Based Training with par
|
|
|
12214
12742
|
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) => {
|
|
12215
12743
|
try {
|
|
12216
12744
|
const projectRoot = process.cwd();
|
|
12217
|
-
const workspace =
|
|
12745
|
+
const workspace = path37.join(projectRoot, ".kairn-evolve");
|
|
12218
12746
|
console.log(ui.section("Evolve Apply"));
|
|
12219
12747
|
try {
|
|
12220
|
-
await
|
|
12748
|
+
await fs36.access(workspace);
|
|
12221
12749
|
} catch {
|
|
12222
12750
|
console.log(ui.error("No .kairn-evolve/ directory found. Run kairn evolve init first."));
|
|
12223
12751
|
process.exit(1);
|
|
@@ -12258,9 +12786,9 @@ evolveCommand.command("apply").description("Apply the best evolved harness to yo
|
|
|
12258
12786
|
evolveCommand.command("report").description("Generate a summary report of the evolution run").option("--json", "Output machine-readable JSON instead of Markdown").action(async (options) => {
|
|
12259
12787
|
try {
|
|
12260
12788
|
const projectRoot = process.cwd();
|
|
12261
|
-
const workspace =
|
|
12789
|
+
const workspace = path37.join(projectRoot, ".kairn-evolve");
|
|
12262
12790
|
try {
|
|
12263
|
-
await
|
|
12791
|
+
await fs36.access(workspace);
|
|
12264
12792
|
} catch {
|
|
12265
12793
|
console.log(ui.error("No .kairn-evolve/ directory found. Run kairn evolve init first."));
|
|
12266
12794
|
process.exit(1);
|
|
@@ -12281,23 +12809,23 @@ evolveCommand.command("report").description("Generate a summary report of the ev
|
|
|
12281
12809
|
evolveCommand.command("diff <iter1> <iter2>").description("Show harness changes between two iterations").action(async (iter1Str, iter2Str) => {
|
|
12282
12810
|
try {
|
|
12283
12811
|
const projectRoot = process.cwd();
|
|
12284
|
-
const workspace =
|
|
12812
|
+
const workspace = path37.join(projectRoot, ".kairn-evolve");
|
|
12285
12813
|
const iter1 = parseInt(iter1Str, 10);
|
|
12286
12814
|
const iter2 = parseInt(iter2Str, 10);
|
|
12287
12815
|
if (isNaN(iter1) || isNaN(iter2)) {
|
|
12288
12816
|
console.log(ui.error("Both arguments must be integers (iteration numbers)"));
|
|
12289
12817
|
process.exit(1);
|
|
12290
12818
|
}
|
|
12291
|
-
const harness1 =
|
|
12292
|
-
const harness2 =
|
|
12819
|
+
const harness1 = path37.join(workspace, "iterations", iter1.toString(), "harness");
|
|
12820
|
+
const harness2 = path37.join(workspace, "iterations", iter2.toString(), "harness");
|
|
12293
12821
|
try {
|
|
12294
|
-
await
|
|
12822
|
+
await fs36.access(harness1);
|
|
12295
12823
|
} catch {
|
|
12296
12824
|
console.log(ui.error(`Iteration ${iter1} harness not found at ${harness1}`));
|
|
12297
12825
|
process.exit(1);
|
|
12298
12826
|
}
|
|
12299
12827
|
try {
|
|
12300
|
-
await
|
|
12828
|
+
await fs36.access(harness2);
|
|
12301
12829
|
} catch {
|
|
12302
12830
|
console.log(ui.error(`Iteration ${iter2} harness not found at ${harness2}`));
|
|
12303
12831
|
process.exit(1);
|
|
@@ -12404,7 +12932,7 @@ evolveCommand.command("research").description("Run cross-repo research to discov
|
|
|
12404
12932
|
const reportText = formatResearchReport2(report);
|
|
12405
12933
|
console.log("\n" + reportText);
|
|
12406
12934
|
if (options.output) {
|
|
12407
|
-
await
|
|
12935
|
+
await fs36.writeFile(options.output, reportText, "utf-8");
|
|
12408
12936
|
console.log(chalk14.green(`
|
|
12409
12937
|
Report saved to ${options.output}`));
|
|
12410
12938
|
}
|
|
@@ -12421,10 +12949,10 @@ evolveCommand.command("research").description("Run cross-repo research to discov
|
|
|
12421
12949
|
async function countFiles(dir) {
|
|
12422
12950
|
let count = 0;
|
|
12423
12951
|
try {
|
|
12424
|
-
const entries = await
|
|
12952
|
+
const entries = await fs36.readdir(dir, { withFileTypes: true });
|
|
12425
12953
|
for (const entry of entries) {
|
|
12426
12954
|
if (entry.isDirectory()) {
|
|
12427
|
-
count += await countFiles(
|
|
12955
|
+
count += await countFiles(path37.join(dir, entry.name));
|
|
12428
12956
|
} else {
|
|
12429
12957
|
count++;
|
|
12430
12958
|
}
|
|
@@ -12440,12 +12968,42 @@ init_scan();
|
|
|
12440
12968
|
init_analyze();
|
|
12441
12969
|
init_types3();
|
|
12442
12970
|
init_cache();
|
|
12971
|
+
init_proposer();
|
|
12443
12972
|
init_ui();
|
|
12444
12973
|
init_logo();
|
|
12445
12974
|
import { Command as Command12 } from "commander";
|
|
12446
12975
|
import chalk15 from "chalk";
|
|
12976
|
+
import fs37 from "fs/promises";
|
|
12977
|
+
import path38 from "path";
|
|
12447
12978
|
import ora3 from "ora";
|
|
12448
12979
|
async function analyzeAction(options) {
|
|
12980
|
+
if (options.ir) {
|
|
12981
|
+
const irPath = path38.join(process.cwd(), ".kairn", "harness-ir.json");
|
|
12982
|
+
try {
|
|
12983
|
+
const raw = await fs37.readFile(irPath, "utf-8");
|
|
12984
|
+
const ir = JSON.parse(raw);
|
|
12985
|
+
if (!options.json) {
|
|
12986
|
+
printCompactBanner();
|
|
12987
|
+
console.log(ui.section("Harness IR"));
|
|
12988
|
+
console.log(buildIRSummary(ir));
|
|
12989
|
+
console.log("");
|
|
12990
|
+
console.log(chalk15.dim(` Source: ${irPath}`));
|
|
12991
|
+
console.log("");
|
|
12992
|
+
} else {
|
|
12993
|
+
console.log(JSON.stringify(ir, null, 2));
|
|
12994
|
+
}
|
|
12995
|
+
} catch {
|
|
12996
|
+
if (options.json) {
|
|
12997
|
+
console.log(JSON.stringify({ error: "No harness IR found. Run `kairn optimize` first." }));
|
|
12998
|
+
} else {
|
|
12999
|
+
printCompactBanner();
|
|
13000
|
+
console.log(ui.warn("No harness IR found. Run `kairn optimize` first."));
|
|
13001
|
+
console.log("");
|
|
13002
|
+
}
|
|
13003
|
+
process.exit(1);
|
|
13004
|
+
}
|
|
13005
|
+
return;
|
|
13006
|
+
}
|
|
12449
13007
|
if (!options.json) {
|
|
12450
13008
|
printCompactBanner();
|
|
12451
13009
|
}
|
|
@@ -12473,8 +13031,8 @@ async function analyzeAction(options) {
|
|
|
12473
13031
|
const profile = await scanProject(targetDir);
|
|
12474
13032
|
scanSpinner?.succeed("Project scanned");
|
|
12475
13033
|
if (!options.json) {
|
|
12476
|
-
if (profile.
|
|
12477
|
-
console.log(ui.kv("
|
|
13034
|
+
if (profile.languages.length > 0)
|
|
13035
|
+
console.log(ui.kv("Languages:", profile.languages.join(", ")));
|
|
12478
13036
|
if (profile.framework)
|
|
12479
13037
|
console.log(ui.kv("Framework:", profile.framework));
|
|
12480
13038
|
}
|
|
@@ -12498,11 +13056,14 @@ async function analyzeAction(options) {
|
|
|
12498
13056
|
indent: 2
|
|
12499
13057
|
}).start();
|
|
12500
13058
|
let analysis;
|
|
13059
|
+
let packedSource = "";
|
|
12501
13060
|
try {
|
|
12502
|
-
|
|
13061
|
+
const result = await analyzeProject(targetDir, profile, config, {
|
|
12503
13062
|
refresh: options.refresh,
|
|
12504
13063
|
tokenBudget: options.tokenBudget
|
|
12505
13064
|
});
|
|
13065
|
+
analysis = result.analysis;
|
|
13066
|
+
packedSource = result.packedSource;
|
|
12506
13067
|
analysisSpinner?.succeed(
|
|
12507
13068
|
options.refresh ? "Re-analyzed from scratch" : "Codebase analyzed"
|
|
12508
13069
|
);
|
|
@@ -12580,9 +13141,10 @@ async function analyzeAction(options) {
|
|
|
12580
13141
|
}
|
|
12581
13142
|
}
|
|
12582
13143
|
console.log("");
|
|
13144
|
+
const packedStats = packedSource ? ` \xB7 ${packedSource.length.toLocaleString()} chars packed` : "";
|
|
12583
13145
|
console.log(
|
|
12584
13146
|
chalk15.dim(
|
|
12585
|
-
` Sampled ${analysis.sampled_files.length} files \xB7 analyzed ${analysis.analyzed_at}`
|
|
13147
|
+
` Sampled ${analysis.sampled_files.length} files${packedStats} \xB7 analyzed ${analysis.analyzed_at}`
|
|
12586
13148
|
)
|
|
12587
13149
|
);
|
|
12588
13150
|
console.log(ui.divider());
|
|
@@ -12590,7 +13152,7 @@ async function analyzeAction(options) {
|
|
|
12590
13152
|
}
|
|
12591
13153
|
var analyzeCommand = new Command12("analyze").description(
|
|
12592
13154
|
"Analyze project source code to understand purpose, architecture, and workflows"
|
|
12593
|
-
).option("--refresh", "Force re-analysis, bypassing cache").option("--json", "Output raw JSON (for piping)").option("--token-budget <tokens>", "Max tokens of source code to sample (default: 60000)", parseInt).action(analyzeAction);
|
|
13155
|
+
).option("--refresh", "Force re-analysis, bypassing cache").option("--json", "Output raw JSON (for piping)").option("--ir", "Display the persisted harness IR from .kairn/harness-ir.json").option("--token-budget <tokens>", "Max tokens of source code to sample (default: 60000)", parseInt).action(analyzeAction);
|
|
12594
13156
|
|
|
12595
13157
|
// src/cli.ts
|
|
12596
13158
|
var require2 = createRequire(import.meta.url);
|