@open330/oac 2026.4.3 → 2026.220.2
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/CHANGELOG.md +115 -0
- package/README.md +2 -2
- package/dist/{chunk-YWIB3TRI.js → chunk-6A37SKAJ.js} +15 -2
- package/dist/chunk-6A37SKAJ.js.map +1 -0
- package/dist/{chunk-HDMLNOND.js → chunk-6JEFW6B7.js} +92 -20
- package/dist/chunk-6JEFW6B7.js.map +1 -0
- package/dist/{chunk-ZRYAHZQJ.js → chunk-7C7SC4TZ.js} +1 -1
- package/dist/{chunk-XUW3XWTX.js → chunk-LRTQCVQK.js} +145 -121
- package/dist/chunk-LRTQCVQK.js.map +1 -0
- package/dist/{chunk-CJAJ4MBO.js → chunk-OS3XDHOJ.js} +57 -18
- package/dist/chunk-OS3XDHOJ.js.map +1 -0
- package/dist/{chunk-7FWC3Z4W.js → chunk-QPVNC7S4.js} +479 -196
- package/dist/chunk-QPVNC7S4.js.map +1 -0
- package/dist/cli/cli.js +6 -6
- package/dist/cli/index.js +6 -6
- package/dist/completion/index.d.ts +5 -0
- package/dist/completion/index.js +49 -8
- package/dist/completion/index.js.map +1 -1
- package/dist/core/index.d.ts +16 -1
- package/dist/core/index.js +8 -4
- package/dist/dashboard/index.js +33 -24
- package/dist/dashboard/index.js.map +1 -1
- package/dist/discovery/index.d.ts +18 -2
- package/dist/discovery/index.js +4 -2
- package/dist/execution/index.d.ts +45 -1
- package/dist/execution/index.js +7 -3
- package/dist/repo/index.d.ts +2 -2
- package/dist/repo/index.js +1 -1
- package/dist/{types-CYCwgojB.d.ts → types-3_IAAxh5.d.ts} +1 -0
- package/docs/config-reference.md +271 -0
- package/docs/multi-agent-support-technical-spec.md +312 -0
- package/package.json +23 -18
- package/dist/chunk-7FWC3Z4W.js.map +0 -1
- package/dist/chunk-CJAJ4MBO.js.map +0 -1
- package/dist/chunk-HDMLNOND.js.map +0 -1
- package/dist/chunk-XUW3XWTX.js.map +0 -1
- package/dist/chunk-YWIB3TRI.js.map +0 -1
- /package/dist/{chunk-ZRYAHZQJ.js.map → chunk-7C7SC4TZ.js.map} +0 -0
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
cloneRepo,
|
|
3
3
|
resolveRepo
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-OS3XDHOJ.js";
|
|
5
5
|
import {
|
|
6
6
|
CompositeScanner,
|
|
7
7
|
GitHubIssuesScanner,
|
|
@@ -9,6 +9,7 @@ import {
|
|
|
9
9
|
TestGapScanner,
|
|
10
10
|
TodoScanner,
|
|
11
11
|
analyzeCodebase,
|
|
12
|
+
buildScanners,
|
|
12
13
|
createBacklog,
|
|
13
14
|
getPendingEpics,
|
|
14
15
|
groupFindingsIntoEpics,
|
|
@@ -19,7 +20,7 @@ import {
|
|
|
19
20
|
persistContext,
|
|
20
21
|
rankTasks,
|
|
21
22
|
updateBacklog
|
|
22
|
-
} from "./chunk-
|
|
23
|
+
} from "./chunk-6JEFW6B7.js";
|
|
23
24
|
import {
|
|
24
25
|
buildEpicExecutionPlan,
|
|
25
26
|
buildExecutionPlan,
|
|
@@ -27,17 +28,20 @@ import {
|
|
|
27
28
|
estimateTokens
|
|
28
29
|
} from "./chunk-5GAUWC3L.js";
|
|
29
30
|
import {
|
|
30
|
-
|
|
31
|
-
CodexAdapter,
|
|
31
|
+
adapterRegistry,
|
|
32
32
|
createSandbox,
|
|
33
33
|
epicAsTask,
|
|
34
34
|
executeTask
|
|
35
|
-
} from "./chunk-
|
|
35
|
+
} from "./chunk-QPVNC7S4.js";
|
|
36
36
|
import {
|
|
37
37
|
UNLIMITED_BUDGET,
|
|
38
38
|
createEventBus,
|
|
39
39
|
loadConfig
|
|
40
|
-
} from "./chunk-
|
|
40
|
+
} from "./chunk-7C7SC4TZ.js";
|
|
41
|
+
import {
|
|
42
|
+
isRecord,
|
|
43
|
+
truncate
|
|
44
|
+
} from "./chunk-6A37SKAJ.js";
|
|
41
45
|
import {
|
|
42
46
|
contributionLogSchema,
|
|
43
47
|
writeContributionLog
|
|
@@ -51,7 +55,7 @@ import Table from "cli-table3";
|
|
|
51
55
|
import { Command as Command2 } from "commander";
|
|
52
56
|
|
|
53
57
|
// src/cli/github-auth.ts
|
|
54
|
-
import { execFileSync } from "child_process";
|
|
58
|
+
import { execFileSync, spawnSync } from "child_process";
|
|
55
59
|
function ensureGitHubAuth() {
|
|
56
60
|
const githubToken = process.env.GITHUB_TOKEN?.trim();
|
|
57
61
|
if (githubToken) {
|
|
@@ -79,24 +83,12 @@ function ensureGitHubAuth() {
|
|
|
79
83
|
}
|
|
80
84
|
function checkGitHubScopes(required = ["repo"]) {
|
|
81
85
|
try {
|
|
82
|
-
const
|
|
86
|
+
const result = spawnSync("gh", ["auth", "status"], {
|
|
83
87
|
timeout: 5e3,
|
|
84
88
|
encoding: "utf-8",
|
|
85
|
-
// Merge stderr into stdout so we can read scope info
|
|
86
89
|
stdio: ["ignore", "pipe", "pipe"]
|
|
87
90
|
});
|
|
88
|
-
|
|
89
|
-
if (!combined.includes("Token scopes")) {
|
|
90
|
-
try {
|
|
91
|
-
combined = execFileSync("sh", ["-c", "gh auth status 2>&1"], {
|
|
92
|
-
timeout: 5e3,
|
|
93
|
-
encoding: "utf-8",
|
|
94
|
-
stdio: ["ignore", "pipe", "ignore"]
|
|
95
|
-
});
|
|
96
|
-
} catch {
|
|
97
|
-
return [];
|
|
98
|
-
}
|
|
99
|
-
}
|
|
91
|
+
const combined = `${result.stdout ?? ""}${result.stderr ?? ""}`;
|
|
100
92
|
const scopeLine = combined.match(/Token scopes:\s*(.+)/);
|
|
101
93
|
if (!scopeLine) return [];
|
|
102
94
|
const scopes = scopeLine[1].split(",").map((s) => s.trim().replace(/^'|'$/g, ""));
|
|
@@ -110,14 +102,15 @@ function checkGitHubScopes(required = ["repo"]) {
|
|
|
110
102
|
import chalk, { Chalk } from "chalk";
|
|
111
103
|
import "commander";
|
|
112
104
|
import ora from "ora";
|
|
105
|
+
import PQueue from "p-queue";
|
|
113
106
|
|
|
114
107
|
// src/cli/config-loader.ts
|
|
115
108
|
import { constants as fsConstants } from "fs";
|
|
116
109
|
import { access, readFile } from "fs/promises";
|
|
117
110
|
import { resolve } from "path";
|
|
118
111
|
import { pathToFileURL } from "url";
|
|
119
|
-
var
|
|
120
|
-
var
|
|
112
|
+
var DEFINE_CONFIG_IMPORT = /@(?:open330\/oac(?:-core)?|oac\/core)/;
|
|
113
|
+
var DEFINE_CONFIG_IMPORT_LINE = /^\s*import\s*\{\s*defineConfig\s*\}\s*from\s*["']@(?:open330\/oac(?:-core)?|oac\/core)["'];\s*$/m;
|
|
121
114
|
var LEGACY_DEFINE_CONFIG_EXPORT = /export\s+default\s+defineConfig\s*\(/;
|
|
122
115
|
async function loadOptionalConfigFile(configPath, options = {}) {
|
|
123
116
|
const absolutePath = resolve(options.cwd ?? process.cwd(), configPath);
|
|
@@ -161,16 +154,19 @@ function shouldTryLegacyDefineConfigFallback(error) {
|
|
|
161
154
|
if (!(error instanceof Error)) {
|
|
162
155
|
return false;
|
|
163
156
|
}
|
|
164
|
-
|
|
157
|
+
if (error.code === "ERR_UNKNOWN_FILE_EXTENSION") {
|
|
158
|
+
return true;
|
|
159
|
+
}
|
|
160
|
+
return DEFINE_CONFIG_IMPORT.test(error.message);
|
|
165
161
|
}
|
|
166
162
|
function transformLegacyDefineConfigSource(source) {
|
|
167
|
-
if (!
|
|
163
|
+
if (!DEFINE_CONFIG_IMPORT_LINE.test(source)) {
|
|
168
164
|
return null;
|
|
169
165
|
}
|
|
170
166
|
if (!LEGACY_DEFINE_CONFIG_EXPORT.test(source)) {
|
|
171
167
|
return null;
|
|
172
168
|
}
|
|
173
|
-
return source.replace(
|
|
169
|
+
return source.replace(DEFINE_CONFIG_IMPORT_LINE, "").replace(LEGACY_DEFINE_CONFIG_EXPORT, "export default (");
|
|
174
170
|
}
|
|
175
171
|
async function importCandidate(moduleSpecifier) {
|
|
176
172
|
const imported = await import(moduleSpecifier);
|
|
@@ -217,12 +213,6 @@ function parseInteger(value) {
|
|
|
217
213
|
function formatInteger(value) {
|
|
218
214
|
return new Intl.NumberFormat("en-US").format(value);
|
|
219
215
|
}
|
|
220
|
-
function truncate(value, maxLength) {
|
|
221
|
-
if (value.length <= maxLength) {
|
|
222
|
-
return value;
|
|
223
|
-
}
|
|
224
|
-
return `${value.slice(0, Math.max(0, maxLength - 3))}...`;
|
|
225
|
-
}
|
|
226
216
|
async function loadOptionalConfig(configPath, verbose, ui) {
|
|
227
217
|
return loadOptionalConfigFile(configPath, {
|
|
228
218
|
onWarning: verbose ? (message) => {
|
|
@@ -263,13 +253,16 @@ function resolveBudget(tokensOption, config) {
|
|
|
263
253
|
async function estimateTaskMap(tasks, providerId, onProgress) {
|
|
264
254
|
let completed = 0;
|
|
265
255
|
const total = tasks.length;
|
|
256
|
+
const queue = new PQueue({ concurrency: 10 });
|
|
266
257
|
const entries = await Promise.all(
|
|
267
|
-
tasks.map(
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
258
|
+
tasks.map(
|
|
259
|
+
(task) => queue.add(async () => {
|
|
260
|
+
const estimate = await estimateTokens(task, providerId);
|
|
261
|
+
completed += 1;
|
|
262
|
+
onProgress?.(completed, total);
|
|
263
|
+
return [task.id, estimate];
|
|
264
|
+
})
|
|
265
|
+
)
|
|
273
266
|
);
|
|
274
267
|
return new Map(entries);
|
|
275
268
|
}
|
|
@@ -382,14 +375,7 @@ function normalizeOutputFormat(value) {
|
|
|
382
375
|
throw new Error(`Unsupported --format value "${value}". Use "table" or "json".`);
|
|
383
376
|
}
|
|
384
377
|
function buildScannerList(config, hasGitHubAuth) {
|
|
385
|
-
|
|
386
|
-
const lint = config?.discovery.scanners.lint ?? true;
|
|
387
|
-
const todo = config?.discovery.scanners.todo ?? true;
|
|
388
|
-
if (lint) scanners.push(new LintScanner());
|
|
389
|
-
if (todo) scanners.push(new TodoScanner());
|
|
390
|
-
scanners.push(new TestGapScanner());
|
|
391
|
-
if (hasGitHubAuth) scanners.push(new GitHubIssuesScanner());
|
|
392
|
-
return scanners;
|
|
378
|
+
return buildScanners(config, hasGitHubAuth).instances;
|
|
393
379
|
}
|
|
394
380
|
|
|
395
381
|
// src/cli/commands/completion.ts
|
|
@@ -598,6 +584,16 @@ async function runDoctorChecks() {
|
|
|
598
584
|
status: codexResult.ok ? "pass" : "fail",
|
|
599
585
|
message: codexResult.ok ? void 0 : explainCommandFailure("codex", codexResult)
|
|
600
586
|
});
|
|
587
|
+
const opencodeResult = await runCommand("opencode", ["--version"]);
|
|
588
|
+
const opencodeVersion = extractVersion(opencodeResult.stdout) ?? "--";
|
|
589
|
+
checks.push({
|
|
590
|
+
id: "opencode",
|
|
591
|
+
name: "OpenCode CLI",
|
|
592
|
+
requirement: "installed",
|
|
593
|
+
value: opencodeVersion,
|
|
594
|
+
status: opencodeResult.ok ? "pass" : "fail",
|
|
595
|
+
message: opencodeResult.ok ? void 0 : explainCommandFailure("opencode", opencodeResult)
|
|
596
|
+
});
|
|
601
597
|
return checks;
|
|
602
598
|
}
|
|
603
599
|
async function checkGithubAuth() {
|
|
@@ -709,7 +705,7 @@ async function runCommand(command, args) {
|
|
|
709
705
|
});
|
|
710
706
|
return {
|
|
711
707
|
ok: result.exitCode === 0,
|
|
712
|
-
exitCode: result.exitCode,
|
|
708
|
+
exitCode: result.exitCode ?? null,
|
|
713
709
|
stdout: result.stdout,
|
|
714
710
|
stderr: result.stderr
|
|
715
711
|
};
|
|
@@ -780,15 +776,15 @@ function createExplainCommand() {
|
|
|
780
776
|
console.log(` ${ui.blue("Scope:")} ${epic.scope}`);
|
|
781
777
|
console.log(` ${ui.blue("Priority:")} ${epic.priority}`);
|
|
782
778
|
console.log(` ${ui.blue("Status:")} ${epic.status}`);
|
|
783
|
-
console.log(` ${ui.blue("Tasks:")} ${epic.
|
|
779
|
+
console.log(` ${ui.blue("Tasks:")} ${epic.subtasks.length}`);
|
|
784
780
|
console.log("");
|
|
785
781
|
console.log(ui.dim("Description:"));
|
|
786
782
|
console.log(` ${epic.description}`);
|
|
787
|
-
if (epic.
|
|
783
|
+
if (epic.subtasks.length > 0) {
|
|
788
784
|
console.log("");
|
|
789
785
|
console.log(ui.dim("Task IDs:"));
|
|
790
|
-
for (const
|
|
791
|
-
console.log(` - ${
|
|
786
|
+
for (const subtask of epic.subtasks) {
|
|
787
|
+
console.log(` - ${subtask.id}`);
|
|
792
788
|
}
|
|
793
789
|
}
|
|
794
790
|
}
|
|
@@ -854,7 +850,7 @@ function printAvailableIds(ui, context, backlog) {
|
|
|
854
850
|
|
|
855
851
|
// src/cli/commands/init.ts
|
|
856
852
|
import { constants as fsConstants2 } from "fs";
|
|
857
|
-
import { access as access2, mkdir, writeFile } from "fs/promises";
|
|
853
|
+
import { access as access2, mkdir, readFile as readFile2, writeFile } from "fs/promises";
|
|
858
854
|
import { resolve as resolve3 } from "path";
|
|
859
855
|
import { checkbox, confirm, input } from "@inquirer/prompts";
|
|
860
856
|
import { Command as Command6 } from "commander";
|
|
@@ -919,6 +915,7 @@ async function runMinimalInit(options, globalOptions, ui) {
|
|
|
919
915
|
});
|
|
920
916
|
await writeFile(configPath, configContent, "utf8");
|
|
921
917
|
await mkdir(trackingDirectory, { recursive: true });
|
|
918
|
+
await ensureGitignoreEntry(process.cwd(), ".oac/");
|
|
922
919
|
const summary = {
|
|
923
920
|
configPath,
|
|
924
921
|
trackingDirectory,
|
|
@@ -947,7 +944,7 @@ async function runInteractiveInit(globalOptions, ui) {
|
|
|
947
944
|
choices: [
|
|
948
945
|
{ name: "Claude Code", value: "claude-code", checked: true },
|
|
949
946
|
{ name: "Codex CLI", value: "codex" },
|
|
950
|
-
{ name: "OpenCode
|
|
947
|
+
{ name: "OpenCode", value: "opencode" }
|
|
951
948
|
],
|
|
952
949
|
validate: (value) => value.length > 0 ? true : "Select at least one provider to continue."
|
|
953
950
|
});
|
|
@@ -1007,6 +1004,7 @@ async function runInteractiveInit(globalOptions, ui) {
|
|
|
1007
1004
|
});
|
|
1008
1005
|
await writeFile(configPath, configContent, "utf8");
|
|
1009
1006
|
await mkdir(trackingDirectory, { recursive: true });
|
|
1007
|
+
await ensureGitignoreEntry(process.cwd(), ".oac/");
|
|
1010
1008
|
const summary = {
|
|
1011
1009
|
configPath,
|
|
1012
1010
|
trackingDirectory,
|
|
@@ -1082,9 +1080,23 @@ async function pathExists2(path) {
|
|
|
1082
1080
|
return false;
|
|
1083
1081
|
}
|
|
1084
1082
|
}
|
|
1083
|
+
async function ensureGitignoreEntry(dir, entry) {
|
|
1084
|
+
const gitignorePath = resolve3(dir, ".gitignore");
|
|
1085
|
+
let content = "";
|
|
1086
|
+
try {
|
|
1087
|
+
content = await readFile2(gitignorePath, "utf8");
|
|
1088
|
+
} catch {
|
|
1089
|
+
}
|
|
1090
|
+
if (content.split("\n").some((line) => line.trim() === entry)) {
|
|
1091
|
+
return;
|
|
1092
|
+
}
|
|
1093
|
+
const separator = content.length > 0 && !content.endsWith("\n") ? "\n" : "";
|
|
1094
|
+
await writeFile(gitignorePath, `${content}${separator}${entry}
|
|
1095
|
+
`, "utf8");
|
|
1096
|
+
}
|
|
1085
1097
|
|
|
1086
1098
|
// src/cli/commands/leaderboard.ts
|
|
1087
|
-
import { readFile as
|
|
1099
|
+
import { readFile as readFile3, readdir } from "fs/promises";
|
|
1088
1100
|
import { resolve as resolve4 } from "path";
|
|
1089
1101
|
import Table2 from "cli-table3";
|
|
1090
1102
|
import { Command as Command7 } from "commander";
|
|
@@ -1144,7 +1156,7 @@ Examples:
|
|
|
1144
1156
|
async function loadLeaderboardEntries(repoPath) {
|
|
1145
1157
|
const leaderboardPath = resolve4(repoPath, ".oac", "leaderboard.json");
|
|
1146
1158
|
try {
|
|
1147
|
-
const leaderboardRaw = await
|
|
1159
|
+
const leaderboardRaw = await readFile3(leaderboardPath, "utf8");
|
|
1148
1160
|
const leaderboardPayload = JSON.parse(leaderboardRaw);
|
|
1149
1161
|
return parseStoredLeaderboardEntries(leaderboardPayload);
|
|
1150
1162
|
} catch (error) {
|
|
@@ -1204,7 +1216,7 @@ async function readContributionLogs(repoPath) {
|
|
|
1204
1216
|
fileNames.map(async (fileName) => {
|
|
1205
1217
|
const filePath = resolve4(contributionsPath, fileName);
|
|
1206
1218
|
try {
|
|
1207
|
-
const content = await
|
|
1219
|
+
const content = await readFile3(filePath, "utf8");
|
|
1208
1220
|
const payload = JSON.parse(content);
|
|
1209
1221
|
const parsed = contributionLogSchema.safeParse(payload);
|
|
1210
1222
|
return parsed.success ? parsed.data : null;
|
|
@@ -1270,9 +1282,6 @@ function sortValue(entry, field) {
|
|
|
1270
1282
|
}
|
|
1271
1283
|
return entry.totalPRsCreated;
|
|
1272
1284
|
}
|
|
1273
|
-
function isRecord(value) {
|
|
1274
|
-
return typeof value === "object" && value !== null;
|
|
1275
|
-
}
|
|
1276
1285
|
function isFileNotFoundError(error) {
|
|
1277
1286
|
if (!isRecord(error)) {
|
|
1278
1287
|
return false;
|
|
@@ -1281,7 +1290,7 @@ function isFileNotFoundError(error) {
|
|
|
1281
1290
|
}
|
|
1282
1291
|
|
|
1283
1292
|
// src/cli/commands/log.ts
|
|
1284
|
-
import { readFile as
|
|
1293
|
+
import { readFile as readFile4, readdir as readdir2 } from "fs/promises";
|
|
1285
1294
|
import { resolve as resolve5 } from "path";
|
|
1286
1295
|
import Table3 from "cli-table3";
|
|
1287
1296
|
import { Command as Command8 } from "commander";
|
|
@@ -1357,7 +1366,7 @@ async function readContributionLogs2(repoPath) {
|
|
|
1357
1366
|
files.map(async (fileName) => {
|
|
1358
1367
|
const filePath = resolve5(contributionsPath, fileName);
|
|
1359
1368
|
try {
|
|
1360
|
-
const content = await
|
|
1369
|
+
const content = await readFile4(filePath, "utf8");
|
|
1361
1370
|
const payload = JSON.parse(content);
|
|
1362
1371
|
const parsed = contributionLogSchema.safeParse(payload);
|
|
1363
1372
|
return parsed.success ? parsed.data : null;
|
|
@@ -1547,7 +1556,7 @@ import { randomUUID } from "crypto";
|
|
|
1547
1556
|
|
|
1548
1557
|
// src/cli/commands/run/epic.ts
|
|
1549
1558
|
import Table6 from "cli-table3";
|
|
1550
|
-
import
|
|
1559
|
+
import PQueue3 from "p-queue";
|
|
1551
1560
|
|
|
1552
1561
|
// src/cli/commands/run/pr.ts
|
|
1553
1562
|
import { execa } from "execa";
|
|
@@ -1593,6 +1602,8 @@ function formatDuration(seconds) {
|
|
|
1593
1602
|
}
|
|
1594
1603
|
|
|
1595
1604
|
// src/cli/commands/run/pr.ts
|
|
1605
|
+
var GITHUB_API_BASE_URL = "https://api.github.com";
|
|
1606
|
+
var OAC_PR_TITLE_PREFIX = "[OAC]";
|
|
1596
1607
|
async function createPullRequest(input2) {
|
|
1597
1608
|
if (!input2.sandbox) {
|
|
1598
1609
|
return void 0;
|
|
@@ -1604,6 +1615,19 @@ async function createPullRequest(input2) {
|
|
|
1604
1615
|
ghEnv.GH_TOKEN = input2.ghToken;
|
|
1605
1616
|
ghEnv.GITHUB_TOKEN = input2.ghToken;
|
|
1606
1617
|
}
|
|
1618
|
+
if (input2.task.linkedIssue && input2.ghToken) {
|
|
1619
|
+
const duplicate = await findExistingOacPR(
|
|
1620
|
+
input2.repoFullName,
|
|
1621
|
+
input2.task.linkedIssue.number,
|
|
1622
|
+
input2.ghToken
|
|
1623
|
+
);
|
|
1624
|
+
if (duplicate) {
|
|
1625
|
+
console.warn(
|
|
1626
|
+
`[oac] Skipping PR: existing OAC PR #${duplicate} already targets issue #${input2.task.linkedIssue.number}`
|
|
1627
|
+
);
|
|
1628
|
+
return void 0;
|
|
1629
|
+
}
|
|
1630
|
+
}
|
|
1607
1631
|
await execa("git", ["push", "--set-upstream", "origin", branchName], {
|
|
1608
1632
|
cwd: sandboxPath,
|
|
1609
1633
|
env: ghEnv,
|
|
@@ -1668,11 +1692,49 @@ async function createPullRequest(input2) {
|
|
|
1668
1692
|
return void 0;
|
|
1669
1693
|
}
|
|
1670
1694
|
}
|
|
1695
|
+
async function findExistingOacPR(repoFullName, issueNumber, token) {
|
|
1696
|
+
const url = `${GITHUB_API_BASE_URL}/repos/${repoFullName}/pulls?state=open&per_page=100&sort=updated&direction=desc`;
|
|
1697
|
+
try {
|
|
1698
|
+
const response = await fetch(url, {
|
|
1699
|
+
method: "GET",
|
|
1700
|
+
headers: {
|
|
1701
|
+
Authorization: `Bearer ${token}`,
|
|
1702
|
+
Accept: "application/vnd.github.v3+json"
|
|
1703
|
+
},
|
|
1704
|
+
signal: AbortSignal.timeout(15e3)
|
|
1705
|
+
});
|
|
1706
|
+
if (!response.ok) {
|
|
1707
|
+
return void 0;
|
|
1708
|
+
}
|
|
1709
|
+
const pulls = await response.json();
|
|
1710
|
+
if (!Array.isArray(pulls)) {
|
|
1711
|
+
return void 0;
|
|
1712
|
+
}
|
|
1713
|
+
const issueRefPattern = /(?:Fixes|Closes|Resolves)\s+#(\d+)/gi;
|
|
1714
|
+
for (const pr of pulls) {
|
|
1715
|
+
if (!pr || typeof pr !== "object") continue;
|
|
1716
|
+
const record = pr;
|
|
1717
|
+
const title = typeof record.title === "string" ? record.title : "";
|
|
1718
|
+
if (!title.startsWith(OAC_PR_TITLE_PREFIX)) continue;
|
|
1719
|
+
const prNumber = typeof record.number === "number" ? record.number : void 0;
|
|
1720
|
+
const body = typeof record.body === "string" ? record.body : "";
|
|
1721
|
+
for (const match of body.matchAll(issueRefPattern)) {
|
|
1722
|
+
const num = Number.parseInt(match[1], 10);
|
|
1723
|
+
if (num === issueNumber) {
|
|
1724
|
+
return prNumber;
|
|
1725
|
+
}
|
|
1726
|
+
}
|
|
1727
|
+
}
|
|
1728
|
+
return void 0;
|
|
1729
|
+
} catch {
|
|
1730
|
+
return void 0;
|
|
1731
|
+
}
|
|
1732
|
+
}
|
|
1671
1733
|
|
|
1672
1734
|
// src/cli/commands/run/task.ts
|
|
1673
1735
|
import Table5 from "cli-table3";
|
|
1674
1736
|
import { execa as execa2 } from "execa";
|
|
1675
|
-
import
|
|
1737
|
+
import PQueue2 from "p-queue";
|
|
1676
1738
|
async function discoverTasks(ctx, options, config, ghToken, resolvedRepo) {
|
|
1677
1739
|
const scannerSelection = selectScannersFromConfig2(config, Boolean(ghToken));
|
|
1678
1740
|
const minPriority = config?.discovery.minPriority ?? 20;
|
|
@@ -1795,7 +1857,7 @@ async function executePlan(ctx, params) {
|
|
|
1795
1857
|
`Executing ${plan.selectedTasks.length} planned task(s)...`
|
|
1796
1858
|
);
|
|
1797
1859
|
let completedCount = 0;
|
|
1798
|
-
const taskQueue = new
|
|
1860
|
+
const taskQueue = new PQueue2({ concurrency });
|
|
1799
1861
|
const executedTasks = await Promise.all(
|
|
1800
1862
|
plan.selectedTasks.map(
|
|
1801
1863
|
(entry) => taskQueue.add(async () => {
|
|
@@ -1820,7 +1882,7 @@ async function executePlan(ctx, params) {
|
|
|
1820
1882
|
);
|
|
1821
1883
|
executionSpinner?.succeed("Execution stage finished");
|
|
1822
1884
|
const completionSpinner = createSpinner(ctx.suppressOutput, "Completing task outputs...");
|
|
1823
|
-
const completionQueue = new
|
|
1885
|
+
const completionQueue = new PQueue2({ concurrency });
|
|
1824
1886
|
const completedTasks = await Promise.all(
|
|
1825
1887
|
executedTasks.map(
|
|
1826
1888
|
(result) => completionQueue.add(async () => {
|
|
@@ -1891,33 +1953,8 @@ function printFinalSummary(ctx, params) {
|
|
|
1891
1953
|
}
|
|
1892
1954
|
}
|
|
1893
1955
|
function selectScannersFromConfig2(config, hasGitHubAuth) {
|
|
1894
|
-
const
|
|
1895
|
-
|
|
1896
|
-
enabled.push("lint");
|
|
1897
|
-
}
|
|
1898
|
-
if (config?.discovery.scanners.todo !== false) {
|
|
1899
|
-
enabled.push("todo");
|
|
1900
|
-
}
|
|
1901
|
-
if (config?.discovery.scanners.testGap !== false) {
|
|
1902
|
-
enabled.push("test-gap");
|
|
1903
|
-
}
|
|
1904
|
-
if (hasGitHubAuth) {
|
|
1905
|
-
enabled.push("github-issues");
|
|
1906
|
-
}
|
|
1907
|
-
if (enabled.length === 0) {
|
|
1908
|
-
enabled.push("lint", "todo", "test-gap");
|
|
1909
|
-
}
|
|
1910
|
-
const uniqueEnabled = [...new Set(enabled)];
|
|
1911
|
-
const scannerInstances = uniqueEnabled.map((scannerName) => {
|
|
1912
|
-
if (scannerName === "lint") return new LintScanner();
|
|
1913
|
-
if (scannerName === "github-issues") return new GitHubIssuesScanner();
|
|
1914
|
-
if (scannerName === "test-gap") return new TestGapScanner();
|
|
1915
|
-
return new TodoScanner();
|
|
1916
|
-
});
|
|
1917
|
-
return {
|
|
1918
|
-
enabled: uniqueEnabled,
|
|
1919
|
-
scanner: new CompositeScanner(scannerInstances)
|
|
1920
|
-
};
|
|
1956
|
+
const { names, composite } = buildScanners(config, hasGitHubAuth);
|
|
1957
|
+
return { enabled: names, scanner: composite };
|
|
1921
1958
|
}
|
|
1922
1959
|
async function executeWithAgent(input2) {
|
|
1923
1960
|
const startedAt = Date.now();
|
|
@@ -2000,15 +2037,12 @@ Automated contribution by OAC using Codex CLI.`],
|
|
|
2000
2037
|
}
|
|
2001
2038
|
}
|
|
2002
2039
|
async function resolveAdapter(providerId) {
|
|
2003
|
-
const normalizedId = providerId
|
|
2004
|
-
const
|
|
2005
|
-
codex: () => new CodexAdapter(),
|
|
2006
|
-
"claude-code": () => new ClaudeCodeAdapter()
|
|
2007
|
-
};
|
|
2008
|
-
const factory = adapters[normalizedId];
|
|
2040
|
+
const normalizedId = adapterRegistry.resolveId(providerId);
|
|
2041
|
+
const factory = adapterRegistry.get(providerId);
|
|
2009
2042
|
if (!factory) {
|
|
2043
|
+
const supported = adapterRegistry.registeredIds().join(", ");
|
|
2010
2044
|
throw new Error(
|
|
2011
|
-
`Unknown provider "${providerId}". Supported providers:
|
|
2045
|
+
`Unknown provider "${providerId}". Supported providers: ${supported}.
|
|
2012
2046
|
Run \`oac doctor\` to check your environment setup.`
|
|
2013
2047
|
);
|
|
2014
2048
|
}
|
|
@@ -2016,7 +2050,7 @@ Run \`oac doctor\` to check your environment setup.`
|
|
|
2016
2050
|
const availability = await adapter.checkAvailability();
|
|
2017
2051
|
if (!availability.available) {
|
|
2018
2052
|
throw new Error(
|
|
2019
|
-
`Agent CLI "${normalizedId}" is not available: ${availability.
|
|
2053
|
+
`Agent CLI "${normalizedId}" is not available: ${availability.error ?? "unknown reason"}.
|
|
2020
2054
|
Install the ${normalizedId} CLI or switch providers.
|
|
2021
2055
|
Run \`oac doctor\` for setup instructions.`
|
|
2022
2056
|
);
|
|
@@ -2254,14 +2288,7 @@ async function tryLoadOrAnalyzeEpics(ctx, params) {
|
|
|
2254
2288
|
return getPendingEpics(backlog);
|
|
2255
2289
|
}
|
|
2256
2290
|
function buildScannerList2(config, hasGitHubAuth) {
|
|
2257
|
-
|
|
2258
|
-
if (config?.discovery.scanners.lint !== false) scanners.push(new LintScanner());
|
|
2259
|
-
if (config?.discovery.scanners.todo !== false) scanners.push(new TodoScanner());
|
|
2260
|
-
if (config?.discovery.scanners.testGap !== false) {
|
|
2261
|
-
scanners.push(new TestGapScanner());
|
|
2262
|
-
}
|
|
2263
|
-
if (hasGitHubAuth) scanners.push(new GitHubIssuesScanner());
|
|
2264
|
-
return scanners;
|
|
2291
|
+
return buildScanners(config, hasGitHubAuth).instances;
|
|
2265
2292
|
}
|
|
2266
2293
|
function makeStubEstimate(taskId, providerId, tokens) {
|
|
2267
2294
|
return {
|
|
@@ -2348,7 +2375,7 @@ async function runEpicPipeline(ctx, params) {
|
|
|
2348
2375
|
ctx.suppressOutput,
|
|
2349
2376
|
`Executing ${epicTotal} epic(s)...`
|
|
2350
2377
|
);
|
|
2351
|
-
const epicQueue = new
|
|
2378
|
+
const epicQueue = new PQueue3({ concurrency });
|
|
2352
2379
|
const allTaskResults = await Promise.all(
|
|
2353
2380
|
epicPlan.selectedEpics.map(
|
|
2354
2381
|
(entry) => epicQueue.add(async () => {
|
|
@@ -2496,7 +2523,7 @@ function renderEpicPlanTable(ui, plan, budget) {
|
|
|
2496
2523
|
}
|
|
2497
2524
|
|
|
2498
2525
|
// src/cli/commands/run/retry.ts
|
|
2499
|
-
import { readFile as
|
|
2526
|
+
import { readFile as readFile5, readdir as readdir3 } from "fs/promises";
|
|
2500
2527
|
import { resolve as resolve6 } from "path";
|
|
2501
2528
|
async function readMostRecentContributionLog(repoPath) {
|
|
2502
2529
|
const contributionsPath = resolve6(repoPath, ".oac", "contributions");
|
|
@@ -2509,7 +2536,7 @@ async function readMostRecentContributionLog(repoPath) {
|
|
|
2509
2536
|
}
|
|
2510
2537
|
for (const fileName of entries) {
|
|
2511
2538
|
try {
|
|
2512
|
-
const content = await
|
|
2539
|
+
const content = await readFile5(resolve6(contributionsPath, fileName), "utf8");
|
|
2513
2540
|
const parsed = contributionLogSchema.safeParse(JSON.parse(content));
|
|
2514
2541
|
if (parsed.success) return parsed.data;
|
|
2515
2542
|
} catch {
|
|
@@ -2967,7 +2994,7 @@ function parseCsv(value) {
|
|
|
2967
2994
|
}
|
|
2968
2995
|
|
|
2969
2996
|
// src/cli/commands/status.ts
|
|
2970
|
-
import { readFile as
|
|
2997
|
+
import { readFile as readFile6 } from "fs/promises";
|
|
2971
2998
|
import { resolve as resolve7 } from "path";
|
|
2972
2999
|
import { Command as Command12 } from "commander";
|
|
2973
3000
|
var WATCH_INTERVAL_MS = 2e3;
|
|
@@ -3009,7 +3036,7 @@ Examples:
|
|
|
3009
3036
|
async function readRunStatus(repoPath) {
|
|
3010
3037
|
const statusPath = resolve7(repoPath, ".oac", "status.json");
|
|
3011
3038
|
try {
|
|
3012
|
-
const raw = await
|
|
3039
|
+
const raw = await readFile6(statusPath, "utf8");
|
|
3013
3040
|
const payload = JSON.parse(raw);
|
|
3014
3041
|
return parseRunStatus(payload);
|
|
3015
3042
|
} catch (error) {
|
|
@@ -3020,7 +3047,7 @@ async function readRunStatus(repoPath) {
|
|
|
3020
3047
|
}
|
|
3021
3048
|
}
|
|
3022
3049
|
function parseRunStatus(payload) {
|
|
3023
|
-
if (!
|
|
3050
|
+
if (!isRecord(payload)) {
|
|
3024
3051
|
throw new Error("Invalid .oac/status.json format.");
|
|
3025
3052
|
}
|
|
3026
3053
|
const runId = payload.runId;
|
|
@@ -3038,7 +3065,7 @@ function parseRunStatus(payload) {
|
|
|
3038
3065
|
};
|
|
3039
3066
|
}
|
|
3040
3067
|
function parseRunStatusTask(task, index) {
|
|
3041
|
-
if (!
|
|
3068
|
+
if (!isRecord(task)) {
|
|
3042
3069
|
throw new Error(`Invalid task at index ${String(index)} in .oac/status.json.`);
|
|
3043
3070
|
}
|
|
3044
3071
|
const taskId = task.taskId;
|
|
@@ -3126,11 +3153,8 @@ function formatTaskList(tasks) {
|
|
|
3126
3153
|
}
|
|
3127
3154
|
return tasks.map((task) => `${task.taskId} (${task.title})`).join(", ");
|
|
3128
3155
|
}
|
|
3129
|
-
function isRecord2(value) {
|
|
3130
|
-
return typeof value === "object" && value !== null;
|
|
3131
|
-
}
|
|
3132
3156
|
function isFileNotFoundError3(error) {
|
|
3133
|
-
if (!
|
|
3157
|
+
if (!isRecord(error)) {
|
|
3134
3158
|
return false;
|
|
3135
3159
|
}
|
|
3136
3160
|
return error.code === "ENOENT";
|
|
@@ -3151,7 +3175,7 @@ function registerCommands(program) {
|
|
|
3151
3175
|
program.addCommand(createExplainCommand());
|
|
3152
3176
|
}
|
|
3153
3177
|
async function createCliProgram() {
|
|
3154
|
-
const version = true ? "2026.
|
|
3178
|
+
const version = true ? "2026.220.2" : "0.0.0";
|
|
3155
3179
|
const program = new Command13();
|
|
3156
3180
|
program.name("oac").description("Open Agent Contribution CLI").version(version).option("--config <path>", "Config file path", "oac.config.ts").option("--verbose", "Enable verbose logging", false).option("--quiet", "Suppress non-error output", false).option("--json", "Output machine-readable JSON", false).option("--no-color", "Disable ANSI colors");
|
|
3157
3181
|
registerCommands(program);
|
|
@@ -3181,4 +3205,4 @@ export {
|
|
|
3181
3205
|
createCliProgram,
|
|
3182
3206
|
runCli
|
|
3183
3207
|
};
|
|
3184
|
-
//# sourceMappingURL=chunk-
|
|
3208
|
+
//# sourceMappingURL=chunk-LRTQCVQK.js.map
|