biaws 0.1.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.
Files changed (76) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +229 -0
  3. package/bin/biaws.js +3 -0
  4. package/docs/command-taxonomy.md +36 -0
  5. package/docs/instance-lifecycle.md +82 -0
  6. package/docs/releasing.md +105 -0
  7. package/package.json +63 -0
  8. package/shared/skillPackage.js +100 -0
  9. package/src/apiClient.js +89 -0
  10. package/src/baseCommands.js +122 -0
  11. package/src/commands/agent/configure.js +3 -0
  12. package/src/commands/agent/doctor.js +3 -0
  13. package/src/commands/agent.js +400 -0
  14. package/src/commands/api.js +17 -0
  15. package/src/commands/applications/get.js +17 -0
  16. package/src/commands/applications/list.js +32 -0
  17. package/src/commands/applications.js +7 -0
  18. package/src/commands/configure/claude.js +25 -0
  19. package/src/commands/configure/codex.js +25 -0
  20. package/src/commands/configure/doctor.js +49 -0
  21. package/src/commands/configure/skills.js +74 -0
  22. package/src/commands/configure.js +19 -0
  23. package/src/commands/demands/complete-task.js +6 -0
  24. package/src/commands/demands/get.js +16 -0
  25. package/src/commands/demands/list.js +48 -0
  26. package/src/commands/demands/task-status.js +49 -0
  27. package/src/commands/demands/tasks.js +44 -0
  28. package/src/commands/demands.js +7 -0
  29. package/src/commands/instance/backup.js +74 -0
  30. package/src/commands/instance/list.js +39 -0
  31. package/src/commands/instance/remove.js +72 -0
  32. package/src/commands/instance/restore.js +96 -0
  33. package/src/commands/instance/setup.js +349 -0
  34. package/src/commands/instance/show.js +31 -0
  35. package/src/commands/instance/start.js +32 -0
  36. package/src/commands/instance/status.js +42 -0
  37. package/src/commands/instance/stop.js +30 -0
  38. package/src/commands/instance.js +18 -0
  39. package/src/commands/issues/get.js +16 -0
  40. package/src/commands/issues/list.js +51 -0
  41. package/src/commands/issues/transition.js +33 -0
  42. package/src/commands/issues.js +7 -0
  43. package/src/commands/monitoring/describe.js +13 -0
  44. package/src/commands/monitoring/signal.js +25 -0
  45. package/src/commands/monitoring/signals.js +17 -0
  46. package/src/commands/monitoring/validate.js +14 -0
  47. package/src/commands/monitoring.js +163 -0
  48. package/src/commands/skills/install-all.js +13 -0
  49. package/src/commands/skills/install.js +16 -0
  50. package/src/commands/skills/list.js +5 -0
  51. package/src/commands/skills/publish-all.js +14 -0
  52. package/src/commands/skills/publish.js +11 -0
  53. package/src/commands/skills/status.js +7 -0
  54. package/src/commands/skills/update.js +12 -0
  55. package/src/commands/skills.js +303 -0
  56. package/src/commands/workspaces/get.js +15 -0
  57. package/src/commands/workspaces/list.js +20 -0
  58. package/src/commands/workspaces.js +7 -0
  59. package/src/compatibilityCommands.js +107 -0
  60. package/src/configure/command.js +50 -0
  61. package/src/core/context.js +162 -0
  62. package/src/core/errors.js +25 -0
  63. package/src/core/processRunner.js +119 -0
  64. package/src/core/prompts.js +138 -0
  65. package/src/core/redaction.js +29 -0
  66. package/src/core/terminal.js +72 -0
  67. package/src/core/wizard.js +279 -0
  68. package/src/domain/readCommand.js +65 -0
  69. package/src/domain/readService.js +126 -0
  70. package/src/domain/writeCommand.js +91 -0
  71. package/src/domain/writeService.js +122 -0
  72. package/src/index.js +5 -0
  73. package/src/instance/command.js +37 -0
  74. package/src/instance/service.js +515 -0
  75. package/src/localSkills.js +143 -0
  76. package/src/skillPackage.js +4 -0
@@ -0,0 +1,400 @@
1
+ import { mkdir, readFile, writeFile } from "node:fs/promises";
2
+ import { spawn } from "node:child_process";
3
+ import path from "node:path";
4
+
5
+ import { BaseCommand, TOOL_DIRECTORY } from "../baseCommands.js";
6
+ import { showTopic } from "../compatibilityCommands.js";
7
+ import { readLock } from "../localSkills.js";
8
+ import { runSkillsCommand } from "./skills.js";
9
+
10
+ const CODEX_BEGIN = "# BEGIN BIAWS MANAGED MCP";
11
+ const CODEX_END = "# END BIAWS MANAGED MCP";
12
+
13
+ function skillTarget(client, projectDirectory) {
14
+ return path.join(
15
+ projectDirectory,
16
+ client === "claude" ? ".claude" : ".agents",
17
+ "skills",
18
+ );
19
+ }
20
+
21
+ function mcpEntrypoint(toolDirectory) {
22
+ return path.resolve(toolDirectory, "..", "biaws-mcp", "src", "index.js");
23
+ }
24
+
25
+ async function readOptional(filePath, fallback = "") {
26
+ try {
27
+ return await readFile(filePath, "utf8");
28
+ } catch (error) {
29
+ if (error.code === "ENOENT") return fallback;
30
+ throw error;
31
+ }
32
+ }
33
+
34
+ async function writeCodexConfiguration(
35
+ projectDirectory,
36
+ entrypoint,
37
+ envFile,
38
+ workspaceId,
39
+ force,
40
+ ) {
41
+ const configPath = path.join(projectDirectory, ".codex", "config.toml");
42
+ await mkdir(path.dirname(configPath), { recursive: true });
43
+ let current = await readOptional(configPath);
44
+ const managedPattern = new RegExp(
45
+ `${CODEX_BEGIN.replace(/[.*+?^${}()|[\]\\]/gu, "\\$&")}[\\s\\S]*?${CODEX_END.replace(/[.*+?^${}()|[\]\\]/gu, "\\$&")}\\n?`,
46
+ "gu",
47
+ );
48
+ current = current.replace(managedPattern, "").trimEnd();
49
+ if (/\[mcp_servers\.biaws\]/u.test(current) && !force) {
50
+ throw new Error(
51
+ `Já existe uma configuração Codex não gerenciada para mcp_servers.biaws em ${configPath}; use --force para mantê-la sob gestão do CLI`,
52
+ );
53
+ }
54
+ if (force) {
55
+ current = current.replace(
56
+ /\n?\[mcp_servers\.biaws\]\n(?:[^[\n].*\n?)*/gu,
57
+ "",
58
+ );
59
+ }
60
+ const managed = `${CODEX_BEGIN}
61
+ [mcp_servers.biaws]
62
+ command = "node"
63
+ args = [${JSON.stringify(entrypoint)}]
64
+ env = {${envFile ? ` BIAWS_ENV_FILE = ${JSON.stringify(envFile)},` : ""} ISSUE_WORKSPACE_ID = ${JSON.stringify(workspaceId)} }
65
+ ${CODEX_END}
66
+ `;
67
+ const output = current ? `${current}\n\n${managed}` : managed;
68
+ await writeFile(configPath, output, "utf8");
69
+ return configPath;
70
+ }
71
+
72
+ async function writeClaudeConfiguration(
73
+ projectDirectory,
74
+ entrypoint,
75
+ envFile,
76
+ workspaceId,
77
+ force,
78
+ ) {
79
+ const configPath = path.join(projectDirectory, ".mcp.json");
80
+ const raw = await readOptional(configPath, "{}");
81
+ let config;
82
+ try {
83
+ config = JSON.parse(raw);
84
+ } catch {
85
+ throw new Error(`JSON inválido em ${configPath}`);
86
+ }
87
+ config.mcpServers ||= {};
88
+ if (config.mcpServers.biaws && !force) {
89
+ const current = config.mcpServers.biaws;
90
+ if (
91
+ current.command !== "node" ||
92
+ JSON.stringify(current.args || []) !== JSON.stringify([entrypoint])
93
+ ) {
94
+ throw new Error(
95
+ `Já existe uma configuração Claude diferente para o servidor biaws em ${configPath}; use --force para substituí-la`,
96
+ );
97
+ }
98
+ }
99
+ config.mcpServers.biaws = {
100
+ type: "stdio",
101
+ command: "node",
102
+ args: [entrypoint],
103
+ env: {
104
+ ...(envFile ? { BIAWS_ENV_FILE: envFile } : {}),
105
+ ISSUE_WORKSPACE_ID: workspaceId,
106
+ },
107
+ };
108
+ await writeFile(configPath, `${JSON.stringify(config, null, 2)}\n`, "utf8");
109
+ return configPath;
110
+ }
111
+
112
+ async function resolveWorkspaceId(api, requestedWorkspaceId = "") {
113
+ let identity;
114
+ try {
115
+ identity = await api.identity();
116
+ } catch (error) {
117
+ if (!requestedWorkspaceId && error.statusCode === 400) {
118
+ throw new Error(
119
+ "A identidade acessa mais de um workspace. Informe --workspace <id> para selecionar o workspace deste projeto.",
120
+ );
121
+ }
122
+ throw error;
123
+ }
124
+ const workspaceId = requestedWorkspaceId || identity?.actor?.workspaceId;
125
+ if (!workspaceId) {
126
+ if ((identity?.actor?.workspaces?.length || 0) > 1) {
127
+ throw new Error(
128
+ "A identidade acessa mais de um workspace. Informe --workspace <id> para selecionar o workspace deste projeto.",
129
+ );
130
+ }
131
+ throw new Error(
132
+ "Não foi possível determinar o workspace. Informe --workspace <id> e confirme que a identidade técnica é membro dele.",
133
+ );
134
+ }
135
+ return { workspaceId, identity };
136
+ }
137
+
138
+ async function configure(api, client, options, context) {
139
+ if (!["codex", "claude"].includes(client)) {
140
+ throw new Error("Informe o cliente: biaws agent configure codex|claude");
141
+ }
142
+ const projectDirectory = path.resolve(options.project || process.cwd());
143
+ const { workspaceId } = await resolveWorkspaceId(api, context.workspaceId);
144
+ const target = skillTarget(client, projectDirectory);
145
+ const entrypoint = mcpEntrypoint(context.toolDirectory);
146
+ const configPath =
147
+ client === "codex"
148
+ ? await writeCodexConfiguration(
149
+ projectDirectory,
150
+ entrypoint,
151
+ context.envFile,
152
+ workspaceId,
153
+ options.force,
154
+ )
155
+ : await writeClaudeConfiguration(
156
+ projectDirectory,
157
+ entrypoint,
158
+ context.envFile,
159
+ workspaceId,
160
+ options.force,
161
+ );
162
+ const installation = await runSkillsCommand(api, "install-all", [], {
163
+ target,
164
+ force: options.force,
165
+ json: false,
166
+ quiet: true,
167
+ });
168
+ const result = {
169
+ client,
170
+ projectDirectory,
171
+ configPath,
172
+ skillTarget: target,
173
+ installation,
174
+ workspaceId,
175
+ };
176
+ if (options.json) console.log(JSON.stringify(result, null, 2));
177
+ else {
178
+ console.log(`Cliente ${client} configurado em ${configPath}`);
179
+ console.log(`Skills disponíveis em ${target}`);
180
+ }
181
+ return result;
182
+ }
183
+
184
+ async function requestStatus(url, headers = {}) {
185
+ try {
186
+ const response = await fetch(url, { headers });
187
+ const body = await response.json().catch(() => null);
188
+ return {
189
+ ok: response.ok,
190
+ status: response.status,
191
+ detail: response.ok ? null : body?.error?.message || null,
192
+ };
193
+ } catch (error) {
194
+ return { ok: false, status: null, detail: error.message };
195
+ }
196
+ }
197
+
198
+ async function mcpStatus(entrypoint, envFile, workspaceId) {
199
+ return new Promise((resolve) => {
200
+ const child = spawn(process.execPath, [entrypoint], {
201
+ stdio: ["pipe", "pipe", "pipe"],
202
+ env: {
203
+ ...process.env,
204
+ ...(envFile ? { BIAWS_ENV_FILE: envFile } : {}),
205
+ ISSUE_WORKSPACE_ID: workspaceId,
206
+ },
207
+ });
208
+ let output = "";
209
+ let errorOutput = "";
210
+ let settled = false;
211
+ let toolsRequested = false;
212
+ const finish = (result) => {
213
+ if (settled) return;
214
+ settled = true;
215
+ clearTimeout(timeout);
216
+ child.kill();
217
+ resolve(result);
218
+ };
219
+ const timeout = setTimeout(
220
+ () =>
221
+ finish({
222
+ ok: false,
223
+ detail: errorOutput.trim() || "MCP não respondeu em 5 segundos",
224
+ }),
225
+ 5_000,
226
+ );
227
+ child.stderr.on("data", (chunk) => {
228
+ errorOutput += chunk;
229
+ });
230
+ child.stdout.on("data", (chunk) => {
231
+ output += chunk;
232
+ const lines = output.split(/\r?\n/u);
233
+ output = lines.pop() || "";
234
+ for (const line of lines) {
235
+ if (!line.trim()) continue;
236
+ try {
237
+ const message = JSON.parse(line);
238
+ if (message.id === 1 && message.result && !toolsRequested) {
239
+ toolsRequested = true;
240
+ child.stdin.write(
241
+ `${JSON.stringify({
242
+ jsonrpc: "2.0",
243
+ method: "notifications/initialized",
244
+ params: {},
245
+ })}\n${JSON.stringify({
246
+ jsonrpc: "2.0",
247
+ id: 2,
248
+ method: "tools/list",
249
+ params: {},
250
+ })}\n`,
251
+ );
252
+ }
253
+ if (message.id === 2 && Array.isArray(message.result?.tools)) {
254
+ finish({ ok: true, toolCount: message.result.tools.length });
255
+ }
256
+ } catch {
257
+ finish({ ok: false, detail: "MCP retornou uma resposta inválida" });
258
+ }
259
+ }
260
+ });
261
+ child.on("error", (error) => finish({ ok: false, detail: error.message }));
262
+ child.stdin.on("error", (error) =>
263
+ finish({ ok: false, detail: error.message }),
264
+ );
265
+ child.on("exit", (code) => {
266
+ if (!settled && code !== null) {
267
+ finish({
268
+ ok: false,
269
+ detail: errorOutput.trim() || `MCP encerrou com código ${code}`,
270
+ });
271
+ }
272
+ });
273
+ child.stdin.write(
274
+ `${JSON.stringify({
275
+ jsonrpc: "2.0",
276
+ id: 1,
277
+ method: "initialize",
278
+ params: { protocolVersion: "2024-11-05", capabilities: {} },
279
+ })}\n`,
280
+ );
281
+ });
282
+ }
283
+
284
+ function configurationStatus(
285
+ client,
286
+ contents,
287
+ entrypoint,
288
+ envFile,
289
+ workspaceId,
290
+ ) {
291
+ if (typeof contents !== "string") {
292
+ return { ok: false, detail: "Configuração MCP não encontrada" };
293
+ }
294
+ if (client === "codex") {
295
+ const ok =
296
+ contents.includes("[mcp_servers.biaws]") &&
297
+ contents.includes(JSON.stringify(entrypoint)) &&
298
+ (!envFile || contents.includes(JSON.stringify(envFile))) &&
299
+ contents.includes(`ISSUE_WORKSPACE_ID = ${JSON.stringify(workspaceId)}`);
300
+ return {
301
+ ok,
302
+ detail: ok
303
+ ? null
304
+ : "A configuração Codex não aponta para a instância selecionada",
305
+ };
306
+ }
307
+ try {
308
+ const server = JSON.parse(contents).mcpServers?.biaws;
309
+ const ok =
310
+ server?.command === "node" &&
311
+ JSON.stringify(server.args || []) === JSON.stringify([entrypoint]) &&
312
+ (!envFile || server.env?.BIAWS_ENV_FILE === envFile) &&
313
+ server.env?.ISSUE_WORKSPACE_ID === workspaceId;
314
+ return {
315
+ ok,
316
+ detail: ok
317
+ ? null
318
+ : "A configuração Claude não aponta para a instância selecionada",
319
+ };
320
+ } catch {
321
+ return { ok: false, detail: "Configuração Claude contém JSON inválido" };
322
+ }
323
+ }
324
+
325
+ async function doctor(api, client, options, context) {
326
+ if (!["codex", "claude"].includes(client)) {
327
+ throw new Error("Informe o cliente: biaws agent doctor codex|claude");
328
+ }
329
+ const projectDirectory = path.resolve(options.project || process.cwd());
330
+ const target = skillTarget(client, projectDirectory);
331
+ const configPath =
332
+ client === "codex"
333
+ ? path.join(projectDirectory, ".codex", "config.toml")
334
+ : path.join(projectDirectory, ".mcp.json");
335
+ const entrypoint = mcpEntrypoint(context.toolDirectory);
336
+ const resolved = await resolveWorkspaceId(api, context.workspaceId);
337
+ const [health, mcp, configContents, lock] = await Promise.all([
338
+ requestStatus(`${context.apiUrl}/api/health`),
339
+ mcpStatus(entrypoint, context.envFile, resolved.workspaceId),
340
+ readOptional(configPath, null),
341
+ readLock(target),
342
+ ]);
343
+ const checks = {
344
+ node: { ok: Number(process.versions.node.split(".")[0]) >= 20 },
345
+ api: health,
346
+ authentication: { ok: true },
347
+ workspace: { ok: true, id: resolved.workspaceId },
348
+ mcp,
349
+ configuration: {
350
+ ...configurationStatus(
351
+ client,
352
+ configContents,
353
+ entrypoint,
354
+ context.envFile,
355
+ resolved.workspaceId,
356
+ ),
357
+ path: configPath,
358
+ },
359
+ skills: {
360
+ ok: Object.keys(lock.skills).length > 0,
361
+ count: Object.keys(lock.skills).length,
362
+ target,
363
+ },
364
+ };
365
+ const ok = Object.values(checks).every((check) => check.ok);
366
+ const result = { ok, client, checks };
367
+ if (options.json) console.log(JSON.stringify(result, null, 2));
368
+ else {
369
+ for (const [name, check] of Object.entries(checks)) {
370
+ console.log(`${check.ok ? "OK" : "FALHA"} ${name}`);
371
+ if (!check.ok && check.detail) console.log(` ${check.detail}`);
372
+ }
373
+ }
374
+ if (!ok) process.exitCode = 1;
375
+ return result;
376
+ }
377
+
378
+ export async function runAgentCommand(
379
+ api,
380
+ action,
381
+ positional,
382
+ options,
383
+ context,
384
+ ) {
385
+ const client = positional[0];
386
+ if (action === "configure") return configure(api, client, options, context);
387
+ if (action === "doctor") return doctor(api, client, options, context);
388
+ throw new Error(`Ação de agente desconhecida: ${action || "(ausente)"}`);
389
+ }
390
+
391
+ export default class Agent extends BaseCommand {
392
+ static description =
393
+ "Configura e diagnostica clientes de agentes (compatibilidade legada)";
394
+
395
+ async run() {
396
+ await showTopic(this, "agent");
397
+ }
398
+ }
399
+
400
+ export const TOOL_DIR = TOOL_DIRECTORY;
@@ -0,0 +1,17 @@
1
+ import { Help } from "@oclif/core";
2
+
3
+ import { AuthenticatedApiCommand } from "../baseCommands.js";
4
+
5
+ export default class Api extends AuthenticatedApiCommand {
6
+ static description =
7
+ "Executa operações autenticadas nos recursos da API do Bondia Workspaces";
8
+ static examples = [
9
+ "<%= config.bin %> api workspaces list --json",
10
+ "<%= config.bin %> api demands get <id-ou-código> --json",
11
+ ];
12
+
13
+ async run() {
14
+ await this.parse(Api);
15
+ await new Help(this.config).showHelp(["api"]);
16
+ }
17
+ }
@@ -0,0 +1,17 @@
1
+ import { Args } from "@oclif/core";
2
+ import { connectionFlags, ReadCommand } from "../../domain/readCommand.js";
3
+ export default class ApplicationGet extends ReadCommand {
4
+ static description = "Obtém uma aplicação por ID";
5
+ static args = { id: Args.string({ required: true }) };
6
+ static flags = connectionFlags;
7
+ async run() {
8
+ const { args, flags } = await this.parse(ApplicationGet);
9
+ const result = await this.read(flags, (api) => api.application(args.id), {
10
+ resource: "application",
11
+ operation: "get",
12
+ });
13
+ this.emit(flags, result, (p) =>
14
+ JSON.stringify(p.application || p, null, 2),
15
+ );
16
+ }
17
+ }
@@ -0,0 +1,32 @@
1
+ import {
2
+ connectionFlags,
3
+ listFlags,
4
+ ReadCommand,
5
+ } from "../../domain/readCommand.js";
6
+ import { table } from "../../domain/readService.js";
7
+ export default class ApplicationList extends ReadCommand {
8
+ static description = "Lista aplicações do workspace";
9
+ static flags = { ...connectionFlags, ...listFlags };
10
+ async run() {
11
+ const { flags } = await this.parse(ApplicationList);
12
+ const result = await this.read(
13
+ flags,
14
+ (api, c) =>
15
+ api.applications(c.workspaceId, {
16
+ q: flags.search,
17
+ status: flags.status,
18
+ page: flags.page,
19
+ limit: flags.limit,
20
+ }),
21
+ { resource: "applications", operation: "list", requireWorkspace: true },
22
+ );
23
+ this.emit(flags, result, (p) =>
24
+ table(p.items || [], [
25
+ ["ID", (x) => x.id],
26
+ ["CHAVE", (x) => x.key],
27
+ ["NOME", (x) => x.name],
28
+ ["STATUS", (x) => x.status],
29
+ ]),
30
+ );
31
+ }
32
+ }
@@ -0,0 +1,7 @@
1
+ import { Command } from "@oclif/core";
2
+ export default class Applications extends Command {
3
+ static description = "Consulta aplicações autorizadas pela API";
4
+ async run() {
5
+ await this.config.runCommand("help", ["applications"]);
6
+ }
7
+ }
@@ -0,0 +1,25 @@
1
+ import { ProjectCommand } from "../../baseCommands.js";
2
+ import {
3
+ configureContextFlags,
4
+ configureContextInput,
5
+ legacyAgentContext,
6
+ } from "../../configure/command.js";
7
+ import { runAgentCommand } from "../agent.js";
8
+
9
+ export default class ConfigureClaude extends ProjectCommand {
10
+ static description =
11
+ "configura MCP e skills do Claude sem persistir credenciais no projeto";
12
+ static flags = configureContextFlags;
13
+
14
+ async run() {
15
+ const { flags } = await this.parse(ConfigureClaude);
16
+ const context = await this.projectContext(configureContextInput(flags));
17
+ await runAgentCommand(
18
+ context.api,
19
+ "configure",
20
+ ["claude"],
21
+ { ...flags, project: context.projectDirectory },
22
+ legacyAgentContext(context),
23
+ );
24
+ }
25
+ }
@@ -0,0 +1,25 @@
1
+ import { ProjectCommand } from "../../baseCommands.js";
2
+ import {
3
+ configureContextFlags,
4
+ configureContextInput,
5
+ legacyAgentContext,
6
+ } from "../../configure/command.js";
7
+ import { runAgentCommand } from "../agent.js";
8
+
9
+ export default class ConfigureCodex extends ProjectCommand {
10
+ static description =
11
+ "configura MCP e skills do Codex sem persistir credenciais no projeto";
12
+ static flags = configureContextFlags;
13
+
14
+ async run() {
15
+ const { flags } = await this.parse(ConfigureCodex);
16
+ const context = await this.projectContext(configureContextInput(flags));
17
+ await runAgentCommand(
18
+ context.api,
19
+ "configure",
20
+ ["codex"],
21
+ { ...flags, project: context.projectDirectory },
22
+ legacyAgentContext(context),
23
+ );
24
+ }
25
+ }
@@ -0,0 +1,49 @@
1
+ import { wizardFlags, collectWizardValues } from "../../core/wizard.js";
2
+ import { ProjectCommand } from "../../baseCommands.js";
3
+ import {
4
+ clientArgument,
5
+ configureContextFlags,
6
+ configureContextInput,
7
+ legacyAgentContext,
8
+ } from "../../configure/command.js";
9
+ import { runAgentCommand } from "../agent.js";
10
+
11
+ const definition = {
12
+ kind: "configure.doctor",
13
+ questions: [
14
+ {
15
+ name: "client",
16
+ type: "select",
17
+ message: "Cliente a diagnosticar",
18
+ choices: [
19
+ { name: "Codex", value: "codex" },
20
+ { name: "Claude", value: "claude" },
21
+ ],
22
+ },
23
+ ],
24
+ };
25
+
26
+ export default class ConfigureDoctor extends ProjectCommand {
27
+ static description = "diagnostica configuração, autenticação, MCP e skills";
28
+ static args = { client: clientArgument };
29
+ static flags = { ...configureContextFlags, ...wizardFlags };
30
+
31
+ async run() {
32
+ const { args, flags } = await this.parse(ConfigureDoctor);
33
+ const context = await this.projectContext(configureContextInput(flags));
34
+ const collected = await collectWizardValues(definition, {
35
+ environment: context.env,
36
+ flags: { client: args.client },
37
+ options: flags,
38
+ promptAdapter: this.adapters.prompts,
39
+ terminal: this.adapters.terminal,
40
+ });
41
+ await runAgentCommand(
42
+ context.api,
43
+ "doctor",
44
+ [collected.values.client],
45
+ { ...flags, project: context.projectDirectory },
46
+ legacyAgentContext(context),
47
+ );
48
+ }
49
+ }
@@ -0,0 +1,74 @@
1
+ import path from "node:path";
2
+
3
+ import { Args, Flags } from "@oclif/core";
4
+
5
+ import { ProjectCommand } from "../../baseCommands.js";
6
+ import {
7
+ configureContextFlags,
8
+ configureContextInput,
9
+ } from "../../configure/command.js";
10
+ import { CliError } from "../../core/errors.js";
11
+ import { runSkillsCommand } from "../skills.js";
12
+
13
+ export default class ConfigureSkills extends ProjectCommand {
14
+ static description = "lista, instala, atualiza e verifica skills do projeto";
15
+ static args = {
16
+ action: Args.string({
17
+ description: "operação sobre as skills",
18
+ options: ["list", "install", "update", "verify"],
19
+ required: true,
20
+ }),
21
+ skill: Args.string({ description: "ID da skill", required: false }),
22
+ };
23
+ static flags = {
24
+ ...configureContextFlags,
25
+ client: Flags.string({
26
+ description: "diretório padrão do cliente",
27
+ options: ["codex", "claude"],
28
+ default: "codex",
29
+ }),
30
+ all: Flags.boolean({ description: "instala todas as skills publicadas" }),
31
+ };
32
+
33
+ async run() {
34
+ const { args, flags } = await this.parse(ConfigureSkills);
35
+ const context = await this.projectContext(configureContextInput(flags));
36
+ const target = path.join(
37
+ context.projectDirectory,
38
+ flags.client === "claude" ? ".claude" : ".agents",
39
+ "skills",
40
+ );
41
+ let action = args.action;
42
+ let positional = args.skill ? [args.skill] : [];
43
+ if (action === "install" && flags.all) action = "install-all";
44
+ if (action === "install" && !args.skill) {
45
+ if (!context.isInteractive) {
46
+ throw new CliError(
47
+ "Informe o ID da skill ou use --all em modo não interativo.",
48
+ { code: "SKILL_SELECTION_REQUIRED", exitCode: 2 },
49
+ );
50
+ }
51
+ const catalog = await context.api.list();
52
+ if (!catalog.items.length)
53
+ throw new CliError("Nenhuma skill publicada no catálogo.", {
54
+ code: "SKILL_CATALOG_EMPTY",
55
+ });
56
+ const selected = await this.adapters.prompts.ask({
57
+ name: "skill",
58
+ type: "select",
59
+ message: "Skill a instalar",
60
+ choices: catalog.items.map((item) => ({
61
+ name: `${item.skillId}@${item.latestVersion}`,
62
+ value: item.skillId,
63
+ })),
64
+ });
65
+ positional = [selected];
66
+ }
67
+ if (action === "verify") action = "status";
68
+ await runSkillsCommand(context.api, action, positional, {
69
+ force: flags.force,
70
+ json: flags.json,
71
+ target,
72
+ });
73
+ }
74
+ }
@@ -0,0 +1,19 @@
1
+ import { Help } from "@oclif/core";
2
+
3
+ import { ProjectCommand } from "../baseCommands.js";
4
+
5
+ export default class Configure extends ProjectCommand {
6
+ static description =
7
+ "Configura projetos, clientes de agentes e skills de desenvolvimento";
8
+ static examples = [
9
+ "<%= config.bin %> configure codex",
10
+ "<%= config.bin %> configure claude",
11
+ "<%= config.bin %> configure skills list",
12
+ "<%= config.bin %> configure doctor codex",
13
+ ];
14
+
15
+ async run() {
16
+ await this.parse(Configure);
17
+ await new Help(this.config).showHelp(["configure"]);
18
+ }
19
+ }
@@ -0,0 +1,6 @@
1
+ import DemandTaskStatus from "./task-status.js";
2
+
3
+ export default class DemandCompleteTask extends DemandTaskStatus {
4
+ static description = "Conclui uma tarefa de uma melhoria";
5
+ static defaultStatus = "Concluído";
6
+ }
@@ -0,0 +1,16 @@
1
+ import { Args } from "@oclif/core";
2
+ import { connectionFlags, ReadCommand } from "../../domain/readCommand.js";
3
+ export default class DemandGet extends ReadCommand {
4
+ static description = "Obtém uma melhoria por ID ou código";
5
+ static args = { id: Args.string({ required: true }) };
6
+ static flags = connectionFlags;
7
+ async run() {
8
+ const { args, flags } = await this.parse(DemandGet);
9
+ const result = await this.read(
10
+ flags,
11
+ (api, c) => api.demand(args.id, { workspaceId: c.workspaceId }),
12
+ { resource: "demand", operation: "get", requireWorkspace: true },
13
+ );
14
+ this.emit(flags, result, (p) => JSON.stringify(p.request || p, null, 2));
15
+ }
16
+ }