kairn-cli 2.14.0 → 2.15.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 +891 -450
- 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 [];
|
|
@@ -3891,7 +3910,7 @@ function extractEnvKeys(content) {
|
|
|
3891
3910
|
return keys;
|
|
3892
3911
|
}
|
|
3893
3912
|
async function scanProject(dir) {
|
|
3894
|
-
const pkg2 = await readJsonSafe(
|
|
3913
|
+
const pkg2 = await readJsonSafe(path10.join(dir, "package.json"));
|
|
3895
3914
|
const deps = pkg2?.dependencies ? Object.keys(pkg2.dependencies) : [];
|
|
3896
3915
|
const devDeps = pkg2?.devDependencies ? Object.keys(pkg2.devDependencies) : [];
|
|
3897
3916
|
const allDeps = [...deps, ...devDeps];
|
|
@@ -3919,19 +3938,19 @@ async function scanProject(dir) {
|
|
|
3919
3938
|
const framework = detectFramework(allDeps);
|
|
3920
3939
|
const typescript = keyFiles.includes("tsconfig.json") || allDeps.includes("typescript");
|
|
3921
3940
|
const testCommand = scripts.test && scripts.test !== 'echo "Error: no test specified" && exit 1' ? scripts.test : null;
|
|
3922
|
-
const hasTests = testCommand !== null || await fileExists(
|
|
3941
|
+
const hasTests = testCommand !== null || await fileExists(path10.join(dir, "tests")) || await fileExists(path10.join(dir, "__tests__")) || await fileExists(path10.join(dir, "test"));
|
|
3923
3942
|
const buildCommand = scripts.build || null;
|
|
3924
3943
|
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(
|
|
3944
|
+
const hasSrc = await fileExists(path10.join(dir, "src"));
|
|
3945
|
+
const hasDocker = await fileExists(path10.join(dir, "docker-compose.yml")) || await fileExists(path10.join(dir, "Dockerfile"));
|
|
3946
|
+
const hasCi = await fileExists(path10.join(dir, ".github/workflows"));
|
|
3947
|
+
const hasEnvFile = await fileExists(path10.join(dir, ".env")) || await fileExists(path10.join(dir, ".env.example"));
|
|
3929
3948
|
let envKeys = [];
|
|
3930
|
-
const envExample = await readFileSafe(
|
|
3949
|
+
const envExample = await readFileSafe(path10.join(dir, ".env.example"));
|
|
3931
3950
|
if (envExample) {
|
|
3932
3951
|
envKeys = extractEnvKeys(envExample);
|
|
3933
3952
|
}
|
|
3934
|
-
const claudeDir =
|
|
3953
|
+
const claudeDir = path10.join(dir, ".claude");
|
|
3935
3954
|
const hasClaudeDir = await fileExists(claudeDir);
|
|
3936
3955
|
let existingClaudeMd = null;
|
|
3937
3956
|
let existingSettings = null;
|
|
@@ -3943,21 +3962,21 @@ async function scanProject(dir) {
|
|
|
3943
3962
|
let mcpServerCount = 0;
|
|
3944
3963
|
let claudeMdLineCount = 0;
|
|
3945
3964
|
if (hasClaudeDir) {
|
|
3946
|
-
existingClaudeMd = await readFileSafe(
|
|
3965
|
+
existingClaudeMd = await readFileSafe(path10.join(claudeDir, "CLAUDE.md"));
|
|
3947
3966
|
if (existingClaudeMd) {
|
|
3948
3967
|
claudeMdLineCount = existingClaudeMd.split("\n").length;
|
|
3949
3968
|
}
|
|
3950
|
-
existingSettings = await readJsonSafe(
|
|
3951
|
-
existingMcpConfig = await readJsonSafe(
|
|
3969
|
+
existingSettings = await readJsonSafe(path10.join(claudeDir, "settings.json"));
|
|
3970
|
+
existingMcpConfig = await readJsonSafe(path10.join(dir, ".mcp.json"));
|
|
3952
3971
|
if (existingMcpConfig?.mcpServers) {
|
|
3953
3972
|
mcpServerCount = Object.keys(existingMcpConfig.mcpServers).length;
|
|
3954
3973
|
}
|
|
3955
|
-
existingCommands = (await listDirSafe(
|
|
3956
|
-
existingRules = (await listDirSafe(
|
|
3957
|
-
existingSkills = await listDirSafe(
|
|
3958
|
-
existingAgents = (await listDirSafe(
|
|
3974
|
+
existingCommands = (await listDirSafe(path10.join(claudeDir, "commands"))).filter((f) => f.endsWith(".md")).map((f) => f.replace(".md", ""));
|
|
3975
|
+
existingRules = (await listDirSafe(path10.join(claudeDir, "rules"))).filter((f) => f.endsWith(".md")).map((f) => f.replace(".md", ""));
|
|
3976
|
+
existingSkills = await listDirSafe(path10.join(claudeDir, "skills"));
|
|
3977
|
+
existingAgents = (await listDirSafe(path10.join(claudeDir, "agents"))).filter((f) => f.endsWith(".md")).map((f) => f.replace(".md", ""));
|
|
3959
3978
|
}
|
|
3960
|
-
const name = pkg2?.name ||
|
|
3979
|
+
const name = pkg2?.name || path10.basename(dir);
|
|
3961
3980
|
const description = pkg2?.description || "";
|
|
3962
3981
|
return {
|
|
3963
3982
|
name,
|
|
@@ -4014,8 +4033,8 @@ var init_types3 = __esm({
|
|
|
4014
4033
|
});
|
|
4015
4034
|
|
|
4016
4035
|
// src/analyzer/patterns.ts
|
|
4017
|
-
import
|
|
4018
|
-
import
|
|
4036
|
+
import fs11 from "fs/promises";
|
|
4037
|
+
import path11 from "path";
|
|
4019
4038
|
function getStrategy(language) {
|
|
4020
4039
|
if (language === null) {
|
|
4021
4040
|
return null;
|
|
@@ -4080,7 +4099,7 @@ function extractPathsFromScripts(scripts) {
|
|
|
4080
4099
|
async function resolveNodeEntryPoints(dir) {
|
|
4081
4100
|
const paths = [];
|
|
4082
4101
|
try {
|
|
4083
|
-
const raw = await
|
|
4102
|
+
const raw = await fs11.readFile(path11.join(dir, "package.json"), "utf-8");
|
|
4084
4103
|
const pkg2 = JSON.parse(raw);
|
|
4085
4104
|
if (typeof pkg2.main === "string") {
|
|
4086
4105
|
paths.push(pkg2.main);
|
|
@@ -4119,7 +4138,7 @@ async function resolveNodeEntryPoints(dir) {
|
|
|
4119
4138
|
async function resolvePythonEntryPoints(dir) {
|
|
4120
4139
|
const paths = [];
|
|
4121
4140
|
try {
|
|
4122
|
-
const raw = await
|
|
4141
|
+
const raw = await fs11.readFile(path11.join(dir, "pyproject.toml"), "utf-8");
|
|
4123
4142
|
const scriptMatches = raw.matchAll(/^\s*\w+\s*=\s*"([^":]+)/gm);
|
|
4124
4143
|
for (const m of scriptMatches) {
|
|
4125
4144
|
const modulePath = m[1].replace(/\./g, "/") + ".py";
|
|
@@ -4130,17 +4149,17 @@ async function resolvePythonEntryPoints(dir) {
|
|
|
4130
4149
|
}
|
|
4131
4150
|
for (const f of ["manage.py", "wsgi.py", "asgi.py"]) {
|
|
4132
4151
|
try {
|
|
4133
|
-
await
|
|
4152
|
+
await fs11.access(path11.join(dir, f));
|
|
4134
4153
|
paths.push(f);
|
|
4135
4154
|
} catch {
|
|
4136
4155
|
}
|
|
4137
4156
|
}
|
|
4138
4157
|
try {
|
|
4139
|
-
const entries = await
|
|
4158
|
+
const entries = await fs11.readdir(path11.join(dir, "src"), { withFileTypes: true });
|
|
4140
4159
|
for (const entry of entries) {
|
|
4141
4160
|
if (entry.isDirectory()) {
|
|
4142
4161
|
try {
|
|
4143
|
-
await
|
|
4162
|
+
await fs11.access(path11.join(dir, "src", entry.name, "__main__.py"));
|
|
4144
4163
|
paths.push(`src/${entry.name}/__main__.py`);
|
|
4145
4164
|
paths.push(`src/${entry.name}/__init__.py`);
|
|
4146
4165
|
} catch {
|
|
@@ -4154,7 +4173,7 @@ async function resolvePythonEntryPoints(dir) {
|
|
|
4154
4173
|
async function resolveRustEntryPoints(dir) {
|
|
4155
4174
|
const paths = [];
|
|
4156
4175
|
try {
|
|
4157
|
-
const raw = await
|
|
4176
|
+
const raw = await fs11.readFile(path11.join(dir, "Cargo.toml"), "utf-8");
|
|
4158
4177
|
const binPaths = raw.matchAll(/path\s*=\s*"([^"]+\.rs)"/g);
|
|
4159
4178
|
for (const m of binPaths) paths.push(m[1]);
|
|
4160
4179
|
} catch {
|
|
@@ -4164,7 +4183,7 @@ async function resolveRustEntryPoints(dir) {
|
|
|
4164
4183
|
async function resolveGoEntryPoints(dir) {
|
|
4165
4184
|
const paths = [];
|
|
4166
4185
|
try {
|
|
4167
|
-
const cmdEntries = await
|
|
4186
|
+
const cmdEntries = await fs11.readdir(path11.join(dir, "cmd"), { withFileTypes: true });
|
|
4168
4187
|
for (const entry of cmdEntries) {
|
|
4169
4188
|
if (entry.isDirectory()) {
|
|
4170
4189
|
paths.push(`cmd/${entry.name}/main.go`);
|
|
@@ -4330,9 +4349,9 @@ var init_patterns = __esm({
|
|
|
4330
4349
|
});
|
|
4331
4350
|
|
|
4332
4351
|
// src/analyzer/repomix-adapter.ts
|
|
4333
|
-
import
|
|
4352
|
+
import path12 from "path";
|
|
4334
4353
|
import os3 from "os";
|
|
4335
|
-
import
|
|
4354
|
+
import fs12 from "fs/promises";
|
|
4336
4355
|
import {
|
|
4337
4356
|
pack,
|
|
4338
4357
|
mergeConfigs,
|
|
@@ -4342,7 +4361,7 @@ import {
|
|
|
4342
4361
|
} from "repomix";
|
|
4343
4362
|
async function packCodebase(dir, options) {
|
|
4344
4363
|
setLogLevel(0);
|
|
4345
|
-
const tempOutputFile =
|
|
4364
|
+
const tempOutputFile = path12.join(
|
|
4346
4365
|
os3.tmpdir(),
|
|
4347
4366
|
`kairn-repomix-${Date.now()}-${Math.random().toString(36).slice(2)}.txt`
|
|
4348
4367
|
);
|
|
@@ -4413,7 +4432,7 @@ ${f.content}`).join("\n\n");
|
|
|
4413
4432
|
message
|
|
4414
4433
|
);
|
|
4415
4434
|
} finally {
|
|
4416
|
-
await
|
|
4435
|
+
await fs12.rm(tempOutputFile, { force: true }).catch(() => {
|
|
4417
4436
|
});
|
|
4418
4437
|
}
|
|
4419
4438
|
}
|
|
@@ -4425,50 +4444,64 @@ var init_repomix_adapter = __esm({
|
|
|
4425
4444
|
});
|
|
4426
4445
|
|
|
4427
4446
|
// src/analyzer/cache.ts
|
|
4428
|
-
import
|
|
4447
|
+
import fs13 from "fs/promises";
|
|
4429
4448
|
import fsSync from "fs";
|
|
4430
|
-
import
|
|
4449
|
+
import path13 from "path";
|
|
4431
4450
|
import { createHash } from "crypto";
|
|
4432
4451
|
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
4433
4452
|
function getKairnVersion() {
|
|
4434
|
-
let dir =
|
|
4453
|
+
let dir = path13.dirname(fileURLToPath3(import.meta.url));
|
|
4435
4454
|
for (let i = 0; i < 5; i++) {
|
|
4436
4455
|
try {
|
|
4437
|
-
const pkgPath =
|
|
4456
|
+
const pkgPath = path13.join(dir, "package.json");
|
|
4438
4457
|
const content = fsSync.readFileSync(pkgPath, "utf-8");
|
|
4439
4458
|
const parsed = JSON.parse(content);
|
|
4440
4459
|
if (parsed.name === "kairn-cli") return parsed.version ?? "0.0.0";
|
|
4441
4460
|
} catch {
|
|
4442
4461
|
}
|
|
4443
|
-
dir =
|
|
4462
|
+
dir = path13.dirname(dir);
|
|
4444
4463
|
}
|
|
4445
4464
|
return "0.0.0";
|
|
4446
4465
|
}
|
|
4447
4466
|
async function readCache(dir) {
|
|
4448
|
-
const filePath =
|
|
4467
|
+
const filePath = path13.join(dir, CACHE_FILENAME);
|
|
4449
4468
|
try {
|
|
4450
|
-
const raw = await
|
|
4469
|
+
const raw = await fs13.readFile(filePath, "utf-8");
|
|
4451
4470
|
const parsed = JSON.parse(raw);
|
|
4452
|
-
|
|
4471
|
+
if (typeof parsed !== "object" || parsed === null || typeof parsed.analysis !== "object" || parsed.analysis === null) {
|
|
4472
|
+
return null;
|
|
4473
|
+
}
|
|
4474
|
+
const cache = parsed;
|
|
4475
|
+
let packedSource = null;
|
|
4476
|
+
try {
|
|
4477
|
+
const packedPath = path13.join(dir, PACKED_SOURCE_FILENAME);
|
|
4478
|
+
packedSource = await fs13.readFile(packedPath, "utf-8");
|
|
4479
|
+
} catch {
|
|
4480
|
+
}
|
|
4481
|
+
return { ...cache, packedSource };
|
|
4453
4482
|
} catch {
|
|
4454
4483
|
return null;
|
|
4455
4484
|
}
|
|
4456
4485
|
}
|
|
4457
|
-
async function writeCache(dir, analysis) {
|
|
4458
|
-
const filePath =
|
|
4486
|
+
async function writeCache(dir, analysis, packedSource) {
|
|
4487
|
+
const filePath = path13.join(dir, CACHE_FILENAME);
|
|
4459
4488
|
const cache = {
|
|
4460
4489
|
analysis,
|
|
4461
4490
|
content_hash: analysis.content_hash,
|
|
4462
4491
|
kairn_version: KAIRN_VERSION
|
|
4463
4492
|
};
|
|
4464
|
-
await
|
|
4493
|
+
await fs13.writeFile(filePath, JSON.stringify(cache, null, 2), "utf-8");
|
|
4494
|
+
if (packedSource !== void 0) {
|
|
4495
|
+
const packedPath = path13.join(dir, PACKED_SOURCE_FILENAME);
|
|
4496
|
+
await fs13.writeFile(packedPath, packedSource, "utf-8");
|
|
4497
|
+
}
|
|
4465
4498
|
}
|
|
4466
4499
|
async function computeContentHash(filePaths, dir) {
|
|
4467
4500
|
const hash = createHash("sha256");
|
|
4468
4501
|
for (const filePath of filePaths) {
|
|
4469
4502
|
try {
|
|
4470
|
-
const fullPath =
|
|
4471
|
-
const content = await
|
|
4503
|
+
const fullPath = path13.join(dir, filePath);
|
|
4504
|
+
const content = await fs13.readFile(fullPath, "utf-8");
|
|
4472
4505
|
hash.update(content);
|
|
4473
4506
|
} catch {
|
|
4474
4507
|
}
|
|
@@ -4478,12 +4511,13 @@ async function computeContentHash(filePaths, dir) {
|
|
|
4478
4511
|
function isCacheValid(cache, currentHash) {
|
|
4479
4512
|
return cache.content_hash === currentHash && cache.kairn_version === KAIRN_VERSION;
|
|
4480
4513
|
}
|
|
4481
|
-
var KAIRN_VERSION, CACHE_FILENAME;
|
|
4514
|
+
var KAIRN_VERSION, CACHE_FILENAME, PACKED_SOURCE_FILENAME;
|
|
4482
4515
|
var init_cache = __esm({
|
|
4483
4516
|
"src/analyzer/cache.ts"() {
|
|
4484
4517
|
"use strict";
|
|
4485
4518
|
KAIRN_VERSION = getKairnVersion();
|
|
4486
4519
|
CACHE_FILENAME = ".kairn-analysis.json";
|
|
4520
|
+
PACKED_SOURCE_FILENAME = ".kairn-packed-source.txt";
|
|
4487
4521
|
}
|
|
4488
4522
|
});
|
|
4489
4523
|
|
|
@@ -4516,7 +4550,10 @@ async function analyzeProject(dir, profile, config, options) {
|
|
|
4516
4550
|
dir
|
|
4517
4551
|
);
|
|
4518
4552
|
if (isCacheValid(cache, currentHash)) {
|
|
4519
|
-
return
|
|
4553
|
+
return {
|
|
4554
|
+
analysis: cache.analysis,
|
|
4555
|
+
packedSource: cache.packedSource ?? ""
|
|
4556
|
+
};
|
|
4520
4557
|
}
|
|
4521
4558
|
}
|
|
4522
4559
|
}
|
|
@@ -4582,8 +4619,8 @@ async function analyzeProject(dir, profile, config, options) {
|
|
|
4582
4619
|
content_hash: contentHash,
|
|
4583
4620
|
analyzed_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
4584
4621
|
};
|
|
4585
|
-
await writeCache(dir, analysis);
|
|
4586
|
-
return analysis;
|
|
4622
|
+
await writeCache(dir, analysis, packed.content);
|
|
4623
|
+
return { analysis, packedSource: packed.content };
|
|
4587
4624
|
}
|
|
4588
4625
|
function buildUserMessage6(language, profile, packedContent) {
|
|
4589
4626
|
return [
|
|
@@ -4656,8 +4693,8 @@ import { Command as Command2 } from "commander";
|
|
|
4656
4693
|
import { confirm as confirm2 } from "@inquirer/prompts";
|
|
4657
4694
|
import chalk5 from "chalk";
|
|
4658
4695
|
import ora from "ora";
|
|
4659
|
-
import
|
|
4660
|
-
import
|
|
4696
|
+
import fs14 from "fs/promises";
|
|
4697
|
+
import path14 from "path";
|
|
4661
4698
|
function simpleDiff(oldContent, newContent) {
|
|
4662
4699
|
const oldLines = oldContent.split("\n");
|
|
4663
4700
|
const newLines = newContent.split("\n");
|
|
@@ -4681,10 +4718,10 @@ async function generateDiff(spec, targetDir) {
|
|
|
4681
4718
|
const fileMap = buildFileMap(spec);
|
|
4682
4719
|
const results = [];
|
|
4683
4720
|
for (const [relativePath, newContent] of fileMap) {
|
|
4684
|
-
const absolutePath =
|
|
4721
|
+
const absolutePath = path14.join(targetDir, relativePath);
|
|
4685
4722
|
let oldContent = null;
|
|
4686
4723
|
try {
|
|
4687
|
-
oldContent = await
|
|
4724
|
+
oldContent = await fs14.readFile(absolutePath, "utf-8");
|
|
4688
4725
|
} catch {
|
|
4689
4726
|
}
|
|
4690
4727
|
if (oldContent === null) {
|
|
@@ -4741,7 +4778,7 @@ Existing .claude/ harness found:`);
|
|
|
4741
4778
|
lines.push(` Agents: ${profile.existingAgents.length > 0 ? profile.existingAgents.join(", ") : "none"}`);
|
|
4742
4779
|
return lines.join("\n");
|
|
4743
4780
|
}
|
|
4744
|
-
function buildOptimizeIntent(profile, analysis) {
|
|
4781
|
+
function buildOptimizeIntent(profile, analysis, packedSource) {
|
|
4745
4782
|
const parts = [];
|
|
4746
4783
|
parts.push("## Project Profile (scanned from actual codebase)\n");
|
|
4747
4784
|
parts.push(buildProfileSummary(profile));
|
|
@@ -4826,6 +4863,13 @@ ${profile.existingClaudeMd}`);
|
|
|
4826
4863
|
}
|
|
4827
4864
|
}
|
|
4828
4865
|
}
|
|
4866
|
+
if (packedSource) {
|
|
4867
|
+
parts.push(`
|
|
4868
|
+
|
|
4869
|
+
## Sampled Source Code (reference for project-specific content)
|
|
4870
|
+
|
|
4871
|
+
${packedSource}`);
|
|
4872
|
+
}
|
|
4829
4873
|
return parts.join("\n");
|
|
4830
4874
|
}
|
|
4831
4875
|
var optimizeCommand;
|
|
@@ -4843,6 +4887,7 @@ var init_optimize = __esm({
|
|
|
4843
4887
|
init_logo();
|
|
4844
4888
|
init_analyze();
|
|
4845
4889
|
init_types3();
|
|
4890
|
+
init_persist();
|
|
4846
4891
|
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
4892
|
printCompactBanner();
|
|
4848
4893
|
const config = await loadConfig();
|
|
@@ -4866,13 +4911,19 @@ var init_optimize = __esm({
|
|
|
4866
4911
|
console.log(ui.section("Codebase Analysis"));
|
|
4867
4912
|
const analysisSpinner = ora({ text: "Analyzing source code...", indent: 2 }).start();
|
|
4868
4913
|
let analysis = null;
|
|
4914
|
+
let packedSource = "";
|
|
4869
4915
|
try {
|
|
4870
|
-
|
|
4916
|
+
const result = await analyzeProject(targetDir, profile, config);
|
|
4917
|
+
analysis = result.analysis;
|
|
4918
|
+
packedSource = result.packedSource;
|
|
4871
4919
|
analysisSpinner.succeed("Codebase analyzed");
|
|
4872
4920
|
console.log(ui.kv("Purpose:", analysis.purpose));
|
|
4873
4921
|
console.log(ui.kv("Domain:", analysis.domain));
|
|
4874
4922
|
console.log(ui.kv("Modules:", analysis.key_modules.map((m) => m.name).join(", ") || "none detected"));
|
|
4875
4923
|
console.log(ui.kv("Workflows:", analysis.workflows.map((w) => w.name).join(", ") || "none detected"));
|
|
4924
|
+
if (packedSource) {
|
|
4925
|
+
console.log(ui.kv("Source:", `${packedSource.length.toLocaleString()} chars sampled`));
|
|
4926
|
+
}
|
|
4876
4927
|
} catch (err) {
|
|
4877
4928
|
if (err instanceof AnalysisError) {
|
|
4878
4929
|
analysisSpinner.fail("Analysis failed");
|
|
@@ -4940,7 +4991,7 @@ Run \`kairn analyze\` for details.`));
|
|
|
4940
4991
|
}
|
|
4941
4992
|
}
|
|
4942
4993
|
}
|
|
4943
|
-
const intent = buildOptimizeIntent(profile, analysis);
|
|
4994
|
+
const intent = buildOptimizeIntent(profile, analysis, packedSource);
|
|
4944
4995
|
let spec;
|
|
4945
4996
|
const spinner = ora({ text: "Compiling optimized environment...", indent: 2 }).start();
|
|
4946
4997
|
try {
|
|
@@ -4954,6 +5005,13 @@ Run \`kairn analyze\` for details.`));
|
|
|
4954
5005
|
console.log(ui.errorBox("KAIRN \u2014 Error", `Optimization failed: ${msg}`));
|
|
4955
5006
|
process.exit(1);
|
|
4956
5007
|
}
|
|
5008
|
+
if (spec.ir) {
|
|
5009
|
+
try {
|
|
5010
|
+
await persistHarnessIR(targetDir, spec.ir);
|
|
5011
|
+
} catch {
|
|
5012
|
+
console.log(ui.warn("Could not persist harness IR to .kairn/harness-ir.json"));
|
|
5013
|
+
}
|
|
5014
|
+
}
|
|
4957
5015
|
const registry = await loadRegistry();
|
|
4958
5016
|
const summary = summarizeSpec(spec, registry);
|
|
4959
5017
|
console.log("");
|
|
@@ -5034,37 +5092,37 @@ Run \`kairn analyze\` for details.`));
|
|
|
5034
5092
|
});
|
|
5035
5093
|
|
|
5036
5094
|
// src/evolve/baseline.ts
|
|
5037
|
-
import
|
|
5038
|
-
import
|
|
5095
|
+
import fs21 from "fs/promises";
|
|
5096
|
+
import path21 from "path";
|
|
5039
5097
|
async function snapshotBaseline(projectRoot, workspacePath) {
|
|
5040
|
-
const claudeDir =
|
|
5041
|
-
const baselineDir =
|
|
5042
|
-
const iter0Dir =
|
|
5098
|
+
const claudeDir = path21.join(projectRoot, ".claude");
|
|
5099
|
+
const baselineDir = path21.join(workspacePath, "baseline");
|
|
5100
|
+
const iter0Dir = path21.join(workspacePath, "iterations", "0", "harness");
|
|
5043
5101
|
try {
|
|
5044
|
-
await
|
|
5102
|
+
await fs21.access(claudeDir);
|
|
5045
5103
|
} catch {
|
|
5046
5104
|
throw new Error(`.claude/ directory not found in ${projectRoot}`);
|
|
5047
5105
|
}
|
|
5048
5106
|
await copyDir(claudeDir, baselineDir);
|
|
5049
5107
|
await copyDir(claudeDir, iter0Dir);
|
|
5050
|
-
const mcpJsonPath =
|
|
5108
|
+
const mcpJsonPath = path21.join(projectRoot, ".mcp.json");
|
|
5051
5109
|
try {
|
|
5052
|
-
await
|
|
5053
|
-
await
|
|
5054
|
-
await
|
|
5110
|
+
await fs21.access(mcpJsonPath);
|
|
5111
|
+
await fs21.copyFile(mcpJsonPath, path21.join(baselineDir, ".mcp.json"));
|
|
5112
|
+
await fs21.copyFile(mcpJsonPath, path21.join(iter0Dir, ".mcp.json"));
|
|
5055
5113
|
} catch {
|
|
5056
5114
|
}
|
|
5057
5115
|
}
|
|
5058
5116
|
async function copyDir(src, dest) {
|
|
5059
|
-
await
|
|
5060
|
-
const entries = await
|
|
5117
|
+
await fs21.mkdir(dest, { recursive: true });
|
|
5118
|
+
const entries = await fs21.readdir(src, { withFileTypes: true });
|
|
5061
5119
|
for (const entry of entries) {
|
|
5062
|
-
const srcPath =
|
|
5063
|
-
const destPath =
|
|
5120
|
+
const srcPath = path21.join(src, entry.name);
|
|
5121
|
+
const destPath = path21.join(dest, entry.name);
|
|
5064
5122
|
if (entry.isDirectory()) {
|
|
5065
5123
|
await copyDir(srcPath, destPath);
|
|
5066
5124
|
} else {
|
|
5067
|
-
await
|
|
5125
|
+
await fs21.copyFile(srcPath, destPath);
|
|
5068
5126
|
}
|
|
5069
5127
|
}
|
|
5070
5128
|
}
|
|
@@ -5075,32 +5133,32 @@ var init_baseline = __esm({
|
|
|
5075
5133
|
});
|
|
5076
5134
|
|
|
5077
5135
|
// src/evolve/trace.ts
|
|
5078
|
-
import
|
|
5079
|
-
import
|
|
5136
|
+
import fs22 from "fs/promises";
|
|
5137
|
+
import path22 from "path";
|
|
5080
5138
|
async function loadTrace(traceDir) {
|
|
5081
|
-
const stdout = await
|
|
5082
|
-
const stderr = await
|
|
5083
|
-
const filesChangedStr = await
|
|
5084
|
-
|
|
5139
|
+
const stdout = await fs22.readFile(path22.join(traceDir, "stdout.log"), "utf-8").catch(() => "");
|
|
5140
|
+
const stderr = await fs22.readFile(path22.join(traceDir, "stderr.log"), "utf-8").catch(() => "");
|
|
5141
|
+
const filesChangedStr = await fs22.readFile(
|
|
5142
|
+
path22.join(traceDir, "files_changed.json"),
|
|
5085
5143
|
"utf-8"
|
|
5086
5144
|
).catch(() => "{}");
|
|
5087
|
-
const timingStr = await
|
|
5088
|
-
|
|
5145
|
+
const timingStr = await fs22.readFile(
|
|
5146
|
+
path22.join(traceDir, "timing.json"),
|
|
5089
5147
|
"utf-8"
|
|
5090
5148
|
).catch(() => "{}");
|
|
5091
|
-
const scoreStr = await
|
|
5092
|
-
|
|
5149
|
+
const scoreStr = await fs22.readFile(
|
|
5150
|
+
path22.join(traceDir, "score.json"),
|
|
5093
5151
|
"utf-8"
|
|
5094
5152
|
).catch(() => '{"pass": false}');
|
|
5095
|
-
const toolCallsStr = await
|
|
5096
|
-
|
|
5153
|
+
const toolCallsStr = await fs22.readFile(
|
|
5154
|
+
path22.join(traceDir, "tool_calls.jsonl"),
|
|
5097
5155
|
"utf-8"
|
|
5098
5156
|
).catch(() => "");
|
|
5099
5157
|
const toolCalls = toolCallsStr.split("\n").filter((line) => line.trim()).map((line) => JSON.parse(line));
|
|
5100
|
-
const parentDir =
|
|
5158
|
+
const parentDir = path22.basename(path22.dirname(traceDir));
|
|
5101
5159
|
const iteration = parseInt(parentDir, 10) || 0;
|
|
5102
5160
|
return {
|
|
5103
|
-
taskId:
|
|
5161
|
+
taskId: path22.basename(traceDir),
|
|
5104
5162
|
iteration,
|
|
5105
5163
|
stdout,
|
|
5106
5164
|
stderr,
|
|
@@ -5111,12 +5169,12 @@ async function loadTrace(traceDir) {
|
|
|
5111
5169
|
};
|
|
5112
5170
|
}
|
|
5113
5171
|
async function loadIterationTraces(workspacePath, iteration) {
|
|
5114
|
-
const tracesDir =
|
|
5172
|
+
const tracesDir = path22.join(workspacePath, "traces", iteration.toString());
|
|
5115
5173
|
const traces = [];
|
|
5116
5174
|
try {
|
|
5117
|
-
const taskDirs = await
|
|
5175
|
+
const taskDirs = await fs22.readdir(tracesDir);
|
|
5118
5176
|
for (const taskId of taskDirs) {
|
|
5119
|
-
const trace = await loadTrace(
|
|
5177
|
+
const trace = await loadTrace(path22.join(tracesDir, taskId));
|
|
5120
5178
|
traces.push(trace);
|
|
5121
5179
|
}
|
|
5122
5180
|
} catch {
|
|
@@ -5124,39 +5182,39 @@ async function loadIterationTraces(workspacePath, iteration) {
|
|
|
5124
5182
|
return traces;
|
|
5125
5183
|
}
|
|
5126
5184
|
async function writeTrace(traceDir, trace) {
|
|
5127
|
-
await
|
|
5128
|
-
await
|
|
5129
|
-
await
|
|
5185
|
+
await fs22.mkdir(traceDir, { recursive: true });
|
|
5186
|
+
await fs22.writeFile(path22.join(traceDir, "stdout.log"), trace.stdout, "utf-8");
|
|
5187
|
+
await fs22.writeFile(path22.join(traceDir, "stderr.log"), trace.stderr, "utf-8");
|
|
5130
5188
|
const toolCallsLines = trace.toolCalls.map((tc) => JSON.stringify(tc)).join("\n");
|
|
5131
|
-
await
|
|
5132
|
-
await
|
|
5133
|
-
|
|
5189
|
+
await fs22.writeFile(path22.join(traceDir, "tool_calls.jsonl"), toolCallsLines, "utf-8");
|
|
5190
|
+
await fs22.writeFile(
|
|
5191
|
+
path22.join(traceDir, "files_changed.json"),
|
|
5134
5192
|
JSON.stringify(trace.filesChanged, null, 2),
|
|
5135
5193
|
"utf-8"
|
|
5136
5194
|
);
|
|
5137
|
-
await
|
|
5138
|
-
|
|
5195
|
+
await fs22.writeFile(
|
|
5196
|
+
path22.join(traceDir, "timing.json"),
|
|
5139
5197
|
JSON.stringify(trace.timing, null, 2),
|
|
5140
5198
|
"utf-8"
|
|
5141
5199
|
);
|
|
5142
|
-
await
|
|
5143
|
-
|
|
5200
|
+
await fs22.writeFile(
|
|
5201
|
+
path22.join(traceDir, "score.json"),
|
|
5144
5202
|
JSON.stringify(trace.score, null, 2),
|
|
5145
5203
|
"utf-8"
|
|
5146
5204
|
);
|
|
5147
5205
|
}
|
|
5148
5206
|
async function writeScore(traceDir, score) {
|
|
5149
|
-
await
|
|
5150
|
-
|
|
5207
|
+
await fs22.writeFile(
|
|
5208
|
+
path22.join(traceDir, "score.json"),
|
|
5151
5209
|
JSON.stringify(score, null, 2),
|
|
5152
5210
|
"utf-8"
|
|
5153
5211
|
);
|
|
5154
5212
|
}
|
|
5155
5213
|
async function writeIterationLog(workspacePath, log) {
|
|
5156
|
-
const iterDir =
|
|
5157
|
-
await
|
|
5158
|
-
await
|
|
5159
|
-
|
|
5214
|
+
const iterDir = path22.join(workspacePath, "iterations", log.iteration.toString());
|
|
5215
|
+
await fs22.mkdir(iterDir, { recursive: true });
|
|
5216
|
+
await fs22.writeFile(
|
|
5217
|
+
path22.join(iterDir, "scores.json"),
|
|
5160
5218
|
JSON.stringify({
|
|
5161
5219
|
score: log.score,
|
|
5162
5220
|
taskResults: log.taskResults,
|
|
@@ -5164,27 +5222,27 @@ async function writeIterationLog(workspacePath, log) {
|
|
|
5164
5222
|
}, null, 2),
|
|
5165
5223
|
"utf-8"
|
|
5166
5224
|
);
|
|
5167
|
-
await
|
|
5168
|
-
|
|
5225
|
+
await fs22.writeFile(
|
|
5226
|
+
path22.join(iterDir, "proposer_reasoning.md"),
|
|
5169
5227
|
log.proposal?.reasoning ?? "Baseline evaluation (no proposal)",
|
|
5170
5228
|
"utf-8"
|
|
5171
5229
|
);
|
|
5172
|
-
await
|
|
5173
|
-
|
|
5230
|
+
await fs22.writeFile(
|
|
5231
|
+
path22.join(iterDir, "mutation_diff.patch"),
|
|
5174
5232
|
log.diffPatch ?? "",
|
|
5175
5233
|
"utf-8"
|
|
5176
5234
|
);
|
|
5177
5235
|
}
|
|
5178
5236
|
async function loadIterationLog(workspacePath, iteration) {
|
|
5179
|
-
const iterDir =
|
|
5237
|
+
const iterDir = path22.join(workspacePath, "iterations", iteration.toString());
|
|
5180
5238
|
try {
|
|
5181
|
-
await
|
|
5239
|
+
await fs22.access(iterDir);
|
|
5182
5240
|
} catch {
|
|
5183
5241
|
return null;
|
|
5184
5242
|
}
|
|
5185
|
-
const scoresStr = await
|
|
5186
|
-
const reasoning = await
|
|
5187
|
-
const diffPatch = await
|
|
5243
|
+
const scoresStr = await fs22.readFile(path22.join(iterDir, "scores.json"), "utf-8").catch(() => "{}");
|
|
5244
|
+
const reasoning = await fs22.readFile(path22.join(iterDir, "proposer_reasoning.md"), "utf-8").catch(() => "");
|
|
5245
|
+
const diffPatch = await fs22.readFile(path22.join(iterDir, "mutation_diff.patch"), "utf-8").catch(() => "");
|
|
5188
5246
|
const scoresData = JSON.parse(scoresStr);
|
|
5189
5247
|
const proposal = reasoning ? { reasoning, mutations: [], expectedImpact: {} } : null;
|
|
5190
5248
|
return {
|
|
@@ -5555,12 +5613,12 @@ Return ONLY valid JSON:
|
|
|
5555
5613
|
// src/evolve/runner.ts
|
|
5556
5614
|
import { exec as exec3, spawn } from "child_process";
|
|
5557
5615
|
import { promisify as promisify3 } from "util";
|
|
5558
|
-
import
|
|
5616
|
+
import fs23 from "fs/promises";
|
|
5559
5617
|
import os4 from "os";
|
|
5560
|
-
import
|
|
5618
|
+
import path23 from "path";
|
|
5561
5619
|
async function deployMcpJson(harnessPath, workDir) {
|
|
5562
|
-
const src =
|
|
5563
|
-
await
|
|
5620
|
+
const src = path23.join(harnessPath, ".mcp.json");
|
|
5621
|
+
await fs23.copyFile(src, path23.join(workDir, ".mcp.json")).catch(() => {
|
|
5564
5622
|
});
|
|
5565
5623
|
}
|
|
5566
5624
|
async function createIsolatedWorkspace(projectRoot, harnessPath) {
|
|
@@ -5570,40 +5628,40 @@ async function createIsolatedWorkspace(projectRoot, harnessPath) {
|
|
|
5570
5628
|
cwd: projectRoot,
|
|
5571
5629
|
timeout: 5e3
|
|
5572
5630
|
});
|
|
5573
|
-
const tmpDir2 =
|
|
5631
|
+
const tmpDir2 = path23.join(os4.tmpdir(), `kairn-evolve-wt-${suffix}`);
|
|
5574
5632
|
await execAsync3(`git worktree add --detach "${tmpDir2}" HEAD`, {
|
|
5575
5633
|
cwd: projectRoot,
|
|
5576
5634
|
timeout: 3e4
|
|
5577
5635
|
});
|
|
5578
|
-
await
|
|
5579
|
-
await copyDir(harnessPath,
|
|
5636
|
+
await fs23.rm(path23.join(tmpDir2, ".claude"), { recursive: true, force: true });
|
|
5637
|
+
await copyDir(harnessPath, path23.join(tmpDir2, ".claude"));
|
|
5580
5638
|
await deployMcpJson(harnessPath, tmpDir2);
|
|
5581
5639
|
return { workDir: tmpDir2, isWorktree: true };
|
|
5582
5640
|
} catch {
|
|
5583
5641
|
}
|
|
5584
|
-
const tmpDir = await
|
|
5642
|
+
const tmpDir = await fs23.mkdtemp(path23.join(os4.tmpdir(), `kairn-evolve-cp-`));
|
|
5585
5643
|
await copyProjectDir(projectRoot, tmpDir);
|
|
5586
|
-
await
|
|
5587
|
-
await copyDir(harnessPath,
|
|
5644
|
+
await fs23.rm(path23.join(tmpDir, ".claude"), { recursive: true, force: true });
|
|
5645
|
+
await copyDir(harnessPath, path23.join(tmpDir, ".claude"));
|
|
5588
5646
|
await deployMcpJson(harnessPath, tmpDir);
|
|
5589
5647
|
return { workDir: tmpDir, isWorktree: false };
|
|
5590
5648
|
}
|
|
5591
5649
|
async function copyProjectDir(src, dest) {
|
|
5592
|
-
await
|
|
5650
|
+
await fs23.mkdir(dest, { recursive: true });
|
|
5593
5651
|
let entries;
|
|
5594
5652
|
try {
|
|
5595
|
-
entries = await
|
|
5653
|
+
entries = await fs23.readdir(src, { withFileTypes: true });
|
|
5596
5654
|
} catch {
|
|
5597
5655
|
return;
|
|
5598
5656
|
}
|
|
5599
5657
|
for (const entry of entries) {
|
|
5600
5658
|
if (COPY_SKIP_DIRS.has(entry.name)) continue;
|
|
5601
|
-
const srcPath =
|
|
5602
|
-
const destPath =
|
|
5659
|
+
const srcPath = path23.join(src, entry.name);
|
|
5660
|
+
const destPath = path23.join(dest, entry.name);
|
|
5603
5661
|
if (entry.isDirectory()) {
|
|
5604
5662
|
await copyDir(srcPath, destPath);
|
|
5605
5663
|
} else {
|
|
5606
|
-
await
|
|
5664
|
+
await fs23.copyFile(srcPath, destPath);
|
|
5607
5665
|
}
|
|
5608
5666
|
}
|
|
5609
5667
|
}
|
|
@@ -5615,7 +5673,7 @@ async function cleanupIsolatedWorkspace(workDir, isWorktree, projectRoot) {
|
|
|
5615
5673
|
timeout: 1e4
|
|
5616
5674
|
});
|
|
5617
5675
|
} catch {
|
|
5618
|
-
await
|
|
5676
|
+
await fs23.rm(workDir, { recursive: true, force: true }).catch(() => {
|
|
5619
5677
|
});
|
|
5620
5678
|
await execAsync3("git worktree prune", {
|
|
5621
5679
|
cwd: projectRoot,
|
|
@@ -5624,12 +5682,12 @@ async function cleanupIsolatedWorkspace(workDir, isWorktree, projectRoot) {
|
|
|
5624
5682
|
});
|
|
5625
5683
|
}
|
|
5626
5684
|
} else {
|
|
5627
|
-
await
|
|
5685
|
+
await fs23.rm(workDir, { recursive: true, force: true }).catch(() => {
|
|
5628
5686
|
});
|
|
5629
5687
|
}
|
|
5630
5688
|
}
|
|
5631
5689
|
async function runTask(task, harnessPath, traceDir, iteration, projectRoot) {
|
|
5632
|
-
await
|
|
5690
|
+
await fs23.mkdir(traceDir, { recursive: true });
|
|
5633
5691
|
const startedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
5634
5692
|
const startMs = Date.now();
|
|
5635
5693
|
const root = projectRoot ?? process.cwd();
|
|
@@ -5709,13 +5767,13 @@ async function snapshotFileList(dir) {
|
|
|
5709
5767
|
async function walk(current) {
|
|
5710
5768
|
let entries;
|
|
5711
5769
|
try {
|
|
5712
|
-
entries = await
|
|
5770
|
+
entries = await fs23.readdir(current, { withFileTypes: true });
|
|
5713
5771
|
} catch {
|
|
5714
5772
|
return;
|
|
5715
5773
|
}
|
|
5716
5774
|
for (const entry of entries) {
|
|
5717
|
-
const fullPath =
|
|
5718
|
-
const relativePath =
|
|
5775
|
+
const fullPath = path23.join(current, entry.name);
|
|
5776
|
+
const relativePath = path23.relative(dir, fullPath);
|
|
5719
5777
|
if (relativePath.startsWith(".claude")) continue;
|
|
5720
5778
|
if (relativePath.startsWith("node_modules")) continue;
|
|
5721
5779
|
if (relativePath.startsWith(".git")) continue;
|
|
@@ -5723,7 +5781,7 @@ async function snapshotFileList(dir) {
|
|
|
5723
5781
|
await walk(fullPath);
|
|
5724
5782
|
} else {
|
|
5725
5783
|
try {
|
|
5726
|
-
const stat = await
|
|
5784
|
+
const stat = await fs23.stat(fullPath);
|
|
5727
5785
|
result[relativePath] = stat.mtimeMs;
|
|
5728
5786
|
} catch {
|
|
5729
5787
|
}
|
|
@@ -5802,7 +5860,7 @@ function computeStddev(values, mean) {
|
|
|
5802
5860
|
}
|
|
5803
5861
|
async function evaluateAll(tasks, harnessPath, workspacePath, iteration, config, onProgress, runsPerTask = 1, parallelTasks = 1) {
|
|
5804
5862
|
const results = {};
|
|
5805
|
-
const projectRoot =
|
|
5863
|
+
const projectRoot = path23.resolve(workspacePath, "..");
|
|
5806
5864
|
const effectiveRuns = Math.max(1, runsPerTask);
|
|
5807
5865
|
const concurrency = Math.max(1, parallelTasks);
|
|
5808
5866
|
const evaluateTask = async (task) => {
|
|
@@ -5812,7 +5870,7 @@ async function evaluateAll(tasks, harnessPath, workspacePath, iteration, config,
|
|
|
5812
5870
|
const runScores = [];
|
|
5813
5871
|
let passCount = 0;
|
|
5814
5872
|
for (let run = 0; run < effectiveRuns; run++) {
|
|
5815
|
-
const traceDir =
|
|
5873
|
+
const traceDir = path23.join(
|
|
5816
5874
|
workspacePath,
|
|
5817
5875
|
"traces",
|
|
5818
5876
|
iteration.toString(),
|
|
@@ -5825,8 +5883,8 @@ async function evaluateAll(tasks, harnessPath, workspacePath, iteration, config,
|
|
|
5825
5883
|
message: `Run ${run + 1}/${effectiveRuns} of ${task.id}`
|
|
5826
5884
|
});
|
|
5827
5885
|
await runTask(task, harnessPath, traceDir, iteration, projectRoot);
|
|
5828
|
-
const stdout = await
|
|
5829
|
-
const stderr = await
|
|
5886
|
+
const stdout = await fs23.readFile(path23.join(traceDir, "stdout.log"), "utf-8").catch(() => "");
|
|
5887
|
+
const stderr = await fs23.readFile(path23.join(traceDir, "stderr.log"), "utf-8").catch(() => "");
|
|
5830
5888
|
const score = await scoreTask(task, traceDir, stdout, stderr, config);
|
|
5831
5889
|
await writeScore(traceDir, score);
|
|
5832
5890
|
runScores.push(score.score ?? (score.pass ? 100 : 0));
|
|
@@ -5846,7 +5904,7 @@ async function evaluateAll(tasks, harnessPath, workspacePath, iteration, config,
|
|
|
5846
5904
|
}
|
|
5847
5905
|
};
|
|
5848
5906
|
} else {
|
|
5849
|
-
const traceDir =
|
|
5907
|
+
const traceDir = path23.join(
|
|
5850
5908
|
workspacePath,
|
|
5851
5909
|
"traces",
|
|
5852
5910
|
iteration.toString(),
|
|
@@ -5855,8 +5913,8 @@ async function evaluateAll(tasks, harnessPath, workspacePath, iteration, config,
|
|
|
5855
5913
|
const taskResult = await runTask(task, harnessPath, traceDir, iteration, projectRoot);
|
|
5856
5914
|
finalScore = taskResult.score;
|
|
5857
5915
|
if (config) {
|
|
5858
|
-
const stdout = await
|
|
5859
|
-
const stderr = await
|
|
5916
|
+
const stdout = await fs23.readFile(path23.join(traceDir, "stdout.log"), "utf-8").catch(() => "");
|
|
5917
|
+
const stderr = await fs23.readFile(path23.join(traceDir, "stderr.log"), "utf-8").catch(() => "");
|
|
5860
5918
|
finalScore = await scoreTask(task, traceDir, stdout, stderr, config);
|
|
5861
5919
|
await writeScore(traceDir, finalScore);
|
|
5862
5920
|
}
|
|
@@ -5904,12 +5962,12 @@ __export(memory_exports, {
|
|
|
5904
5962
|
loadProposerMemory: () => loadProposerMemory,
|
|
5905
5963
|
saveRunSummary: () => saveRunSummary
|
|
5906
5964
|
});
|
|
5907
|
-
import
|
|
5908
|
-
import
|
|
5965
|
+
import fs24 from "fs/promises";
|
|
5966
|
+
import path24 from "path";
|
|
5909
5967
|
async function loadProposerMemory(workspacePath) {
|
|
5910
|
-
const memoryPath =
|
|
5968
|
+
const memoryPath = path24.join(workspacePath, MEMORY_FILE);
|
|
5911
5969
|
try {
|
|
5912
|
-
const raw = await
|
|
5970
|
+
const raw = await fs24.readFile(memoryPath, "utf-8");
|
|
5913
5971
|
const parsed = JSON.parse(raw);
|
|
5914
5972
|
if (Array.isArray(parsed)) return parsed;
|
|
5915
5973
|
return [];
|
|
@@ -5948,8 +6006,8 @@ async function saveRunSummary(workspacePath, summary) {
|
|
|
5948
6006
|
const existing = await loadProposerMemory(workspacePath);
|
|
5949
6007
|
existing.push(summary);
|
|
5950
6008
|
const trimmed = existing.slice(-MAX_ENTRIES);
|
|
5951
|
-
const memoryPath =
|
|
5952
|
-
await
|
|
6009
|
+
const memoryPath = path24.join(workspacePath, MEMORY_FILE);
|
|
6010
|
+
await fs24.writeFile(memoryPath, JSON.stringify(trimmed, null, 2), "utf-8");
|
|
5953
6011
|
}
|
|
5954
6012
|
function formatMemoryForProposer(memory) {
|
|
5955
6013
|
if (memory.length === 0) return "";
|
|
@@ -5994,26 +6052,26 @@ __export(knowledge_exports, {
|
|
|
5994
6052
|
savePattern: () => savePattern,
|
|
5995
6053
|
saveProjectHistory: () => saveProjectHistory
|
|
5996
6054
|
});
|
|
5997
|
-
import
|
|
5998
|
-
import
|
|
6055
|
+
import fs25 from "fs/promises";
|
|
6056
|
+
import path25 from "path";
|
|
5999
6057
|
import crypto2 from "crypto";
|
|
6000
6058
|
function getKnowledgeDir() {
|
|
6001
|
-
return
|
|
6059
|
+
return path25.join(getKairnDir(), "knowledge");
|
|
6002
6060
|
}
|
|
6003
6061
|
function getPatternsPath() {
|
|
6004
|
-
return
|
|
6062
|
+
return path25.join(getKnowledgeDir(), "patterns.jsonl");
|
|
6005
6063
|
}
|
|
6006
6064
|
function getProjectsDir() {
|
|
6007
|
-
return
|
|
6065
|
+
return path25.join(getKnowledgeDir(), "projects");
|
|
6008
6066
|
}
|
|
6009
6067
|
function getConvergencePath() {
|
|
6010
|
-
return
|
|
6068
|
+
return path25.join(getKnowledgeDir(), "convergence.json");
|
|
6011
6069
|
}
|
|
6012
6070
|
async function loadKnowledgeBase(filter) {
|
|
6013
6071
|
const patternsPath = getPatternsPath();
|
|
6014
6072
|
let content;
|
|
6015
6073
|
try {
|
|
6016
|
-
content = await
|
|
6074
|
+
content = await fs25.readFile(patternsPath, "utf-8");
|
|
6017
6075
|
} catch {
|
|
6018
6076
|
return [];
|
|
6019
6077
|
}
|
|
@@ -6033,9 +6091,9 @@ async function loadKnowledgeBase(filter) {
|
|
|
6033
6091
|
}
|
|
6034
6092
|
async function savePattern(pattern) {
|
|
6035
6093
|
const dir = getKnowledgeDir();
|
|
6036
|
-
await
|
|
6094
|
+
await fs25.mkdir(dir, { recursive: true });
|
|
6037
6095
|
const patternsPath = getPatternsPath();
|
|
6038
|
-
await
|
|
6096
|
+
await fs25.appendFile(patternsPath, JSON.stringify(pattern) + "\n", "utf-8");
|
|
6039
6097
|
}
|
|
6040
6098
|
async function extractAndSavePatterns(history, projectName, language) {
|
|
6041
6099
|
const patterns = [];
|
|
@@ -6107,13 +6165,13 @@ function formatKnowledgeForArchitect(patterns, language) {
|
|
|
6107
6165
|
}
|
|
6108
6166
|
async function saveProjectHistory(projectName, summary) {
|
|
6109
6167
|
const projectsDir = getProjectsDir();
|
|
6110
|
-
await
|
|
6111
|
-
const filePath =
|
|
6112
|
-
await
|
|
6168
|
+
await fs25.mkdir(projectsDir, { recursive: true });
|
|
6169
|
+
const filePath = path25.join(projectsDir, `${projectName}.json`);
|
|
6170
|
+
await fs25.writeFile(filePath, JSON.stringify(summary, null, 2), "utf-8");
|
|
6113
6171
|
}
|
|
6114
6172
|
async function loadConvergence() {
|
|
6115
6173
|
try {
|
|
6116
|
-
const content = await
|
|
6174
|
+
const content = await fs25.readFile(getConvergencePath(), "utf-8");
|
|
6117
6175
|
return JSON.parse(content);
|
|
6118
6176
|
} catch {
|
|
6119
6177
|
return null;
|
|
@@ -6121,8 +6179,8 @@ async function loadConvergence() {
|
|
|
6121
6179
|
}
|
|
6122
6180
|
async function saveConvergence(convergence) {
|
|
6123
6181
|
const dir = getKnowledgeDir();
|
|
6124
|
-
await
|
|
6125
|
-
await
|
|
6182
|
+
await fs25.mkdir(dir, { recursive: true });
|
|
6183
|
+
await fs25.writeFile(
|
|
6126
6184
|
getConvergencePath(),
|
|
6127
6185
|
JSON.stringify(convergence, null, 2),
|
|
6128
6186
|
"utf-8"
|
|
@@ -6136,25 +6194,112 @@ var init_knowledge = __esm({
|
|
|
6136
6194
|
});
|
|
6137
6195
|
|
|
6138
6196
|
// src/evolve/proposer.ts
|
|
6139
|
-
import
|
|
6140
|
-
import
|
|
6197
|
+
import fs26 from "fs/promises";
|
|
6198
|
+
import path26 from "path";
|
|
6199
|
+
function countSettingsHooks(settings) {
|
|
6200
|
+
let count = 0;
|
|
6201
|
+
const categories = settings.hooks;
|
|
6202
|
+
for (const key of Object.keys(categories)) {
|
|
6203
|
+
const entries = categories[key];
|
|
6204
|
+
if (entries) {
|
|
6205
|
+
count += entries.length;
|
|
6206
|
+
}
|
|
6207
|
+
}
|
|
6208
|
+
return count;
|
|
6209
|
+
}
|
|
6210
|
+
function buildIRSummary(ir) {
|
|
6211
|
+
const lines = [];
|
|
6212
|
+
lines.push("## Harness Structure (IR)");
|
|
6213
|
+
const meta = ir.meta;
|
|
6214
|
+
const techParts = [meta.techStack.language];
|
|
6215
|
+
if (meta.techStack.framework) techParts.push(meta.techStack.framework);
|
|
6216
|
+
if (meta.techStack.buildTool) techParts.push(meta.techStack.buildTool);
|
|
6217
|
+
lines.push(`Project: ${meta.name || "(unnamed)"} \u2014 ${techParts.join(", ")}`);
|
|
6218
|
+
const sectionIds = ir.sections.map((s) => s.id);
|
|
6219
|
+
lines.push(`Sections (${ir.sections.length}): ${sectionIds.join(", ") || "none"}`);
|
|
6220
|
+
const commandSummaries = ir.commands.map((c) => c.name);
|
|
6221
|
+
lines.push(`Commands (${ir.commands.length}): ${commandSummaries.join(", ") || "none"}`);
|
|
6222
|
+
const ruleSummaries = ir.rules.map((r) => {
|
|
6223
|
+
const pathHint = r.paths && r.paths.length > 0 ? ` (${r.paths.join(", ")})` : "";
|
|
6224
|
+
return `${r.name}${pathHint}`;
|
|
6225
|
+
});
|
|
6226
|
+
lines.push(`Rules (${ir.rules.length}): ${ruleSummaries.join(", ") || "none"}`);
|
|
6227
|
+
const agentNames = ir.agents.map((a) => a.name);
|
|
6228
|
+
lines.push(`Agents (${ir.agents.length}): ${agentNames.join(", ") || "none"}`);
|
|
6229
|
+
const serverNames = ir.mcpServers.map((s) => s.id);
|
|
6230
|
+
lines.push(`MCP Servers (${ir.mcpServers.length}): ${serverNames.join(", ") || "none"}`);
|
|
6231
|
+
if (ir.skills.length > 0) {
|
|
6232
|
+
const skillNames = ir.skills.map((s) => s.name);
|
|
6233
|
+
lines.push(`Skills (${ir.skills.length}): ${skillNames.join(", ")}`);
|
|
6234
|
+
}
|
|
6235
|
+
if (ir.docs.length > 0) {
|
|
6236
|
+
const docNames = ir.docs.map((d) => d.name);
|
|
6237
|
+
lines.push(`Docs (${ir.docs.length}): ${docNames.join(", ")}`);
|
|
6238
|
+
}
|
|
6239
|
+
if (ir.hooks.length > 0) {
|
|
6240
|
+
const hookNames = ir.hooks.map((h) => h.name);
|
|
6241
|
+
lines.push(`Hooks (${ir.hooks.length}): ${hookNames.join(", ")}`);
|
|
6242
|
+
}
|
|
6243
|
+
const settingsParts = [];
|
|
6244
|
+
if (ir.settings.statusLine) {
|
|
6245
|
+
settingsParts.push("statusLine=enabled");
|
|
6246
|
+
}
|
|
6247
|
+
if (ir.settings.denyPatterns && ir.settings.denyPatterns.length > 0) {
|
|
6248
|
+
settingsParts.push(`denyPatterns=${ir.settings.denyPatterns.length}`);
|
|
6249
|
+
}
|
|
6250
|
+
const hooksCount = countSettingsHooks(ir.settings);
|
|
6251
|
+
if (hooksCount > 0) {
|
|
6252
|
+
settingsParts.push(`hooks=${hooksCount}`);
|
|
6253
|
+
}
|
|
6254
|
+
lines.push(`Settings: ${settingsParts.join(", ") || "default"}`);
|
|
6255
|
+
return lines.join("\n");
|
|
6256
|
+
}
|
|
6257
|
+
function formatAnalysisForProposer(analysis) {
|
|
6258
|
+
const lines = [];
|
|
6259
|
+
lines.push(`Purpose: ${analysis.purpose}`);
|
|
6260
|
+
lines.push(`Domain: ${analysis.domain}`);
|
|
6261
|
+
lines.push(`Architecture: ${analysis.architecture_style}`);
|
|
6262
|
+
lines.push(`Deployment: ${analysis.deployment_model}`);
|
|
6263
|
+
if (analysis.key_modules.length > 0) {
|
|
6264
|
+
lines.push("");
|
|
6265
|
+
lines.push("Key Modules:");
|
|
6266
|
+
for (const mod of analysis.key_modules) {
|
|
6267
|
+
lines.push(`- ${mod.name} (${mod.path}): ${mod.description}`);
|
|
6268
|
+
}
|
|
6269
|
+
}
|
|
6270
|
+
if (analysis.workflows.length > 0) {
|
|
6271
|
+
lines.push("");
|
|
6272
|
+
lines.push("Workflows:");
|
|
6273
|
+
for (const wf of analysis.workflows) {
|
|
6274
|
+
lines.push(`- ${wf.name}: ${wf.description} (trigger: ${wf.trigger})`);
|
|
6275
|
+
}
|
|
6276
|
+
}
|
|
6277
|
+
if (analysis.config_keys.length > 0) {
|
|
6278
|
+
lines.push("");
|
|
6279
|
+
lines.push("Config Keys:");
|
|
6280
|
+
for (const key of analysis.config_keys) {
|
|
6281
|
+
lines.push(`- ${key.name}: ${key.purpose}`);
|
|
6282
|
+
}
|
|
6283
|
+
}
|
|
6284
|
+
return lines.join("\n");
|
|
6285
|
+
}
|
|
6141
6286
|
async function readHarnessFiles(harnessPath) {
|
|
6142
6287
|
const result = {};
|
|
6143
6288
|
async function walk(dir, prefix) {
|
|
6144
6289
|
let entries;
|
|
6145
6290
|
try {
|
|
6146
|
-
entries = await
|
|
6291
|
+
entries = await fs26.readdir(dir, { withFileTypes: true });
|
|
6147
6292
|
} catch {
|
|
6148
6293
|
return;
|
|
6149
6294
|
}
|
|
6150
6295
|
for (const entry of entries) {
|
|
6151
|
-
const relativePath = prefix ?
|
|
6152
|
-
const fullPath =
|
|
6296
|
+
const relativePath = prefix ? path26.join(prefix, entry.name) : entry.name;
|
|
6297
|
+
const fullPath = path26.join(dir, entry.name);
|
|
6153
6298
|
if (entry.isDirectory()) {
|
|
6154
6299
|
await walk(fullPath, relativePath);
|
|
6155
6300
|
} else if (entry.isFile()) {
|
|
6156
6301
|
try {
|
|
6157
|
-
result[relativePath] = await
|
|
6302
|
+
result[relativePath] = await fs26.readFile(fullPath, "utf-8");
|
|
6158
6303
|
} catch {
|
|
6159
6304
|
}
|
|
6160
6305
|
}
|
|
@@ -6170,7 +6315,7 @@ function truncateStdout(stdout, limit) {
|
|
|
6170
6315
|
return `[...truncated, showing last ${limit} chars...]
|
|
6171
6316
|
${stdout.slice(-limit)}`;
|
|
6172
6317
|
}
|
|
6173
|
-
function buildProposerUserMessage(harnessFiles, traces, tasks, history, memorySection) {
|
|
6318
|
+
function buildProposerUserMessage(harnessFiles, traces, tasks, history, memorySection, projectContext) {
|
|
6174
6319
|
const harnessSection = ["## Current Harness Files\n"];
|
|
6175
6320
|
const fileEntries = Object.entries(harnessFiles);
|
|
6176
6321
|
if (fileEntries.length === 0) {
|
|
@@ -6197,7 +6342,24 @@ ${content}
|
|
|
6197
6342
|
);
|
|
6198
6343
|
}
|
|
6199
6344
|
}
|
|
6200
|
-
|
|
6345
|
+
let projectSection = "";
|
|
6346
|
+
if (projectContext) {
|
|
6347
|
+
const parts = ["## Project Understanding\n"];
|
|
6348
|
+
parts.push("### Analysis Summary");
|
|
6349
|
+
parts.push(formatAnalysisForProposer(projectContext.analysis));
|
|
6350
|
+
parts.push("");
|
|
6351
|
+
parts.push("### Harness Structure");
|
|
6352
|
+
parts.push(projectContext.irSummary);
|
|
6353
|
+
if (projectContext.keySourceFiles) {
|
|
6354
|
+
parts.push("");
|
|
6355
|
+
parts.push("### Key Source Files");
|
|
6356
|
+
parts.push("```");
|
|
6357
|
+
parts.push(projectContext.keySourceFiles);
|
|
6358
|
+
parts.push("```");
|
|
6359
|
+
}
|
|
6360
|
+
projectSection = "\n" + parts.join("\n") + "\n";
|
|
6361
|
+
}
|
|
6362
|
+
const fixedContent = harnessSection.join("\n") + "\n" + taskSection.join("\n") + projectSection;
|
|
6201
6363
|
const remainingBudget = MAX_CONTEXT_CHARS - fixedContent.length;
|
|
6202
6364
|
if (remainingBudget <= 0) {
|
|
6203
6365
|
return fixedContent + "\n\n[...traces and history omitted \u2014 harness + tasks fill context budget...]";
|
|
@@ -6359,7 +6521,7 @@ function parseProposerResponse(raw) {
|
|
|
6359
6521
|
expectedImpact
|
|
6360
6522
|
};
|
|
6361
6523
|
}
|
|
6362
|
-
async function propose(iteration, workspacePath, harnessPath, history, tasks, config, proposerModel) {
|
|
6524
|
+
async function propose(iteration, workspacePath, harnessPath, history, tasks, config, proposerModel, projectContext) {
|
|
6363
6525
|
const harnessFiles = await readHarnessFiles(harnessPath);
|
|
6364
6526
|
const traces = await loadIterationTraces(workspacePath, iteration);
|
|
6365
6527
|
const { loadProposerMemory: loadProposerMemory2, formatMemoryForProposer: formatMemoryForProposer2 } = await Promise.resolve().then(() => (init_memory(), memory_exports));
|
|
@@ -6373,7 +6535,7 @@ async function propose(iteration, workspacePath, harnessPath, history, tasks, co
|
|
|
6373
6535
|
} catch {
|
|
6374
6536
|
}
|
|
6375
6537
|
const combinedMemory = memorySection + (knowledgeSection ? "\n" + knowledgeSection : "");
|
|
6376
|
-
const userMessage = buildProposerUserMessage(harnessFiles, traces, tasks, history, combinedMemory || void 0);
|
|
6538
|
+
const userMessage = buildProposerUserMessage(harnessFiles, traces, tasks, history, combinedMemory || void 0, projectContext);
|
|
6377
6539
|
const proposerConfig = { ...config, model: proposerModel };
|
|
6378
6540
|
const response = await callLLM(proposerConfig, userMessage, {
|
|
6379
6541
|
systemPrompt: PROPOSER_SYSTEM_PROMPT,
|
|
@@ -6460,8 +6622,8 @@ Return ONLY valid JSON.`;
|
|
|
6460
6622
|
});
|
|
6461
6623
|
|
|
6462
6624
|
// src/ir/parser.ts
|
|
6463
|
-
import
|
|
6464
|
-
import
|
|
6625
|
+
import fs27 from "fs/promises";
|
|
6626
|
+
import path27 from "path";
|
|
6465
6627
|
function slugify(heading) {
|
|
6466
6628
|
return heading.toLowerCase().trim().replace(/\s+/g, "-").replace(/[^a-z0-9-]/g, "").replace(/-+/g, "-").replace(/^-|-$/g, "");
|
|
6467
6629
|
}
|
|
@@ -6723,33 +6885,33 @@ function parseMcpConfig(content) {
|
|
|
6723
6885
|
}
|
|
6724
6886
|
async function readDirSafe(dirPath) {
|
|
6725
6887
|
try {
|
|
6726
|
-
return await
|
|
6888
|
+
return await fs27.readdir(dirPath);
|
|
6727
6889
|
} catch {
|
|
6728
6890
|
return [];
|
|
6729
6891
|
}
|
|
6730
6892
|
}
|
|
6731
6893
|
async function readFileSafe2(filePath) {
|
|
6732
6894
|
try {
|
|
6733
|
-
return await
|
|
6895
|
+
return await fs27.readFile(filePath, "utf-8");
|
|
6734
6896
|
} catch {
|
|
6735
6897
|
return null;
|
|
6736
6898
|
}
|
|
6737
6899
|
}
|
|
6738
6900
|
async function isDirectory(filePath) {
|
|
6739
6901
|
try {
|
|
6740
|
-
const stat = await
|
|
6902
|
+
const stat = await fs27.stat(filePath);
|
|
6741
6903
|
return stat.isDirectory();
|
|
6742
6904
|
} catch {
|
|
6743
6905
|
return false;
|
|
6744
6906
|
}
|
|
6745
6907
|
}
|
|
6746
6908
|
async function parseCommands(harnessPath) {
|
|
6747
|
-
const dirPath =
|
|
6909
|
+
const dirPath = path27.join(harnessPath, "commands");
|
|
6748
6910
|
const entries = await readDirSafe(dirPath);
|
|
6749
6911
|
const nodes = [];
|
|
6750
6912
|
for (const entry of entries) {
|
|
6751
6913
|
if (!entry.endsWith(".md")) continue;
|
|
6752
|
-
const filePath =
|
|
6914
|
+
const filePath = path27.join(dirPath, entry);
|
|
6753
6915
|
const content = await readFileSafe2(filePath);
|
|
6754
6916
|
if (content === null) continue;
|
|
6755
6917
|
const name = entry.replace(/\.md$/, "");
|
|
@@ -6760,12 +6922,12 @@ async function parseCommands(harnessPath) {
|
|
|
6760
6922
|
return nodes;
|
|
6761
6923
|
}
|
|
6762
6924
|
async function parseRules(harnessPath) {
|
|
6763
|
-
const dirPath =
|
|
6925
|
+
const dirPath = path27.join(harnessPath, "rules");
|
|
6764
6926
|
const entries = await readDirSafe(dirPath);
|
|
6765
6927
|
const nodes = [];
|
|
6766
6928
|
for (const entry of entries) {
|
|
6767
6929
|
if (!entry.endsWith(".md")) continue;
|
|
6768
|
-
const filePath =
|
|
6930
|
+
const filePath = path27.join(dirPath, entry);
|
|
6769
6931
|
const rawContent = await readFileSafe2(filePath);
|
|
6770
6932
|
if (rawContent === null) continue;
|
|
6771
6933
|
const name = entry.replace(/\.md$/, "");
|
|
@@ -6780,12 +6942,12 @@ async function parseRules(harnessPath) {
|
|
|
6780
6942
|
return nodes;
|
|
6781
6943
|
}
|
|
6782
6944
|
async function parseAgents(harnessPath) {
|
|
6783
|
-
const dirPath =
|
|
6945
|
+
const dirPath = path27.join(harnessPath, "agents");
|
|
6784
6946
|
const entries = await readDirSafe(dirPath);
|
|
6785
6947
|
const nodes = [];
|
|
6786
6948
|
for (const entry of entries) {
|
|
6787
6949
|
if (!entry.endsWith(".md")) continue;
|
|
6788
|
-
const filePath =
|
|
6950
|
+
const filePath = path27.join(dirPath, entry);
|
|
6789
6951
|
const rawContent = await readFileSafe2(filePath);
|
|
6790
6952
|
if (rawContent === null) continue;
|
|
6791
6953
|
const fileBaseName = entry.replace(/\.md$/, "");
|
|
@@ -6829,20 +6991,20 @@ async function parseAgents(harnessPath) {
|
|
|
6829
6991
|
return nodes;
|
|
6830
6992
|
}
|
|
6831
6993
|
async function parseSkills(harnessPath) {
|
|
6832
|
-
const dirPath =
|
|
6994
|
+
const dirPath = path27.join(harnessPath, "skills");
|
|
6833
6995
|
const entries = await readDirSafe(dirPath);
|
|
6834
6996
|
const nodes = [];
|
|
6835
6997
|
for (const entry of entries) {
|
|
6836
|
-
const entryPath =
|
|
6998
|
+
const entryPath = path27.join(dirPath, entry);
|
|
6837
6999
|
if (entry.endsWith(".md")) {
|
|
6838
7000
|
const content = await readFileSafe2(entryPath);
|
|
6839
7001
|
if (content === null) continue;
|
|
6840
7002
|
const name = entry.replace(/\.md$/, "");
|
|
6841
7003
|
nodes.push({ name, content });
|
|
6842
7004
|
} else if (await isDirectory(entryPath)) {
|
|
6843
|
-
let content = await readFileSafe2(
|
|
7005
|
+
let content = await readFileSafe2(path27.join(entryPath, "skill.md"));
|
|
6844
7006
|
if (content === null) {
|
|
6845
|
-
content = await readFileSafe2(
|
|
7007
|
+
content = await readFileSafe2(path27.join(entryPath, "SKILL.md"));
|
|
6846
7008
|
}
|
|
6847
7009
|
if (content === null) continue;
|
|
6848
7010
|
nodes.push({ name: entry, content });
|
|
@@ -6851,12 +7013,12 @@ async function parseSkills(harnessPath) {
|
|
|
6851
7013
|
return nodes;
|
|
6852
7014
|
}
|
|
6853
7015
|
async function parseDocs(harnessPath) {
|
|
6854
|
-
const dirPath =
|
|
7016
|
+
const dirPath = path27.join(harnessPath, "docs");
|
|
6855
7017
|
const entries = await readDirSafe(dirPath);
|
|
6856
7018
|
const nodes = [];
|
|
6857
7019
|
for (const entry of entries) {
|
|
6858
7020
|
if (!entry.endsWith(".md")) continue;
|
|
6859
|
-
const filePath =
|
|
7021
|
+
const filePath = path27.join(dirPath, entry);
|
|
6860
7022
|
const content = await readFileSafe2(filePath);
|
|
6861
7023
|
if (content === null) continue;
|
|
6862
7024
|
const name = entry.replace(/\.md$/, "");
|
|
@@ -6865,12 +7027,12 @@ async function parseDocs(harnessPath) {
|
|
|
6865
7027
|
return nodes;
|
|
6866
7028
|
}
|
|
6867
7029
|
async function parseHooks(harnessPath) {
|
|
6868
|
-
const dirPath =
|
|
7030
|
+
const dirPath = path27.join(harnessPath, "hooks");
|
|
6869
7031
|
const entries = await readDirSafe(dirPath);
|
|
6870
7032
|
const nodes = [];
|
|
6871
7033
|
for (const entry of entries) {
|
|
6872
7034
|
if (!entry.endsWith(".mjs")) continue;
|
|
6873
|
-
const filePath =
|
|
7035
|
+
const filePath = path27.join(dirPath, entry);
|
|
6874
7036
|
const content = await readFileSafe2(filePath);
|
|
6875
7037
|
if (content === null) continue;
|
|
6876
7038
|
const name = entry.replace(/\.mjs$/, "");
|
|
@@ -6881,7 +7043,7 @@ async function parseHooks(harnessPath) {
|
|
|
6881
7043
|
async function parseHarness(harnessPath) {
|
|
6882
7044
|
const ir = createEmptyIR();
|
|
6883
7045
|
const claudeMdContent = await readFileSafe2(
|
|
6884
|
-
|
|
7046
|
+
path27.join(harnessPath, "CLAUDE.md")
|
|
6885
7047
|
);
|
|
6886
7048
|
if (claudeMdContent !== null) {
|
|
6887
7049
|
const { meta, sections } = parseClaudeMd(claudeMdContent);
|
|
@@ -6893,7 +7055,7 @@ async function parseHarness(harnessPath) {
|
|
|
6893
7055
|
ir.sections = sections;
|
|
6894
7056
|
}
|
|
6895
7057
|
const settingsContent = await readFileSafe2(
|
|
6896
|
-
|
|
7058
|
+
path27.join(harnessPath, "settings.json")
|
|
6897
7059
|
);
|
|
6898
7060
|
if (settingsContent !== null) {
|
|
6899
7061
|
ir.settings = parseSettings(settingsContent);
|
|
@@ -6914,7 +7076,7 @@ async function parseHarness(harnessPath) {
|
|
|
6914
7076
|
ir.hooks = hooks;
|
|
6915
7077
|
const mcpServers = [];
|
|
6916
7078
|
const seenIds = /* @__PURE__ */ new Set();
|
|
6917
|
-
const parentMcpPath =
|
|
7079
|
+
const parentMcpPath = path27.join(path27.dirname(harnessPath), ".mcp.json");
|
|
6918
7080
|
const parentMcpContent = await readFileSafe2(parentMcpPath);
|
|
6919
7081
|
if (parentMcpContent !== null) {
|
|
6920
7082
|
for (const node of parseMcpConfig(parentMcpContent)) {
|
|
@@ -6924,7 +7086,7 @@ async function parseHarness(harnessPath) {
|
|
|
6924
7086
|
}
|
|
6925
7087
|
}
|
|
6926
7088
|
}
|
|
6927
|
-
const innerMcpPath =
|
|
7089
|
+
const innerMcpPath = path27.join(harnessPath, ".mcp.json");
|
|
6928
7090
|
const innerMcpContent = await readFileSafe2(innerMcpPath);
|
|
6929
7091
|
if (innerMcpContent !== null) {
|
|
6930
7092
|
for (const node of parseMcpConfig(innerMcpContent)) {
|
|
@@ -7161,8 +7323,8 @@ function deepSet(obj, segments, value) {
|
|
|
7161
7323
|
const child = existing !== null && typeof existing === "object" && !Array.isArray(existing) ? existing : {};
|
|
7162
7324
|
return { ...obj, [head]: deepSet(child, rest, value) };
|
|
7163
7325
|
}
|
|
7164
|
-
function applySettingsUpdate(settings,
|
|
7165
|
-
const segments =
|
|
7326
|
+
function applySettingsUpdate(settings, path39, value) {
|
|
7327
|
+
const segments = path39.split(".");
|
|
7166
7328
|
const topKey = segments[0];
|
|
7167
7329
|
if (STRUCTURED_SETTINGS_KEYS.has(topKey)) {
|
|
7168
7330
|
const settingsRecord = { ...settings };
|
|
@@ -7620,10 +7782,10 @@ var init_diff = __esm({
|
|
|
7620
7782
|
});
|
|
7621
7783
|
|
|
7622
7784
|
// src/evolve/mutator.ts
|
|
7623
|
-
import
|
|
7624
|
-
import
|
|
7785
|
+
import fs28 from "fs/promises";
|
|
7786
|
+
import path28 from "path";
|
|
7625
7787
|
async function applyMutations(currentHarnessPath, nextIterationDir, mutations) {
|
|
7626
|
-
const newHarnessPath =
|
|
7788
|
+
const newHarnessPath = path28.join(nextIterationDir, "harness");
|
|
7627
7789
|
let baselineIR = null;
|
|
7628
7790
|
try {
|
|
7629
7791
|
baselineIR = await parseHarness(currentHarnessPath);
|
|
@@ -7664,6 +7826,11 @@ async function applyMutationsViaIR(currentHarnessPath, newHarnessPath, mutations
|
|
|
7664
7826
|
for (const mutation of rawTextMutations) {
|
|
7665
7827
|
await applyLegacyMutation(newHarnessPath, mutation);
|
|
7666
7828
|
}
|
|
7829
|
+
await fs28.writeFile(
|
|
7830
|
+
path28.join(newHarnessPath, "..", "harness-ir.json"),
|
|
7831
|
+
JSON.stringify(currentIR, null, 2),
|
|
7832
|
+
"utf-8"
|
|
7833
|
+
);
|
|
7667
7834
|
const irDiff = diffIR(baselineIR, currentIR);
|
|
7668
7835
|
let diffPatch = formatIRDiff(irDiff);
|
|
7669
7836
|
if (diffPatch === "No changes." && rawTextMutations.length > 0) {
|
|
@@ -7700,9 +7867,9 @@ async function renderAffectedFiles(ir, targetDir, touchedCategories) {
|
|
|
7700
7867
|
for (const [relativePath, content] of fileMap) {
|
|
7701
7868
|
const category = getFileCategory(relativePath);
|
|
7702
7869
|
if (touchedCategories.has(category)) {
|
|
7703
|
-
const fullPath =
|
|
7704
|
-
await
|
|
7705
|
-
await
|
|
7870
|
+
const fullPath = path28.join(targetDir, relativePath);
|
|
7871
|
+
await fs28.mkdir(path28.dirname(fullPath), { recursive: true });
|
|
7872
|
+
await fs28.writeFile(fullPath, content, "utf-8");
|
|
7706
7873
|
}
|
|
7707
7874
|
}
|
|
7708
7875
|
if (touchedCategories.has("commands")) {
|
|
@@ -7715,11 +7882,11 @@ async function renderAffectedFiles(ir, targetDir, touchedCategories) {
|
|
|
7715
7882
|
await deleteOrphanedFiles(targetDir, "agents", fileMap);
|
|
7716
7883
|
}
|
|
7717
7884
|
if (touchedCategories.has("mcp") && !fileMap.has(".mcp.json")) {
|
|
7718
|
-
await
|
|
7885
|
+
await fs28.unlink(path28.join(targetDir, ".mcp.json")).catch(() => {
|
|
7719
7886
|
});
|
|
7720
7887
|
}
|
|
7721
7888
|
if (touchedCategories.has("settings") && !fileMap.has("settings.json")) {
|
|
7722
|
-
await
|
|
7889
|
+
await fs28.unlink(path28.join(targetDir, "settings.json")).catch(() => {
|
|
7723
7890
|
});
|
|
7724
7891
|
}
|
|
7725
7892
|
}
|
|
@@ -7736,17 +7903,17 @@ function getFileCategory(relativePath) {
|
|
|
7736
7903
|
return "unknown";
|
|
7737
7904
|
}
|
|
7738
7905
|
async function deleteOrphanedFiles(targetDir, subdir, renderedMap) {
|
|
7739
|
-
const subdirPath =
|
|
7906
|
+
const subdirPath = path28.join(targetDir, subdir);
|
|
7740
7907
|
let entries;
|
|
7741
7908
|
try {
|
|
7742
|
-
entries = await
|
|
7909
|
+
entries = await fs28.readdir(subdirPath);
|
|
7743
7910
|
} catch {
|
|
7744
7911
|
return;
|
|
7745
7912
|
}
|
|
7746
7913
|
for (const entry of entries) {
|
|
7747
7914
|
const relativePath = `${subdir}/${entry}`;
|
|
7748
7915
|
if (!renderedMap.has(relativePath)) {
|
|
7749
|
-
await
|
|
7916
|
+
await fs28.unlink(path28.join(subdirPath, entry)).catch(() => {
|
|
7750
7917
|
});
|
|
7751
7918
|
}
|
|
7752
7919
|
}
|
|
@@ -7763,56 +7930,56 @@ async function applyLegacyMutation(harnessPath, mutation) {
|
|
|
7763
7930
|
if (mutation.file.includes("..")) {
|
|
7764
7931
|
return;
|
|
7765
7932
|
}
|
|
7766
|
-
const filePath =
|
|
7933
|
+
const filePath = path28.join(harnessPath, mutation.file);
|
|
7767
7934
|
if (mutation.action === "replace") {
|
|
7768
7935
|
if (!mutation.oldText) {
|
|
7769
7936
|
return;
|
|
7770
7937
|
}
|
|
7771
7938
|
let content;
|
|
7772
7939
|
try {
|
|
7773
|
-
content = await
|
|
7940
|
+
content = await fs28.readFile(filePath, "utf-8");
|
|
7774
7941
|
} catch {
|
|
7775
7942
|
return;
|
|
7776
7943
|
}
|
|
7777
7944
|
if (!content.includes(mutation.oldText)) {
|
|
7778
7945
|
return;
|
|
7779
7946
|
}
|
|
7780
|
-
await
|
|
7947
|
+
await fs28.writeFile(
|
|
7781
7948
|
filePath,
|
|
7782
7949
|
content.replace(mutation.oldText, mutation.newText),
|
|
7783
7950
|
"utf-8"
|
|
7784
7951
|
);
|
|
7785
7952
|
} else if (mutation.action === "add_section") {
|
|
7786
7953
|
try {
|
|
7787
|
-
const content = await
|
|
7788
|
-
await
|
|
7954
|
+
const content = await fs28.readFile(filePath, "utf-8");
|
|
7955
|
+
await fs28.writeFile(
|
|
7789
7956
|
filePath,
|
|
7790
7957
|
content + "\n\n" + mutation.newText,
|
|
7791
7958
|
"utf-8"
|
|
7792
7959
|
);
|
|
7793
7960
|
} catch {
|
|
7794
|
-
await
|
|
7795
|
-
await
|
|
7961
|
+
await fs28.mkdir(path28.dirname(filePath), { recursive: true });
|
|
7962
|
+
await fs28.writeFile(filePath, mutation.newText, "utf-8");
|
|
7796
7963
|
}
|
|
7797
7964
|
} else if (mutation.action === "create_file") {
|
|
7798
|
-
await
|
|
7799
|
-
await
|
|
7965
|
+
await fs28.mkdir(path28.dirname(filePath), { recursive: true });
|
|
7966
|
+
await fs28.writeFile(filePath, mutation.newText, "utf-8");
|
|
7800
7967
|
} else if (mutation.action === "delete_section") {
|
|
7801
7968
|
if (!mutation.oldText) {
|
|
7802
7969
|
return;
|
|
7803
7970
|
}
|
|
7804
7971
|
let sectionContent;
|
|
7805
7972
|
try {
|
|
7806
|
-
sectionContent = await
|
|
7973
|
+
sectionContent = await fs28.readFile(filePath, "utf-8");
|
|
7807
7974
|
} catch {
|
|
7808
7975
|
return;
|
|
7809
7976
|
}
|
|
7810
7977
|
if (!sectionContent.includes(mutation.oldText)) {
|
|
7811
7978
|
return;
|
|
7812
7979
|
}
|
|
7813
|
-
await
|
|
7980
|
+
await fs28.writeFile(filePath, sectionContent.replace(mutation.oldText, ""), "utf-8");
|
|
7814
7981
|
} else if (mutation.action === "delete_file") {
|
|
7815
|
-
await
|
|
7982
|
+
await fs28.unlink(filePath).catch(() => {
|
|
7816
7983
|
});
|
|
7817
7984
|
}
|
|
7818
7985
|
}
|
|
@@ -7880,17 +8047,17 @@ async function readAllFiles(dir) {
|
|
|
7880
8047
|
async function walk(current) {
|
|
7881
8048
|
let entries;
|
|
7882
8049
|
try {
|
|
7883
|
-
entries = await
|
|
8050
|
+
entries = await fs28.readdir(current, { withFileTypes: true });
|
|
7884
8051
|
} catch {
|
|
7885
8052
|
return;
|
|
7886
8053
|
}
|
|
7887
8054
|
for (const entry of entries) {
|
|
7888
|
-
const fullPath =
|
|
7889
|
-
const relativePath =
|
|
8055
|
+
const fullPath = path28.join(current, entry.name);
|
|
8056
|
+
const relativePath = path28.relative(dir, fullPath);
|
|
7890
8057
|
if (entry.isDirectory()) {
|
|
7891
8058
|
await walk(fullPath);
|
|
7892
8059
|
} else {
|
|
7893
|
-
result[relativePath] = await
|
|
8060
|
+
result[relativePath] = await fs28.readFile(fullPath, "utf-8");
|
|
7894
8061
|
}
|
|
7895
8062
|
}
|
|
7896
8063
|
}
|
|
@@ -8030,7 +8197,7 @@ ${taskScores}
|
|
|
8030
8197
|
}
|
|
8031
8198
|
return "## Iteration History\n\n(History omitted to fit context budget)\n";
|
|
8032
8199
|
}
|
|
8033
|
-
function buildArchitectUserMessage(harnessFiles, traces, tasks, history, knowledgeContext) {
|
|
8200
|
+
function buildArchitectUserMessage(harnessFiles, traces, tasks, history, knowledgeContext, projectContext) {
|
|
8034
8201
|
const harnessSection = ["## Current Harness Files\n"];
|
|
8035
8202
|
const fileEntries = Object.entries(harnessFiles);
|
|
8036
8203
|
if (fileEntries.length === 0) {
|
|
@@ -8059,11 +8226,28 @@ ${content}
|
|
|
8059
8226
|
}
|
|
8060
8227
|
const summarySection = buildEvolutionSummary(history);
|
|
8061
8228
|
const workingSection = buildWhatsWorking(history, tasks);
|
|
8229
|
+
let projectSection = "";
|
|
8230
|
+
if (projectContext) {
|
|
8231
|
+
const parts = ["## Project Understanding\n"];
|
|
8232
|
+
parts.push("### Analysis Summary");
|
|
8233
|
+
parts.push(formatAnalysisForProposer(projectContext.analysis));
|
|
8234
|
+
parts.push("");
|
|
8235
|
+
parts.push("### Harness Structure");
|
|
8236
|
+
parts.push(projectContext.irSummary);
|
|
8237
|
+
if (projectContext.keySourceFiles) {
|
|
8238
|
+
parts.push("");
|
|
8239
|
+
parts.push("### Key Source Files");
|
|
8240
|
+
parts.push("```");
|
|
8241
|
+
parts.push(projectContext.keySourceFiles);
|
|
8242
|
+
parts.push("```");
|
|
8243
|
+
}
|
|
8244
|
+
projectSection = "\n" + parts.join("\n") + "\n";
|
|
8245
|
+
}
|
|
8062
8246
|
const knowledgeSection = knowledgeContext ? `## Knowledge Base (patterns from other projects)
|
|
8063
8247
|
|
|
8064
8248
|
${knowledgeContext}
|
|
8065
8249
|
` : "";
|
|
8066
|
-
const fixedContent = harnessSection.join("\n") + "\n" + taskSection.join("\n") + "\n" + summarySection + (summarySection ? "\n" : "") + workingSection + (workingSection ? "\n" : "") + knowledgeSection;
|
|
8250
|
+
const fixedContent = harnessSection.join("\n") + "\n" + taskSection.join("\n") + "\n" + summarySection + (summarySection ? "\n" : "") + workingSection + (workingSection ? "\n" : "") + projectSection + knowledgeSection;
|
|
8067
8251
|
const remainingBudget = MAX_CONTEXT_CHARS2 - fixedContent.length;
|
|
8068
8252
|
if (remainingBudget <= 0) {
|
|
8069
8253
|
return fixedContent + "\n\n[...traces and history omitted \u2014 context budget exceeded...]";
|
|
@@ -8074,7 +8258,7 @@ ${knowledgeContext}
|
|
|
8074
8258
|
const historySection = buildHistorySection2(history, historyBudget);
|
|
8075
8259
|
return fixedContent + "\n" + traceSection + "\n" + historySection;
|
|
8076
8260
|
}
|
|
8077
|
-
async function proposeArchitecture(iteration, workspacePath, harnessPath, history, tasks, config, architectModel, knowledgeContext) {
|
|
8261
|
+
async function proposeArchitecture(iteration, workspacePath, harnessPath, history, tasks, config, architectModel, knowledgeContext, projectContext) {
|
|
8078
8262
|
const harnessFiles = await readHarnessFiles(harnessPath);
|
|
8079
8263
|
const traces = await loadIterationTraces(workspacePath, iteration);
|
|
8080
8264
|
let effectiveKnowledge = knowledgeContext;
|
|
@@ -8091,7 +8275,8 @@ async function proposeArchitecture(iteration, workspacePath, harnessPath, histor
|
|
|
8091
8275
|
traces,
|
|
8092
8276
|
tasks,
|
|
8093
8277
|
history,
|
|
8094
|
-
effectiveKnowledge
|
|
8278
|
+
effectiveKnowledge,
|
|
8279
|
+
projectContext
|
|
8095
8280
|
);
|
|
8096
8281
|
const architectConfig = { ...config, model: architectModel };
|
|
8097
8282
|
const response = await callLLM(architectConfig, userMessage, {
|
|
@@ -8206,8 +8391,8 @@ var init_schedule = __esm({
|
|
|
8206
8391
|
});
|
|
8207
8392
|
|
|
8208
8393
|
// src/evolve/sampling.ts
|
|
8209
|
-
import
|
|
8210
|
-
import
|
|
8394
|
+
import fs29 from "fs/promises";
|
|
8395
|
+
import path29 from "path";
|
|
8211
8396
|
function initBeliefs(tasks) {
|
|
8212
8397
|
return tasks.map((task) => ({
|
|
8213
8398
|
taskId: task.id,
|
|
@@ -8268,9 +8453,9 @@ function updateBeliefs(beliefs, results) {
|
|
|
8268
8453
|
});
|
|
8269
8454
|
}
|
|
8270
8455
|
async function loadBeliefs(workspacePath) {
|
|
8271
|
-
const beliefsPath =
|
|
8456
|
+
const beliefsPath = path29.join(workspacePath, "task-beliefs.json");
|
|
8272
8457
|
try {
|
|
8273
|
-
const content = await
|
|
8458
|
+
const content = await fs29.readFile(beliefsPath, "utf-8");
|
|
8274
8459
|
const parsed = JSON.parse(content);
|
|
8275
8460
|
if (!Array.isArray(parsed)) return null;
|
|
8276
8461
|
for (const entry of parsed) {
|
|
@@ -8284,9 +8469,9 @@ async function loadBeliefs(workspacePath) {
|
|
|
8284
8469
|
}
|
|
8285
8470
|
}
|
|
8286
8471
|
async function saveBeliefs(workspacePath, beliefs) {
|
|
8287
|
-
const beliefsPath =
|
|
8288
|
-
await
|
|
8289
|
-
await
|
|
8472
|
+
const beliefsPath = path29.join(workspacePath, "task-beliefs.json");
|
|
8473
|
+
await fs29.mkdir(path29.dirname(beliefsPath), { recursive: true });
|
|
8474
|
+
await fs29.writeFile(beliefsPath, JSON.stringify(beliefs, null, 2), "utf-8");
|
|
8290
8475
|
}
|
|
8291
8476
|
var init_sampling = __esm({
|
|
8292
8477
|
"src/evolve/sampling.ts"() {
|
|
@@ -8295,8 +8480,8 @@ var init_sampling = __esm({
|
|
|
8295
8480
|
});
|
|
8296
8481
|
|
|
8297
8482
|
// src/evolve/regularization.ts
|
|
8298
|
-
import
|
|
8299
|
-
import
|
|
8483
|
+
import fs30 from "fs/promises";
|
|
8484
|
+
import path30 from "path";
|
|
8300
8485
|
async function measureComplexity(harnessPath) {
|
|
8301
8486
|
let totalLines = 0;
|
|
8302
8487
|
let totalFiles = 0;
|
|
@@ -8430,18 +8615,18 @@ async function readAllFilesRecursive(dir) {
|
|
|
8430
8615
|
async function walk(current) {
|
|
8431
8616
|
let entries;
|
|
8432
8617
|
try {
|
|
8433
|
-
entries = await
|
|
8618
|
+
entries = await fs30.readdir(current, { withFileTypes: true });
|
|
8434
8619
|
} catch {
|
|
8435
8620
|
return;
|
|
8436
8621
|
}
|
|
8437
8622
|
for (const entry of entries) {
|
|
8438
|
-
const fullPath =
|
|
8439
|
-
const relativePath =
|
|
8623
|
+
const fullPath = path30.join(current, entry.name);
|
|
8624
|
+
const relativePath = path30.relative(dir, fullPath);
|
|
8440
8625
|
if (entry.isDirectory()) {
|
|
8441
8626
|
await walk(fullPath);
|
|
8442
8627
|
} else {
|
|
8443
8628
|
try {
|
|
8444
|
-
result[relativePath] = await
|
|
8629
|
+
result[relativePath] = await fs30.readFile(fullPath, "utf-8");
|
|
8445
8630
|
} catch {
|
|
8446
8631
|
}
|
|
8447
8632
|
}
|
|
@@ -8538,14 +8723,56 @@ var init_targeting = __esm({
|
|
|
8538
8723
|
"test-writing": ["verification", "commands"],
|
|
8539
8724
|
"config-change": ["settings", "mcp"],
|
|
8540
8725
|
"documentation": ["general"],
|
|
8541
|
-
"persistence-completion": ["commands", "verification"]
|
|
8726
|
+
"persistence-completion": ["commands", "verification"],
|
|
8727
|
+
"real-bug-fix": ["general"],
|
|
8728
|
+
"real-feature-add": ["general"],
|
|
8729
|
+
"codebase-question": ["general"]
|
|
8542
8730
|
};
|
|
8543
8731
|
}
|
|
8544
8732
|
});
|
|
8545
8733
|
|
|
8546
8734
|
// src/evolve/loop.ts
|
|
8547
|
-
import
|
|
8548
|
-
import
|
|
8735
|
+
import fs31 from "fs/promises";
|
|
8736
|
+
import path31 from "path";
|
|
8737
|
+
async function loadProjectContext(projectDir) {
|
|
8738
|
+
try {
|
|
8739
|
+
const analysisPath = path31.join(projectDir, CACHE_FILENAME);
|
|
8740
|
+
const analysisRaw = await fs31.readFile(analysisPath, "utf-8");
|
|
8741
|
+
const analysisCache = JSON.parse(analysisRaw);
|
|
8742
|
+
const analysis = analysisCache.analysis;
|
|
8743
|
+
let keySourceFiles = "";
|
|
8744
|
+
try {
|
|
8745
|
+
const packedPath = path31.join(projectDir, PACKED_SOURCE_FILENAME);
|
|
8746
|
+
const packedSource = await fs31.readFile(packedPath, "utf-8");
|
|
8747
|
+
keySourceFiles = packedSource.slice(0, KEY_SOURCE_CHARS_LIMIT);
|
|
8748
|
+
} catch {
|
|
8749
|
+
}
|
|
8750
|
+
return { analysis, keySourceFiles };
|
|
8751
|
+
} catch {
|
|
8752
|
+
return null;
|
|
8753
|
+
}
|
|
8754
|
+
}
|
|
8755
|
+
async function buildProjectContext(projectData, harnessPath) {
|
|
8756
|
+
let irSummary = "";
|
|
8757
|
+
try {
|
|
8758
|
+
const irPath = path31.join(harnessPath, "..", "harness-ir.json");
|
|
8759
|
+
const irRaw = await fs31.readFile(irPath, "utf-8");
|
|
8760
|
+
const ir = JSON.parse(irRaw);
|
|
8761
|
+
irSummary = buildIRSummary(ir);
|
|
8762
|
+
} catch {
|
|
8763
|
+
try {
|
|
8764
|
+
const ir = await parseHarness(harnessPath);
|
|
8765
|
+
irSummary = buildIRSummary(ir);
|
|
8766
|
+
} catch {
|
|
8767
|
+
irSummary = "(IR not available)";
|
|
8768
|
+
}
|
|
8769
|
+
}
|
|
8770
|
+
return {
|
|
8771
|
+
analysis: projectData.analysis,
|
|
8772
|
+
irSummary,
|
|
8773
|
+
keySourceFiles: projectData.keySourceFiles || void 0
|
|
8774
|
+
};
|
|
8775
|
+
}
|
|
8549
8776
|
function computeMutationCap(iter, maxIterations, maxMutations) {
|
|
8550
8777
|
if (maxIterations <= 1) return maxMutations;
|
|
8551
8778
|
const progress = iter / (maxIterations - 1);
|
|
@@ -8564,7 +8791,7 @@ async function evolve(workspacePath, tasks, kairnConfig, evolveConfig, onProgres
|
|
|
8564
8791
|
let baselineComplexity = null;
|
|
8565
8792
|
let baselineIR = null;
|
|
8566
8793
|
if (useKL) {
|
|
8567
|
-
const baselineHarness =
|
|
8794
|
+
const baselineHarness = path31.join(workspacePath, "iterations", "0", "harness");
|
|
8568
8795
|
try {
|
|
8569
8796
|
baselineIR = await parseHarness(baselineHarness);
|
|
8570
8797
|
baselineComplexity = measureComplexityFromIR(baselineIR);
|
|
@@ -8576,20 +8803,22 @@ async function evolve(workspacePath, tasks, kairnConfig, evolveConfig, onProgres
|
|
|
8576
8803
|
}
|
|
8577
8804
|
}
|
|
8578
8805
|
let lastChangedAspects = null;
|
|
8806
|
+
const projectDir = path31.resolve(workspacePath, "..");
|
|
8807
|
+
const projectData = await loadProjectContext(projectDir);
|
|
8579
8808
|
let rngState = evolveConfig.rngSeed ?? 42;
|
|
8580
8809
|
const rng = () => {
|
|
8581
8810
|
rngState = rngState * 1664525 + 1013904223 & 4294967295;
|
|
8582
8811
|
return (rngState >>> 0) / 4294967296;
|
|
8583
8812
|
};
|
|
8584
8813
|
for (let iter = 0; iter < evolveConfig.maxIterations; iter++) {
|
|
8585
|
-
const harnessPath =
|
|
8814
|
+
const harnessPath = path31.join(
|
|
8586
8815
|
workspacePath,
|
|
8587
8816
|
"iterations",
|
|
8588
8817
|
iter.toString(),
|
|
8589
8818
|
"harness"
|
|
8590
8819
|
);
|
|
8591
8820
|
try {
|
|
8592
|
-
await
|
|
8821
|
+
await fs31.access(harnessPath);
|
|
8593
8822
|
} catch {
|
|
8594
8823
|
if (iter === 0) {
|
|
8595
8824
|
throw new Error(
|
|
@@ -8698,7 +8927,7 @@ async function evolve(workspacePath, tasks, kairnConfig, evolveConfig, onProgres
|
|
|
8698
8927
|
}
|
|
8699
8928
|
const diffRatio = await computeDiffRatio(
|
|
8700
8929
|
harnessPath,
|
|
8701
|
-
|
|
8930
|
+
path31.join(workspacePath, "iterations", "0", "harness")
|
|
8702
8931
|
);
|
|
8703
8932
|
currentComplexity.diffFromBaseline = diffRatio;
|
|
8704
8933
|
iterComplexityCost = computeComplexityCost(currentComplexity, baselineComplexity);
|
|
@@ -8767,7 +8996,7 @@ async function evolve(workspacePath, tasks, kairnConfig, evolveConfig, onProgres
|
|
|
8767
8996
|
};
|
|
8768
8997
|
await writeIterationLog(workspacePath, rollbackLog);
|
|
8769
8998
|
history.push(rollbackLog);
|
|
8770
|
-
const bestHarnessPath =
|
|
8999
|
+
const bestHarnessPath = path31.join(
|
|
8771
9000
|
workspacePath,
|
|
8772
9001
|
"iterations",
|
|
8773
9002
|
bestIteration.toString(),
|
|
@@ -8776,6 +9005,7 @@ async function evolve(workspacePath, tasks, kairnConfig, evolveConfig, onProgres
|
|
|
8776
9005
|
if (iter + 1 < evolveConfig.maxIterations) {
|
|
8777
9006
|
onProgress?.({ type: "proposing", iteration: iter, message: "Proposing new mutations after rollback" });
|
|
8778
9007
|
try {
|
|
9008
|
+
const rollbackProjectCtx = projectData ? await buildProjectContext(projectData, bestHarnessPath) : void 0;
|
|
8779
9009
|
let rollbackProposal = await propose(
|
|
8780
9010
|
iter,
|
|
8781
9011
|
workspacePath,
|
|
@@ -8783,7 +9013,8 @@ async function evolve(workspacePath, tasks, kairnConfig, evolveConfig, onProgres
|
|
|
8783
9013
|
history,
|
|
8784
9014
|
tasks,
|
|
8785
9015
|
kairnConfig,
|
|
8786
|
-
evolveConfig.proposerModel
|
|
9016
|
+
evolveConfig.proposerModel,
|
|
9017
|
+
rollbackProjectCtx
|
|
8787
9018
|
);
|
|
8788
9019
|
const rollbackCap = computeMutationCap(iter, evolveConfig.maxIterations, evolveConfig.maxMutationsPerIteration);
|
|
8789
9020
|
if (rollbackProposal.mutations.length > rollbackCap) {
|
|
@@ -8792,7 +9023,7 @@ async function evolve(workspacePath, tasks, kairnConfig, evolveConfig, onProgres
|
|
|
8792
9023
|
mutations: rollbackProposal.mutations.slice(0, rollbackCap)
|
|
8793
9024
|
};
|
|
8794
9025
|
}
|
|
8795
|
-
const nextIterDir2 =
|
|
9026
|
+
const nextIterDir2 = path31.join(workspacePath, "iterations", (iter + 1).toString());
|
|
8796
9027
|
await applyMutations(bestHarnessPath, nextIterDir2, rollbackProposal.mutations);
|
|
8797
9028
|
try {
|
|
8798
9029
|
const rollbackIR = await parseHarness(bestHarnessPath);
|
|
@@ -8807,8 +9038,8 @@ async function evolve(workspacePath, tasks, kairnConfig, evolveConfig, onProgres
|
|
|
8807
9038
|
mutationCount: rollbackProposal.mutations.length
|
|
8808
9039
|
});
|
|
8809
9040
|
} catch {
|
|
8810
|
-
const nextIterDir2 =
|
|
8811
|
-
await copyDir(bestHarnessPath,
|
|
9041
|
+
const nextIterDir2 = path31.join(workspacePath, "iterations", (iter + 1).toString());
|
|
9042
|
+
await copyDir(bestHarnessPath, path31.join(nextIterDir2, "harness"));
|
|
8812
9043
|
}
|
|
8813
9044
|
}
|
|
8814
9045
|
continue;
|
|
@@ -8859,6 +9090,7 @@ async function evolve(workspacePath, tasks, kairnConfig, evolveConfig, onProgres
|
|
|
8859
9090
|
onProgress?.({ type: "architect-start", iteration: iter });
|
|
8860
9091
|
let architectProposal;
|
|
8861
9092
|
try {
|
|
9093
|
+
const architectProjectCtx = projectData ? await buildProjectContext(projectData, harnessPath) : void 0;
|
|
8862
9094
|
architectProposal = await proposeArchitecture(
|
|
8863
9095
|
iter,
|
|
8864
9096
|
workspacePath,
|
|
@@ -8866,7 +9098,9 @@ async function evolve(workspacePath, tasks, kairnConfig, evolveConfig, onProgres
|
|
|
8866
9098
|
history,
|
|
8867
9099
|
tasks,
|
|
8868
9100
|
kairnConfig,
|
|
8869
|
-
evolveConfig.architectModel
|
|
9101
|
+
evolveConfig.architectModel,
|
|
9102
|
+
void 0,
|
|
9103
|
+
architectProjectCtx
|
|
8870
9104
|
);
|
|
8871
9105
|
const architectCap = computeArchitectMutationBudget(iter, evolveConfig.maxIterations);
|
|
8872
9106
|
if (architectProposal.mutations.length > architectCap) {
|
|
@@ -8882,8 +9116,8 @@ async function evolve(workspacePath, tasks, kairnConfig, evolveConfig, onProgres
|
|
|
8882
9116
|
iteration: iter,
|
|
8883
9117
|
message: `Architect failed: ${errMsg}. Falling back to reactive proposer.`
|
|
8884
9118
|
});
|
|
8885
|
-
const nextIterDir2 =
|
|
8886
|
-
await copyDir(harnessPath,
|
|
9119
|
+
const nextIterDir2 = path31.join(workspacePath, "iterations", (iter + 1).toString());
|
|
9120
|
+
await copyDir(harnessPath, path31.join(nextIterDir2, "harness"));
|
|
8887
9121
|
const skipLog = {
|
|
8888
9122
|
iteration: iter,
|
|
8889
9123
|
score: aggregate,
|
|
@@ -8900,7 +9134,7 @@ async function evolve(workspacePath, tasks, kairnConfig, evolveConfig, onProgres
|
|
|
8900
9134
|
continue;
|
|
8901
9135
|
}
|
|
8902
9136
|
onProgress?.({ type: "architect-staging", iteration: iter });
|
|
8903
|
-
const stagingDir =
|
|
9137
|
+
const stagingDir = path31.join(workspacePath, "staging", iter.toString());
|
|
8904
9138
|
let stagingHarnessPath;
|
|
8905
9139
|
try {
|
|
8906
9140
|
const stagingResult = await applyMutations(
|
|
@@ -8911,8 +9145,8 @@ async function evolve(workspacePath, tasks, kairnConfig, evolveConfig, onProgres
|
|
|
8911
9145
|
stagingHarnessPath = stagingResult.newHarnessPath;
|
|
8912
9146
|
} catch {
|
|
8913
9147
|
onProgress?.({ type: "architect-rejected", iteration: iter, score: 0, message: "Failed to apply mutations to staging" });
|
|
8914
|
-
const nextIterDir2 =
|
|
8915
|
-
await copyDir(harnessPath,
|
|
9148
|
+
const nextIterDir2 = path31.join(workspacePath, "iterations", (iter + 1).toString());
|
|
9149
|
+
await copyDir(harnessPath, path31.join(nextIterDir2, "harness"));
|
|
8916
9150
|
const rejectLog = {
|
|
8917
9151
|
iteration: iter,
|
|
8918
9152
|
score: aggregate,
|
|
@@ -8926,7 +9160,7 @@ async function evolve(workspacePath, tasks, kairnConfig, evolveConfig, onProgres
|
|
|
8926
9160
|
};
|
|
8927
9161
|
await writeIterationLog(workspacePath, rejectLog);
|
|
8928
9162
|
history.push(rejectLog);
|
|
8929
|
-
await
|
|
9163
|
+
await fs31.rm(stagingDir, { recursive: true, force: true }).catch(() => {
|
|
8930
9164
|
});
|
|
8931
9165
|
continue;
|
|
8932
9166
|
}
|
|
@@ -8943,12 +9177,12 @@ async function evolve(workspacePath, tasks, kairnConfig, evolveConfig, onProgres
|
|
|
8943
9177
|
);
|
|
8944
9178
|
if (stagingScore >= bestScore) {
|
|
8945
9179
|
onProgress?.({ type: "architect-accepted", iteration: iter, score: stagingScore });
|
|
8946
|
-
const nextIterDir2 =
|
|
8947
|
-
await copyDir(stagingHarnessPath,
|
|
9180
|
+
const nextIterDir2 = path31.join(workspacePath, "iterations", (iter + 1).toString());
|
|
9181
|
+
await copyDir(stagingHarnessPath, path31.join(nextIterDir2, "harness"));
|
|
8948
9182
|
} else {
|
|
8949
9183
|
onProgress?.({ type: "architect-rejected", iteration: iter, score: stagingScore });
|
|
8950
|
-
const nextIterDir2 =
|
|
8951
|
-
await copyDir(harnessPath,
|
|
9184
|
+
const nextIterDir2 = path31.join(workspacePath, "iterations", (iter + 1).toString());
|
|
9185
|
+
await copyDir(harnessPath, path31.join(nextIterDir2, "harness"));
|
|
8952
9186
|
}
|
|
8953
9187
|
const architectLog = {
|
|
8954
9188
|
iteration: iter,
|
|
@@ -8963,13 +9197,14 @@ async function evolve(workspacePath, tasks, kairnConfig, evolveConfig, onProgres
|
|
|
8963
9197
|
};
|
|
8964
9198
|
await writeIterationLog(workspacePath, architectLog);
|
|
8965
9199
|
history.push(architectLog);
|
|
8966
|
-
await
|
|
9200
|
+
await fs31.rm(stagingDir, { recursive: true, force: true }).catch(() => {
|
|
8967
9201
|
});
|
|
8968
9202
|
continue;
|
|
8969
9203
|
}
|
|
8970
9204
|
onProgress?.({ type: "proposing", iteration: iter });
|
|
8971
9205
|
let proposal;
|
|
8972
9206
|
try {
|
|
9207
|
+
const iterProjectCtx = projectData ? await buildProjectContext(projectData, harnessPath) : void 0;
|
|
8973
9208
|
proposal = await propose(
|
|
8974
9209
|
iter,
|
|
8975
9210
|
workspacePath,
|
|
@@ -8977,7 +9212,8 @@ async function evolve(workspacePath, tasks, kairnConfig, evolveConfig, onProgres
|
|
|
8977
9212
|
history,
|
|
8978
9213
|
tasks,
|
|
8979
9214
|
kairnConfig,
|
|
8980
|
-
evolveConfig.proposerModel
|
|
9215
|
+
evolveConfig.proposerModel,
|
|
9216
|
+
iterProjectCtx
|
|
8981
9217
|
);
|
|
8982
9218
|
const iterCap = computeMutationCap(iter, evolveConfig.maxIterations, evolveConfig.maxMutationsPerIteration);
|
|
8983
9219
|
if (proposal.mutations.length > iterCap) {
|
|
@@ -8993,12 +9229,12 @@ async function evolve(workspacePath, tasks, kairnConfig, evolveConfig, onProgres
|
|
|
8993
9229
|
iteration: iter,
|
|
8994
9230
|
message: `Proposer failed: ${errMsg}`
|
|
8995
9231
|
});
|
|
8996
|
-
const nextIterDir2 =
|
|
9232
|
+
const nextIterDir2 = path31.join(
|
|
8997
9233
|
workspacePath,
|
|
8998
9234
|
"iterations",
|
|
8999
9235
|
(iter + 1).toString()
|
|
9000
9236
|
);
|
|
9001
|
-
await copyDir(harnessPath,
|
|
9237
|
+
await copyDir(harnessPath, path31.join(nextIterDir2, "harness"));
|
|
9002
9238
|
const skipLog = {
|
|
9003
9239
|
iteration: iter,
|
|
9004
9240
|
score: aggregate,
|
|
@@ -9014,7 +9250,7 @@ async function evolve(workspacePath, tasks, kairnConfig, evolveConfig, onProgres
|
|
|
9014
9250
|
history.push(skipLog);
|
|
9015
9251
|
continue;
|
|
9016
9252
|
}
|
|
9017
|
-
const nextIterDir =
|
|
9253
|
+
const nextIterDir = path31.join(
|
|
9018
9254
|
workspacePath,
|
|
9019
9255
|
"iterations",
|
|
9020
9256
|
(iter + 1).toString()
|
|
@@ -9035,7 +9271,7 @@ async function evolve(workspacePath, tasks, kairnConfig, evolveConfig, onProgres
|
|
|
9035
9271
|
lastChangedAspects = null;
|
|
9036
9272
|
}
|
|
9037
9273
|
} catch {
|
|
9038
|
-
await copyDir(harnessPath,
|
|
9274
|
+
await copyDir(harnessPath, path31.join(nextIterDir, "harness"));
|
|
9039
9275
|
lastChangedAspects = null;
|
|
9040
9276
|
}
|
|
9041
9277
|
onProgress?.({
|
|
@@ -9059,8 +9295,9 @@ async function evolve(workspacePath, tasks, kairnConfig, evolveConfig, onProgres
|
|
|
9059
9295
|
}
|
|
9060
9296
|
if (evolveConfig.usePrincipal && history.length >= 2) {
|
|
9061
9297
|
onProgress?.({ type: "proposing", iteration: history.length, message: "Principal Proposer synthesizing final harness" });
|
|
9062
|
-
const baselineHarnessPath =
|
|
9298
|
+
const baselineHarnessPath = path31.join(workspacePath, "iterations", "0", "harness");
|
|
9063
9299
|
try {
|
|
9300
|
+
const principalProjectCtx = projectData ? await buildProjectContext(projectData, baselineHarnessPath) : void 0;
|
|
9064
9301
|
let principalProposal = await propose(
|
|
9065
9302
|
history.length,
|
|
9066
9303
|
workspacePath,
|
|
@@ -9068,7 +9305,8 @@ async function evolve(workspacePath, tasks, kairnConfig, evolveConfig, onProgres
|
|
|
9068
9305
|
history,
|
|
9069
9306
|
tasks,
|
|
9070
9307
|
kairnConfig,
|
|
9071
|
-
evolveConfig.proposerModel
|
|
9308
|
+
evolveConfig.proposerModel,
|
|
9309
|
+
principalProjectCtx
|
|
9072
9310
|
);
|
|
9073
9311
|
if (principalProposal.mutations.length > evolveConfig.maxMutationsPerIteration) {
|
|
9074
9312
|
principalProposal = {
|
|
@@ -9077,7 +9315,7 @@ async function evolve(workspacePath, tasks, kairnConfig, evolveConfig, onProgres
|
|
|
9077
9315
|
};
|
|
9078
9316
|
}
|
|
9079
9317
|
const principalIterNum = history.length;
|
|
9080
|
-
const principalIterDir =
|
|
9318
|
+
const principalIterDir = path31.join(workspacePath, "iterations", principalIterNum.toString());
|
|
9081
9319
|
const mutResult = await applyMutations(baselineHarnessPath, principalIterDir, principalProposal.mutations);
|
|
9082
9320
|
onProgress?.({ type: "iteration-start", iteration: principalIterNum });
|
|
9083
9321
|
const { results: principalResults, aggregate: principalAggregate } = await evaluateAll(
|
|
@@ -9118,7 +9356,7 @@ async function evolve(workspacePath, tasks, kairnConfig, evolveConfig, onProgres
|
|
|
9118
9356
|
}
|
|
9119
9357
|
try {
|
|
9120
9358
|
const { extractAndSavePatterns: extractAndSavePatterns2 } = await Promise.resolve().then(() => (init_knowledge(), knowledge_exports));
|
|
9121
|
-
const projectName =
|
|
9359
|
+
const projectName = path31.basename(path31.resolve(workspacePath, ".."));
|
|
9122
9360
|
await extractAndSavePatterns2(history, projectName, null);
|
|
9123
9361
|
} catch {
|
|
9124
9362
|
}
|
|
@@ -9134,6 +9372,7 @@ async function evolve(workspacePath, tasks, kairnConfig, evolveConfig, onProgres
|
|
|
9134
9372
|
baselineScore
|
|
9135
9373
|
};
|
|
9136
9374
|
}
|
|
9375
|
+
var KEY_SOURCE_CHARS_LIMIT;
|
|
9137
9376
|
var init_loop = __esm({
|
|
9138
9377
|
"src/evolve/loop.ts"() {
|
|
9139
9378
|
"use strict";
|
|
@@ -9149,11 +9388,13 @@ var init_loop = __esm({
|
|
|
9149
9388
|
init_parser();
|
|
9150
9389
|
init_translate();
|
|
9151
9390
|
init_targeting();
|
|
9391
|
+
init_cache();
|
|
9392
|
+
KEY_SOURCE_CHARS_LIMIT = 1e4;
|
|
9152
9393
|
}
|
|
9153
9394
|
});
|
|
9154
9395
|
|
|
9155
9396
|
// src/evolve/synthesis.ts
|
|
9156
|
-
import
|
|
9397
|
+
import path34 from "path";
|
|
9157
9398
|
function buildMetaPrincipalSystemPrompt(numBranches) {
|
|
9158
9399
|
return `You are reviewing the COMPLETE results of ${numBranches} independent evolution runs.
|
|
9159
9400
|
Each branch explored different mutations and saw different task subsets.
|
|
@@ -9309,7 +9550,7 @@ async function runSynthesis(context, kairnConfig, evolveConfig, workspacePath) {
|
|
|
9309
9550
|
if (mutations.length === 0) {
|
|
9310
9551
|
return null;
|
|
9311
9552
|
}
|
|
9312
|
-
const synthesisDir =
|
|
9553
|
+
const synthesisDir = path34.join(workspacePath, "synthesis");
|
|
9313
9554
|
const { newHarnessPath } = await applyMutations(
|
|
9314
9555
|
context.baselineHarnessPath,
|
|
9315
9556
|
synthesisDir,
|
|
@@ -9343,26 +9584,26 @@ __export(population_exports, {
|
|
|
9343
9584
|
initBranches: () => initBranches,
|
|
9344
9585
|
runPopulation: () => runPopulation
|
|
9345
9586
|
});
|
|
9346
|
-
import
|
|
9347
|
-
import
|
|
9587
|
+
import fs34 from "fs/promises";
|
|
9588
|
+
import path35 from "path";
|
|
9348
9589
|
async function initBranches(workspacePath, baselinePath, numBranches) {
|
|
9349
|
-
const branchesDir =
|
|
9350
|
-
await
|
|
9590
|
+
const branchesDir = path35.join(workspacePath, "branches");
|
|
9591
|
+
await fs34.mkdir(branchesDir, { recursive: true });
|
|
9351
9592
|
const configs = [];
|
|
9352
9593
|
for (let i = 0; i < numBranches; i++) {
|
|
9353
|
-
const branchPath =
|
|
9354
|
-
const harnessPath =
|
|
9594
|
+
const branchPath = path35.join(branchesDir, i.toString());
|
|
9595
|
+
const harnessPath = path35.join(branchPath, "iterations", "0", "harness");
|
|
9355
9596
|
await copyDir(baselinePath, harnessPath);
|
|
9356
|
-
const tasksYaml =
|
|
9597
|
+
const tasksYaml = path35.join(workspacePath, "tasks.yaml");
|
|
9357
9598
|
try {
|
|
9358
|
-
await
|
|
9359
|
-
await
|
|
9599
|
+
await fs34.access(tasksYaml);
|
|
9600
|
+
await fs34.copyFile(tasksYaml, path35.join(branchPath, "tasks.yaml"));
|
|
9360
9601
|
} catch {
|
|
9361
9602
|
}
|
|
9362
|
-
const configYaml =
|
|
9603
|
+
const configYaml = path35.join(workspacePath, "config.yaml");
|
|
9363
9604
|
try {
|
|
9364
|
-
await
|
|
9365
|
-
await
|
|
9605
|
+
await fs34.access(configYaml);
|
|
9606
|
+
await fs34.copyFile(configYaml, path35.join(branchPath, "config.yaml"));
|
|
9366
9607
|
} catch {
|
|
9367
9608
|
}
|
|
9368
9609
|
const seed = 42 + i * 1337;
|
|
@@ -9376,7 +9617,7 @@ async function initBranches(workspacePath, baselinePath, numBranches) {
|
|
|
9376
9617
|
}
|
|
9377
9618
|
async function runPopulation(workspacePath, tasks, kairnConfig, evolveConfig, numBranches, onProgress) {
|
|
9378
9619
|
const branches = numBranches ?? evolveConfig.pbtBranches;
|
|
9379
|
-
const baselinePath =
|
|
9620
|
+
const baselinePath = path35.join(workspacePath, "baseline");
|
|
9380
9621
|
const branchConfigs = await initBranches(workspacePath, baselinePath, branches);
|
|
9381
9622
|
const branchPromises = branchConfigs.map(async (branchConfig) => {
|
|
9382
9623
|
const branchEvolveConfig = {
|
|
@@ -9396,7 +9637,7 @@ async function runPopulation(workspacePath, tasks, kairnConfig, evolveConfig, nu
|
|
|
9396
9637
|
branchEvolveConfig,
|
|
9397
9638
|
branchProgress
|
|
9398
9639
|
);
|
|
9399
|
-
const finalHarnessPath =
|
|
9640
|
+
const finalHarnessPath = path35.join(
|
|
9400
9641
|
branchConfig.workspacePath,
|
|
9401
9642
|
"iterations",
|
|
9402
9643
|
result.bestIteration.toString(),
|
|
@@ -9404,8 +9645,8 @@ async function runPopulation(workspacePath, tasks, kairnConfig, evolveConfig, nu
|
|
|
9404
9645
|
);
|
|
9405
9646
|
let beliefs = [];
|
|
9406
9647
|
try {
|
|
9407
|
-
const beliefsPath =
|
|
9408
|
-
const beliefsContent = await
|
|
9648
|
+
const beliefsPath = path35.join(branchConfig.workspacePath, "task-beliefs.json");
|
|
9649
|
+
const beliefsContent = await fs34.readFile(beliefsPath, "utf-8");
|
|
9409
9650
|
beliefs = JSON.parse(beliefsContent);
|
|
9410
9651
|
} catch {
|
|
9411
9652
|
}
|
|
@@ -9427,7 +9668,7 @@ async function runPopulation(workspacePath, tasks, kairnConfig, evolveConfig, nu
|
|
|
9427
9668
|
}
|
|
9428
9669
|
let synthesizedResult;
|
|
9429
9670
|
try {
|
|
9430
|
-
const baselinePath2 =
|
|
9671
|
+
const baselinePath2 = path35.join(workspacePath, "baseline");
|
|
9431
9672
|
const synthesisResult = await runSynthesis(
|
|
9432
9673
|
{ branches: branchResults, tasks, baselineHarnessPath: baselinePath2 },
|
|
9433
9674
|
kairnConfig,
|
|
@@ -9489,8 +9730,8 @@ __export(research_exports, {
|
|
|
9489
9730
|
formatResearchReport: () => formatResearchReport,
|
|
9490
9731
|
runResearch: () => runResearch
|
|
9491
9732
|
});
|
|
9492
|
-
import
|
|
9493
|
-
import
|
|
9733
|
+
import fs35 from "fs/promises";
|
|
9734
|
+
import path36 from "path";
|
|
9494
9735
|
import os5 from "os";
|
|
9495
9736
|
import { exec as exec4 } from "child_process";
|
|
9496
9737
|
import { promisify as promisify4 } from "util";
|
|
@@ -9509,8 +9750,8 @@ async function runResearch(config, _kairnConfig, _evolveConfig, onProgress) {
|
|
|
9509
9750
|
}
|
|
9510
9751
|
const allPatterns = [];
|
|
9511
9752
|
const repoResults = [];
|
|
9512
|
-
const tempBase =
|
|
9513
|
-
await
|
|
9753
|
+
const tempBase = path36.join(os5.tmpdir(), `kairn-research-${Date.now()}`);
|
|
9754
|
+
await fs35.mkdir(tempBase, { recursive: true });
|
|
9514
9755
|
try {
|
|
9515
9756
|
for (let i = 0; i < config.repos.length; i++) {
|
|
9516
9757
|
const url = config.repos[i];
|
|
@@ -9522,7 +9763,7 @@ async function runResearch(config, _kairnConfig, _evolveConfig, onProgress) {
|
|
|
9522
9763
|
totalRepos: config.repos.length,
|
|
9523
9764
|
message: `Cloning ${name}...`
|
|
9524
9765
|
});
|
|
9525
|
-
const repoDir =
|
|
9766
|
+
const repoDir = path36.join(tempBase, name);
|
|
9526
9767
|
try {
|
|
9527
9768
|
await execAsync4(`git clone --depth 1 '${url.replace(/'/g, "'\\''")}' '${repoDir.replace(/'/g, "'\\''")}' 2>/dev/null`, { timeout: 6e4 });
|
|
9528
9769
|
} catch (err) {
|
|
@@ -9536,10 +9777,10 @@ async function runResearch(config, _kairnConfig, _evolveConfig, onProgress) {
|
|
|
9536
9777
|
repoResults.push({ repo: name, bestScore: 0, patternsFound: 0 });
|
|
9537
9778
|
continue;
|
|
9538
9779
|
}
|
|
9539
|
-
const claudeDir =
|
|
9780
|
+
const claudeDir = path36.join(repoDir, ".claude");
|
|
9540
9781
|
let hasHarness = false;
|
|
9541
9782
|
try {
|
|
9542
|
-
await
|
|
9783
|
+
await fs35.access(claudeDir);
|
|
9543
9784
|
hasHarness = true;
|
|
9544
9785
|
} catch {
|
|
9545
9786
|
onProgress?.({
|
|
@@ -9589,7 +9830,7 @@ async function runResearch(config, _kairnConfig, _evolveConfig, onProgress) {
|
|
|
9589
9830
|
onProgress?.({ type: "research-complete", message: "Research complete" });
|
|
9590
9831
|
return report;
|
|
9591
9832
|
} finally {
|
|
9592
|
-
await
|
|
9833
|
+
await fs35.rm(tempBase, { recursive: true, force: true }).catch(() => {
|
|
9593
9834
|
});
|
|
9594
9835
|
}
|
|
9595
9836
|
}
|
|
@@ -9960,6 +10201,7 @@ async function detectExistingRepo(dir) {
|
|
|
9960
10201
|
}
|
|
9961
10202
|
|
|
9962
10203
|
// src/commands/describe.ts
|
|
10204
|
+
init_persist();
|
|
9963
10205
|
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
10206
|
printFullBanner("The Agent Environment Compiler");
|
|
9965
10207
|
const config = await loadConfig();
|
|
@@ -10059,6 +10301,13 @@ Autonomy level: ${autonomyLevel} (${autonomyLabel(autonomyLevel)})`;
|
|
|
10059
10301
|
`));
|
|
10060
10302
|
process.exit(1);
|
|
10061
10303
|
}
|
|
10304
|
+
if (spec.ir) {
|
|
10305
|
+
try {
|
|
10306
|
+
await persistHarnessIR(process.cwd(), spec.ir);
|
|
10307
|
+
} catch {
|
|
10308
|
+
console.log(ui.warn("Could not persist harness IR to .kairn/harness-ir.json"));
|
|
10309
|
+
}
|
|
10310
|
+
}
|
|
10062
10311
|
const registry = await loadRegistry();
|
|
10063
10312
|
const summary = summarizeSpec(spec, registry);
|
|
10064
10313
|
console.log("");
|
|
@@ -10133,14 +10382,14 @@ init_ui();
|
|
|
10133
10382
|
init_logo();
|
|
10134
10383
|
import { Command as Command4 } from "commander";
|
|
10135
10384
|
import chalk7 from "chalk";
|
|
10136
|
-
import
|
|
10137
|
-
import
|
|
10385
|
+
import fs15 from "fs/promises";
|
|
10386
|
+
import path15 from "path";
|
|
10138
10387
|
var listCommand = new Command4("list").description("Show saved environments").action(async () => {
|
|
10139
10388
|
printCompactBanner();
|
|
10140
10389
|
const envsDir = getEnvsDir();
|
|
10141
10390
|
let files;
|
|
10142
10391
|
try {
|
|
10143
|
-
files = await
|
|
10392
|
+
files = await fs15.readdir(envsDir);
|
|
10144
10393
|
} catch {
|
|
10145
10394
|
console.log(chalk7.dim(" No environments yet. Run ") + chalk7.bold("kairn describe") + chalk7.dim(" to create one.\n"));
|
|
10146
10395
|
return;
|
|
@@ -10153,7 +10402,7 @@ var listCommand = new Command4("list").description("Show saved environments").ac
|
|
|
10153
10402
|
let first = true;
|
|
10154
10403
|
for (const file of jsonFiles) {
|
|
10155
10404
|
try {
|
|
10156
|
-
const data = await
|
|
10405
|
+
const data = await fs15.readFile(path15.join(envsDir, file), "utf-8");
|
|
10157
10406
|
const spec = JSON.parse(data);
|
|
10158
10407
|
const date = new Date(spec.created_at).toLocaleDateString();
|
|
10159
10408
|
const toolCount = spec.tools?.length ?? 0;
|
|
@@ -10178,8 +10427,8 @@ init_ui();
|
|
|
10178
10427
|
init_logo();
|
|
10179
10428
|
import { Command as Command5 } from "commander";
|
|
10180
10429
|
import chalk8 from "chalk";
|
|
10181
|
-
import
|
|
10182
|
-
import
|
|
10430
|
+
import fs16 from "fs/promises";
|
|
10431
|
+
import path16 from "path";
|
|
10183
10432
|
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
10433
|
printCompactBanner();
|
|
10185
10434
|
const envsDir = getEnvsDir();
|
|
@@ -10189,7 +10438,7 @@ var activateCommand = new Command5("activate").description("Re-deploy a saved en
|
|
|
10189
10438
|
let fromTemplate = false;
|
|
10190
10439
|
let envFiles = [];
|
|
10191
10440
|
try {
|
|
10192
|
-
envFiles = await
|
|
10441
|
+
envFiles = await fs16.readdir(envsDir);
|
|
10193
10442
|
} catch {
|
|
10194
10443
|
}
|
|
10195
10444
|
match = envFiles.find(
|
|
@@ -10200,7 +10449,7 @@ var activateCommand = new Command5("activate").description("Re-deploy a saved en
|
|
|
10200
10449
|
} else {
|
|
10201
10450
|
let templateFiles = [];
|
|
10202
10451
|
try {
|
|
10203
|
-
templateFiles = await
|
|
10452
|
+
templateFiles = await fs16.readdir(templatesDir);
|
|
10204
10453
|
} catch {
|
|
10205
10454
|
}
|
|
10206
10455
|
match = templateFiles.find(
|
|
@@ -10216,7 +10465,7 @@ var activateCommand = new Command5("activate").description("Re-deploy a saved en
|
|
|
10216
10465
|
process.exit(1);
|
|
10217
10466
|
}
|
|
10218
10467
|
}
|
|
10219
|
-
const data = await
|
|
10468
|
+
const data = await fs16.readFile(path16.join(sourceDir, match), "utf-8");
|
|
10220
10469
|
const spec = JSON.parse(data);
|
|
10221
10470
|
const label = fromTemplate ? chalk8.dim(" (template)") : "";
|
|
10222
10471
|
console.log(chalk8.cyan(` Activating: ${spec.name}`) + label);
|
|
@@ -10236,21 +10485,21 @@ init_ui();
|
|
|
10236
10485
|
init_logo();
|
|
10237
10486
|
import { Command as Command6 } from "commander";
|
|
10238
10487
|
import chalk9 from "chalk";
|
|
10239
|
-
import
|
|
10240
|
-
import
|
|
10488
|
+
import fs17 from "fs/promises";
|
|
10489
|
+
import path17 from "path";
|
|
10241
10490
|
import { fileURLToPath as fileURLToPath4 } from "url";
|
|
10242
10491
|
var REGISTRY_URL = "https://raw.githubusercontent.com/ashtonperlroth/kairn/main/src/registry/tools.json";
|
|
10243
10492
|
async function getLocalRegistryPath() {
|
|
10244
10493
|
const __filename3 = fileURLToPath4(import.meta.url);
|
|
10245
|
-
const __dirname3 =
|
|
10494
|
+
const __dirname3 = path17.dirname(__filename3);
|
|
10246
10495
|
const candidates = [
|
|
10247
|
-
|
|
10248
|
-
|
|
10249
|
-
|
|
10496
|
+
path17.resolve(__dirname3, "../registry/tools.json"),
|
|
10497
|
+
path17.resolve(__dirname3, "../src/registry/tools.json"),
|
|
10498
|
+
path17.resolve(__dirname3, "../../src/registry/tools.json")
|
|
10250
10499
|
];
|
|
10251
10500
|
for (const candidate of candidates) {
|
|
10252
10501
|
try {
|
|
10253
|
-
await
|
|
10502
|
+
await fs17.access(candidate);
|
|
10254
10503
|
return candidate;
|
|
10255
10504
|
} catch {
|
|
10256
10505
|
continue;
|
|
@@ -10287,10 +10536,10 @@ var updateRegistryCommand = new Command6("update-registry").description("Fetch t
|
|
|
10287
10536
|
const registryPath = await getLocalRegistryPath();
|
|
10288
10537
|
const backupPath = registryPath + ".bak";
|
|
10289
10538
|
try {
|
|
10290
|
-
await
|
|
10539
|
+
await fs17.copyFile(registryPath, backupPath);
|
|
10291
10540
|
} catch {
|
|
10292
10541
|
}
|
|
10293
|
-
await
|
|
10542
|
+
await fs17.writeFile(registryPath, JSON.stringify(tools, null, 2), "utf-8");
|
|
10294
10543
|
console.log(ui.success(`Registry updated: ${tools.length} tools`));
|
|
10295
10544
|
console.log(chalk9.dim(` Saved to: ${registryPath}`));
|
|
10296
10545
|
console.log(chalk9.dim(` Backup: ${backupPath}
|
|
@@ -10669,14 +10918,14 @@ init_ui();
|
|
|
10669
10918
|
init_logo();
|
|
10670
10919
|
import { Command as Command9 } from "commander";
|
|
10671
10920
|
import chalk12 from "chalk";
|
|
10672
|
-
import
|
|
10673
|
-
import
|
|
10921
|
+
import fs18 from "fs/promises";
|
|
10922
|
+
import path18 from "path";
|
|
10674
10923
|
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
10924
|
printCompactBanner();
|
|
10676
10925
|
const templatesDir = getTemplatesDir();
|
|
10677
10926
|
let files;
|
|
10678
10927
|
try {
|
|
10679
|
-
files = await
|
|
10928
|
+
files = await fs18.readdir(templatesDir);
|
|
10680
10929
|
} catch {
|
|
10681
10930
|
console.log(
|
|
10682
10931
|
chalk12.dim(
|
|
@@ -10701,8 +10950,8 @@ var templatesCommand = new Command9("templates").description("Browse available t
|
|
|
10701
10950
|
const templates = [];
|
|
10702
10951
|
for (const file of jsonFiles) {
|
|
10703
10952
|
try {
|
|
10704
|
-
const data = await
|
|
10705
|
-
|
|
10953
|
+
const data = await fs18.readFile(
|
|
10954
|
+
path18.join(templatesDir, file),
|
|
10706
10955
|
"utf-8"
|
|
10707
10956
|
);
|
|
10708
10957
|
const spec = JSON.parse(data);
|
|
@@ -10753,8 +11002,8 @@ init_loader();
|
|
|
10753
11002
|
import { Command as Command10 } from "commander";
|
|
10754
11003
|
import { password as password3 } from "@inquirer/prompts";
|
|
10755
11004
|
import chalk13 from "chalk";
|
|
10756
|
-
import
|
|
10757
|
-
import
|
|
11005
|
+
import fs19 from "fs/promises";
|
|
11006
|
+
import path19 from "path";
|
|
10758
11007
|
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
11008
|
printCompactBanner();
|
|
10760
11009
|
const targetDir = process.cwd();
|
|
@@ -10866,8 +11115,8 @@ var keysCommand = new Command10("keys").description("Add or update API keys for
|
|
|
10866
11115
|
for (const [key, value] of envEntries) {
|
|
10867
11116
|
envLines.push(`${key}=${value}`);
|
|
10868
11117
|
}
|
|
10869
|
-
const envPath =
|
|
10870
|
-
await
|
|
11118
|
+
const envPath = path19.join(targetDir, ".env");
|
|
11119
|
+
await fs19.writeFile(envPath, envLines.join("\n") + "\n", "utf-8");
|
|
10871
11120
|
console.log(chalk13.green(` \u2713 ${keysEntered} key(s) saved to .env`));
|
|
10872
11121
|
console.log("");
|
|
10873
11122
|
});
|
|
@@ -10877,90 +11126,131 @@ init_ui();
|
|
|
10877
11126
|
import { Command as Command11 } from "commander";
|
|
10878
11127
|
import chalk14 from "chalk";
|
|
10879
11128
|
import ora2 from "ora";
|
|
10880
|
-
import
|
|
10881
|
-
import
|
|
11129
|
+
import fs36 from "fs/promises";
|
|
11130
|
+
import path37 from "path";
|
|
10882
11131
|
import { parse as yamlParse2 } from "yaml";
|
|
10883
11132
|
import { confirm as confirm4, input as input4, select as select4 } from "@inquirer/prompts";
|
|
10884
11133
|
|
|
10885
11134
|
// src/evolve/init.ts
|
|
10886
11135
|
init_config();
|
|
10887
|
-
|
|
10888
|
-
import
|
|
11136
|
+
init_cache();
|
|
11137
|
+
import fs20 from "fs/promises";
|
|
11138
|
+
import path20 from "path";
|
|
10889
11139
|
import { stringify as yamlStringify } from "yaml";
|
|
10890
11140
|
|
|
10891
11141
|
// src/evolve/templates.ts
|
|
10892
11142
|
init_llm();
|
|
10893
11143
|
var EVAL_TEMPLATES = {
|
|
11144
|
+
// --- Harness-sensitivity templates (probe whether agent follows .claude/ harness) ---
|
|
10894
11145
|
"add-feature": {
|
|
10895
11146
|
id: "add-feature",
|
|
10896
11147
|
name: "Add Feature",
|
|
10897
11148
|
description: "Can the agent add a new capability?",
|
|
10898
|
-
bestFor: ["feature-development", "api-building", "full-stack"]
|
|
11149
|
+
bestFor: ["feature-development", "api-building", "full-stack"],
|
|
11150
|
+
category: "harness-sensitivity"
|
|
10899
11151
|
},
|
|
10900
11152
|
"fix-bug": {
|
|
10901
11153
|
id: "fix-bug",
|
|
10902
11154
|
name: "Fix Bug",
|
|
10903
11155
|
description: "Can the agent diagnose and fix a problem?",
|
|
10904
|
-
bestFor: ["maintenance", "debugging", "qa"]
|
|
11156
|
+
bestFor: ["maintenance", "debugging", "qa"],
|
|
11157
|
+
category: "harness-sensitivity"
|
|
10905
11158
|
},
|
|
10906
11159
|
"refactor": {
|
|
10907
11160
|
id: "refactor",
|
|
10908
11161
|
name: "Refactor",
|
|
10909
11162
|
description: "Can the agent restructure code?",
|
|
10910
|
-
bestFor: ["maintenance", "architecture", "backend"]
|
|
11163
|
+
bestFor: ["maintenance", "architecture", "backend"],
|
|
11164
|
+
category: "harness-sensitivity"
|
|
10911
11165
|
},
|
|
10912
11166
|
"test-writing": {
|
|
10913
11167
|
id: "test-writing",
|
|
10914
11168
|
name: "Test Writing",
|
|
10915
11169
|
description: "Can the agent write tests?",
|
|
10916
|
-
bestFor: ["tdd", "qa", "backend"]
|
|
11170
|
+
bestFor: ["tdd", "qa", "backend"],
|
|
11171
|
+
category: "harness-sensitivity"
|
|
10917
11172
|
},
|
|
10918
11173
|
"config-change": {
|
|
10919
11174
|
id: "config-change",
|
|
10920
11175
|
name: "Config Change",
|
|
10921
11176
|
description: "Can the agent update configuration?",
|
|
10922
|
-
bestFor: ["devops", "infrastructure", "backend"]
|
|
11177
|
+
bestFor: ["devops", "infrastructure", "backend"],
|
|
11178
|
+
category: "harness-sensitivity"
|
|
10923
11179
|
},
|
|
10924
11180
|
"documentation": {
|
|
10925
11181
|
id: "documentation",
|
|
10926
11182
|
name: "Documentation",
|
|
10927
11183
|
description: "Can the agent write and update docs?",
|
|
10928
|
-
bestFor: ["content", "api-building", "full-stack"]
|
|
11184
|
+
bestFor: ["content", "api-building", "full-stack"],
|
|
11185
|
+
category: "harness-sensitivity"
|
|
10929
11186
|
},
|
|
10930
11187
|
"convention-adherence": {
|
|
10931
11188
|
id: "convention-adherence",
|
|
10932
11189
|
name: "Convention Adherence",
|
|
10933
11190
|
description: "Does the agent follow all project conventions defined in CLAUDE.md?",
|
|
10934
|
-
bestFor: ["feature-development", "full-stack", "backend", "maintenance"]
|
|
11191
|
+
bestFor: ["feature-development", "full-stack", "backend", "maintenance"],
|
|
11192
|
+
category: "harness-sensitivity"
|
|
10935
11193
|
},
|
|
10936
11194
|
"workflow-compliance": {
|
|
10937
11195
|
id: "workflow-compliance",
|
|
10938
11196
|
name: "Workflow Compliance",
|
|
10939
11197
|
description: "Does the agent use the project workflow commands and skills?",
|
|
10940
|
-
bestFor: ["feature-development", "full-stack", "tdd", "qa"]
|
|
11198
|
+
bestFor: ["feature-development", "full-stack", "tdd", "qa"],
|
|
11199
|
+
category: "harness-sensitivity"
|
|
10941
11200
|
},
|
|
10942
11201
|
"rule-compliance": {
|
|
10943
11202
|
id: "rule-compliance",
|
|
10944
11203
|
name: "Rule Compliance",
|
|
10945
11204
|
description: "Does the agent follow all project rules without violations?",
|
|
10946
|
-
bestFor: ["feature-development", "backend", "maintenance", "architecture"]
|
|
11205
|
+
bestFor: ["feature-development", "backend", "maintenance", "architecture"],
|
|
11206
|
+
category: "harness-sensitivity"
|
|
10947
11207
|
},
|
|
10948
11208
|
"intent-routing": {
|
|
10949
11209
|
id: "intent-routing",
|
|
10950
11210
|
name: "Intent Routing",
|
|
10951
11211
|
description: "Test that natural language prompts route to the correct workflow command via intent hooks",
|
|
10952
|
-
bestFor: ["feature-development", "full-stack", "api-building"]
|
|
11212
|
+
bestFor: ["feature-development", "full-stack", "api-building"],
|
|
11213
|
+
category: "harness-sensitivity"
|
|
10953
11214
|
},
|
|
10954
11215
|
"persistence-completion": {
|
|
10955
11216
|
id: "persistence-completion",
|
|
10956
11217
|
name: "Persistence Completion",
|
|
10957
11218
|
description: "Can the agent complete a multi-criterion task using the persistence loop?",
|
|
10958
|
-
bestFor: ["feature-development", "full-stack", "api-building", "maintenance"]
|
|
11219
|
+
bestFor: ["feature-development", "full-stack", "api-building", "maintenance"],
|
|
11220
|
+
category: "harness-sensitivity"
|
|
11221
|
+
},
|
|
11222
|
+
// --- Substantive SWE-bench-style templates (test real coding ability) ---
|
|
11223
|
+
"real-bug-fix": {
|
|
11224
|
+
id: "real-bug-fix",
|
|
11225
|
+
name: "Real Bug Fix",
|
|
11226
|
+
description: "Injects a known bug into a source file and asks the agent to diagnose and fix it, mimicking a real GitHub issue",
|
|
11227
|
+
bestFor: ["debugging", "maintenance", "qa", "backend"],
|
|
11228
|
+
category: "substantive"
|
|
11229
|
+
},
|
|
11230
|
+
"real-feature-add": {
|
|
11231
|
+
id: "real-feature-add",
|
|
11232
|
+
name: "Real Feature Add",
|
|
11233
|
+
description: "Describes a concrete feature with clear acceptance criteria and verifies the agent implements it correctly",
|
|
11234
|
+
bestFor: ["feature-development", "full-stack", "api-building", "backend"],
|
|
11235
|
+
category: "substantive"
|
|
11236
|
+
},
|
|
11237
|
+
"codebase-question": {
|
|
11238
|
+
id: "codebase-question",
|
|
11239
|
+
name: "Codebase Question",
|
|
11240
|
+
description: "Asks a factual question about codebase knowledge and checks the answer via LLM-as-judge against a known-correct answer",
|
|
11241
|
+
bestFor: ["research", "architecture", "maintenance", "debugging"],
|
|
11242
|
+
category: "substantive"
|
|
10959
11243
|
}
|
|
10960
11244
|
};
|
|
11245
|
+
var SUBSTANTIVE_FLOOR = [
|
|
11246
|
+
"real-bug-fix",
|
|
11247
|
+
"real-feature-add",
|
|
11248
|
+
"codebase-question"
|
|
11249
|
+
];
|
|
11250
|
+
var MAX_HARNESS_TEMPLATES = 4;
|
|
10961
11251
|
function selectTemplatesForWorkflow(workflowType) {
|
|
10962
|
-
const
|
|
10963
|
-
"feature-development": ["add-feature", "test-writing", "
|
|
11252
|
+
const harnessMapping = {
|
|
11253
|
+
"feature-development": ["add-feature", "test-writing", "intent-routing", "convention-adherence", "workflow-compliance", "persistence-completion"],
|
|
10964
11254
|
"api-building": ["add-feature", "fix-bug", "test-writing", "convention-adherence", "persistence-completion"],
|
|
10965
11255
|
"full-stack": ["add-feature", "fix-bug", "test-writing", "convention-adherence", "persistence-completion"],
|
|
10966
11256
|
"maintenance": ["fix-bug", "refactor", "test-writing", "rule-compliance", "persistence-completion"],
|
|
@@ -10974,7 +11264,8 @@ function selectTemplatesForWorkflow(workflowType) {
|
|
|
10974
11264
|
"content": ["documentation", "add-feature", "convention-adherence"],
|
|
10975
11265
|
"research": ["documentation", "add-feature", "convention-adherence"]
|
|
10976
11266
|
};
|
|
10977
|
-
|
|
11267
|
+
const harness = (harnessMapping[workflowType] ?? ["add-feature", "fix-bug", "test-writing", "convention-adherence"]).slice(0, MAX_HARNESS_TEMPLATES);
|
|
11268
|
+
return [...SUBSTANTIVE_FLOOR, ...harness];
|
|
10978
11269
|
}
|
|
10979
11270
|
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
11271
|
|
|
@@ -10988,6 +11279,15 @@ IMPORTANT: For harness-aware templates (convention-adherence, workflow-complianc
|
|
|
10988
11279
|
|
|
10989
11280
|
These harness-aware tasks are critical \u2014 they test whether the .claude/ environment actually improves agent behavior.
|
|
10990
11281
|
|
|
11282
|
+
SUBSTANTIVE SWE-bench-style templates test real coding ability beyond harness adherence:
|
|
11283
|
+
- 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".
|
|
11284
|
+
- 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".
|
|
11285
|
+
- 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".
|
|
11286
|
+
|
|
11287
|
+
Each task MUST include a "category" field:
|
|
11288
|
+
- "harness-sensitivity" for templates that test .claude/ harness adherence
|
|
11289
|
+
- "substantive" for SWE-bench-style templates that test real coding ability
|
|
11290
|
+
|
|
10991
11291
|
Return a JSON object with a "tasks" array. Each task has:
|
|
10992
11292
|
- id: kebab-case identifier (e.g., "add-health-endpoint")
|
|
10993
11293
|
- template: which eval template this instantiates
|
|
@@ -10995,8 +11295,11 @@ Return a JSON object with a "tasks" array. Each task has:
|
|
|
10995
11295
|
- setup: shell commands to prepare the workspace (e.g., "npm install")
|
|
10996
11296
|
- expected_outcome: multi-line string describing what success looks like
|
|
10997
11297
|
- scoring: "pass-fail", "llm-judge", or "rubric"
|
|
11298
|
+
- category: "harness-sensitivity" or "substantive"
|
|
10998
11299
|
- timeout: seconds (300 for features/bugs, 600 for refactors, 180 for config/docs/tests)
|
|
10999
11300
|
|
|
11301
|
+
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.
|
|
11302
|
+
|
|
11000
11303
|
Return ONLY valid JSON, no markdown fences.`;
|
|
11001
11304
|
function parseJsonResponse(raw) {
|
|
11002
11305
|
let cleaned = raw.trim();
|
|
@@ -11038,7 +11341,56 @@ function validateTask(obj, index) {
|
|
|
11038
11341
|
}
|
|
11039
11342
|
return record;
|
|
11040
11343
|
}
|
|
11041
|
-
function
|
|
11344
|
+
function buildAnalysisContext(analysis) {
|
|
11345
|
+
const lines = ["## Project Analysis", ""];
|
|
11346
|
+
lines.push(`Purpose: ${analysis.purpose}`);
|
|
11347
|
+
lines.push(`Domain: ${analysis.domain}`);
|
|
11348
|
+
lines.push(`Architecture: ${analysis.architecture_style}`);
|
|
11349
|
+
lines.push(`Deployment: ${analysis.deployment_model}`);
|
|
11350
|
+
lines.push("");
|
|
11351
|
+
if (analysis.key_modules.length > 0) {
|
|
11352
|
+
lines.push("### Key Modules");
|
|
11353
|
+
lines.push("");
|
|
11354
|
+
for (const mod of analysis.key_modules) {
|
|
11355
|
+
lines.push(`- **${mod.name}** (${mod.path}): ${mod.description}`);
|
|
11356
|
+
if (mod.responsibilities.length > 0) {
|
|
11357
|
+
lines.push(` Responsibilities: ${mod.responsibilities.join(", ")}`);
|
|
11358
|
+
}
|
|
11359
|
+
}
|
|
11360
|
+
lines.push("");
|
|
11361
|
+
}
|
|
11362
|
+
if (analysis.workflows.length > 0) {
|
|
11363
|
+
lines.push("### Workflows");
|
|
11364
|
+
lines.push("");
|
|
11365
|
+
for (const wf of analysis.workflows) {
|
|
11366
|
+
lines.push(`- **${wf.name}**: ${wf.description} (trigger: ${wf.trigger})`);
|
|
11367
|
+
lines.push(` Steps: ${wf.steps.join(" -> ")}`);
|
|
11368
|
+
}
|
|
11369
|
+
lines.push("");
|
|
11370
|
+
}
|
|
11371
|
+
if (analysis.config_keys.length > 0) {
|
|
11372
|
+
lines.push("### Config Keys");
|
|
11373
|
+
lines.push("");
|
|
11374
|
+
for (const key of analysis.config_keys) {
|
|
11375
|
+
lines.push(`- ${key.name}: ${key.purpose}`);
|
|
11376
|
+
}
|
|
11377
|
+
lines.push("");
|
|
11378
|
+
}
|
|
11379
|
+
if (analysis.dataflow.length > 0) {
|
|
11380
|
+
lines.push("### Data Flow");
|
|
11381
|
+
lines.push("");
|
|
11382
|
+
for (const edge of analysis.dataflow) {
|
|
11383
|
+
lines.push(`- ${edge.from} -> ${edge.to}: ${edge.data}`);
|
|
11384
|
+
}
|
|
11385
|
+
lines.push("");
|
|
11386
|
+
}
|
|
11387
|
+
lines.push("IMPORTANT: Use this analysis to generate domain-specific tasks:");
|
|
11388
|
+
lines.push("- real-bug-fix tasks should reference actual module names and paths listed above");
|
|
11389
|
+
lines.push("- codebase-question tasks should ask about actual workflows, modules, and config keys");
|
|
11390
|
+
lines.push("- real-feature-add tasks should extend actual functionality in the modules listed above");
|
|
11391
|
+
return lines.join("\n");
|
|
11392
|
+
}
|
|
11393
|
+
function buildTaskGenerationMessage(claudeMd, projectProfile, templates, analysis) {
|
|
11042
11394
|
const profileLines = [
|
|
11043
11395
|
`Language: ${projectProfile.language ?? "unknown"}`,
|
|
11044
11396
|
`Framework: ${projectProfile.framework ?? "none"}`,
|
|
@@ -11049,7 +11401,7 @@ function buildTaskGenerationMessage(claudeMd, projectProfile, templates) {
|
|
|
11049
11401
|
const meta = EVAL_TEMPLATES[t];
|
|
11050
11402
|
return `- ${t}: ${meta.description}`;
|
|
11051
11403
|
}).join("\n");
|
|
11052
|
-
|
|
11404
|
+
const sections = [
|
|
11053
11405
|
"## CLAUDE.md",
|
|
11054
11406
|
"",
|
|
11055
11407
|
claudeMd,
|
|
@@ -11057,16 +11409,23 @@ function buildTaskGenerationMessage(claudeMd, projectProfile, templates) {
|
|
|
11057
11409
|
"## Project Profile",
|
|
11058
11410
|
"",
|
|
11059
11411
|
...profileLines,
|
|
11060
|
-
""
|
|
11412
|
+
""
|
|
11413
|
+
];
|
|
11414
|
+
if (analysis) {
|
|
11415
|
+
sections.push(buildAnalysisContext(analysis));
|
|
11416
|
+
sections.push("");
|
|
11417
|
+
}
|
|
11418
|
+
sections.push(
|
|
11061
11419
|
"## Selected Eval Templates",
|
|
11062
11420
|
"",
|
|
11063
11421
|
templateDescriptions,
|
|
11064
11422
|
"",
|
|
11065
11423
|
"Generate concrete, project-specific tasks for each template above."
|
|
11066
|
-
|
|
11424
|
+
);
|
|
11425
|
+
return sections.join("\n");
|
|
11067
11426
|
}
|
|
11068
|
-
async function generateTasksFromTemplates(claudeMd, projectProfile, templates, config) {
|
|
11069
|
-
const userMessage = buildTaskGenerationMessage(claudeMd, projectProfile, templates);
|
|
11427
|
+
async function generateTasksFromTemplates(claudeMd, projectProfile, templates, config, analysis) {
|
|
11428
|
+
const userMessage = buildTaskGenerationMessage(claudeMd, projectProfile, templates, analysis);
|
|
11070
11429
|
const rawResponse = await callLLM(config, userMessage, {
|
|
11071
11430
|
systemPrompt: TASK_GENERATION_PROMPT,
|
|
11072
11431
|
maxTokens: 4096
|
|
@@ -11088,10 +11447,10 @@ async function generateTasksFromTemplates(claudeMd, projectProfile, templates, c
|
|
|
11088
11447
|
|
|
11089
11448
|
// src/evolve/init.ts
|
|
11090
11449
|
async function createEvolveWorkspace(projectRoot, config) {
|
|
11091
|
-
const workspace =
|
|
11092
|
-
await
|
|
11093
|
-
await
|
|
11094
|
-
await
|
|
11450
|
+
const workspace = path20.join(projectRoot, ".kairn-evolve");
|
|
11451
|
+
await fs20.mkdir(path20.join(workspace, "baseline"), { recursive: true });
|
|
11452
|
+
await fs20.mkdir(path20.join(workspace, "traces"), { recursive: true });
|
|
11453
|
+
await fs20.mkdir(path20.join(workspace, "iterations"), { recursive: true });
|
|
11095
11454
|
const configObj = {
|
|
11096
11455
|
model: config.model,
|
|
11097
11456
|
proposer_model: config.proposerModel,
|
|
@@ -11099,8 +11458,8 @@ async function createEvolveWorkspace(projectRoot, config) {
|
|
|
11099
11458
|
max_iterations: config.maxIterations,
|
|
11100
11459
|
parallel_tasks: config.parallelTasks
|
|
11101
11460
|
};
|
|
11102
|
-
await
|
|
11103
|
-
|
|
11461
|
+
await fs20.writeFile(
|
|
11462
|
+
path20.join(workspace, "config.yaml"),
|
|
11104
11463
|
yamlStringify(configObj),
|
|
11105
11464
|
"utf-8"
|
|
11106
11465
|
);
|
|
@@ -11116,12 +11475,13 @@ async function writeTasksFile(workspacePath, tasks) {
|
|
|
11116
11475
|
expected_outcome: t.expected_outcome,
|
|
11117
11476
|
scoring: t.scoring,
|
|
11118
11477
|
...t.rubric ? { rubric: t.rubric } : {},
|
|
11119
|
-
timeout: t.timeout
|
|
11478
|
+
timeout: t.timeout,
|
|
11479
|
+
...t.category ? { category: t.category } : {}
|
|
11120
11480
|
}))
|
|
11121
11481
|
};
|
|
11122
11482
|
const header = "# .kairn-evolve/tasks.yaml\n# Auto-generated by kairn evolve init \u2014 edit freely\n";
|
|
11123
|
-
await
|
|
11124
|
-
|
|
11483
|
+
await fs20.writeFile(
|
|
11484
|
+
path20.join(workspacePath, "tasks.yaml"),
|
|
11125
11485
|
header + yamlStringify(doc),
|
|
11126
11486
|
"utf-8"
|
|
11127
11487
|
);
|
|
@@ -11134,8 +11494,8 @@ async function buildProjectProfile(projectRoot) {
|
|
|
11134
11494
|
keyFiles: []
|
|
11135
11495
|
};
|
|
11136
11496
|
try {
|
|
11137
|
-
const pkgStr = await
|
|
11138
|
-
|
|
11497
|
+
const pkgStr = await fs20.readFile(
|
|
11498
|
+
path20.join(projectRoot, "package.json"),
|
|
11139
11499
|
"utf-8"
|
|
11140
11500
|
);
|
|
11141
11501
|
const pkg2 = JSON.parse(pkgStr);
|
|
@@ -11162,18 +11522,18 @@ async function buildProjectProfile(projectRoot) {
|
|
|
11162
11522
|
}
|
|
11163
11523
|
if (!profile.language) {
|
|
11164
11524
|
try {
|
|
11165
|
-
await
|
|
11525
|
+
await fs20.access(path20.join(projectRoot, "pyproject.toml"));
|
|
11166
11526
|
profile.language = "python";
|
|
11167
11527
|
} catch {
|
|
11168
11528
|
try {
|
|
11169
|
-
await
|
|
11529
|
+
await fs20.access(path20.join(projectRoot, "requirements.txt"));
|
|
11170
11530
|
profile.language = "python";
|
|
11171
11531
|
} catch {
|
|
11172
11532
|
}
|
|
11173
11533
|
}
|
|
11174
11534
|
}
|
|
11175
11535
|
try {
|
|
11176
|
-
const entries = await
|
|
11536
|
+
const entries = await fs20.readdir(projectRoot);
|
|
11177
11537
|
const keyPatterns = [
|
|
11178
11538
|
"README.md",
|
|
11179
11539
|
"package.json",
|
|
@@ -11189,6 +11549,16 @@ async function buildProjectProfile(projectRoot) {
|
|
|
11189
11549
|
}
|
|
11190
11550
|
return profile;
|
|
11191
11551
|
}
|
|
11552
|
+
async function loadProjectAnalysis(projectRoot) {
|
|
11553
|
+
try {
|
|
11554
|
+
const cache = await readCache(projectRoot);
|
|
11555
|
+
if (cache?.analysis) {
|
|
11556
|
+
return cache.analysis;
|
|
11557
|
+
}
|
|
11558
|
+
} catch {
|
|
11559
|
+
}
|
|
11560
|
+
return void 0;
|
|
11561
|
+
}
|
|
11192
11562
|
async function autoGenerateTasks(projectRoot, workflowType) {
|
|
11193
11563
|
const config = await loadConfig();
|
|
11194
11564
|
if (!config) {
|
|
@@ -11196,15 +11566,16 @@ async function autoGenerateTasks(projectRoot, workflowType) {
|
|
|
11196
11566
|
}
|
|
11197
11567
|
let claudeMd = "";
|
|
11198
11568
|
try {
|
|
11199
|
-
claudeMd = await
|
|
11200
|
-
|
|
11569
|
+
claudeMd = await fs20.readFile(
|
|
11570
|
+
path20.join(projectRoot, ".claude", "CLAUDE.md"),
|
|
11201
11571
|
"utf-8"
|
|
11202
11572
|
);
|
|
11203
11573
|
} catch {
|
|
11204
11574
|
}
|
|
11205
11575
|
const profile = await buildProjectProfile(projectRoot);
|
|
11576
|
+
const analysis = await loadProjectAnalysis(projectRoot);
|
|
11206
11577
|
const templates = selectTemplatesForWorkflow(workflowType);
|
|
11207
|
-
return generateTasksFromTemplates(claudeMd, profile, templates, config);
|
|
11578
|
+
return generateTasksFromTemplates(claudeMd, profile, templates, config, analysis);
|
|
11208
11579
|
}
|
|
11209
11580
|
|
|
11210
11581
|
// src/commands/evolve.ts
|
|
@@ -11216,8 +11587,8 @@ init_loop();
|
|
|
11216
11587
|
|
|
11217
11588
|
// src/evolve/report.ts
|
|
11218
11589
|
init_trace();
|
|
11219
|
-
import
|
|
11220
|
-
import
|
|
11590
|
+
import fs32 from "fs/promises";
|
|
11591
|
+
import path32 from "path";
|
|
11221
11592
|
|
|
11222
11593
|
// src/evolve/diagnosis.ts
|
|
11223
11594
|
function numericScore(s) {
|
|
@@ -11267,10 +11638,10 @@ function numericScore2(s) {
|
|
|
11267
11638
|
return s.score ?? (s.pass ? 100 : 0);
|
|
11268
11639
|
}
|
|
11269
11640
|
async function loadAllIterations(workspacePath) {
|
|
11270
|
-
const iterDir =
|
|
11641
|
+
const iterDir = path32.join(workspacePath, "iterations");
|
|
11271
11642
|
let entries;
|
|
11272
11643
|
try {
|
|
11273
|
-
entries = await
|
|
11644
|
+
entries = await fs32.readdir(iterDir);
|
|
11274
11645
|
} catch {
|
|
11275
11646
|
return [];
|
|
11276
11647
|
}
|
|
@@ -11284,13 +11655,41 @@ async function loadAllIterations(workspacePath) {
|
|
|
11284
11655
|
}
|
|
11285
11656
|
async function loadTasks(workspacePath) {
|
|
11286
11657
|
try {
|
|
11287
|
-
const content = await
|
|
11658
|
+
const content = await fs32.readFile(path32.join(workspacePath, "tasks.yaml"), "utf-8");
|
|
11288
11659
|
const parsed = yamlParse(content);
|
|
11289
|
-
|
|
11660
|
+
const tasks = parsed?.tasks ?? [];
|
|
11661
|
+
return tasks.map((t) => ({
|
|
11662
|
+
...t,
|
|
11663
|
+
category: t.category ?? "harness-sensitivity"
|
|
11664
|
+
}));
|
|
11290
11665
|
} catch {
|
|
11291
11666
|
return [];
|
|
11292
11667
|
}
|
|
11293
11668
|
}
|
|
11669
|
+
function computeCategoryBreakdown(tasks, taskResults) {
|
|
11670
|
+
const harnessTasks = [];
|
|
11671
|
+
const substantiveTasks = [];
|
|
11672
|
+
for (const task of tasks) {
|
|
11673
|
+
const result = taskResults[task.id];
|
|
11674
|
+
if (!result) continue;
|
|
11675
|
+
const score = numericScore2(result);
|
|
11676
|
+
const category = task.category ?? "harness-sensitivity";
|
|
11677
|
+
if (category === "harness-sensitivity") {
|
|
11678
|
+
harnessTasks.push(score);
|
|
11679
|
+
} else {
|
|
11680
|
+
substantiveTasks.push(score);
|
|
11681
|
+
}
|
|
11682
|
+
}
|
|
11683
|
+
if (harnessTasks.length === 0 || substantiveTasks.length === 0) {
|
|
11684
|
+
return void 0;
|
|
11685
|
+
}
|
|
11686
|
+
const harnessAvg = harnessTasks.reduce((a, b) => a + b, 0) / harnessTasks.length;
|
|
11687
|
+
const substantiveAvg = substantiveTasks.reduce((a, b) => a + b, 0) / substantiveTasks.length;
|
|
11688
|
+
return {
|
|
11689
|
+
harnessAdherence: { score: harnessAvg, count: harnessTasks.length },
|
|
11690
|
+
substantiveTasks: { score: substantiveAvg, count: substantiveTasks.length }
|
|
11691
|
+
};
|
|
11692
|
+
}
|
|
11294
11693
|
function buildLeaderboard(iterations, tasks) {
|
|
11295
11694
|
const taskIds = tasks.map((t) => t.id);
|
|
11296
11695
|
return taskIds.map((taskId) => {
|
|
@@ -11350,6 +11749,11 @@ async function generateMarkdownReport(workspacePath) {
|
|
|
11350
11749
|
lines.push(`| Best score | ${bestIter.score.toFixed(1)}% |`);
|
|
11351
11750
|
lines.push(`| Best iteration | ${bestIter.iteration} |`);
|
|
11352
11751
|
lines.push(`| Improvement | ${improvement >= 0 ? "+" : ""}${improvement.toFixed(1)} points |`);
|
|
11752
|
+
const categoryBreakdown = computeCategoryBreakdown(tasks, bestIter.taskResults);
|
|
11753
|
+
if (categoryBreakdown) {
|
|
11754
|
+
lines.push(`| Harness adherence | ${categoryBreakdown.harnessAdherence.score.toFixed(1)}% (${categoryBreakdown.harnessAdherence.count} tasks) |`);
|
|
11755
|
+
lines.push(`| Substantive tasks | ${categoryBreakdown.substantiveTasks.score.toFixed(1)}% (${categoryBreakdown.substantiveTasks.count} tasks) |`);
|
|
11756
|
+
}
|
|
11353
11757
|
lines.push("");
|
|
11354
11758
|
lines.push("## Iterations");
|
|
11355
11759
|
lines.push("");
|
|
@@ -11433,10 +11837,12 @@ async function generateJsonReport(workspacePath) {
|
|
|
11433
11837
|
const iterations = await loadAllIterations(workspacePath);
|
|
11434
11838
|
const tasks = await loadTasks(workspacePath);
|
|
11435
11839
|
const baselineScore = iterations.length > 0 ? iterations[0].score : 0;
|
|
11436
|
-
const
|
|
11840
|
+
const bestIterLog = iterations.length > 0 ? iterations.reduce((best, curr) => curr.score > best.score ? curr : best, iterations[0]) : void 0;
|
|
11841
|
+
const bestIter = bestIterLog ?? { score: 0, iteration: 0 };
|
|
11437
11842
|
const improvement = bestIter.score - baselineScore;
|
|
11438
11843
|
const counterfactuals = diagnoseCounterfactuals(iterations, tasks);
|
|
11439
11844
|
const leaderboard = buildLeaderboard(iterations, tasks);
|
|
11845
|
+
const categoryBreakdown = bestIterLog ? computeCategoryBreakdown(tasks, bestIterLog.taskResults) : void 0;
|
|
11440
11846
|
return {
|
|
11441
11847
|
overview: {
|
|
11442
11848
|
title: "Evolution Report",
|
|
@@ -11444,7 +11850,8 @@ async function generateJsonReport(workspacePath) {
|
|
|
11444
11850
|
baselineScore,
|
|
11445
11851
|
bestScore: bestIter.score,
|
|
11446
11852
|
bestIteration: bestIter.iteration,
|
|
11447
|
-
improvement
|
|
11853
|
+
improvement,
|
|
11854
|
+
...categoryBreakdown ? { categoryBreakdown } : {}
|
|
11448
11855
|
},
|
|
11449
11856
|
iterations: iterations.map((iter) => {
|
|
11450
11857
|
const stddevs = Object.values(iter.taskResults).map((s) => s.variance?.stddev).filter((v) => v !== void 0);
|
|
@@ -11470,13 +11877,13 @@ init_mutator();
|
|
|
11470
11877
|
init_baseline();
|
|
11471
11878
|
init_mutator();
|
|
11472
11879
|
init_trace();
|
|
11473
|
-
import
|
|
11474
|
-
import
|
|
11880
|
+
import fs33 from "fs/promises";
|
|
11881
|
+
import path33 from "path";
|
|
11475
11882
|
async function listIterations(workspacePath) {
|
|
11476
|
-
const iterationsDir =
|
|
11883
|
+
const iterationsDir = path33.join(workspacePath, "iterations");
|
|
11477
11884
|
let entries;
|
|
11478
11885
|
try {
|
|
11479
|
-
entries = await
|
|
11886
|
+
entries = await fs33.readdir(iterationsDir);
|
|
11480
11887
|
} catch {
|
|
11481
11888
|
return [];
|
|
11482
11889
|
}
|
|
@@ -11485,7 +11892,7 @@ async function listIterations(workspacePath) {
|
|
|
11485
11892
|
const n = parseInt(entry, 10);
|
|
11486
11893
|
if (!isNaN(n)) {
|
|
11487
11894
|
try {
|
|
11488
|
-
await
|
|
11895
|
+
await fs33.access(path33.join(iterationsDir, entry, "harness"));
|
|
11489
11896
|
nums.push(n);
|
|
11490
11897
|
} catch {
|
|
11491
11898
|
}
|
|
@@ -11511,16 +11918,16 @@ async function listFilesRecursive(dir) {
|
|
|
11511
11918
|
async function walk(current) {
|
|
11512
11919
|
let entries;
|
|
11513
11920
|
try {
|
|
11514
|
-
entries = await
|
|
11921
|
+
entries = await fs33.readdir(current, { withFileTypes: true });
|
|
11515
11922
|
} catch {
|
|
11516
11923
|
return;
|
|
11517
11924
|
}
|
|
11518
11925
|
for (const entry of entries) {
|
|
11519
|
-
const fullPath =
|
|
11926
|
+
const fullPath = path33.join(current, entry.name);
|
|
11520
11927
|
if (entry.isDirectory()) {
|
|
11521
11928
|
await walk(fullPath);
|
|
11522
11929
|
} else {
|
|
11523
|
-
results.push(
|
|
11930
|
+
results.push(path33.relative(dir, fullPath));
|
|
11524
11931
|
}
|
|
11525
11932
|
}
|
|
11526
11933
|
}
|
|
@@ -11528,10 +11935,10 @@ async function listFilesRecursive(dir) {
|
|
|
11528
11935
|
return results;
|
|
11529
11936
|
}
|
|
11530
11937
|
async function findBestPBTHarness(workspacePath) {
|
|
11531
|
-
const branchesDir =
|
|
11938
|
+
const branchesDir = path33.join(workspacePath, "branches");
|
|
11532
11939
|
let branchEntries;
|
|
11533
11940
|
try {
|
|
11534
|
-
branchEntries = await
|
|
11941
|
+
branchEntries = await fs33.readdir(branchesDir);
|
|
11535
11942
|
} catch {
|
|
11536
11943
|
return null;
|
|
11537
11944
|
}
|
|
@@ -11539,7 +11946,7 @@ async function findBestPBTHarness(workspacePath) {
|
|
|
11539
11946
|
let bestPath = "";
|
|
11540
11947
|
let bestLabel = "";
|
|
11541
11948
|
for (const branchId of branchEntries) {
|
|
11542
|
-
const branchPath =
|
|
11949
|
+
const branchPath = path33.join(branchesDir, branchId);
|
|
11543
11950
|
const branchIterations = await listIterations(branchPath);
|
|
11544
11951
|
if (branchIterations.length === 0) continue;
|
|
11545
11952
|
const bestIter = await findBestIteration(branchPath, branchIterations);
|
|
@@ -11547,13 +11954,13 @@ async function findBestPBTHarness(workspacePath) {
|
|
|
11547
11954
|
const score = log?.score ?? 0;
|
|
11548
11955
|
if (score > bestScore) {
|
|
11549
11956
|
bestScore = score;
|
|
11550
|
-
bestPath =
|
|
11957
|
+
bestPath = path33.join(branchPath, "iterations", bestIter.toString(), "harness");
|
|
11551
11958
|
bestLabel = `branch ${branchId}, iteration ${bestIter} (${score.toFixed(1)}%)`;
|
|
11552
11959
|
}
|
|
11553
11960
|
}
|
|
11554
|
-
const synthesisHarness =
|
|
11961
|
+
const synthesisHarness = path33.join(workspacePath, "synthesis", "harness");
|
|
11555
11962
|
try {
|
|
11556
|
-
await
|
|
11963
|
+
await fs33.access(synthesisHarness);
|
|
11557
11964
|
const synthesisLog = await loadIterationLog(workspacePath, 999);
|
|
11558
11965
|
const synthScore = synthesisLog?.score ?? 0;
|
|
11559
11966
|
if (synthScore > bestScore) {
|
|
@@ -11572,26 +11979,26 @@ async function applyEvolution(workspacePath, projectRoot, targetIteration, pbt)
|
|
|
11572
11979
|
if (!pbtResult) {
|
|
11573
11980
|
throw new Error("No PBT results found. Run `kairn evolve pbt` first.");
|
|
11574
11981
|
}
|
|
11575
|
-
const claudeDir2 =
|
|
11982
|
+
const claudeDir2 = path33.join(projectRoot, ".claude");
|
|
11576
11983
|
const diffPreview2 = await generateDiff2(claudeDir2, pbtResult.harnessPath);
|
|
11577
11984
|
const currentFiles2 = await listFilesRecursive(claudeDir2);
|
|
11578
11985
|
const targetFiles2 = await listFilesRecursive(pbtResult.harnessPath);
|
|
11579
11986
|
const allPaths2 = /* @__PURE__ */ new Set([...currentFiles2, ...targetFiles2]);
|
|
11580
11987
|
const filesChanged2 = [];
|
|
11581
11988
|
for (const filePath of allPaths2) {
|
|
11582
|
-
const currentContent = await
|
|
11583
|
-
const targetContent = await
|
|
11989
|
+
const currentContent = await fs33.readFile(path33.join(claudeDir2, filePath), "utf-8").catch(() => null);
|
|
11990
|
+
const targetContent = await fs33.readFile(path33.join(pbtResult.harnessPath, filePath), "utf-8").catch(() => null);
|
|
11584
11991
|
if (currentContent !== targetContent) {
|
|
11585
11992
|
filesChanged2.push(filePath);
|
|
11586
11993
|
}
|
|
11587
11994
|
}
|
|
11588
|
-
await
|
|
11995
|
+
await fs33.rm(claudeDir2, { recursive: true, force: true });
|
|
11589
11996
|
await copyDir(pbtResult.harnessPath, claudeDir2);
|
|
11590
|
-
const harnessMcpJson2 =
|
|
11591
|
-
const projectMcpJson2 =
|
|
11997
|
+
const harnessMcpJson2 = path33.join(pbtResult.harnessPath, ".mcp.json");
|
|
11998
|
+
const projectMcpJson2 = path33.join(projectRoot, ".mcp.json");
|
|
11592
11999
|
try {
|
|
11593
|
-
await
|
|
11594
|
-
await
|
|
12000
|
+
await fs33.access(harnessMcpJson2);
|
|
12001
|
+
await fs33.copyFile(harnessMcpJson2, projectMcpJson2);
|
|
11595
12002
|
if (!filesChanged2.includes(".mcp.json")) filesChanged2.push(".mcp.json");
|
|
11596
12003
|
} catch {
|
|
11597
12004
|
}
|
|
@@ -11617,37 +12024,37 @@ async function applyEvolution(workspacePath, projectRoot, targetIteration, pbt)
|
|
|
11617
12024
|
} else {
|
|
11618
12025
|
iter = await findBestIteration(workspacePath, iterations);
|
|
11619
12026
|
}
|
|
11620
|
-
const harnessPath =
|
|
12027
|
+
const harnessPath = path33.join(
|
|
11621
12028
|
workspacePath,
|
|
11622
12029
|
"iterations",
|
|
11623
12030
|
iter.toString(),
|
|
11624
12031
|
"harness"
|
|
11625
12032
|
);
|
|
11626
|
-
const claudeDir =
|
|
12033
|
+
const claudeDir = path33.join(projectRoot, ".claude");
|
|
11627
12034
|
const diffPreview = await generateDiff2(claudeDir, harnessPath);
|
|
11628
12035
|
const currentFiles = await listFilesRecursive(claudeDir);
|
|
11629
12036
|
const targetFiles = await listFilesRecursive(harnessPath);
|
|
11630
12037
|
const allPaths = /* @__PURE__ */ new Set([...currentFiles, ...targetFiles]);
|
|
11631
12038
|
const filesChanged = [];
|
|
11632
12039
|
for (const filePath of allPaths) {
|
|
11633
|
-
const currentContent = await
|
|
11634
|
-
const targetContent = await
|
|
12040
|
+
const currentContent = await fs33.readFile(path33.join(claudeDir, filePath), "utf-8").catch(() => null);
|
|
12041
|
+
const targetContent = await fs33.readFile(path33.join(harnessPath, filePath), "utf-8").catch(() => null);
|
|
11635
12042
|
if (currentContent !== targetContent) {
|
|
11636
12043
|
filesChanged.push(filePath);
|
|
11637
12044
|
}
|
|
11638
12045
|
}
|
|
11639
|
-
await
|
|
12046
|
+
await fs33.rm(claudeDir, { recursive: true, force: true });
|
|
11640
12047
|
await copyDir(harnessPath, claudeDir);
|
|
11641
|
-
const harnessMcpJson =
|
|
11642
|
-
const projectMcpJson =
|
|
12048
|
+
const harnessMcpJson = path33.join(harnessPath, ".mcp.json");
|
|
12049
|
+
const projectMcpJson = path33.join(projectRoot, ".mcp.json");
|
|
11643
12050
|
try {
|
|
11644
|
-
await
|
|
11645
|
-
const currentMcp = await
|
|
11646
|
-
const targetMcp = await
|
|
12051
|
+
await fs33.access(harnessMcpJson);
|
|
12052
|
+
const currentMcp = await fs33.readFile(projectMcpJson, "utf-8").catch(() => null);
|
|
12053
|
+
const targetMcp = await fs33.readFile(harnessMcpJson, "utf-8").catch(() => null);
|
|
11647
12054
|
if (currentMcp !== targetMcp) {
|
|
11648
12055
|
filesChanged.push(".mcp.json");
|
|
11649
12056
|
}
|
|
11650
|
-
await
|
|
12057
|
+
await fs33.copyFile(harnessMcpJson, projectMcpJson);
|
|
11651
12058
|
} catch {
|
|
11652
12059
|
}
|
|
11653
12060
|
return {
|
|
@@ -11680,7 +12087,7 @@ var DEFAULT_CONFIG = {
|
|
|
11680
12087
|
};
|
|
11681
12088
|
async function loadEvolveConfigFromWorkspace(workspacePath) {
|
|
11682
12089
|
try {
|
|
11683
|
-
const configStr = await
|
|
12090
|
+
const configStr = await fs36.readFile(path37.join(workspacePath, "config.yaml"), "utf-8");
|
|
11684
12091
|
const parsed = yamlParse2(configStr);
|
|
11685
12092
|
return {
|
|
11686
12093
|
model: parsed.model ?? DEFAULT_CONFIG.model,
|
|
@@ -11710,9 +12117,9 @@ evolveCommand.command("init").description("Initialize an evolution workspace wit
|
|
|
11710
12117
|
try {
|
|
11711
12118
|
const projectRoot = process.cwd();
|
|
11712
12119
|
console.log(ui.section("Evolve Init"));
|
|
11713
|
-
const claudeDir =
|
|
12120
|
+
const claudeDir = path37.join(projectRoot, ".claude");
|
|
11714
12121
|
try {
|
|
11715
|
-
await
|
|
12122
|
+
await fs36.access(claudeDir);
|
|
11716
12123
|
} catch {
|
|
11717
12124
|
console.log(ui.error("No .claude/ directory found. Run kairn describe first."));
|
|
11718
12125
|
process.exit(1);
|
|
@@ -11762,7 +12169,7 @@ evolveCommand.command("init").description("Initialize an evolution workspace wit
|
|
|
11762
12169
|
if (config) {
|
|
11763
12170
|
let claudeMd = "";
|
|
11764
12171
|
try {
|
|
11765
|
-
claudeMd = await
|
|
12172
|
+
claudeMd = await fs36.readFile(path37.join(claudeDir, "CLAUDE.md"), "utf-8");
|
|
11766
12173
|
} catch {
|
|
11767
12174
|
}
|
|
11768
12175
|
const profile = await buildProjectProfile(projectRoot);
|
|
@@ -11793,16 +12200,16 @@ evolveCommand.command("init").description("Initialize an evolution workspace wit
|
|
|
11793
12200
|
evolveCommand.command("baseline").description("Snapshot current .claude/ directory as baseline").action(async () => {
|
|
11794
12201
|
try {
|
|
11795
12202
|
const projectRoot = process.cwd();
|
|
11796
|
-
const workspace =
|
|
12203
|
+
const workspace = path37.join(projectRoot, ".kairn-evolve");
|
|
11797
12204
|
console.log(ui.section("Evolve Baseline"));
|
|
11798
12205
|
try {
|
|
11799
|
-
await
|
|
12206
|
+
await fs36.access(workspace);
|
|
11800
12207
|
} catch {
|
|
11801
12208
|
console.log(ui.error("No .kairn-evolve/ directory found. Run kairn evolve init first."));
|
|
11802
12209
|
process.exit(1);
|
|
11803
12210
|
}
|
|
11804
12211
|
await snapshotBaseline(projectRoot, workspace);
|
|
11805
|
-
const baselineDir =
|
|
12212
|
+
const baselineDir = path37.join(workspace, "baseline");
|
|
11806
12213
|
const fileCount = await countFiles(baselineDir);
|
|
11807
12214
|
console.log(ui.success(`Baseline snapshot created (${fileCount} files)`));
|
|
11808
12215
|
} catch (err) {
|
|
@@ -11814,18 +12221,18 @@ evolveCommand.command("baseline").description("Snapshot current .claude/ directo
|
|
|
11814
12221
|
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
12222
|
try {
|
|
11816
12223
|
const projectRoot = process.cwd();
|
|
11817
|
-
const workspace =
|
|
12224
|
+
const workspace = path37.join(projectRoot, ".kairn-evolve");
|
|
11818
12225
|
console.log(ui.section("Evolve Run"));
|
|
11819
12226
|
try {
|
|
11820
|
-
await
|
|
12227
|
+
await fs36.access(workspace);
|
|
11821
12228
|
} catch {
|
|
11822
12229
|
console.log(ui.error("No .kairn-evolve/ directory found. Run kairn evolve init first."));
|
|
11823
12230
|
process.exit(1);
|
|
11824
12231
|
}
|
|
11825
|
-
const tasksPath =
|
|
12232
|
+
const tasksPath = path37.join(workspace, "tasks.yaml");
|
|
11826
12233
|
let tasksContent;
|
|
11827
12234
|
try {
|
|
11828
|
-
tasksContent = await
|
|
12235
|
+
tasksContent = await fs36.readFile(tasksPath, "utf-8");
|
|
11829
12236
|
} catch {
|
|
11830
12237
|
console.log(ui.error("No tasks.yaml found. Run kairn evolve init first."));
|
|
11831
12238
|
process.exit(1);
|
|
@@ -11844,15 +12251,15 @@ evolveCommand.command("run").description("Run tasks against the current harness"
|
|
|
11844
12251
|
console.log(ui.info(`Running ${tasksToRun.length} task(s)...`));
|
|
11845
12252
|
console.log("");
|
|
11846
12253
|
const config = await loadConfig();
|
|
11847
|
-
const harnessPath =
|
|
12254
|
+
const harnessPath = path37.join(projectRoot, ".claude");
|
|
11848
12255
|
const results = [];
|
|
11849
12256
|
for (const task of tasksToRun) {
|
|
11850
|
-
const traceDir =
|
|
12257
|
+
const traceDir = path37.join(workspace, "traces", "0", task.id);
|
|
11851
12258
|
const spinner = ora2(`Running: ${task.id}`).start();
|
|
11852
12259
|
const result = await runTask(task, harnessPath, traceDir, 0);
|
|
11853
12260
|
if (config) {
|
|
11854
|
-
const stdout = await
|
|
11855
|
-
const stderr = await
|
|
12261
|
+
const stdout = await fs36.readFile(path37.join(traceDir, "stdout.log"), "utf-8").catch(() => "");
|
|
12262
|
+
const stderr = await fs36.readFile(path37.join(traceDir, "stderr.log"), "utf-8").catch(() => "");
|
|
11856
12263
|
const score = await scoreTask(task, traceDir, stdout, stderr, config);
|
|
11857
12264
|
result.score = score;
|
|
11858
12265
|
await writeScore(traceDir, score);
|
|
@@ -12019,7 +12426,7 @@ evolveCommand.command("run").description("Run tasks against the current harness"
|
|
|
12019
12426
|
}
|
|
12020
12427
|
}
|
|
12021
12428
|
try {
|
|
12022
|
-
await
|
|
12429
|
+
await fs36.access(path37.join(workspace, "iterations", "0", "harness"));
|
|
12023
12430
|
} catch {
|
|
12024
12431
|
console.log(ui.error("No baseline harness found. Run kairn evolve baseline first."));
|
|
12025
12432
|
process.exit(1);
|
|
@@ -12126,16 +12533,16 @@ evolveCommand.command("run").description("Run tasks against the current harness"
|
|
|
12126
12533
|
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
12534
|
try {
|
|
12128
12535
|
const projectRoot = process.cwd();
|
|
12129
|
-
const workspace =
|
|
12536
|
+
const workspace = path37.join(projectRoot, ".kairn-evolve");
|
|
12130
12537
|
console.log(ui.section("Evolve PBT"));
|
|
12131
12538
|
try {
|
|
12132
|
-
await
|
|
12539
|
+
await fs36.access(workspace);
|
|
12133
12540
|
} catch {
|
|
12134
12541
|
console.log(ui.error("No .kairn-evolve/ directory found. Run kairn evolve init first."));
|
|
12135
12542
|
process.exit(1);
|
|
12136
12543
|
}
|
|
12137
12544
|
try {
|
|
12138
|
-
await
|
|
12545
|
+
await fs36.access(path37.join(workspace, "iterations", "0", "harness"));
|
|
12139
12546
|
} catch {
|
|
12140
12547
|
console.log(ui.error("No baseline harness found. Run kairn evolve baseline first."));
|
|
12141
12548
|
process.exit(1);
|
|
@@ -12155,8 +12562,8 @@ evolveCommand.command("pbt").description("Run Population-Based Training with par
|
|
|
12155
12562
|
if (sampling === "thompson" || sampling === "uniform") {
|
|
12156
12563
|
evolveConfig.samplingStrategy = sampling;
|
|
12157
12564
|
}
|
|
12158
|
-
const tasksPath =
|
|
12159
|
-
const tasksContent = await
|
|
12565
|
+
const tasksPath = path37.join(workspace, "tasks.yaml");
|
|
12566
|
+
const tasksContent = await fs36.readFile(tasksPath, "utf-8");
|
|
12160
12567
|
const parsed = yamlParse2(tasksContent);
|
|
12161
12568
|
if (!parsed?.tasks || parsed.tasks.length === 0) {
|
|
12162
12569
|
console.log(ui.error("No tasks found in tasks.yaml"));
|
|
@@ -12214,10 +12621,10 @@ evolveCommand.command("pbt").description("Run Population-Based Training with par
|
|
|
12214
12621
|
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
12622
|
try {
|
|
12216
12623
|
const projectRoot = process.cwd();
|
|
12217
|
-
const workspace =
|
|
12624
|
+
const workspace = path37.join(projectRoot, ".kairn-evolve");
|
|
12218
12625
|
console.log(ui.section("Evolve Apply"));
|
|
12219
12626
|
try {
|
|
12220
|
-
await
|
|
12627
|
+
await fs36.access(workspace);
|
|
12221
12628
|
} catch {
|
|
12222
12629
|
console.log(ui.error("No .kairn-evolve/ directory found. Run kairn evolve init first."));
|
|
12223
12630
|
process.exit(1);
|
|
@@ -12258,9 +12665,9 @@ evolveCommand.command("apply").description("Apply the best evolved harness to yo
|
|
|
12258
12665
|
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
12666
|
try {
|
|
12260
12667
|
const projectRoot = process.cwd();
|
|
12261
|
-
const workspace =
|
|
12668
|
+
const workspace = path37.join(projectRoot, ".kairn-evolve");
|
|
12262
12669
|
try {
|
|
12263
|
-
await
|
|
12670
|
+
await fs36.access(workspace);
|
|
12264
12671
|
} catch {
|
|
12265
12672
|
console.log(ui.error("No .kairn-evolve/ directory found. Run kairn evolve init first."));
|
|
12266
12673
|
process.exit(1);
|
|
@@ -12281,23 +12688,23 @@ evolveCommand.command("report").description("Generate a summary report of the ev
|
|
|
12281
12688
|
evolveCommand.command("diff <iter1> <iter2>").description("Show harness changes between two iterations").action(async (iter1Str, iter2Str) => {
|
|
12282
12689
|
try {
|
|
12283
12690
|
const projectRoot = process.cwd();
|
|
12284
|
-
const workspace =
|
|
12691
|
+
const workspace = path37.join(projectRoot, ".kairn-evolve");
|
|
12285
12692
|
const iter1 = parseInt(iter1Str, 10);
|
|
12286
12693
|
const iter2 = parseInt(iter2Str, 10);
|
|
12287
12694
|
if (isNaN(iter1) || isNaN(iter2)) {
|
|
12288
12695
|
console.log(ui.error("Both arguments must be integers (iteration numbers)"));
|
|
12289
12696
|
process.exit(1);
|
|
12290
12697
|
}
|
|
12291
|
-
const harness1 =
|
|
12292
|
-
const harness2 =
|
|
12698
|
+
const harness1 = path37.join(workspace, "iterations", iter1.toString(), "harness");
|
|
12699
|
+
const harness2 = path37.join(workspace, "iterations", iter2.toString(), "harness");
|
|
12293
12700
|
try {
|
|
12294
|
-
await
|
|
12701
|
+
await fs36.access(harness1);
|
|
12295
12702
|
} catch {
|
|
12296
12703
|
console.log(ui.error(`Iteration ${iter1} harness not found at ${harness1}`));
|
|
12297
12704
|
process.exit(1);
|
|
12298
12705
|
}
|
|
12299
12706
|
try {
|
|
12300
|
-
await
|
|
12707
|
+
await fs36.access(harness2);
|
|
12301
12708
|
} catch {
|
|
12302
12709
|
console.log(ui.error(`Iteration ${iter2} harness not found at ${harness2}`));
|
|
12303
12710
|
process.exit(1);
|
|
@@ -12404,7 +12811,7 @@ evolveCommand.command("research").description("Run cross-repo research to discov
|
|
|
12404
12811
|
const reportText = formatResearchReport2(report);
|
|
12405
12812
|
console.log("\n" + reportText);
|
|
12406
12813
|
if (options.output) {
|
|
12407
|
-
await
|
|
12814
|
+
await fs36.writeFile(options.output, reportText, "utf-8");
|
|
12408
12815
|
console.log(chalk14.green(`
|
|
12409
12816
|
Report saved to ${options.output}`));
|
|
12410
12817
|
}
|
|
@@ -12421,10 +12828,10 @@ evolveCommand.command("research").description("Run cross-repo research to discov
|
|
|
12421
12828
|
async function countFiles(dir) {
|
|
12422
12829
|
let count = 0;
|
|
12423
12830
|
try {
|
|
12424
|
-
const entries = await
|
|
12831
|
+
const entries = await fs36.readdir(dir, { withFileTypes: true });
|
|
12425
12832
|
for (const entry of entries) {
|
|
12426
12833
|
if (entry.isDirectory()) {
|
|
12427
|
-
count += await countFiles(
|
|
12834
|
+
count += await countFiles(path37.join(dir, entry.name));
|
|
12428
12835
|
} else {
|
|
12429
12836
|
count++;
|
|
12430
12837
|
}
|
|
@@ -12440,12 +12847,42 @@ init_scan();
|
|
|
12440
12847
|
init_analyze();
|
|
12441
12848
|
init_types3();
|
|
12442
12849
|
init_cache();
|
|
12850
|
+
init_proposer();
|
|
12443
12851
|
init_ui();
|
|
12444
12852
|
init_logo();
|
|
12445
12853
|
import { Command as Command12 } from "commander";
|
|
12446
12854
|
import chalk15 from "chalk";
|
|
12855
|
+
import fs37 from "fs/promises";
|
|
12856
|
+
import path38 from "path";
|
|
12447
12857
|
import ora3 from "ora";
|
|
12448
12858
|
async function analyzeAction(options) {
|
|
12859
|
+
if (options.ir) {
|
|
12860
|
+
const irPath = path38.join(process.cwd(), ".kairn", "harness-ir.json");
|
|
12861
|
+
try {
|
|
12862
|
+
const raw = await fs37.readFile(irPath, "utf-8");
|
|
12863
|
+
const ir = JSON.parse(raw);
|
|
12864
|
+
if (!options.json) {
|
|
12865
|
+
printCompactBanner();
|
|
12866
|
+
console.log(ui.section("Harness IR"));
|
|
12867
|
+
console.log(buildIRSummary(ir));
|
|
12868
|
+
console.log("");
|
|
12869
|
+
console.log(chalk15.dim(` Source: ${irPath}`));
|
|
12870
|
+
console.log("");
|
|
12871
|
+
} else {
|
|
12872
|
+
console.log(JSON.stringify(ir, null, 2));
|
|
12873
|
+
}
|
|
12874
|
+
} catch {
|
|
12875
|
+
if (options.json) {
|
|
12876
|
+
console.log(JSON.stringify({ error: "No harness IR found. Run `kairn optimize` first." }));
|
|
12877
|
+
} else {
|
|
12878
|
+
printCompactBanner();
|
|
12879
|
+
console.log(ui.warn("No harness IR found. Run `kairn optimize` first."));
|
|
12880
|
+
console.log("");
|
|
12881
|
+
}
|
|
12882
|
+
process.exit(1);
|
|
12883
|
+
}
|
|
12884
|
+
return;
|
|
12885
|
+
}
|
|
12449
12886
|
if (!options.json) {
|
|
12450
12887
|
printCompactBanner();
|
|
12451
12888
|
}
|
|
@@ -12498,11 +12935,14 @@ async function analyzeAction(options) {
|
|
|
12498
12935
|
indent: 2
|
|
12499
12936
|
}).start();
|
|
12500
12937
|
let analysis;
|
|
12938
|
+
let packedSource = "";
|
|
12501
12939
|
try {
|
|
12502
|
-
|
|
12940
|
+
const result = await analyzeProject(targetDir, profile, config, {
|
|
12503
12941
|
refresh: options.refresh,
|
|
12504
12942
|
tokenBudget: options.tokenBudget
|
|
12505
12943
|
});
|
|
12944
|
+
analysis = result.analysis;
|
|
12945
|
+
packedSource = result.packedSource;
|
|
12506
12946
|
analysisSpinner?.succeed(
|
|
12507
12947
|
options.refresh ? "Re-analyzed from scratch" : "Codebase analyzed"
|
|
12508
12948
|
);
|
|
@@ -12580,9 +13020,10 @@ async function analyzeAction(options) {
|
|
|
12580
13020
|
}
|
|
12581
13021
|
}
|
|
12582
13022
|
console.log("");
|
|
13023
|
+
const packedStats = packedSource ? ` \xB7 ${packedSource.length.toLocaleString()} chars packed` : "";
|
|
12583
13024
|
console.log(
|
|
12584
13025
|
chalk15.dim(
|
|
12585
|
-
` Sampled ${analysis.sampled_files.length} files \xB7 analyzed ${analysis.analyzed_at}`
|
|
13026
|
+
` Sampled ${analysis.sampled_files.length} files${packedStats} \xB7 analyzed ${analysis.analyzed_at}`
|
|
12586
13027
|
)
|
|
12587
13028
|
);
|
|
12588
13029
|
console.log(ui.divider());
|
|
@@ -12590,7 +13031,7 @@ async function analyzeAction(options) {
|
|
|
12590
13031
|
}
|
|
12591
13032
|
var analyzeCommand = new Command12("analyze").description(
|
|
12592
13033
|
"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);
|
|
13034
|
+
).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
13035
|
|
|
12595
13036
|
// src/cli.ts
|
|
12596
13037
|
var require2 = createRequire(import.meta.url);
|