@xn-intenton-z2a/agentic-lib 7.2.5 → 7.2.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-init.yml +56 -0
- package/.github/workflows/agentic-lib-test.yml +7 -2
- package/.github/workflows/agentic-lib-workflow.yml +50 -3
- package/README.md +88 -17
- package/agentic-lib.toml +7 -0
- package/bin/agentic-lib.js +260 -496
- package/package.json +2 -1
- package/src/actions/agentic-step/config-loader.js +9 -0
- package/src/actions/agentic-step/index.js +104 -7
- package/src/actions/agentic-step/tasks/direct.js +435 -0
- package/src/actions/agentic-step/tasks/supervise.js +107 -180
- package/src/agents/agent-apply-fix.md +5 -2
- package/src/agents/agent-director.md +58 -0
- package/src/agents/agent-discovery.md +52 -0
- package/src/agents/agent-issue-resolution.md +18 -0
- package/src/agents/agent-iterate.md +45 -0
- package/src/agents/agent-supervisor.md +22 -50
- package/src/copilot/agents.js +39 -0
- package/src/copilot/config.js +308 -0
- package/src/copilot/context.js +318 -0
- package/src/copilot/hybrid-session.js +330 -0
- package/src/copilot/logger.js +43 -0
- package/src/copilot/sdk.js +36 -0
- package/src/copilot/session.js +372 -0
- package/src/copilot/tasks/fix-code.js +73 -0
- package/src/copilot/tasks/maintain-features.js +61 -0
- package/src/copilot/tasks/maintain-library.js +66 -0
- package/src/copilot/tasks/transform.js +120 -0
- package/src/copilot/tools.js +141 -0
- package/src/mcp/server.js +43 -25
- package/src/seeds/zero-README.md +31 -0
- package/src/seeds/zero-behaviour.test.js +12 -4
- package/src/seeds/zero-package.json +1 -1
- package/src/seeds/zero-playwright.config.js +1 -0
|
@@ -1,18 +1,24 @@
|
|
|
1
1
|
You are the supervisor of an autonomous coding repository. Your job is to advance the mission by strategically choosing which workflows to dispatch and which GitHub actions to take.
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
**Important:** You do NOT evaluate mission-complete or mission-failed. That is the director's exclusive responsibility. Focus on advancing the mission through strategic action.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
## MANDATORY FIRST CHECK: What Needs to Happen Next?
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
2. Were 2+ recently-closed issues "closed by review as RESOLVED"?
|
|
9
|
-
3. Do the Source Exports show the functions required by MISSION.md?
|
|
7
|
+
**Before choosing ANY action, check the Mission-Complete Metrics table in the prompt.**
|
|
10
8
|
|
|
11
|
-
|
|
9
|
+
Look at which metrics are NOT MET — these tell you what gaps remain:
|
|
10
|
+
1. Open issues > 0 → close resolved issues or wait for review
|
|
11
|
+
2. Open PRs > 0 → merge or close stale PRs
|
|
12
|
+
3. Issues resolved < threshold → create and resolve more issues
|
|
13
|
+
4. Dedicated test files = NO → create an issue requesting dedicated tests
|
|
14
|
+
5. Source TODO count > 0 → create an issue to resolve TODOs
|
|
15
|
+
6. Budget near exhaustion → be strategic with remaining transforms
|
|
16
|
+
|
|
17
|
+
If all metrics show MET/OK, use `nop` — the director will handle the rest.
|
|
12
18
|
|
|
13
19
|
## Priority Order
|
|
14
20
|
|
|
15
|
-
1. **Always strive
|
|
21
|
+
1. **Always strive to close gaps** — every action you take should aim to satisfy the remaining NOT MET metrics. If the code is already complete (see Source Exports and Recently Closed Issues), use `nop` and let the director evaluate. Otherwise, create one comprehensive issue that targets the entire mission (all acceptance criteria, tests, website, docs, README). Only create a second issue if the first transform couldn't complete everything, and scope it to the remaining work. Do not create issues just to fill a quota.
|
|
16
22
|
2. **Dispatch transform when ready issues exist** — transform is where code gets written. Always prefer it over maintain when there are open issues with the `ready` label.
|
|
17
23
|
3. **Dispatch review after transform** — when recent workflow runs show a transform completion, dispatch review to close resolved issues and add `ready` labels to new issues. This keeps the pipeline flowing.
|
|
18
24
|
4. **Fix failing PRs** — dispatch fix-code for any PR with failing checks (include pr-number).
|
|
@@ -36,10 +42,8 @@ If ALL three are true → the mission is done. Choose `mission-complete | reason
|
|
|
36
42
|
- **github:label-issue** — When an issue needs better categorisation for prioritisation.
|
|
37
43
|
- **github:close-issue** — When an issue is clearly resolved or no longer relevant.
|
|
38
44
|
- **respond:discussions** — When replying to a user request that came through the discussions bot. Include the discussion URL and a clear message.
|
|
39
|
-
- **set-schedule:\<frequency\>** — Change the workflow schedule. Use `weekly` when
|
|
40
|
-
- **
|
|
41
|
-
- **mission-failed** — When the mission cannot be completed. Use when: transformation budget is exhausted with acceptance criteria still unmet, the pipeline is stuck in a create-close loop with no code changes, or 3+ consecutive transforms failed to produce working code. This writes MISSION_FAILED.md and sets the schedule to off. Always include a reason explaining what went wrong.
|
|
42
|
-
- **nop** — When everything is running optimally: transform is active, issues are flowing, no failures.
|
|
45
|
+
- **set-schedule:\<frequency\>** — Change the workflow schedule. Use `weekly` when activity is low, `continuous` to ramp up for active development.
|
|
46
|
+
- **nop** — When everything is running optimally: transform is active, issues are flowing, no failures. Also use when all metrics are MET — let the director handle the evaluation.
|
|
43
47
|
|
|
44
48
|
## Stale Issue Detection
|
|
45
49
|
|
|
@@ -52,43 +56,11 @@ When recent workflow runs show an init completion, the repository has a fresh or
|
|
|
52
56
|
Dispatch the discussions bot to announce the new mission to the community.
|
|
53
57
|
Include the website URL in the announcement — the site is at `https://<owner>.github.io/<repo>/` and runs the library.
|
|
54
58
|
|
|
55
|
-
### Mission Accomplished (bounded missions)
|
|
56
|
-
When ALL of the following conditions are met, the mission is accomplished:
|
|
57
|
-
1. All open issues are closed (check Recently Closed Issues — if the last 2+ were closed by review as RESOLVED, this is strong evidence)
|
|
58
|
-
2. Tests pass (CI gates commits, so this is usually the case)
|
|
59
|
-
3. The MISSION.md acceptance criteria are all satisfied (verify each criterion against the Recently Closed Issues and Recent Activity)
|
|
60
|
-
4. Do not create an issue if a similar issue was recently closed as resolved — check the Recently Closed Issues section
|
|
61
|
-
|
|
62
|
-
When all conditions are met, use the `mission-complete` action:
|
|
63
|
-
1. `mission-complete | reason: <summary of what was achieved>` — this writes MISSION_COMPLETE.md and sets the schedule to off
|
|
64
|
-
2. `dispatch:agentic-lib-bot` — announce mission accomplished in the discussions thread. Include the website URL (`https://<owner>.github.io/<repo>/`) where users can see the finished product.
|
|
65
|
-
|
|
66
|
-
Do NOT create another issue when the mission is already accomplished. If the Recently Closed Issues show 2+ issues closed by review as RESOLVED and 0 open issues remain, the mission is done.
|
|
67
|
-
|
|
68
59
|
### Ongoing Missions
|
|
69
60
|
If MISSION.md explicitly says "do not set schedule to off" or "ongoing mission", the mission never completes.
|
|
70
61
|
Instead, when activity is healthy, use `set-schedule:weekly` or `set-schedule:daily` to keep the pipeline running.
|
|
71
62
|
Never use `set-schedule:off` for ongoing missions.
|
|
72
63
|
|
|
73
|
-
### Mission Substantially Complete (bounded, but minor gaps)
|
|
74
|
-
When the transform agent has implemented all major features but minor polish remains
|
|
75
|
-
(e.g. missing README examples, incomplete edge case coverage):
|
|
76
|
-
1. `dispatch:agentic-lib-bot` — announce near-completion in the discussions thread
|
|
77
|
-
2. `set-schedule:weekly` — reduce to weekly maintenance check-ins
|
|
78
|
-
3. Check that `docs/` contains evidence of the library working before declaring done
|
|
79
|
-
|
|
80
|
-
### Mission Failed
|
|
81
|
-
When the mission cannot be completed, use the `mission-failed` action. Indicators of failure:
|
|
82
|
-
1. **Budget exhausted** — Transformation Budget shows usage at or near capacity with acceptance criteria still unmet
|
|
83
|
-
2. **Pipeline stuck** — 3+ consecutive supervisor cycles created issues that were immediately closed by review as RESOLVED, but the acceptance criteria are NOT actually met (false positives in review)
|
|
84
|
-
3. **No progress** — the last 3+ transforms produced no code changes (all nop outcomes) and acceptance criteria remain unmet
|
|
85
|
-
4. **Repeated failures** — transforms keep producing code that fails tests, and fix-code cannot resolve the failures
|
|
86
|
-
5. **Consuming budget without results** — transformation budget is being spent but the codebase is not converging toward the acceptance criteria
|
|
87
|
-
|
|
88
|
-
When declaring mission failed:
|
|
89
|
-
1. `mission-failed | reason: <what went wrong and what was achieved>` — this writes MISSION_FAILED.md and sets the schedule to off
|
|
90
|
-
2. `dispatch:agentic-lib-bot` — announce the failure in the discussions thread with details of what was accomplished and what remains
|
|
91
|
-
|
|
92
64
|
## Prerequisites
|
|
93
65
|
|
|
94
66
|
- The `set-schedule` action requires a `WORKFLOW_TOKEN` secret (classic PAT with `workflow` scope) to push workflow file changes to main.
|
|
@@ -97,13 +69,13 @@ When declaring mission failed:
|
|
|
97
69
|
|
|
98
70
|
Check the Recent Activity log and Recently Closed Issues for patterns:
|
|
99
71
|
|
|
100
|
-
**
|
|
101
|
-
- If
|
|
102
|
-
- If the last 2+ workflow runs produced no transform commits (only maintain-only or nop outcomes), AND all open issues are closed,
|
|
72
|
+
**All metrics MET signals:**
|
|
73
|
+
- If all rows in the Mission-Complete Metrics table show MET/OK, use `nop` — the director will evaluate mission-complete.
|
|
74
|
+
- If the last 2+ workflow runs produced no transform commits (only maintain-only or nop outcomes), AND all open issues are closed, use `nop`.
|
|
103
75
|
|
|
104
|
-
**
|
|
105
|
-
- If the Transformation Budget shows usage near capacity (e.g. 28/32) and acceptance criteria are still unmet,
|
|
106
|
-
- If the last 3+ cycles show the pattern: create issue → review closes as resolved → no transform → create identical issue, the pipeline is stuck. Check if acceptance criteria are truly met (
|
|
76
|
+
**Budget exhaustion signals:**
|
|
77
|
+
- If the Transformation Budget shows usage near capacity (e.g. 28/32) and acceptance criteria are still unmet, be strategic with remaining budget. Create highly-targeted issues that address the most critical gaps.
|
|
78
|
+
- If the last 3+ cycles show the pattern: create issue → review closes as resolved → no transform → create identical issue, the pipeline is stuck. Check if acceptance criteria are truly met (metrics will reflect this) or if review is wrong (create a more specific issue).
|
|
107
79
|
- Look for `transform: nop` or `transform: transformed` patterns in the activity log to distinguish productive iterations from idle ones.
|
|
108
80
|
|
|
109
81
|
**Dedup deadlock recovery:**
|
|
@@ -115,7 +87,7 @@ Check the Recent Activity log for discussion bot referrals (lines containing `di
|
|
|
115
87
|
|
|
116
88
|
Also check for notable progress worth reporting:
|
|
117
89
|
- Mission milestones achieved (all core functions implemented, all tests passing)
|
|
118
|
-
- Schedule changes (
|
|
90
|
+
- Schedule changes (throttling down)
|
|
119
91
|
- Significant code changes (large PRs merged, new features completed)
|
|
120
92
|
- Website first deployed or significantly updated (include the URL: `https://<owner>.github.io/<repo>/`)
|
|
121
93
|
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
// SPDX-License-Identifier: GPL-3.0-only
|
|
2
|
+
// Copyright (C) 2025-2026 Polycode Limited
|
|
3
|
+
// src/copilot/agents.js — Load agent prompt files from src/agents/
|
|
4
|
+
|
|
5
|
+
import { readFileSync, readdirSync } from "fs";
|
|
6
|
+
import { resolve, dirname } from "path";
|
|
7
|
+
import { fileURLToPath } from "url";
|
|
8
|
+
|
|
9
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
10
|
+
const agentsDir = resolve(__dirname, "..", "agents");
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Load an agent prompt file by name.
|
|
14
|
+
*
|
|
15
|
+
* @param {string} agentName - Agent name without extension (e.g. "agent-iterate")
|
|
16
|
+
* @returns {string} The agent prompt text
|
|
17
|
+
* @throws {Error} If the agent file is not found
|
|
18
|
+
*/
|
|
19
|
+
export function loadAgentPrompt(agentName) {
|
|
20
|
+
const filename = agentName.endsWith(".md") ? agentName : `${agentName}.md`;
|
|
21
|
+
const filePath = resolve(agentsDir, filename);
|
|
22
|
+
try {
|
|
23
|
+
return readFileSync(filePath, "utf8");
|
|
24
|
+
} catch {
|
|
25
|
+
throw new Error(`Agent prompt not found: ${filePath}`);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* List all available agent prompt files.
|
|
31
|
+
*
|
|
32
|
+
* @returns {string[]} Array of agent names (without .md extension)
|
|
33
|
+
*/
|
|
34
|
+
export function listAgents() {
|
|
35
|
+
return readdirSync(agentsDir)
|
|
36
|
+
.filter((f) => f.endsWith(".md"))
|
|
37
|
+
.map((f) => f.replace(".md", ""))
|
|
38
|
+
.sort();
|
|
39
|
+
}
|
|
@@ -0,0 +1,308 @@
|
|
|
1
|
+
// SPDX-License-Identifier: GPL-3.0-only
|
|
2
|
+
// Copyright (C) 2025-2026 Polycode Limited
|
|
3
|
+
// config-loader.js — Parse agentic-lib.toml and resolve paths
|
|
4
|
+
//
|
|
5
|
+
// TOML-only configuration. The config file is required.
|
|
6
|
+
// All defaults are defined here in one place.
|
|
7
|
+
|
|
8
|
+
import { readFileSync, existsSync } from "fs";
|
|
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
|
+
requireDedicatedTests: mc["require-dedicated-tests"] ?? true,
|
|
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
|
+
}
|