opencode-auto-agent 1.1.0 → 1.3.0
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 +23 -5
- package/bin/cli.js +275 -51
- package/package.json +3 -3
- package/src/commands/doctor.js +337 -54
- package/src/commands/init.js +337 -114
- package/src/commands/run.js +182 -61
- package/src/commands/setup.js +321 -21
- package/src/lib/constants.js +90 -14
- package/src/lib/models.js +172 -0
- package/src/lib/models.test.js +42 -0
- package/src/lib/prerequisites.js +151 -0
- package/src/lib/ui.js +501 -0
- package/src/lib/ui.test.js +26 -0
- package/templates/sample-config.json +47 -8
package/README.md
CHANGED
|
@@ -2,14 +2,17 @@
|
|
|
2
2
|
|
|
3
3
|
`opencode-auto-agent` is a lightweight starter template for running a reusable AI agent team with **Ralphy + OpenCode**.
|
|
4
4
|
|
|
5
|
-
It scaffolds a standard project integration folder, ships language-agnostic agent definitions,
|
|
5
|
+
It scaffolds a standard project integration folder, ships language-agnostic agent definitions, supports stack presets like Java, Spring Boot, and Next.js, and now includes model-aware setup aligned with OpenCode's agent schema.
|
|
6
6
|
|
|
7
7
|
## Features
|
|
8
8
|
|
|
9
|
-
- One-command project setup
|
|
9
|
+
- One-command project setup with interactive TUI
|
|
10
10
|
- Reusable agent team (orchestrator, planner, developer, qa, reviewer, docs)
|
|
11
11
|
- Preset-based context (Java, Spring Boot, Next.js)
|
|
12
12
|
- Orchestrated workflow: plan -> implement -> test -> verify
|
|
13
|
+
- Model discovery from `opencode models` and per-agent model assignment
|
|
14
|
+
- OpenCode-aligned config generation (`opencode.json` + `.opencode/config.json`)
|
|
15
|
+
- Validation tooling for config, agents, rules, and model availability
|
|
13
16
|
- Auto-release pipeline with semantic-release (GitHub Actions)
|
|
14
17
|
|
|
15
18
|
## Installation
|
|
@@ -30,11 +33,21 @@ npm install -g opencode-auto-agent
|
|
|
30
33
|
|
|
31
34
|
```bash
|
|
32
35
|
opencode-auto-agent init [--preset=<name>]
|
|
33
|
-
opencode-auto-agent run [--engine=<name>]
|
|
34
|
-
opencode-auto-agent setup
|
|
35
|
-
opencode-auto-agent doctor
|
|
36
|
+
opencode-auto-agent run [--engine=<name>] [--dry-run]
|
|
37
|
+
opencode-auto-agent setup [preset] [--models]
|
|
38
|
+
opencode-auto-agent doctor [--verbose]
|
|
39
|
+
opencode-auto-agent models [provider]
|
|
40
|
+
opencode-auto-agent config
|
|
36
41
|
```
|
|
37
42
|
|
|
43
|
+
### Common Flags
|
|
44
|
+
|
|
45
|
+
- `--dir=<path>`: target directory (default: current directory)
|
|
46
|
+
- `--force`: overwrite existing generated files during `init`
|
|
47
|
+
- `--non-interactive`: skip prompts and use defaults
|
|
48
|
+
- `--models`: reconfigure model assignments during `setup`
|
|
49
|
+
- `--dry-run`: assemble runtime context without launching Ralphy
|
|
50
|
+
|
|
38
51
|
### Presets
|
|
39
52
|
|
|
40
53
|
- `java`
|
|
@@ -46,6 +59,7 @@ opencode-auto-agent doctor
|
|
|
46
59
|
```bash
|
|
47
60
|
npx opencode-auto-agent init --preset=nextjs
|
|
48
61
|
npx opencode-auto-agent doctor
|
|
62
|
+
npx opencode-auto-agent models
|
|
49
63
|
npx opencode-auto-agent run
|
|
50
64
|
```
|
|
51
65
|
|
|
@@ -67,12 +81,16 @@ Plus helper config files:
|
|
|
67
81
|
- `opencode.json`
|
|
68
82
|
- `.ralphy/config.yaml`
|
|
69
83
|
|
|
84
|
+
`config.json` includes a `models` map (role -> `provider/model-id`) and mirrored agent model settings in `agents`.
|
|
85
|
+
|
|
70
86
|
## Requirements
|
|
71
87
|
|
|
72
88
|
- Node.js 18+
|
|
73
89
|
- OpenCode CLI installed and configured
|
|
74
90
|
- Ralphy CLI installed
|
|
75
91
|
|
|
92
|
+
The CLI validates prerequisites and does not auto-install missing tools.
|
|
93
|
+
|
|
76
94
|
## Releasing
|
|
77
95
|
|
|
78
96
|
Releases are fully automated via semantic-release on pushes to `main`.
|
package/bin/cli.js
CHANGED
|
@@ -1,19 +1,22 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
|
-
* opencode-auto-agent CLI
|
|
4
|
+
* opencode-auto-agent CLI — main entry point.
|
|
5
5
|
*
|
|
6
6
|
* Commands:
|
|
7
|
-
* init
|
|
8
|
-
* run
|
|
9
|
-
* setup
|
|
10
|
-
* doctor
|
|
7
|
+
* init Scaffold .opencode/ into the current project
|
|
8
|
+
* run Start the orchestrator (Ralphy + OpenCode agents)
|
|
9
|
+
* setup [preset] Apply or switch a preset (java, springboot, nextjs, ...)
|
|
10
|
+
* doctor Validate configuration, tools, and agent definitions
|
|
11
|
+
* models Show available models from OpenCode
|
|
12
|
+
* config Show current project configuration
|
|
13
|
+
* help Show this help message
|
|
11
14
|
*/
|
|
12
15
|
|
|
13
|
-
import { resolve
|
|
16
|
+
import { resolve } from "node:path";
|
|
14
17
|
import { argv, exit, cwd } from "node:process";
|
|
15
18
|
|
|
16
|
-
// ──
|
|
19
|
+
// ── Arg parsing (zero-dep) ─────────────────────────────────────────────────
|
|
17
20
|
const args = argv.slice(2);
|
|
18
21
|
const command = args[0] || "help";
|
|
19
22
|
const positional = args.slice(1).filter((a) => !a.startsWith("--"));
|
|
@@ -28,77 +31,298 @@ const flags = Object.fromEntries(
|
|
|
28
31
|
|
|
29
32
|
const TARGET_DIR = resolve(flags.dir || cwd());
|
|
30
33
|
|
|
31
|
-
// ──
|
|
34
|
+
// ── Command router ─────────────────────────────────────────────────────────
|
|
32
35
|
async function main() {
|
|
33
36
|
switch (command) {
|
|
34
37
|
case "init": {
|
|
35
38
|
const { init } = await import("../src/commands/init.js");
|
|
36
|
-
await init(TARGET_DIR, {
|
|
39
|
+
await init(TARGET_DIR, {
|
|
40
|
+
preset: positional[0] || flags.preset,
|
|
41
|
+
force: !!flags.force,
|
|
42
|
+
nonInteractive: !!flags["non-interactive"],
|
|
43
|
+
});
|
|
37
44
|
break;
|
|
38
45
|
}
|
|
46
|
+
|
|
39
47
|
case "run": {
|
|
40
48
|
const { run } = await import("../src/commands/run.js");
|
|
41
|
-
await run(TARGET_DIR,
|
|
49
|
+
await run(TARGET_DIR, {
|
|
50
|
+
engine: flags.engine,
|
|
51
|
+
dryRun: !!flags["dry-run"],
|
|
52
|
+
verbose: !!flags.verbose,
|
|
53
|
+
});
|
|
42
54
|
break;
|
|
43
55
|
}
|
|
56
|
+
|
|
44
57
|
case "setup": {
|
|
45
58
|
const { setup } = await import("../src/commands/setup.js");
|
|
46
|
-
const preset = positional[0] || flags.preset;
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
}
|
|
52
|
-
await setup(TARGET_DIR, preset);
|
|
59
|
+
const preset = positional[0] || flags.preset || null;
|
|
60
|
+
await setup(TARGET_DIR, preset, {
|
|
61
|
+
models: !!flags.models,
|
|
62
|
+
nonInteractive: !!flags["non-interactive"],
|
|
63
|
+
});
|
|
53
64
|
break;
|
|
54
65
|
}
|
|
66
|
+
|
|
55
67
|
case "doctor": {
|
|
56
68
|
const { doctor } = await import("../src/commands/doctor.js");
|
|
57
|
-
await doctor(TARGET_DIR
|
|
69
|
+
await doctor(TARGET_DIR, {
|
|
70
|
+
verbose: !!flags.verbose,
|
|
71
|
+
});
|
|
58
72
|
break;
|
|
59
73
|
}
|
|
74
|
+
|
|
75
|
+
case "models": {
|
|
76
|
+
await showModels(positional[0] || flags.provider);
|
|
77
|
+
break;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
case "config": {
|
|
81
|
+
await showConfig(TARGET_DIR);
|
|
82
|
+
break;
|
|
83
|
+
}
|
|
84
|
+
|
|
60
85
|
case "help":
|
|
61
86
|
case "--help":
|
|
62
87
|
case "-h":
|
|
88
|
+
await printHelp();
|
|
89
|
+
break;
|
|
90
|
+
|
|
91
|
+
case "--version":
|
|
92
|
+
case "-v":
|
|
93
|
+
await printVersion();
|
|
94
|
+
break;
|
|
95
|
+
|
|
63
96
|
default:
|
|
64
|
-
|
|
97
|
+
// Check if it's a known flag passed as first arg
|
|
98
|
+
if (command.startsWith("-")) {
|
|
99
|
+
await printHelp();
|
|
100
|
+
} else {
|
|
101
|
+
const { c } = await import("../src/lib/ui.js");
|
|
102
|
+
console.log(`\n ${c.red("Unknown command:")} ${command}`);
|
|
103
|
+
console.log(` ${c.gray("Run 'opencode-auto-agent help' for usage.\n")}`);
|
|
104
|
+
exit(1);
|
|
105
|
+
}
|
|
65
106
|
break;
|
|
66
107
|
}
|
|
67
108
|
}
|
|
68
109
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
110
|
+
// ── models command ─────────────────────────────────────────────────────────
|
|
111
|
+
|
|
112
|
+
async function showModels(providerFilter) {
|
|
113
|
+
const { banner, section, status, c, table } = await import("../src/lib/ui.js");
|
|
114
|
+
const { discoverModelsInteractive, groupByProvider } = await import("../src/lib/models.js");
|
|
115
|
+
|
|
116
|
+
banner("OpenCode Models", "Available models from configured providers");
|
|
117
|
+
|
|
118
|
+
const models = discoverModelsInteractive();
|
|
119
|
+
|
|
120
|
+
if (models.length === 0) {
|
|
121
|
+
status("warn", "No models found.", "Is opencode configured with providers?");
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
const groups = groupByProvider(models);
|
|
126
|
+
|
|
127
|
+
if (providerFilter) {
|
|
128
|
+
// Show only the specified provider
|
|
129
|
+
const filtered = groups.get(providerFilter);
|
|
130
|
+
if (!filtered) {
|
|
131
|
+
status("fail", `Provider "${providerFilter}" not found.`);
|
|
132
|
+
console.log(` ${c.gray("Available providers:")} ${[...groups.keys()].join(", ")}`);
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
section(`Provider: ${providerFilter}`);
|
|
137
|
+
table(
|
|
138
|
+
["Model ID", "Model Name"],
|
|
139
|
+
filtered.map((m) => [c.cyan(m.id), m.model])
|
|
140
|
+
);
|
|
141
|
+
} else {
|
|
142
|
+
// Show all providers grouped
|
|
143
|
+
for (const [provider, providerModels] of groups) {
|
|
144
|
+
section(`Provider: ${provider}`);
|
|
145
|
+
table(
|
|
146
|
+
["Model ID", "Model Name"],
|
|
147
|
+
providerModels.map((m) => [c.cyan(m.id), m.model])
|
|
148
|
+
);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
console.log();
|
|
153
|
+
status("info", `Total: ${c.bold(String(models.length))} models from ${c.bold(String(groups.size))} providers`);
|
|
154
|
+
console.log();
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// ── config command ─────────────────────────────────────────────────────────
|
|
158
|
+
|
|
159
|
+
async function showConfig(targetDir) {
|
|
160
|
+
const { join } = await import("node:path");
|
|
161
|
+
const { banner, section, kv, status, c, table, error } = await import("../src/lib/ui.js");
|
|
162
|
+
const { readJsonSync } = await import("../src/lib/fs-utils.js");
|
|
163
|
+
const { SCAFFOLD_DIR, CONFIG_FILE, AGENTS, AGENT_ROLES } = await import("../src/lib/constants.js");
|
|
164
|
+
|
|
165
|
+
banner("OpenCode Auto-Agent Config", "Current project configuration");
|
|
166
|
+
|
|
167
|
+
const scaffoldDir = join(targetDir, SCAFFOLD_DIR);
|
|
168
|
+
const configPath = join(scaffoldDir, CONFIG_FILE);
|
|
169
|
+
const config = readJsonSync(configPath);
|
|
170
|
+
|
|
171
|
+
if (!config) {
|
|
172
|
+
error(
|
|
173
|
+
"No configuration found.",
|
|
174
|
+
"Run 'npx opencode-auto-agent init' to scaffold the project."
|
|
175
|
+
);
|
|
176
|
+
return;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
section("General");
|
|
180
|
+
kv("Version", config.version || "unknown");
|
|
181
|
+
kv("Preset", config.preset || "(none)");
|
|
182
|
+
kv("Engine", config.engine || "opencode");
|
|
183
|
+
kv("Tasks path", config.tasksPath || "PRD.md");
|
|
184
|
+
kv("Docs path", config.docsPath || "Docs");
|
|
185
|
+
kv("Output path", config.outputPath || "./");
|
|
186
|
+
|
|
187
|
+
if (config.models) {
|
|
188
|
+
section("Model Assignments");
|
|
189
|
+
table(
|
|
190
|
+
["Agent", "Model", "Mode", "Status"],
|
|
191
|
+
AGENTS.map((role) => {
|
|
192
|
+
const enabled = config.agents?.[role]?.enabled !== false;
|
|
193
|
+
return [
|
|
194
|
+
c.bold(role),
|
|
195
|
+
c.cyan(config.models[role] || "(not set)"),
|
|
196
|
+
c.gray(AGENT_ROLES[role]?.mode || "subagent"),
|
|
197
|
+
enabled ? c.green("enabled") : c.gray("disabled"),
|
|
198
|
+
];
|
|
199
|
+
})
|
|
200
|
+
);
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
if (config.orchestrator) {
|
|
204
|
+
section("Orchestrator");
|
|
205
|
+
kv("Workflow", (config.orchestrator.workflowSteps || []).join(" \u2192 "));
|
|
206
|
+
kv("Require plan approval", String(config.orchestrator.requirePlanApproval ?? false));
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
if (config.instructions) {
|
|
210
|
+
section("Instructions");
|
|
211
|
+
for (const instr of config.instructions) {
|
|
212
|
+
status("dot", instr);
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
if (config.rules) {
|
|
217
|
+
section("Rules");
|
|
218
|
+
for (const rule of config.rules) {
|
|
219
|
+
status("dot", rule);
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
// Also show opencode.json status
|
|
224
|
+
const { existsSync } = await import("node:fs");
|
|
225
|
+
const opencodeConfig = join(targetDir, "opencode.json");
|
|
226
|
+
const ralphyConfig = join(targetDir, ".ralphy", "config.yaml");
|
|
227
|
+
|
|
228
|
+
console.log();
|
|
229
|
+
section("Integration Files");
|
|
230
|
+
status(
|
|
231
|
+
existsSync(opencodeConfig) ? "pass" : "warn",
|
|
232
|
+
"opencode.json",
|
|
233
|
+
existsSync(opencodeConfig) ? "exists" : "missing"
|
|
234
|
+
);
|
|
235
|
+
status(
|
|
236
|
+
existsSync(ralphyConfig) ? "pass" : "warn",
|
|
237
|
+
".ralphy/config.yaml",
|
|
238
|
+
existsSync(ralphyConfig) ? "exists" : "missing"
|
|
239
|
+
);
|
|
240
|
+
|
|
241
|
+
console.log();
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
// ── help command ───────────────────────────────────────────────────────────
|
|
245
|
+
|
|
246
|
+
async function printHelp() {
|
|
247
|
+
const { banner, c } = await import("../src/lib/ui.js");
|
|
248
|
+
const pkg = await loadPkg();
|
|
249
|
+
|
|
250
|
+
banner(
|
|
251
|
+
"opencode-auto-agent",
|
|
252
|
+
`v${pkg.version} \u2014 AI agent team starter kit (Ralphy + OpenCode)`
|
|
253
|
+
);
|
|
254
|
+
|
|
255
|
+
const indent = " ";
|
|
256
|
+
const cmd = (name, desc) => `${indent}${c.cyan(name.padEnd(28))} ${c.gray(desc)}`;
|
|
257
|
+
|
|
258
|
+
console.log(`${indent}${c.bold("USAGE")}`);
|
|
259
|
+
console.log(`${indent} npx opencode-auto-agent <command> [options]\n`);
|
|
260
|
+
|
|
261
|
+
console.log(`${indent}${c.bold("COMMANDS")}`);
|
|
262
|
+
console.log(cmd("init [--preset=<name>]", "Scaffold .opencode/ into the current project"));
|
|
263
|
+
console.log(cmd("run [--engine=<name>]", "Start the orchestrator (Ralphy task loop)"));
|
|
264
|
+
console.log(cmd("setup [preset]", "Apply or switch a preset"));
|
|
265
|
+
console.log(cmd("doctor", "Validate config, tools, and agent definitions"));
|
|
266
|
+
console.log(cmd("models [provider]", "Show available models from OpenCode"));
|
|
267
|
+
console.log(cmd("config", "Show current project configuration"));
|
|
268
|
+
console.log(cmd("help", "Show this help message"));
|
|
269
|
+
console.log();
|
|
270
|
+
|
|
271
|
+
console.log(`${indent}${c.bold("OPTIONS")}`);
|
|
272
|
+
console.log(cmd("--dir=<path>", "Target directory (default: cwd)"));
|
|
273
|
+
console.log(cmd("--preset=<name>", "Preset to apply during init/setup"));
|
|
274
|
+
console.log(cmd("--engine=<name>", "Ralphy engine (opencode | claude | cursor)"));
|
|
275
|
+
console.log(cmd("--force", "Overwrite existing files during init"));
|
|
276
|
+
console.log(cmd("--non-interactive", "Skip all prompts, use defaults"));
|
|
277
|
+
console.log(cmd("--dry-run", "Assemble context without launching Ralphy"));
|
|
278
|
+
console.log(cmd("--models", "Reconfigure model assignments (with setup)"));
|
|
279
|
+
console.log(cmd("--verbose", "Show extra detail (doctor, run)"));
|
|
280
|
+
console.log(cmd("--version, -v", "Show version"));
|
|
281
|
+
console.log();
|
|
282
|
+
|
|
283
|
+
console.log(`${indent}${c.bold("PRESETS")}`);
|
|
284
|
+
console.log(cmd("java", "General Java project (Maven/Gradle)"));
|
|
285
|
+
console.log(cmd("springboot", "Spring Boot 3.x web application"));
|
|
286
|
+
console.log(cmd("nextjs", "Next.js 14+ with App Router"));
|
|
287
|
+
console.log();
|
|
288
|
+
|
|
289
|
+
console.log(`${indent}${c.bold("EXAMPLES")}`);
|
|
290
|
+
console.log(`${indent} ${c.gray("$")} npx opencode-auto-agent init --preset=nextjs`);
|
|
291
|
+
console.log(`${indent} ${c.gray("$")} npx opencode-auto-agent setup springboot --models`);
|
|
292
|
+
console.log(`${indent} ${c.gray("$")} npx opencode-auto-agent models openai`);
|
|
293
|
+
console.log(`${indent} ${c.gray("$")} npx opencode-auto-agent run --dry-run`);
|
|
294
|
+
console.log(`${indent} ${c.gray("$")} npx opencode-auto-agent doctor --verbose`);
|
|
295
|
+
console.log();
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
// ── version command ────────────────────────────────────────────────────────
|
|
299
|
+
|
|
300
|
+
async function printVersion() {
|
|
301
|
+
const pkg = await loadPkg();
|
|
302
|
+
console.log(`opencode-auto-agent v${pkg.version}`);
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
// ── Helpers ────────────────────────────────────────────────────────────────
|
|
306
|
+
|
|
307
|
+
async function loadPkg() {
|
|
308
|
+
const { readFileSync } = await import("node:fs");
|
|
309
|
+
const { resolve: resolvePath, dirname } = await import("node:path");
|
|
310
|
+
const { fileURLToPath } = await import("node:url");
|
|
311
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
312
|
+
try {
|
|
313
|
+
return JSON.parse(readFileSync(resolvePath(__dirname, "..", "package.json"), "utf8"));
|
|
314
|
+
} catch {
|
|
315
|
+
return { version: "0.0.0" };
|
|
316
|
+
}
|
|
99
317
|
}
|
|
100
318
|
|
|
319
|
+
// ── Run ────────────────────────────────────────────────────────────────────
|
|
101
320
|
main().catch((err) => {
|
|
102
|
-
|
|
103
|
-
|
|
321
|
+
import("../src/lib/ui.js").then(({ error: uiError }) => {
|
|
322
|
+
uiError(err.message || String(err));
|
|
323
|
+
exit(1);
|
|
324
|
+
}).catch(() => {
|
|
325
|
+
console.error("Fatal:", err.message || err);
|
|
326
|
+
exit(1);
|
|
327
|
+
});
|
|
104
328
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "opencode-auto-agent",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.3.0",
|
|
4
4
|
"description": "Scaffold and run an AI agent team powered by Ralphy + OpenCode. Presets for Java, Spring Boot, Next.js, and more.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -15,8 +15,8 @@
|
|
|
15
15
|
],
|
|
16
16
|
"scripts": {
|
|
17
17
|
"dev": "node bin/cli.js",
|
|
18
|
-
"test": "node --test src/**/*.test.js
|
|
19
|
-
"verify": "node bin/cli.js help"
|
|
18
|
+
"test": "node --test src/**/*.test.js",
|
|
19
|
+
"verify": "npm run test && node bin/cli.js help"
|
|
20
20
|
},
|
|
21
21
|
"keywords": [
|
|
22
22
|
"ai",
|