@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
package/lib/cli/doctor.js CHANGED
@@ -166,12 +166,16 @@ async function checkPort() {
166
166
  try {
167
167
  const inUse = await portInUse(18789, "127.0.0.1");
168
168
  if (inUse) {
169
- return { name: "porta 18789", status: "ok", message: "Serviço respondendo em localhost:18789" };
169
+ return {
170
+ name: "porta 18789",
171
+ status: "ok",
172
+ message: "✅ Control UI acessível em http://127.0.0.1:18789"
173
+ };
170
174
  }
171
175
  return {
172
176
  name: "porta 18789",
173
177
  status: "warn",
174
- message: "Porta livre — serviço OpenClaw pode estar parado",
178
+ message: "Porta livre — serviço OpenClaw pode estar parado (UI indisponível)",
175
179
  };
176
180
  } catch {
177
181
  return {
@@ -182,6 +186,30 @@ async function checkPort() {
182
186
  }
183
187
  }
184
188
 
189
+ /**
190
+ * Verifica interfaces de rede para detectar VPN WireGuard ativa (Linux only).
191
+ * @returns {CheckResult}
192
+ */
193
+ function checkVpnInterface() {
194
+ const osType = require("os").type();
195
+ if (osType !== "Linux") {
196
+ return { name: "vpn interface", status: "ok", message: "N/A (não é Linux)" };
197
+ }
198
+
199
+ try {
200
+ const interfaces = require("os").networkInterfaces();
201
+ const wgInterface = Object.keys(interfaces).find(iface => iface.startsWith("wg"));
202
+
203
+ if (wgInterface) {
204
+ return { name: "vpn interface", status: "ok", message: `Detectada: ${wgInterface}` };
205
+ } else {
206
+ return { name: "vpn interface", status: "warn", message: "Nenhuma interface wg* encontrada" };
207
+ }
208
+ } catch (e) {
209
+ return { name: "vpn interface", status: "warn", message: "Erro ao listar interfaces" };
210
+ }
211
+ }
212
+
185
213
  /**
186
214
  * Verifica ambiente detectado.
187
215
  * @returns {CheckResult}
@@ -252,6 +280,12 @@ async function run({ targetPath, flags }) {
252
280
  // 4. .agent/
253
281
  results.push(...checkAgentDir(agentDir));
254
282
 
283
+ // 5. VPN (se Linux)
284
+ const vpnCheck = checkVpnInterface();
285
+ if (vpnCheck.message !== "N/A (não é Linux)") {
286
+ results.push(vpnCheck);
287
+ }
288
+
255
289
  // Exibir relatório
256
290
  printReport(results);
257
291
  console.log("");
@@ -263,4 +297,4 @@ async function run({ targetPath, flags }) {
263
297
  }
264
298
  }
265
299
 
266
- module.exports = { run, checkConfig, checkAgentDir, checkPort, checkEnvironment };
300
+ module.exports = { run, checkConfig, checkAgentDir, checkPort, checkEnvironment, checkVpnInterface };
package/lib/cli/ide.js ADDED
@@ -0,0 +1,218 @@
1
+ "use strict";
2
+
3
+ /**
4
+ * Comando CLI: ide
5
+ *
6
+ * Gerencia a instalação do OpenClaw AI OS em IDEs.
7
+ * Sub-comandos: install (plan/apply), doctor.
8
+ * Sempre segue o protocolo consent-first.
9
+ */
10
+
11
+ const fs = require("fs");
12
+ const path = require("path");
13
+ const readline = require("readline");
14
+ const { detectContext, getAuditHeader } = require("../context");
15
+ const { copyDirRecursive } = require("./init");
16
+
17
+ // Caminho dos templates do pacote
18
+ const TEMPLATES_DIR = path.join(__dirname, "..", "..", "templates");
19
+
20
+ function ask(q) {
21
+ const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
22
+ return new Promise((res) => rl.question(q, (ans) => { rl.close(); res(ans.trim()); }));
23
+ }
24
+
25
+ /**
26
+ * Grava log de auditoria para o comando ide.
27
+ */
28
+ function writeAudit(targetPath, lines, flags) {
29
+ if (flags.audit === false) return;
30
+ const auditDir = path.join(targetPath, ".agent", "audit");
31
+ if (!fs.existsSync(auditDir)) {
32
+ try { fs.mkdirSync(auditDir, { recursive: true }); } catch (e) { }
33
+ }
34
+ const filename = `ide-${new Date().toISOString().replace(/[:.]/g, "-")}.md`;
35
+ try {
36
+ fs.writeFileSync(path.join(auditDir, filename), lines.join("\n") + "\n", "utf8");
37
+ } catch (e) {
38
+ console.error("⚠️ Falha ao gravar auditoria:", e.message);
39
+ }
40
+ }
41
+
42
+ /**
43
+ * Executa o comando ide.
44
+ * Uso: openclaw ide install [--apply] [--force]
45
+ * openclaw ide doctor
46
+ *
47
+ * @param {object} options
48
+ * @param {string} options.targetPath — diretório alvo
49
+ * @param {object} options.flags — flags do CLI
50
+ */
51
+ async function run({ targetPath, flags }) {
52
+ // Detectar sub-comando a partir de args restantes
53
+ // O bin/openclaw.js passa o comando "ide", então precisamos
54
+ // verificar os args para o sub-comando
55
+ const args = process.argv.slice(3);
56
+ const subCmd = args.find(a => !a.startsWith("-")) || "install";
57
+
58
+ if (subCmd === "doctor") {
59
+ return runDoctor({ targetPath, flags });
60
+ }
61
+
62
+ return runInstall({ targetPath, flags });
63
+ }
64
+
65
+ /**
66
+ * Sub-comando: ide install
67
+ * Instala .agent/ no projeto para uso em IDEs (plan/apply).
68
+ */
69
+ async function runInstall({ targetPath, flags }) {
70
+ const agentDst = path.join(targetPath, ".agent");
71
+ const agentSrc = path.join(TEMPLATES_DIR, ".agent");
72
+ const ctx = detectContext(targetPath);
73
+ const planMode = !flags.apply;
74
+ const audit = [getAuditHeader(ctx, "ide install", flags)];
75
+
76
+ console.log(`\n🧭 IDE Install — Plano (${planMode ? "SIMULAÇÃO" : "APPLY"}):\n`);
77
+ console.log(` Contexto: ${ctx.env} | IDE: ${ctx.ide}\n`);
78
+
79
+ if (fs.existsSync(agentDst) && !flags.force) {
80
+ console.log(" ✅ KEEP .agent/ (já existe)");
81
+ console.log(" 📦 MERGE Novos templates serão adicionados (preservando existentes)");
82
+ } else if (fs.existsSync(agentDst) && flags.force) {
83
+ console.log(" 🔥 DELETE .agent/ (--force)");
84
+ console.log(" 📦 COPY templates/.agent -> .agent/");
85
+ } else {
86
+ console.log(" 📁 CREATE .agent/");
87
+ console.log(" 📦 COPY templates/.agent -> .agent/");
88
+ }
89
+
90
+ // State templates (mission_control.json, MEMORY.md)
91
+ const stateDir = path.join(agentDst, "state");
92
+ if (!fs.existsSync(stateDir)) {
93
+ console.log(" 📁 CREATE .agent/state/ (Mission Control + MEMORY)");
94
+ } else {
95
+ console.log(" ✅ KEEP .agent/state/ (já existe)");
96
+ }
97
+
98
+ if (planMode) {
99
+ console.log("\n🔒 Modo PLAN (Read-Only). Nenhuma alteração feita.");
100
+ console.log(" Para aplicar, rode: npx openclaw ide install --apply");
101
+ return;
102
+ }
103
+
104
+ // Confirmação
105
+ if (!flags.yes) {
106
+ if (flags.force && fs.existsSync(agentDst)) {
107
+ const phrase = await ask("⚠️ Digite 'DELETE .agent' para confirmar: ");
108
+ if (phrase !== "DELETE .agent") {
109
+ console.log("⏹️ Cancelado.");
110
+ return;
111
+ }
112
+ } else {
113
+ const ok = await ask("\nAplicar instalação IDE? (y/N): ");
114
+ if (ok.toLowerCase() !== "y") {
115
+ console.log("⏹️ Cancelado.");
116
+ return;
117
+ }
118
+ }
119
+ }
120
+
121
+ // Execução
122
+ try {
123
+ console.log("\n🚀 Executando...");
124
+
125
+ if (fs.existsSync(agentDst) && flags.force) {
126
+ fs.rmSync(agentDst, { recursive: true, force: true });
127
+ audit.push("- ACT: DELETED .agent/");
128
+ }
129
+
130
+ const isMerge = fs.existsSync(agentDst);
131
+ const stats = copyDirRecursive(agentSrc, agentDst, undefined, isMerge);
132
+ audit.push(`- ACT: ${isMerge ? "MERGED" : "COPIED"} templates (Files: ${stats.files}, Skipped: ${stats.skipped})`);
133
+
134
+ // Criar state se necessário
135
+ const stateTarget = path.join(agentDst, "state");
136
+ if (!fs.existsSync(stateTarget)) {
137
+ fs.mkdirSync(stateTarget, { recursive: true });
138
+ // Criar mission_control.json default
139
+ const mcDefault = {
140
+ project_status: "active",
141
+ project_name: path.basename(targetPath),
142
+ sprint_goal: "",
143
+ agents: [
144
+ { id: "orchestrator", role: "orchestrator", active: true },
145
+ { id: "researcher", role: "researcher", active: true },
146
+ { id: "writer", role: "writer", active: true },
147
+ ],
148
+ task_queue: [],
149
+ history: [],
150
+ settings: {
151
+ work_dir: "mission_control",
152
+ max_tasks_per_tick: 2,
153
+ default_priority: "medium",
154
+ },
155
+ };
156
+ fs.writeFileSync(path.join(stateTarget, "mission_control.json"), JSON.stringify(mcDefault, null, 2));
157
+ fs.writeFileSync(path.join(stateTarget, "MEMORY.md"), "# Memória Persistente\n\n(Adicione aqui resumos e decisões importantes)\n");
158
+ audit.push("- ACT: CREATED .agent/state/ (mission_control.json + MEMORY.md)");
159
+ }
160
+
161
+ console.log("\n✨ IDE install concluído com sucesso!");
162
+ writeAudit(targetPath, audit, flags);
163
+
164
+ } catch (err) {
165
+ console.error(`\n❌ Falha: ${err.message}`);
166
+ audit.push(`\n## ERROR: ${err.message}`);
167
+ writeAudit(targetPath, audit, flags);
168
+ process.exit(1);
169
+ }
170
+ }
171
+
172
+ /**
173
+ * Sub-comando: ide doctor
174
+ * Verifica se a IDE está "armada" com regras, hooks e skills.
175
+ */
176
+ async function runDoctor({ targetPath }) {
177
+ const agentDir = path.join(targetPath, ".agent");
178
+ const checks = [];
179
+
180
+ console.log("\n🏥 IDE Doctor — Verificando instalação para IDE:\n");
181
+
182
+ // Verificar .agent/
183
+ checks.push({ name: ".agent/", ok: fs.existsSync(agentDir) });
184
+
185
+ // Verificar rules
186
+ const rulesDir = path.join(agentDir, "rules");
187
+ const requiredRules = ["CONSENT_FIRST.md", "ROUTER_PROTOCOL.md"];
188
+ for (const rule of requiredRules) {
189
+ checks.push({ name: `rules/${rule}`, ok: fs.existsSync(path.join(rulesDir, rule)) });
190
+ }
191
+
192
+ // Verificar skills críticas
193
+ const skillsDir = path.join(agentDir, "skills");
194
+ const criticalSkills = ["openclaw-router", "openclaw-inspect", "openclaw-dev"];
195
+ for (const skill of criticalSkills) {
196
+ checks.push({ name: `skills/${skill}/SKILL.md`, ok: fs.existsSync(path.join(skillsDir, skill, "SKILL.md")) });
197
+ }
198
+
199
+ // Verificar hooks
200
+ const hooksDir = path.join(agentDir, "hooks");
201
+ checks.push({ name: "hooks/pre-tool-use.js", ok: fs.existsSync(path.join(hooksDir, "pre-tool-use.js")) });
202
+
203
+ // Exibir resultado
204
+ let allOk = true;
205
+ for (const c of checks) {
206
+ const icon = c.ok ? "✅" : "❌";
207
+ console.log(` ${icon} ${c.name}`);
208
+ if (!c.ok) allOk = false;
209
+ }
210
+
211
+ if (allOk) {
212
+ console.log("\n🎉 IDE está totalmente configurada!");
213
+ } else {
214
+ console.log("\n⚠️ Componentes ausentes. Rode: npx openclaw ide install --apply");
215
+ }
216
+ }
217
+
218
+ module.exports = { run };
package/lib/cli/init.js CHANGED
@@ -10,19 +10,42 @@
10
10
 
11
11
  const fs = require("fs");
12
12
  const path = require("path");
13
+ const readline = require("readline");
13
14
  const { initConfigDefaults, writeJsonSafe } = require("../config");
15
+ const { detectContext, getAuditHeader } = require("../context");
14
16
 
15
17
  // Caminho dos templates incluídos no pacote
16
18
  const TEMPLATES_DIR = path.join(__dirname, "..", "..", "templates", ".agent");
17
19
 
20
+ function ask(q) {
21
+ const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
22
+ return new Promise((res) => rl.question(q, (ans) => { rl.close(); res(ans.trim()); }));
23
+ }
24
+
25
+ function safeRel(targetPath, p) {
26
+ return path.relative(targetPath, p);
27
+ }
28
+
29
+ function writeAudit(targetPath, lines, flags) {
30
+ if (flags.audit === false) return;
31
+ const auditDir = path.join(targetPath, ".agent", "audit");
32
+ if (!fs.existsSync(auditDir)) {
33
+ // Tenta criar apenas se estivermos em modo apply, mas aqui já devemos estar
34
+ try { fs.mkdirSync(auditDir, { recursive: true }); } catch (e) { }
35
+ }
36
+ const filename = `init-${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
+
18
45
  /**
19
- * Copia diretório recursivamente.
20
- * @param {string} src — diretório fonte
21
- * @param {string} dest — diretório destino
22
- * @param {object} [stats] — contador de arquivos copiados
23
- * @returns {object} stats com { files, dirs }
46
+ * Copia diretório recursivamente (Utilitário mantido)
24
47
  */
25
- function copyDirRecursive(src, dest, stats = { files: 0, dirs: 0 }) {
48
+ function copyDirRecursive(src, dest, stats = { files: 0, dirs: 0, skipped: 0 }, merge = false) {
26
49
  if (!fs.existsSync(dest)) {
27
50
  fs.mkdirSync(dest, { recursive: true });
28
51
  stats.dirs++;
@@ -35,110 +58,143 @@ function copyDirRecursive(src, dest, stats = { files: 0, dirs: 0 }) {
35
58
  const destPath = path.join(dest, entry.name);
36
59
 
37
60
  if (entry.isDirectory()) {
38
- copyDirRecursive(srcPath, destPath, stats);
61
+ copyDirRecursive(srcPath, destPath, stats, merge);
39
62
  } else {
40
- fs.copyFileSync(srcPath, destPath);
41
- stats.files++;
63
+ if (merge && fs.existsSync(destPath)) {
64
+ stats.skipped++;
65
+ } else {
66
+ fs.copyFileSync(srcPath, destPath);
67
+ stats.files++;
68
+ }
42
69
  }
43
70
  }
44
-
45
71
  return stats;
46
72
  }
47
73
 
48
74
  /**
49
- * Executa o comando init.
50
- * @param {object} options
51
- * @param {string} options.targetPath — diretório alvo
52
- * @param {object} options.flags — flags do CLI (force, quiet)
75
+ * Executa o comando init com segurança.
53
76
  */
54
77
  async function run({ targetPath, flags }) {
55
78
  const agentDir = path.join(targetPath, ".agent");
56
79
  const configPath = path.join(targetPath, "openclaw.json");
80
+ const ctx = detectContext(targetPath);
57
81
 
58
- // Verificar se existe
59
- if (fs.existsSync(agentDir) && !flags.force) {
60
- console.error("❌ Diretório .agent/ já existe.");
61
- console.error(" Use --force para sobrescrever ou 'openclaw update' para atualizar.");
62
- process.exit(1);
63
- }
82
+ // Default: Plan Mode (read-only), exceto se --apply for passado
83
+ const planMode = !flags.apply;
84
+
85
+ const actions = [];
86
+ const audit = [getAuditHeader(ctx, "init", flags)];
87
+ const errors = [];
64
88
 
65
- // Verificar se templates existem
89
+ // 1. Validar Templates
66
90
  if (!fs.existsSync(TEMPLATES_DIR)) {
67
- console.error("❌ Templates não encontrados. Pacote pode estar corrompido.");
91
+ console.error("❌ Templates não encontrados. Pacote corrompido.");
68
92
  process.exit(1);
69
93
  }
70
94
 
71
- if (!flags.quiet) {
72
- console.log("🦀 OpenClaw — Inicializando projeto...\n");
73
- }
74
-
75
- // Se --force e existe, alertar
76
- if (fs.existsSync(agentDir) && flags.force) {
77
- if (!flags.quiet) {
78
- console.log("⚠️ --force: substituindo .agent/ existente\n");
95
+ // 2. Construir Plano
96
+ if (fs.existsSync(agentDir)) {
97
+ if (!flags.force && !flags.merge) {
98
+ console.error("❌ Diretório .agent/ já existe.");
99
+ console.error(" Use --merge (seguro) ou --force (destrutivo).");
100
+ process.exit(1);
79
101
  }
80
- fs.rmSync(agentDir, { recursive: true, force: true });
102
+ if (flags.force) {
103
+ actions.push({ type: "DELETE_DIR", path: agentDir, reason: "--force requested" });
104
+ actions.push({ type: "CREATE_DIR", path: agentDir });
105
+ actions.push({ type: "COPY_DIR", from: TEMPLATES_DIR, to: agentDir });
106
+ } else if (flags.merge) {
107
+ actions.push({ type: "MERGE_DIR", from: TEMPLATES_DIR, to: agentDir, reason: "--merge requested" });
108
+ }
109
+ } else {
110
+ actions.push({ type: "CREATE_DIR", path: agentDir });
111
+ actions.push({ type: "COPY_DIR", from: TEMPLATES_DIR, to: agentDir });
81
112
  }
82
113
 
83
- // Copiar templates
84
- const stats = copyDirRecursive(TEMPLATES_DIR, agentDir);
114
+ if (!fs.existsSync(configPath)) {
115
+ actions.push({ type: "CREATE_FILE", path: configPath, reason: "Default config" });
116
+ } else {
117
+ actions.push({ type: "NOOP", path: configPath, reason: "Config exists" });
118
+ }
85
119
 
86
- if (!flags.quiet) {
87
- console.log(`✅ .agent/ instalado com sucesso!`);
88
- console.log(` 📁 ${stats.dirs} diretórios criados`);
89
- console.log(` 📄 ${stats.files} arquivos copiados\n`);
120
+ // 3. Exibir Plano
121
+ console.log(`\n🧭 Plano de Execução (${planMode ? "SIMULAÇÃO" : "APPLY"}):\n`);
122
+ console.log(` Contexto: ${ctx.env} | IDE: ${ctx.ide}\n`);
123
+
124
+ for (const a of actions) {
125
+ if (a.type === "DELETE_DIR") console.log(` 🔥 DELETE ${safeRel(targetPath, a.path)} (${a.reason})`);
126
+ if (a.type === "CREATE_DIR") console.log(` 📁 CREATE ${safeRel(targetPath, a.path)}`);
127
+ if (a.type === "COPY_DIR") console.log(` 📦 COPY templates -> ${safeRel(targetPath, a.to)}`);
128
+ if (a.type === "MERGE_DIR") console.log(` 🔄 MERGE templates -> ${safeRel(targetPath, a.to)} (Preservando existentes)`);
129
+ if (a.type === "CREATE_FILE") console.log(` 📝 CREATE ${safeRel(targetPath, a.path)}`);
130
+ if (a.type === "NOOP") console.log(` ✅ KEEP ${safeRel(targetPath, a.path)}`);
90
131
  }
91
132
 
92
- // Criar openclaw.json com defaults (se não existir)
93
- if (!fs.existsSync(configPath)) {
94
- const defaults = initConfigDefaults({});
95
- writeJsonSafe(configPath, defaults);
133
+ if (planMode) {
134
+ console.log("\n🔒 Modo PLAN (Read-Only). Nenhuma alteração feita.");
135
+ console.log(" Para aplicar, rode: npx openclaw init --apply [--merge|--force]");
136
+ return;
137
+ }
96
138
 
97
- if (!flags.quiet) {
98
- console.log("📋 openclaw.json criado com configurações padrão\n");
139
+ // 4. Confirmação
140
+ if (!flags.yes) {
141
+ if (actions.some(a => a.type === "DELETE_DIR")) {
142
+ console.log("\n⚠️ PERIGO: Operação destrutiva detectada (--force).");
143
+ const phrase = await ask("Digite 'DELETE .agent' para confirmar: ");
144
+ if (phrase !== "DELETE .agent") {
145
+ console.log("⏹️ Cancelado.");
146
+ return;
147
+ }
148
+ } else {
149
+ const ok = await ask("\nAplicar este plano? (y/N): ");
150
+ if (ok.toLowerCase() !== "y") {
151
+ console.log("⏹️ Cancelado.");
152
+ return;
153
+ }
99
154
  }
100
- } else if (!flags.quiet) {
101
- console.log("📋 openclaw.json já existe — mantido\n");
102
155
  }
103
156
 
104
- // Resumo final
105
- if (!flags.quiet) {
106
- console.log("📂 Estrutura instalada:");
107
- listInstalledStructure(agentDir, " ");
157
+ // 5. Execução
158
+ try {
159
+ console.log("\n🚀 Executando...");
108
160
 
109
- console.log("\n🚀 Próximos passos:");
110
- console.log(" 1. openclaw setup — configurar ambiente");
111
- console.log(" 2. openclaw doctor — verificar saúde");
112
- console.log(" 3. openclaw status — ver status\n");
113
- }
114
- }
161
+ for (const a of actions) {
162
+ if (a.type === "DELETE_DIR") {
163
+ fs.rmSync(a.path, { recursive: true, force: true });
164
+ audit.push(`- ACT: DELETED ${a.path}`);
165
+ }
166
+ }
115
167
 
116
- /**
117
- * Lista a estrutura instalada de forma visual.
118
- * @param {string} dir — diretório para listar
119
- * @param {string} prefix — prefixo para indentação
120
- */
121
- function listInstalledStructure(dir, prefix = "") {
122
- const entries = fs.readdirSync(dir, { withFileTypes: true })
123
- .sort((a, b) => {
124
- // Diretórios primeiro, depois arquivos
125
- if (a.isDirectory() && !b.isDirectory()) return -1;
126
- if (!a.isDirectory() && b.isDirectory()) return 1;
127
- return a.name.localeCompare(b.name);
128
- });
129
-
130
- for (let i = 0; i < entries.length; i++) {
131
- const entry = entries[i];
132
- const isLast = i === entries.length - 1;
133
- const connector = isLast ? "└── " : "├── ";
134
- const icon = entry.isDirectory() ? "📁" : "📄";
135
-
136
- console.log(`${prefix}${connector}${icon} ${entry.name}`);
168
+ // Executar cópia/merge se necessário
169
+ const copyAction = actions.find(a => a.type === "COPY_DIR" || a.type === "MERGE_DIR");
170
+ if (copyAction) {
171
+ const isMerge = copyAction.type === "MERGE_DIR";
172
+ const stats = copyDirRecursive(TEMPLATES_DIR, agentDir, undefined, isMerge);
173
+ audit.push(`- ACT: ${isMerge ? "MERGED" : "COPIED"} templates (Files: ${stats.files}, Skipped: ${stats.skipped})`);
174
+ console.log(` ✅ Templates processados.`);
175
+ }
137
176
 
138
- if (entry.isDirectory()) {
139
- const childPrefix = prefix + (isLast ? " " : "│ ");
140
- listInstalledStructure(path.join(dir, entry.name), childPrefix);
177
+ // Criar config se necessário
178
+ if (actions.some(a => a.type === "CREATE_FILE" && a.path === configPath)) {
179
+ const defaults = initConfigDefaults({});
180
+ writeJsonSafe(configPath, defaults);
181
+ audit.push(`- ACT: CREATED openclaw.json`);
182
+ console.log(` ✅ Config criada.`);
141
183
  }
184
+
185
+ // Gravar contexto se ainda não existe
186
+ const contextDir = path.join(agentDir, "context");
187
+ if (!fs.existsSync(contextDir)) fs.mkdirSync(contextDir, { recursive: true });
188
+ fs.writeFileSync(path.join(contextDir, "context.json"), JSON.stringify(ctx, null, 2));
189
+
190
+ console.log("\n✨ Concluído com sucesso!");
191
+ writeAudit(targetPath, audit, flags);
192
+
193
+ } catch (err) {
194
+ console.error(`\n❌ Falha na execução: ${err.message}`);
195
+ audit.push(`\n## ERROR: ${err.message}`);
196
+ writeAudit(targetPath, audit, flags);
197
+ process.exit(1);
142
198
  }
143
199
  }
144
200
 
@@ -0,0 +1,58 @@
1
+ "use strict";
2
+
3
+ /**
4
+ * Comando CLI: inspect
5
+ *
6
+ * 100% Read-Only. Coleta e exibe contexto do ambiente sem alterar nada.
7
+ * Usado como primeiro passo antes de qualquer ação.
8
+ */
9
+
10
+ const path = require("path");
11
+ const collectContext = require("../context/collector");
12
+
13
+ // Caminho dos templates do pacote
14
+ const TEMPLATES_DIR = path.join(__dirname, "..", "..", "templates");
15
+
16
+ /**
17
+ * Executa o comando inspect (read-only puro).
18
+ * @param {object} options
19
+ * @param {string} options.targetPath — diretório alvo
20
+ * @param {object} options.flags — flags do CLI
21
+ */
22
+ async function run({ targetPath, flags }) {
23
+ const ctx = collectContext({
24
+ targetPath,
25
+ templatesDir: TEMPLATES_DIR,
26
+ });
27
+
28
+ if (flags.quiet) {
29
+ // Modo silencioso: só JSON
30
+ console.log(JSON.stringify(ctx, null, 2));
31
+ return ctx;
32
+ }
33
+
34
+ console.log("\n🔎 OpenClaw Inspect (Read-Only)\n");
35
+ console.log(` 🖥️ Plataforma: ${ctx.env.platform}`);
36
+ console.log(` 🐳 Docker: ${ctx.env.docker}`);
37
+ console.log(` 🪟 WSL: ${ctx.env.wsl}`);
38
+ console.log(` 💻 IDE: ${ctx.ide}`);
39
+ console.log(` 📂 Path: ${ctx.targetPath}`);
40
+ console.log(` 📦 OpenClaw instalado: ${ctx.openclaw.hasAgentDir ? "Sim" : "Não"}`);
41
+ console.log(` 📋 Config: ${ctx.openclaw.hasConfig ? "Sim" : "Não"}`);
42
+ console.log(` 🐙 Git repo: ${ctx.git.isRepo ? "Sim" : "Não"}`);
43
+
44
+ if (ctx.skillsInstalled.length > 0) {
45
+ console.log(`\n 🧠 Skills instaladas (${ctx.skillsInstalled.length}):`);
46
+ ctx.skillsInstalled.forEach(s => console.log(` • ${s.name}`));
47
+ }
48
+
49
+ if (ctx.skillsInTemplates.length > 0) {
50
+ console.log(`\n 📦 Skills disponíveis nos templates (${ctx.skillsInTemplates.length}):`);
51
+ ctx.skillsInTemplates.forEach(s => console.log(` • ${s.name}`));
52
+ }
53
+
54
+ console.log("\n✅ Inspect concluído (nenhuma alteração feita).\n");
55
+ return ctx;
56
+ }
57
+
58
+ module.exports = { run };