@autonsh/cli 0.17.2 → 0.18.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/dist/index.js CHANGED
@@ -19,7 +19,9 @@ import { cmdModel, modelLs, modelSet, modelTest } from "./model.js";
19
19
  import { cmdChat } from "./chat.js";
20
20
  import { resolveAgent } from "./picker.js";
21
21
  import { getDefaultModel, getActiveProvider } from "./config.js";
22
- import { TEMPLATES, promptInit, promptConfirmUndeploy, promptGateApproval, promptGateQuestion, parseGateLine, } from "./prompts.js";
22
+ import { promptInit, promptConfirmUndeploy, promptGateApproval, promptGateQuestion, parseGateLine, listTemplatesForDisplay, } from "./prompts.js";
23
+ import { getTemplateRegistry } from "./template-registry.js";
24
+ import { renderTemplate } from "./template-engine.js";
23
25
  import { loadTaskManifest, MODES, SKILLS, EXIT, ExitError, } from "./task.js";
24
26
  import { runVerify } from "./verify.js";
25
27
  const SRC = dirname(fileURLToPath(import.meta.url));
@@ -109,59 +111,113 @@ async function installRuntime(dir) {
109
111
  bar.stop();
110
112
  }
111
113
  }
112
- async function cmdInit(name, template, opts = {}) {
114
+ async function cmdInit(name, templateId, opts = {}) {
115
+ const registry = await getTemplateRegistry();
116
+ const template = await registry.get(templateId);
117
+ if (!template) {
118
+ throw new ExitError(EXIT.USAGE, `template não encontrado: ${templateId}. Rode 'auton template list' para ver disponíveis.`);
119
+ }
113
120
  const plain = name === "." ? basename(process.cwd()) : name;
114
121
  const dir = resolve(name === "." ? process.cwd() : join(process.cwd(), name));
115
122
  if (existsSync(dir) && readdirSync(dir).length > 0) {
116
123
  throw new ExitError(EXIT.USAGE, `diretório não vazio: ${dir}`);
117
124
  }
125
+ // Prepara contexto de renderização
126
+ const context = {
127
+ name: plain,
128
+ ...opts.variables,
129
+ };
130
+ if (opts.dryRun) {
131
+ console.log(info(`[dry-run] Scaffold seria criado em: ${dir}`));
132
+ console.log(info(`[dry-run] Template: ${template.name} (${template.id})`));
133
+ console.log(info(`[dry-run] Variáveis: ${JSON.stringify(context, null, 2)}`));
134
+ return;
135
+ }
118
136
  mkdirSync(dir, { recursive: true });
119
- cpSync(join(TEMPLATES_SRC, template), dir, { recursive: true });
120
- // seta o nome real no manifest (task.yaml desde 0.9.1)
137
+ // Renderiza template via engine (Handlebars)
138
+ const result = await renderTemplate(template, context, dir);
139
+ // Substitui name no task.yaml (backward compat: templates built-in têm name hardcoded)
121
140
  const yamlPath = join(dir, "task.yaml");
122
- const yaml = (await readFile(yamlPath, "utf8")).replace(/^name: .*$/m, `name: ${plain}`);
123
- await writeFile(yamlPath, yaml);
124
- // mode (pivô 0.11): permissões do worker; docs do modo viram agent.md do projeto.
141
+ if (existsSync(yamlPath)) {
142
+ let yaml = await readFile(yamlPath, "utf8");
143
+ yaml = yaml.replace(/^name: .*$/m, `name: ${plain}`);
144
+ await writeFile(yamlPath, yaml);
145
+ }
146
+ // Garante package.json (pode vir do template ou ser gerado)
147
+ const pkgPath = join(dir, "package.json");
148
+ if (!existsSync(pkgPath)) {
149
+ const pkgName = plain.toLowerCase().replace(/[^a-z0-9._-]/g, "-");
150
+ await writeFile(pkgPath, JSON.stringify({
151
+ name: pkgName || "agent",
152
+ version: "0.0.0",
153
+ private: true,
154
+ type: "module",
155
+ dependencies: { "@autonsh/runtime": "^0.11.0" },
156
+ }, null, 2) + "\n");
157
+ }
158
+ // Genesis custom (se fornecido via --genesis ou prompt)
159
+ if (opts.genesis) {
160
+ const yamlPath = join(dir, "task.yaml");
161
+ let yaml = await readFile(yamlPath, "utf8");
162
+ // Substitui ou adiciona bloco genesis
163
+ if (yaml.includes("genesis:")) {
164
+ yaml = yaml.replace(/^genesis:[\s\S]*?(?=\n\S|\n*$)/m, `genesis: |\n ${opts.genesis.replace(/\n/g, "\n ")}\n`);
165
+ }
166
+ else {
167
+ yaml += `\ngenesis: |\n ${opts.genesis.replace(/\n/g, "\n ")}\n`;
168
+ }
169
+ await writeFile(yamlPath, yaml);
170
+ }
171
+ // Mode + skills do template (podem ser sobrescritos por opts)
125
172
  const mode = opts.mode && MODES.includes(opts.mode)
126
173
  ? opts.mode
127
- : "build";
174
+ : template.defaultMode;
175
+ const skills = (opts.skills ?? template.skills).filter((s) => SKILLS.includes(s));
176
+ // Atualiza task.yaml com mode/skills apenas se override explícito
177
+ if (opts.mode || opts.skills) {
178
+ const yamlPath2 = join(dir, "task.yaml");
179
+ let yaml = await readFile(yamlPath2, "utf8");
180
+ yaml = yaml
181
+ .split("\n")
182
+ .filter((line) => !/^(mode|skills):/m.test(line) && !/^\s*-\s/.test(line))
183
+ .join("\n")
184
+ .trimEnd()
185
+ .concat("\n", `mode: ${mode}`, skills.length > 0
186
+ ? `\nskills:\n${skills.map((s) => ` - ${s}`).join("\n")}`
187
+ : "")
188
+ .concat("\n");
189
+ await writeFile(yamlPath2, yaml);
190
+ }
191
+ // Copia agent.md do modo se não existir
128
192
  const modeDoc = join(TEMPLATES_SRC, "modes", mode, "agent.md");
129
- if (existsSync(modeDoc)) {
193
+ if (existsSync(modeDoc) && !existsSync(join(dir, "agent.md"))) {
130
194
  cpSync(modeDoc, join(dir, "agent.md"));
131
195
  }
132
- const skills = (opts.skills ?? []).filter((s) => SKILLS.includes(s));
196
+ // Skills built-in
133
197
  for (const s of skills) {
134
- cpSync(join(TEMPLATES_SRC, "skills", s, "SKILL.md"), join(dir, "skills", s, "SKILL.md"), {
135
- recursive: true,
136
- });
198
+ try {
199
+ cpSync(join(TEMPLATES_SRC, "skills", s, "SKILL.md"), join(dir, "skills", s, "SKILL.md"), {
200
+ recursive: true,
201
+ });
202
+ }
203
+ catch {
204
+ // skill não existe — ignora
205
+ }
137
206
  }
138
- await writeFile(yamlPath, yaml
139
- .split("\n")
140
- .filter((line) => !/^(mode|skills):/m.test(line))
141
- .join("\n")
142
- .trimEnd()
143
- .concat("\n", `mode: ${mode}`, skills.length > 0
144
- ? `\nskills:\n${skills.map((s) => ` - ${s}`).join("\n")}`
145
- : "")
146
- .concat("\n"));
147
- // package.json do projeto (0.0.7): remove o passo manual de criar pacote.
148
- // `npm install` abaixo salva/adiciona a dep se preciso; aqui o pacote já existe.
149
- const pkgName = plain.toLowerCase().replace(/[^a-z0-9._-]/g, "-");
150
- await writeFile(join(dir, "package.json"), JSON.stringify({
151
- name: pkgName || "agent",
152
- version: "0.0.0",
153
- private: true,
154
- type: "module",
155
- dependencies: { "@autonsh/runtime": "^0.11.0" },
156
- }, null, 2) + "\n");
157
207
  ensureRuntimeLink(dir);
158
208
  if (!HAS_LOCAL_RUNTIME) {
159
209
  await installRuntime(dir);
160
210
  }
161
211
  if (isTTY)
162
212
  console.log(logo());
163
- console.log(ok(`${plain} (template: ${template}) em ${dim(dir)}`));
213
+ console.log(ok(`${plain} (template: ${template.name}) em ${dim(dir)}`));
164
214
  console.log(info(`próximo: ${bold(`cd ${plain} && auton run "<objetivo>"`)}`));
215
+ // Deploy opcional
216
+ if (opts.deploy) {
217
+ console.log(info(`deployando ${bold(plain)}...`));
218
+ await cmdDeploy(dir);
219
+ console.log(ok(`${plain} deployado`));
220
+ }
165
221
  }
166
222
  const VERSION = JSON.parse(readFileSync(resolve(SRC, "../package.json"), "utf8")).version;
167
223
  const API = process.env.AUTON_API ?? "http://localhost:8080";
@@ -261,29 +317,29 @@ Usage:
261
317
 
262
318
  Modelos:
263
319
  auton model Seletor interativo do modelo default
264
- (local ollama + providers configurados)
320
+ (local ollama + providers configurados)
265
321
  auton model ls [--json] Lista modelos locais + providers
266
322
  auton model set <spec> Define default (ex: ollama:qwen3)
267
323
  auton model test Testa latência dos providers
268
324
  auton login [provider] [--apikey X]
269
- Conecta provider (gemini/groq/ollama)
325
+ Conecta provider (gemini/groq/ollama)
270
326
  auton logout [id] Remove provider configurado
271
327
 
272
328
  Agentes:
273
329
  auton [nome] Sessão de trabalho (estilo opencode): dá
274
- tasks, acompanha o agente ao vivo, gates
275
- inline. Sem nome: cwd/picker.
330
+ tasks, acompanha o agente ao vivo, gates
331
+ inline. Sem nome: cwd/picker.
276
332
  auton chat [name] Alias da sessão de trabalho
277
333
  auton agent [dir] Deploy do agente no diretório
278
334
  auton ps Lista agentes + status
279
335
  auton logs [name] [--tail N] [--follow] [--since X] [--turn N]
280
- Stream de logs do agente (SSE)
336
+ Stream de logs do agente (SSE)
281
337
  auton status [name] Status do agente
282
338
  auton restart [name] Reinicia o agente
283
339
  auton invoke [name] [--stream] [-- prompt]
284
- Roda 1 turno (legado v0)
340
+ Roda 1 turno (legado v0)
285
341
  auton approve|reject [name] [-- razão]
286
- Decide o gate de aprovação
342
+ Decide o gate de aprovação
287
343
  auton answer [name] [-- texto] Responde pergunta pendente (ctx.ask)
288
344
  auton context [name] Work context: decisions/findings/artifacts
289
345
  auton undeploy [name] Remove o agente (--all remove tudo)
@@ -296,13 +352,18 @@ Tasks:
296
352
  auton verify [dir] [--json] Roda gates mecânicos do verify
297
353
  auton inspect [dir] [--json] Inspeciona task + evidência
298
354
  auton continue [name] [goal] Retoma a última task (sem redeploy)
299
- auton init [nome] [template] Scaffold de task (support|with-approval|...)
355
+ auton init [nome] [template] [--mode=X] [--skills=a,b] [--deploy] [--dry-run] [--genesis] [--var key=value]
356
+ Scaffold de task (templates built-in + custom)
357
+
358
+ Templates:
359
+ auton template list [--json] Lista templates (built-in + custom)
360
+ auton template create <id> Cria template custom (futuro)
300
361
 
301
362
  Infra:
302
363
  auton kv get|set|ls [name] [key] [value]
303
- Inspeciona o storage KV
364
+ Inspeciona o storage KV
304
365
  auton env set|get|ls [name] [key] [value]
305
- Env vars por agente
366
+ Env vars por agente
306
367
 
307
368
  Options:
308
369
  --help, -h Mostra ajuda
@@ -1303,6 +1364,69 @@ async function cmdLogin(realArgs) {
1303
1364
  async function cmdLogout(providerId) {
1304
1365
  await logoutCmd(providerId);
1305
1366
  }
1367
+ /** Project mode: `auton` bare — se não há agente, cria + deploya + entra no chat. */
1368
+ async function cmdProject() {
1369
+ // 1. Tenta achar agente no cwd (task.yaml)
1370
+ let agentName;
1371
+ try {
1372
+ const { loadTaskManifest } = await import("./task.js");
1373
+ const manifest = await loadTaskManifest(process.cwd());
1374
+ if (manifest?.name)
1375
+ agentName = manifest.name;
1376
+ }
1377
+ catch {
1378
+ // sem task.yaml
1379
+ }
1380
+ // 2. Se não há no cwd, tenta deployed agents
1381
+ if (!agentName) {
1382
+ const deployed = await listAgentNames();
1383
+ if (deployed.length === 1) {
1384
+ agentName = deployed[0];
1385
+ }
1386
+ else if (deployed.length > 1) {
1387
+ // Múltiplos deployados — usa picker (cmdChat já faz isso)
1388
+ await cmdChat(undefined);
1389
+ return;
1390
+ }
1391
+ }
1392
+ // 3. Tem agente (cwd ou único deployado) → entra no chat
1393
+ if (agentName) {
1394
+ console.log(info(`entrando no agente ${bold(agentName)}...`));
1395
+ await cmdChat(agentName);
1396
+ return;
1397
+ }
1398
+ // 4. Nenhum agente — oferece init interativo
1399
+ console.log(box("auton — nenhum agente encontrado", [
1400
+ "Este diretório não tem task.yaml e não há agentes deployados.",
1401
+ "Quer criar um novo agente aqui?",
1402
+ ]));
1403
+ const { confirm } = await import("@clack/prompts");
1404
+ const create = await confirm({
1405
+ message: "criar novo agente neste diretório?",
1406
+ active: "sim",
1407
+ inactive: "não",
1408
+ });
1409
+ if (!create) {
1410
+ console.log(dim("até logo."));
1411
+ return;
1412
+ }
1413
+ // Init interativo
1414
+ const { promptInit } = await import("./prompts.js");
1415
+ const p = await promptInit();
1416
+ // Deploy automático
1417
+ console.log(info(`criando e deployando ${bold(p.name)}...`));
1418
+ await cmdInit(p.name, p.templateId, {
1419
+ mode: p.mode,
1420
+ skills: p.skills,
1421
+ genesis: p.genesis,
1422
+ deploy: true,
1423
+ dryRun: false,
1424
+ variables: p.variables,
1425
+ });
1426
+ // Entra no chat
1427
+ console.log(info(`entrando no agente ${bold(p.name)}...`));
1428
+ await cmdChat(p.name);
1429
+ }
1306
1430
  async function main() {
1307
1431
  let realArgs = process.argv.slice(2);
1308
1432
  // G3: auto-start CP para comandos que precisam dele (--no-auto-cp desliga)
@@ -1317,8 +1441,9 @@ async function main() {
1317
1441
  if (realArgs.length === 0) {
1318
1442
  // 0.17.0 — `auton` bare: abre sessão de trabalho (estilo opencode):
1319
1443
  // cwd é agente → sessão nele; senão picker. Pipe: mosta ajuda.
1444
+ // NOVO: project mode — se não há agente, oferece init + deploy + chat
1320
1445
  if (isTTY) {
1321
- await cmdChat(undefined);
1446
+ await cmdProject();
1322
1447
  return;
1323
1448
  }
1324
1449
  console.log(USAGE);
@@ -1345,6 +1470,7 @@ async function main() {
1345
1470
  "login", "logout", "model", "agent", "logs", "status", "restart", "run",
1346
1471
  "verify", "inspect", "continue", "invoke", "undeploy", "approve", "reject",
1347
1472
  "ps", "context", "env", "init", "kv", "chat", "answer", "wait", "result",
1473
+ "template",
1348
1474
  ]);
1349
1475
  if (!KNOWN.has(realArgs[0]) && isTTY) {
1350
1476
  const deployed = await listAgentNames();
@@ -1594,27 +1720,88 @@ async function main() {
1594
1720
  break;
1595
1721
  }
1596
1722
  case "init": {
1597
- // deploy init [nome] [template] [--mode=X] [--skills=a,b] sem argumentos (TTY): pergunta interativo
1723
+ // auton init [nome] [template] [--mode=X] [--skills=a,b] [--deploy] [--dry-run] [--genesis] [--var key=value]
1598
1724
  const initFlags = realArgs.filter((a) => a.startsWith("--"));
1599
1725
  const initPos = realArgs.filter((a) => !a.startsWith("--"));
1600
- const initOpts = {
1601
- mode: initFlags.find((f) => f.startsWith("--mode="))?.slice(7),
1602
- skills: initFlags
1603
- .find((f) => f.startsWith("--skills="))
1604
- ?.slice(9)
1605
- .split(",")
1606
- .filter(Boolean),
1607
- };
1726
+ // Parse flags
1727
+ const deploy = initFlags.includes("--deploy");
1728
+ const dryRun = initFlags.includes("--dry-run");
1729
+ const mode = initFlags.find((f) => f.startsWith("--mode="))?.slice(7);
1730
+ const skills = initFlags
1731
+ .find((f) => f.startsWith("--skills="))
1732
+ ?.slice(9)
1733
+ .split(",")
1734
+ .filter(Boolean);
1735
+ const genesis = initFlags.find((f) => f.startsWith("--genesis="))?.slice(10);
1736
+ // Parse --var key=value
1737
+ const variables = {};
1738
+ for (const f of initFlags) {
1739
+ if (f.startsWith("--var=")) {
1740
+ const rest = f.slice(6); // remove "--var="
1741
+ const eqIdx = rest.indexOf("=");
1742
+ if (eqIdx > 0) {
1743
+ const key = rest.slice(0, eqIdx);
1744
+ const val = rest.slice(eqIdx + 1);
1745
+ variables[key] = val === "true" ? true : val === "false" ? false : val;
1746
+ }
1747
+ }
1748
+ }
1608
1749
  if (isTTY && !initPos[1] && !initPos[2]) {
1609
1750
  const p = await promptInit();
1610
- await cmdInit(p.name, p.template, initOpts);
1751
+ await cmdInit(p.name, p.templateId, {
1752
+ mode: p.mode ?? mode,
1753
+ skills: p.skills ?? skills,
1754
+ genesis: p.genesis ?? genesis,
1755
+ deploy,
1756
+ dryRun,
1757
+ variables,
1758
+ });
1611
1759
  }
1612
1760
  else {
1613
- const tpl = (initPos[2] ?? "support");
1614
- if (!TEMPLATES.includes(tpl)) {
1615
- throw new ExitError(EXIT.USAGE, `template desconhecido: ${tpl} (disponíveis: ${TEMPLATES.join(", ")})`);
1761
+ const name = initPos[1] ?? ".";
1762
+ const templateId = initPos[2];
1763
+ if (!templateId) {
1764
+ throw new ExitError(EXIT.USAGE, "uso: auton init <nome> <template-id> [--deploy] [--dry-run] ...");
1616
1765
  }
1617
- await cmdInit(initPos[1] ?? ".", tpl, initOpts);
1766
+ // Valida template existe
1767
+ const registry = await getTemplateRegistry();
1768
+ const tpl = await registry.get(templateId);
1769
+ if (!tpl) {
1770
+ const list = await listTemplatesForDisplay();
1771
+ throw new ExitError(EXIT.USAGE, `template desconhecido: ${templateId}\nDisponíveis: ${list.map(t => `${t.id} (${t.category})`).join(", ")}`);
1772
+ }
1773
+ await cmdInit(name, templateId, {
1774
+ mode,
1775
+ skills,
1776
+ genesis,
1777
+ deploy,
1778
+ dryRun,
1779
+ variables,
1780
+ });
1781
+ }
1782
+ break;
1783
+ }
1784
+ case "template": {
1785
+ const sub = realArgs[1];
1786
+ if (sub === "list" || sub === "ls" || !sub) {
1787
+ const list = await listTemplatesForDisplay();
1788
+ if (realArgs.includes("--json")) {
1789
+ console.log(JSON.stringify(list, null, 2));
1790
+ }
1791
+ else {
1792
+ console.log(bold("Templates disponíveis:"));
1793
+ for (const t of list) {
1794
+ const srcTag = t.source === "custom" ? pc.cyan("(custom)") : pc.dim("(builtin)");
1795
+ console.log(` ${bold(t.id)} ${srcTag} — ${t.name} [${t.category}]`);
1796
+ console.log(` ${dim(t.description)}`);
1797
+ }
1798
+ }
1799
+ }
1800
+ else if (sub === "create") {
1801
+ console.log(info("Use 'auton init' com template custom — criação de template virá em versão futura"));
1802
+ }
1803
+ else {
1804
+ console.log(USAGE);
1618
1805
  }
1619
1806
  break;
1620
1807
  }