@innominatum/agentforge-cli 1.1.3 → 1.1.9

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
@@ -301,29 +301,20 @@ async function resolveAgentId(slug, config) {
301
301
  const deployCmd = program
302
302
  .command("deploy")
303
303
  .description("Faz o deploy de entidades para a plataforma GoClaw");
304
- deployCmd
305
- .command("skill <slug>")
306
- .description("Faz o build da skill e envia para a API do GoClaw")
307
- .action(async (slug) => {
308
- const config = await getConfig();
309
- if (!config.goclaw || !config.goclaw.token) {
310
- console.error("❌ Configure sua chave de API (token) no agentforge.json antes de fazer o deploy.");
311
- process.exit(1);
312
- }
313
- const basePath = getWorkspaceRoot();
304
+ async function deploySkill(slug, config, basePath) {
314
305
  const skillPath = path_1.default.join(basePath, "skills", slug);
315
306
  const exportsPath = path_1.default.join(basePath, "exports");
316
- const zipPath = path_1.default.join(exportsPath, `${slug}.zip`);
307
+ const safeSlug = slug.replace(/[\\\/]/g, '_');
308
+ const zipPath = path_1.default.join(exportsPath, `${safeSlug}.zip`);
317
309
  if (!(await fs_extra_1.default.pathExists(skillPath))) {
318
310
  console.error(`❌ A skill "${slug}" não foi encontrada em skills/${slug}.`);
319
- process.exit(1);
311
+ return;
320
312
  }
321
313
  await fs_extra_1.default.ensureDir(exportsPath);
322
314
  const zip = new adm_zip_1.default();
323
315
  zip.addLocalFolder(skillPath, "");
324
316
  zip.writeZip(zipPath);
325
- console.log(`✅ Build concluído: ${slug}.zip preparado para envio.`);
326
- console.log(`🚀 Fazendo upload para o GoClaw...`);
317
+ console.log(`🚀 Fazendo upload da skill "${slug}" para o GoClaw...`);
327
318
  const form = new form_data_1.default();
328
319
  form.append("file", fs_extra_1.default.createReadStream(zipPath));
329
320
  try {
@@ -336,11 +327,58 @@ deployCmd
336
327
  });
337
328
  const data = response.data;
338
329
  if (data && data.version) {
339
- console.log(`📌 Skill atualizada para a versão ${data.version}.`);
330
+ console.log(`✅ Arquivos da skill "${slug}" atualizados (versão ${data.version}).`);
331
+ }
332
+ else {
333
+ console.log(`✅ Arquivos da skill "${slug}" atualizados.`);
334
+ }
335
+ // Sincronizar metadados (visibility, description, tags, etc)
336
+ const skillsListRes = await axios_1.default.get(`${config.goclaw.api_url}/v1/skills`, {
337
+ headers: { Authorization: `Bearer ${config.goclaw.token}`, "X-GoClaw-User-Id": config.goclaw.username || "system" }
338
+ });
339
+ const remoteSkill = skillsListRes.data.skills?.find((s) => s.slug === slug);
340
+ if (remoteSkill) {
341
+ const metadataPath = path_1.default.join(skillPath, "metadata.json");
342
+ if (await fs_extra_1.default.pathExists(metadataPath)) {
343
+ console.log(`🚀 Sincronizando metadados da skill "${slug}"...`);
344
+ const metadata = await fs_extra_1.default.readJson(metadataPath);
345
+ // Remover campos que não devem ser enviados no PUT
346
+ const payload = { ...metadata };
347
+ delete payload.id;
348
+ delete payload.slug;
349
+ delete payload.name;
350
+ await axios_1.default.put(`${config.goclaw.api_url}/v1/skills/${remoteSkill.id}`, payload, {
351
+ headers: { Authorization: `Bearer ${config.goclaw.token}`, "X-GoClaw-User-Id": config.goclaw.username || "system" }
352
+ });
353
+ console.log(`✅ Metadados sincronizados com sucesso.`);
354
+ }
355
+ // Sincronizar permissões (grants)
356
+ const grantsPath = path_1.default.join(skillPath, "grants.jsonl");
357
+ if (await fs_extra_1.default.pathExists(grantsPath)) {
358
+ console.log(`🚀 Sincronizando permissões (grants) da skill "${slug}"...`);
359
+ const grantsContent = await fs_extra_1.default.readFile(grantsPath, 'utf8');
360
+ const lines = grantsContent.split('\n').filter(l => l.trim());
361
+ for (const line of lines) {
362
+ try {
363
+ const grant = JSON.parse(line);
364
+ if (grant.agent_key) {
365
+ const agentId = await resolveAgentId(grant.agent_key, config);
366
+ if (agentId) {
367
+ await axios_1.default.post(`${config.goclaw.api_url}/v1/skills/${remoteSkill.id}/grants/agent`, { agent_id: agentId, version: grant.pinned_version || null }, { headers: { Authorization: `Bearer ${config.goclaw.token}`, "X-GoClaw-User-Id": config.goclaw.username || "system" } });
368
+ console.log(` ➕ Permissão concedida ao agente: ${grant.agent_key}`);
369
+ }
370
+ }
371
+ }
372
+ catch (e) {
373
+ console.warn(` ⚠️ Falha ao conceder permissão: ${e.response?.data?.error || e.message}`);
374
+ }
375
+ }
376
+ console.log(`✅ Permissões sincronizadas.`);
377
+ }
340
378
  }
341
379
  }
342
380
  catch (error) {
343
- console.error("❌ Erro durante o deploy:");
381
+ console.error(`❌ Erro no deploy da skill "${slug}":`);
344
382
  if (error.response) {
345
383
  console.error(error.response.data);
346
384
  }
@@ -348,6 +386,18 @@ deployCmd
348
386
  console.error(error.message);
349
387
  }
350
388
  }
389
+ }
390
+ deployCmd
391
+ .command("skill <slug>")
392
+ .description("Faz build e upload automático de uma skill para o GoClaw")
393
+ .action(async (slug) => {
394
+ const config = await getConfig();
395
+ if (!config.goclaw || !config.goclaw.token) {
396
+ console.error("❌ Configure sua chave de API (token) no agentforge.json antes de fazer o deploy.");
397
+ process.exit(1);
398
+ }
399
+ const basePath = getWorkspaceRoot();
400
+ await deploySkill(slug, config, basePath);
351
401
  });
352
402
  async function deployContextFiles(slug, config, resolvedId) {
353
403
  const agentId = resolvedId || (await resolveAgentId(slug, config)) || slug;
@@ -481,8 +531,44 @@ deployCmd
481
531
  }
482
532
  await deployAgent(slug, config);
483
533
  });
534
+ async function deployAllAgents(config, basePath) {
535
+ const agentsDir = path_1.default.join(basePath, "agents");
536
+ if (await fs_extra_1.default.pathExists(agentsDir)) {
537
+ const agents = await fs_extra_1.default.readdir(agentsDir);
538
+ console.log(`🚀 Iniciando deploy em lote de ${agents.length} agentes...`);
539
+ for (const slug of agents) {
540
+ const agentPath = path_1.default.join(agentsDir, slug);
541
+ if ((await fs_extra_1.default.stat(agentPath)).isDirectory()) {
542
+ await deployAgent(slug, config);
543
+ }
544
+ }
545
+ }
546
+ else {
547
+ console.log("Nenhum agente encontrado em agents/.");
548
+ }
549
+ }
550
+ async function deployAllSkills(config, basePath) {
551
+ const skillsDir = path_1.default.join(basePath, "skills");
552
+ if (await fs_extra_1.default.pathExists(skillsDir)) {
553
+ const skills = await fs_extra_1.default.readdir(skillsDir);
554
+ console.log(`🚀 Iniciando deploy em lote de skills...`);
555
+ for (const item of skills) {
556
+ const itemPath = path_1.default.join(skillsDir, item);
557
+ if ((await fs_extra_1.default.stat(itemPath)).isDirectory()) {
558
+ if (item === "system") {
559
+ console.log("⏩ Ignorando pasta 'system/' (skills nativas do GoClaw são apenas de leitura)");
560
+ continue;
561
+ }
562
+ await deploySkill(item, config, basePath);
563
+ }
564
+ }
565
+ }
566
+ else {
567
+ console.log("Nenhuma skill encontrada em skills/.");
568
+ }
569
+ }
484
570
  deployCmd
485
- .command("all")
571
+ .command("agents")
486
572
  .description("Faz deploy de todos os agentes do workspace")
487
573
  .action(async () => {
488
574
  const config = await getConfig();
@@ -491,20 +577,35 @@ deployCmd
491
577
  process.exit(1);
492
578
  }
493
579
  const basePath = getWorkspaceRoot();
494
- const agentsDir = path_1.default.join(basePath, "agents");
495
- if (!(await fs_extra_1.default.pathExists(agentsDir))) {
496
- console.log("Nenhum agente encontrado em agents/.");
497
- return;
580
+ await deployAllAgents(config, basePath);
581
+ console.log("🏁 Deploy de agentes concluído!");
582
+ });
583
+ deployCmd
584
+ .command("skills")
585
+ .description("Faz deploy de todas as skills do workspace")
586
+ .action(async () => {
587
+ const config = await getConfig();
588
+ if (!config.goclaw || !config.goclaw.token) {
589
+ console.error("❌ Configure sua chave de API (token) no agentforge.json.");
590
+ process.exit(1);
498
591
  }
499
- const agents = await fs_extra_1.default.readdir(agentsDir);
500
- console.log(`🚀 Iniciando deploy em lote de ${agents.length} agentes...`);
501
- for (const slug of agents) {
502
- const agentPath = path_1.default.join(agentsDir, slug);
503
- if ((await fs_extra_1.default.stat(agentPath)).isDirectory()) {
504
- await deployAgent(slug, config);
505
- }
592
+ const basePath = getWorkspaceRoot();
593
+ await deployAllSkills(config, basePath);
594
+ console.log("🏁 Deploy de skills concluído!");
595
+ });
596
+ deployCmd
597
+ .command("all")
598
+ .description("Faz deploy de todos os agentes e skills do workspace")
599
+ .action(async () => {
600
+ const config = await getConfig();
601
+ if (!config.goclaw || !config.goclaw.token) {
602
+ console.error("❌ Configure sua chave de API (token) no agentforge.json.");
603
+ process.exit(1);
506
604
  }
507
- console.log("🏁 Deploy em lote concluído!");
605
+ const basePath = getWorkspaceRoot();
606
+ await deployAllAgents(config, basePath);
607
+ await deployAllSkills(config, basePath);
608
+ console.log("🏁 Deploy completo (agentes e skills) concluído!");
508
609
  });
509
610
  const pullCmd = program
510
611
  .command("pull")
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@innominatum/agentforge-cli",
3
- "version": "1.1.3",
3
+ "version": "1.1.9",
4
4
  "description": "A powerful command-line interface to scaffold, manage, build, and deploy AI Agents and Skills for the GoClaw platform.",
5
5
  "main": "dist/index.js",
6
6
  "bin": {
package/src/index.ts CHANGED
@@ -314,9 +314,106 @@ const deployCmd = program
314
314
  .command("deploy")
315
315
  .description("Faz o deploy de entidades para a plataforma GoClaw");
316
316
 
317
+ async function deploySkill(slug: string, config: any, basePath: string) {
318
+ const skillPath = path.join(basePath, "skills", slug);
319
+ const exportsPath = path.join(basePath, "exports");
320
+ const safeSlug = slug.replace(/[\\\/]/g, '_');
321
+ const zipPath = path.join(exportsPath, `${safeSlug}.zip`);
322
+
323
+ if (!(await fs.pathExists(skillPath))) {
324
+ console.error(`❌ A skill "${slug}" não foi encontrada em skills/${slug}.`);
325
+ return;
326
+ }
327
+
328
+ await fs.ensureDir(exportsPath);
329
+ const zip = new AdmZip();
330
+ zip.addLocalFolder(skillPath, "");
331
+ zip.writeZip(zipPath);
332
+
333
+ console.log(`🚀 Fazendo upload da skill "${slug}" para o GoClaw...`);
334
+ const form = new FormData();
335
+ form.append("file", fs.createReadStream(zipPath));
336
+
337
+ try {
338
+ const url = `${config.goclaw.api_url}/v1/skills/upload`;
339
+ const response = await axios.post(url, form, {
340
+ headers: {
341
+ ...form.getHeaders(),
342
+ Authorization: `Bearer ${config.goclaw.token}`, "X-GoClaw-User-Id": config.goclaw.username || "system"
343
+ }
344
+ });
345
+ const data = response.data;
346
+ if (data && data.version) {
347
+ console.log(`✅ Arquivos da skill "${slug}" atualizados (versão ${data.version}).`);
348
+ } else {
349
+ console.log(`✅ Arquivos da skill "${slug}" atualizados.`);
350
+ }
351
+
352
+ // Sincronizar metadados (visibility, description, tags, etc)
353
+ const skillsListRes = await axios.get(`${config.goclaw.api_url}/v1/skills`, {
354
+ headers: { Authorization: `Bearer ${config.goclaw.token}`, "X-GoClaw-User-Id": config.goclaw.username || "system" }
355
+ });
356
+ const remoteSkill = skillsListRes.data.skills?.find((s: any) => s.slug === slug);
357
+
358
+ if (remoteSkill) {
359
+ const metadataPath = path.join(skillPath, "metadata.json");
360
+ if (await fs.pathExists(metadataPath)) {
361
+ console.log(`🚀 Sincronizando metadados da skill "${slug}"...`);
362
+ const metadata = await fs.readJson(metadataPath);
363
+
364
+ // Remover campos que não devem ser enviados no PUT
365
+ const payload = { ...metadata };
366
+ delete payload.id;
367
+ delete payload.slug;
368
+ delete payload.name;
369
+
370
+ await axios.put(`${config.goclaw.api_url}/v1/skills/${remoteSkill.id}`, payload, {
371
+ headers: { Authorization: `Bearer ${config.goclaw.token}`, "X-GoClaw-User-Id": config.goclaw.username || "system" }
372
+ });
373
+ console.log(`✅ Metadados sincronizados com sucesso.`);
374
+ }
375
+
376
+ // Sincronizar permissões (grants)
377
+ const grantsPath = path.join(skillPath, "grants.jsonl");
378
+ if (await fs.pathExists(grantsPath)) {
379
+ console.log(`🚀 Sincronizando permissões (grants) da skill "${slug}"...`);
380
+ const grantsContent = await fs.readFile(grantsPath, 'utf8');
381
+ const lines = grantsContent.split('\n').filter(l => l.trim());
382
+
383
+ for (const line of lines) {
384
+ try {
385
+ const grant = JSON.parse(line);
386
+ if (grant.agent_key) {
387
+ const agentId = await resolveAgentId(grant.agent_key, config);
388
+ if (agentId) {
389
+ await axios.post(`${config.goclaw.api_url}/v1/skills/${remoteSkill.id}/grants/agent`,
390
+ { agent_id: agentId, version: grant.pinned_version || null },
391
+ { headers: { Authorization: `Bearer ${config.goclaw.token}`, "X-GoClaw-User-Id": config.goclaw.username || "system" } }
392
+ );
393
+ console.log(` ➕ Permissão concedida ao agente: ${grant.agent_key}`);
394
+ }
395
+ }
396
+ } catch (e: any) {
397
+ console.warn(` ⚠️ Falha ao conceder permissão: ${e.response?.data?.error || e.message}`);
398
+ }
399
+ }
400
+ console.log(`✅ Permissões sincronizadas.`);
401
+ }
402
+ }
403
+
404
+ } catch (error: any) {
405
+ console.error(`❌ Erro no deploy da skill "${slug}":`);
406
+ if (error.response) {
407
+ console.error(error.response.data);
408
+ } else {
409
+ console.error(error.message);
410
+ }
411
+ }
412
+ }
413
+
317
414
  deployCmd
318
415
  .command("skill <slug>")
319
- .description("Faz o build da skill e envia para a API do GoClaw")
416
+ .description("Faz build e upload automático de uma skill para o GoClaw")
320
417
  .action(async (slug: string) => {
321
418
  const config = await getConfig();
322
419
  if (!config.goclaw || !config.goclaw.token) {
@@ -325,46 +422,7 @@ deployCmd
325
422
  }
326
423
 
327
424
  const basePath = getWorkspaceRoot();
328
- const skillPath = path.join(basePath, "skills", slug);
329
- const exportsPath = path.join(basePath, "exports");
330
- const zipPath = path.join(exportsPath, `${slug}.zip`);
331
-
332
- if (!(await fs.pathExists(skillPath))) {
333
- console.error(`❌ A skill "${slug}" não foi encontrada em skills/${slug}.`);
334
- process.exit(1);
335
- }
336
-
337
- await fs.ensureDir(exportsPath);
338
- const zip = new AdmZip();
339
- zip.addLocalFolder(skillPath, "");
340
- zip.writeZip(zipPath);
341
-
342
- console.log(`✅ Build concluído: ${slug}.zip preparado para envio.`);
343
-
344
- console.log(`🚀 Fazendo upload para o GoClaw...`);
345
- const form = new FormData();
346
- form.append("file", fs.createReadStream(zipPath));
347
-
348
- try {
349
- const url = `${config.goclaw.api_url}/v1/skills/upload`;
350
- const response = await axios.post(url, form, {
351
- headers: {
352
- ...form.getHeaders(),
353
- Authorization: `Bearer ${config.goclaw.token}`, "X-GoClaw-User-Id": config.goclaw.username || "system"
354
- }
355
- });
356
- const data = response.data;
357
- if (data && data.version) {
358
- console.log(`📌 Skill atualizada para a versão ${data.version}.`);
359
- }
360
- } catch (error: any) {
361
- console.error("❌ Erro durante o deploy:");
362
- if (error.response) {
363
- console.error(error.response.data);
364
- } else {
365
- console.error(error.message);
366
- }
367
- }
425
+ await deploySkill(slug, config, basePath);
368
426
  });
369
427
 
370
428
  async function deployContextFiles(slug: string, config: any, resolvedId?: string | null) {
@@ -519,8 +577,45 @@ deployCmd
519
577
  await deployAgent(slug, config);
520
578
  });
521
579
 
580
+
581
+ async function deployAllAgents(config: any, basePath: string) {
582
+ const agentsDir = path.join(basePath, "agents");
583
+ if (await fs.pathExists(agentsDir)) {
584
+ const agents = await fs.readdir(agentsDir);
585
+ console.log(`🚀 Iniciando deploy em lote de ${agents.length} agentes...`);
586
+ for (const slug of agents) {
587
+ const agentPath = path.join(agentsDir, slug);
588
+ if ((await fs.stat(agentPath)).isDirectory()) {
589
+ await deployAgent(slug, config);
590
+ }
591
+ }
592
+ } else {
593
+ console.log("Nenhum agente encontrado em agents/.");
594
+ }
595
+ }
596
+
597
+ async function deployAllSkills(config: any, basePath: string) {
598
+ const skillsDir = path.join(basePath, "skills");
599
+ if (await fs.pathExists(skillsDir)) {
600
+ const skills = await fs.readdir(skillsDir);
601
+ console.log(`🚀 Iniciando deploy em lote de skills...`);
602
+ for (const item of skills) {
603
+ const itemPath = path.join(skillsDir, item);
604
+ if ((await fs.stat(itemPath)).isDirectory()) {
605
+ if (item === "system") {
606
+ console.log("⏩ Ignorando pasta 'system/' (skills nativas do GoClaw são apenas de leitura)");
607
+ continue;
608
+ }
609
+ await deploySkill(item, config, basePath);
610
+ }
611
+ }
612
+ } else {
613
+ console.log("Nenhuma skill encontrada em skills/.");
614
+ }
615
+ }
616
+
522
617
  deployCmd
523
- .command("all")
618
+ .command("agents")
524
619
  .description("Faz deploy de todos os agentes do workspace")
525
620
  .action(async () => {
526
621
  const config = await getConfig();
@@ -528,26 +623,40 @@ deployCmd
528
623
  console.error("❌ Configure sua chave de API (token) no agentforge.json.");
529
624
  process.exit(1);
530
625
  }
531
-
532
626
  const basePath = getWorkspaceRoot();
533
- const agentsDir = path.join(basePath, "agents");
534
-
535
- if (!(await fs.pathExists(agentsDir))) {
536
- console.log("Nenhum agente encontrado em agents/.");
537
- return;
627
+ await deployAllAgents(config, basePath);
628
+ console.log("🏁 Deploy de agentes concluído!");
629
+ });
630
+
631
+ deployCmd
632
+ .command("skills")
633
+ .description("Faz deploy de todas as skills do workspace")
634
+ .action(async () => {
635
+ const config = await getConfig();
636
+ if (!config.goclaw || !config.goclaw.token) {
637
+ console.error("❌ Configure sua chave de API (token) no agentforge.json.");
638
+ process.exit(1);
538
639
  }
640
+ const basePath = getWorkspaceRoot();
641
+ await deployAllSkills(config, basePath);
642
+ console.log("🏁 Deploy de skills concluído!");
643
+ });
539
644
 
540
- const agents = await fs.readdir(agentsDir);
541
- console.log(`🚀 Iniciando deploy em lote de ${agents.length} agentes...`);
542
-
543
- for (const slug of agents) {
544
- const agentPath = path.join(agentsDir, slug);
545
- if ((await fs.stat(agentPath)).isDirectory()) {
546
- await deployAgent(slug, config);
547
- }
645
+ deployCmd
646
+ .command("all")
647
+ .description("Faz deploy de todos os agentes e skills do workspace")
648
+ .action(async () => {
649
+ const config = await getConfig();
650
+ if (!config.goclaw || !config.goclaw.token) {
651
+ console.error("❌ Configure sua chave de API (token) no agentforge.json.");
652
+ process.exit(1);
548
653
  }
654
+
655
+ const basePath = getWorkspaceRoot();
656
+ await deployAllAgents(config, basePath);
657
+ await deployAllSkills(config, basePath);
549
658
 
550
- console.log("🏁 Deploy em lote concluído!");
659
+ console.log("🏁 Deploy completo (agentes e skills) concluído!");
551
660
  });
552
661
 
553
662
  const pullCmd = program
@@ -42,10 +42,20 @@ agentforge pull all
42
42
  Downloads all agents and skills from the GoClaw server. It performs a **surgical extraction**, retrieving only the core `agent.json`, `context_files/`, and skill definitions to keep your workspace perfectly clean. Note: This will ask for confirmation before overwriting local files.
43
43
 
44
44
  ### Bulk Deployment
45
+ ```bash
46
+ agentforge deploy agents
47
+ ```
48
+ Performs a full deployment (config + context + memory) for **all agents** found in your `agents/` directory.
49
+
50
+ ```bash
51
+ agentforge deploy skills
52
+ ```
53
+ Packages and uploads **all skills** (including those in the `system/` directory) found in your `skills/` directory.
54
+
45
55
  ```bash
46
56
  agentforge deploy all
47
57
  ```
48
- Performs a full deployment (config + context + memory) for **all agents** found in your `agents/` directory. This is the most efficient way to synchronize your entire team after making cross-cutting changes.
58
+ Performs a full deployment for **all agents and skills**. This is the most efficient way to synchronize your entire team and toolset after making cross-cutting changes.
49
59
 
50
60
  ### Agent Management
51
61
  ```bash