automata-cli 0.1.0-007-git-publish-release.45 → 0.1.0-008-test-command.62
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 +83 -5
- package/dist/index.js +158 -28
- package/package.json +3 -2
package/README.md
CHANGED
|
@@ -14,12 +14,90 @@ npm install -g automata-cli
|
|
|
14
14
|
automata --help
|
|
15
15
|
```
|
|
16
16
|
|
|
17
|
-
|
|
17
|
+
---
|
|
18
18
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
19
|
+
## `automata config`
|
|
20
|
+
|
|
21
|
+
Launch the interactive configuration wizard to select the remote environment type.
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
automata config
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
Configuration is saved to `.automata/config.json` in the current directory.
|
|
28
|
+
|
|
29
|
+
### `automata config set type <value>`
|
|
30
|
+
|
|
31
|
+
Set the remote type non-interactively (useful in scripts or CI).
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
automata config set type gh # GitHub (requires gh CLI)
|
|
35
|
+
automata config set type azdo # Azure DevOps
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
---
|
|
39
|
+
|
|
40
|
+
## `automata git`
|
|
41
|
+
|
|
42
|
+
Git workflow helpers. Most commands require the [`gh` CLI](https://cli.github.com/) installed and authenticated. `publish-release` only requires `git`.
|
|
43
|
+
|
|
44
|
+
### `automata git get-pr-info`
|
|
45
|
+
|
|
46
|
+
Show the pull request for the current branch, including CI check results.
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
automata git get-pr-info # human-readable output
|
|
50
|
+
automata git get-pr-info --json # full PR object as JSON
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
### `automata git get-pr-comments`
|
|
54
|
+
|
|
55
|
+
List open (unresolved) review thread comments on the current branch's PR.
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
automata git get-pr-comments # human-readable output
|
|
59
|
+
automata git get-pr-comments --json # JSON array output
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
### `automata git finish-feature`
|
|
63
|
+
|
|
64
|
+
Clean up a merged feature branch: checkout `develop`, pull latest, delete the local branch.
|
|
65
|
+
|
|
66
|
+
```bash
|
|
67
|
+
automata git finish-feature
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
Checks that the PR exists and is merged, the working tree is clean, and the remote tracking branch is gone before making any changes.
|
|
71
|
+
|
|
72
|
+
### `automata git publish-release`
|
|
73
|
+
|
|
74
|
+
Execute the full GitFlow release sequence and push to `origin`. Only requires `git`.
|
|
75
|
+
|
|
76
|
+
```bash
|
|
77
|
+
automata git publish-release # auto-detect version from master tag
|
|
78
|
+
automata git publish-release 2.0.0 # explicit version
|
|
79
|
+
automata git publish-release --dry-run # preview without executing
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
When no version is given, the latest semver tag on `master` is detected and the minor segment is incremented (e.g. `1.2.0 → 1.3.0`).
|
|
83
|
+
|
|
84
|
+
---
|
|
85
|
+
|
|
86
|
+
## `automata implement-next`
|
|
87
|
+
|
|
88
|
+
Find the next open GitHub issue matching the configured filter, claim it, and invoke Claude Code to implement it.
|
|
89
|
+
|
|
90
|
+
```bash
|
|
91
|
+
automata implement-next # find, claim, and implement
|
|
92
|
+
automata implement-next --query-only # print the issue and exit
|
|
93
|
+
automata implement-next --yolo # skip Claude permission prompts
|
|
94
|
+
automata implement-next --json # JSON output
|
|
95
|
+
automata implement-next --no-claude # claim without launching Claude
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
See [docs/implement-next.md](docs/implement-next.md) for full details.
|
|
99
|
+
|
|
100
|
+
---
|
|
23
101
|
|
|
24
102
|
## Development
|
|
25
103
|
|
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 Command5 } from "commander";
|
|
5
5
|
|
|
6
6
|
// src/version.ts
|
|
7
7
|
import { readFileSync } from "fs";
|
|
@@ -876,9 +876,6 @@ var gitCommand = new Command2("git").description("Git workflow commands (some re
|
|
|
876
876
|
|
|
877
877
|
// src/commands/getReady.ts
|
|
878
878
|
import { Command as Command3 } from "commander";
|
|
879
|
-
import { spawnSync as spawnSync4 } from "child_process";
|
|
880
|
-
import { existsSync } from "fs";
|
|
881
|
-
import { delimiter, join as join2 } from "path";
|
|
882
879
|
|
|
883
880
|
// src/config/githubService.ts
|
|
884
881
|
import { spawnSync as spawnSync3 } from "child_process";
|
|
@@ -903,10 +900,6 @@ function listIssues(technique, value) {
|
|
|
903
900
|
"list",
|
|
904
901
|
"--state",
|
|
905
902
|
"open",
|
|
906
|
-
"--sort",
|
|
907
|
-
"created",
|
|
908
|
-
"--order",
|
|
909
|
-
"asc",
|
|
910
903
|
"--limit",
|
|
911
904
|
"1",
|
|
912
905
|
"--json",
|
|
@@ -941,7 +934,11 @@ function postComment(issueNumber, body) {
|
|
|
941
934
|
}
|
|
942
935
|
}
|
|
943
936
|
|
|
944
|
-
// src/
|
|
937
|
+
// src/claude/claudeService.ts
|
|
938
|
+
import { spawn, spawnSync as spawnSync4 } from "child_process";
|
|
939
|
+
import { createInterface } from "readline";
|
|
940
|
+
import { existsSync } from "fs";
|
|
941
|
+
import { delimiter, join as join2 } from "path";
|
|
945
942
|
function resolveCommand(name) {
|
|
946
943
|
const pathDirs = (process.env["PATH"] ?? "").split(delimiter);
|
|
947
944
|
for (const dir of pathDirs) {
|
|
@@ -950,33 +947,140 @@ function resolveCommand(name) {
|
|
|
950
947
|
}
|
|
951
948
|
return name;
|
|
952
949
|
}
|
|
953
|
-
function invokeClaudeCode(
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
950
|
+
function invokeClaudeCode(prompt, options = {}) {
|
|
951
|
+
if (options.verbose) {
|
|
952
|
+
return invokeClaudeCodeVerbose(prompt, options.yolo ?? false, options.model);
|
|
953
|
+
}
|
|
954
|
+
invokeClaudeCodeSync(prompt, options.yolo ?? false, options.model);
|
|
955
|
+
}
|
|
956
|
+
function invokeClaudeCodeSync(prompt, yolo, model) {
|
|
957
957
|
const claudeBin = resolveCommand("claude");
|
|
958
|
-
const
|
|
959
|
-
if (
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
958
|
+
const args = [];
|
|
959
|
+
if (yolo) args.push("--dangerously-skip-permissions");
|
|
960
|
+
if (model) args.push("--model", model);
|
|
961
|
+
args.push("-p", prompt);
|
|
962
|
+
const result = spawnSync4(claudeBin, args, { encoding: "utf8", stdio: "inherit" });
|
|
963
|
+
handleSpawnError(result.error);
|
|
964
|
+
handleExitCode(result.status);
|
|
965
|
+
}
|
|
966
|
+
function invokeClaudeCodeVerbose(prompt, yolo, model) {
|
|
967
|
+
return new Promise((resolve2) => {
|
|
968
|
+
const claudeBin = resolveCommand("claude");
|
|
969
|
+
const args = [];
|
|
970
|
+
if (yolo) args.push("--dangerously-skip-permissions");
|
|
971
|
+
if (model) args.push("--model", model);
|
|
972
|
+
args.push("--verbose", "--output-format", "stream-json", "-p", prompt);
|
|
973
|
+
const child = spawn(claudeBin, args, { stdio: ["inherit", "pipe", "inherit"] });
|
|
974
|
+
const rl = createInterface({ input: child.stdout });
|
|
975
|
+
let turnCount = 0;
|
|
976
|
+
child.on("error", (err) => {
|
|
977
|
+
handleSpawnError(err);
|
|
978
|
+
});
|
|
979
|
+
rl.on("line", (line) => {
|
|
980
|
+
try {
|
|
981
|
+
const event = JSON.parse(line);
|
|
982
|
+
formatEvent(event, turnCount);
|
|
983
|
+
if (event["type"] === "assistant") turnCount++;
|
|
984
|
+
} catch {
|
|
985
|
+
}
|
|
986
|
+
});
|
|
987
|
+
child.on("close", (code) => {
|
|
988
|
+
handleExitCode(code);
|
|
989
|
+
resolve2();
|
|
990
|
+
});
|
|
991
|
+
});
|
|
992
|
+
}
|
|
993
|
+
function formatEvent(event, turnCount) {
|
|
994
|
+
const type = event["type"];
|
|
995
|
+
if (type === "assistant") {
|
|
996
|
+
const message = event["message"];
|
|
997
|
+
const content = message?.["content"];
|
|
998
|
+
if (!content) return;
|
|
999
|
+
for (const block of content) {
|
|
1000
|
+
if (block["type"] === "tool_use") {
|
|
1001
|
+
const toolName = block["name"];
|
|
1002
|
+
const input = block["input"];
|
|
1003
|
+
const summary = summarizeTool(toolName, input);
|
|
1004
|
+
process.stderr.write(` [step ${turnCount + 1}] ${summary}
|
|
1005
|
+
`);
|
|
1006
|
+
} else if (block["type"] === "text") {
|
|
1007
|
+
const text = block["text"] ?? "";
|
|
1008
|
+
if (text.length > 0) {
|
|
1009
|
+
const preview = text.length > 120 ? text.slice(0, 120) + "..." : text;
|
|
1010
|
+
const firstLine = preview.split("\n")[0];
|
|
1011
|
+
process.stderr.write(` [step ${turnCount + 1}] ${firstLine}
|
|
1012
|
+
`);
|
|
1013
|
+
}
|
|
1014
|
+
}
|
|
964
1015
|
}
|
|
965
|
-
|
|
1016
|
+
} else if (type === "result") {
|
|
1017
|
+
const result = event["result"];
|
|
1018
|
+
const cost = event["cost_usd"];
|
|
1019
|
+
const duration = event["duration_ms"];
|
|
1020
|
+
const turns = event["num_turns"];
|
|
1021
|
+
process.stderr.write("\n--- Result ---\n");
|
|
1022
|
+
if (cost !== void 0 || duration !== void 0 || turns !== void 0) {
|
|
1023
|
+
const parts = [];
|
|
1024
|
+
if (turns !== void 0) parts.push(`${turns} turns`);
|
|
1025
|
+
if (duration !== void 0) parts.push(`${(duration / 1e3).toFixed(1)}s`);
|
|
1026
|
+
if (cost !== void 0) parts.push(`$${cost.toFixed(4)}`);
|
|
1027
|
+
process.stderr.write(` [info] ${parts.join(" | ")}
|
|
966
1028
|
`);
|
|
1029
|
+
}
|
|
1030
|
+
if (result) {
|
|
1031
|
+
process.stdout.write(result + "\n");
|
|
1032
|
+
}
|
|
1033
|
+
}
|
|
1034
|
+
}
|
|
1035
|
+
function summarizeTool(name, input) {
|
|
1036
|
+
if (!input) return `tool: ${name}`;
|
|
1037
|
+
switch (name) {
|
|
1038
|
+
case "Read":
|
|
1039
|
+
return `reading ${input["file_path"] ?? "file"}`;
|
|
1040
|
+
case "Write":
|
|
1041
|
+
return `writing ${input["file_path"] ?? "file"}`;
|
|
1042
|
+
case "Edit":
|
|
1043
|
+
return `editing ${input["file_path"] ?? "file"}`;
|
|
1044
|
+
case "Bash":
|
|
1045
|
+
return `running: ${truncate(String(input["command"] ?? ""), 80)}`;
|
|
1046
|
+
case "Glob":
|
|
1047
|
+
return `searching files: ${input["pattern"] ?? ""}`;
|
|
1048
|
+
case "Grep":
|
|
1049
|
+
return `searching content: ${truncate(String(input["pattern"] ?? ""), 60)}`;
|
|
1050
|
+
case "Agent":
|
|
1051
|
+
return `spawning agent: ${input["description"] ?? name}`;
|
|
1052
|
+
default:
|
|
1053
|
+
return `tool: ${name}`;
|
|
1054
|
+
}
|
|
1055
|
+
}
|
|
1056
|
+
function truncate(str, max) {
|
|
1057
|
+
return str.length > max ? str.slice(0, max) + "..." : str;
|
|
1058
|
+
}
|
|
1059
|
+
function handleSpawnError(error) {
|
|
1060
|
+
if (!error) return;
|
|
1061
|
+
const err = error;
|
|
1062
|
+
if (err.code === "ENOENT") {
|
|
1063
|
+
process.stderr.write("Error: `claude` CLI is not installed or not on PATH.\n");
|
|
967
1064
|
process.exit(1);
|
|
968
1065
|
}
|
|
969
|
-
|
|
970
|
-
process.stderr.write(`Error: Claude Code exited with code ${result.status ?? "unknown"}.
|
|
1066
|
+
process.stderr.write(`Error: ${err.message}
|
|
971
1067
|
`);
|
|
972
|
-
|
|
1068
|
+
process.exit(1);
|
|
1069
|
+
}
|
|
1070
|
+
function handleExitCode(status) {
|
|
1071
|
+
if (status !== null && status !== 0) {
|
|
1072
|
+
process.stderr.write(`Error: Claude Code exited with code ${status}.
|
|
1073
|
+
`);
|
|
1074
|
+
process.exit(status);
|
|
973
1075
|
}
|
|
974
1076
|
}
|
|
975
|
-
|
|
1077
|
+
|
|
1078
|
+
// src/commands/getReady.ts
|
|
1079
|
+
var implementNextCommand = new Command3("implement-next").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").option("--query-only", "Print issue content and exit without claiming or invoking Claude").option("--yolo", "Launch Claude Code with --dangerously-skip-permissions").action((options) => {
|
|
976
1080
|
const config = readConfig();
|
|
977
1081
|
if (config.remoteType !== "gh") {
|
|
978
1082
|
process.stderr.write(
|
|
979
|
-
"Error:
|
|
1083
|
+
"Error: implement-next is not supported in Azure DevOps mode. Work item discovery is not available in azdo-cli. See docs/azdo-gap.md for details.\n"
|
|
980
1084
|
);
|
|
981
1085
|
process.exit(1);
|
|
982
1086
|
}
|
|
@@ -1014,6 +1118,9 @@ URL: ${issue.url}
|
|
|
1014
1118
|
${issue.body}
|
|
1015
1119
|
`);
|
|
1016
1120
|
}
|
|
1121
|
+
if (options.queryOnly) {
|
|
1122
|
+
process.exit(0);
|
|
1123
|
+
}
|
|
1017
1124
|
try {
|
|
1018
1125
|
postComment(issue.number, "working");
|
|
1019
1126
|
} catch (err) {
|
|
@@ -1022,16 +1129,39 @@ ${issue.body}
|
|
|
1022
1129
|
process.exit(1);
|
|
1023
1130
|
}
|
|
1024
1131
|
if (options.claude !== false) {
|
|
1025
|
-
|
|
1132
|
+
const prompt = config.claudeSystemPrompt ? `${config.claudeSystemPrompt}
|
|
1133
|
+
|
|
1134
|
+
${issue.body}` : issue.body;
|
|
1135
|
+
invokeClaudeCode(prompt, { yolo: options.yolo });
|
|
1136
|
+
}
|
|
1137
|
+
});
|
|
1138
|
+
|
|
1139
|
+
// src/commands/test.ts
|
|
1140
|
+
import { Command as Command4 } from "commander";
|
|
1141
|
+
var MODEL_IDS = {
|
|
1142
|
+
opus: "claude-opus-4-6",
|
|
1143
|
+
sonnet: "claude-sonnet-4-6",
|
|
1144
|
+
haiku: "claude-haiku-4-5-20251001"
|
|
1145
|
+
};
|
|
1146
|
+
var testClaudeCmd = new Command4("claude").description("Test Claude Code invocation with a user-supplied prompt").requiredOption("--prompt <string>", "Prompt to send to Claude Code").option("--yolo", "Launch Claude Code with --dangerously-skip-permissions").option("--verbose", "Show step-by-step progress summary and final result").option("--opus", "Use claude-opus-4-6").option("--sonnet", "Use claude-sonnet-4-6").option("--haiku", "Use claude-haiku-4-5-20251001").action(async (options) => {
|
|
1147
|
+
const selected = ["opus", "sonnet", "haiku"].filter((m) => options[m]);
|
|
1148
|
+
if (selected.length > 1) {
|
|
1149
|
+
process.stderr.write(`Error: --${selected[0]} and --${selected[1]} are mutually exclusive.
|
|
1150
|
+
`);
|
|
1151
|
+
process.exit(1);
|
|
1026
1152
|
}
|
|
1153
|
+
const model = selected.length === 1 ? MODEL_IDS[selected[0]] : void 0;
|
|
1154
|
+
await invokeClaudeCode(options.prompt, { yolo: options.yolo, verbose: options.verbose, model });
|
|
1027
1155
|
});
|
|
1156
|
+
var testCommand = new Command4("test").description("Test commands for verifying automata integrations").addCommand(testClaudeCmd);
|
|
1028
1157
|
|
|
1029
1158
|
// src/index.ts
|
|
1030
|
-
var program = new
|
|
1159
|
+
var program = new Command5();
|
|
1031
1160
|
program.name("automata").description("Automata CLI tool").version(version, "-v, --version");
|
|
1032
1161
|
program.addCommand(configCommand);
|
|
1033
1162
|
program.addCommand(gitCommand);
|
|
1034
|
-
program.addCommand(
|
|
1163
|
+
program.addCommand(implementNextCommand);
|
|
1164
|
+
program.addCommand(testCommand);
|
|
1035
1165
|
program.showHelpAfterError();
|
|
1036
1166
|
program.parse();
|
|
1037
1167
|
if (process.argv.length <= 2) {
|
package/package.json
CHANGED
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "automata-cli",
|
|
3
|
-
"version": "0.1.0-
|
|
3
|
+
"version": "0.1.0-008-test-command.62",
|
|
4
4
|
"description": "Automata CLI tool",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
7
7
|
"automata": "./dist/index.js"
|
|
8
8
|
},
|
|
9
9
|
"files": [
|
|
10
|
-
"dist"
|
|
10
|
+
"dist",
|
|
11
|
+
"README.md"
|
|
11
12
|
],
|
|
12
13
|
"scripts": {
|
|
13
14
|
"build": "tsup",
|