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.
- package/dist/index.js +305 -23
- 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
|
|
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
|
|
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
|
|
54
|
-
const
|
|
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 (
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
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
|
|
174
|
+
/* @__PURE__ */ jsx(Text, { bold: true, children: "Configure Claude System Prompt" }),
|
|
71
175
|
/* @__PURE__ */ jsx(Text, { children: " " }),
|
|
72
|
-
/* @__PURE__ */
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
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: "
|
|
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
|
|
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
|
|
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) {
|