automata-cli 0.2.0-develop.21 → 0.2.0-develop.31
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/README.md +7 -59
- package/dist/index.js +238 -22
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# automata-cli
|
|
2
2
|
|
|
3
|
-
A command-line interface tool.
|
|
3
|
+
A command-line interface tool for automating Git and project workflows.
|
|
4
4
|
|
|
5
5
|
## Installation
|
|
6
6
|
|
|
@@ -8,7 +8,7 @@ A command-line interface tool.
|
|
|
8
8
|
npm install -g automata-cli
|
|
9
9
|
```
|
|
10
10
|
|
|
11
|
-
##
|
|
11
|
+
## Quick start
|
|
12
12
|
|
|
13
13
|
```bash
|
|
14
14
|
automata --help
|
|
@@ -16,62 +16,10 @@ automata --help
|
|
|
16
16
|
|
|
17
17
|
## Commands
|
|
18
18
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
```bash
|
|
24
|
-
automata config
|
|
25
|
-
```
|
|
26
|
-
|
|
27
|
-
### `automata config set type <value>`
|
|
28
|
-
|
|
29
|
-
Set a configuration value non-interactively (useful in scripts or CI).
|
|
30
|
-
|
|
31
|
-
```bash
|
|
32
|
-
automata config set type gh # GitHub
|
|
33
|
-
automata config set type azdo # Azure DevOps
|
|
34
|
-
```
|
|
35
|
-
|
|
36
|
-
Configuration is saved to `.automata/config.json` in the current directory.
|
|
37
|
-
|
|
38
|
-
### `automata git get-pr-info`
|
|
39
|
-
|
|
40
|
-
Show the pull request associated with the current branch (requires [`gh` CLI](https://cli.github.com/) installed and authenticated).
|
|
41
|
-
|
|
42
|
-
```bash
|
|
43
|
-
automata git get-pr-info # human-readable output
|
|
44
|
-
automata git get-pr-info --json # JSON output
|
|
45
|
-
```
|
|
46
|
-
|
|
47
|
-
Example output:
|
|
48
|
-
|
|
49
|
-
```
|
|
50
|
-
PR: #42
|
|
51
|
-
Title: Fix authentication bug
|
|
52
|
-
State: MERGED
|
|
53
|
-
URL: https://github.com/org/repo/pull/42
|
|
54
|
-
```
|
|
55
|
-
|
|
56
|
-
If no PR exists for the current branch, a friendly message is printed and the command exits with code 0.
|
|
57
|
-
|
|
58
|
-
### `automata git finish-feature`
|
|
59
|
-
|
|
60
|
-
Clean up a merged feature branch in one step: checkout `develop`, pull the latest, and delete the local branch.
|
|
61
|
-
|
|
62
|
-
```bash
|
|
63
|
-
automata git finish-feature
|
|
64
|
-
```
|
|
65
|
-
|
|
66
|
-
The command validates all preconditions before making any changes:
|
|
67
|
-
|
|
68
|
-
- Must **not** be on the `develop` branch
|
|
69
|
-
- Working tree must be clean (no uncommitted changes)
|
|
70
|
-
- A pull request for the branch must exist
|
|
71
|
-
- The PR must be in `merged` state (not open or closed-without-merge)
|
|
72
|
-
- The remote tracking branch must no longer exist (`origin/<branch>` is gone)
|
|
73
|
-
|
|
74
|
-
If any precondition fails, the command prints a descriptive error to stderr and exits with a non-zero code.
|
|
19
|
+
| Command group | Description | Docs |
|
|
20
|
+
|---|---|---|
|
|
21
|
+
| `automata config` | Configure the tool | [docs/config.md](docs/config.md) |
|
|
22
|
+
| `automata git` | Git workflow helpers (requires `gh` CLI) | [docs/git.md](docs/git.md) |
|
|
75
23
|
|
|
76
24
|
## Development
|
|
77
25
|
|
|
@@ -91,7 +39,7 @@ npm install
|
|
|
91
39
|
### Scripts
|
|
92
40
|
|
|
93
41
|
| Command | Description |
|
|
94
|
-
|
|
42
|
+
|---|---|
|
|
95
43
|
| `npm run build` | Build the CLI with tsup |
|
|
96
44
|
| `npm test` | Build and run tests with vitest |
|
|
97
45
|
| `npm run lint` | Lint source files with ESLint |
|
package/dist/index.js
CHANGED
|
@@ -233,9 +233,59 @@ var configCommand = new Command("config").description("Configure automata settin
|
|
|
233
233
|
import { Command as Command2 } from "commander";
|
|
234
234
|
|
|
235
235
|
// src/git/gitService.ts
|
|
236
|
+
import { spawnSync as spawnSync2 } from "child_process";
|
|
237
|
+
|
|
238
|
+
// src/config/azdoService.ts
|
|
236
239
|
import { spawnSync } from "child_process";
|
|
237
240
|
function run(cmd, args) {
|
|
238
241
|
const result = spawnSync(cmd, args, { encoding: "utf8" });
|
|
242
|
+
if (result.error) {
|
|
243
|
+
const err = result.error;
|
|
244
|
+
if (err.code === "ENOENT") {
|
|
245
|
+
throw new Error("`azdo` CLI is not installed or not on PATH.");
|
|
246
|
+
}
|
|
247
|
+
throw new Error(err.message);
|
|
248
|
+
}
|
|
249
|
+
return {
|
|
250
|
+
stdout: result.stdout ?? "",
|
|
251
|
+
stderr: result.stderr ?? "",
|
|
252
|
+
status: result.status ?? 1
|
|
253
|
+
};
|
|
254
|
+
}
|
|
255
|
+
function mapStatus(azdoStatus) {
|
|
256
|
+
switch (azdoStatus) {
|
|
257
|
+
case "active":
|
|
258
|
+
return "OPEN";
|
|
259
|
+
case "completed":
|
|
260
|
+
return "MERGED";
|
|
261
|
+
case "abandoned":
|
|
262
|
+
return "CLOSED";
|
|
263
|
+
default:
|
|
264
|
+
return azdoStatus.toUpperCase();
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
function getPrInfo() {
|
|
268
|
+
const { stdout, stderr, status } = run("azdo", ["pr", "status", "--json"]);
|
|
269
|
+
if (status !== 0) {
|
|
270
|
+
throw new Error(stderr.trim() || "Failed to query Azure DevOps PR status. Is `azdo` installed and authenticated?");
|
|
271
|
+
}
|
|
272
|
+
const parsed = JSON.parse(stdout);
|
|
273
|
+
if (parsed.pullRequests.length === 0) {
|
|
274
|
+
return null;
|
|
275
|
+
}
|
|
276
|
+
const pr = parsed.pullRequests[0];
|
|
277
|
+
return {
|
|
278
|
+
number: pr.id,
|
|
279
|
+
title: pr.title,
|
|
280
|
+
state: mapStatus(pr.status),
|
|
281
|
+
url: pr.url,
|
|
282
|
+
checks: []
|
|
283
|
+
};
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
// src/git/gitService.ts
|
|
287
|
+
function run2(cmd, args) {
|
|
288
|
+
const result = spawnSync2(cmd, args, { encoding: "utf8" });
|
|
239
289
|
return {
|
|
240
290
|
stdout: result.stdout ?? "",
|
|
241
291
|
stderr: result.stderr ?? "",
|
|
@@ -243,19 +293,55 @@ function run(cmd, args) {
|
|
|
243
293
|
};
|
|
244
294
|
}
|
|
245
295
|
function getCurrentBranch() {
|
|
246
|
-
const { stdout, status } =
|
|
296
|
+
const { stdout, status } = run2("git", ["rev-parse", "--abbrev-ref", "HEAD"]);
|
|
247
297
|
if (status !== 0) {
|
|
248
298
|
throw new Error("Failed to determine current branch. Are you inside a git repository?");
|
|
249
299
|
}
|
|
250
300
|
return stdout.trim();
|
|
251
301
|
}
|
|
252
|
-
function
|
|
253
|
-
const { stdout,
|
|
302
|
+
function parseOwnerRepo() {
|
|
303
|
+
const { stdout, status } = run2("git", ["remote", "get-url", "origin"]);
|
|
304
|
+
if (status !== 0) return null;
|
|
305
|
+
const url = stdout.trim();
|
|
306
|
+
const https = url.match(/github\.com\/([^/]+\/[^/]+?)(?:\.git)?$/);
|
|
307
|
+
if (https) return https[1];
|
|
308
|
+
const ssh = url.match(/github\.com:([^/]+\/[^/]+?)(?:\.git)?$/);
|
|
309
|
+
if (ssh) return ssh[1];
|
|
310
|
+
return null;
|
|
311
|
+
}
|
|
312
|
+
function extractLastMarkdownUrl(markdown) {
|
|
313
|
+
const matches = [...markdown.matchAll(/\]\((https?:\/\/[^)]+)\)/g)];
|
|
314
|
+
return matches.length > 0 ? matches[matches.length - 1][1] ?? null : null;
|
|
315
|
+
}
|
|
316
|
+
function fetchCheckRunOutputs(ownerRepo, sha) {
|
|
317
|
+
const { stdout, status } = run2("gh", [
|
|
318
|
+
"api",
|
|
319
|
+
`repos/${ownerRepo}/commits/${sha}/check-runs`,
|
|
320
|
+
"--jq",
|
|
321
|
+
".check_runs[] | {name, html_url, details_url, output}"
|
|
322
|
+
]);
|
|
323
|
+
const map = /* @__PURE__ */ new Map();
|
|
324
|
+
if (status !== 0) return map;
|
|
325
|
+
for (const line of stdout.trim().split("\n")) {
|
|
326
|
+
if (!line) continue;
|
|
327
|
+
try {
|
|
328
|
+
const item = JSON.parse(line);
|
|
329
|
+
const title = item.output?.title ?? "";
|
|
330
|
+
const summaryUrl = item.output?.summary ? extractLastMarkdownUrl(item.output.summary) : null;
|
|
331
|
+
const detailsUrl = summaryUrl ?? (item.details_url !== item.html_url ? item.details_url : "") ?? item.html_url;
|
|
332
|
+
map.set(item.name, { title, detailsUrl });
|
|
333
|
+
} catch {
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
return map;
|
|
337
|
+
}
|
|
338
|
+
function getPrInfoGh(branch) {
|
|
339
|
+
const { stdout, stderr, status } = run2("gh", [
|
|
254
340
|
"pr",
|
|
255
341
|
"view",
|
|
256
342
|
branch,
|
|
257
343
|
"--json",
|
|
258
|
-
"number,title,state,url"
|
|
344
|
+
"number,title,state,url,headRefOid,statusCheckRollup"
|
|
259
345
|
]);
|
|
260
346
|
if (status !== 0) {
|
|
261
347
|
if (stderr.includes("no pull requests found") || stderr.includes("Could not resolve")) {
|
|
@@ -263,41 +349,125 @@ function getPrInfo(branch) {
|
|
|
263
349
|
}
|
|
264
350
|
throw new Error(stderr.trim() || "Failed to query GitHub. Is `gh` installed and authenticated?");
|
|
265
351
|
}
|
|
266
|
-
|
|
352
|
+
const raw = JSON.parse(stdout);
|
|
353
|
+
const failedChecks = (raw.statusCheckRollup ?? []).filter(
|
|
354
|
+
(c) => c.conclusion !== null && ["FAILURE", "TIMED_OUT", "ACTION_REQUIRED", "CANCELLED"].includes(c.conclusion)
|
|
355
|
+
);
|
|
356
|
+
const ownerRepo = failedChecks.length > 0 ? parseOwnerRepo() : null;
|
|
357
|
+
const checkOutputs = ownerRepo ? fetchCheckRunOutputs(ownerRepo, raw.headRefOid) : /* @__PURE__ */ new Map();
|
|
358
|
+
const checks = (raw.statusCheckRollup ?? []).map((c) => {
|
|
359
|
+
const enriched = checkOutputs.get(c.name);
|
|
360
|
+
return {
|
|
361
|
+
name: c.name,
|
|
362
|
+
status: c.status,
|
|
363
|
+
conclusion: c.conclusion,
|
|
364
|
+
description: enriched?.title || c.description || "",
|
|
365
|
+
detailsUrl: enriched?.detailsUrl || c.detailsUrl || ""
|
|
366
|
+
};
|
|
367
|
+
});
|
|
368
|
+
return { number: raw.number, title: raw.title, state: raw.state, url: raw.url, checks };
|
|
369
|
+
}
|
|
370
|
+
function getPrInfo2(branch) {
|
|
371
|
+
const config = readConfig();
|
|
372
|
+
if (config.remoteType === "azdo") {
|
|
373
|
+
return getPrInfo();
|
|
374
|
+
}
|
|
375
|
+
return getPrInfoGh(branch);
|
|
267
376
|
}
|
|
268
377
|
function isUpstreamGone(branch) {
|
|
269
|
-
const { status } =
|
|
378
|
+
const { status } = run2("git", ["ls-remote", "--exit-code", "--heads", "origin", branch]);
|
|
270
379
|
return status !== 0;
|
|
271
380
|
}
|
|
272
381
|
function hasUncommittedChanges() {
|
|
273
|
-
const { stdout } =
|
|
382
|
+
const { stdout } = run2("git", ["status", "--porcelain"]);
|
|
274
383
|
return stdout.trim().length > 0;
|
|
275
384
|
}
|
|
276
385
|
function checkoutAndPull(targetBranch) {
|
|
277
|
-
const checkout =
|
|
386
|
+
const checkout = run2("git", ["checkout", targetBranch]);
|
|
278
387
|
if (checkout.status !== 0) {
|
|
279
388
|
throw new Error(`Failed to checkout ${targetBranch}: ${checkout.stderr.trim()}`);
|
|
280
389
|
}
|
|
281
|
-
const pull =
|
|
390
|
+
const pull = run2("git", ["pull"]);
|
|
282
391
|
if (pull.status !== 0) {
|
|
283
392
|
throw new Error(`Failed to pull ${targetBranch}: ${pull.stderr.trim()}`);
|
|
284
393
|
}
|
|
285
394
|
}
|
|
286
395
|
function fetchPrune() {
|
|
287
|
-
const result =
|
|
396
|
+
const result = run2("git", ["fetch", "--prune"]);
|
|
288
397
|
if (result.status !== 0) {
|
|
289
398
|
throw new Error(`Failed to fetch --prune: ${result.stderr.trim()}`);
|
|
290
399
|
}
|
|
291
400
|
}
|
|
292
401
|
function deleteLocalBranch(branch) {
|
|
293
|
-
const result =
|
|
402
|
+
const result = run2("git", ["branch", "-D", branch]);
|
|
294
403
|
if (result.status !== 0) {
|
|
295
404
|
throw new Error(`Failed to delete branch ${branch}: ${result.stderr.trim()}`);
|
|
296
405
|
}
|
|
297
406
|
}
|
|
298
407
|
|
|
299
408
|
// src/commands/git.ts
|
|
300
|
-
var
|
|
409
|
+
var FAIL_CONCLUSIONS = /* @__PURE__ */ new Set(["FAILURE", "TIMED_OUT", "ACTION_REQUIRED", "CANCELLED"]);
|
|
410
|
+
var SKIP_CONCLUSIONS = /* @__PURE__ */ new Set(["SKIPPED", "NEUTRAL"]);
|
|
411
|
+
function checkSymbol(check) {
|
|
412
|
+
if (check.status !== "COMPLETED") return "\u25CF";
|
|
413
|
+
if (check.conclusion === "SUCCESS") return "\u2713";
|
|
414
|
+
if (check.conclusion !== null && SKIP_CONCLUSIONS.has(check.conclusion)) return "\u25CB";
|
|
415
|
+
if (check.conclusion !== null && FAIL_CONCLUSIONS.has(check.conclusion)) return "\u2717";
|
|
416
|
+
return "\u25CF";
|
|
417
|
+
}
|
|
418
|
+
function formatCheckSummary(checks) {
|
|
419
|
+
const running = checks.some((c) => c.status !== "COMPLETED");
|
|
420
|
+
const failed = checks.filter((c) => c.conclusion !== null && FAIL_CONCLUSIONS.has(c.conclusion));
|
|
421
|
+
const errors = failed.length === 0 ? "none" : failed.map((c) => `${c.name}: ${c.description.trim() || c.detailsUrl || "no details available"}`).join("; ");
|
|
422
|
+
return `Checks Running: ${String(running)}
|
|
423
|
+
Check Errors: ${errors}
|
|
424
|
+
`;
|
|
425
|
+
}
|
|
426
|
+
function formatChecks(checks) {
|
|
427
|
+
if (checks.length === 0) return "Checks: none\n";
|
|
428
|
+
const lines = ["Checks:"];
|
|
429
|
+
for (const check of checks) {
|
|
430
|
+
const sym = checkSymbol(check);
|
|
431
|
+
const pending = check.status !== "COMPLETED" ? " (pending)" : "";
|
|
432
|
+
lines.push(` ${sym} ${check.name}${pending}`);
|
|
433
|
+
if (check.conclusion !== null && FAIL_CONCLUSIONS.has(check.conclusion)) {
|
|
434
|
+
const desc = check.description.trim();
|
|
435
|
+
const url = check.detailsUrl.trim();
|
|
436
|
+
if (desc) lines.push(` Details: ${desc}`);
|
|
437
|
+
if (url) lines.push(` URL: ${url}`);
|
|
438
|
+
if (!desc && !url) lines.push(` Details: (no details available)`);
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
return lines.join("\n") + "\n";
|
|
442
|
+
}
|
|
443
|
+
function sleep(ms) {
|
|
444
|
+
return new Promise((resolve2) => setTimeout(resolve2, ms));
|
|
445
|
+
}
|
|
446
|
+
function formatFailedChecks(failed) {
|
|
447
|
+
const lines = [];
|
|
448
|
+
for (const check of failed) {
|
|
449
|
+
lines.push(` \u2717 ${check.name}`);
|
|
450
|
+
const desc = check.description.trim();
|
|
451
|
+
const url = check.detailsUrl.trim();
|
|
452
|
+
if (desc) lines.push(` Details: ${desc}`);
|
|
453
|
+
if (url) lines.push(` URL: ${url}`);
|
|
454
|
+
if (!desc && !url) lines.push(` Details: (no details available)`);
|
|
455
|
+
}
|
|
456
|
+
return lines.join("\n") + "\n";
|
|
457
|
+
}
|
|
458
|
+
var POLL_INTERVAL_MS = 1e4;
|
|
459
|
+
var getPrInfoCmd = new Command2("get-pr-info").description("Show pull request info for the current branch").option("--json", "Output as JSON").option("--wait-finish-checks", "Poll until all checks complete, then report pass/fail (exit 1 on failure)").addHelpText(
|
|
460
|
+
"after",
|
|
461
|
+
`
|
|
462
|
+
Check status symbols:
|
|
463
|
+
\u2713 Passed (conclusion: SUCCESS)
|
|
464
|
+
\u2717 Failed (conclusion: FAILURE / TIMED_OUT / ACTION_REQUIRED / CANCELLED)
|
|
465
|
+
\u25CF Pending (status: QUEUED or IN_PROGRESS)
|
|
466
|
+
\u25CB Skipped (conclusion: SKIPPED or NEUTRAL)
|
|
467
|
+
|
|
468
|
+
Failure details are printed beneath each \u2717 check.
|
|
469
|
+
See docs/git.md for full output reference.`
|
|
470
|
+
).action(async (options) => {
|
|
301
471
|
let branch;
|
|
302
472
|
try {
|
|
303
473
|
branch = getCurrentBranch();
|
|
@@ -306,9 +476,51 @@ var getPrInfoCmd = new Command2("get-pr-info").description("Show pull request in
|
|
|
306
476
|
`);
|
|
307
477
|
process.exit(1);
|
|
308
478
|
}
|
|
479
|
+
if (options.waitFinishChecks) {
|
|
480
|
+
let pr2;
|
|
481
|
+
while (true) {
|
|
482
|
+
try {
|
|
483
|
+
pr2 = getPrInfo2(branch);
|
|
484
|
+
} catch (err) {
|
|
485
|
+
process.stderr.write(`Error: ${err.message}
|
|
486
|
+
`);
|
|
487
|
+
process.exit(1);
|
|
488
|
+
}
|
|
489
|
+
if (pr2 === null) {
|
|
490
|
+
process.stderr.write(`Error: No pull request found for branch: ${branch}
|
|
491
|
+
`);
|
|
492
|
+
process.exit(1);
|
|
493
|
+
}
|
|
494
|
+
const running = pr2.checks.filter((c) => c.status !== "COMPLETED");
|
|
495
|
+
if (running.length === 0) break;
|
|
496
|
+
process.stdout.write(`Waiting for ${running.length} check(s) to complete...
|
|
497
|
+
`);
|
|
498
|
+
await sleep(POLL_INTERVAL_MS);
|
|
499
|
+
}
|
|
500
|
+
const failed = pr2.checks.filter((c) => c.conclusion !== null && FAIL_CONCLUSIONS.has(c.conclusion));
|
|
501
|
+
if (failed.length === 0) {
|
|
502
|
+
if (options.json) {
|
|
503
|
+
process.stdout.write(JSON.stringify({ result: "passed" }, null, 2) + "\n");
|
|
504
|
+
} else {
|
|
505
|
+
process.stdout.write(`All checks passed. \u2713
|
|
506
|
+
`);
|
|
507
|
+
}
|
|
508
|
+
process.exit(0);
|
|
509
|
+
} else {
|
|
510
|
+
if (options.json) {
|
|
511
|
+
process.stdout.write(JSON.stringify({ result: "failed", failed }, null, 2) + "\n");
|
|
512
|
+
} else {
|
|
513
|
+
process.stdout.write(`${failed.length} check(s) failed:
|
|
514
|
+
`);
|
|
515
|
+
process.stdout.write(formatFailedChecks(failed));
|
|
516
|
+
}
|
|
517
|
+
process.exit(1);
|
|
518
|
+
}
|
|
519
|
+
return;
|
|
520
|
+
}
|
|
309
521
|
let pr;
|
|
310
522
|
try {
|
|
311
|
-
pr =
|
|
523
|
+
pr = getPrInfo2(branch);
|
|
312
524
|
} catch (err) {
|
|
313
525
|
process.stderr.write(`Error: ${err.message}
|
|
314
526
|
`);
|
|
@@ -327,6 +539,8 @@ Title: ${pr.title}
|
|
|
327
539
|
State: ${pr.state}
|
|
328
540
|
URL: ${pr.url}
|
|
329
541
|
`);
|
|
542
|
+
process.stdout.write(formatCheckSummary(pr.checks));
|
|
543
|
+
process.stdout.write(formatChecks(pr.checks));
|
|
330
544
|
}
|
|
331
545
|
});
|
|
332
546
|
var finishFeatureCmd = new Command2("finish-feature").description("Clean up a merged feature branch: checkout develop, pull, and delete local branch").action(() => {
|
|
@@ -350,7 +564,7 @@ var finishFeatureCmd = new Command2("finish-feature").description("Clean up a me
|
|
|
350
564
|
}
|
|
351
565
|
let pr;
|
|
352
566
|
try {
|
|
353
|
-
pr =
|
|
567
|
+
pr = getPrInfo2(branch);
|
|
354
568
|
} catch (err) {
|
|
355
569
|
process.stderr.write(`Error: ${err.message}
|
|
356
570
|
`);
|
|
@@ -402,14 +616,14 @@ var gitCommand = new Command2("git").description("Git workflow commands (require
|
|
|
402
616
|
|
|
403
617
|
// src/commands/getReady.ts
|
|
404
618
|
import { Command as Command3 } from "commander";
|
|
405
|
-
import { spawnSync as
|
|
619
|
+
import { spawnSync as spawnSync4 } from "child_process";
|
|
406
620
|
import { existsSync } from "fs";
|
|
407
621
|
import { delimiter, join as join2 } from "path";
|
|
408
622
|
|
|
409
623
|
// src/config/githubService.ts
|
|
410
|
-
import { spawnSync as
|
|
411
|
-
function
|
|
412
|
-
const result =
|
|
624
|
+
import { spawnSync as spawnSync3 } from "child_process";
|
|
625
|
+
function run3(cmd, args) {
|
|
626
|
+
const result = spawnSync3(cmd, args, { encoding: "utf8" });
|
|
413
627
|
if (result.error) {
|
|
414
628
|
const err = result.error;
|
|
415
629
|
if (err.code === "ENOENT") {
|
|
@@ -450,7 +664,7 @@ function listIssues(technique, value) {
|
|
|
450
664
|
filterArgs = ["--search", `${value} in:title`];
|
|
451
665
|
break;
|
|
452
666
|
}
|
|
453
|
-
const { stdout, stderr, status } =
|
|
667
|
+
const { stdout, stderr, status } = run3("gh", [...baseArgs, ...filterArgs]);
|
|
454
668
|
if (status !== 0) {
|
|
455
669
|
throw new Error(stderr.trim() || "Failed to query GitHub issues. Is `gh` installed and authenticated?");
|
|
456
670
|
}
|
|
@@ -461,7 +675,7 @@ function listIssues(technique, value) {
|
|
|
461
675
|
return issues[0];
|
|
462
676
|
}
|
|
463
677
|
function postComment(issueNumber, body) {
|
|
464
|
-
const { stderr, status } =
|
|
678
|
+
const { stderr, status } = run3("gh", ["issue", "comment", String(issueNumber), "--body", body]);
|
|
465
679
|
if (status !== 0) {
|
|
466
680
|
throw new Error(stderr.trim() || `Failed to post comment on issue #${issueNumber}.`);
|
|
467
681
|
}
|
|
@@ -481,7 +695,7 @@ function invokeClaudeCode(issue, systemPrompt) {
|
|
|
481
695
|
|
|
482
696
|
${issue.body}` : issue.body;
|
|
483
697
|
const claudeBin = resolveCommand("claude");
|
|
484
|
-
const result =
|
|
698
|
+
const result = spawnSync4(claudeBin, ["-p", prompt], { encoding: "utf8", stdio: "inherit" });
|
|
485
699
|
if (result.error) {
|
|
486
700
|
const err = result.error;
|
|
487
701
|
if (err.code === "ENOENT") {
|
|
@@ -501,7 +715,9 @@ ${issue.body}` : issue.body;
|
|
|
501
715
|
var getReadyCommand = new Command3("get-ready").description("Find the next open GitHub issue matching the configured filter, claim it, and invoke Claude Code").option("--json", "Output issue details as JSON").option("--no-claude", "Skip Claude Code invocation after claiming the issue").action((options) => {
|
|
502
716
|
const config = readConfig();
|
|
503
717
|
if (config.remoteType !== "gh") {
|
|
504
|
-
process.stderr.write(
|
|
718
|
+
process.stderr.write(
|
|
719
|
+
"Error: get-ready is not supported in Azure DevOps mode. Work item discovery is not available in azdo-cli. See docs/azdo-gap.md for details.\n"
|
|
720
|
+
);
|
|
505
721
|
process.exit(1);
|
|
506
722
|
}
|
|
507
723
|
if (!config.issueDiscoveryTechnique) {
|