@xn-intenton-z2a/agentic-lib 7.1.71 → 7.1.73
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/bin/agentic-lib.js +283 -70
- package/package.json +3 -1
- package/src/actions/agentic-step/config-loader.js +1 -0
- package/src/actions/agentic-step/tasks/discussions.js +114 -24
- package/src/actions/agentic-step/tasks/review-issue.js +21 -0
- package/src/actions/agentic-step/tasks/supervise.js +95 -19
- package/src/actions/commit-if-changed/action.yml +2 -0
- package/src/seeds/zero-index.html +0 -13
- package/src/seeds/zero-package.json +1 -1
- package/src/seeds/zero-web.test.js +0 -5
package/bin/agentic-lib.js
CHANGED
|
@@ -856,10 +856,30 @@ function clearDirContents(dirPath, label) {
|
|
|
856
856
|
}
|
|
857
857
|
}
|
|
858
858
|
|
|
859
|
-
function initReseed() {
|
|
859
|
+
function initReseed(initTimestamp) {
|
|
860
860
|
console.log("\n--- Reseed: Clear Features + Activity Log ---");
|
|
861
861
|
removeFile(resolve(target, "intentïon.md"), "intentïon.md");
|
|
862
862
|
removeFile(resolve(target, "MISSION_COMPLETE.md"), "MISSION_COMPLETE.md");
|
|
863
|
+
removeFile(resolve(target, "MISSION_FAILED.md"), "MISSION_FAILED.md");
|
|
864
|
+
|
|
865
|
+
// Write init epoch header to the activity log
|
|
866
|
+
const pkg = JSON.parse(readFileSync(resolve(pkgRoot, "package.json"), "utf8"));
|
|
867
|
+
const mode = purge ? "purge" : "reseed";
|
|
868
|
+
const initHeader = [
|
|
869
|
+
`# intentïon activity log`,
|
|
870
|
+
"",
|
|
871
|
+
`**init ${mode}** at ${initTimestamp} (agentic-lib@${pkg.version})`,
|
|
872
|
+
`**mission:** ${mission}`,
|
|
873
|
+
"",
|
|
874
|
+
"---",
|
|
875
|
+
"",
|
|
876
|
+
].join("\n");
|
|
877
|
+
if (!dryRun) {
|
|
878
|
+
writeFileSync(resolve(target, "intentïon.md"), initHeader);
|
|
879
|
+
}
|
|
880
|
+
console.log(" WRITE: intentïon.md (init epoch header)");
|
|
881
|
+
initChanges++;
|
|
882
|
+
|
|
863
883
|
clearDirContents(resolve(target, "features"), "features");
|
|
864
884
|
|
|
865
885
|
// Clear old features location if it exists
|
|
@@ -916,7 +936,7 @@ function clearAndRecreateDir(dirPath, label) {
|
|
|
916
936
|
if (!dryRun) mkdirSync(fullPath, { recursive: true });
|
|
917
937
|
}
|
|
918
938
|
|
|
919
|
-
function initPurge(seedsDir, missionName) {
|
|
939
|
+
function initPurge(seedsDir, missionName, initTimestamp) {
|
|
920
940
|
console.log("\n--- Purge: Reset Source Files to Seed State ---");
|
|
921
941
|
|
|
922
942
|
const { sourcePath, testsPath, examplesPath, webPath } = readTomlPaths();
|
|
@@ -980,10 +1000,46 @@ function initPurge(seedsDir, missionName) {
|
|
|
980
1000
|
}
|
|
981
1001
|
process.exit(1);
|
|
982
1002
|
}
|
|
1003
|
+
|
|
1004
|
+
// Write init metadata to agentic-lib.toml
|
|
1005
|
+
const tomlTarget = resolve(target, "agentic-lib.toml");
|
|
1006
|
+
if (existsSync(tomlTarget)) {
|
|
1007
|
+
let toml = readFileSync(tomlTarget, "utf8");
|
|
1008
|
+
const pkg = JSON.parse(readFileSync(resolve(pkgRoot, "package.json"), "utf8"));
|
|
1009
|
+
const initSection = [
|
|
1010
|
+
"",
|
|
1011
|
+
"[init]",
|
|
1012
|
+
`timestamp = "${initTimestamp}"`,
|
|
1013
|
+
`mode = "purge"`,
|
|
1014
|
+
`mission = "${missionName}"`,
|
|
1015
|
+
`version = "${pkg.version}"`,
|
|
1016
|
+
].join("\n");
|
|
1017
|
+
// Replace existing [init] section or append
|
|
1018
|
+
if (/^\[init\]/m.test(toml)) {
|
|
1019
|
+
toml = toml.replace(/\n?\[init\][^\[]*/, initSection);
|
|
1020
|
+
} else {
|
|
1021
|
+
toml = toml.trimEnd() + "\n" + initSection + "\n";
|
|
1022
|
+
}
|
|
1023
|
+
if (!dryRun) {
|
|
1024
|
+
writeFileSync(tomlTarget, toml);
|
|
1025
|
+
}
|
|
1026
|
+
console.log(" WRITE: [init] section in agentic-lib.toml");
|
|
1027
|
+
initChanges++;
|
|
1028
|
+
}
|
|
1029
|
+
}
|
|
1030
|
+
|
|
1031
|
+
function ghExec(cmd, input) {
|
|
1032
|
+
const opts = { cwd: target, encoding: "utf8", timeout: 30000, stdio: ["pipe", "pipe", "pipe"] };
|
|
1033
|
+
if (input) opts.input = input;
|
|
1034
|
+
return execSync(cmd, opts);
|
|
1035
|
+
}
|
|
1036
|
+
|
|
1037
|
+
function ghGraphQL(query) {
|
|
1038
|
+
return JSON.parse(ghExec("gh api graphql --input -", JSON.stringify({ query })));
|
|
983
1039
|
}
|
|
984
1040
|
|
|
985
1041
|
function initPurgeGitHub() {
|
|
986
|
-
console.log("\n--- Purge:
|
|
1042
|
+
console.log("\n--- Purge: Clean Slate (Issues, PRs, Runs, Branches, Labels, Discussions) ---");
|
|
987
1043
|
|
|
988
1044
|
// Detect the GitHub repo from git remote
|
|
989
1045
|
let repoSlug = "";
|
|
@@ -993,7 +1049,6 @@ function initPurgeGitHub() {
|
|
|
993
1049
|
encoding: "utf8",
|
|
994
1050
|
timeout: 10000,
|
|
995
1051
|
}).trim();
|
|
996
|
-
// Parse owner/repo from git@github.com:owner/repo.git or https://github.com/owner/repo.git
|
|
997
1052
|
const match = remoteUrl.match(/github\.com[:/]([^/]+\/[^/.]+)/);
|
|
998
1053
|
if (match) repoSlug = match[1].replace(/\.git$/, "");
|
|
999
1054
|
} catch {
|
|
@@ -1005,7 +1060,6 @@ function initPurgeGitHub() {
|
|
|
1005
1060
|
return;
|
|
1006
1061
|
}
|
|
1007
1062
|
|
|
1008
|
-
// Check gh CLI is available
|
|
1009
1063
|
try {
|
|
1010
1064
|
execSync("gh --version", { encoding: "utf8", timeout: 5000, stdio: "pipe" });
|
|
1011
1065
|
} catch {
|
|
@@ -1013,29 +1067,205 @@ function initPurgeGitHub() {
|
|
|
1013
1067
|
return;
|
|
1014
1068
|
}
|
|
1015
1069
|
|
|
1016
|
-
|
|
1070
|
+
const [owner, repo] = repoSlug.split("/");
|
|
1071
|
+
|
|
1072
|
+
// ── A1: Close + lock ALL issues (open and closed) ──────────────────
|
|
1073
|
+
console.log("\n --- Issues: close open, lock all ---");
|
|
1074
|
+
try {
|
|
1075
|
+
// Close all open issues with not_planned reason
|
|
1076
|
+
const openIssuesJson = ghExec(`gh api repos/${repoSlug}/issues?state=open&per_page=100`);
|
|
1077
|
+
const openIssues = JSON.parse(openIssuesJson || "[]").filter((i) => !i.pull_request);
|
|
1078
|
+
for (const issue of openIssues) {
|
|
1079
|
+
console.log(` CLOSE: issue #${issue.number} — ${issue.title}`);
|
|
1080
|
+
if (!dryRun) {
|
|
1081
|
+
try {
|
|
1082
|
+
ghExec(
|
|
1083
|
+
`gh api repos/${repoSlug}/issues/${issue.number} -X PATCH -f state=closed -f state_reason=not_planned`,
|
|
1084
|
+
);
|
|
1085
|
+
ghExec(
|
|
1086
|
+
`gh api repos/${repoSlug}/issues/${issue.number}/comments -X POST -f body="Closed by init --purge (mission reset)"`,
|
|
1087
|
+
);
|
|
1088
|
+
initChanges++;
|
|
1089
|
+
} catch (err) {
|
|
1090
|
+
console.log(` WARN: Failed to close issue #${issue.number}: ${err.message}`);
|
|
1091
|
+
}
|
|
1092
|
+
} else {
|
|
1093
|
+
initChanges++;
|
|
1094
|
+
}
|
|
1095
|
+
}
|
|
1096
|
+
if (openIssues.length === 0) console.log(" No open issues to close");
|
|
1097
|
+
|
|
1098
|
+
// Lock ALL issues (open and closed) to prevent bleed
|
|
1099
|
+
const allIssuesJson = ghExec(`gh api repos/${repoSlug}/issues?state=all&per_page=100`);
|
|
1100
|
+
const allIssues = JSON.parse(allIssuesJson || "[]").filter((i) => !i.pull_request && !i.locked);
|
|
1101
|
+
for (const issue of allIssues) {
|
|
1102
|
+
console.log(` LOCK: issue #${issue.number} — ${issue.title}`);
|
|
1103
|
+
if (!dryRun) {
|
|
1104
|
+
try {
|
|
1105
|
+
ghExec(
|
|
1106
|
+
`gh api repos/${repoSlug}/issues/${issue.number}/lock -X PUT -f lock_reason=resolved`,
|
|
1107
|
+
);
|
|
1108
|
+
initChanges++;
|
|
1109
|
+
} catch (err) {
|
|
1110
|
+
console.log(` WARN: Failed to lock issue #${issue.number}: ${err.message}`);
|
|
1111
|
+
}
|
|
1112
|
+
} else {
|
|
1113
|
+
initChanges++;
|
|
1114
|
+
}
|
|
1115
|
+
}
|
|
1116
|
+
if (allIssues.length === 0) console.log(" No unlocked issues to lock");
|
|
1117
|
+
} catch (err) {
|
|
1118
|
+
console.log(` WARN: Issue cleanup failed: ${err.message}`);
|
|
1119
|
+
}
|
|
1120
|
+
|
|
1121
|
+
// ── A2: Close all open PRs + delete branches ──────────────────────
|
|
1122
|
+
console.log("\n --- PRs: close all open ---");
|
|
1123
|
+
try {
|
|
1124
|
+
const prsJson = ghExec(`gh pr list --repo ${repoSlug} --state open --json number,title,headRefName --limit 100`);
|
|
1125
|
+
const prs = JSON.parse(prsJson || "[]");
|
|
1126
|
+
for (const pr of prs) {
|
|
1127
|
+
console.log(` CLOSE: PR #${pr.number} — ${pr.title} (${pr.headRefName})`);
|
|
1128
|
+
if (!dryRun) {
|
|
1129
|
+
try {
|
|
1130
|
+
ghExec(`gh pr close ${pr.number} --repo ${repoSlug} --delete-branch`);
|
|
1131
|
+
initChanges++;
|
|
1132
|
+
} catch (err) {
|
|
1133
|
+
console.log(` WARN: Failed to close PR #${pr.number}: ${err.message}`);
|
|
1134
|
+
}
|
|
1135
|
+
} else {
|
|
1136
|
+
initChanges++;
|
|
1137
|
+
}
|
|
1138
|
+
}
|
|
1139
|
+
if (prs.length === 0) console.log(" No open PRs to close");
|
|
1140
|
+
} catch (err) {
|
|
1141
|
+
console.log(` WARN: PR cleanup failed: ${err.message}`);
|
|
1142
|
+
}
|
|
1143
|
+
|
|
1144
|
+
// ── A3: Delete old workflow runs ──────────────────────────────────
|
|
1145
|
+
console.log("\n --- Workflow runs: delete old ---");
|
|
1017
1146
|
try {
|
|
1018
|
-
const
|
|
1147
|
+
const runsJson = ghExec(`gh api repos/${repoSlug}/actions/runs?per_page=100`);
|
|
1148
|
+
const runs = JSON.parse(runsJson || '{"workflow_runs":[]}').workflow_runs || [];
|
|
1149
|
+
let deleted = 0;
|
|
1150
|
+
for (const run of runs) {
|
|
1151
|
+
// Skip currently running workflows (in_progress or queued)
|
|
1152
|
+
if (run.status === "in_progress" || run.status === "queued") {
|
|
1153
|
+
console.log(` SKIP: run ${run.id} (${run.name}) — ${run.status}`);
|
|
1154
|
+
continue;
|
|
1155
|
+
}
|
|
1156
|
+
if (!dryRun) {
|
|
1157
|
+
try {
|
|
1158
|
+
ghExec(`gh api repos/${repoSlug}/actions/runs/${run.id} -X DELETE`);
|
|
1159
|
+
deleted++;
|
|
1160
|
+
} catch (err) {
|
|
1161
|
+
console.log(` WARN: Failed to delete run ${run.id}: ${err.message}`);
|
|
1162
|
+
}
|
|
1163
|
+
} else {
|
|
1164
|
+
deleted++;
|
|
1165
|
+
}
|
|
1166
|
+
}
|
|
1167
|
+
console.log(` ${deleted > 0 ? `DELETE: ${deleted} workflow run(s)` : "No workflow runs to delete"}`);
|
|
1168
|
+
initChanges += deleted;
|
|
1169
|
+
} catch (err) {
|
|
1170
|
+
console.log(` WARN: Workflow run cleanup failed: ${err.message}`);
|
|
1171
|
+
}
|
|
1172
|
+
|
|
1173
|
+
// ── A4: Delete stale remote branches ──────────────────────────────
|
|
1174
|
+
console.log("\n --- Branches: delete stale remotes ---");
|
|
1175
|
+
try {
|
|
1176
|
+
const branchesJson = ghExec(`gh api repos/${repoSlug}/branches?per_page=100`);
|
|
1177
|
+
const branches = JSON.parse(branchesJson || "[]");
|
|
1178
|
+
const currentBranch = execSync("git rev-parse --abbrev-ref HEAD", {
|
|
1019
1179
|
cwd: target,
|
|
1020
1180
|
encoding: "utf8",
|
|
1021
|
-
timeout:
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1181
|
+
timeout: 5000,
|
|
1182
|
+
}).trim();
|
|
1183
|
+
const keepBranches = new Set(["main", "master", "template", "gh-pages", currentBranch]);
|
|
1184
|
+
let deletedBranches = 0;
|
|
1185
|
+
for (const branch of branches) {
|
|
1186
|
+
if (keepBranches.has(branch.name)) continue;
|
|
1187
|
+
if (branch.protected) continue;
|
|
1188
|
+
console.log(` DELETE: branch ${branch.name}`);
|
|
1189
|
+
if (!dryRun) {
|
|
1190
|
+
try {
|
|
1191
|
+
ghExec(`gh api repos/${repoSlug}/git/refs/heads/${branch.name} -X DELETE`);
|
|
1192
|
+
deletedBranches++;
|
|
1193
|
+
} catch (err) {
|
|
1194
|
+
console.log(` WARN: Failed to delete branch ${branch.name}: ${err.message}`);
|
|
1195
|
+
}
|
|
1196
|
+
} else {
|
|
1197
|
+
deletedBranches++;
|
|
1198
|
+
}
|
|
1199
|
+
}
|
|
1200
|
+
if (deletedBranches === 0) console.log(" No stale branches to delete");
|
|
1201
|
+
initChanges += deletedBranches;
|
|
1202
|
+
} catch (err) {
|
|
1203
|
+
console.log(` WARN: Branch cleanup failed: ${err.message}`);
|
|
1204
|
+
}
|
|
1205
|
+
|
|
1206
|
+
// ── A5: Reset labels ──────────────────────────────────────────────
|
|
1207
|
+
console.log("\n --- Labels: reset to pipeline defaults ---");
|
|
1208
|
+
const GITHUB_DEFAULT_LABELS = new Set([
|
|
1209
|
+
"bug",
|
|
1210
|
+
"documentation",
|
|
1211
|
+
"duplicate",
|
|
1212
|
+
"enhancement",
|
|
1213
|
+
"good first issue",
|
|
1214
|
+
"help wanted",
|
|
1215
|
+
"invalid",
|
|
1216
|
+
"question",
|
|
1217
|
+
"wontfix",
|
|
1218
|
+
]);
|
|
1219
|
+
const PIPELINE_LABELS = [
|
|
1220
|
+
{ name: "automated", color: "0e8a16", description: "Created by the autonomous pipeline" },
|
|
1221
|
+
{ name: "ready", color: "0075ca", description: "Issue is ready for transformation" },
|
|
1222
|
+
{ name: "in-progress", color: "e4e669", description: "Work in progress" },
|
|
1223
|
+
{ name: "merged", color: "6f42c1", description: "Associated PR has been merged" },
|
|
1224
|
+
{ name: "automerge", color: "1d76db", description: "PR should be auto-merged when checks pass" },
|
|
1225
|
+
];
|
|
1226
|
+
try {
|
|
1227
|
+
const labelsJson = ghExec(`gh api repos/${repoSlug}/labels?per_page=100`);
|
|
1228
|
+
const labels = JSON.parse(labelsJson || "[]");
|
|
1229
|
+
const pipelineNames = new Set(PIPELINE_LABELS.map((l) => l.name));
|
|
1230
|
+
// Delete non-default, non-pipeline labels
|
|
1231
|
+
for (const label of labels) {
|
|
1232
|
+
if (GITHUB_DEFAULT_LABELS.has(label.name)) continue;
|
|
1233
|
+
if (pipelineNames.has(label.name)) continue;
|
|
1234
|
+
console.log(` DELETE: label "${label.name}"`);
|
|
1235
|
+
if (!dryRun) {
|
|
1236
|
+
try {
|
|
1237
|
+
ghExec(`gh api repos/${repoSlug}/labels/${encodeURIComponent(label.name)} -X DELETE`);
|
|
1238
|
+
initChanges++;
|
|
1239
|
+
} catch (err) {
|
|
1240
|
+
console.log(` WARN: Failed to delete label "${label.name}": ${err.message}`);
|
|
1241
|
+
}
|
|
1242
|
+
} else {
|
|
1243
|
+
initChanges++;
|
|
1244
|
+
}
|
|
1245
|
+
}
|
|
1246
|
+
// Ensure pipeline labels exist with correct config
|
|
1247
|
+
const existingNames = new Set(labels.map((l) => l.name));
|
|
1248
|
+
for (const pl of PIPELINE_LABELS) {
|
|
1249
|
+
if (existingNames.has(pl.name)) {
|
|
1250
|
+
// Update to ensure correct color/description
|
|
1030
1251
|
if (!dryRun) {
|
|
1031
1252
|
try {
|
|
1032
|
-
|
|
1033
|
-
`gh
|
|
1034
|
-
|
|
1253
|
+
ghExec(
|
|
1254
|
+
`gh api repos/${repoSlug}/labels/${encodeURIComponent(pl.name)} -X PATCH -f color=${pl.color} -f description="${pl.description}"`,
|
|
1255
|
+
);
|
|
1256
|
+
} catch { /* ignore */ }
|
|
1257
|
+
}
|
|
1258
|
+
console.log(` UPDATE: label "${pl.name}"`);
|
|
1259
|
+
} else {
|
|
1260
|
+
console.log(` CREATE: label "${pl.name}"`);
|
|
1261
|
+
if (!dryRun) {
|
|
1262
|
+
try {
|
|
1263
|
+
ghExec(
|
|
1264
|
+
`gh api repos/${repoSlug}/labels -X POST -f name="${pl.name}" -f color=${pl.color} -f description="${pl.description}"`,
|
|
1035
1265
|
);
|
|
1036
1266
|
initChanges++;
|
|
1037
1267
|
} catch (err) {
|
|
1038
|
-
console.log(` WARN: Failed to
|
|
1268
|
+
console.log(` WARN: Failed to create label "${pl.name}": ${err.message}`);
|
|
1039
1269
|
}
|
|
1040
1270
|
} else {
|
|
1041
1271
|
initChanges++;
|
|
@@ -1043,23 +1273,15 @@ function initPurgeGitHub() {
|
|
|
1043
1273
|
}
|
|
1044
1274
|
}
|
|
1045
1275
|
} catch (err) {
|
|
1046
|
-
console.log(` WARN:
|
|
1276
|
+
console.log(` WARN: Label cleanup failed: ${err.message}`);
|
|
1047
1277
|
}
|
|
1048
1278
|
|
|
1049
|
-
//
|
|
1050
|
-
|
|
1279
|
+
// ── Discussions: close all open, create fresh one ─────────────────
|
|
1280
|
+
console.log("\n --- Discussions: close open, create fresh ---");
|
|
1051
1281
|
try {
|
|
1052
|
-
const
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
const result = execSync(`gh api graphql --input -`, {
|
|
1056
|
-
cwd: target,
|
|
1057
|
-
encoding: "utf8",
|
|
1058
|
-
timeout: 30000,
|
|
1059
|
-
input: query,
|
|
1060
|
-
stdio: ["pipe", "pipe", "pipe"],
|
|
1061
|
-
});
|
|
1062
|
-
const parsed = JSON.parse(result);
|
|
1282
|
+
const parsed = ghGraphQL(
|
|
1283
|
+
`{ repository(owner:"${owner}", name:"${repo}") { discussions(first:50, states:OPEN) { nodes { id number title } } } }`,
|
|
1284
|
+
);
|
|
1063
1285
|
const discussions = parsed?.data?.repository?.discussions?.nodes || [];
|
|
1064
1286
|
if (discussions.length === 0) {
|
|
1065
1287
|
console.log(" No open discussions to close");
|
|
@@ -1068,16 +1290,7 @@ function initPurgeGitHub() {
|
|
|
1068
1290
|
console.log(` CLOSE: discussion #${disc.number} — ${disc.title}`);
|
|
1069
1291
|
if (!dryRun) {
|
|
1070
1292
|
try {
|
|
1071
|
-
|
|
1072
|
-
query: `mutation { closeDiscussion(input: { discussionId: "${disc.id}" }) { discussion { number } } }`,
|
|
1073
|
-
});
|
|
1074
|
-
execSync(`gh api graphql --input -`, {
|
|
1075
|
-
cwd: target,
|
|
1076
|
-
encoding: "utf8",
|
|
1077
|
-
timeout: 15000,
|
|
1078
|
-
input: mutation,
|
|
1079
|
-
stdio: ["pipe", "pipe", "pipe"],
|
|
1080
|
-
});
|
|
1293
|
+
ghGraphQL(`mutation { closeDiscussion(input: { discussionId: "${disc.id}" }) { discussion { number } } }`);
|
|
1081
1294
|
initChanges++;
|
|
1082
1295
|
} catch {
|
|
1083
1296
|
console.log(` SKIP: Could not close discussion #${disc.number} (may need admin permissions)`);
|
|
@@ -1093,18 +1306,9 @@ function initPurgeGitHub() {
|
|
|
1093
1306
|
|
|
1094
1307
|
// Create a new "Talk to the repository" discussion
|
|
1095
1308
|
try {
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
});
|
|
1100
|
-
const repoResult = execSync(`gh api graphql --input -`, {
|
|
1101
|
-
cwd: target,
|
|
1102
|
-
encoding: "utf8",
|
|
1103
|
-
timeout: 30000,
|
|
1104
|
-
input: repoQuery,
|
|
1105
|
-
stdio: ["pipe", "pipe", "pipe"],
|
|
1106
|
-
});
|
|
1107
|
-
const repoParsed = JSON.parse(repoResult);
|
|
1309
|
+
const repoParsed = ghGraphQL(
|
|
1310
|
+
`{ repository(owner:"${owner}", name:"${repo}") { id discussionCategories(first:20) { nodes { id name } } } }`,
|
|
1311
|
+
);
|
|
1108
1312
|
const repoId = repoParsed?.data?.repository?.id;
|
|
1109
1313
|
const categories = repoParsed?.data?.repository?.discussionCategories?.nodes || [];
|
|
1110
1314
|
const generalCat = categories.find((c) => c.name === "General");
|
|
@@ -1113,17 +1317,9 @@ function initPurgeGitHub() {
|
|
|
1113
1317
|
} else {
|
|
1114
1318
|
console.log(' CREATE: discussion "Talk to the repository" in General category');
|
|
1115
1319
|
if (!dryRun) {
|
|
1116
|
-
const
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
const createResult = execSync(`gh api graphql --input -`, {
|
|
1120
|
-
cwd: target,
|
|
1121
|
-
encoding: "utf8",
|
|
1122
|
-
timeout: 15000,
|
|
1123
|
-
input: createMutation,
|
|
1124
|
-
stdio: ["pipe", "pipe", "pipe"],
|
|
1125
|
-
});
|
|
1126
|
-
const createParsed = JSON.parse(createResult);
|
|
1320
|
+
const createParsed = ghGraphQL(
|
|
1321
|
+
`mutation { createDiscussion(input: { repositoryId: "${repoId}", categoryId: "${generalCat.id}", title: "Talk to the repository", body: "This discussion is the main channel for interacting with the repository's autonomous agents.\\n\\nUse this thread to:\\n- Submit feature requests or ideas\\n- Ask questions about the project\\n- Chat with the discussions bot\\n\\n---\\n*Created by init --purge*" }) { discussion { number url } } }`,
|
|
1322
|
+
);
|
|
1127
1323
|
const newDisc = createParsed?.data?.createDiscussion?.discussion;
|
|
1128
1324
|
if (newDisc) {
|
|
1129
1325
|
console.log(` CREATED: discussion #${newDisc.number} — ${newDisc.url}`);
|
|
@@ -1137,7 +1333,7 @@ function initPurgeGitHub() {
|
|
|
1137
1333
|
console.log(` SKIP: Could not create discussion (${err.message})`);
|
|
1138
1334
|
}
|
|
1139
1335
|
|
|
1140
|
-
// Enable GitHub Pages
|
|
1336
|
+
// ── Enable GitHub Pages ───────────────────────────────────────────
|
|
1141
1337
|
console.log("\n--- Enable GitHub Pages ---");
|
|
1142
1338
|
try {
|
|
1143
1339
|
if (!dryRun) {
|
|
@@ -1187,14 +1383,31 @@ function runInit() {
|
|
|
1187
1383
|
console.log(`Mode: ${dryRun ? "DRY RUN" : "LIVE"}`);
|
|
1188
1384
|
console.log("");
|
|
1189
1385
|
|
|
1386
|
+
// Capture existing init timestamp before any destructive operations (for idempotency).
|
|
1387
|
+
// The TOML [init] section is the authoritative record. If it matches the current
|
|
1388
|
+
// mode/mission/version, reuse its timestamp so that re-running init is a no-op.
|
|
1389
|
+
const pkg = JSON.parse(readFileSync(resolve(pkgRoot, "package.json"), "utf8"));
|
|
1390
|
+
let initTimestamp = null;
|
|
1391
|
+
const tomlPath = resolve(target, "agentic-lib.toml");
|
|
1392
|
+
if (existsSync(tomlPath)) {
|
|
1393
|
+
const tomlContent = readFileSync(tomlPath, "utf8");
|
|
1394
|
+
const tm = tomlContent.match(/^\[init\]\s*\ntimestamp\s*=\s*"([^"]+)"\s*\nmode\s*=\s*"([^"]+)"\s*\nmission\s*=\s*"([^"]+)"\s*\nversion\s*=\s*"([^"]+)"/m);
|
|
1395
|
+
const mode = purge ? "purge" : reseed ? "reseed" : null;
|
|
1396
|
+
if (tm && mode && tm[2] === mode && tm[3] === mission && tm[4] === pkg.version) {
|
|
1397
|
+
initTimestamp = tm[1];
|
|
1398
|
+
}
|
|
1399
|
+
}
|
|
1400
|
+
// Use a single timestamp for the entire init run (for consistency across files)
|
|
1401
|
+
if (!initTimestamp) initTimestamp = new Date().toISOString();
|
|
1402
|
+
|
|
1190
1403
|
initWorkflows();
|
|
1191
1404
|
initActions(agenticDir);
|
|
1192
1405
|
initDirContents("agents", resolve(agenticDir, "agents"), "Agents");
|
|
1193
1406
|
initDirContents("seeds", resolve(agenticDir, "seeds"), "Seeds");
|
|
1194
1407
|
initScripts(agenticDir);
|
|
1195
1408
|
initConfig(seedsDir);
|
|
1196
|
-
if (reseed) initReseed();
|
|
1197
|
-
if (purge) initPurge(seedsDir, mission);
|
|
1409
|
+
if (reseed) initReseed(initTimestamp);
|
|
1410
|
+
if (purge) initPurge(seedsDir, mission, initTimestamp);
|
|
1198
1411
|
if (purge) initPurgeGitHub();
|
|
1199
1412
|
|
|
1200
1413
|
console.log(`\n${initChanges} change(s)${dryRun ? " (dry run)" : ""}`);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@xn-intenton-z2a/agentic-lib",
|
|
3
|
-
"version": "7.1.
|
|
3
|
+
"version": "7.1.73",
|
|
4
4
|
"description": "Agentic-lib Agentic Coding Systems SDK powering automated GitHub workflows.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"scripts": {
|
|
@@ -19,6 +19,7 @@
|
|
|
19
19
|
"test:copilot": "node scripts/test-copilot-local.js",
|
|
20
20
|
"test:discussions": "node scripts/test-discussions-local.js",
|
|
21
21
|
"test:transform": "node scripts/test-transform-local.js ../repository0",
|
|
22
|
+
"test:spike-llm": "vitest --run tests/spike-local-llm.test.js",
|
|
22
23
|
"test:system": "bash scripts/system-test.sh --init-only",
|
|
23
24
|
"test:system:dry-run": "bash scripts/system-test.sh --dry-run",
|
|
24
25
|
"test:system:live": "bash scripts/system-test.sh",
|
|
@@ -40,6 +41,7 @@
|
|
|
40
41
|
"js-yaml": "^4.1.1",
|
|
41
42
|
"markdown-it": "^14.1.1",
|
|
42
43
|
"markdown-it-github": "^0.5.0",
|
|
44
|
+
"node-llama-cpp": "^3.17.1",
|
|
43
45
|
"prettier": "^3.8.1",
|
|
44
46
|
"vitest": "^4.0.18"
|
|
45
47
|
},
|
|
@@ -53,7 +53,7 @@ async function fetchDiscussion(octokit, discussionUrl, commentsLimit = 10) {
|
|
|
53
53
|
}
|
|
54
54
|
}
|
|
55
55
|
|
|
56
|
-
function buildPrompt(discussionUrl, discussion, context, t) {
|
|
56
|
+
function buildPrompt(discussionUrl, discussion, context, t, repoContext) {
|
|
57
57
|
const { config, instructions } = context;
|
|
58
58
|
const { title, body, comments } = discussion;
|
|
59
59
|
|
|
@@ -99,6 +99,28 @@ function buildPrompt(discussionUrl, discussion, context, t) {
|
|
|
99
99
|
`### Mission\n${mission}`,
|
|
100
100
|
contributing ? `### Contributing\n${contributing}` : "",
|
|
101
101
|
`### Current Features\n${featureNames.join(", ") || "none"}`,
|
|
102
|
+
);
|
|
103
|
+
|
|
104
|
+
// Add issue context
|
|
105
|
+
if (repoContext?.issuesSummary?.length > 0) {
|
|
106
|
+
parts.push(`### Open Issues (${repoContext.issuesSummary.length})`, repoContext.issuesSummary.join("\n"));
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// Add actions-since-init context
|
|
110
|
+
if (repoContext?.actionsSinceInit?.length > 0) {
|
|
111
|
+
parts.push(
|
|
112
|
+
`### Actions Since Last Init${repoContext.initTimestamp ? ` (${repoContext.initTimestamp})` : ""}`,
|
|
113
|
+
...repoContext.actionsSinceInit.map((a) => {
|
|
114
|
+
let line = `- ${a.name}: ${a.conclusion} (${a.created}) [${a.commitSha}] ${a.commitMessage}`;
|
|
115
|
+
if (a.prNumber) {
|
|
116
|
+
line += ` — PR #${a.prNumber}: +${a.additions}/-${a.deletions} in ${a.changedFiles} file(s)`;
|
|
117
|
+
}
|
|
118
|
+
return line;
|
|
119
|
+
}),
|
|
120
|
+
);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
parts.push(
|
|
102
124
|
recentActivity ? `### Recent Activity\n${recentActivity}` : "",
|
|
103
125
|
config.configToml ? `### Configuration (agentic-lib.toml)\n\`\`\`toml\n${config.configToml}\n\`\`\`` : "",
|
|
104
126
|
config.packageJson ? `### Dependencies (package.json)\n\`\`\`json\n${config.packageJson}\n\`\`\`` : "",
|
|
@@ -151,15 +173,72 @@ async function postReply(octokit, nodeId, replyBody) {
|
|
|
151
173
|
* @returns {Promise<Object>} Result with outcome, action, tokensUsed, model
|
|
152
174
|
*/
|
|
153
175
|
export async function discussions(context) {
|
|
154
|
-
const { octokit, model, discussionUrl } = context;
|
|
155
|
-
const t =
|
|
176
|
+
const { octokit, model, discussionUrl, repo, config } = context;
|
|
177
|
+
const t = config?.tuning || {};
|
|
156
178
|
|
|
157
179
|
if (!discussionUrl) {
|
|
158
180
|
throw new Error("discussions task requires discussion-url input");
|
|
159
181
|
}
|
|
160
182
|
|
|
183
|
+
// Gather repo context: issues + actions since init
|
|
184
|
+
const repoContext = { issuesSummary: [], actionsSinceInit: [], initTimestamp: null };
|
|
185
|
+
if (octokit && repo) {
|
|
186
|
+
try {
|
|
187
|
+
const { data: openIssues } = await octokit.rest.issues.listForRepo({
|
|
188
|
+
...repo, state: "open", per_page: 10, sort: "created", direction: "asc",
|
|
189
|
+
});
|
|
190
|
+
repoContext.issuesSummary = openIssues
|
|
191
|
+
.filter((i) => !i.pull_request)
|
|
192
|
+
.map((i) => {
|
|
193
|
+
const labels = i.labels.map((l) => l.name).join(", ");
|
|
194
|
+
return `#${i.number}: ${i.title} [${labels || "no labels"}]`;
|
|
195
|
+
});
|
|
196
|
+
} catch (err) {
|
|
197
|
+
core.warning(`Could not fetch issues for discussion context: ${err.message}`);
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
const initTimestamp = config?.init?.timestamp || null;
|
|
201
|
+
repoContext.initTimestamp = initTimestamp;
|
|
202
|
+
try {
|
|
203
|
+
const { data: runs } = await octokit.rest.actions.listWorkflowRunsForRepo({
|
|
204
|
+
...repo, per_page: 20,
|
|
205
|
+
});
|
|
206
|
+
const initDate = initTimestamp ? new Date(initTimestamp) : null;
|
|
207
|
+
const relevantRuns = initDate
|
|
208
|
+
? runs.workflow_runs.filter((r) => new Date(r.created_at) >= initDate)
|
|
209
|
+
: runs.workflow_runs.slice(0, 10);
|
|
210
|
+
|
|
211
|
+
for (const run of relevantRuns) {
|
|
212
|
+
const commit = run.head_commit;
|
|
213
|
+
const entry = {
|
|
214
|
+
name: run.name,
|
|
215
|
+
conclusion: run.conclusion || run.status,
|
|
216
|
+
created: run.created_at,
|
|
217
|
+
commitMessage: commit?.message?.split("\n")[0] || "",
|
|
218
|
+
commitSha: run.head_sha?.substring(0, 7) || "",
|
|
219
|
+
};
|
|
220
|
+
if (run.head_branch?.startsWith("agentic-lib-issue-")) {
|
|
221
|
+
try {
|
|
222
|
+
const { data: prs } = await octokit.rest.pulls.list({
|
|
223
|
+
...repo, head: `${repo.owner}:${run.head_branch}`, state: "all", per_page: 1,
|
|
224
|
+
});
|
|
225
|
+
if (prs.length > 0) {
|
|
226
|
+
entry.prNumber = prs[0].number;
|
|
227
|
+
entry.additions = prs[0].additions;
|
|
228
|
+
entry.deletions = prs[0].deletions;
|
|
229
|
+
entry.changedFiles = prs[0].changed_files;
|
|
230
|
+
}
|
|
231
|
+
} catch { /* ignore */ }
|
|
232
|
+
}
|
|
233
|
+
repoContext.actionsSinceInit.push(entry);
|
|
234
|
+
}
|
|
235
|
+
} catch (err) {
|
|
236
|
+
core.warning(`Could not fetch workflow runs for discussion context: ${err.message}`);
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
|
|
161
240
|
const discussion = await fetchDiscussion(octokit, discussionUrl, t.discussionComments || 10);
|
|
162
|
-
const prompt = buildPrompt(discussionUrl, discussion, context, t);
|
|
241
|
+
const prompt = buildPrompt(discussionUrl, discussion, context, t, repoContext);
|
|
163
242
|
const { content, tokensUsed, inputTokens, outputTokens, cost } = await runCopilotTask({
|
|
164
243
|
model,
|
|
165
244
|
systemMessage:
|
|
@@ -205,33 +284,44 @@ export async function discussions(context) {
|
|
|
205
284
|
}
|
|
206
285
|
}
|
|
207
286
|
|
|
287
|
+
// Guard: never dispatch workflows from the SDK repo itself (agentic-lib)
|
|
288
|
+
const isSdkRepo = process.env.GITHUB_REPOSITORY === "xn-intenton-z2a/agentic-lib";
|
|
289
|
+
|
|
208
290
|
// Request supervisor evaluation
|
|
209
291
|
if (action === "request-supervisor") {
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
292
|
+
if (isSdkRepo) {
|
|
293
|
+
core.info("Skipping supervisor dispatch — running in SDK repo");
|
|
294
|
+
} else {
|
|
295
|
+
try {
|
|
296
|
+
await octokit.rest.actions.createWorkflowDispatch({
|
|
297
|
+
...context.repo,
|
|
298
|
+
workflow_id: "agentic-lib-workflow.yml",
|
|
299
|
+
ref: "main",
|
|
300
|
+
inputs: { message: actionArg || "Discussion bot referral" },
|
|
301
|
+
});
|
|
302
|
+
core.info(`Dispatched supervisor with message: ${actionArg}`);
|
|
303
|
+
} catch (err) {
|
|
304
|
+
core.warning(`Failed to dispatch supervisor: ${err.message}`);
|
|
305
|
+
}
|
|
220
306
|
}
|
|
221
307
|
}
|
|
222
308
|
|
|
223
309
|
// Stop automation
|
|
224
310
|
if (action === "stop") {
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
311
|
+
if (isSdkRepo) {
|
|
312
|
+
core.info("Skipping schedule dispatch — running in SDK repo");
|
|
313
|
+
} else {
|
|
314
|
+
try {
|
|
315
|
+
await octokit.rest.actions.createWorkflowDispatch({
|
|
316
|
+
...context.repo,
|
|
317
|
+
workflow_id: "agentic-lib-schedule.yml",
|
|
318
|
+
ref: "main",
|
|
319
|
+
inputs: { frequency: "off" },
|
|
320
|
+
});
|
|
321
|
+
core.info("Automation stopped via discussions bot");
|
|
322
|
+
} catch (err) {
|
|
323
|
+
core.warning(`Failed to stop automation: ${err.message}`);
|
|
324
|
+
}
|
|
235
325
|
}
|
|
236
326
|
}
|
|
237
327
|
|
|
@@ -60,6 +60,24 @@ async function reviewSingleIssue({ octokit, repo, config, targetIssueNumber, ins
|
|
|
60
60
|
|
|
61
61
|
const agentInstructions = instructions || "Review whether this issue has been resolved by the current codebase.";
|
|
62
62
|
|
|
63
|
+
// Gather recent commits since init for context
|
|
64
|
+
let recentCommitsSummary = [];
|
|
65
|
+
try {
|
|
66
|
+
const initTimestamp = config.init?.timestamp || null;
|
|
67
|
+
const since = initTimestamp || new Date(Date.now() - 7 * 86400000).toISOString();
|
|
68
|
+
const { data: commits } = await octokit.rest.repos.listCommits({
|
|
69
|
+
...repo,
|
|
70
|
+
sha: "main",
|
|
71
|
+
since,
|
|
72
|
+
per_page: 10,
|
|
73
|
+
});
|
|
74
|
+
recentCommitsSummary = commits.map((c) => {
|
|
75
|
+
const msg = c.commit.message.split("\n")[0];
|
|
76
|
+
const sha = c.sha.substring(0, 7);
|
|
77
|
+
return `- [${sha}] ${msg} (${c.commit.author?.date || ""})`;
|
|
78
|
+
});
|
|
79
|
+
} catch { /* ignore */ }
|
|
80
|
+
|
|
63
81
|
const prompt = [
|
|
64
82
|
"## Instructions",
|
|
65
83
|
agentInstructions,
|
|
@@ -84,6 +102,9 @@ async function reviewSingleIssue({ octokit, repo, config, targetIssueNumber, ins
|
|
|
84
102
|
...(docsFiles.length > 0
|
|
85
103
|
? [`## Documentation (${docsFiles.length} files)`, ...docsFiles.map((f) => `- ${f.name}`), ""]
|
|
86
104
|
: []),
|
|
105
|
+
...(recentCommitsSummary.length > 0
|
|
106
|
+
? [`## Recent Commits (since init)`, ...recentCommitsSummary, ""]
|
|
107
|
+
: []),
|
|
87
108
|
config.configToml ? `## Configuration (agentic-lib.toml)\n\`\`\`toml\n${config.configToml}\n\`\`\`` : "",
|
|
88
109
|
config.packageJson ? `## Dependencies (package.json)\n\`\`\`json\n${config.packageJson}\n\`\`\`` : "",
|
|
89
110
|
"",
|
|
@@ -110,13 +110,55 @@ async function gatherContext(octokit, repo, config, t) {
|
|
|
110
110
|
return `#${pr.number}: ${pr.title} (${pr.head.ref}) [${labels || "no labels"}] (${age}d old)`;
|
|
111
111
|
});
|
|
112
112
|
|
|
113
|
+
// Read init timestamp for epoch boundary
|
|
114
|
+
const initTimestamp = config.init?.timestamp || null;
|
|
115
|
+
|
|
113
116
|
let workflowsSummary = [];
|
|
117
|
+
let actionsSinceInit = [];
|
|
114
118
|
try {
|
|
115
119
|
const { data: runs } = await octokit.rest.actions.listWorkflowRunsForRepo({
|
|
116
120
|
...repo,
|
|
117
|
-
per_page:
|
|
121
|
+
per_page: 20,
|
|
118
122
|
});
|
|
119
123
|
workflowsSummary = runs.workflow_runs.map((r) => `${r.name}: ${r.conclusion || r.status} (${r.created_at})`);
|
|
124
|
+
|
|
125
|
+
// Build detailed actions-since-init with commit context
|
|
126
|
+
const initDate = initTimestamp ? new Date(initTimestamp) : null;
|
|
127
|
+
const relevantRuns = initDate
|
|
128
|
+
? runs.workflow_runs.filter((r) => new Date(r.created_at) >= initDate)
|
|
129
|
+
: runs.workflow_runs.slice(0, 10);
|
|
130
|
+
|
|
131
|
+
for (const run of relevantRuns) {
|
|
132
|
+
const commit = run.head_commit;
|
|
133
|
+
const entry = {
|
|
134
|
+
name: run.name,
|
|
135
|
+
conclusion: run.conclusion || run.status,
|
|
136
|
+
created: run.created_at,
|
|
137
|
+
commitMessage: commit?.message?.split("\n")[0] || "",
|
|
138
|
+
commitSha: run.head_sha?.substring(0, 7) || "",
|
|
139
|
+
branch: run.head_branch || "",
|
|
140
|
+
};
|
|
141
|
+
|
|
142
|
+
// For transform branches, try to get PR change stats
|
|
143
|
+
if (run.head_branch?.startsWith("agentic-lib-issue-")) {
|
|
144
|
+
try {
|
|
145
|
+
const { data: prs } = await octokit.rest.pulls.list({
|
|
146
|
+
...repo,
|
|
147
|
+
head: `${repo.owner}:${run.head_branch}`,
|
|
148
|
+
state: "all",
|
|
149
|
+
per_page: 1,
|
|
150
|
+
});
|
|
151
|
+
if (prs.length > 0) {
|
|
152
|
+
entry.prNumber = prs[0].number;
|
|
153
|
+
entry.prTitle = prs[0].title;
|
|
154
|
+
entry.additions = prs[0].additions;
|
|
155
|
+
entry.deletions = prs[0].deletions;
|
|
156
|
+
entry.changedFiles = prs[0].changed_files;
|
|
157
|
+
}
|
|
158
|
+
} catch { /* ignore */ }
|
|
159
|
+
}
|
|
160
|
+
actionsSinceInit.push(entry);
|
|
161
|
+
}
|
|
120
162
|
} catch (err) {
|
|
121
163
|
core.warning(`Could not fetch workflow runs: ${err.message}`);
|
|
122
164
|
}
|
|
@@ -132,6 +174,8 @@ async function gatherContext(octokit, repo, config, t) {
|
|
|
132
174
|
oldestReadyIssue,
|
|
133
175
|
prsSummary,
|
|
134
176
|
workflowsSummary,
|
|
177
|
+
actionsSinceInit,
|
|
178
|
+
initTimestamp,
|
|
135
179
|
supervisor: config.supervisor,
|
|
136
180
|
configToml: config.configToml,
|
|
137
181
|
packageJson: config.packageJson,
|
|
@@ -175,6 +219,20 @@ function buildPrompt(ctx, agentInstructions) {
|
|
|
175
219
|
`### Recent Workflow Runs`,
|
|
176
220
|
ctx.workflowsSummary.join("\n") || "none",
|
|
177
221
|
"",
|
|
222
|
+
...(ctx.actionsSinceInit.length > 0
|
|
223
|
+
? [
|
|
224
|
+
`### Actions Since Last Init${ctx.initTimestamp ? ` (${ctx.initTimestamp})` : ""}`,
|
|
225
|
+
"Each entry: workflow | outcome | commit | branch | changes",
|
|
226
|
+
...ctx.actionsSinceInit.map((a) => {
|
|
227
|
+
let line = `- ${a.name}: ${a.conclusion} (${a.created}) [${a.commitSha}] ${a.commitMessage}`;
|
|
228
|
+
if (a.prNumber) {
|
|
229
|
+
line += ` — PR #${a.prNumber}: +${a.additions}/-${a.deletions} in ${a.changedFiles} file(s)`;
|
|
230
|
+
}
|
|
231
|
+
return line;
|
|
232
|
+
}),
|
|
233
|
+
"",
|
|
234
|
+
]
|
|
235
|
+
: []),
|
|
178
236
|
`### Recent Activity`,
|
|
179
237
|
ctx.recentActivity || "none",
|
|
180
238
|
"",
|
|
@@ -289,6 +347,12 @@ async function executeDispatch(octokit, repo, actionName, params) {
|
|
|
289
347
|
if (params["pr-number"]) inputs["pr-number"] = params["pr-number"];
|
|
290
348
|
if (params["issue-number"]) inputs["issue-number"] = params["issue-number"];
|
|
291
349
|
|
|
350
|
+
// Guard: never dispatch workflows from the SDK repo itself (agentic-lib)
|
|
351
|
+
if (process.env.GITHUB_REPOSITORY === "xn-intenton-z2a/agentic-lib") {
|
|
352
|
+
core.info(`Skipping dispatch of ${workflowFile} — running in SDK repo`);
|
|
353
|
+
return `skipped:sdk-repo:${workflowFile}`;
|
|
354
|
+
}
|
|
355
|
+
|
|
292
356
|
// Guard: skip transform dispatch if one is already running
|
|
293
357
|
if (workflowFile === "agentic-lib-workflow.yml") {
|
|
294
358
|
try {
|
|
@@ -370,6 +434,10 @@ async function executeRespondDiscussions(octokit, repo, params) {
|
|
|
370
434
|
const message = params.message || "";
|
|
371
435
|
const url = params["discussion-url"] || "";
|
|
372
436
|
if (message) {
|
|
437
|
+
if (process.env.GITHUB_REPOSITORY === "xn-intenton-z2a/agentic-lib") {
|
|
438
|
+
core.info("Skipping bot dispatch — running in SDK repo");
|
|
439
|
+
return `skipped:sdk-repo:respond-discussions`;
|
|
440
|
+
}
|
|
373
441
|
core.info(`Dispatching discussions bot with response: ${message.substring(0, 100)}`);
|
|
374
442
|
const inputs = { message };
|
|
375
443
|
if (url) inputs["discussion-url"] = url;
|
|
@@ -397,15 +465,17 @@ async function executeMissionComplete(octokit, repo, params) {
|
|
|
397
465
|
].join("\n");
|
|
398
466
|
writeFileSync("MISSION_COMPLETE.md", signal);
|
|
399
467
|
core.info(`Mission complete signal written: ${reason}`);
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
468
|
+
if (process.env.GITHUB_REPOSITORY !== "xn-intenton-z2a/agentic-lib") {
|
|
469
|
+
try {
|
|
470
|
+
await octokit.rest.actions.createWorkflowDispatch({
|
|
471
|
+
...repo,
|
|
472
|
+
workflow_id: "agentic-lib-schedule.yml",
|
|
473
|
+
ref: "main",
|
|
474
|
+
inputs: { frequency: "off" },
|
|
475
|
+
});
|
|
476
|
+
} catch (err) {
|
|
477
|
+
core.warning(`Could not set schedule to off: ${err.message}`);
|
|
478
|
+
}
|
|
409
479
|
}
|
|
410
480
|
return `mission-complete:${reason.substring(0, 100)}`;
|
|
411
481
|
}
|
|
@@ -423,15 +493,17 @@ async function executeMissionFailed(octokit, repo, params) {
|
|
|
423
493
|
].join("\n");
|
|
424
494
|
writeFileSync("MISSION_FAILED.md", signal);
|
|
425
495
|
core.info(`Mission failed signal written: ${reason}`);
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
496
|
+
if (process.env.GITHUB_REPOSITORY !== "xn-intenton-z2a/agentic-lib") {
|
|
497
|
+
try {
|
|
498
|
+
await octokit.rest.actions.createWorkflowDispatch({
|
|
499
|
+
...repo,
|
|
500
|
+
workflow_id: "agentic-lib-schedule.yml",
|
|
501
|
+
ref: "main",
|
|
502
|
+
inputs: { frequency: "off" },
|
|
503
|
+
});
|
|
504
|
+
} catch (err) {
|
|
505
|
+
core.warning(`Could not set schedule to off: ${err.message}`);
|
|
506
|
+
}
|
|
435
507
|
}
|
|
436
508
|
return `mission-failed:${reason.substring(0, 100)}`;
|
|
437
509
|
}
|
|
@@ -450,6 +522,10 @@ async function executeSetSchedule(octokit, repo, frequency) {
|
|
|
450
522
|
if (!valid.includes(frequency)) {
|
|
451
523
|
return `skipped:invalid-frequency:${frequency}`;
|
|
452
524
|
}
|
|
525
|
+
if (process.env.GITHUB_REPOSITORY === "xn-intenton-z2a/agentic-lib") {
|
|
526
|
+
core.info(`Skipping schedule dispatch — running in SDK repo`);
|
|
527
|
+
return `skipped:sdk-repo:set-schedule:${frequency}`;
|
|
528
|
+
}
|
|
453
529
|
core.info(`Setting supervisor schedule to: ${frequency}`);
|
|
454
530
|
await octokit.rest.actions.createWorkflowDispatch({
|
|
455
531
|
...repo,
|
|
@@ -26,6 +26,8 @@ runs:
|
|
|
26
26
|
git config --local user.email 'action@github.com'
|
|
27
27
|
git config --local user.name 'GitHub Actions[bot]'
|
|
28
28
|
git add -A
|
|
29
|
+
# Unstage workflow files — GITHUB_TOKEN cannot push workflow changes
|
|
30
|
+
git reset HEAD -- '.github/workflows/' 2>/dev/null || true
|
|
29
31
|
# Unstage log files on non-default branches to avoid merge conflicts
|
|
30
32
|
REF="${{ inputs.push-ref }}"
|
|
31
33
|
if [ -n "$REF" ] && [ "$REF" != "main" ] && [ "$REF" != "master" ]; then
|
|
@@ -14,9 +14,6 @@
|
|
|
14
14
|
.identity { background: #f6f8fa; padding: 1rem; border-radius: 6px; margin: 1rem 0; font-family: monospace; }
|
|
15
15
|
.identity dt { font-weight: bold; color: #666; }
|
|
16
16
|
.identity dd { margin: 0 0 0.5rem 0; font-size: 1.1rem; }
|
|
17
|
-
.social { display: flex; gap: 1rem; margin: 1rem 0; flex-wrap: wrap; }
|
|
18
|
-
.social a { display: inline-block; padding: 0.4rem 0.8rem; border: 1px solid #ddd; border-radius: 4px; text-decoration: none; font-size: 0.9rem; }
|
|
19
|
-
.social a:hover { background: #f6f8fa; }
|
|
20
17
|
#demo-output { background: #1e1e1e; color: #d4d4d4; padding: 1rem; border-radius: 6px; font-family: monospace; white-space: pre-wrap; min-height: 2rem; }
|
|
21
18
|
</style>
|
|
22
19
|
</head>
|
|
@@ -32,12 +29,6 @@
|
|
|
32
29
|
</div>
|
|
33
30
|
<h2>Output</h2>
|
|
34
31
|
<div id="demo-output">Loading library...</div>
|
|
35
|
-
<h2>Links</h2>
|
|
36
|
-
<div class="social">
|
|
37
|
-
<a id="share-github" href="https://github.com/xn-intenton-z2a/repository0" target="_blank" rel="noopener">GitHub</a>
|
|
38
|
-
<a id="share-twitter" href="#" target="_blank" rel="noopener">X/Twitter</a>
|
|
39
|
-
<a id="share-linkedin" href="#" target="_blank" rel="noopener">LinkedIn</a>
|
|
40
|
-
</div>
|
|
41
32
|
<h2>About</h2>
|
|
42
33
|
<p>This website uses the library. See <a href="https://github.com/xn-intenton-z2a/repository0">the repository</a> for source code and mission details.</p>
|
|
43
34
|
<script type="module">
|
|
@@ -48,10 +39,6 @@
|
|
|
48
39
|
document.getElementById('lib-description').textContent = description || '(no description)';
|
|
49
40
|
document.getElementById('demo-output').textContent = JSON.stringify({ name, version, description }, null, 2);
|
|
50
41
|
document.title = name + ' v' + version;
|
|
51
|
-
const pageUrl = encodeURIComponent(window.location.href);
|
|
52
|
-
const text = encodeURIComponent('Check out ' + name + ' v' + version + ' — ' + description);
|
|
53
|
-
document.getElementById('share-twitter').href = 'https://x.com/intent/tweet?url=' + pageUrl + '&text=' + text;
|
|
54
|
-
document.getElementById('share-linkedin').href = 'https://www.linkedin.com/sharing/share-offsite/?url=' + pageUrl;
|
|
55
42
|
} catch (e) {
|
|
56
43
|
document.getElementById('lib-version').textContent = '(build required)';
|
|
57
44
|
document.getElementById('lib-description').textContent = 'Run npm run build:web to generate library metadata';
|
|
@@ -25,9 +25,4 @@ describe("Website", () => {
|
|
|
25
25
|
expect(html).toContain("lib-name");
|
|
26
26
|
expect(html).toContain("lib-version");
|
|
27
27
|
});
|
|
28
|
-
|
|
29
|
-
test("index.html has social share links", () => {
|
|
30
|
-
const html = readFileSync("src/web/index.html", "utf8");
|
|
31
|
-
expect(html).toMatch(/share.*(twitter|x\.com|linkedin)/is);
|
|
32
|
-
});
|
|
33
28
|
});
|