@xn-intenton-z2a/agentic-lib 7.2.15 → 7.2.17
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-test.yml +3 -20
- package/.github/workflows/agentic-lib-workflow.yml +62 -4
- package/bin/agentic-lib.js +27 -0
- package/package.json +1 -1
- package/src/actions/agentic-step/index.js +39 -0
- package/src/actions/commit-if-changed/action.yml +2 -12
- package/src/scripts/push-to-logs.sh +86 -0
- package/src/seeds/zero-.gitignore +5 -1
- package/src/seeds/zero-package.json +1 -1
|
@@ -122,29 +122,12 @@ jobs:
|
|
|
122
122
|
path: SCREENSHOT_INDEX.png
|
|
123
123
|
if-no-files-found: ignore
|
|
124
124
|
|
|
125
|
-
- name: Push screenshot
|
|
125
|
+
- name: Push screenshot to .logs branch
|
|
126
126
|
if: |
|
|
127
127
|
github.ref == 'refs/heads/main' &&
|
|
128
|
+
github.repository != 'xn-intenton-z2a/agentic-lib' &&
|
|
128
129
|
(github.event_name == 'schedule' || inputs.push-screenshot == 'true' || inputs.push-screenshot == true)
|
|
129
|
-
run:
|
|
130
|
-
git config --global --add safe.directory "$GITHUB_WORKSPACE"
|
|
131
|
-
git config user.name "github-actions[bot]"
|
|
132
|
-
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
|
|
133
|
-
git add SCREENSHOT_INDEX.png
|
|
134
|
-
git diff --cached --quiet && echo "No screenshot changes" && exit 0
|
|
135
|
-
git commit -m "test: update SCREENSHOT_INDEX.png [skip ci]"
|
|
136
|
-
for attempt in 1 2 3; do
|
|
137
|
-
git push origin HEAD:main && break
|
|
138
|
-
echo "Push failed (attempt $attempt) — pulling and retrying"
|
|
139
|
-
git pull --rebase origin main || {
|
|
140
|
-
echo "Rebase conflict — aborting rebase and retrying"
|
|
141
|
-
git rebase --abort 2>/dev/null || true
|
|
142
|
-
}
|
|
143
|
-
sleep $((attempt * 2))
|
|
144
|
-
if [ "$attempt" -eq 3 ]; then
|
|
145
|
-
echo "::warning::Failed to push screenshot after 3 attempts"
|
|
146
|
-
fi
|
|
147
|
-
done
|
|
130
|
+
run: bash .github/agentic-lib/scripts/push-to-logs.sh SCREENSHOT_INDEX.png
|
|
148
131
|
|
|
149
132
|
# ─── Dispatch fix workflow when unit tests fail on main ─────────────
|
|
150
133
|
dispatch-fix:
|
|
@@ -428,12 +428,26 @@ jobs:
|
|
|
428
428
|
const missionComplete = fs.existsSync('MISSION_COMPLETE.md');
|
|
429
429
|
const missionFailed = fs.existsSync('MISSION_FAILED.md');
|
|
430
430
|
|
|
431
|
-
// Activity log stats
|
|
431
|
+
// Activity log stats (fetched from .logs branch)
|
|
432
432
|
let activityStats = null;
|
|
433
433
|
try {
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
434
|
+
// Try .logs branch first (new location), fall back to local file (legacy)
|
|
435
|
+
let log = null;
|
|
436
|
+
for (const logFile of ['intenti\u00F6n.md', 'intention.md']) {
|
|
437
|
+
try {
|
|
438
|
+
const { data } = await github.rest.repos.getContent({
|
|
439
|
+
owner, repo, path: logFile, ref: '.logs',
|
|
440
|
+
});
|
|
441
|
+
log = Buffer.from(data.content, 'base64').toString('utf8');
|
|
442
|
+
break;
|
|
443
|
+
} catch { /* try next */ }
|
|
444
|
+
}
|
|
445
|
+
// Fall back to local file (for repos not yet using .logs branch)
|
|
446
|
+
if (!log) {
|
|
447
|
+
const logPath = fs.existsSync('intenti\u00F6n.md') ? 'intenti\u00F6n.md' : (fs.existsSync('intention.md') ? 'intention.md' : null);
|
|
448
|
+
if (logPath) log = fs.readFileSync(logPath, 'utf8');
|
|
449
|
+
}
|
|
450
|
+
if (log) {
|
|
437
451
|
const entries = log.split('\n## ').length - 1;
|
|
438
452
|
const costMatches = [...log.matchAll(/\*\*agentic-lib transformation cost:\*\* (\d+)/g)];
|
|
439
453
|
const totalCost = costMatches.reduce((sum, m) => sum + parseInt(m[1], 10), 0);
|
|
@@ -526,6 +540,12 @@ jobs:
|
|
|
526
540
|
with:
|
|
527
541
|
fetch-depth: 0
|
|
528
542
|
|
|
543
|
+
- name: Fetch log from .logs branch
|
|
544
|
+
run: |
|
|
545
|
+
for f in "intentïon.md" "intention.md"; do
|
|
546
|
+
git show "origin/.logs:${f}" > "$f" 2>/dev/null || true
|
|
547
|
+
done
|
|
548
|
+
|
|
529
549
|
- name: Check mission-complete signal
|
|
530
550
|
id: mission-check
|
|
531
551
|
run: |
|
|
@@ -622,6 +642,10 @@ jobs:
|
|
|
622
642
|
commit-message: "agentic-step: maintain features and library"
|
|
623
643
|
push-ref: ${{ github.ref_name }}
|
|
624
644
|
|
|
645
|
+
- name: Push log to .logs branch
|
|
646
|
+
if: github.repository != 'xn-intenton-z2a/agentic-lib' && needs.params.outputs.dry-run != 'true'
|
|
647
|
+
run: bash .github/agentic-lib/scripts/push-to-logs.sh "intentïon.md" "intention.md"
|
|
648
|
+
|
|
625
649
|
# ─── Director: LLM evaluates mission status (complete/failed/in-progress) ──
|
|
626
650
|
director:
|
|
627
651
|
needs: [params, telemetry, maintain]
|
|
@@ -635,6 +659,14 @@ jobs:
|
|
|
635
659
|
analysis: ${{ steps.director.outputs.director-analysis }}
|
|
636
660
|
steps:
|
|
637
661
|
- uses: actions/checkout@v6
|
|
662
|
+
with:
|
|
663
|
+
fetch-depth: 0
|
|
664
|
+
|
|
665
|
+
- name: Fetch log from .logs branch
|
|
666
|
+
run: |
|
|
667
|
+
for f in "intentïon.md" "intention.md"; do
|
|
668
|
+
git show "origin/.logs:${f}" > "$f" 2>/dev/null || true
|
|
669
|
+
done
|
|
638
670
|
|
|
639
671
|
- uses: actions/setup-node@v6
|
|
640
672
|
with:
|
|
@@ -661,6 +693,10 @@ jobs:
|
|
|
661
693
|
instructions: ".github/agentic-lib/agents/agent-director.md"
|
|
662
694
|
model: ${{ needs.params.outputs.model }}
|
|
663
695
|
|
|
696
|
+
- name: Push log to .logs branch
|
|
697
|
+
if: github.repository != 'xn-intenton-z2a/agentic-lib' && needs.params.outputs.dry-run != 'true'
|
|
698
|
+
run: bash .github/agentic-lib/scripts/push-to-logs.sh "intentïon.md" "intention.md"
|
|
699
|
+
|
|
664
700
|
# ─── Supervisor: LLM decides what to do (after director evaluates) ──
|
|
665
701
|
supervisor:
|
|
666
702
|
needs: [params, pr-cleanup, telemetry, maintain, director]
|
|
@@ -673,6 +709,14 @@ jobs:
|
|
|
673
709
|
runs-on: ubuntu-latest
|
|
674
710
|
steps:
|
|
675
711
|
- uses: actions/checkout@v6
|
|
712
|
+
with:
|
|
713
|
+
fetch-depth: 0
|
|
714
|
+
|
|
715
|
+
- name: Fetch log from .logs branch
|
|
716
|
+
run: |
|
|
717
|
+
for f in "intentïon.md" "intention.md"; do
|
|
718
|
+
git show "origin/.logs:${f}" > "$f" 2>/dev/null || true
|
|
719
|
+
done
|
|
676
720
|
|
|
677
721
|
- uses: actions/setup-node@v6
|
|
678
722
|
with:
|
|
@@ -699,6 +743,10 @@ jobs:
|
|
|
699
743
|
instructions: ".github/agentic-lib/agents/agent-supervisor.md"
|
|
700
744
|
model: ${{ needs.params.outputs.model }}
|
|
701
745
|
|
|
746
|
+
- name: Push log to .logs branch
|
|
747
|
+
if: github.repository != 'xn-intenton-z2a/agentic-lib' && needs.params.outputs.dry-run != 'true'
|
|
748
|
+
run: bash .github/agentic-lib/scripts/push-to-logs.sh "intentïon.md" "intention.md"
|
|
749
|
+
|
|
702
750
|
# ─── Fix stuck PRs with failing checks ─────────────────────────────
|
|
703
751
|
fix-stuck:
|
|
704
752
|
needs: [params, supervisor]
|
|
@@ -1079,6 +1127,12 @@ jobs:
|
|
|
1079
1127
|
fetch-depth: 0
|
|
1080
1128
|
token: ${{ secrets.GITHUB_TOKEN }}
|
|
1081
1129
|
|
|
1130
|
+
- name: Fetch log from .logs branch
|
|
1131
|
+
run: |
|
|
1132
|
+
for f in "intentïon.md" "intention.md"; do
|
|
1133
|
+
git show "origin/.logs:${f}" > "$f" 2>/dev/null || true
|
|
1134
|
+
done
|
|
1135
|
+
|
|
1082
1136
|
- uses: actions/setup-node@v6
|
|
1083
1137
|
with:
|
|
1084
1138
|
node-version: "24"
|
|
@@ -1239,6 +1293,10 @@ jobs:
|
|
|
1239
1293
|
commit-message: "agentic-step: transform issue #${{ steps.issue.outputs.issue-number }}"
|
|
1240
1294
|
push-ref: ${{ steps.branch.outputs.branchName }}
|
|
1241
1295
|
|
|
1296
|
+
- name: Push log to .logs branch
|
|
1297
|
+
if: github.repository != 'xn-intenton-z2a/agentic-lib' && needs.params.outputs.dry-run != 'true'
|
|
1298
|
+
run: bash .github/agentic-lib/scripts/push-to-logs.sh "intentïon.md" "intention.md"
|
|
1299
|
+
|
|
1242
1300
|
- name: Create PR and attempt merge
|
|
1243
1301
|
if: github.repository != 'xn-intenton-z2a/agentic-lib' && steps.issue.outputs.issue-number != '' && needs.params.outputs.dry-run != 'true' && steps.pre-commit-test.outputs.tests-passed == 'true' && steps.pre-commit-behaviour-test.outputs.tests-passed != 'false'
|
|
1244
1302
|
uses: actions/github-script@v8
|
package/bin/agentic-lib.js
CHANGED
|
@@ -559,6 +559,7 @@ function initScripts(agenticDir) {
|
|
|
559
559
|
"clean.sh",
|
|
560
560
|
"initialise.sh",
|
|
561
561
|
"md-to-html.js",
|
|
562
|
+
"push-to-logs.sh",
|
|
562
563
|
"update.sh",
|
|
563
564
|
];
|
|
564
565
|
if (!existsSync(scriptsDir)) return;
|
|
@@ -1133,6 +1134,32 @@ function initPurgeGitHub() {
|
|
|
1133
1134
|
console.log(` SKIP: Could not create discussion (${err.message})`);
|
|
1134
1135
|
}
|
|
1135
1136
|
|
|
1137
|
+
// ── Create/reset .logs orphan branch ─────────────────────────────
|
|
1138
|
+
console.log("\n--- .logs branch (activity log + screenshot) ---");
|
|
1139
|
+
try {
|
|
1140
|
+
if (!dryRun) {
|
|
1141
|
+
// Delete existing .logs branch if present
|
|
1142
|
+
try {
|
|
1143
|
+
ghExec(`gh api repos/${repoSlug}/git/refs/heads/.logs -X DELETE`);
|
|
1144
|
+
console.log(" DELETE: existing .logs branch");
|
|
1145
|
+
} catch { /* branch may not exist */ }
|
|
1146
|
+
// Create orphan .logs branch with an empty commit via the API
|
|
1147
|
+
// First get the empty tree SHA
|
|
1148
|
+
const emptyTree = JSON.parse(ghExec(`gh api repos/${repoSlug}/git/trees -X POST -f "tree[0][path]=.gitkeep" -f "tree[0][mode]=100644" -f "tree[0][type]=blob" -f "tree[0][content]="`));
|
|
1149
|
+
const commitData = JSON.parse(ghExec(
|
|
1150
|
+
`gh api repos/${repoSlug}/git/commits -X POST -f "message=init .logs branch" -f "tree=${emptyTree.sha}"`,
|
|
1151
|
+
));
|
|
1152
|
+
ghExec(`gh api repos/${repoSlug}/git/refs -X POST -f "ref=refs/heads/.logs" -f "sha=${commitData.sha}"`);
|
|
1153
|
+
console.log(" CREATE: .logs orphan branch");
|
|
1154
|
+
initChanges++;
|
|
1155
|
+
} else {
|
|
1156
|
+
console.log(" CREATE: .logs orphan branch (dry run)");
|
|
1157
|
+
initChanges++;
|
|
1158
|
+
}
|
|
1159
|
+
} catch (err) {
|
|
1160
|
+
console.log(` SKIP: Could not create .logs branch (${err.message})`);
|
|
1161
|
+
}
|
|
1162
|
+
|
|
1136
1163
|
// ── Enable GitHub Pages ───────────────────────────────────────────
|
|
1137
1164
|
console.log("\n--- Enable GitHub Pages ---");
|
|
1138
1165
|
try {
|
package/package.json
CHANGED
|
@@ -304,6 +304,45 @@ async function run() {
|
|
|
304
304
|
}
|
|
305
305
|
} catch (_) { /* API not available */ }
|
|
306
306
|
|
|
307
|
+
// Count resolved issues (if not already provided by the task)
|
|
308
|
+
if (result.resolvedCount == null) {
|
|
309
|
+
let resolvedCount = 0;
|
|
310
|
+
try {
|
|
311
|
+
const initTimestamp = config.init?.timestamp;
|
|
312
|
+
const { data: closedIssuesRaw } = await context.octokit.rest.issues.listForRepo({
|
|
313
|
+
...context.repo, state: "closed", labels: "automated", per_page: 10, sort: "updated", direction: "desc",
|
|
314
|
+
});
|
|
315
|
+
const initEpoch = initTimestamp ? new Date(initTimestamp).getTime() : 0;
|
|
316
|
+
const closedFiltered = closedIssuesRaw.filter((i) =>
|
|
317
|
+
!i.pull_request && (initEpoch <= 0 || new Date(i.created_at).getTime() >= initEpoch)
|
|
318
|
+
);
|
|
319
|
+
for (const ci of closedFiltered) {
|
|
320
|
+
let isResolved = false;
|
|
321
|
+
try {
|
|
322
|
+
const { data: comments } = await context.octokit.rest.issues.listComments({
|
|
323
|
+
...context.repo, issue_number: ci.number, per_page: 5, sort: "created", direction: "desc",
|
|
324
|
+
});
|
|
325
|
+
if (comments.some((c) => c.body?.includes("Automated Review Result"))) {
|
|
326
|
+
isResolved = true;
|
|
327
|
+
} else {
|
|
328
|
+
const { data: events } = await context.octokit.rest.issues.listEvents({
|
|
329
|
+
...context.repo, issue_number: ci.number, per_page: 10,
|
|
330
|
+
});
|
|
331
|
+
if (events.some((e) => e.event === "closed" && e.commit_id)) {
|
|
332
|
+
isResolved = true;
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
if (!isResolved) {
|
|
336
|
+
const issueLabels = ci.labels.map((l) => (typeof l === "string" ? l : l.name));
|
|
337
|
+
if (issueLabels.includes("merged")) isResolved = true;
|
|
338
|
+
}
|
|
339
|
+
} catch { /* ignore */ }
|
|
340
|
+
if (isResolved) resolvedCount++;
|
|
341
|
+
}
|
|
342
|
+
} catch (_) { /* API not available */ }
|
|
343
|
+
result.resolvedCount = resolvedCount;
|
|
344
|
+
}
|
|
345
|
+
|
|
307
346
|
const budgetCap = config.transformationBudget || 0;
|
|
308
347
|
const featCap = config.paths?.features?.limit || 4;
|
|
309
348
|
const libCap = config.paths?.library?.limit || 32;
|
|
@@ -28,20 +28,10 @@ runs:
|
|
|
28
28
|
git add -A
|
|
29
29
|
# Unstage workflow files — GITHUB_TOKEN cannot push workflow changes
|
|
30
30
|
git reset HEAD -- '.github/workflows/' 2>/dev/null || true
|
|
31
|
-
# Unstage log files
|
|
32
|
-
|
|
33
|
-
if [ -n "$REF" ] && [ "$REF" != "main" ] && [ "$REF" != "master" ]; then
|
|
34
|
-
git reset HEAD -- 'intentïon.md' 'intention.md' 2>/dev/null || true
|
|
35
|
-
fi
|
|
31
|
+
# Unstage log/screenshot files — these live on the .logs branch
|
|
32
|
+
git reset HEAD -- 'intentïon.md' 'intention.md' 'SCREENSHOT_INDEX.png' 2>/dev/null || true
|
|
36
33
|
if git diff --cached --quiet; then
|
|
37
34
|
echo "No changes to commit"
|
|
38
|
-
elif git diff --cached --name-only | grep -qvE '(intentïon\.md|intention\.md)$'; then
|
|
39
|
-
# At least one non-log file changed — proceed with commit
|
|
40
|
-
true
|
|
41
|
-
else
|
|
42
|
-
echo "Only log files changed — skipping commit"
|
|
43
|
-
git reset HEAD -- . > /dev/null 2>&1
|
|
44
|
-
exit 0
|
|
45
35
|
fi
|
|
46
36
|
if ! git diff --cached --quiet; then
|
|
47
37
|
git commit -m "${{ inputs.commit-message }}"
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# SPDX-License-Identifier: MIT
|
|
3
|
+
# Copyright (C) 2025-2026 Polycode Limited
|
|
4
|
+
# push-to-logs.sh — Push log/screenshot files to the .logs orphan branch.
|
|
5
|
+
#
|
|
6
|
+
# Usage: bash .github/agentic-lib/scripts/push-to-logs.sh [file ...]
|
|
7
|
+
# e.g. bash .github/agentic-lib/scripts/push-to-logs.sh "intentïon.md" SCREENSHOT_INDEX.png
|
|
8
|
+
#
|
|
9
|
+
# Creates the .logs branch if it doesn't exist. Uses rebase/retry for
|
|
10
|
+
# concurrent pushes (same strategy as commit-if-changed).
|
|
11
|
+
|
|
12
|
+
set -euo pipefail
|
|
13
|
+
|
|
14
|
+
BRANCH=".logs"
|
|
15
|
+
MAX_RETRIES=3
|
|
16
|
+
|
|
17
|
+
# Collect files that actually exist in the workspace
|
|
18
|
+
FILES=()
|
|
19
|
+
for arg in "$@"; do
|
|
20
|
+
if [ -f "$arg" ]; then
|
|
21
|
+
FILES+=("$arg")
|
|
22
|
+
fi
|
|
23
|
+
done
|
|
24
|
+
|
|
25
|
+
if [ ${#FILES[@]} -eq 0 ]; then
|
|
26
|
+
echo "push-to-logs: no files to push"
|
|
27
|
+
exit 0
|
|
28
|
+
fi
|
|
29
|
+
|
|
30
|
+
echo "push-to-logs: pushing ${FILES[*]} to ${BRANCH}"
|
|
31
|
+
|
|
32
|
+
# Configure git
|
|
33
|
+
git config --local user.email 'action@github.com'
|
|
34
|
+
git config --local user.name 'GitHub Actions[bot]'
|
|
35
|
+
|
|
36
|
+
# Save file contents to temp
|
|
37
|
+
TMPDIR=$(mktemp -d)
|
|
38
|
+
for f in "${FILES[@]}"; do
|
|
39
|
+
cp "$f" "${TMPDIR}/$(basename "$f")"
|
|
40
|
+
done
|
|
41
|
+
|
|
42
|
+
# Fetch the .logs branch (may not exist yet)
|
|
43
|
+
REMOTE_EXISTS=""
|
|
44
|
+
git fetch origin "${BRANCH}" 2>/dev/null && REMOTE_EXISTS="true" || true
|
|
45
|
+
|
|
46
|
+
if [ "$REMOTE_EXISTS" = "true" ]; then
|
|
47
|
+
# Check out existing .logs branch into a detached worktree-like state
|
|
48
|
+
git checkout "origin/${BRANCH}" -- . 2>/dev/null || true
|
|
49
|
+
git checkout -B "${BRANCH}" "origin/${BRANCH}"
|
|
50
|
+
else
|
|
51
|
+
# Create orphan branch
|
|
52
|
+
git checkout --orphan "${BRANCH}"
|
|
53
|
+
git rm -rf . 2>/dev/null || true
|
|
54
|
+
fi
|
|
55
|
+
|
|
56
|
+
# Copy files in
|
|
57
|
+
for f in "${FILES[@]}"; do
|
|
58
|
+
cp "${TMPDIR}/$(basename "$f")" "$f"
|
|
59
|
+
git add "$f"
|
|
60
|
+
done
|
|
61
|
+
|
|
62
|
+
# Commit if changed
|
|
63
|
+
if git diff --cached --quiet 2>/dev/null; then
|
|
64
|
+
echo "push-to-logs: no changes to commit"
|
|
65
|
+
else
|
|
66
|
+
git commit -m "logs: update ${FILES[*]} [skip ci]"
|
|
67
|
+
|
|
68
|
+
for attempt in $(seq 1 $MAX_RETRIES); do
|
|
69
|
+
git push origin "${BRANCH}" && break
|
|
70
|
+
echo "push-to-logs: push failed (attempt $attempt) — pulling and retrying"
|
|
71
|
+
git pull --rebase origin "${BRANCH}" || {
|
|
72
|
+
echo "push-to-logs: rebase conflict — aborting and retrying"
|
|
73
|
+
git rebase --abort 2>/dev/null || true
|
|
74
|
+
}
|
|
75
|
+
sleep $((attempt * 2))
|
|
76
|
+
if [ "$attempt" -eq "$MAX_RETRIES" ]; then
|
|
77
|
+
echo "::warning::push-to-logs: failed to push after $MAX_RETRIES attempts"
|
|
78
|
+
fi
|
|
79
|
+
done
|
|
80
|
+
fi
|
|
81
|
+
|
|
82
|
+
# Return to previous branch
|
|
83
|
+
git checkout - 2>/dev/null || git checkout main 2>/dev/null || true
|
|
84
|
+
|
|
85
|
+
# Clean up
|
|
86
|
+
rm -rf "$TMPDIR"
|
|
@@ -20,8 +20,12 @@ Thumbs.db
|
|
|
20
20
|
*.swp
|
|
21
21
|
*.swo
|
|
22
22
|
|
|
23
|
+
# Activity log and screenshot (live on .logs branch, not main)
|
|
24
|
+
/intentïon.md
|
|
25
|
+
/intention.md
|
|
26
|
+
/SCREENSHOT_INDEX.png
|
|
27
|
+
|
|
23
28
|
# Generated files at repo root (agents should use examples/ instead)
|
|
24
|
-
/*.png
|
|
25
29
|
/*.svg
|
|
26
30
|
/*.pdf
|
|
27
31
|
/*.csv
|