@xn-intenton-z2a/agentic-lib 7.4.5 → 7.4.7
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/.github/workflows/agentic-lib-bot.yml +5 -1
- package/.github/workflows/agentic-lib-workflow.yml +30 -6
- package/README.md +2 -2
- package/bin/agentic-lib.js +90 -50
- package/package.json +1 -1
- package/src/actions/agentic-step/config-loader.js +4 -304
- package/src/actions/agentic-step/copilot.js +45 -527
- package/src/actions/agentic-step/index.js +60 -328
- package/src/actions/agentic-step/metrics.js +115 -0
- package/src/actions/agentic-step/tools.js +19 -134
- package/src/copilot/context.js +158 -19
- package/src/copilot/guards.js +87 -0
- package/src/copilot/hybrid-session.js +16 -9
- package/src/copilot/sdk.js +5 -3
- package/src/copilot/telemetry.js +172 -0
- package/src/seeds/zero-package.json +1 -1
- package/src/copilot/tasks/fix-code.js +0 -73
- package/src/copilot/tasks/maintain-features.js +0 -61
- package/src/copilot/tasks/maintain-library.js +0 -66
- package/src/copilot/tasks/transform.js +0 -120
|
@@ -128,7 +128,11 @@ jobs:
|
|
|
128
128
|
|
|
129
129
|
- name: Install agentic-step dependencies
|
|
130
130
|
working-directory: .github/agentic-lib/actions/agentic-step
|
|
131
|
-
run:
|
|
131
|
+
run: |
|
|
132
|
+
npm ci
|
|
133
|
+
if [ -d "../../copilot" ]; then
|
|
134
|
+
ln -sf "$(pwd)/node_modules" ../../copilot/node_modules
|
|
135
|
+
fi
|
|
132
136
|
|
|
133
137
|
- name: Read test command from config
|
|
134
138
|
id: config
|
|
@@ -601,7 +601,11 @@ jobs:
|
|
|
601
601
|
|
|
602
602
|
- name: Install agentic-step dependencies
|
|
603
603
|
working-directory: .github/agentic-lib/actions/agentic-step
|
|
604
|
-
run:
|
|
604
|
+
run: |
|
|
605
|
+
npm ci
|
|
606
|
+
if [ -d "../../copilot" ]; then
|
|
607
|
+
ln -sf "$(pwd)/node_modules" ../../copilot/node_modules
|
|
608
|
+
fi
|
|
605
609
|
|
|
606
610
|
- name: Apply profile and model to config
|
|
607
611
|
if: inputs.profile != '' || inputs.model != ''
|
|
@@ -713,7 +717,11 @@ jobs:
|
|
|
713
717
|
|
|
714
718
|
- name: Install agentic-step dependencies
|
|
715
719
|
working-directory: .github/agentic-lib/actions/agentic-step
|
|
716
|
-
run:
|
|
720
|
+
run: |
|
|
721
|
+
npm ci
|
|
722
|
+
if [ -d "../../copilot" ]; then
|
|
723
|
+
ln -sf "$(pwd)/node_modules" ../../copilot/node_modules
|
|
724
|
+
fi
|
|
717
725
|
|
|
718
726
|
- name: Run director
|
|
719
727
|
id: director
|
|
@@ -764,7 +772,11 @@ jobs:
|
|
|
764
772
|
|
|
765
773
|
- name: Install agentic-step dependencies
|
|
766
774
|
working-directory: .github/agentic-lib/actions/agentic-step
|
|
767
|
-
run:
|
|
775
|
+
run: |
|
|
776
|
+
npm ci
|
|
777
|
+
if [ -d "../../copilot" ]; then
|
|
778
|
+
ln -sf "$(pwd)/node_modules" ../../copilot/node_modules
|
|
779
|
+
fi
|
|
768
780
|
|
|
769
781
|
- name: Run supervisor
|
|
770
782
|
if: github.repository != 'xn-intenton-z2a/agentic-lib'
|
|
@@ -814,7 +826,11 @@ jobs:
|
|
|
814
826
|
|
|
815
827
|
- name: Install agentic-step dependencies
|
|
816
828
|
working-directory: .github/agentic-lib/actions/agentic-step
|
|
817
|
-
run:
|
|
829
|
+
run: |
|
|
830
|
+
npm ci
|
|
831
|
+
if [ -d "../../copilot" ]; then
|
|
832
|
+
ln -sf "$(pwd)/node_modules" ../../copilot/node_modules
|
|
833
|
+
fi
|
|
818
834
|
|
|
819
835
|
- name: Check mission-complete signal
|
|
820
836
|
id: fix-mission-check
|
|
@@ -1128,7 +1144,11 @@ jobs:
|
|
|
1128
1144
|
|
|
1129
1145
|
- name: Install agentic-step dependencies
|
|
1130
1146
|
working-directory: .github/agentic-lib/actions/agentic-step
|
|
1131
|
-
run:
|
|
1147
|
+
run: |
|
|
1148
|
+
npm ci
|
|
1149
|
+
if [ -d "../../copilot" ]; then
|
|
1150
|
+
ln -sf "$(pwd)/node_modules" ../../copilot/node_modules
|
|
1151
|
+
fi
|
|
1132
1152
|
|
|
1133
1153
|
- name: Review issues
|
|
1134
1154
|
uses: ./.github/agentic-lib/actions/agentic-step
|
|
@@ -1186,7 +1206,11 @@ jobs:
|
|
|
1186
1206
|
|
|
1187
1207
|
- name: Install agentic-step dependencies
|
|
1188
1208
|
working-directory: .github/agentic-lib/actions/agentic-step
|
|
1189
|
-
run:
|
|
1209
|
+
run: |
|
|
1210
|
+
npm ci
|
|
1211
|
+
if [ -d "../../copilot" ]; then
|
|
1212
|
+
ln -sf "$(pwd)/node_modules" ../../copilot/node_modules
|
|
1213
|
+
fi
|
|
1190
1214
|
|
|
1191
1215
|
- name: Load config
|
|
1192
1216
|
id: config
|
package/README.md
CHANGED
|
@@ -390,8 +390,8 @@ agentic-lib iterate --mission 6-kyu-understand-hamming-distance --model gpt-5-mi
|
|
|
390
390
|
Target: /tmp/bench
|
|
391
391
|
Model: gpt-5-mini
|
|
392
392
|
|
|
393
|
-
[
|
|
394
|
-
[
|
|
393
|
+
[agentic-lib] Creating session (model=gpt-5-mini, workspace=/tmp/bench)
|
|
394
|
+
[agentic-lib] Session: sess_abc123
|
|
395
395
|
[tool] read_file
|
|
396
396
|
[tool] read_file
|
|
397
397
|
[tool] write_file
|
package/bin/agentic-lib.js
CHANGED
|
@@ -30,6 +30,12 @@ const flags = args.slice(1);
|
|
|
30
30
|
|
|
31
31
|
let initChanges = 0;
|
|
32
32
|
const TASK_COMMANDS = ["transform", "maintain-features", "maintain-library", "fix-code"];
|
|
33
|
+
const TASK_AGENT_MAP = {
|
|
34
|
+
"transform": "agent-issue-resolution",
|
|
35
|
+
"fix-code": "agent-apply-fix",
|
|
36
|
+
"maintain-features": "agent-maintain-features",
|
|
37
|
+
"maintain-library": "agent-maintain-library",
|
|
38
|
+
};
|
|
33
39
|
const INIT_COMMANDS = ["init", "update", "reset"];
|
|
34
40
|
const ALL_COMMANDS = [...INIT_COMMANDS, ...TASK_COMMANDS, "version", "mcp", "iterate"];
|
|
35
41
|
|
|
@@ -332,7 +338,14 @@ async function runIterate() {
|
|
|
332
338
|
}
|
|
333
339
|
|
|
334
340
|
// Build context-aware user prompt
|
|
335
|
-
const userPrompt = buildUserPrompt(agentName, localContext, githubContext, {
|
|
341
|
+
const { prompt: userPrompt } = buildUserPrompt(agentName, localContext, githubContext, {
|
|
342
|
+
tuning: config.tuning,
|
|
343
|
+
config,
|
|
344
|
+
});
|
|
345
|
+
|
|
346
|
+
// Derive maxToolCalls from transformation budget
|
|
347
|
+
const budget = config.transformationBudget || 0;
|
|
348
|
+
const effectiveMaxToolCalls = budget > 0 ? budget * 20 : undefined;
|
|
336
349
|
|
|
337
350
|
const result = await runHybridSession({
|
|
338
351
|
workspacePath: target,
|
|
@@ -342,6 +355,7 @@ async function runIterate() {
|
|
|
342
355
|
agentPrompt,
|
|
343
356
|
userPrompt,
|
|
344
357
|
writablePaths: config.writablePaths?.length > 0 ? config.writablePaths : undefined,
|
|
358
|
+
maxToolCalls: effectiveMaxToolCalls,
|
|
345
359
|
});
|
|
346
360
|
|
|
347
361
|
console.log("");
|
|
@@ -366,84 +380,109 @@ async function runIterate() {
|
|
|
366
380
|
// ─── Task Runner ─────────────────────────────────────────────────────
|
|
367
381
|
|
|
368
382
|
async function runTask(taskName) {
|
|
383
|
+
// Task commands are now aliases for iterate --agent <agent-name>
|
|
384
|
+
const agentName = TASK_AGENT_MAP[taskName];
|
|
385
|
+
if (!agentName) {
|
|
386
|
+
console.error(`Unknown task: ${taskName}`);
|
|
387
|
+
return 1;
|
|
388
|
+
}
|
|
389
|
+
|
|
369
390
|
console.log("");
|
|
370
|
-
console.log(`=== agentic-lib ${taskName} ===`);
|
|
391
|
+
console.log(`=== agentic-lib ${taskName} (→ iterate --agent ${agentName}) ===`);
|
|
371
392
|
console.log(`Target: ${target}`);
|
|
372
393
|
console.log(`Model: ${model}`);
|
|
373
394
|
console.log(`Dry-run: ${dryRun}`);
|
|
374
395
|
console.log("");
|
|
375
396
|
|
|
376
|
-
// Load config from shared module
|
|
377
397
|
const { loadConfig } = await import("../src/copilot/config.js");
|
|
378
|
-
|
|
379
|
-
|
|
398
|
+
let config;
|
|
399
|
+
try {
|
|
400
|
+
config = loadConfig(resolve(target, "agentic-lib.toml"));
|
|
401
|
+
} catch {
|
|
402
|
+
config = { tuning: {}, model: "gpt-5-mini", paths: {}, writablePaths: [], readOnlyPaths: [] };
|
|
403
|
+
}
|
|
404
|
+
const effectiveModel = model || config.model || "gpt-5-mini";
|
|
380
405
|
|
|
381
|
-
console.log(`[config]
|
|
382
|
-
console.log(`[config] writable=${config.writablePaths.join(", ")}`);
|
|
383
|
-
console.log(`[config] test=${config.testScript}`);
|
|
406
|
+
console.log(`[config] writable=${(config.writablePaths || []).join(", ")}`);
|
|
384
407
|
console.log("");
|
|
385
408
|
|
|
409
|
+
// Short-circuit guards — skip LLM invocation when unnecessary
|
|
410
|
+
const { checkGuards } = await import("../src/copilot/guards.js");
|
|
411
|
+
const guardResult = checkGuards(taskName, config, target);
|
|
412
|
+
if (guardResult.skip) {
|
|
413
|
+
console.log(`=== ${taskName} skipped (nop) ===`);
|
|
414
|
+
console.log(`Reason: ${guardResult.reason}`);
|
|
415
|
+
console.log("");
|
|
416
|
+
return 0;
|
|
417
|
+
}
|
|
418
|
+
|
|
386
419
|
if (dryRun) {
|
|
387
420
|
console.log("=== DRY RUN — task would run but not sending to Copilot ===");
|
|
388
421
|
return 0;
|
|
389
422
|
}
|
|
390
423
|
|
|
391
|
-
// Change to target directory so relative paths in config work
|
|
392
|
-
const originalCwd = process.cwd();
|
|
393
|
-
process.chdir(target);
|
|
394
|
-
|
|
395
424
|
try {
|
|
396
|
-
const
|
|
425
|
+
const { loadAgentPrompt } = await import("../src/copilot/agents.js");
|
|
426
|
+
const { runHybridSession } = await import("../src/copilot/hybrid-session.js");
|
|
427
|
+
const { gatherLocalContext, gatherGitHubContext, buildUserPrompt } = await import("../src/copilot/context.js");
|
|
428
|
+
|
|
429
|
+
const agentPrompt = loadAgentPrompt(agentName);
|
|
430
|
+
const localContext = gatherLocalContext(target, config);
|
|
431
|
+
|
|
432
|
+
let githubContext;
|
|
433
|
+
if (issueNumber || prNumber) {
|
|
434
|
+
githubContext = gatherGitHubContext({
|
|
435
|
+
issueNumber: issueNumber || undefined,
|
|
436
|
+
prNumber: prNumber || undefined,
|
|
437
|
+
workspacePath: target,
|
|
438
|
+
});
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
const { prompt: userPrompt, promptBudget } = buildUserPrompt(agentName, localContext, githubContext, {
|
|
442
|
+
tuning: config.tuning,
|
|
397
443
|
config,
|
|
398
|
-
|
|
444
|
+
});
|
|
445
|
+
|
|
446
|
+
// Derive maxToolCalls from transformation budget (budget × 20, or unlimited)
|
|
447
|
+
const budget = config.transformationBudget || 0;
|
|
448
|
+
const effectiveMaxToolCalls = budget > 0 ? budget * 20 : undefined;
|
|
449
|
+
|
|
450
|
+
const result = await runHybridSession({
|
|
451
|
+
workspacePath: target,
|
|
399
452
|
model: effectiveModel,
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
case "maintain-features": {
|
|
412
|
-
const { maintainFeatures } = await import("../src/copilot/tasks/maintain-features.js");
|
|
413
|
-
result = await maintainFeatures(context);
|
|
414
|
-
break;
|
|
415
|
-
}
|
|
416
|
-
case "maintain-library": {
|
|
417
|
-
const { maintainLibrary } = await import("../src/copilot/tasks/maintain-library.js");
|
|
418
|
-
result = await maintainLibrary(context);
|
|
419
|
-
break;
|
|
420
|
-
}
|
|
421
|
-
case "fix-code": {
|
|
422
|
-
const { fixCode } = await import("../src/copilot/tasks/fix-code.js");
|
|
423
|
-
result = await fixCode(context);
|
|
424
|
-
break;
|
|
425
|
-
}
|
|
426
|
-
default:
|
|
427
|
-
console.error(`Unknown task: ${taskName}`);
|
|
428
|
-
return 1;
|
|
429
|
-
}
|
|
453
|
+
tuning: config.tuning || {},
|
|
454
|
+
timeoutMs,
|
|
455
|
+
agentPrompt,
|
|
456
|
+
userPrompt,
|
|
457
|
+
writablePaths: config.writablePaths?.length > 0 ? config.writablePaths : undefined,
|
|
458
|
+
maxToolCalls: effectiveMaxToolCalls,
|
|
459
|
+
});
|
|
460
|
+
|
|
461
|
+
// Build enriched result (10e)
|
|
462
|
+
const outcome = result.success ? "transformed" : "error";
|
|
463
|
+
const tokensUsed = result.tokensIn + result.tokensOut;
|
|
430
464
|
|
|
431
465
|
console.log("");
|
|
432
466
|
console.log(`=== ${taskName} completed ===`);
|
|
433
|
-
console.log(`Outcome:
|
|
434
|
-
|
|
435
|
-
|
|
467
|
+
console.log(`Outcome: ${outcome}`);
|
|
468
|
+
console.log(`Session time: ${result.sessionTime}s`);
|
|
469
|
+
console.log(`Tool calls: ${result.toolCalls}`);
|
|
470
|
+
console.log(`Tokens: ${tokensUsed} (in=${result.tokensIn} out=${result.tokensOut})`);
|
|
436
471
|
if (result.narrative) console.log(`Narrative: ${result.narrative}`);
|
|
472
|
+
if (promptBudget) {
|
|
473
|
+
console.log("Prompt budget:");
|
|
474
|
+
for (const entry of promptBudget) {
|
|
475
|
+
console.log(` ${entry.section}: ${entry.size} chars, ${entry.files} files ${entry.notes}`);
|
|
476
|
+
}
|
|
477
|
+
}
|
|
437
478
|
console.log("");
|
|
438
|
-
return 0;
|
|
479
|
+
return result.success ? 0 : 1;
|
|
439
480
|
} catch (err) {
|
|
440
481
|
console.error("");
|
|
441
482
|
console.error(`=== ${taskName} FAILED ===`);
|
|
442
483
|
console.error(err.message);
|
|
443
484
|
if (err.stack) console.error(err.stack);
|
|
444
485
|
return 1;
|
|
445
|
-
} finally {
|
|
446
|
-
process.chdir(originalCwd);
|
|
447
486
|
}
|
|
448
487
|
}
|
|
449
488
|
|
|
@@ -1229,6 +1268,7 @@ function runInit() {
|
|
|
1229
1268
|
|
|
1230
1269
|
initWorkflows();
|
|
1231
1270
|
initActions(agenticDir);
|
|
1271
|
+
initDirContents("copilot", resolve(agenticDir, "copilot"), "Copilot (shared modules)");
|
|
1232
1272
|
initDirContents("agents", resolve(agenticDir, "agents"), "Agents");
|
|
1233
1273
|
initDirContents("seeds", resolve(agenticDir, "seeds"), "Seeds");
|
|
1234
1274
|
initScripts(agenticDir);
|
package/package.json
CHANGED
|
@@ -1,308 +1,8 @@
|
|
|
1
1
|
// SPDX-License-Identifier: GPL-3.0-only
|
|
2
2
|
// Copyright (C) 2025-2026 Polycode Limited
|
|
3
|
-
// config-loader.js —
|
|
3
|
+
// config-loader.js — Thin re-export from shared src/copilot/config.js
|
|
4
4
|
//
|
|
5
|
-
//
|
|
6
|
-
//
|
|
5
|
+
// Phase 4: Configuration logic now lives in src/copilot/config.js.
|
|
6
|
+
// This file re-exports for backwards compatibility with existing imports.
|
|
7
7
|
|
|
8
|
-
|
|
9
|
-
import { dirname, join } from "path";
|
|
10
|
-
import { parse as parseToml } from "smol-toml";
|
|
11
|
-
|
|
12
|
-
/**
|
|
13
|
-
* @typedef {Object} PathConfig
|
|
14
|
-
* @property {string} path - The filesystem path
|
|
15
|
-
* @property {string[]} permissions - Access permissions (e.g. ['write'])
|
|
16
|
-
* @property {number} [limit] - Maximum number of files allowed
|
|
17
|
-
*/
|
|
18
|
-
|
|
19
|
-
/**
|
|
20
|
-
* @typedef {Object} AgenticConfig
|
|
21
|
-
* @property {string} schedule - Schedule identifier
|
|
22
|
-
* @property {string} supervisor - Supervisor frequency (off | weekly | daily | hourly | continuous)
|
|
23
|
-
* @property {string} model - Copilot SDK model for LLM requests
|
|
24
|
-
* @property {Object<string, PathConfig>} paths - Mapped paths with permissions
|
|
25
|
-
* @property {string} testScript - Self-contained test command (e.g. "npm ci && npm test")
|
|
26
|
-
* @property {number} featureDevelopmentIssuesWipLimit - Max concurrent feature issues
|
|
27
|
-
* @property {number} maintenanceIssuesWipLimit - Max concurrent maintenance issues
|
|
28
|
-
* @property {number} attemptsPerBranch - Max attempts per branch
|
|
29
|
-
* @property {number} attemptsPerIssue - Max attempts per issue
|
|
30
|
-
* @property {Object} seeding - Seed file configuration
|
|
31
|
-
* @property {Object} intentionBot - Bot configuration
|
|
32
|
-
* @property {boolean} tdd - Whether TDD mode is enabled
|
|
33
|
-
* @property {string[]} writablePaths - All paths with write permission
|
|
34
|
-
* @property {string[]} readOnlyPaths - All paths without write permission
|
|
35
|
-
*/
|
|
36
|
-
|
|
37
|
-
// Keys whose paths are writable by agents
|
|
38
|
-
const WRITABLE_KEYS = ["source", "tests", "behaviour", "features", "dependencies", "docs", "readme", "examples", "web"];
|
|
39
|
-
|
|
40
|
-
// Default paths — every key that task handlers might access
|
|
41
|
-
const PATH_DEFAULTS = {
|
|
42
|
-
mission: "MISSION.md",
|
|
43
|
-
source: "src/lib/",
|
|
44
|
-
tests: "tests/unit/",
|
|
45
|
-
behaviour: "tests/behaviour/",
|
|
46
|
-
features: "features/",
|
|
47
|
-
docs: "docs/",
|
|
48
|
-
examples: "examples/",
|
|
49
|
-
readme: "README.md",
|
|
50
|
-
dependencies: "package.json",
|
|
51
|
-
library: "library/",
|
|
52
|
-
librarySources: "SOURCES.md",
|
|
53
|
-
contributing: "CONTRIBUTING.md",
|
|
54
|
-
web: "src/web/",
|
|
55
|
-
};
|
|
56
|
-
|
|
57
|
-
// Default limits for path-specific constraints
|
|
58
|
-
const LIMIT_DEFAULTS = {
|
|
59
|
-
features: 4,
|
|
60
|
-
library: 32,
|
|
61
|
-
};
|
|
62
|
-
|
|
63
|
-
// Fallback profile defaults — used only when [profiles.*] is missing from TOML.
|
|
64
|
-
// The canonical source of truth is the [profiles.*] sections in agentic-lib.toml.
|
|
65
|
-
const FALLBACK_TUNING = {
|
|
66
|
-
reasoningEffort: "medium",
|
|
67
|
-
infiniteSessions: true,
|
|
68
|
-
transformationBudget: 32,
|
|
69
|
-
featuresScan: 10,
|
|
70
|
-
sourceScan: 10,
|
|
71
|
-
sourceContent: 5000,
|
|
72
|
-
testContent: 3000,
|
|
73
|
-
issuesScan: 20,
|
|
74
|
-
issueBodyLimit: 500,
|
|
75
|
-
staleDays: 30,
|
|
76
|
-
documentSummary: 2000,
|
|
77
|
-
discussionComments: 10,
|
|
78
|
-
};
|
|
79
|
-
|
|
80
|
-
const FALLBACK_LIMITS = {
|
|
81
|
-
featureIssues: 2,
|
|
82
|
-
maintenanceIssues: 1,
|
|
83
|
-
attemptsPerBranch: 3,
|
|
84
|
-
attemptsPerIssue: 2,
|
|
85
|
-
featuresLimit: 4,
|
|
86
|
-
libraryLimit: 32,
|
|
87
|
-
};
|
|
88
|
-
|
|
89
|
-
/**
|
|
90
|
-
* Parse a TOML profile section into tuning defaults (camelCase keys).
|
|
91
|
-
*/
|
|
92
|
-
function parseTuningProfile(profileSection) {
|
|
93
|
-
if (!profileSection) return null;
|
|
94
|
-
return {
|
|
95
|
-
reasoningEffort: profileSection["reasoning-effort"] || "medium",
|
|
96
|
-
infiniteSessions: profileSection["infinite-sessions"] ?? true,
|
|
97
|
-
transformationBudget: profileSection["transformation-budget"] || 32,
|
|
98
|
-
featuresScan: profileSection["max-feature-files"] || 10,
|
|
99
|
-
sourceScan: profileSection["max-source-files"] || 10,
|
|
100
|
-
sourceContent: profileSection["max-source-chars"] || 5000,
|
|
101
|
-
testContent: profileSection["max-test-chars"] || 3000,
|
|
102
|
-
issuesScan: profileSection["max-issues"] || 20,
|
|
103
|
-
issueBodyLimit: profileSection["issue-body-limit"] || 500,
|
|
104
|
-
staleDays: profileSection["stale-days"] || 30,
|
|
105
|
-
documentSummary: profileSection["max-summary-chars"] || 2000,
|
|
106
|
-
discussionComments: profileSection["max-discussion-comments"] || 10,
|
|
107
|
-
};
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
/**
|
|
111
|
-
* Parse a TOML profile section into limits defaults (camelCase keys).
|
|
112
|
-
*/
|
|
113
|
-
function parseLimitsProfile(profileSection) {
|
|
114
|
-
if (!profileSection) return null;
|
|
115
|
-
return {
|
|
116
|
-
featureIssues: profileSection["max-feature-issues"] || 2,
|
|
117
|
-
maintenanceIssues: profileSection["max-maintenance-issues"] || 1,
|
|
118
|
-
attemptsPerBranch: profileSection["max-attempts-per-branch"] || 3,
|
|
119
|
-
attemptsPerIssue: profileSection["max-attempts-per-issue"] || 2,
|
|
120
|
-
featuresLimit: profileSection["features-limit"] || 4,
|
|
121
|
-
libraryLimit: profileSection["library-limit"] || 32,
|
|
122
|
-
};
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
/**
|
|
126
|
-
* Read package.json from the project root, returning empty string if not found.
|
|
127
|
-
* @param {string} tomlPath - Path to the TOML config (used to derive project root)
|
|
128
|
-
* @param {string} depsRelPath - Relative path to package.json (from config)
|
|
129
|
-
* @returns {string} Raw package.json content or empty string
|
|
130
|
-
*/
|
|
131
|
-
function readPackageJson(tomlPath, depsRelPath) {
|
|
132
|
-
try {
|
|
133
|
-
const projectRoot = dirname(tomlPath);
|
|
134
|
-
const pkgPath = join(projectRoot, depsRelPath);
|
|
135
|
-
return existsSync(pkgPath) ? readFileSync(pkgPath, "utf8") : "";
|
|
136
|
-
} catch {
|
|
137
|
-
return "";
|
|
138
|
-
}
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
/**
|
|
142
|
-
* Resolve tuning configuration: start from profile defaults, apply explicit overrides.
|
|
143
|
-
* @param {Object} tuningSection - The [tuning] section from TOML
|
|
144
|
-
* @param {Object} [profilesSection] - The [profiles] section from TOML (source of truth)
|
|
145
|
-
*/
|
|
146
|
-
function resolveTuning(tuningSection, profilesSection) {
|
|
147
|
-
const profileName = tuningSection.profile || "recommended";
|
|
148
|
-
const tomlProfile = profilesSection?.[profileName];
|
|
149
|
-
const profile = parseTuningProfile(tomlProfile) || FALLBACK_TUNING;
|
|
150
|
-
const tuning = { ...profile, profileName };
|
|
151
|
-
|
|
152
|
-
// "none" explicitly disables reasoning-effort regardless of profile
|
|
153
|
-
if (tuningSection["reasoning-effort"]) {
|
|
154
|
-
tuning.reasoningEffort = tuningSection["reasoning-effort"] === "none" ? "" : tuningSection["reasoning-effort"];
|
|
155
|
-
}
|
|
156
|
-
if (tuningSection["infinite-sessions"] === true || tuningSection["infinite-sessions"] === false) {
|
|
157
|
-
tuning.infiniteSessions = tuningSection["infinite-sessions"];
|
|
158
|
-
}
|
|
159
|
-
const numericOverrides = {
|
|
160
|
-
"transformation-budget": "transformationBudget",
|
|
161
|
-
"max-feature-files": "featuresScan",
|
|
162
|
-
"max-source-files": "sourceScan",
|
|
163
|
-
"max-source-chars": "sourceContent",
|
|
164
|
-
"max-test-chars": "testContent",
|
|
165
|
-
"max-issues": "issuesScan",
|
|
166
|
-
"issue-body-limit": "issueBodyLimit",
|
|
167
|
-
"stale-days": "staleDays",
|
|
168
|
-
"max-summary-chars": "documentSummary",
|
|
169
|
-
"max-discussion-comments": "discussionComments",
|
|
170
|
-
};
|
|
171
|
-
for (const [tomlKey, jsKey] of Object.entries(numericOverrides)) {
|
|
172
|
-
if (tuningSection[tomlKey] > 0) tuning[jsKey] = tuningSection[tomlKey];
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
return tuning;
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
/**
|
|
179
|
-
* Resolve limits configuration: start from profile defaults, apply explicit overrides.
|
|
180
|
-
* @param {Object} limitsSection - The [limits] section from TOML
|
|
181
|
-
* @param {string} profileName - Active profile name
|
|
182
|
-
* @param {Object} [profilesSection] - The [profiles] section from TOML (source of truth)
|
|
183
|
-
*/
|
|
184
|
-
function resolveLimits(limitsSection, profileName, profilesSection) {
|
|
185
|
-
const tomlProfile = profilesSection?.[profileName];
|
|
186
|
-
const profile = parseLimitsProfile(tomlProfile) || FALLBACK_LIMITS;
|
|
187
|
-
return {
|
|
188
|
-
featureIssues: limitsSection["max-feature-issues"] || profile.featureIssues,
|
|
189
|
-
maintenanceIssues: limitsSection["max-maintenance-issues"] || profile.maintenanceIssues,
|
|
190
|
-
attemptsPerBranch: limitsSection["max-attempts-per-branch"] || profile.attemptsPerBranch,
|
|
191
|
-
attemptsPerIssue: limitsSection["max-attempts-per-issue"] || profile.attemptsPerIssue,
|
|
192
|
-
featuresLimit: limitsSection["features-limit"] || profile.featuresLimit,
|
|
193
|
-
libraryLimit: limitsSection["library-limit"] || profile.libraryLimit,
|
|
194
|
-
};
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
/**
|
|
198
|
-
* Load configuration from agentic-lib.toml.
|
|
199
|
-
*
|
|
200
|
-
* If configPath ends in .toml, it is used directly.
|
|
201
|
-
* Otherwise, the project root is derived (3 levels up from configPath)
|
|
202
|
-
* and agentic-lib.toml is loaded from there.
|
|
203
|
-
*
|
|
204
|
-
* @param {string} configPath - Path to config file or YAML path (for project root derivation)
|
|
205
|
-
* @returns {AgenticConfig} Parsed configuration object
|
|
206
|
-
* @throws {Error} If no TOML config file is found
|
|
207
|
-
*/
|
|
208
|
-
export function loadConfig(configPath) {
|
|
209
|
-
let tomlPath;
|
|
210
|
-
if (configPath.endsWith(".toml")) {
|
|
211
|
-
tomlPath = configPath;
|
|
212
|
-
} else {
|
|
213
|
-
const configDir = dirname(configPath);
|
|
214
|
-
const projectRoot = join(configDir, "..", "..", "..");
|
|
215
|
-
tomlPath = join(projectRoot, "agentic-lib.toml");
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
if (!existsSync(tomlPath)) {
|
|
219
|
-
throw new Error(`Config file not found: ${tomlPath}. Create agentic-lib.toml in the project root.`);
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
const rawToml = readFileSync(tomlPath, "utf8");
|
|
223
|
-
const toml = parseToml(rawToml);
|
|
224
|
-
|
|
225
|
-
// Merge TOML paths with defaults, normalising library-sources → librarySources
|
|
226
|
-
const rawPaths = { ...toml.paths };
|
|
227
|
-
if (rawPaths["library-sources"]) {
|
|
228
|
-
rawPaths.librarySources = rawPaths["library-sources"];
|
|
229
|
-
delete rawPaths["library-sources"];
|
|
230
|
-
}
|
|
231
|
-
const mergedPaths = { ...PATH_DEFAULTS, ...rawPaths };
|
|
232
|
-
|
|
233
|
-
// Build path objects with permissions
|
|
234
|
-
const paths = {};
|
|
235
|
-
const writablePaths = [];
|
|
236
|
-
const readOnlyPaths = [];
|
|
237
|
-
|
|
238
|
-
for (const [key, value] of Object.entries(mergedPaths)) {
|
|
239
|
-
const isWritable = WRITABLE_KEYS.includes(key);
|
|
240
|
-
paths[key] = { path: value, permissions: isWritable ? ["write"] : [] };
|
|
241
|
-
if (isWritable) {
|
|
242
|
-
writablePaths.push(value);
|
|
243
|
-
} else {
|
|
244
|
-
readOnlyPaths.push(value);
|
|
245
|
-
}
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
const profilesSection = toml.profiles || {};
|
|
249
|
-
const tuning = resolveTuning(toml.tuning || {}, profilesSection);
|
|
250
|
-
const limitsSection = toml.limits || {};
|
|
251
|
-
const resolvedLimits = resolveLimits(limitsSection, tuning.profileName, profilesSection);
|
|
252
|
-
|
|
253
|
-
// Apply resolved limits to path objects
|
|
254
|
-
paths.features.limit = resolvedLimits.featuresLimit;
|
|
255
|
-
paths.library.limit = resolvedLimits.libraryLimit;
|
|
256
|
-
|
|
257
|
-
const execution = toml.execution || {};
|
|
258
|
-
const bot = toml.bot || {};
|
|
259
|
-
|
|
260
|
-
// Mission-complete thresholds (with safe defaults)
|
|
261
|
-
const mc = toml["mission-complete"] || {};
|
|
262
|
-
const missionCompleteThresholds = {
|
|
263
|
-
minResolvedIssues: mc["min-resolved-issues"] ?? 3,
|
|
264
|
-
minDedicatedTests: mc["min-dedicated-tests"] ?? (mc["require-dedicated-tests"] === false ? 0 : 1),
|
|
265
|
-
maxSourceTodos: mc["max-source-todos"] ?? 0,
|
|
266
|
-
};
|
|
267
|
-
|
|
268
|
-
return {
|
|
269
|
-
supervisor: toml.schedule?.supervisor || "daily",
|
|
270
|
-
model: toml.tuning?.model || toml.schedule?.model || "gpt-5-mini",
|
|
271
|
-
tuning,
|
|
272
|
-
paths,
|
|
273
|
-
testScript: execution.test || "npm ci && npm test",
|
|
274
|
-
featureDevelopmentIssuesWipLimit: resolvedLimits.featureIssues,
|
|
275
|
-
maintenanceIssuesWipLimit: resolvedLimits.maintenanceIssues,
|
|
276
|
-
attemptsPerBranch: resolvedLimits.attemptsPerBranch,
|
|
277
|
-
attemptsPerIssue: resolvedLimits.attemptsPerIssue,
|
|
278
|
-
transformationBudget: tuning.transformationBudget,
|
|
279
|
-
seeding: toml.seeding || {},
|
|
280
|
-
intentionBot: {
|
|
281
|
-
intentionFilepath: bot["log-file"] || "intentïon.md",
|
|
282
|
-
},
|
|
283
|
-
init: toml.init || null,
|
|
284
|
-
tdd: toml.tdd === true,
|
|
285
|
-
missionCompleteThresholds,
|
|
286
|
-
writablePaths,
|
|
287
|
-
readOnlyPaths,
|
|
288
|
-
configToml: rawToml,
|
|
289
|
-
packageJson: readPackageJson(tomlPath, mergedPaths.dependencies),
|
|
290
|
-
};
|
|
291
|
-
}
|
|
292
|
-
|
|
293
|
-
/**
|
|
294
|
-
* Get the writable paths from config, optionally overridden by an input string.
|
|
295
|
-
*
|
|
296
|
-
* @param {AgenticConfig} config - Parsed config
|
|
297
|
-
* @param {string} [override] - Semicolon-separated override paths
|
|
298
|
-
* @returns {string[]} Array of writable paths
|
|
299
|
-
*/
|
|
300
|
-
export function getWritablePaths(config, override) {
|
|
301
|
-
if (override) {
|
|
302
|
-
return override
|
|
303
|
-
.split(";")
|
|
304
|
-
.map((p) => p.trim())
|
|
305
|
-
.filter(Boolean);
|
|
306
|
-
}
|
|
307
|
-
return config.writablePaths;
|
|
308
|
-
}
|
|
8
|
+
export { loadConfig, getWritablePaths } from "../../copilot/config.js";
|