automata-cli 0.11.0-develop.477 → 0.11.0-develop.485
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/dist/index.js +202 -28
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -458,6 +458,7 @@ function parseOrigin(rawUrl) {
|
|
|
458
458
|
|
|
459
459
|
// src/config/azdoService.ts
|
|
460
460
|
import { spawnSync as spawnSync2 } from "child_process";
|
|
461
|
+
var UNRESOLVED_THREAD_STATUSES = /* @__PURE__ */ new Set(["active", "pending"]);
|
|
461
462
|
function run(cmd, args) {
|
|
462
463
|
const result = spawnSync2(cmd, args, { encoding: "utf8" });
|
|
463
464
|
if (result.error) {
|
|
@@ -564,6 +565,32 @@ function getPrInfo(branch) {
|
|
|
564
565
|
}
|
|
565
566
|
return toPrInfo(pr, (pr.checks ?? []).map(toPrCheck));
|
|
566
567
|
}
|
|
568
|
+
function getPrComments(branch) {
|
|
569
|
+
const listed = runJson(
|
|
570
|
+
["pr", "list", "--branch", branch, "--status", "active"],
|
|
571
|
+
"Failed to list Azure DevOps pull requests. Is `azdo` installed and authenticated?"
|
|
572
|
+
);
|
|
573
|
+
const pr = listed.pullRequests[0];
|
|
574
|
+
if (pr === void 0) return null;
|
|
575
|
+
const output = runJson(
|
|
576
|
+
["pr", "comments", "--pr-number", String(pr.id), "--exclude-resolved", "--code-related-only", "--exclude-system"],
|
|
577
|
+
"Failed to read the Azure DevOps pull request threads."
|
|
578
|
+
);
|
|
579
|
+
const comments = [];
|
|
580
|
+
for (const thread of output.threads) {
|
|
581
|
+
if (!UNRESOLVED_THREAD_STATUSES.has(thread.status) || thread.threadContext === null) continue;
|
|
582
|
+
const first = thread.comments.find((c) => c.commentType !== "system");
|
|
583
|
+
if (first === void 0) continue;
|
|
584
|
+
comments.push({
|
|
585
|
+
author: first.author ?? "Unknown",
|
|
586
|
+
body: first.content,
|
|
587
|
+
path: thread.threadContext,
|
|
588
|
+
line: thread.line,
|
|
589
|
+
createdAt: first.publishedAt ?? ""
|
|
590
|
+
});
|
|
591
|
+
}
|
|
592
|
+
return comments;
|
|
593
|
+
}
|
|
567
594
|
|
|
568
595
|
// src/remote/backend.ts
|
|
569
596
|
var AZDO_GAP_DOC = "docs/azdo-gap.md";
|
|
@@ -1316,10 +1343,11 @@ function getPrCommentsGh(branch) {
|
|
|
1316
1343
|
};
|
|
1317
1344
|
});
|
|
1318
1345
|
}
|
|
1319
|
-
function
|
|
1346
|
+
function getPrComments2(branch) {
|
|
1320
1347
|
const config = readConfig();
|
|
1321
1348
|
if (selectBackend(config) === "azdo") {
|
|
1322
|
-
|
|
1349
|
+
assertAzdoRemote();
|
|
1350
|
+
return getPrComments(branch);
|
|
1323
1351
|
}
|
|
1324
1352
|
return getPrCommentsGh(branch);
|
|
1325
1353
|
}
|
|
@@ -1332,11 +1360,10 @@ function resolveCurrentBranchComments() {
|
|
|
1332
1360
|
}
|
|
1333
1361
|
let raw;
|
|
1334
1362
|
try {
|
|
1335
|
-
raw =
|
|
1363
|
+
raw = getPrComments2(branch);
|
|
1336
1364
|
} catch (err) {
|
|
1337
1365
|
return { ok: false, kind: "error", message: err.message };
|
|
1338
1366
|
}
|
|
1339
|
-
if (raw === "unsupported") return { ok: false, kind: "unsupported" };
|
|
1340
1367
|
if (raw === null) return { ok: false, kind: "no-pr", branch };
|
|
1341
1368
|
return { ok: true, branch, comments: raw };
|
|
1342
1369
|
}
|
|
@@ -1782,12 +1809,7 @@ var CONTROL_CHARS_RE = /[\x00-\x08\x0b\x0c\x0e-\x1f\x7f]/g;
|
|
|
1782
1809
|
function sanitizeText(text) {
|
|
1783
1810
|
return text.replace(ANSI_ESCAPE_RE, "").replace(CONTROL_CHARS_RE, "");
|
|
1784
1811
|
}
|
|
1785
|
-
var getPrCommentsCmd = new Command2("get-pr-comments").description("List open (unresolved) review comments on the pull request for the current branch
|
|
1786
|
-
"after",
|
|
1787
|
-
`
|
|
1788
|
-
Only GitHub (remoteType: gh) is supported. Azure DevOps is not supported.
|
|
1789
|
-
See docs/azdo-gap.md for details.`
|
|
1790
|
-
).action((options) => {
|
|
1812
|
+
var getPrCommentsCmd = new Command2("get-pr-comments").description("List open (unresolved) review comments on the pull request for the current branch").option("--json", "Output as JSON array").action((options) => {
|
|
1791
1813
|
let branch;
|
|
1792
1814
|
try {
|
|
1793
1815
|
branch = getCurrentBranch();
|
|
@@ -1798,19 +1820,12 @@ See docs/azdo-gap.md for details.`
|
|
|
1798
1820
|
}
|
|
1799
1821
|
let comments;
|
|
1800
1822
|
try {
|
|
1801
|
-
comments =
|
|
1823
|
+
comments = getPrComments2(branch);
|
|
1802
1824
|
} catch (err) {
|
|
1803
1825
|
process.stderr.write(`Error: ${err.message}
|
|
1804
1826
|
`);
|
|
1805
1827
|
process.exit(1);
|
|
1806
1828
|
}
|
|
1807
|
-
if (comments === "unsupported") {
|
|
1808
|
-
process.stderr.write(
|
|
1809
|
-
`Error: ${azdoUnsupportedMessage("get-pr-comments")}
|
|
1810
|
-
`
|
|
1811
|
-
);
|
|
1812
|
-
process.exit(1);
|
|
1813
|
-
}
|
|
1814
1829
|
if (comments === null) {
|
|
1815
1830
|
process.stderr.write(`Error: No pull request found for branch: ${branch}
|
|
1816
1831
|
`);
|
|
@@ -2528,6 +2543,39 @@ function assignPrToAgent(prNumber, agentUser) {
|
|
|
2528
2543
|
);
|
|
2529
2544
|
}
|
|
2530
2545
|
}
|
|
2546
|
+
function getWatchTarget(id) {
|
|
2547
|
+
const { owner, repo } = getRepoSlug();
|
|
2548
|
+
const raw = ghJson(["api", `repos/${owner}/${repo}/issues/${String(id)}`], `read #${String(id)}`);
|
|
2549
|
+
return {
|
|
2550
|
+
number: raw.number,
|
|
2551
|
+
kind: raw.pull_request === void 0 ? "issue" : "pr",
|
|
2552
|
+
state: raw.state === "closed" ? "closed" : "open",
|
|
2553
|
+
title: raw.title
|
|
2554
|
+
};
|
|
2555
|
+
}
|
|
2556
|
+
function applyDiscovery(target, technique, value) {
|
|
2557
|
+
const edit = target.kind === "pr" ? "pr" : "issue";
|
|
2558
|
+
let flag;
|
|
2559
|
+
let arg;
|
|
2560
|
+
switch (technique) {
|
|
2561
|
+
case "label":
|
|
2562
|
+
flag = "--add-label";
|
|
2563
|
+
arg = value;
|
|
2564
|
+
break;
|
|
2565
|
+
case "assignee":
|
|
2566
|
+
flag = "--add-assignee";
|
|
2567
|
+
arg = value;
|
|
2568
|
+
break;
|
|
2569
|
+
default:
|
|
2570
|
+
throw new Error(
|
|
2571
|
+
"issueDiscoveryTechnique `title-contains` cannot be applied to an existing item; use `label` or `assignee`."
|
|
2572
|
+
);
|
|
2573
|
+
}
|
|
2574
|
+
const { stderr, status } = run4("gh", [edit, "edit", String(target.number), flag, arg]);
|
|
2575
|
+
if (status !== 0) {
|
|
2576
|
+
throw new Error(stderr.trim() || `Failed to apply the discovery setting to #${String(target.number)}.`);
|
|
2577
|
+
}
|
|
2578
|
+
}
|
|
2531
2579
|
function postMarker(_surface, number, body) {
|
|
2532
2580
|
const { owner, repo } = getRepoSlug();
|
|
2533
2581
|
const raw = ghJson(
|
|
@@ -3452,13 +3500,6 @@ var executeFixCommentsCmd = addAiOptions(
|
|
|
3452
3500
|
`);
|
|
3453
3501
|
process.exit(1);
|
|
3454
3502
|
}
|
|
3455
|
-
if (result.kind === "unsupported") {
|
|
3456
|
-
process.stderr.write(
|
|
3457
|
-
`Error: ${azdoUnsupportedMessage("fix-comments")}
|
|
3458
|
-
`
|
|
3459
|
-
);
|
|
3460
|
-
process.exit(1);
|
|
3461
|
-
}
|
|
3462
3503
|
process.stderr.write(`Error: No pull request found for branch: ${result.branch}
|
|
3463
3504
|
`);
|
|
3464
3505
|
process.exit(1);
|
|
@@ -8244,11 +8285,135 @@ function toPlanJson(decision) {
|
|
|
8244
8285
|
|
|
8245
8286
|
// src/commands/conductor.ts
|
|
8246
8287
|
import { Command as Command7 } from "commander";
|
|
8288
|
+
|
|
8289
|
+
// src/conductor/watchList.ts
|
|
8290
|
+
function parseWatchId(raw) {
|
|
8291
|
+
const text = raw.trim().replace(/^#/, "");
|
|
8292
|
+
if (!/^[1-9]\d*$/.test(text)) return null;
|
|
8293
|
+
const value = Number(text);
|
|
8294
|
+
return Number.isSafeInteger(value) ? value : null;
|
|
8295
|
+
}
|
|
8296
|
+
function normalizeWatch(raw) {
|
|
8297
|
+
if (!Array.isArray(raw)) return [];
|
|
8298
|
+
const seen = /* @__PURE__ */ new Set();
|
|
8299
|
+
for (const entry of raw) {
|
|
8300
|
+
if (typeof entry === "number" && Number.isSafeInteger(entry) && entry > 0) seen.add(entry);
|
|
8301
|
+
}
|
|
8302
|
+
return [...seen];
|
|
8303
|
+
}
|
|
8304
|
+
function withWatched(list, ...ids) {
|
|
8305
|
+
return normalizeWatch([...list, ...ids]);
|
|
8306
|
+
}
|
|
8307
|
+
function withoutWatched(list, id) {
|
|
8308
|
+
return list.filter((entry) => entry !== id);
|
|
8309
|
+
}
|
|
8310
|
+
|
|
8311
|
+
// src/commands/conductor.ts
|
|
8247
8312
|
function fail2(message) {
|
|
8248
8313
|
process.stderr.write(`Error: ${message}
|
|
8249
8314
|
`);
|
|
8250
8315
|
return 1;
|
|
8251
8316
|
}
|
|
8317
|
+
function updateWatch(change) {
|
|
8318
|
+
const raw = readRawConfig();
|
|
8319
|
+
const current = normalizeWatch(raw.conductor?.watch);
|
|
8320
|
+
const next = change(current);
|
|
8321
|
+
if (next.length === current.length && next.every((id, i) => id === current[i])) return;
|
|
8322
|
+
writeConfig({ ...raw, conductor: { ...raw.conductor, watch: next } });
|
|
8323
|
+
}
|
|
8324
|
+
function describe(target) {
|
|
8325
|
+
return `${target.kind === "pr" ? "PR" : "issue"} #${String(target.number)} (${target.state}): ${target.title}`;
|
|
8326
|
+
}
|
|
8327
|
+
function pruneWatchList(config) {
|
|
8328
|
+
const watch = normalizeWatch(config.conductor?.watch);
|
|
8329
|
+
const dropped = [];
|
|
8330
|
+
for (const id of watch) {
|
|
8331
|
+
try {
|
|
8332
|
+
const target = getWatchTarget(id);
|
|
8333
|
+
if (target.state === "closed") {
|
|
8334
|
+
process.stdout.write(
|
|
8335
|
+
`Conductor: dropped ${target.kind === "pr" ? "PR" : "issue"} #${String(id)} from the watch list (closed).
|
|
8336
|
+
`
|
|
8337
|
+
);
|
|
8338
|
+
dropped.push(id);
|
|
8339
|
+
}
|
|
8340
|
+
} catch (err) {
|
|
8341
|
+
process.stderr.write(`Warning: could not check #${String(id)}, keeping it watched: ${err.message}
|
|
8342
|
+
`);
|
|
8343
|
+
}
|
|
8344
|
+
}
|
|
8345
|
+
if (dropped.length > 0) updateWatch((current) => current.filter((id) => !dropped.includes(id)));
|
|
8346
|
+
}
|
|
8347
|
+
function loadGitHubConfig() {
|
|
8348
|
+
const config = readRawConfig();
|
|
8349
|
+
if (!isExplicitGitHub(config)) {
|
|
8350
|
+
return fail2(
|
|
8351
|
+
azdoUnsupportedMessage(
|
|
8352
|
+
"conductor",
|
|
8353
|
+
"It needs the GitHub APIs; set the remote with `automata config set type gh`."
|
|
8354
|
+
)
|
|
8355
|
+
);
|
|
8356
|
+
}
|
|
8357
|
+
return config;
|
|
8358
|
+
}
|
|
8359
|
+
function runWatchAdd(rawId) {
|
|
8360
|
+
const id = parseWatchId(rawId);
|
|
8361
|
+
if (id === null) return fail2(`"${rawId}" is not an issue or pull request number.`);
|
|
8362
|
+
const config = loadGitHubConfig();
|
|
8363
|
+
if (typeof config === "number") return config;
|
|
8364
|
+
const technique = config.issueDiscoveryTechnique;
|
|
8365
|
+
if (!technique) return fail2("No issue discovery technique configured. Run `automata config`.");
|
|
8366
|
+
const value = (config.issueDiscoveryValue ?? "").trim();
|
|
8367
|
+
if (technique !== "title-contains" && value.length === 0) {
|
|
8368
|
+
return fail2("No issue discovery value configured. Run `automata config`.");
|
|
8369
|
+
}
|
|
8370
|
+
try {
|
|
8371
|
+
const target = getWatchTarget(id);
|
|
8372
|
+
if (target.state === "closed") return fail2(`${describe(target)} is closed; nothing to watch.`);
|
|
8373
|
+
const followed = target.kind === "issue" ? (getOpenPrLinkMap().byIssue.get(id) ?? []).map((pr) => pr.number) : [];
|
|
8374
|
+
applyDiscovery(target, technique, value);
|
|
8375
|
+
updateWatch((current) => withWatched(current, id, ...followed));
|
|
8376
|
+
process.stdout.write(`Watching ${describe(target)}.
|
|
8377
|
+
`);
|
|
8378
|
+
for (const pr of followed) process.stdout.write(`Also following linked PR #${String(pr)}.
|
|
8379
|
+
`);
|
|
8380
|
+
return 0;
|
|
8381
|
+
} catch (err) {
|
|
8382
|
+
return fail2(err.message);
|
|
8383
|
+
}
|
|
8384
|
+
}
|
|
8385
|
+
function runWatchRemove(rawId) {
|
|
8386
|
+
const id = parseWatchId(rawId);
|
|
8387
|
+
if (id === null) return fail2(`"${rawId}" is not an issue or pull request number.`);
|
|
8388
|
+
const before = normalizeWatch(readRawConfig().conductor?.watch);
|
|
8389
|
+
if (!before.includes(id)) return fail2(`#${String(id)} is not on the watch list.`);
|
|
8390
|
+
updateWatch((current) => withoutWatched(current, id));
|
|
8391
|
+
process.stdout.write(`Stopped watching #${String(id)}.
|
|
8392
|
+
`);
|
|
8393
|
+
return 0;
|
|
8394
|
+
}
|
|
8395
|
+
function runWatchList() {
|
|
8396
|
+
const config = loadGitHubConfig();
|
|
8397
|
+
if (typeof config === "number") return config;
|
|
8398
|
+
const watch = normalizeWatch(config.conductor?.watch);
|
|
8399
|
+
if (watch.length === 0) {
|
|
8400
|
+
process.stdout.write("The watch list is empty.\n");
|
|
8401
|
+
return 0;
|
|
8402
|
+
}
|
|
8403
|
+
for (const id of watch) {
|
|
8404
|
+
try {
|
|
8405
|
+
process.stdout.write(`${describe(getWatchTarget(id))}
|
|
8406
|
+
`);
|
|
8407
|
+
} catch (err) {
|
|
8408
|
+
process.stdout.write(`#${String(id)}: unavailable (${err.message})
|
|
8409
|
+
`);
|
|
8410
|
+
}
|
|
8411
|
+
}
|
|
8412
|
+
return 0;
|
|
8413
|
+
}
|
|
8414
|
+
function exitWith(code) {
|
|
8415
|
+
if (code !== 0) process.exit(code);
|
|
8416
|
+
}
|
|
8252
8417
|
function runConductor() {
|
|
8253
8418
|
let config;
|
|
8254
8419
|
try {
|
|
@@ -8304,8 +8469,9 @@ function runConductor() {
|
|
|
8304
8469
|
return 0;
|
|
8305
8470
|
}
|
|
8306
8471
|
try {
|
|
8307
|
-
process.stdout.write(`Conductor: running as ${login2 ?? ""}.
|
|
8472
|
+
process.stdout.write(`Conductor: running as ${login2 ?? ""}.
|
|
8308
8473
|
`);
|
|
8474
|
+
pruneWatchList(config);
|
|
8309
8475
|
return 0;
|
|
8310
8476
|
} finally {
|
|
8311
8477
|
lock.handle.release();
|
|
@@ -8314,8 +8480,16 @@ function runConductor() {
|
|
|
8314
8480
|
var conductorCommand = new Command7("conductor").description(
|
|
8315
8481
|
"Run one conductor tick as an allowed (human) account: verify the identity, take the conductor's own run lock, then act"
|
|
8316
8482
|
).action(() => {
|
|
8317
|
-
|
|
8318
|
-
|
|
8483
|
+
exitWith(runConductor());
|
|
8484
|
+
});
|
|
8485
|
+
conductorCommand.command("add <id>").description("Watch an issue or pull request and make do-work pick it up (an issue also follows its linked PR)").action((id) => {
|
|
8486
|
+
exitWith(runWatchAdd(id));
|
|
8487
|
+
});
|
|
8488
|
+
conductorCommand.command("remove <id>").description("Stop watching an issue or pull request (the discovery label/assignee is left alone)").action((id) => {
|
|
8489
|
+
exitWith(runWatchRemove(id));
|
|
8490
|
+
});
|
|
8491
|
+
conductorCommand.command("list").description("List the watched issues and pull requests").action(() => {
|
|
8492
|
+
exitWith(runWatchList());
|
|
8319
8493
|
});
|
|
8320
8494
|
|
|
8321
8495
|
// src/index.ts
|