@xn-intenton-z2a/agentic-lib 7.1.25 → 7.1.26
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/package.json
CHANGED
|
@@ -13,7 +13,7 @@ inputs:
|
|
|
13
13
|
task:
|
|
14
14
|
description: >
|
|
15
15
|
The task to perform. One of: resolve-issue, fix-code, transform, maintain-features,
|
|
16
|
-
maintain-library, enhance-issue, review-issue, discussions
|
|
16
|
+
maintain-library, enhance-issue, review-issue, discussions, supervise
|
|
17
17
|
required: true
|
|
18
18
|
config:
|
|
19
19
|
description: "Path to agentic-lib.yml configuration file"
|
|
@@ -20,6 +20,7 @@ import { maintainLibrary } from "./tasks/maintain-library.js";
|
|
|
20
20
|
import { enhanceIssue } from "./tasks/enhance-issue.js";
|
|
21
21
|
import { reviewIssue } from "./tasks/review-issue.js";
|
|
22
22
|
import { discussions } from "./tasks/discussions.js";
|
|
23
|
+
import { supervise } from "./tasks/supervise.js";
|
|
23
24
|
|
|
24
25
|
const TASKS = {
|
|
25
26
|
"resolve-issue": resolveIssue,
|
|
@@ -30,6 +31,7 @@ const TASKS = {
|
|
|
30
31
|
"enhance-issue": enhanceIssue,
|
|
31
32
|
"review-issue": reviewIssue,
|
|
32
33
|
"discussions": discussions,
|
|
34
|
+
"supervise": supervise,
|
|
33
35
|
};
|
|
34
36
|
|
|
35
37
|
async function run() {
|
|
@@ -0,0 +1,285 @@
|
|
|
1
|
+
// SPDX-License-Identifier: GPL-3.0-only
|
|
2
|
+
// Copyright (C) 2025-2026 Polycode Limited
|
|
3
|
+
// tasks/supervise.js — Supervisor orchestration via LLM
|
|
4
|
+
//
|
|
5
|
+
// Gathers repository context (issues, PRs, workflows, features, library, activity),
|
|
6
|
+
// asks the Copilot SDK to choose multiple concurrent actions, then dispatches them.
|
|
7
|
+
|
|
8
|
+
import * as core from "@actions/core";
|
|
9
|
+
import { existsSync } from "fs";
|
|
10
|
+
import { runCopilotTask, readOptionalFile, scanDirectory } from "../copilot.js";
|
|
11
|
+
|
|
12
|
+
async function gatherContext(octokit, repo, config) {
|
|
13
|
+
const mission = readOptionalFile(config.paths.mission.path);
|
|
14
|
+
const recentActivity = readOptionalFile(config.intentionBot.intentionFilepath).split("\n").slice(-20).join("\n");
|
|
15
|
+
|
|
16
|
+
const featuresPath = config.paths.features.path;
|
|
17
|
+
const featureNames = existsSync(featuresPath)
|
|
18
|
+
? scanDirectory(featuresPath, ".md").map((f) => f.name.replace(".md", ""))
|
|
19
|
+
: [];
|
|
20
|
+
const featuresLimit = config.paths.features.limit || 4;
|
|
21
|
+
|
|
22
|
+
const libraryPath = config.paths.library?.path || "library/";
|
|
23
|
+
const libraryNames = existsSync(libraryPath)
|
|
24
|
+
? scanDirectory(libraryPath, ".md").map((f) => f.name.replace(".md", ""))
|
|
25
|
+
: [];
|
|
26
|
+
const libraryLimit = config.paths.library?.limit || 32;
|
|
27
|
+
|
|
28
|
+
const { data: openIssues } = await octokit.rest.issues.listForRepo({
|
|
29
|
+
...repo,
|
|
30
|
+
state: "open",
|
|
31
|
+
per_page: 20,
|
|
32
|
+
sort: "updated",
|
|
33
|
+
direction: "desc",
|
|
34
|
+
});
|
|
35
|
+
const issuesSummary = openIssues
|
|
36
|
+
.filter((i) => !i.pull_request)
|
|
37
|
+
.map((i) => {
|
|
38
|
+
const age = Math.floor((Date.now() - new Date(i.created_at).getTime()) / 86400000);
|
|
39
|
+
const labels = i.labels.map((l) => l.name).join(", ");
|
|
40
|
+
return `#${i.number}: ${i.title} [${labels || "no labels"}] (${age}d old)`;
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
const { data: openPRs } = await octokit.rest.pulls.list({
|
|
44
|
+
...repo,
|
|
45
|
+
state: "open",
|
|
46
|
+
per_page: 10,
|
|
47
|
+
sort: "updated",
|
|
48
|
+
direction: "desc",
|
|
49
|
+
});
|
|
50
|
+
const prsSummary = openPRs.map((pr) => {
|
|
51
|
+
const age = Math.floor((Date.now() - new Date(pr.created_at).getTime()) / 86400000);
|
|
52
|
+
const labels = pr.labels.map((l) => l.name).join(", ");
|
|
53
|
+
return `#${pr.number}: ${pr.title} (${pr.head.ref}) [${labels || "no labels"}] (${age}d old)`;
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
let workflowsSummary = [];
|
|
57
|
+
try {
|
|
58
|
+
const { data: runs } = await octokit.rest.actions.listWorkflowRunsForRepo({
|
|
59
|
+
...repo,
|
|
60
|
+
per_page: 10,
|
|
61
|
+
});
|
|
62
|
+
workflowsSummary = runs.workflow_runs.map((r) => `${r.name}: ${r.conclusion || r.status} (${r.created_at})`);
|
|
63
|
+
} catch (err) {
|
|
64
|
+
core.warning(`Could not fetch workflow runs: ${err.message}`);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
return {
|
|
68
|
+
mission,
|
|
69
|
+
recentActivity,
|
|
70
|
+
featureNames,
|
|
71
|
+
featuresLimit,
|
|
72
|
+
libraryNames,
|
|
73
|
+
libraryLimit,
|
|
74
|
+
issuesSummary,
|
|
75
|
+
prsSummary,
|
|
76
|
+
workflowsSummary,
|
|
77
|
+
schedule: config.schedule,
|
|
78
|
+
supervisor: config.supervisor,
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function buildPrompt(ctx, agentInstructions) {
|
|
83
|
+
return [
|
|
84
|
+
"## Instructions",
|
|
85
|
+
agentInstructions,
|
|
86
|
+
"",
|
|
87
|
+
"## Mission",
|
|
88
|
+
ctx.mission || "(no mission defined)",
|
|
89
|
+
"",
|
|
90
|
+
"## Repository State",
|
|
91
|
+
`### Open Issues (${ctx.issuesSummary.length})`,
|
|
92
|
+
ctx.issuesSummary.join("\n") || "none",
|
|
93
|
+
"",
|
|
94
|
+
`### Open PRs (${ctx.prsSummary.length})`,
|
|
95
|
+
ctx.prsSummary.join("\n") || "none",
|
|
96
|
+
"",
|
|
97
|
+
`### Features (${ctx.featureNames.length}/${ctx.featuresLimit})`,
|
|
98
|
+
ctx.featureNames.join(", ") || "none",
|
|
99
|
+
"",
|
|
100
|
+
`### Library Docs (${ctx.libraryNames.length}/${ctx.libraryLimit})`,
|
|
101
|
+
ctx.libraryNames.join(", ") || "none",
|
|
102
|
+
"",
|
|
103
|
+
`### Recent Workflow Runs`,
|
|
104
|
+
ctx.workflowsSummary.join("\n") || "none",
|
|
105
|
+
"",
|
|
106
|
+
`### Recent Activity`,
|
|
107
|
+
ctx.recentActivity || "none",
|
|
108
|
+
"",
|
|
109
|
+
`### Schedule: ${ctx.schedule}, Supervisor: ${ctx.supervisor}`,
|
|
110
|
+
"",
|
|
111
|
+
"## Available Actions",
|
|
112
|
+
"Pick one or more actions. Output them in the format below.",
|
|
113
|
+
"",
|
|
114
|
+
"### Workflow Dispatches",
|
|
115
|
+
"- `dispatch:agent-flow-transform` — Pick up next issue, generate code, open PR",
|
|
116
|
+
"- `dispatch:agent-flow-maintain` — Refresh feature definitions and library docs",
|
|
117
|
+
"- `dispatch:agent-flow-review` — Close resolved issues, enhance issue criteria",
|
|
118
|
+
"- `dispatch:agent-flow-fix-code | pr-number: <N>` — Fix a failing PR",
|
|
119
|
+
"- `dispatch:agent-discussions-bot` — Proactively post in discussions",
|
|
120
|
+
"",
|
|
121
|
+
"### GitHub API Actions",
|
|
122
|
+
"- `github:create-issue | title: <text> | labels: <comma-separated>` — Create a new issue",
|
|
123
|
+
"- `github:label-issue | issue-number: <N> | labels: <comma-separated>` — Add labels to an issue",
|
|
124
|
+
"- `github:close-issue | issue-number: <N>` — Close an issue",
|
|
125
|
+
"",
|
|
126
|
+
"### Communication",
|
|
127
|
+
"- `respond:discussions | message: <text> | discussion-url: <url>` — Reply via discussions bot",
|
|
128
|
+
"- `nop` — No action needed this cycle",
|
|
129
|
+
"",
|
|
130
|
+
"## Output Format",
|
|
131
|
+
"Respond with EXACTLY this structure:",
|
|
132
|
+
"```",
|
|
133
|
+
"[ACTIONS]",
|
|
134
|
+
"action-name | param: value | param: value",
|
|
135
|
+
"[/ACTIONS]",
|
|
136
|
+
"[REASONING]",
|
|
137
|
+
"Why you chose these actions...",
|
|
138
|
+
"[/REASONING]",
|
|
139
|
+
"```",
|
|
140
|
+
].join("\n");
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
function parseActions(content) {
|
|
144
|
+
const actionsMatch = content.match(/\[ACTIONS\]([\s\S]*?)\[\/ACTIONS\]/);
|
|
145
|
+
if (!actionsMatch) return [];
|
|
146
|
+
|
|
147
|
+
return actionsMatch[1]
|
|
148
|
+
.split("\n")
|
|
149
|
+
.map((line) => line.trim())
|
|
150
|
+
.filter((line) => line && !line.startsWith("#"))
|
|
151
|
+
.map((line) => {
|
|
152
|
+
const parts = line.split("|").map((p) => p.trim());
|
|
153
|
+
const action = parts[0];
|
|
154
|
+
const params = {};
|
|
155
|
+
for (const part of parts.slice(1)) {
|
|
156
|
+
const colonIdx = part.indexOf(":");
|
|
157
|
+
if (colonIdx > 0) {
|
|
158
|
+
params[part.substring(0, colonIdx).trim()] = part.substring(colonIdx + 1).trim();
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
return { action, params };
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
function parseReasoning(content) {
|
|
166
|
+
const match = content.match(/\[REASONING\]([\s\S]*?)\[\/REASONING\]/);
|
|
167
|
+
return match ? match[1].trim() : "";
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
async function executeDispatch(octokit, repo, actionName, params) {
|
|
171
|
+
const workflowFile = actionName.replace("dispatch:", "") + ".yml";
|
|
172
|
+
const inputs = {};
|
|
173
|
+
if (params["pr-number"]) inputs["pr-number"] = params["pr-number"];
|
|
174
|
+
core.info(`Dispatching workflow: ${workflowFile}`);
|
|
175
|
+
await octokit.rest.actions.createWorkflowDispatch({ ...repo, workflow_id: workflowFile, ref: "main", inputs });
|
|
176
|
+
return `dispatched:${workflowFile}`;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
async function executeCreateIssue(octokit, repo, params) {
|
|
180
|
+
const title = params.title || "Untitled issue";
|
|
181
|
+
const labels = params.labels ? params.labels.split(",").map((l) => l.trim()) : ["automated"];
|
|
182
|
+
core.info(`Creating issue: ${title}`);
|
|
183
|
+
const { data: issue } = await octokit.rest.issues.create({ ...repo, title, labels });
|
|
184
|
+
return `created-issue:#${issue.number}`;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
async function executeLabelIssue(octokit, repo, params) {
|
|
188
|
+
const issueNumber = Number(params["issue-number"]);
|
|
189
|
+
const labels = params.labels ? params.labels.split(",").map((l) => l.trim()) : [];
|
|
190
|
+
if (issueNumber && labels.length > 0) {
|
|
191
|
+
core.info(`Labelling issue #${issueNumber}: ${labels.join(", ")}`);
|
|
192
|
+
await octokit.rest.issues.addLabels({ ...repo, issue_number: issueNumber, labels });
|
|
193
|
+
return `labelled-issue:#${issueNumber}`;
|
|
194
|
+
}
|
|
195
|
+
return "skipped:label-issue-missing-params";
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
async function executeCloseIssue(octokit, repo, params) {
|
|
199
|
+
const issueNumber = Number(params["issue-number"]);
|
|
200
|
+
if (issueNumber) {
|
|
201
|
+
core.info(`Closing issue #${issueNumber}`);
|
|
202
|
+
await octokit.rest.issues.update({ ...repo, issue_number: issueNumber, state: "closed" });
|
|
203
|
+
return `closed-issue:#${issueNumber}`;
|
|
204
|
+
}
|
|
205
|
+
return "skipped:close-issue-missing-number";
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
async function executeRespondDiscussions(octokit, repo, params) {
|
|
209
|
+
const message = params.message || "";
|
|
210
|
+
const url = params["discussion-url"] || "";
|
|
211
|
+
if (message) {
|
|
212
|
+
core.info(`Dispatching discussions bot with response: ${message.substring(0, 100)}`);
|
|
213
|
+
await octokit.rest.actions.createWorkflowDispatch({
|
|
214
|
+
...repo,
|
|
215
|
+
workflow_id: "agent-discussions-bot.yml",
|
|
216
|
+
ref: "main",
|
|
217
|
+
inputs: {},
|
|
218
|
+
});
|
|
219
|
+
return `respond-discussions:${url || "no-url"}`;
|
|
220
|
+
}
|
|
221
|
+
return "skipped:respond-no-message";
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
const ACTION_HANDLERS = {
|
|
225
|
+
"github:create-issue": executeCreateIssue,
|
|
226
|
+
"github:label-issue": executeLabelIssue,
|
|
227
|
+
"github:close-issue": executeCloseIssue,
|
|
228
|
+
"respond:discussions": executeRespondDiscussions,
|
|
229
|
+
};
|
|
230
|
+
|
|
231
|
+
async function executeAction(octokit, repo, action, params) {
|
|
232
|
+
if (action.startsWith("dispatch:")) return executeDispatch(octokit, repo, action, params);
|
|
233
|
+
if (action === "nop") return "nop";
|
|
234
|
+
const handler = ACTION_HANDLERS[action];
|
|
235
|
+
if (handler) return handler(octokit, repo, params);
|
|
236
|
+
core.warning(`Unknown action: ${action}`);
|
|
237
|
+
return `unknown:${action}`;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
/**
|
|
241
|
+
* Supervisor task: gather context, ask LLM to choose actions, execute them.
|
|
242
|
+
*
|
|
243
|
+
* @param {Object} context - Task context from index.js
|
|
244
|
+
* @returns {Promise<Object>} Result with outcome, tokensUsed, model
|
|
245
|
+
*/
|
|
246
|
+
export async function supervise(context) {
|
|
247
|
+
const { octokit, repo, config, instructions, model } = context;
|
|
248
|
+
|
|
249
|
+
const ctx = await gatherContext(octokit, repo, config);
|
|
250
|
+
const agentInstructions = instructions || "You are the supervisor. Decide what actions to take.";
|
|
251
|
+
const prompt = buildPrompt(ctx, agentInstructions);
|
|
252
|
+
|
|
253
|
+
const { content, tokensUsed } = await runCopilotTask({
|
|
254
|
+
model,
|
|
255
|
+
systemMessage:
|
|
256
|
+
"You are the supervisor of an autonomous coding repository. Your job is to advance the mission by choosing which workflows to dispatch and which GitHub actions to take. Pick multiple actions when appropriate. Be strategic — consider what's already in progress, what's blocked, and what will make the most impact.",
|
|
257
|
+
prompt,
|
|
258
|
+
writablePaths: [],
|
|
259
|
+
});
|
|
260
|
+
|
|
261
|
+
const actions = parseActions(content);
|
|
262
|
+
const reasoning = parseReasoning(content);
|
|
263
|
+
|
|
264
|
+
core.info(`Supervisor reasoning: ${reasoning.substring(0, 200)}`);
|
|
265
|
+
core.info(`Supervisor chose ${actions.length} action(s)`);
|
|
266
|
+
|
|
267
|
+
const results = [];
|
|
268
|
+
for (const { action, params } of actions) {
|
|
269
|
+
try {
|
|
270
|
+
const result = await executeAction(octokit, repo, action, params);
|
|
271
|
+
results.push(result);
|
|
272
|
+
core.info(`Action result: ${result}`);
|
|
273
|
+
} catch (err) {
|
|
274
|
+
core.warning(`Action ${action} failed: ${err.message}`);
|
|
275
|
+
results.push(`error:${action}:${err.message}`);
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
return {
|
|
280
|
+
outcome: actions.length === 0 ? "nop" : `supervised:${actions.length}-actions`,
|
|
281
|
+
tokensUsed,
|
|
282
|
+
model,
|
|
283
|
+
details: `Actions: ${results.join(", ")}\nReasoning: ${reasoning.substring(0, 300)}`,
|
|
284
|
+
};
|
|
285
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
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
|
+
|
|
3
|
+
## Decision Framework
|
|
4
|
+
|
|
5
|
+
1. **Check what's already in progress** — don't duplicate work. If a transform is running, don't dispatch another.
|
|
6
|
+
2. **Prioritise unblocking** — fix failing PRs before starting new features. Close resolved issues to free capacity.
|
|
7
|
+
3. **Balance the pipeline** — maintain a healthy mix of feature work, maintenance, and review.
|
|
8
|
+
4. **Respect limits** — don't create issues beyond the WIP limit. Don't dispatch workflows that will fail due to missing prerequisites.
|
|
9
|
+
5. **Be strategic about timing** — if the schedule is hourly, you can afford to spread work across cycles. If daily, batch more aggressively.
|
|
10
|
+
|
|
11
|
+
## When to use each action
|
|
12
|
+
|
|
13
|
+
- **dispatch:agent-flow-transform** — When there are open issues ready to work on and no transform is currently running.
|
|
14
|
+
- **dispatch:agent-flow-maintain** — When features or library docs are below their limits, or haven't been refreshed recently.
|
|
15
|
+
- **dispatch:agent-flow-review** — When there are open automated issues that might be resolved, or issues that need better acceptance criteria.
|
|
16
|
+
- **dispatch:agent-flow-fix-code** — When a specific PR has failing checks. Always include the pr-number.
|
|
17
|
+
- **dispatch:agent-discussions-bot** — When you want to proactively engage in discussions or respond to a user request.
|
|
18
|
+
- **github:create-issue** — When you identify a gap between the mission and current features. Always include a descriptive title and relevant labels.
|
|
19
|
+
- **github:label-issue** — When an issue needs better categorisation for prioritisation.
|
|
20
|
+
- **github:close-issue** — When an issue is clearly resolved or no longer relevant.
|
|
21
|
+
- **respond:discussions** — When replying to a user request that came through the discussions bot. Include the discussion URL and a clear message.
|
|
22
|
+
- **nop** — When everything is running smoothly and no intervention is needed.
|
|
23
|
+
|
|
24
|
+
## Guidelines
|
|
25
|
+
|
|
26
|
+
- Pick multiple actions when appropriate — concurrent work is encouraged.
|
|
27
|
+
- Always explain your reasoning — this helps future cycles understand the trajectory.
|
|
28
|
+
- When a user has made a request via discussions, prioritise responding to it.
|
|
29
|
+
- Don't dispatch the same workflow twice in one cycle.
|
|
30
|
+
- If recent workflow runs show failures, investigate before dispatching more work.
|