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.
Files changed (2) hide show
  1. package/dist/index.js +202 -28
  2. 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 getPrComments(branch) {
1346
+ function getPrComments2(branch) {
1320
1347
  const config = readConfig();
1321
1348
  if (selectBackend(config) === "azdo") {
1322
- return "unsupported";
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 = getPrComments(branch);
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 (GitHub only)").option("--json", "Output as JSON array").addHelpText(
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 = getPrComments(branch);
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 ?? ""}. Nothing to do yet.
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
- const exitCode = runConductor();
8318
- if (exitCode !== 0) process.exit(exitCode);
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
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "automata-cli",
3
- "version": "0.11.0-develop.477",
3
+ "version": "0.11.0-develop.485",
4
4
  "description": "Automata CLI tool",
5
5
  "type": "module",
6
6
  "engines": {