automata-cli 0.11.0-develop.484 → 0.11.0-develop.492

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 +174 -3
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -2543,6 +2543,39 @@ function assignPrToAgent(prNumber, agentUser) {
2543
2543
  );
2544
2544
  }
2545
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
+ }
2546
2579
  function postMarker(_surface, number, body) {
2547
2580
  const { owner, repo } = getRepoSlug();
2548
2581
  const raw = ghJson(
@@ -8252,11 +8285,140 @@ function toPlanJson(decision) {
8252
8285
 
8253
8286
  // src/commands/conductor.ts
8254
8287
  import { Command as Command7 } from "commander";
8288
+
8289
+ // src/conductor/replyDecision.ts
8290
+ function isWatchClosed(state) {
8291
+ return state === "closed";
8292
+ }
8293
+
8294
+ // src/conductor/watchList.ts
8295
+ function parseWatchId(raw) {
8296
+ const text = raw.trim().replace(/^#/, "");
8297
+ if (!/^[1-9]\d*$/.test(text)) return null;
8298
+ const value = Number(text);
8299
+ return Number.isSafeInteger(value) ? value : null;
8300
+ }
8301
+ function normalizeWatch(raw) {
8302
+ if (!Array.isArray(raw)) return [];
8303
+ const seen = /* @__PURE__ */ new Set();
8304
+ for (const entry of raw) {
8305
+ if (typeof entry === "number" && Number.isSafeInteger(entry) && entry > 0) seen.add(entry);
8306
+ }
8307
+ return [...seen];
8308
+ }
8309
+ function withWatched(list, ...ids) {
8310
+ return normalizeWatch([...list, ...ids]);
8311
+ }
8312
+ function withoutWatched(list, id) {
8313
+ return list.filter((entry) => entry !== id);
8314
+ }
8315
+
8316
+ // src/commands/conductor.ts
8255
8317
  function fail2(message) {
8256
8318
  process.stderr.write(`Error: ${message}
8257
8319
  `);
8258
8320
  return 1;
8259
8321
  }
8322
+ function updateWatch(change) {
8323
+ const raw = readRawConfig();
8324
+ const current = normalizeWatch(raw.conductor?.watch);
8325
+ const next = change(current);
8326
+ if (next.length === current.length && next.every((id, i) => id === current[i])) return;
8327
+ writeConfig({ ...raw, conductor: { ...raw.conductor, watch: next } });
8328
+ }
8329
+ function describe(target) {
8330
+ return `${target.kind === "pr" ? "PR" : "issue"} #${String(target.number)} (${target.state}): ${target.title}`;
8331
+ }
8332
+ function pruneWatchList(config) {
8333
+ const watch = normalizeWatch(config.conductor?.watch);
8334
+ const dropped = [];
8335
+ for (const id of watch) {
8336
+ try {
8337
+ const target = getWatchTarget(id);
8338
+ if (isWatchClosed(target.state)) {
8339
+ process.stdout.write(
8340
+ `Conductor: dropped ${target.kind === "pr" ? "PR" : "issue"} #${String(id)} from the watch list (closed).
8341
+ `
8342
+ );
8343
+ dropped.push(id);
8344
+ }
8345
+ } catch (err) {
8346
+ process.stderr.write(`Warning: could not check #${String(id)}, keeping it watched: ${err.message}
8347
+ `);
8348
+ }
8349
+ }
8350
+ if (dropped.length > 0) updateWatch((current) => current.filter((id) => !dropped.includes(id)));
8351
+ }
8352
+ function loadGitHubConfig() {
8353
+ const config = readRawConfig();
8354
+ if (!isExplicitGitHub(config)) {
8355
+ return fail2(
8356
+ azdoUnsupportedMessage(
8357
+ "conductor",
8358
+ "It needs the GitHub APIs; set the remote with `automata config set type gh`."
8359
+ )
8360
+ );
8361
+ }
8362
+ return config;
8363
+ }
8364
+ function runWatchAdd(rawId) {
8365
+ const id = parseWatchId(rawId);
8366
+ if (id === null) return fail2(`"${rawId}" is not an issue or pull request number.`);
8367
+ const config = loadGitHubConfig();
8368
+ if (typeof config === "number") return config;
8369
+ const technique = config.issueDiscoveryTechnique;
8370
+ if (!technique) return fail2("No issue discovery technique configured. Run `automata config`.");
8371
+ const value = (config.issueDiscoveryValue ?? "").trim();
8372
+ if (technique !== "title-contains" && value.length === 0) {
8373
+ return fail2("No issue discovery value configured. Run `automata config`.");
8374
+ }
8375
+ try {
8376
+ const target = getWatchTarget(id);
8377
+ if (target.state === "closed") return fail2(`${describe(target)} is closed; nothing to watch.`);
8378
+ const followed = target.kind === "issue" ? (getOpenPrLinkMap().byIssue.get(id) ?? []).map((pr) => pr.number) : [];
8379
+ applyDiscovery(target, technique, value);
8380
+ updateWatch((current) => withWatched(current, id, ...followed));
8381
+ process.stdout.write(`Watching ${describe(target)}.
8382
+ `);
8383
+ for (const pr of followed) process.stdout.write(`Also following linked PR #${String(pr)}.
8384
+ `);
8385
+ return 0;
8386
+ } catch (err) {
8387
+ return fail2(err.message);
8388
+ }
8389
+ }
8390
+ function runWatchRemove(rawId) {
8391
+ const id = parseWatchId(rawId);
8392
+ if (id === null) return fail2(`"${rawId}" is not an issue or pull request number.`);
8393
+ const before = normalizeWatch(readRawConfig().conductor?.watch);
8394
+ if (!before.includes(id)) return fail2(`#${String(id)} is not on the watch list.`);
8395
+ updateWatch((current) => withoutWatched(current, id));
8396
+ process.stdout.write(`Stopped watching #${String(id)}.
8397
+ `);
8398
+ return 0;
8399
+ }
8400
+ function runWatchList() {
8401
+ const config = loadGitHubConfig();
8402
+ if (typeof config === "number") return config;
8403
+ const watch = normalizeWatch(config.conductor?.watch);
8404
+ if (watch.length === 0) {
8405
+ process.stdout.write("The watch list is empty.\n");
8406
+ return 0;
8407
+ }
8408
+ for (const id of watch) {
8409
+ try {
8410
+ process.stdout.write(`${describe(getWatchTarget(id))}
8411
+ `);
8412
+ } catch (err) {
8413
+ process.stdout.write(`#${String(id)}: unavailable (${err.message})
8414
+ `);
8415
+ }
8416
+ }
8417
+ return 0;
8418
+ }
8419
+ function exitWith(code) {
8420
+ if (code !== 0) process.exit(code);
8421
+ }
8260
8422
  function runConductor() {
8261
8423
  let config;
8262
8424
  try {
@@ -8312,8 +8474,9 @@ function runConductor() {
8312
8474
  return 0;
8313
8475
  }
8314
8476
  try {
8315
- process.stdout.write(`Conductor: running as ${login2 ?? ""}. Nothing to do yet.
8477
+ process.stdout.write(`Conductor: running as ${login2 ?? ""}.
8316
8478
  `);
8479
+ pruneWatchList(config);
8317
8480
  return 0;
8318
8481
  } finally {
8319
8482
  lock.handle.release();
@@ -8322,8 +8485,16 @@ function runConductor() {
8322
8485
  var conductorCommand = new Command7("conductor").description(
8323
8486
  "Run one conductor tick as an allowed (human) account: verify the identity, take the conductor's own run lock, then act"
8324
8487
  ).action(() => {
8325
- const exitCode = runConductor();
8326
- if (exitCode !== 0) process.exit(exitCode);
8488
+ exitWith(runConductor());
8489
+ });
8490
+ 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) => {
8491
+ exitWith(runWatchAdd(id));
8492
+ });
8493
+ conductorCommand.command("remove <id>").description("Stop watching an issue or pull request (the discovery label/assignee is left alone)").action((id) => {
8494
+ exitWith(runWatchRemove(id));
8495
+ });
8496
+ conductorCommand.command("list").description("List the watched issues and pull requests").action(() => {
8497
+ exitWith(runWatchList());
8327
8498
  });
8328
8499
 
8329
8500
  // src/index.ts
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "automata-cli",
3
- "version": "0.11.0-develop.484",
3
+ "version": "0.11.0-develop.492",
4
4
  "description": "Automata CLI tool",
5
5
  "type": "module",
6
6
  "engines": {