automata-cli 0.2.0-develop.13 → 0.2.0-develop.20

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 +305 -23
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  // src/index.ts
4
- import { Command as Command3 } from "commander";
4
+ import { Command as Command4 } from "commander";
5
5
 
6
6
  // src/version.ts
7
7
  import { readFileSync } from "fs";
@@ -44,43 +44,151 @@ function writeConfig(config) {
44
44
 
45
45
  // src/config/ConfigWizard.tsx
46
46
  import { jsx, jsxs } from "react/jsx-runtime";
47
- var OPTIONS = [
47
+ var REMOTE_OPTIONS = [
48
48
  { label: "GitHub", value: "gh" },
49
49
  { label: "Azure DevOps", value: "azdo" }
50
50
  ];
51
+ var TECHNIQUE_OPTIONS = [
52
+ { label: "By Label", value: "label" },
53
+ { label: "By Assignee", value: "assignee" },
54
+ { label: "By Title Contains", value: "title-contains" }
55
+ ];
51
56
  function ConfigWizard() {
52
57
  const existing = readConfig();
53
- const initialIndex = OPTIONS.findIndex((o) => o.value === existing.remoteType);
54
- const [selectedIndex, setSelectedIndex] = useState(initialIndex >= 0 ? initialIndex : 0);
58
+ const initialRemoteIndex = REMOTE_OPTIONS.findIndex((o) => o.value === existing.remoteType);
59
+ const initialTechIndex = TECHNIQUE_OPTIONS.findIndex((o) => o.value === existing.issueDiscoveryTechnique);
60
+ const [screen, setScreen] = useState("remote");
61
+ const [selectedRemoteIndex, setSelectedRemoteIndex] = useState(initialRemoteIndex >= 0 ? initialRemoteIndex : 0);
62
+ const [selectedTechIndex, setSelectedTechIndex] = useState(initialTechIndex >= 0 ? initialTechIndex : 0);
63
+ const [discoveryValue, setDiscoveryValue] = useState(existing.issueDiscoveryValue ?? "");
64
+ const [systemPrompt, setSystemPrompt] = useState(existing.claudeSystemPrompt ?? "");
65
+ const [pendingRemote, setPendingRemote] = useState(existing.remoteType ?? "gh");
66
+ const [pendingTechnique, setPendingTechnique] = useState(
67
+ existing.issueDiscoveryTechnique ?? "label"
68
+ );
55
69
  const { exit } = useApp();
56
70
  useInput((input, key) => {
57
- if (key.upArrow) {
58
- setSelectedIndex((i) => i > 0 ? i - 1 : OPTIONS.length - 1);
59
- } else if (key.downArrow) {
60
- setSelectedIndex((i) => i < OPTIONS.length - 1 ? i + 1 : 0);
61
- } else if (key.return) {
62
- const chosen = OPTIONS[selectedIndex];
63
- writeConfig({ ...existing, remoteType: chosen.value });
64
- exit();
65
- } else if (key.escape || key.ctrl && input === "c") {
66
- exit();
71
+ if (screen === "remote") {
72
+ if (key.upArrow) {
73
+ setSelectedRemoteIndex((i) => i > 0 ? i - 1 : REMOTE_OPTIONS.length - 1);
74
+ } else if (key.downArrow) {
75
+ setSelectedRemoteIndex((i) => i < REMOTE_OPTIONS.length - 1 ? i + 1 : 0);
76
+ } else if (key.return) {
77
+ const chosen = REMOTE_OPTIONS[selectedRemoteIndex];
78
+ setPendingRemote(chosen.value);
79
+ if (chosen.value === "gh") {
80
+ setScreen("technique");
81
+ } else {
82
+ writeConfig({ ...existing, remoteType: chosen.value });
83
+ exit();
84
+ }
85
+ } else if (key.escape || key.ctrl && input === "c") {
86
+ exit();
87
+ }
88
+ } else if (screen === "technique") {
89
+ if (key.upArrow) {
90
+ setSelectedTechIndex((i) => i > 0 ? i - 1 : TECHNIQUE_OPTIONS.length - 1);
91
+ } else if (key.downArrow) {
92
+ setSelectedTechIndex((i) => i < TECHNIQUE_OPTIONS.length - 1 ? i + 1 : 0);
93
+ } else if (key.return) {
94
+ const chosen = TECHNIQUE_OPTIONS[selectedTechIndex];
95
+ setPendingTechnique(chosen.value);
96
+ setScreen("value");
97
+ } else if (key.escape || key.ctrl && input === "c") {
98
+ exit();
99
+ }
100
+ } else if (screen === "value") {
101
+ if (key.return) {
102
+ setScreen("system-prompt");
103
+ } else if (key.backspace || key.delete) {
104
+ setDiscoveryValue((v) => v.slice(0, -1));
105
+ } else if (key.escape || key.ctrl && input === "c") {
106
+ exit();
107
+ } else if (input && !key.ctrl && !key.meta) {
108
+ setDiscoveryValue((v) => v + input);
109
+ }
110
+ } else if (screen === "system-prompt") {
111
+ if (key.return) {
112
+ writeConfig({
113
+ ...existing,
114
+ remoteType: pendingRemote,
115
+ issueDiscoveryTechnique: pendingTechnique,
116
+ issueDiscoveryValue: discoveryValue || void 0,
117
+ claudeSystemPrompt: systemPrompt || void 0
118
+ });
119
+ exit();
120
+ } else if (key.backspace || key.delete) {
121
+ setSystemPrompt((v) => v.slice(0, -1));
122
+ } else if (key.escape || key.ctrl && input === "c") {
123
+ exit();
124
+ } else if (input && !key.ctrl && !key.meta) {
125
+ setSystemPrompt((v) => v + input);
126
+ }
67
127
  }
68
128
  });
129
+ if (screen === "remote") {
130
+ return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", marginY: 1, children: [
131
+ /* @__PURE__ */ jsx(Text, { bold: true, children: "Configure Automata" }),
132
+ /* @__PURE__ */ jsx(Text, { children: " " }),
133
+ /* @__PURE__ */ jsx(Text, { children: "Remote environment type:" }),
134
+ REMOTE_OPTIONS.map((option, index) => /* @__PURE__ */ jsx(Box, { children: /* @__PURE__ */ jsxs(Text, { color: index === selectedRemoteIndex ? "cyan" : void 0, children: [
135
+ index === selectedRemoteIndex ? "\u276F " : " ",
136
+ option.label
137
+ ] }) }, option.value)),
138
+ /* @__PURE__ */ jsx(Text, { children: " " }),
139
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2191/\u2193 to move \xB7 Enter to confirm \xB7 Ctrl+C to cancel" })
140
+ ] });
141
+ }
142
+ if (screen === "technique") {
143
+ return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", marginY: 1, children: [
144
+ /* @__PURE__ */ jsx(Text, { bold: true, children: "Configure Issue Discovery Technique" }),
145
+ /* @__PURE__ */ jsx(Text, { children: " " }),
146
+ /* @__PURE__ */ jsx(Text, { children: "How to find the next issue to work on:" }),
147
+ TECHNIQUE_OPTIONS.map((option, index) => /* @__PURE__ */ jsx(Box, { children: /* @__PURE__ */ jsxs(Text, { color: index === selectedTechIndex ? "cyan" : void 0, children: [
148
+ index === selectedTechIndex ? "\u276F " : " ",
149
+ option.label
150
+ ] }) }, option.value)),
151
+ /* @__PURE__ */ jsx(Text, { children: " " }),
152
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2191/\u2193 to move \xB7 Enter to confirm \xB7 Ctrl+C to cancel" })
153
+ ] });
154
+ }
155
+ if (screen === "value") {
156
+ const techLabel = TECHNIQUE_OPTIONS.find((t) => t.value === pendingTechnique)?.label ?? pendingTechnique;
157
+ return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", marginY: 1, children: [
158
+ /* @__PURE__ */ jsx(Text, { bold: true, children: "Configure Issue Discovery Value" }),
159
+ /* @__PURE__ */ jsx(Text, { children: " " }),
160
+ /* @__PURE__ */ jsxs(Text, { children: [
161
+ techLabel,
162
+ " value:",
163
+ " ",
164
+ /* @__PURE__ */ jsxs(Text, { color: "cyan", children: [
165
+ discoveryValue,
166
+ /* @__PURE__ */ jsx(Text, { children: "_" })
167
+ ] })
168
+ ] }),
169
+ /* @__PURE__ */ jsx(Text, { children: " " }),
170
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: "Type value \xB7 Enter to confirm \xB7 Ctrl+C to cancel" })
171
+ ] });
172
+ }
69
173
  return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", marginY: 1, children: [
70
- /* @__PURE__ */ jsx(Text, { bold: true, children: "Configure Automata" }),
174
+ /* @__PURE__ */ jsx(Text, { bold: true, children: "Configure Claude System Prompt" }),
71
175
  /* @__PURE__ */ jsx(Text, { children: " " }),
72
- /* @__PURE__ */ jsx(Text, { children: "Remote environment type:" }),
73
- OPTIONS.map((option, index) => /* @__PURE__ */ jsx(Box, { children: /* @__PURE__ */ jsxs(Text, { color: index === selectedIndex ? "cyan" : void 0, children: [
74
- index === selectedIndex ? "\u276F " : " ",
75
- option.label
76
- ] }) }, option.value)),
176
+ /* @__PURE__ */ jsxs(Text, { children: [
177
+ "System prompt (optional):",
178
+ " ",
179
+ /* @__PURE__ */ jsxs(Text, { color: "cyan", children: [
180
+ systemPrompt,
181
+ /* @__PURE__ */ jsx(Text, { children: "_" })
182
+ ] })
183
+ ] }),
77
184
  /* @__PURE__ */ jsx(Text, { children: " " }),
78
- /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2191/\u2193 to move \xB7 Enter to confirm \xB7 Ctrl+C to cancel" })
185
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: "Type prompt \xB7 Enter to save and exit \xB7 Ctrl+C to cancel" })
79
186
  ] });
80
187
  }
81
188
 
82
189
  // src/commands/config.ts
83
190
  var VALID_TYPES = ["gh", "azdo"];
191
+ var VALID_TECHNIQUES = ["label", "assignee", "title-contains"];
84
192
  var configSetType = new Command("type").description("Set the remote environment type").argument("<value>", "Remote type: gh (GitHub) or azdo (Azure DevOps)").action((value) => {
85
193
  if (!VALID_TYPES.includes(value)) {
86
194
  process.stderr.write(`Error: invalid type "${value}". Must be one of: ${VALID_TYPES.join(", ")}
@@ -92,7 +200,30 @@ var configSetType = new Command("type").description("Set the remote environment
92
200
  process.stdout.write(`Remote type set to: ${value}
93
201
  `);
94
202
  });
95
- var configSet = new Command("set").description("Set a configuration value").addCommand(configSetType);
203
+ var configSetIssueDiscoveryTechnique = new Command("issue-discovery-technique").description("Set the issue discovery technique (GitHub mode only)").argument("<value>", `Technique: ${VALID_TECHNIQUES.join(", ")}`).action((value) => {
204
+ if (!VALID_TECHNIQUES.includes(value)) {
205
+ process.stderr.write(`Error: invalid technique "${value}". Must be one of: ${VALID_TECHNIQUES.join(", ")}
206
+ `);
207
+ process.exit(1);
208
+ }
209
+ const current = readConfig();
210
+ writeConfig({ ...current, issueDiscoveryTechnique: value });
211
+ process.stdout.write(`Issue discovery technique set to: ${value}
212
+ `);
213
+ });
214
+ var configSetIssueDiscoveryValue = new Command("issue-discovery-value").description("Set the value for the issue discovery technique (label name, username, or search string)").argument("<value>", "The filter value").action((value) => {
215
+ const current = readConfig();
216
+ writeConfig({ ...current, issueDiscoveryValue: value });
217
+ process.stdout.write(`Issue discovery value set to: ${value}
218
+ `);
219
+ });
220
+ var configSetClaudeSystemPrompt = new Command("claude-system-prompt").description("Set the system prompt used when invoking Claude Code").argument("<value>", "System prompt text").action((value) => {
221
+ const current = readConfig();
222
+ writeConfig({ ...current, claudeSystemPrompt: value });
223
+ process.stdout.write(`Claude system prompt set.
224
+ `);
225
+ });
226
+ var configSet = new Command("set").description("Set a configuration value").addCommand(configSetType).addCommand(configSetIssueDiscoveryTechnique).addCommand(configSetIssueDiscoveryValue).addCommand(configSetClaudeSystemPrompt);
96
227
  var configCommand = new Command("config").description("Configure automata settings").addCommand(configSet).action(async () => {
97
228
  const { waitUntilExit } = render(React2.createElement(ConfigWizard));
98
229
  await waitUntilExit();
@@ -260,11 +391,162 @@ var finishFeatureCmd = new Command2("finish-feature").description("Clean up a me
260
391
  });
261
392
  var gitCommand = new Command2("git").description("Git workflow commands (requires gh CLI)").addCommand(getPrInfoCmd).addCommand(finishFeatureCmd);
262
393
 
394
+ // src/commands/getReady.ts
395
+ import { Command as Command3 } from "commander";
396
+ import { spawnSync as spawnSync3 } from "child_process";
397
+ import { existsSync } from "fs";
398
+ import { delimiter, join as join2 } from "path";
399
+
400
+ // src/config/githubService.ts
401
+ import { spawnSync as spawnSync2 } from "child_process";
402
+ function run2(cmd, args) {
403
+ const result = spawnSync2(cmd, args, { encoding: "utf8" });
404
+ if (result.error) {
405
+ const err = result.error;
406
+ if (err.code === "ENOENT") {
407
+ throw new Error("`gh` CLI is not installed or not on PATH.");
408
+ }
409
+ throw new Error(err.message);
410
+ }
411
+ return {
412
+ stdout: result.stdout ?? "",
413
+ stderr: result.stderr ?? "",
414
+ status: result.status ?? 1
415
+ };
416
+ }
417
+ function listIssues(technique, value) {
418
+ const baseArgs = [
419
+ "issue",
420
+ "list",
421
+ "--state",
422
+ "open",
423
+ "--sort",
424
+ "created",
425
+ "--order",
426
+ "asc",
427
+ "--limit",
428
+ "1",
429
+ "--json",
430
+ "number,title,body,url"
431
+ ];
432
+ let filterArgs;
433
+ switch (technique) {
434
+ case "label":
435
+ filterArgs = ["--label", value];
436
+ break;
437
+ case "assignee":
438
+ filterArgs = ["--assignee", value];
439
+ break;
440
+ case "title-contains":
441
+ filterArgs = ["--search", `${value} in:title`];
442
+ break;
443
+ }
444
+ const { stdout, stderr, status } = run2("gh", [...baseArgs, ...filterArgs]);
445
+ if (status !== 0) {
446
+ throw new Error(stderr.trim() || "Failed to query GitHub issues. Is `gh` installed and authenticated?");
447
+ }
448
+ const issues = JSON.parse(stdout);
449
+ if (issues.length === 0) {
450
+ return null;
451
+ }
452
+ return issues[0];
453
+ }
454
+ function postComment(issueNumber, body) {
455
+ const { stderr, status } = run2("gh", ["issue", "comment", String(issueNumber), "--body", body]);
456
+ if (status !== 0) {
457
+ throw new Error(stderr.trim() || `Failed to post comment on issue #${issueNumber}.`);
458
+ }
459
+ }
460
+
461
+ // src/commands/getReady.ts
462
+ function resolveCommand(name) {
463
+ const pathDirs = (process.env["PATH"] ?? "").split(delimiter);
464
+ for (const dir of pathDirs) {
465
+ const candidate = join2(dir, name);
466
+ if (existsSync(candidate)) return candidate;
467
+ }
468
+ return name;
469
+ }
470
+ function invokeClaudeCode(issue, systemPrompt) {
471
+ const prompt = systemPrompt ? `${systemPrompt}
472
+
473
+ ${issue.body}` : issue.body;
474
+ const claudeBin = resolveCommand("claude");
475
+ const result = spawnSync3(claudeBin, ["-p", prompt], { encoding: "utf8", stdio: "inherit" });
476
+ if (result.error) {
477
+ const err = result.error;
478
+ if (err.code === "ENOENT") {
479
+ process.stderr.write("Error: `claude` CLI is not installed or not on PATH.\n");
480
+ process.exit(1);
481
+ }
482
+ process.stderr.write(`Error: ${err.message}
483
+ `);
484
+ process.exit(1);
485
+ }
486
+ if (result.status !== 0) {
487
+ process.stderr.write(`Error: Claude Code exited with code ${result.status ?? "unknown"}.
488
+ `);
489
+ process.exit(result.status ?? 1);
490
+ }
491
+ }
492
+ 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) => {
493
+ const config = readConfig();
494
+ if (config.remoteType !== "gh") {
495
+ process.stderr.write("Error: get-ready is only available for GitHub (gh) mode.\n");
496
+ process.exit(1);
497
+ }
498
+ if (!config.issueDiscoveryTechnique) {
499
+ process.stderr.write(
500
+ "Error: No issue discovery technique configured. Run `automata config` to set one.\n"
501
+ );
502
+ process.exit(1);
503
+ }
504
+ if (!config.issueDiscoveryValue) {
505
+ process.stderr.write(
506
+ "Error: No issue discovery value configured. Run `automata config` to set one.\n"
507
+ );
508
+ process.exit(1);
509
+ }
510
+ let issue;
511
+ try {
512
+ issue = listIssues(config.issueDiscoveryTechnique, config.issueDiscoveryValue);
513
+ } catch (err) {
514
+ process.stderr.write(`Error: ${err.message}
515
+ `);
516
+ process.exit(1);
517
+ }
518
+ if (issue === null) {
519
+ process.stdout.write("No issues found matching the configured filter.\n");
520
+ process.exit(0);
521
+ }
522
+ if (options.json) {
523
+ process.stdout.write(JSON.stringify({ number: issue.number, title: issue.title, body: issue.body, url: issue.url }, null, 2) + "\n");
524
+ } else {
525
+ process.stdout.write(`Issue: #${issue.number}
526
+ Title: ${issue.title}
527
+ URL: ${issue.url}
528
+
529
+ ${issue.body}
530
+ `);
531
+ }
532
+ try {
533
+ postComment(issue.number, "working");
534
+ } catch (err) {
535
+ process.stderr.write(`Error: ${err.message}
536
+ `);
537
+ process.exit(1);
538
+ }
539
+ if (options.claude !== false) {
540
+ invokeClaudeCode(issue, config.claudeSystemPrompt);
541
+ }
542
+ });
543
+
263
544
  // src/index.ts
264
- var program = new Command3();
545
+ var program = new Command4();
265
546
  program.name("automata").description("Automata CLI tool").version(version, "-v, --version");
266
547
  program.addCommand(configCommand);
267
548
  program.addCommand(gitCommand);
549
+ program.addCommand(getReadyCommand);
268
550
  program.showHelpAfterError();
269
551
  program.parse();
270
552
  if (process.argv.length <= 2) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "automata-cli",
3
- "version": "0.2.0-develop.13",
3
+ "version": "0.2.0-develop.20",
4
4
  "description": "Automata CLI tool",
5
5
  "type": "module",
6
6
  "bin": {