@xn-intenton-z2a/agentic-lib 7.1.73 → 7.1.75
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-schedule.yml +4 -4
- package/.github/workflows/agentic-lib-workflow.yml +47 -37
- package/package.json +1 -1
- package/src/actions/agentic-step/action.yml +2 -0
- package/src/actions/agentic-step/copilot.js +32 -2
- package/src/actions/agentic-step/index.js +1 -0
- package/src/actions/agentic-step/tasks/discussions.js +12 -1
- package/src/actions/agentic-step/tasks/enhance-issue.js +2 -0
- package/src/actions/agentic-step/tasks/fix-code.js +5 -5
- package/src/actions/agentic-step/tasks/maintain-features.js +13 -5
- package/src/actions/agentic-step/tasks/maintain-library.js +4 -3
- package/src/actions/agentic-step/tasks/resolve-issue.js +3 -3
- package/src/actions/agentic-step/tasks/review-issue.js +3 -0
- package/src/actions/agentic-step/tasks/supervise.js +9 -5
- package/src/actions/agentic-step/tasks/transform.js +5 -4
- package/src/seeds/zero-package.json +1 -1
|
@@ -81,10 +81,10 @@ jobs:
|
|
|
81
81
|
|
|
82
82
|
const SCHEDULE_MAP = {
|
|
83
83
|
off: null,
|
|
84
|
-
weekly: '
|
|
85
|
-
daily: '
|
|
86
|
-
hourly: '
|
|
87
|
-
continuous: '
|
|
84
|
+
weekly: '15 6 * * 1',
|
|
85
|
+
daily: '15 6 * * *',
|
|
86
|
+
hourly: '15 * * * *',
|
|
87
|
+
continuous: '5,15,25,35,45,55 * * * *',
|
|
88
88
|
};
|
|
89
89
|
|
|
90
90
|
// Update agentic-lib-workflow.yml schedule
|
|
@@ -293,44 +293,10 @@ jobs:
|
|
|
293
293
|
outputs:
|
|
294
294
|
telemetry: ${{ steps.gather.outputs.telemetry }}
|
|
295
295
|
|
|
296
|
-
# ─── Supervisor: LLM decides what to do ─────────────────────────────
|
|
297
|
-
supervisor:
|
|
298
|
-
needs: [params, pr-cleanup, telemetry]
|
|
299
|
-
if: |
|
|
300
|
-
always() &&
|
|
301
|
-
(needs.params.outputs.mode == 'full' || needs.params.outputs.mode == 'dev-only') &&
|
|
302
|
-
needs.params.result == 'success'
|
|
303
|
-
runs-on: ubuntu-latest
|
|
304
|
-
steps:
|
|
305
|
-
- uses: actions/checkout@v6
|
|
306
|
-
|
|
307
|
-
- uses: actions/setup-node@v6
|
|
308
|
-
with:
|
|
309
|
-
node-version: "24"
|
|
310
|
-
|
|
311
|
-
- name: Self-init (agentic-lib dev only)
|
|
312
|
-
if: hashFiles('scripts/self-init.sh') != '' && hashFiles('.github/agentic-lib/actions/agentic-step/package.json') == ''
|
|
313
|
-
run: bash scripts/self-init.sh
|
|
314
|
-
|
|
315
|
-
- name: Install agentic-step dependencies
|
|
316
|
-
working-directory: .github/agentic-lib/actions/agentic-step
|
|
317
|
-
run: npm ci
|
|
318
|
-
|
|
319
|
-
- name: Run supervisor
|
|
320
|
-
if: github.repository != 'xn-intenton-z2a/agentic-lib'
|
|
321
|
-
uses: ./.github/agentic-lib/actions/agentic-step
|
|
322
|
-
env:
|
|
323
|
-
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
324
|
-
COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }}
|
|
325
|
-
with:
|
|
326
|
-
task: "supervise"
|
|
327
|
-
config: ${{ needs.params.outputs.config-path }}
|
|
328
|
-
instructions: ".github/agentic-lib/agents/agent-supervisor.md"
|
|
329
|
-
model: ${{ needs.params.outputs.model }}
|
|
330
|
-
|
|
331
296
|
# ─── Maintain: features + library (push to main) ───────────────────
|
|
297
|
+
# Runs early (parallel with pr-cleanup/telemetry) so supervisor sees features.
|
|
332
298
|
maintain:
|
|
333
|
-
needs: [params
|
|
299
|
+
needs: [params]
|
|
334
300
|
if: |
|
|
335
301
|
always() &&
|
|
336
302
|
(needs.params.outputs.mode == 'full' || needs.params.outputs.mode == 'maintain-only') &&
|
|
@@ -375,6 +341,7 @@ jobs:
|
|
|
375
341
|
echo "libraryWritablePaths=${LIBRARY};${SOURCES}" >> $GITHUB_OUTPUT
|
|
376
342
|
|
|
377
343
|
- name: Maintain features
|
|
344
|
+
id: maintain-features
|
|
378
345
|
if: steps.mission-check.outputs.mission-complete != 'true'
|
|
379
346
|
uses: ./.github/agentic-lib/actions/agentic-step
|
|
380
347
|
env:
|
|
@@ -388,6 +355,7 @@ jobs:
|
|
|
388
355
|
model: ${{ needs.params.outputs.model }}
|
|
389
356
|
|
|
390
357
|
- name: Maintain library
|
|
358
|
+
id: maintain-library
|
|
391
359
|
if: steps.mission-check.outputs.mission-complete != 'true'
|
|
392
360
|
uses: ./.github/agentic-lib/actions/agentic-step
|
|
393
361
|
env:
|
|
@@ -400,6 +368,13 @@ jobs:
|
|
|
400
368
|
writable-paths: ${{ steps.config.outputs.libraryWritablePaths }}
|
|
401
369
|
model: ${{ needs.params.outputs.model }}
|
|
402
370
|
|
|
371
|
+
- name: Log narrative thread
|
|
372
|
+
if: steps.mission-check.outputs.mission-complete != 'true'
|
|
373
|
+
run: |
|
|
374
|
+
echo "## Maintain Narrative Thread" >> $GITHUB_STEP_SUMMARY
|
|
375
|
+
echo "- **Features:** ${{ steps.maintain-features.outputs.narrative }}" >> $GITHUB_STEP_SUMMARY
|
|
376
|
+
echo "- **Library:** ${{ steps.maintain-library.outputs.narrative }}" >> $GITHUB_STEP_SUMMARY
|
|
377
|
+
|
|
403
378
|
- name: Commit and push changes
|
|
404
379
|
if: github.repository != 'xn-intenton-z2a/agentic-lib' && needs.params.outputs.dry-run != 'true'
|
|
405
380
|
uses: ./.github/agentic-lib/actions/commit-if-changed
|
|
@@ -407,6 +382,41 @@ jobs:
|
|
|
407
382
|
commit-message: "agentic-step: maintain features and library"
|
|
408
383
|
push-ref: ${{ github.ref_name }}
|
|
409
384
|
|
|
385
|
+
# ─── Supervisor: LLM decides what to do (after maintain has features) ──
|
|
386
|
+
supervisor:
|
|
387
|
+
needs: [params, pr-cleanup, telemetry, maintain]
|
|
388
|
+
if: |
|
|
389
|
+
always() &&
|
|
390
|
+
(needs.params.outputs.mode == 'full' || needs.params.outputs.mode == 'dev-only') &&
|
|
391
|
+
needs.params.result == 'success'
|
|
392
|
+
runs-on: ubuntu-latest
|
|
393
|
+
steps:
|
|
394
|
+
- uses: actions/checkout@v6
|
|
395
|
+
|
|
396
|
+
- uses: actions/setup-node@v6
|
|
397
|
+
with:
|
|
398
|
+
node-version: "24"
|
|
399
|
+
|
|
400
|
+
- name: Self-init (agentic-lib dev only)
|
|
401
|
+
if: hashFiles('scripts/self-init.sh') != '' && hashFiles('.github/agentic-lib/actions/agentic-step/package.json') == ''
|
|
402
|
+
run: bash scripts/self-init.sh
|
|
403
|
+
|
|
404
|
+
- name: Install agentic-step dependencies
|
|
405
|
+
working-directory: .github/agentic-lib/actions/agentic-step
|
|
406
|
+
run: npm ci
|
|
407
|
+
|
|
408
|
+
- name: Run supervisor
|
|
409
|
+
if: github.repository != 'xn-intenton-z2a/agentic-lib'
|
|
410
|
+
uses: ./.github/agentic-lib/actions/agentic-step
|
|
411
|
+
env:
|
|
412
|
+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
413
|
+
COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }}
|
|
414
|
+
with:
|
|
415
|
+
task: "supervise"
|
|
416
|
+
config: ${{ needs.params.outputs.config-path }}
|
|
417
|
+
instructions: ".github/agentic-lib/agents/agent-supervisor.md"
|
|
418
|
+
model: ${{ needs.params.outputs.model }}
|
|
419
|
+
|
|
410
420
|
# ─── Fix stuck PRs with failing checks ─────────────────────────────
|
|
411
421
|
fix-stuck:
|
|
412
422
|
needs: [params, supervisor]
|
|
@@ -570,7 +580,7 @@ jobs:
|
|
|
570
580
|
|
|
571
581
|
# ─── Review: close resolved issues, enhance with criteria ──────────
|
|
572
582
|
review-features:
|
|
573
|
-
needs: [params,
|
|
583
|
+
needs: [params, supervisor]
|
|
574
584
|
if: |
|
|
575
585
|
always() &&
|
|
576
586
|
(needs.params.outputs.mode == 'full' || needs.params.outputs.mode == 'review-only') &&
|
package/package.json
CHANGED
|
@@ -56,6 +56,8 @@ outputs:
|
|
|
56
56
|
description: "Action chosen by the task (e.g. request-supervisor, create-feature, nop)"
|
|
57
57
|
action-arg:
|
|
58
58
|
description: "Argument for the chosen action (free text)"
|
|
59
|
+
narrative:
|
|
60
|
+
description: "One-sentence English narrative of what the task did and why"
|
|
59
61
|
|
|
60
62
|
runs:
|
|
61
63
|
using: "node24"
|
|
@@ -83,19 +83,27 @@ export function generateOutline(raw, filePath) {
|
|
|
83
83
|
}
|
|
84
84
|
|
|
85
85
|
/**
|
|
86
|
-
* Filter issues by recency and label quality.
|
|
86
|
+
* Filter issues by recency, init epoch, and label quality.
|
|
87
87
|
*
|
|
88
88
|
* @param {Array} issues - GitHub issue objects
|
|
89
89
|
* @param {Object} [options]
|
|
90
90
|
* @param {number} [options.staleDays=30] - Issues older than this with no activity are excluded
|
|
91
91
|
* @param {boolean} [options.excludeBotOnly=true] - Exclude issues with only bot labels
|
|
92
|
+
* @param {string} [options.initTimestamp] - ISO timestamp; exclude issues created before this epoch
|
|
92
93
|
* @returns {Array} Filtered issues
|
|
93
94
|
*/
|
|
94
95
|
export function filterIssues(issues, options = {}) {
|
|
95
|
-
const { staleDays = 30, excludeBotOnly = true } = options;
|
|
96
|
+
const { staleDays = 30, excludeBotOnly = true, initTimestamp } = options;
|
|
96
97
|
const cutoff = Date.now() - staleDays * 86400000;
|
|
98
|
+
const initEpoch = initTimestamp ? new Date(initTimestamp).getTime() : 0;
|
|
97
99
|
|
|
98
100
|
return issues.filter((issue) => {
|
|
101
|
+
// Exclude issues created before the most recent init
|
|
102
|
+
if (initEpoch > 0) {
|
|
103
|
+
const created = new Date(issue.created_at).getTime();
|
|
104
|
+
if (created < initEpoch) return false;
|
|
105
|
+
}
|
|
106
|
+
|
|
99
107
|
const lastActivity = new Date(issue.updated_at || issue.created_at).getTime();
|
|
100
108
|
if (lastActivity < cutoff) return false;
|
|
101
109
|
|
|
@@ -398,6 +406,28 @@ async function _runCopilotTaskOnce({
|
|
|
398
406
|
}
|
|
399
407
|
}
|
|
400
408
|
|
|
409
|
+
/**
|
|
410
|
+
* Extract a [NARRATIVE] line from an LLM response.
|
|
411
|
+
* Returns the text after the tag, or a fallback summary.
|
|
412
|
+
*
|
|
413
|
+
* @param {string} content - Raw LLM response content
|
|
414
|
+
* @param {string} [fallback] - Fallback if no [NARRATIVE] tag found
|
|
415
|
+
* @returns {string} The narrative sentence
|
|
416
|
+
*/
|
|
417
|
+
export function extractNarrative(content, fallback) {
|
|
418
|
+
if (!content) return fallback || "";
|
|
419
|
+
const match = content.match(/\[NARRATIVE\]\s*(.+)/);
|
|
420
|
+
if (match) return match[1].trim();
|
|
421
|
+
return fallback || "";
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
/**
|
|
425
|
+
* Narrative solicitation to append to system messages.
|
|
426
|
+
* Asks the LLM to end its response with a one-sentence summary.
|
|
427
|
+
*/
|
|
428
|
+
export const NARRATIVE_INSTRUCTION =
|
|
429
|
+
"\n\nAfter completing your task, end your response with a line starting with [NARRATIVE] followed by one plain English sentence describing what you did and why, for the activity log.";
|
|
430
|
+
|
|
401
431
|
/**
|
|
402
432
|
* Read a file, returning empty string on failure. For optional context files.
|
|
403
433
|
*
|
|
@@ -98,6 +98,7 @@ async function run() {
|
|
|
98
98
|
if (result.model) core.setOutput("model", result.model);
|
|
99
99
|
if (result.action) core.setOutput("action", result.action);
|
|
100
100
|
if (result.actionArg) core.setOutput("action-arg", result.actionArg);
|
|
101
|
+
if (result.narrative) core.setOutput("narrative", result.narrative);
|
|
101
102
|
|
|
102
103
|
const profileName = config.tuning?.profileName || "unknown";
|
|
103
104
|
|
|
@@ -187,8 +187,10 @@ export async function discussions(context) {
|
|
|
187
187
|
const { data: openIssues } = await octokit.rest.issues.listForRepo({
|
|
188
188
|
...repo, state: "open", per_page: 10, sort: "created", direction: "asc",
|
|
189
189
|
});
|
|
190
|
+
const initTimestampForIssues = config?.init?.timestamp || null;
|
|
191
|
+
const initEpochForIssues = initTimestampForIssues ? new Date(initTimestampForIssues).getTime() : 0;
|
|
190
192
|
repoContext.issuesSummary = openIssues
|
|
191
|
-
.filter((i) => !i.pull_request)
|
|
193
|
+
.filter((i) => !i.pull_request && (initEpochForIssues <= 0 || new Date(i.created_at).getTime() >= initEpochForIssues))
|
|
192
194
|
.map((i) => {
|
|
193
195
|
const labels = i.labels.map((l) => l.name).join(", ");
|
|
194
196
|
return `#${i.number}: ${i.title} [${labels || "no labels"}]`;
|
|
@@ -238,6 +240,14 @@ export async function discussions(context) {
|
|
|
238
240
|
}
|
|
239
241
|
|
|
240
242
|
const discussion = await fetchDiscussion(octokit, discussionUrl, t.discussionComments || 10);
|
|
243
|
+
|
|
244
|
+
// Filter discussion comments to only those after the most recent init
|
|
245
|
+
const initTs = config?.init?.timestamp || null;
|
|
246
|
+
if (initTs && discussion.comments.length > 0) {
|
|
247
|
+
const initDate = new Date(initTs);
|
|
248
|
+
discussion.comments = discussion.comments.filter((c) => new Date(c.createdAt) >= initDate);
|
|
249
|
+
}
|
|
250
|
+
|
|
241
251
|
const prompt = buildPrompt(discussionUrl, discussion, context, t, repoContext);
|
|
242
252
|
const { content, tokensUsed, inputTokens, outputTokens, cost } = await runCopilotTask({
|
|
243
253
|
model,
|
|
@@ -336,6 +346,7 @@ export async function discussions(context) {
|
|
|
336
346
|
cost,
|
|
337
347
|
model,
|
|
338
348
|
details: `Action: ${action}${argSuffix}\nReply: ${replyBody.substring(0, 200)}`,
|
|
349
|
+
narrative: `Responded to discussion with action ${action}${argSuffix}.`,
|
|
339
350
|
action,
|
|
340
351
|
actionArg,
|
|
341
352
|
replyBody,
|
|
@@ -102,6 +102,7 @@ async function enhanceSingleIssue({ octokit, repo, config, issueNumber, instruct
|
|
|
102
102
|
cost,
|
|
103
103
|
model,
|
|
104
104
|
details: `Enhanced issue #${issueNumber} with acceptance criteria`,
|
|
105
|
+
narrative: `Enhanced issue #${issueNumber} with testable acceptance criteria.`,
|
|
105
106
|
};
|
|
106
107
|
}
|
|
107
108
|
|
|
@@ -173,5 +174,6 @@ export async function enhanceIssue(context) {
|
|
|
173
174
|
.map((r) => r.details)
|
|
174
175
|
.join("; ")
|
|
175
176
|
.substring(0, 500)}`,
|
|
177
|
+
narrative: `Enhanced ${enhanced} issue(s) with testable acceptance criteria.`,
|
|
176
178
|
};
|
|
177
179
|
}
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
import * as core from "@actions/core";
|
|
10
10
|
import { readFileSync } from "fs";
|
|
11
11
|
import { execSync } from "child_process";
|
|
12
|
-
import { runCopilotTask, formatPathsSection } from "../copilot.js";
|
|
12
|
+
import { runCopilotTask, formatPathsSection, extractNarrative, NARRATIVE_INSTRUCTION } from "../copilot.js";
|
|
13
13
|
|
|
14
14
|
/**
|
|
15
15
|
* Extract run_id from a check run's details_url.
|
|
@@ -100,7 +100,7 @@ async function resolveConflicts({ config, pr, prNumber, instructions, model, wri
|
|
|
100
100
|
const t = config.tuning || {};
|
|
101
101
|
const { tokensUsed, inputTokens, outputTokens, cost, content: resultContent } = await runCopilotTask({
|
|
102
102
|
model,
|
|
103
|
-
systemMessage: `You are resolving git merge conflicts on PR #${prNumber}. Write resolved versions of each conflicted file, removing all conflict markers. Preserve the PR's feature intent while incorporating main's updates
|
|
103
|
+
systemMessage: `You are resolving git merge conflicts on PR #${prNumber}. Write resolved versions of each conflicted file, removing all conflict markers. Preserve the PR's feature intent while incorporating main's updates.` + NARRATIVE_INSTRUCTION,
|
|
104
104
|
prompt,
|
|
105
105
|
writablePaths,
|
|
106
106
|
tuning: t,
|
|
@@ -116,7 +116,7 @@ async function resolveConflicts({ config, pr, prNumber, instructions, model, wri
|
|
|
116
116
|
cost,
|
|
117
117
|
model,
|
|
118
118
|
details: `Resolved ${conflicts.length} conflicted file(s) on PR #${prNumber}`,
|
|
119
|
-
narrative: (resultContent
|
|
119
|
+
narrative: extractNarrative(resultContent, `Resolved ${conflicts.length} merge conflict(s) on PR #${prNumber}.`),
|
|
120
120
|
};
|
|
121
121
|
}
|
|
122
122
|
|
|
@@ -189,7 +189,7 @@ export async function fixCode(context) {
|
|
|
189
189
|
const t = config.tuning || {};
|
|
190
190
|
const { tokensUsed, inputTokens, outputTokens, cost, content: resultContent } = await runCopilotTask({
|
|
191
191
|
model,
|
|
192
|
-
systemMessage: `You are an autonomous coding agent fixing failing tests on PR #${prNumber}. Make minimal, targeted changes to fix the test failures
|
|
192
|
+
systemMessage: `You are an autonomous coding agent fixing failing tests on PR #${prNumber}. Make minimal, targeted changes to fix the test failures.` + NARRATIVE_INSTRUCTION,
|
|
193
193
|
prompt,
|
|
194
194
|
writablePaths,
|
|
195
195
|
tuning: t,
|
|
@@ -205,6 +205,6 @@ export async function fixCode(context) {
|
|
|
205
205
|
cost,
|
|
206
206
|
model,
|
|
207
207
|
details: `Applied fix for ${failedChecks.length} failing check(s) on PR #${prNumber}`,
|
|
208
|
-
narrative: (resultContent
|
|
208
|
+
narrative: extractNarrative(resultContent, `Fixed ${failedChecks.length} failing check(s) on PR #${prNumber}.`),
|
|
209
209
|
};
|
|
210
210
|
}
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
// prunes completed/irrelevant features, and ensures quality.
|
|
7
7
|
|
|
8
8
|
import { existsSync } from "fs";
|
|
9
|
-
import { runCopilotTask, readOptionalFile, scanDirectory, formatPathsSection, extractFeatureSummary } from "../copilot.js";
|
|
9
|
+
import { runCopilotTask, readOptionalFile, scanDirectory, formatPathsSection, extractFeatureSummary, extractNarrative, NARRATIVE_INSTRUCTION } from "../copilot.js";
|
|
10
10
|
import { checkWipLimit } from "../safety.js";
|
|
11
11
|
|
|
12
12
|
/**
|
|
@@ -45,13 +45,20 @@ export async function maintainFeatures(context) {
|
|
|
45
45
|
contentLimit: t.documentSummary || 1000,
|
|
46
46
|
});
|
|
47
47
|
|
|
48
|
-
|
|
48
|
+
// Filter closed issues to only those created after the most recent init
|
|
49
|
+
const initTimestamp = config.init?.timestamp || null;
|
|
50
|
+
const initEpoch = initTimestamp ? new Date(initTimestamp).getTime() : 0;
|
|
51
|
+
|
|
52
|
+
const { data: closedIssuesRaw } = await octokit.rest.issues.listForRepo({
|
|
49
53
|
...repo,
|
|
50
54
|
state: "closed",
|
|
51
55
|
per_page: t.issuesScan || 20,
|
|
52
56
|
sort: "updated",
|
|
53
57
|
direction: "desc",
|
|
54
58
|
});
|
|
59
|
+
const closedIssues = initEpoch > 0
|
|
60
|
+
? closedIssuesRaw.filter((i) => new Date(i.created_at).getTime() >= initEpoch)
|
|
61
|
+
: closedIssuesRaw;
|
|
55
62
|
|
|
56
63
|
const agentInstructions = instructions || "Maintain the feature set by creating, updating, or pruning features.";
|
|
57
64
|
|
|
@@ -68,7 +75,7 @@ export async function maintainFeatures(context) {
|
|
|
68
75
|
libraryDocs.length > 0 ? `## Library Documents (${libraryDocs.length})` : "",
|
|
69
76
|
...libraryDocs.map((d) => `### ${d.name}\n${d.content}`),
|
|
70
77
|
"",
|
|
71
|
-
`## Recently Closed Issues (${closedIssues.length})`,
|
|
78
|
+
`## Recently Closed Issues (${closedIssues.length}${initTimestamp ? `, since init ${initTimestamp}` : ""})`,
|
|
72
79
|
...closedIssues.slice(0, Math.floor((t.issuesScan || 20) / 2)).map((i) => `- #${i.number}: ${i.title}`),
|
|
73
80
|
"",
|
|
74
81
|
"## Your Task",
|
|
@@ -83,10 +90,10 @@ export async function maintainFeatures(context) {
|
|
|
83
90
|
"- Feature files must be markdown with a descriptive filename (e.g. HTTP_SERVER.md)",
|
|
84
91
|
].join("\n");
|
|
85
92
|
|
|
86
|
-
const { tokensUsed, inputTokens, outputTokens, cost } = await runCopilotTask({
|
|
93
|
+
const { content: resultContent, tokensUsed, inputTokens, outputTokens, cost } = await runCopilotTask({
|
|
87
94
|
model,
|
|
88
95
|
systemMessage:
|
|
89
|
-
"You are a feature lifecycle manager. Create, update, and prune feature specification files to keep the project focused on its mission.",
|
|
96
|
+
"You are a feature lifecycle manager. Create, update, and prune feature specification files to keep the project focused on its mission." + NARRATIVE_INSTRUCTION,
|
|
90
97
|
prompt,
|
|
91
98
|
writablePaths,
|
|
92
99
|
tuning: t,
|
|
@@ -100,5 +107,6 @@ export async function maintainFeatures(context) {
|
|
|
100
107
|
cost,
|
|
101
108
|
model,
|
|
102
109
|
details: `Maintained features (${features.length} existing, limit ${featureLimit})`,
|
|
110
|
+
narrative: extractNarrative(resultContent, `Maintained ${features.length} features (limit ${featureLimit}).`),
|
|
103
111
|
};
|
|
104
112
|
}
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
|
|
8
8
|
import * as core from "@actions/core";
|
|
9
9
|
import { existsSync } from "fs";
|
|
10
|
-
import { runCopilotTask, readOptionalFile, scanDirectory, formatPathsSection } from "../copilot.js";
|
|
10
|
+
import { runCopilotTask, readOptionalFile, scanDirectory, formatPathsSection, extractNarrative, NARRATIVE_INSTRUCTION } from "../copilot.js";
|
|
11
11
|
|
|
12
12
|
/**
|
|
13
13
|
* Maintain the library of knowledge documents from source URLs.
|
|
@@ -81,10 +81,10 @@ export async function maintainLibrary(context) {
|
|
|
81
81
|
].join("\n");
|
|
82
82
|
}
|
|
83
83
|
|
|
84
|
-
const { tokensUsed, inputTokens, outputTokens, cost } = await runCopilotTask({
|
|
84
|
+
const { content: resultContent, tokensUsed, inputTokens, outputTokens, cost } = await runCopilotTask({
|
|
85
85
|
model,
|
|
86
86
|
systemMessage:
|
|
87
|
-
"You are a knowledge librarian. Maintain a library of technical documents extracted from web sources.",
|
|
87
|
+
"You are a knowledge librarian. Maintain a library of technical documents extracted from web sources." + NARRATIVE_INSTRUCTION,
|
|
88
88
|
prompt,
|
|
89
89
|
writablePaths,
|
|
90
90
|
tuning: t,
|
|
@@ -103,5 +103,6 @@ export async function maintainLibrary(context) {
|
|
|
103
103
|
cost,
|
|
104
104
|
model,
|
|
105
105
|
details: detailsMsg,
|
|
106
|
+
narrative: extractNarrative(resultContent, detailsMsg),
|
|
106
107
|
};
|
|
107
108
|
}
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
|
|
8
8
|
import * as core from "@actions/core";
|
|
9
9
|
import { checkAttemptLimit, checkWipLimit, isIssueResolved } from "../safety.js";
|
|
10
|
-
import { runCopilotTask, readOptionalFile, formatPathsSection } from "../copilot.js";
|
|
10
|
+
import { runCopilotTask, readOptionalFile, formatPathsSection, extractNarrative, NARRATIVE_INSTRUCTION } from "../copilot.js";
|
|
11
11
|
|
|
12
12
|
/**
|
|
13
13
|
* Resolve a GitHub issue by generating code and creating a PR.
|
|
@@ -81,7 +81,7 @@ export async function resolveIssue(context) {
|
|
|
81
81
|
const t = config.tuning || {};
|
|
82
82
|
const { content: resultContent, tokensUsed, inputTokens, outputTokens, cost } = await runCopilotTask({
|
|
83
83
|
model,
|
|
84
|
-
systemMessage: `You are an autonomous coding agent resolving GitHub issue #${issueNumber}. Write clean, tested code. Only modify files listed under "Writable" paths. Read-only paths are for context only
|
|
84
|
+
systemMessage: `You are an autonomous coding agent resolving GitHub issue #${issueNumber}. Write clean, tested code. Only modify files listed under "Writable" paths. Read-only paths are for context only.` + NARRATIVE_INSTRUCTION,
|
|
85
85
|
prompt,
|
|
86
86
|
writablePaths,
|
|
87
87
|
tuning: t,
|
|
@@ -99,6 +99,6 @@ export async function resolveIssue(context) {
|
|
|
99
99
|
model,
|
|
100
100
|
commitUrl: null,
|
|
101
101
|
details: `Generated code for issue #${issueNumber}: ${resultContent.substring(0, 200)}`,
|
|
102
|
-
narrative: (resultContent
|
|
102
|
+
narrative: extractNarrative(resultContent, `Generated code for issue #${issueNumber}.`),
|
|
103
103
|
};
|
|
104
104
|
}
|
|
@@ -161,6 +161,7 @@ async function reviewSingleIssue({ octokit, repo, config, targetIssueNumber, ins
|
|
|
161
161
|
cost,
|
|
162
162
|
model,
|
|
163
163
|
details: `Closed issue #${targetIssueNumber}: ${verdict.substring(0, 200)}`,
|
|
164
|
+
narrative: `Reviewed issue #${targetIssueNumber} and closed it as resolved.`,
|
|
164
165
|
};
|
|
165
166
|
}
|
|
166
167
|
|
|
@@ -173,6 +174,7 @@ async function reviewSingleIssue({ octokit, repo, config, targetIssueNumber, ins
|
|
|
173
174
|
cost,
|
|
174
175
|
model,
|
|
175
176
|
details: `Issue #${targetIssueNumber} remains open: ${verdict.substring(0, 200)}`,
|
|
177
|
+
narrative: `Reviewed issue #${targetIssueNumber} — still open, not yet resolved.`,
|
|
176
178
|
};
|
|
177
179
|
}
|
|
178
180
|
|
|
@@ -271,5 +273,6 @@ export async function reviewIssue(context) {
|
|
|
271
273
|
.map((r) => r.details)
|
|
272
274
|
.join("; ")
|
|
273
275
|
.substring(0, 500)}`,
|
|
276
|
+
narrative: `Reviewed ${reviewed} issue(s), closed ${closed} as resolved.`,
|
|
274
277
|
};
|
|
275
278
|
}
|
|
@@ -51,6 +51,9 @@ async function gatherContext(octokit, repo, config, t) {
|
|
|
51
51
|
: [];
|
|
52
52
|
const libraryLimit = config.paths.library?.limit || 32;
|
|
53
53
|
|
|
54
|
+
// Read init timestamp for epoch boundary (used by issue filtering below)
|
|
55
|
+
const initTimestamp = config.init?.timestamp || null;
|
|
56
|
+
|
|
54
57
|
const { data: openIssues } = await octokit.rest.issues.listForRepo({
|
|
55
58
|
...repo,
|
|
56
59
|
state: "open",
|
|
@@ -59,7 +62,7 @@ async function gatherContext(octokit, repo, config, t) {
|
|
|
59
62
|
direction: "asc",
|
|
60
63
|
});
|
|
61
64
|
const issuesOnly = openIssues.filter((i) => !i.pull_request);
|
|
62
|
-
const filteredIssues = filterIssues(issuesOnly, { staleDays: t.staleDays || 30 });
|
|
65
|
+
const filteredIssues = filterIssues(issuesOnly, { staleDays: t.staleDays || 30, initTimestamp });
|
|
63
66
|
const oldestReadyIssue = filteredIssues.find((i) => i.labels.some((l) => l.name === "ready"));
|
|
64
67
|
const issuesSummary = filteredIssues.map((i) => {
|
|
65
68
|
const age = Math.floor((Date.now() - new Date(i.created_at).getTime()) / 86400000);
|
|
@@ -77,7 +80,11 @@ async function gatherContext(octokit, repo, config, t) {
|
|
|
77
80
|
sort: "updated",
|
|
78
81
|
direction: "desc",
|
|
79
82
|
});
|
|
80
|
-
|
|
83
|
+
const initEpoch = initTimestamp ? new Date(initTimestamp).getTime() : 0;
|
|
84
|
+
const closedIssuesFiltered = closedIssuesRaw.filter((i) =>
|
|
85
|
+
!i.pull_request && (initEpoch <= 0 || new Date(i.created_at).getTime() >= initEpoch)
|
|
86
|
+
);
|
|
87
|
+
for (const ci of closedIssuesFiltered) {
|
|
81
88
|
let closeReason = "closed";
|
|
82
89
|
try {
|
|
83
90
|
const { data: comments } = await octokit.rest.issues.listComments({
|
|
@@ -110,9 +117,6 @@ async function gatherContext(octokit, repo, config, t) {
|
|
|
110
117
|
return `#${pr.number}: ${pr.title} (${pr.head.ref}) [${labels || "no labels"}] (${age}d old)`;
|
|
111
118
|
});
|
|
112
119
|
|
|
113
|
-
// Read init timestamp for epoch boundary
|
|
114
|
-
const initTimestamp = config.init?.timestamp || null;
|
|
115
|
-
|
|
116
120
|
let workflowsSummary = [];
|
|
117
121
|
let actionsSinceInit = [];
|
|
118
122
|
try {
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
|
|
8
8
|
import * as core from "@actions/core";
|
|
9
9
|
import { writeFileSync, existsSync } from "fs";
|
|
10
|
-
import { runCopilotTask, readOptionalFile, scanDirectory, formatPathsSection, filterIssues, summariseIssue, extractFeatureSummary } from "../copilot.js";
|
|
10
|
+
import { runCopilotTask, readOptionalFile, scanDirectory, formatPathsSection, filterIssues, summariseIssue, extractFeatureSummary, extractNarrative, NARRATIVE_INSTRUCTION } from "../copilot.js";
|
|
11
11
|
|
|
12
12
|
/**
|
|
13
13
|
* Run the full transformation pipeline from mission to code.
|
|
@@ -162,7 +162,7 @@ export async function transform(context) {
|
|
|
162
162
|
} = await runCopilotTask({
|
|
163
163
|
model,
|
|
164
164
|
systemMessage:
|
|
165
|
-
"You are an autonomous code transformation agent. Your goal is to advance the repository toward its mission by making the most impactful change possible in a single step.",
|
|
165
|
+
"You are an autonomous code transformation agent. Your goal is to advance the repository toward its mission by making the most impactful change possible in a single step." + NARRATIVE_INSTRUCTION,
|
|
166
166
|
prompt,
|
|
167
167
|
writablePaths,
|
|
168
168
|
tuning: t,
|
|
@@ -201,7 +201,7 @@ export async function transform(context) {
|
|
|
201
201
|
cost,
|
|
202
202
|
model,
|
|
203
203
|
details: resultContent.substring(0, 500),
|
|
204
|
-
narrative: (resultContent
|
|
204
|
+
narrative: extractNarrative(resultContent, "Transformation step completed."),
|
|
205
205
|
promptBudget,
|
|
206
206
|
contextNotes: `Transformed with ${sourceFiles.length} source files (mtime-sorted, cleaned), ${features.length} features, ${openIssues.length} issues (${rawIssues.length - openIssues.length} stale filtered).`,
|
|
207
207
|
};
|
|
@@ -324,7 +324,7 @@ async function transformTdd({
|
|
|
324
324
|
const phase2 = await runCopilotTask({
|
|
325
325
|
model,
|
|
326
326
|
systemMessage:
|
|
327
|
-
"You are a TDD agent. A failing test was written in Phase 1. Write the minimum implementation to make it pass. Do not modify the test.",
|
|
327
|
+
"You are a TDD agent. A failing test was written in Phase 1. Write the minimum implementation to make it pass. Do not modify the test." + NARRATIVE_INSTRUCTION,
|
|
328
328
|
prompt: implPrompt,
|
|
329
329
|
writablePaths,
|
|
330
330
|
tuning: t,
|
|
@@ -341,5 +341,6 @@ async function transformTdd({
|
|
|
341
341
|
cost: (phase1.cost || 0) + (phase2.cost || 0),
|
|
342
342
|
model,
|
|
343
343
|
details: `TDD transformation: Phase 1 (failing test) + Phase 2 (implementation). ${testResult.substring(0, 200)}`,
|
|
344
|
+
narrative: extractNarrative(phase2.content, "TDD transformation: wrote failing test then implementation."),
|
|
344
345
|
};
|
|
345
346
|
}
|