@fabioforest/openclaw 3.0.0 → 3.4.1

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.
Files changed (36) hide show
  1. package/README.md +417 -46
  2. package/bin/openclaw.js +37 -8
  3. package/lib/cli/assist.js +84 -0
  4. package/lib/cli/doctor.js +37 -3
  5. package/lib/cli/ide.js +218 -0
  6. package/lib/cli/init.js +135 -79
  7. package/lib/cli/inspect.js +58 -0
  8. package/lib/cli/orchestrate.js +43 -15
  9. package/lib/cli/update.js +113 -47
  10. package/lib/context/collector.js +104 -0
  11. package/lib/context/index.js +75 -0
  12. package/lib/router/match.js +107 -0
  13. package/lib/setup/config_wizard.js +2 -0
  14. package/package.json +2 -2
  15. package/templates/.agent/agents/workflow-automator.md +31 -0
  16. package/templates/.agent/rules/CONSENT_FIRST.md +24 -0
  17. package/templates/.agent/rules/DEV_MODE.md +18 -0
  18. package/templates/.agent/rules/ROUTER_PROTOCOL.md +22 -0
  19. package/templates/.agent/rules/WEB_AUTOMATION.md +52 -0
  20. package/templates/.agent/skills/content-sourcer/SKILL.md +48 -0
  21. package/templates/.agent/skills/context-flush/SKILL.md +30 -0
  22. package/templates/.agent/skills/drive-organizer/SKILL.md +40 -0
  23. package/templates/.agent/skills/linkedin-optimizer/SKILL.md +48 -0
  24. package/templates/.agent/skills/mission-control/SKILL.md +37 -0
  25. package/templates/.agent/skills/openclaw-assist/SKILL.md +30 -0
  26. package/templates/.agent/skills/openclaw-dev/SKILL.md +26 -0
  27. package/templates/.agent/skills/openclaw-inspect/SKILL.md +21 -0
  28. package/templates/.agent/skills/openclaw-installation-debugger/scripts/debug.js +16 -2
  29. package/templates/.agent/skills/openclaw-router/SKILL.md +34 -0
  30. package/templates/.agent/skills/openclaw-security/SKILL.md +21 -0
  31. package/templates/.agent/skills/site-tester/SKILL.md +49 -0
  32. package/templates/.agent/skills/smart-router/SKILL.md +116 -0
  33. package/templates/.agent/skills/web-scraper/SKILL.md +51 -0
  34. package/templates/.agent/state/MEMORY.md +8 -0
  35. package/templates/.agent/state/mission_control.json +34 -0
  36. package/templates/.agent/workflows/ai-capture.md +39 -0
@@ -6,6 +6,19 @@ const initCmd = require("./init");
6
6
  const doctorCmd = require("./doctor");
7
7
  const debugCmd = require("./debug");
8
8
 
9
+ const readline = require("readline");
10
+
11
+ function askQuestion(query) {
12
+ const rl = readline.createInterface({
13
+ input: process.stdin,
14
+ output: process.stdout,
15
+ });
16
+ return new Promise((resolve) => rl.question(query, (ans) => {
17
+ rl.close();
18
+ resolve(ans);
19
+ }));
20
+ }
21
+
9
22
  module.exports = {
10
23
  run: async function ({ targetPath, flags }) {
11
24
  const agentDir = path.join(targetPath, ".agent");
@@ -15,29 +28,44 @@ module.exports = {
15
28
  if (!fs.existsSync(agentDir)) {
16
29
  console.log("⚠️ Instalação não encontrada.");
17
30
  console.log("🚀 Iniciando processo de instalação (init)...");
18
-
19
- // Repassa flags, força path atual
20
31
  await initCmd.run({ targetPath, flags });
21
32
  return;
22
33
  }
23
34
 
24
- console.log("✅ Instalação detectada.");
25
- console.log("🏥 Executando verificação de saúde (doctor)...");
35
+ console.log("✅ Instalação detectada! Contexto preservado.");
36
+ console.log(" (Arquivos em .agent/ encontrados)\n");
26
37
 
27
- try {
28
- // Doctor lança erro ou exit(1) se falhar?
29
- // Precisamos capturar o exit code do doctor se ele for desenhado para matar o processo.
30
- // O doctor atual usa exit(1) em erros críticos ou apenas loga?
31
- // Vamos checar o doctor depois. Por enquanto, assumimos que ele roda.
38
+ console.log("O que deseja fazer?");
39
+ console.log(" [1] 🏥 Verificar Saúde (Doctor) - Recomendado");
40
+ console.log(" [2] 🔄 Atualizar (Safe Merge) - Adiciona novidades, mantém edições");
41
+ console.log(" [3] ⚠️ Reinstalar (Force) - ALERTA: Sobrescreve TUDO");
42
+ console.log(" [4] 🚪 Sair\n");
32
43
 
33
- await doctorCmd.run({ targetPath, flags });
44
+ const ans = await askQuestion("Escolha uma opção [1-4]: ");
45
+ const choice = ans.trim();
34
46
 
35
- console.log("\n✨ Sistema parece saudável.");
36
- } catch (err) {
37
- console.log("\n❌ Doctor encontrou problemas ou falhou.");
38
- console.log("🔍 Iniciando diagnóstico avançado (debug)...");
47
+ console.log(""); // quebra de linha
39
48
 
40
- await debugCmd.run({ targetPath, flags });
49
+ if (choice === "1" || choice === "") {
50
+ console.log("🏥 Iniciando Doctor...");
51
+ await doctorCmd.run({ targetPath, flags });
52
+ } else if (choice === "2") {
53
+ console.log("🔄 Iniciando Safe Merge...");
54
+ // Como o usuário já confirmou interativamente aqui, passamos apply + yes
55
+ const safeFlags = { ...flags, merge: true, apply: true, yes: true };
56
+ await initCmd.run({ targetPath, flags: safeFlags });
57
+ } else if (choice === "3") {
58
+ const confirm = await askQuestion("Tem certeza? Isso apagará todas as suas customizações em .agent/. Digite 'sim' para confirmar: ");
59
+ if (confirm.toLowerCase() === "sim") {
60
+ console.log("⚠️ Iniciando Reinstalação Forçada...");
61
+ // Aqui passamos force, apply e yes (já confirmou a string "sim")
62
+ const forceFlags = { ...flags, force: true, apply: true, yes: true };
63
+ await initCmd.run({ targetPath, flags: forceFlags });
64
+ } else {
65
+ console.log("⏹️ Cancelado.");
66
+ }
67
+ } else {
68
+ console.log("👋 Saindo.");
41
69
  }
42
70
  }
43
71
  };
package/lib/cli/update.js CHANGED
@@ -12,14 +12,38 @@
12
12
  const fs = require("fs");
13
13
  const path = require("path");
14
14
  const crypto = require("crypto");
15
+ const readline = require("readline");
16
+ const { detectContext, getAuditHeader } = require("../context");
15
17
 
16
18
  // Caminho dos templates incluídos no pacote
17
19
  const TEMPLATES_DIR = path.join(__dirname, "..", "..", "templates", ".agent");
18
20
 
21
+ function ask(q) {
22
+ const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
23
+ return new Promise((res) => rl.question(q, (ans) => { rl.close(); res(ans.trim()); }));
24
+ }
25
+
26
+ function safeRel(targetPath, p) {
27
+ return path.relative(targetPath, p);
28
+ }
29
+
30
+ function writeAudit(targetPath, lines, flags) {
31
+ if (flags.audit === false) return;
32
+ const auditDir = path.join(targetPath, ".agent", "audit");
33
+ if (!fs.existsSync(auditDir)) {
34
+ try { fs.mkdirSync(auditDir, { recursive: true }); } catch (e) { }
35
+ }
36
+ const filename = `update-${new Date().toISOString().replace(/[:.]/g, "-")}.md`;
37
+ const auditPath = path.join(auditDir, filename);
38
+ try {
39
+ fs.writeFileSync(auditPath, lines.join("\n") + "\n", "utf8");
40
+ } catch (e) {
41
+ console.error("⚠️ Falha ao gravar auditoria:", e.message);
42
+ }
43
+ }
44
+
19
45
  /**
20
- * Calcula o SHA-256 de um arquivo.
21
- * @param {string} filePath — caminho do arquivo
22
- * @returns {string} hash em hex
46
+ * Calcula o SHA-256 de um arquivo (Utilitário mantido)
23
47
  */
24
48
  function fileHash(filePath) {
25
49
  const content = fs.readFileSync(filePath);
@@ -27,14 +51,13 @@ function fileHash(filePath) {
27
51
  }
28
52
 
29
53
  /**
30
- * Compara e atualiza um diretório recursivamente.
31
- * @param {string} src diretório fonte (template)
32
- * @param {string} dest — diretório destino (instalado)
33
- * @param {object} stats — contadores { updated, skipped, added }
54
+ * Analisa atualizações necessárias.
55
+ * Retorna lista de ações planejadas.
34
56
  */
35
- function updateDirRecursive(src, dest, stats) {
57
+ function planUpdates(src, dest, actions = { added: [], updated: [], skipped: [] }) {
36
58
  if (!fs.existsSync(dest)) {
37
- fs.mkdirSync(dest, { recursive: true });
59
+ // Diretório não existe no destino, será criado implicitamente na cópia
60
+ // Mas a lógica recursiva precisa entrar
38
61
  }
39
62
 
40
63
  const entries = fs.readdirSync(src, { withFileTypes: true });
@@ -44,79 +67,122 @@ function updateDirRecursive(src, dest, stats) {
44
67
  const destPath = path.join(dest, entry.name);
45
68
 
46
69
  if (entry.isDirectory()) {
47
- updateDirRecursive(srcPath, destPath, stats);
70
+ if (!fs.existsSync(destPath)) {
71
+ // Diretório novo, tudo dentro será added
72
+ // Simplificação: marcar diretório como added e não recursar?
73
+ // Melhor recursar para listar arquivos
74
+ }
75
+ planUpdates(srcPath, destPath, actions);
48
76
  } else {
49
77
  if (!fs.existsSync(destPath)) {
50
- // Arquivo novo copiar
51
- fs.copyFileSync(srcPath, destPath);
52
- stats.added.push(path.relative(dest, destPath) || entry.name);
78
+ actions.added.push({ src: srcPath, dest: destPath });
53
79
  } else {
54
- // Arquivo já existe — comparar hashes
55
80
  const srcHash = fileHash(srcPath);
56
81
  const destHash = fileHash(destPath);
57
-
58
82
  if (srcHash === destHash) {
59
- // Idêntico nada a fazer
60
- stats.skipped.push(path.relative(dest, destPath) || entry.name);
83
+ actions.skipped.push({ src: srcPath, dest: destPath });
61
84
  } else {
62
- // Diferente arquivo foi customizado ou template atualizado
63
- // Preserva o original do usuário fazendo backup
64
- const backupPath = destPath + ".bak";
65
- fs.copyFileSync(destPath, backupPath);
66
- fs.copyFileSync(srcPath, destPath);
67
- stats.updated.push(path.relative(dest, destPath) || entry.name);
85
+ actions.updated.push({ src: srcPath, dest: destPath });
68
86
  }
69
87
  }
70
88
  }
71
89
  }
90
+ return actions;
72
91
  }
73
92
 
74
93
  /**
75
- * Executa o comando update.
76
- * @param {object} options
77
- * @param {string} options.targetPath — diretório alvo
78
- * @param {object} options.flags — flags do CLI
94
+ * Executa o comando update com segurança.
79
95
  */
80
96
  async function run({ targetPath, flags }) {
81
97
  const agentDir = path.join(targetPath, ".agent");
98
+ const ctx = detectContext(targetPath);
99
+
100
+ // Default: Plan Mode
101
+ const planMode = !flags.apply;
82
102
 
83
103
  if (!fs.existsSync(agentDir)) {
84
104
  console.error("❌ Diretório .agent/ não encontrado.");
85
- console.error(" Rode 'openclaw init' primeiro para instalar os templates.");
105
+ console.error(" Rode 'openclaw init' primeiro.");
86
106
  process.exit(1);
87
107
  }
88
-
89
108
  if (!fs.existsSync(TEMPLATES_DIR)) {
90
- console.error("❌ Templates não encontrados. Pacote pode estar corrompido.");
109
+ console.error("❌ Templates não encontrados.");
91
110
  process.exit(1);
92
111
  }
93
112
 
94
- if (!flags.quiet) {
95
- console.log("\n🔄 OpenClaw Update Atualizando templates...\n");
113
+ // 1. Planejar
114
+ const actions = planUpdates(TEMPLATES_DIR, agentDir);
115
+ const audit = [getAuditHeader(ctx, "update", flags)];
116
+
117
+ // 2. Exibir Plano
118
+ console.log(`\n🧭 Plano de Atualização (${planMode ? "SIMULAÇÃO" : "APPLY"}):\n`);
119
+ console.log(` Contexto: ${ctx.env} | IDE: ${ctx.ide}\n`);
120
+
121
+ if (actions.added.length > 0) {
122
+ console.log(`📄 Novos (${actions.added.length}):`);
123
+ actions.added.forEach(a => console.log(` + CREATE ${safeRel(targetPath, a.dest)}`));
124
+ }
125
+ if (actions.updated.length > 0) {
126
+ console.log(`\n🔄 Modificados (${actions.updated.length}):`);
127
+ actions.updated.forEach(a => console.log(` ~ UPDATE ${safeRel(targetPath, a.dest)} (Backup gerado)`));
128
+ }
129
+ if (actions.skipped.length > 0) {
130
+ console.log(`\n⏭️ Ignorados (${actions.skipped.length} arquivos idênticos)`);
96
131
  }
97
132
 
98
- const stats = { updated: [], skipped: [], added: [] };
99
- updateDirRecursive(TEMPLATES_DIR, agentDir, stats);
133
+ if (actions.added.length === 0 && actions.updated.length === 0) {
134
+ console.log("\n✅ Tudo atualizado. Nenhuma alteração necessária.");
135
+ return;
136
+ }
137
+
138
+ if (planMode) {
139
+ console.log("\n🔒 Modo PLAN (Read-Only). Nenhuma alteração feita.");
140
+ console.log(" Para aplicar, rode: npx openclaw update --apply");
141
+ return;
142
+ }
100
143
 
101
- if (!flags.quiet) {
102
- if (stats.added.length > 0) {
103
- console.log(`📄 Novos (${stats.added.length}):`);
104
- stats.added.forEach((f) => console.log(` + ${f}`));
144
+ // 3. Confirmação
145
+ if (!flags.yes) {
146
+ const ok = await ask("\nAplicar este plano? (y/N): ");
147
+ if (ok.toLowerCase() !== "y") {
148
+ console.log("⏹️ Cancelado.");
149
+ return;
150
+ }
151
+ }
152
+
153
+ // 4. Execução
154
+ try {
155
+ console.log("\n🚀 Executando atualizações...");
156
+
157
+ // Criar diretórios necessários
158
+ function ensureDir(p) {
159
+ const dir = path.dirname(p);
160
+ if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
105
161
  }
106
162
 
107
- if (stats.updated.length > 0) {
108
- console.log(`\n🔄 Atualizados (${stats.updated.length}):`);
109
- stats.updated.forEach((f) => console.log(` ~ ${f} (backup: ${f}.bak)`));
163
+ for (const action of actions.added) {
164
+ ensureDir(action.dest);
165
+ fs.copyFileSync(action.src, action.dest);
166
+ audit.push(`- ACT: CREATED ${safeRel(targetPath, action.dest)}`);
110
167
  }
111
168
 
112
- if (stats.skipped.length > 0) {
113
- console.log(`\n⏭️ Sem alteração (${stats.skipped.length}):`);
114
- stats.skipped.forEach((f) => console.log(` = ${f}`));
169
+ for (const action of actions.updated) {
170
+ ensureDir(action.dest);
171
+ const backupPath = action.dest + ".bak";
172
+ fs.copyFileSync(action.dest, backupPath);
173
+ fs.copyFileSync(action.src, action.dest);
174
+ audit.push(`- ACT: UPDATED ${safeRel(targetPath, action.dest)} (Backup: ${path.basename(backupPath)})`);
115
175
  }
116
176
 
117
- const total = stats.added.length + stats.updated.length;
118
- console.log(`\n✅ Update concluído: ${total} alterações, ${stats.skipped.length} mantidos\n`);
177
+ console.log("\n✨ Atualização concluída com sucesso!");
178
+ writeAudit(targetPath, audit, flags);
179
+
180
+ } catch (err) {
181
+ console.error(`\n❌ Falha na execução: ${err.message}`);
182
+ audit.push(`\n## ERROR: ${err.message}`);
183
+ writeAudit(targetPath, audit, flags);
184
+ process.exit(1);
119
185
  }
120
186
  }
121
187
 
122
- module.exports = { run, updateDirRecursive, fileHash };
188
+ module.exports = { run, fileHash };
@@ -0,0 +1,104 @@
1
+ "use strict";
2
+
3
+ /**
4
+ * Context Collector — Read-only snapshot do ambiente.
5
+ * Nunca altera arquivos. Apenas lê e retorna dados.
6
+ *
7
+ * Baseado no módulo do openclaw-agents-addons,
8
+ * adaptado para o projeto principal.
9
+ */
10
+
11
+ const fs = require("fs");
12
+ const os = require("os");
13
+ const path = require("path");
14
+
15
+ function exists(p) {
16
+ try { return fs.existsSync(p); } catch { return false; }
17
+ }
18
+
19
+ /**
20
+ * Detecta IDE ativa no workspace.
21
+ */
22
+ function detectIDE(targetPath) {
23
+ if (exists(path.join(targetPath, ".cursor"))) return "cursor";
24
+ if (exists(path.join(targetPath, ".vscode"))) return "vscode";
25
+ if (exists(path.join(targetPath, ".idea"))) return "jetbrains";
26
+ return "unknown";
27
+ }
28
+
29
+ /**
30
+ * Detecta ambiente de execução (SO, Docker, WSL).
31
+ */
32
+ function detectEnvironment() {
33
+ const platform = os.platform();
34
+ const docker = exists("/.dockerenv") ||
35
+ (exists("/proc/1/cgroup") && fs.readFileSync("/proc/1/cgroup", "utf8").includes("docker"));
36
+ const wsl = platform === "linux" &&
37
+ (os.release().toLowerCase().includes("microsoft") || !!process.env.WSL_DISTRO_NAME);
38
+ return { platform, docker, wsl };
39
+ }
40
+
41
+ /**
42
+ * Detecta instalação existente do OpenClaw.
43
+ */
44
+ function detectOpenClaw(targetPath) {
45
+ const agentDir = path.join(targetPath, ".agent");
46
+ const config = path.join(targetPath, "openclaw.json");
47
+ const dockerCompose = path.join(targetPath, "docker-compose.yml");
48
+ return {
49
+ hasAgentDir: exists(agentDir),
50
+ hasConfig: exists(config),
51
+ hasDockerCompose: exists(dockerCompose),
52
+ };
53
+ }
54
+
55
+ /**
56
+ * Lista skills disponíveis nos templates.
57
+ */
58
+ function listSkillsFromTemplates(templatesDir) {
59
+ const skillsDir = path.join(templatesDir, ".agent", "skills");
60
+ if (!exists(skillsDir)) return [];
61
+ const out = [];
62
+ for (const name of fs.readdirSync(skillsDir)) {
63
+ const skillPath = path.join(skillsDir, name, "SKILL.md");
64
+ if (exists(skillPath)) out.push({ name, skillPath });
65
+ }
66
+ return out;
67
+ }
68
+
69
+ /**
70
+ * Lista skills instaladas no workspace do usuário.
71
+ */
72
+ function listInstalledSkills(targetPath) {
73
+ const skillsDir = path.join(targetPath, ".agent", "skills");
74
+ if (!exists(skillsDir)) return [];
75
+ const out = [];
76
+ for (const name of fs.readdirSync(skillsDir)) {
77
+ const skillPath = path.join(skillsDir, name, "SKILL.md");
78
+ if (exists(skillPath)) out.push({ name, skillPath });
79
+ }
80
+ return out;
81
+ }
82
+
83
+ /**
84
+ * Coleta contexto completo (read-only).
85
+ * @param {object} options
86
+ * @param {string} options.targetPath — diretório do workspace
87
+ * @param {string} options.templatesDir — diretório de templates do pacote
88
+ * @returns {object} snapshot do contexto
89
+ */
90
+ function collectContext({ targetPath, templatesDir }) {
91
+ const env = detectEnvironment();
92
+ return {
93
+ targetPath,
94
+ env,
95
+ ide: detectIDE(targetPath),
96
+ openclaw: detectOpenClaw(targetPath),
97
+ git: { isRepo: exists(path.join(targetPath, ".git")) },
98
+ skillsInTemplates: listSkillsFromTemplates(templatesDir),
99
+ skillsInstalled: listInstalledSkills(targetPath),
100
+ ts: new Date().toISOString(),
101
+ };
102
+ }
103
+
104
+ module.exports = collectContext;
@@ -0,0 +1,75 @@
1
+ "use strict";
2
+
3
+ const fs = require("fs");
4
+ const path = require("path");
5
+ const os = require("os");
6
+
7
+ /**
8
+ * Detecta o ambiente de execução e metadados de contexto.
9
+ * Retorna um objeto seguro para logs e auditoria.
10
+ */
11
+ function detectContext(cwd = process.cwd()) {
12
+ const ctx = {
13
+ timestamp: new Date().toISOString(),
14
+ platform: os.platform(),
15
+ cwd: cwd,
16
+ env: "local",
17
+ ide: "unknown",
18
+ isDocker: false,
19
+ hasExistingInstall: false,
20
+ };
21
+
22
+ // 1. Detectar Docker
23
+ if (fs.existsSync("/.dockerenv") || fs.existsSync("/run/.containerenv")) {
24
+ ctx.isDocker = true;
25
+ ctx.env = "docker";
26
+ }
27
+
28
+ // 2. Detectar IDEs comuns
29
+ const ideMarkers = [
30
+ { name: "vscode", path: ".vscode" },
31
+ { name: "cursor", path: ".cursor" },
32
+ { name: "idea", path: ".idea" },
33
+ { name: "antigravity", path: ".agent/antigravity" } // Marcador fictício ou real se existir
34
+ ];
35
+
36
+ for (const m of ideMarkers) {
37
+ if (fs.existsSync(path.join(cwd, m.path))) {
38
+ ctx.ide = m.name;
39
+ break;
40
+ }
41
+ }
42
+
43
+ // 3. Detectar instalação existente
44
+ const agentDir = path.join(cwd, ".agent");
45
+ const configPath = path.join(cwd, "openclaw.json");
46
+
47
+ if (fs.existsSync(agentDir) || fs.existsSync(configPath)) {
48
+ ctx.hasExistingInstall = true;
49
+ }
50
+
51
+ return ctx;
52
+ }
53
+
54
+ /**
55
+ * Gera um cabeçalho de auditoria formatado em Markdown.
56
+ */
57
+ function getAuditHeader(ctx, command, flags) {
58
+ return [
59
+ `# OpenClaw Audit Log`,
60
+ `- **Time**: ${ctx.timestamp}`,
61
+ `- **Command**: ${command}`,
62
+ `- **Mode**: ${flags.plan ? "PLAN (Simulation)" : "APPLY (Execution)"}`,
63
+ `- **Environment**: ${ctx.env} (Docker: ${ctx.isDocker})`,
64
+ `- **IDE**: ${ctx.ide}`,
65
+ `- **Existing Install**: ${ctx.hasExistingInstall}`,
66
+ `- **Flags**: ${JSON.stringify(flags)}`,
67
+ `---`,
68
+ ``
69
+ ].join("\n");
70
+ }
71
+
72
+ module.exports = {
73
+ detectContext,
74
+ getAuditHeader
75
+ };
@@ -0,0 +1,107 @@
1
+ "use strict";
2
+
3
+ /**
4
+ * Skill Matcher — Escolhe a skill mais adequada para uma solicitação.
5
+ *
6
+ * Faz parse do YAML frontmatter de cada SKILL.md e pontua
7
+ * a relevância com base nos triggers e descrição.
8
+ */
9
+
10
+ const fs = require("fs");
11
+ const path = require("path");
12
+
13
+ /**
14
+ * Extrai metadados (name, description, triggers) do frontmatter YAML.
15
+ * @param {string} md — conteúdo Markdown com frontmatter
16
+ * @returns {object|null} metadados extraídos ou null
17
+ */
18
+ function parseFrontmatter(md) {
19
+ const m = md.match(/^---\s*\n([\s\S]*?)\n---\s*\n/);
20
+ if (!m) return null;
21
+
22
+ const yaml = m[1];
23
+
24
+ // Extrair listas (ex: triggers)
25
+ const getList = (key) => {
26
+ const r = new RegExp(`^${key}:\\s*\\n([\\s\\S]*?)(\\n\\w|$)`, "m");
27
+ const mm = yaml.match(r);
28
+ if (!mm) return [];
29
+ return mm[1]
30
+ .split("\n")
31
+ .map(l => l.trim())
32
+ .filter(l => l.startsWith("-"))
33
+ .map(l => l.replace(/^-\s*/, "").trim())
34
+ .filter(Boolean);
35
+ };
36
+
37
+ // Extrair escalares (ex: name, description)
38
+ const getScalar = (key) => {
39
+ const r = new RegExp(`^${key}:\\s*(.+)$`, "m");
40
+ const mm = yaml.match(r);
41
+ return mm ? mm[1].trim() : "";
42
+ };
43
+
44
+ return {
45
+ name: getScalar("name"),
46
+ description: getScalar("description"),
47
+ triggers: getList("triggers"),
48
+ };
49
+ }
50
+
51
+ /**
52
+ * Carrega todas as skills de um diretório de templates.
53
+ * @param {string} skillsDir — diretório com subpastas de skills
54
+ * @returns {Array} lista de skills com metadados
55
+ */
56
+ function loadSkills(skillsDir) {
57
+ if (!fs.existsSync(skillsDir)) return [];
58
+ const skills = [];
59
+ for (const folder of fs.readdirSync(skillsDir)) {
60
+ const p = path.join(skillsDir, folder, "SKILL.md");
61
+ if (!fs.existsSync(p)) continue;
62
+ const md = fs.readFileSync(p, "utf8");
63
+ const meta = parseFrontmatter(md);
64
+ if (!meta) continue;
65
+ skills.push({ ...meta, path: p, folder });
66
+ }
67
+ return skills;
68
+ }
69
+
70
+ /**
71
+ * Calcula score de relevância de uma skill para um texto.
72
+ * @param {object} skill — skill com triggers e description
73
+ * @param {string} text — texto do usuário
74
+ * @returns {number} pontuação (maior = mais relevante)
75
+ */
76
+ function scoreSkill(skill, text) {
77
+ const t = (text || "").toLowerCase();
78
+ let s = 0;
79
+ for (const trig of (skill.triggers || [])) {
80
+ if (t.includes(String(trig).toLowerCase())) s += 5;
81
+ }
82
+ // Bonus parcial para match de descrição
83
+ if (skill.description && t.includes(skill.description.toLowerCase().slice(0, 12))) s += 1;
84
+ return s;
85
+ }
86
+
87
+ /**
88
+ * Encontra a skill mais relevante para uma solicitação.
89
+ * @param {object} options
90
+ * @param {string} options.skillsDir — diretório de skills (templates/.agent/skills)
91
+ * @param {string} options.userText — texto da solicitação do usuário
92
+ * @returns {object} { chosen, alternatives, ranked }
93
+ */
94
+ function matchSkill({ skillsDir, userText }) {
95
+ const skills = loadSkills(skillsDir);
96
+ const ranked = skills
97
+ .map(sk => ({ sk, score: scoreSkill(sk, userText) }))
98
+ .sort((a, b) => b.score - a.score);
99
+
100
+ return {
101
+ chosen: ranked[0]?.sk || null,
102
+ alternatives: ranked.slice(1, 4).map(x => x.sk),
103
+ ranked,
104
+ };
105
+ }
106
+
107
+ module.exports = { matchSkill, loadSkills, parseFrontmatter };
@@ -176,6 +176,8 @@ async function main() {
176
176
  }
177
177
 
178
178
  console.log("\n✅ Setup finalizado.");
179
+ console.log("\n🌐 CONTROL UI: http://127.0.0.1:18789");
180
+ console.log(" Acesse para gerenciar seus agentes visualmente.\n");
179
181
  console.log("Próximo passo: configurar VPN (WireGuard) e aplicar policies (skills/openclaw-ops).");
180
182
  rl.close();
181
183
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@fabioforest/openclaw",
3
- "version": "3.0.0",
4
- "description": "CLI e starter kit para configuração segura do OpenClaw em VPS, Mac, Windows e Docker",
3
+ "version": "3.4.1",
4
+ "description": "Agentes autônomos para engenharia de software",
5
5
  "publishConfig": {
6
6
  "access": "public"
7
7
  },
@@ -0,0 +1,31 @@
1
+ ---
2
+ name: Workflow Automator
3
+ description: Especialista em criar, validar e otimizar workflows e automações do OpenClaw.
4
+ system_prompt: |
5
+ Você é o Workflow Automator, um agente especializado na arquitetura de automação do OpenClaw.
6
+
7
+ ## Suas Responsabilidades
8
+ 1. **Criar Workflows**: Escrever arquivos `.md` válidos em `.agent/workflows/` seguindo a sintaxe de nodes.
9
+ 2. **Explicar Conceitos**: Ensinar sobre `AI Capture`, `Interactive Nodes` e `Message Nodes`.
10
+ 3. **Validar Sintaxe**: Garantir que o YAML frontmatter e a estrutura dos passos estejam corretos.
11
+
12
+ ## Conhecimento de Sintaxe
13
+ Você domina a estrutura de workflows do OpenClaw:
14
+ - **Frontmatter**: `description`, `params` (opcional).
15
+ - **Steps**: Lista numerada ou bullets.
16
+ - **AI Capture**: Uso de prompts para extrair JSON de conversas.
17
+ - **Integração**: Como chamar skills dentro de workflows.
18
+
19
+ ## Personalidade
20
+ Técnico, preciso e focado em eficiência. Você adora transformar processos manuais em arquivos `.md` elegantes.
21
+ ---
22
+ # Workflow Automator
23
+
24
+ Olá! Eu sou o especialista em **Workflows Inteligentes**.
25
+
26
+ Posso ajudar você a:
27
+ 1. Criar um workflow de **Onboarding** que coleta dados do usuário.
28
+ 2. Configurar um **AI Capture** para estruturar pedidos ou tickets.
29
+ 3. Debugar um workflow que não está rodando corretamente.
30
+
31
+ Comando sugerido: `Crie um workflow para coletar feedback de usuários`
@@ -0,0 +1,24 @@
1
+ ---
2
+ description: Regra de Ouro: Consentimento Prévio para Alterações
3
+ ---
4
+
5
+ # Consent First (Segurança Absoluta)
6
+
7
+ Como um agente OpenClaw, você opera sob um contrato estrito de "Read-Only por Padrão".
8
+
9
+ ## 1. Regra de Ouro
10
+ **Nunca altere, apague ou crie arquivos sem que o usuário tenha solicitado explicitamente essa ação específica.**
11
+
12
+ ## 2. Protocolo de Modificação
13
+ Antes de qualquer operação de escrita (write, edit, delete, move), você deve:
14
+ 1. **Analisar**: Entender o contexto e o impacto.
15
+ 2. **Planejar**: Explicar ao usuário o que será feito.
16
+ 3. **Confirmar**: Perguntar "Posso prosseguir?" ou aguardar comando explícito (ex: `--apply`).
17
+
18
+ ## 3. Proibições Estritas
19
+ - Nunca execute `rm -rf`, `git clean` ou deletar diretórios inteiros sem um aviso gigante e confirmação dupla.
20
+ - Nunca sobrescreva arquivos de configuração (`openclaw.json`, `.env`) silenciosamente.
21
+ - Nunca assuma que pode "consertar" algo sem perguntar antes.
22
+
23
+ ## 4. Auditoria
24
+ Sempre que realizar uma alteração, registre o que foi feito. O sistema já gera logs em `.agent/audit/`, mas você deve comunicar o sucesso ao usuário com clareza.