opencode-auto-agent 1.0.0 → 1.2.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 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, and supports stack presets like Java, Spring Boot, and Next.js.
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 <preset>
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
 
@@ -54,7 +68,7 @@ npx opencode-auto-agent run
54
68
  The `init` command creates:
55
69
 
56
70
  ```text
57
- .ai-starter-kit/
71
+ .opencode/
58
72
  agents/
59
73
  presets/
60
74
  context/
@@ -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 Scaffold .ai-starter-kit/ into the current repo
8
- * run Start the orchestrator (Ralphy + OpenCode agents)
9
- * setup <preset> Apply or switch a preset (java, springboot, nextjs, ...)
10
- * doctor Validate configuration and tool availability
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, basename } from "node:path";
16
+ import { resolve } from "node:path";
14
17
  import { argv, exit, cwd } from "node:process";
15
18
 
16
- // ── arg parsing (zero-dep) ─────────────────────────────────────────────────
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
- // ── dynamic import of command modules ──────────────────────────────────────
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, { preset: positional[0] || flags.preset });
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, flags);
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
- if (!preset) {
48
- console.error("Usage: opencode-auto-agent setup <preset>");
49
- console.error("Available presets: java, springboot, nextjs");
50
- exit(1);
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
- printHelp();
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
- function printHelp() {
70
- const name = "opencode-auto-agent";
71
- console.log(`
72
- ${name} v0.1.0 AI agent team starter kit (Ralphy + OpenCode)
73
-
74
- USAGE
75
- npx ${name} <command> [options]
76
-
77
- COMMANDS
78
- init [--preset=<name>] Scaffold .ai-starter-kit/ into the current project
79
- run [--engine=<name>] Start the orchestrator (Ralphy task loop)
80
- setup <preset> Apply or switch a preset (java | springboot | nextjs)
81
- doctor Validate config, tools, and agent definitions
82
-
83
- OPTIONS
84
- --dir=<path> Target directory (default: cwd)
85
- --preset=<name> Preset to apply during init
86
- --engine=<name> Ralphy engine (opencode | claude | cursor, default: opencode)
87
-
88
- PRESETS
89
- java General Java project (Maven/Gradle)
90
- springboot Spring Boot web application
91
- nextjs Next.js (React) application
92
-
93
- EXAMPLES
94
- npx ${name} init --preset=nextjs
95
- npx ${name} run
96
- npx ${name} setup springboot
97
- npx ${name} doctor
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
- console.error("Fatal:", err.message || err);
103
- exit(1);
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.0.0",
3
+ "version": "1.2.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",