@xn-intenton-z2a/agentic-lib 7.4.8 → 7.4.9
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/{src → .github}/agents/agent-apply-fix.md +10 -0
- package/{src → .github}/agents/agent-director.md +10 -0
- package/{src → .github}/agents/agent-discovery.md +8 -0
- package/{src → .github}/agents/agent-discussion-bot.md +9 -0
- package/{src → .github}/agents/agent-issue-resolution.md +12 -0
- package/{src → .github}/agents/agent-iterate.md +8 -0
- package/{src → .github}/agents/agent-maintain-features.md +8 -0
- package/{src → .github}/agents/agent-maintain-library.md +7 -0
- package/{src → .github}/agents/agent-review-issue.md +8 -0
- package/{src → .github}/agents/agent-supervisor.md +9 -0
- package/.github/workflows/agentic-lib-test.yml +4 -2
- package/.github/workflows/agentic-lib-workflow.yml +70 -26
- package/README.md +5 -7
- package/agentic-lib.toml +16 -38
- package/bin/agentic-lib.js +49 -60
- package/package.json +3 -4
- package/src/actions/agentic-step/action.yml +1 -1
- package/src/actions/agentic-step/copilot.js +0 -5
- package/src/actions/agentic-step/index.js +8 -1
- package/src/actions/agentic-step/logging.js +14 -2
- package/src/actions/agentic-step/tasks/direct.js +86 -65
- package/src/actions/agentic-step/tasks/discussions.js +198 -264
- package/src/actions/agentic-step/tasks/enhance-issue.js +84 -33
- package/src/actions/agentic-step/tasks/fix-code.js +111 -57
- package/src/actions/agentic-step/tasks/maintain-features.js +69 -52
- package/src/actions/agentic-step/tasks/maintain-library.js +57 -19
- package/src/actions/agentic-step/tasks/resolve-issue.js +43 -18
- package/src/actions/agentic-step/tasks/review-issue.js +117 -117
- package/src/actions/agentic-step/tasks/supervise.js +140 -151
- package/src/actions/agentic-step/tasks/transform.js +106 -258
- package/src/copilot/agents.js +2 -2
- package/src/copilot/config.js +2 -18
- package/src/copilot/{hybrid-session.js → copilot-session.js} +39 -7
- package/src/copilot/github-tools.js +514 -0
- package/src/copilot/guards.js +1 -1
- package/src/copilot/session.js +0 -141
- package/src/copilot/tools.js +4 -0
- package/src/iterate.js +1 -1
- package/src/scripts/push-to-logs.sh +1 -1
- package/src/seeds/zero-SCREENSHOT_INDEX.png +0 -0
- package/src/seeds/zero-package.json +1 -1
- package/src/agents/agentic-lib.yml +0 -66
- package/src/copilot/context.js +0 -457
- package/src/mcp/server.js +0 -830
- /package/{src → .github}/agents/agent-ready-issue.md +0 -0
package/src/iterate.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// SPDX-License-Identifier: GPL-3.0-only
|
|
2
2
|
// Copyright (C) 2025-2026 Polycode Limited
|
|
3
|
-
// src/iterate.js — Shared iteration loop for CLI
|
|
3
|
+
// src/iterate.js — Shared iteration loop for CLI
|
|
4
4
|
//
|
|
5
5
|
// Runs N cycles of maintain → transform → fix, tracking transformation cost
|
|
6
6
|
// against a budget. Stops early on consecutive test passes, no-progress, or
|
|
Binary file
|
|
@@ -1,66 +0,0 @@
|
|
|
1
|
-
# Mapping for from symbolic keys to filepaths for access by agentic-lib workflows with limits and access permissions
|
|
2
|
-
paths:
|
|
3
|
-
# Filepaths for elaborator workflows
|
|
4
|
-
missionFilepath:
|
|
5
|
-
path: "MISSION.md"
|
|
6
|
-
librarySourcesFilepath:
|
|
7
|
-
path: "SOURCES.md"
|
|
8
|
-
permissions: ["write"]
|
|
9
|
-
limit: 32
|
|
10
|
-
libraryDocumentsPath:
|
|
11
|
-
path: "library/"
|
|
12
|
-
permissions: ["write"]
|
|
13
|
-
limit: 32
|
|
14
|
-
featuresPath:
|
|
15
|
-
path: "features/"
|
|
16
|
-
permissions: ["write"]
|
|
17
|
-
limit: 4
|
|
18
|
-
|
|
19
|
-
# Filepaths for engineer workflows
|
|
20
|
-
contributingFilepath:
|
|
21
|
-
path: "CONTRIBUTING.md"
|
|
22
|
-
targetTestsPath:
|
|
23
|
-
path: "tests/unit/"
|
|
24
|
-
permissions: ["write"]
|
|
25
|
-
targetSourcePath:
|
|
26
|
-
path: "src/lib/"
|
|
27
|
-
permissions: ["write"]
|
|
28
|
-
targetWebPath:
|
|
29
|
-
path: "src/web/"
|
|
30
|
-
permissions: ["write"]
|
|
31
|
-
dependenciesFilepath:
|
|
32
|
-
path: "package.json"
|
|
33
|
-
permissions: ["write"]
|
|
34
|
-
documentationPath:
|
|
35
|
-
path: "docs/"
|
|
36
|
-
permissions: ["write"]
|
|
37
|
-
|
|
38
|
-
# Filepaths for maintainer workflows
|
|
39
|
-
formattingFilepath:
|
|
40
|
-
path: ".prettierrc"
|
|
41
|
-
lintingFilepath:
|
|
42
|
-
path: "eslint.config.js"
|
|
43
|
-
readmeFilepath:
|
|
44
|
-
path: "README.md"
|
|
45
|
-
permissions: ["write"]
|
|
46
|
-
|
|
47
|
-
# Execution commands
|
|
48
|
-
testScript: "npm ci && npm test"
|
|
49
|
-
|
|
50
|
-
# How many issues should be available to be picked up?
|
|
51
|
-
featureDevelopmentIssuesWipLimit: 2
|
|
52
|
-
maintenanceIssuesWipLimit: 1
|
|
53
|
-
|
|
54
|
-
# How many attempts should be made to work on an issue?
|
|
55
|
-
attemptsPerBranch: 3
|
|
56
|
-
attemptsPerIssue: 2
|
|
57
|
-
|
|
58
|
-
# Repository seeding
|
|
59
|
-
seeding:
|
|
60
|
-
missionFilepath: "src/seeds/zero-MISSION.md"
|
|
61
|
-
sourcePath: "src/seeds/zero-main.js"
|
|
62
|
-
testsPath: "src/seeds/zero-main.test.js"
|
|
63
|
-
dependenciesFilepath: "src/seeds/zero-package.json"
|
|
64
|
-
|
|
65
|
-
intentionBot:
|
|
66
|
-
intentionFilepath: "intentïon.md"
|
package/src/copilot/context.js
DELETED
|
@@ -1,457 +0,0 @@
|
|
|
1
|
-
// SPDX-License-Identifier: GPL-3.0-only
|
|
2
|
-
// Copyright (C) 2025-2026 Polycode Limited
|
|
3
|
-
// src/copilot/context.js — Context gathering and user prompt assembly
|
|
4
|
-
//
|
|
5
|
-
// Builds user prompts for each agent type from available local and GitHub context.
|
|
6
|
-
// Works with or without GitHub data — local-only context is always sufficient.
|
|
7
|
-
|
|
8
|
-
import { resolve } from "path";
|
|
9
|
-
import { execSync } from "child_process";
|
|
10
|
-
import { scanDirectory, readOptionalFile, extractFeatureSummary, formatPathsSection, summariseIssue, filterIssues } from "./session.js";
|
|
11
|
-
import { readCumulativeCost } from "./telemetry.js";
|
|
12
|
-
import { defaultLogger } from "./logger.js";
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
* Per-agent refinements that control context precision and prompt quality.
|
|
16
|
-
* These restore the tight scoping that existed in the old per-task handlers.
|
|
17
|
-
*/
|
|
18
|
-
const AGENT_REFINEMENTS = {
|
|
19
|
-
"agent-issue-resolution": {
|
|
20
|
-
sortFeatures: "incomplete-first",
|
|
21
|
-
includeWebFiles: true,
|
|
22
|
-
highlightTargetIssue: true,
|
|
23
|
-
trackPromptBudget: true,
|
|
24
|
-
},
|
|
25
|
-
"agent-apply-fix": {
|
|
26
|
-
emphasizeTestOutput: true,
|
|
27
|
-
},
|
|
28
|
-
"agent-maintain-features": {
|
|
29
|
-
sortFeatures: "incomplete-first",
|
|
30
|
-
injectLimit: "features",
|
|
31
|
-
},
|
|
32
|
-
"agent-maintain-library": {
|
|
33
|
-
checkSourcesForUrls: true,
|
|
34
|
-
injectLimit: "library",
|
|
35
|
-
},
|
|
36
|
-
};
|
|
37
|
-
|
|
38
|
-
/**
|
|
39
|
-
* Context requirements per agent. Defines what context each agent needs.
|
|
40
|
-
* All fields are optional — the builder includes whatever is available.
|
|
41
|
-
*/
|
|
42
|
-
const AGENT_CONTEXT = {
|
|
43
|
-
"agent-iterate": { mission: true, source: true, tests: true, features: true },
|
|
44
|
-
"agent-discovery": { source: true, tests: true },
|
|
45
|
-
"agent-issue-resolution": { mission: true, source: true, tests: true, features: true, issues: true },
|
|
46
|
-
"agent-apply-fix": { source: true, tests: true },
|
|
47
|
-
"agent-maintain-features": { mission: true, features: true, issues: true },
|
|
48
|
-
"agent-maintain-library": { library: true, librarySources: true },
|
|
49
|
-
"agent-ready-issue": { mission: true, features: true, issues: true },
|
|
50
|
-
"agent-review-issue": { source: true, tests: true, issues: true },
|
|
51
|
-
"agent-discussion-bot": { mission: true, features: true },
|
|
52
|
-
"agent-supervisor": { mission: true, features: true, issues: true },
|
|
53
|
-
"agent-director": { mission: true, features: true, issues: true, source: true, tests: true },
|
|
54
|
-
};
|
|
55
|
-
|
|
56
|
-
/**
|
|
57
|
-
* Gather local context from the workspace filesystem.
|
|
58
|
-
*
|
|
59
|
-
* @param {string} workspacePath - Path to the workspace
|
|
60
|
-
* @param {Object} config - Parsed agentic config (from config.js)
|
|
61
|
-
* @param {Object} [options]
|
|
62
|
-
* @param {Object} [options.logger]
|
|
63
|
-
* @returns {Object} Context object with all available local data
|
|
64
|
-
*/
|
|
65
|
-
export function gatherLocalContext(workspacePath, config, { logger = defaultLogger } = {}) {
|
|
66
|
-
const wsPath = resolve(workspacePath);
|
|
67
|
-
const paths = config.paths || {};
|
|
68
|
-
const tuning = config.tuning || {};
|
|
69
|
-
|
|
70
|
-
const context = {};
|
|
71
|
-
|
|
72
|
-
// Mission
|
|
73
|
-
const missionPath = paths.mission?.path || "MISSION.md";
|
|
74
|
-
context.mission = readOptionalFile(resolve(wsPath, missionPath));
|
|
75
|
-
|
|
76
|
-
// Source files
|
|
77
|
-
const sourcePath = paths.source?.path || "src/lib/";
|
|
78
|
-
const sourceDir = resolve(wsPath, sourcePath);
|
|
79
|
-
context.sourceFiles = scanDirectory(sourceDir, [".js", ".ts", ".mjs", ".cjs"], {
|
|
80
|
-
fileLimit: tuning.sourceScan || 10,
|
|
81
|
-
contentLimit: tuning.sourceContent || 5000,
|
|
82
|
-
sortByMtime: true,
|
|
83
|
-
clean: true,
|
|
84
|
-
outline: true,
|
|
85
|
-
}, logger);
|
|
86
|
-
|
|
87
|
-
// Test files
|
|
88
|
-
const testsPath = paths.tests?.path || "tests/";
|
|
89
|
-
const testsDir = resolve(wsPath, testsPath);
|
|
90
|
-
context.testFiles = scanDirectory(testsDir, [".js", ".ts", ".test.js", ".test.ts", ".spec.js"], {
|
|
91
|
-
fileLimit: tuning.sourceScan || 10,
|
|
92
|
-
contentLimit: tuning.testContent || 3000,
|
|
93
|
-
sortByMtime: true,
|
|
94
|
-
clean: true,
|
|
95
|
-
}, logger);
|
|
96
|
-
|
|
97
|
-
// Features
|
|
98
|
-
const featuresPath = paths.features?.path || "features/";
|
|
99
|
-
const featuresDir = resolve(wsPath, featuresPath);
|
|
100
|
-
const featureFiles = scanDirectory(featuresDir, [".md"], {
|
|
101
|
-
fileLimit: tuning.featuresScan || 10,
|
|
102
|
-
sortByMtime: true,
|
|
103
|
-
}, logger);
|
|
104
|
-
context.features = featureFiles.map((f) => extractFeatureSummary(f.content, f.name));
|
|
105
|
-
|
|
106
|
-
// Library
|
|
107
|
-
const libraryPath = paths.library?.path || "library/";
|
|
108
|
-
const libraryDir = resolve(wsPath, libraryPath);
|
|
109
|
-
context.libraryFiles = scanDirectory(libraryDir, [".md"], {
|
|
110
|
-
fileLimit: 10,
|
|
111
|
-
contentLimit: tuning.documentSummary || 2000,
|
|
112
|
-
}, logger);
|
|
113
|
-
|
|
114
|
-
// Library sources
|
|
115
|
-
const sourcesPath = paths.librarySources?.path || "SOURCES.md";
|
|
116
|
-
context.librarySources = readOptionalFile(resolve(wsPath, sourcesPath));
|
|
117
|
-
|
|
118
|
-
// Web files
|
|
119
|
-
const webPath = paths.web?.path || "src/web/";
|
|
120
|
-
const webDir = resolve(wsPath, webPath);
|
|
121
|
-
context.webFiles = scanDirectory(webDir, [".html", ".css", ".js"], {
|
|
122
|
-
fileLimit: tuning.sourceScan || 10,
|
|
123
|
-
contentLimit: tuning.sourceContent || 5000,
|
|
124
|
-
sortByMtime: true,
|
|
125
|
-
clean: true,
|
|
126
|
-
}, logger);
|
|
127
|
-
|
|
128
|
-
// Contributing guide
|
|
129
|
-
const contributingPath = paths.contributing?.path || "CONTRIBUTING.md";
|
|
130
|
-
context.contributing = readOptionalFile(resolve(wsPath, contributingPath), 2000);
|
|
131
|
-
|
|
132
|
-
// Package.json
|
|
133
|
-
context.packageJson = config.packageJson || readOptionalFile(resolve(wsPath, "package.json"), 3000);
|
|
134
|
-
|
|
135
|
-
// Config TOML
|
|
136
|
-
context.configToml = config.configToml || "";
|
|
137
|
-
|
|
138
|
-
// Paths
|
|
139
|
-
context.writablePaths = config.writablePaths || [];
|
|
140
|
-
context.readOnlyPaths = config.readOnlyPaths || [];
|
|
141
|
-
|
|
142
|
-
// Initial test output
|
|
143
|
-
try {
|
|
144
|
-
context.testOutput = execSync("npm test 2>&1", { cwd: wsPath, encoding: "utf8", timeout: 120000 });
|
|
145
|
-
} catch (err) {
|
|
146
|
-
context.testOutput = `STDOUT:\n${err.stdout || ""}\nSTDERR:\n${err.stderr || ""}`;
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
return context;
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
/**
|
|
153
|
-
* Fetch GitHub context using the `gh` CLI.
|
|
154
|
-
* Returns null fields when gh is unavailable or data can't be fetched.
|
|
155
|
-
*
|
|
156
|
-
* @param {Object} options
|
|
157
|
-
* @param {number} [options.issueNumber] - Issue number to fetch
|
|
158
|
-
* @param {number} [options.prNumber] - PR number to fetch
|
|
159
|
-
* @param {string} [options.discussionUrl] - Discussion URL to fetch
|
|
160
|
-
* @param {string} [options.workspacePath] - CWD for gh commands
|
|
161
|
-
* @param {Object} [options.logger]
|
|
162
|
-
* @returns {Object} GitHub context
|
|
163
|
-
*/
|
|
164
|
-
export function gatherGitHubContext({ issueNumber, prNumber, discussionUrl, workspacePath, logger = defaultLogger } = {}) {
|
|
165
|
-
const github = { issues: [], issueDetail: null, prDetail: null, discussionDetail: null };
|
|
166
|
-
const cwd = workspacePath || process.cwd();
|
|
167
|
-
|
|
168
|
-
try {
|
|
169
|
-
// Fetch open issues list
|
|
170
|
-
const issuesJson = execSync("gh issue list --state open --limit 20 --json number,title,labels,body,createdAt,updatedAt", {
|
|
171
|
-
cwd,
|
|
172
|
-
encoding: "utf8",
|
|
173
|
-
timeout: 30000,
|
|
174
|
-
});
|
|
175
|
-
const rawIssues = JSON.parse(issuesJson);
|
|
176
|
-
github.issues = filterIssues(rawIssues.map((i) => ({
|
|
177
|
-
number: i.number,
|
|
178
|
-
title: i.title,
|
|
179
|
-
body: i.body,
|
|
180
|
-
labels: i.labels,
|
|
181
|
-
created_at: i.createdAt,
|
|
182
|
-
updated_at: i.updatedAt,
|
|
183
|
-
})));
|
|
184
|
-
} catch (err) {
|
|
185
|
-
logger.info(`[context] Could not fetch issues: ${err.message}`);
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
// Fetch specific issue detail
|
|
189
|
-
if (issueNumber) {
|
|
190
|
-
try {
|
|
191
|
-
const issueJson = execSync(`gh issue view ${issueNumber} --json number,title,body,labels,comments,createdAt`, {
|
|
192
|
-
cwd,
|
|
193
|
-
encoding: "utf8",
|
|
194
|
-
timeout: 30000,
|
|
195
|
-
});
|
|
196
|
-
github.issueDetail = JSON.parse(issueJson);
|
|
197
|
-
} catch (err) {
|
|
198
|
-
logger.info(`[context] Could not fetch issue #${issueNumber}: ${err.message}`);
|
|
199
|
-
}
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
// Fetch specific PR detail
|
|
203
|
-
if (prNumber) {
|
|
204
|
-
try {
|
|
205
|
-
const prJson = execSync(`gh pr view ${prNumber} --json number,title,body,files,statusCheckRollup`, {
|
|
206
|
-
cwd,
|
|
207
|
-
encoding: "utf8",
|
|
208
|
-
timeout: 30000,
|
|
209
|
-
});
|
|
210
|
-
github.prDetail = JSON.parse(prJson);
|
|
211
|
-
} catch (err) {
|
|
212
|
-
logger.info(`[context] Could not fetch PR #${prNumber}: ${err.message}`);
|
|
213
|
-
}
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
// Fetch discussion
|
|
217
|
-
if (discussionUrl) {
|
|
218
|
-
try {
|
|
219
|
-
// Extract discussion number from URL
|
|
220
|
-
const match = discussionUrl.match(/discussions\/(\d+)/);
|
|
221
|
-
if (match) {
|
|
222
|
-
const num = match[1];
|
|
223
|
-
const discussionJson = execSync(
|
|
224
|
-
`gh api graphql -f query='{ repository(owner:"{owner}", name:"{repo}") { discussion(number: ${num}) { title body comments(last: 10) { nodes { body author { login } createdAt } } } } }'`,
|
|
225
|
-
{ cwd, encoding: "utf8", timeout: 30000 },
|
|
226
|
-
);
|
|
227
|
-
github.discussionDetail = JSON.parse(discussionJson);
|
|
228
|
-
}
|
|
229
|
-
} catch (err) {
|
|
230
|
-
logger.info(`[context] Could not fetch discussion: ${err.message}`);
|
|
231
|
-
}
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
return github;
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
/**
|
|
238
|
-
* Build a user prompt for the given agent from available context.
|
|
239
|
-
*
|
|
240
|
-
* Returns an object with the assembled prompt and optional promptBudget metadata.
|
|
241
|
-
* The prompt includes config-driven limits and per-agent refinements that restore
|
|
242
|
-
* the context precision from the old per-task handlers.
|
|
243
|
-
*
|
|
244
|
-
* @param {string} agentName - Agent name (e.g. "agent-iterate")
|
|
245
|
-
* @param {Object} localContext - From gatherLocalContext()
|
|
246
|
-
* @param {Object} [githubContext] - From gatherGitHubContext() (optional)
|
|
247
|
-
* @param {Object} [options]
|
|
248
|
-
* @param {Object} [options.tuning] - Tuning config for limits
|
|
249
|
-
* @param {Object} [options.config] - Full parsed config (for limits injection)
|
|
250
|
-
* @returns {{ prompt: string, promptBudget: Array|null }}
|
|
251
|
-
*/
|
|
252
|
-
export function buildUserPrompt(agentName, localContext, githubContext, { tuning, config } = {}) {
|
|
253
|
-
const needs = AGENT_CONTEXT[agentName] || AGENT_CONTEXT["agent-iterate"];
|
|
254
|
-
const refinements = AGENT_REFINEMENTS[agentName] || {};
|
|
255
|
-
const sections = [];
|
|
256
|
-
const promptBudget = refinements.trackPromptBudget ? [] : null;
|
|
257
|
-
|
|
258
|
-
// Target issue — placed prominently when highlightTargetIssue is set
|
|
259
|
-
if (refinements.highlightTargetIssue && githubContext?.issueDetail) {
|
|
260
|
-
const issue = githubContext.issueDetail;
|
|
261
|
-
const issueSection = [
|
|
262
|
-
`# Target Issue #${issue.number}: ${issue.title}`,
|
|
263
|
-
issue.body || "(no description)",
|
|
264
|
-
`Labels: ${(issue.labels || []).map((l) => typeof l === "string" ? l : l.name).join(", ") || "none"}`,
|
|
265
|
-
"",
|
|
266
|
-
"**Focus your transformation on resolving this specific issue.**",
|
|
267
|
-
];
|
|
268
|
-
if (issue.comments?.length > 0) {
|
|
269
|
-
issueSection.push("\n## Comments");
|
|
270
|
-
for (const c of issue.comments.slice(-5)) {
|
|
271
|
-
issueSection.push(`**${c.author?.login || "unknown"}**: ${c.body}`);
|
|
272
|
-
}
|
|
273
|
-
}
|
|
274
|
-
const text = issueSection.join("\n");
|
|
275
|
-
sections.push(text);
|
|
276
|
-
if (promptBudget) promptBudget.push({ section: "target-issue", size: text.length, files: "1", notes: "" });
|
|
277
|
-
}
|
|
278
|
-
|
|
279
|
-
// Mission
|
|
280
|
-
if (needs.mission && localContext.mission) {
|
|
281
|
-
const text = `# Mission\n\n${localContext.mission}`;
|
|
282
|
-
sections.push(text);
|
|
283
|
-
if (promptBudget) promptBudget.push({ section: "mission", size: localContext.mission.length, files: "1", notes: "full" });
|
|
284
|
-
}
|
|
285
|
-
|
|
286
|
-
// Current test state — emphasised for fix-code agent
|
|
287
|
-
if (localContext.testOutput) {
|
|
288
|
-
const testPreview = localContext.testOutput.substring(0, 4000);
|
|
289
|
-
if (refinements.emphasizeTestOutput) {
|
|
290
|
-
sections.push(`# Failing Test Output\n\nThe tests are currently failing. Fix the root cause.\n\n\`\`\`\n${testPreview}\n\`\`\``);
|
|
291
|
-
} else {
|
|
292
|
-
sections.push(`# Current Test State\n\n\`\`\`\n${testPreview}\n\`\`\``);
|
|
293
|
-
}
|
|
294
|
-
}
|
|
295
|
-
|
|
296
|
-
// Source files
|
|
297
|
-
if (needs.source && localContext.sourceFiles?.length > 0) {
|
|
298
|
-
const sourceSection = [`# Source Files (${localContext.sourceFiles.length})`];
|
|
299
|
-
for (const f of localContext.sourceFiles) {
|
|
300
|
-
sourceSection.push(`## ${f.name}\n\`\`\`\n${f.content}\n\`\`\``);
|
|
301
|
-
}
|
|
302
|
-
const text = sourceSection.join("\n\n");
|
|
303
|
-
sections.push(text);
|
|
304
|
-
if (promptBudget) promptBudget.push({ section: "source", size: text.length, files: `${localContext.sourceFiles.length}`, notes: "" });
|
|
305
|
-
}
|
|
306
|
-
|
|
307
|
-
// Web files — only when refinements request it
|
|
308
|
-
if (refinements.includeWebFiles && localContext.webFiles?.length > 0) {
|
|
309
|
-
const webSection = [`# Website Files (${localContext.webFiles.length})`];
|
|
310
|
-
for (const f of localContext.webFiles) {
|
|
311
|
-
webSection.push(`## ${f.name}\n\`\`\`\n${f.content}\n\`\`\``);
|
|
312
|
-
}
|
|
313
|
-
const text = webSection.join("\n\n");
|
|
314
|
-
sections.push(text);
|
|
315
|
-
if (promptBudget) promptBudget.push({ section: "web", size: text.length, files: `${localContext.webFiles.length}`, notes: "" });
|
|
316
|
-
}
|
|
317
|
-
|
|
318
|
-
// Test files
|
|
319
|
-
if (needs.tests && localContext.testFiles?.length > 0) {
|
|
320
|
-
const testSection = [`# Test Files (${localContext.testFiles.length})`];
|
|
321
|
-
for (const f of localContext.testFiles) {
|
|
322
|
-
testSection.push(`## ${f.name}\n\`\`\`\n${f.content}\n\`\`\``);
|
|
323
|
-
}
|
|
324
|
-
const text = testSection.join("\n\n");
|
|
325
|
-
sections.push(text);
|
|
326
|
-
if (promptBudget) promptBudget.push({ section: "tests", size: text.length, files: `${localContext.testFiles.length}`, notes: "" });
|
|
327
|
-
}
|
|
328
|
-
|
|
329
|
-
// Features — sorted incomplete-first when refinements request it
|
|
330
|
-
if (needs.features && localContext.features?.length > 0) {
|
|
331
|
-
let features = [...localContext.features];
|
|
332
|
-
if (refinements.sortFeatures === "incomplete-first") {
|
|
333
|
-
features.sort((a, b) => {
|
|
334
|
-
const aIncomplete = /Remaining:/.test(a) || /\[ \]/.test(a) ? 0 : 1;
|
|
335
|
-
const bIncomplete = /Remaining:/.test(b) || /\[ \]/.test(b) ? 0 : 1;
|
|
336
|
-
return aIncomplete - bIncomplete;
|
|
337
|
-
});
|
|
338
|
-
}
|
|
339
|
-
|
|
340
|
-
const limit = config?.paths?.features?.limit;
|
|
341
|
-
const header = limit
|
|
342
|
-
? `# Features (${features.length}/${limit} max)`
|
|
343
|
-
: `# Features (${features.length})`;
|
|
344
|
-
const featureSection = [header];
|
|
345
|
-
for (const f of features) {
|
|
346
|
-
featureSection.push(f);
|
|
347
|
-
}
|
|
348
|
-
const text = featureSection.join("\n\n");
|
|
349
|
-
sections.push(text);
|
|
350
|
-
if (promptBudget) promptBudget.push({ section: "features", size: text.length, files: `${features.length}`, notes: "" });
|
|
351
|
-
}
|
|
352
|
-
|
|
353
|
-
// Library
|
|
354
|
-
if (needs.library && localContext.libraryFiles?.length > 0) {
|
|
355
|
-
const libLimit = config?.paths?.library?.limit;
|
|
356
|
-
const header = libLimit
|
|
357
|
-
? `# Library Files (${localContext.libraryFiles.length}/${libLimit} max)`
|
|
358
|
-
: `# Library Files (${localContext.libraryFiles.length})`;
|
|
359
|
-
const libSection = [header];
|
|
360
|
-
for (const f of localContext.libraryFiles) {
|
|
361
|
-
libSection.push(`## ${f.name}\n${f.content}`);
|
|
362
|
-
}
|
|
363
|
-
sections.push(libSection.join("\n\n"));
|
|
364
|
-
}
|
|
365
|
-
|
|
366
|
-
// Library sources — vary strategy based on URL presence
|
|
367
|
-
if (needs.librarySources && localContext.librarySources) {
|
|
368
|
-
if (refinements.checkSourcesForUrls) {
|
|
369
|
-
const hasUrls = /https?:\/\//.test(localContext.librarySources);
|
|
370
|
-
if (!hasUrls) {
|
|
371
|
-
sections.push(`# Sources\n\n${localContext.librarySources}\n\nPopulate SOURCES.md with 3-8 relevant reference URLs aligned with the mission.`);
|
|
372
|
-
} else {
|
|
373
|
-
sections.push(`# Sources\n\n${localContext.librarySources}`);
|
|
374
|
-
}
|
|
375
|
-
} else {
|
|
376
|
-
sections.push(`# Sources\n\n${localContext.librarySources}`);
|
|
377
|
-
}
|
|
378
|
-
}
|
|
379
|
-
|
|
380
|
-
// Issues (from GitHub context) — not duplicated if target issue already shown
|
|
381
|
-
if (needs.issues && githubContext?.issues?.length > 0) {
|
|
382
|
-
const issueSection = [`# Open Issues (${githubContext.issues.length})`];
|
|
383
|
-
for (const issue of githubContext.issues) {
|
|
384
|
-
issueSection.push(summariseIssue(issue, tuning?.issueBodyLimit || 500));
|
|
385
|
-
}
|
|
386
|
-
const text = issueSection.join("\n\n");
|
|
387
|
-
sections.push(text);
|
|
388
|
-
if (promptBudget) promptBudget.push({ section: "issues", size: text.length, files: `${githubContext.issues.length}`, notes: "" });
|
|
389
|
-
}
|
|
390
|
-
|
|
391
|
-
// Specific issue detail (only if not already highlighted as target issue)
|
|
392
|
-
if (!refinements.highlightTargetIssue && githubContext?.issueDetail) {
|
|
393
|
-
const issue = githubContext.issueDetail;
|
|
394
|
-
const issueSection = [`# Issue #${issue.number}: ${issue.title}\n\n${issue.body || "(no body)"}`];
|
|
395
|
-
if (issue.comments?.length > 0) {
|
|
396
|
-
issueSection.push("## Comments");
|
|
397
|
-
for (const c of issue.comments.slice(-10)) {
|
|
398
|
-
issueSection.push(`**${c.author?.login || "unknown"}**: ${c.body}`);
|
|
399
|
-
}
|
|
400
|
-
}
|
|
401
|
-
sections.push(issueSection.join("\n\n"));
|
|
402
|
-
}
|
|
403
|
-
|
|
404
|
-
// Specific PR detail
|
|
405
|
-
if (githubContext?.prDetail) {
|
|
406
|
-
const pr = githubContext.prDetail;
|
|
407
|
-
const prSection = [`# PR #${pr.number}: ${pr.title}\n\n${pr.body || "(no body)"}`];
|
|
408
|
-
if (pr.files?.length > 0) {
|
|
409
|
-
prSection.push(`## Changed Files\n${pr.files.map((f) => `- ${f.path}`).join("\n")}`);
|
|
410
|
-
}
|
|
411
|
-
sections.push(prSection.join("\n\n"));
|
|
412
|
-
}
|
|
413
|
-
|
|
414
|
-
// File paths section
|
|
415
|
-
if (localContext.writablePaths?.length > 0 || localContext.readOnlyPaths?.length > 0) {
|
|
416
|
-
sections.push(formatPathsSection(
|
|
417
|
-
localContext.writablePaths || [],
|
|
418
|
-
localContext.readOnlyPaths || [],
|
|
419
|
-
{ configToml: localContext.configToml, packageJson: localContext.packageJson },
|
|
420
|
-
));
|
|
421
|
-
}
|
|
422
|
-
|
|
423
|
-
// Limits section — inject concrete numbers from agentic-lib.toml
|
|
424
|
-
if (config) {
|
|
425
|
-
const limitsLines = ["# Limits (from agentic-lib.toml)", ""];
|
|
426
|
-
const budget = config.transformationBudget || 0;
|
|
427
|
-
const featLimit = config.paths?.features?.limit;
|
|
428
|
-
const libLimit = config.paths?.library?.limit;
|
|
429
|
-
|
|
430
|
-
if (featLimit) limitsLines.push(`- Maximum feature files: ${featLimit}`);
|
|
431
|
-
if (libLimit) limitsLines.push(`- Maximum library documents: ${libLimit}`);
|
|
432
|
-
if (budget > 0) {
|
|
433
|
-
const intentionPath = config.intentionBot?.intentionFilepath;
|
|
434
|
-
const cumulativeCost = readCumulativeCost(intentionPath);
|
|
435
|
-
const remaining = Math.max(0, budget - cumulativeCost);
|
|
436
|
-
limitsLines.push(`- Transformation budget: ${budget} (used: ${cumulativeCost}, remaining: ${remaining})`);
|
|
437
|
-
}
|
|
438
|
-
if (config.featureDevelopmentIssuesWipLimit) limitsLines.push(`- Maximum concurrent feature issues: ${config.featureDevelopmentIssuesWipLimit}`);
|
|
439
|
-
if (config.maintenanceIssuesWipLimit) limitsLines.push(`- Maximum concurrent maintenance issues: ${config.maintenanceIssuesWipLimit}`);
|
|
440
|
-
if (config.attemptsPerBranch) limitsLines.push(`- Maximum attempts per branch: ${config.attemptsPerBranch}`);
|
|
441
|
-
if (config.attemptsPerIssue) limitsLines.push(`- Maximum attempts per issue: ${config.attemptsPerIssue}`);
|
|
442
|
-
|
|
443
|
-
if (limitsLines.length > 2) {
|
|
444
|
-
sections.push(limitsLines.join("\n"));
|
|
445
|
-
}
|
|
446
|
-
}
|
|
447
|
-
|
|
448
|
-
// Instructions
|
|
449
|
-
sections.push([
|
|
450
|
-
"Implement this mission. Read the existing source code and tests,",
|
|
451
|
-
"make the required changes, run run_tests to verify, and iterate until all tests pass.",
|
|
452
|
-
"",
|
|
453
|
-
"Start by reading the existing files, then implement the solution.",
|
|
454
|
-
].join("\n"));
|
|
455
|
-
|
|
456
|
-
return { prompt: sections.join("\n\n"), promptBudget };
|
|
457
|
-
}
|