oxe-cc 0.3.5 → 0.3.6
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/.cursor/commands/oxe-help.md +3 -9
- package/.cursor/commands/oxe-next.md +3 -9
- package/.cursor/commands/oxe-plan.md +5 -12
- package/.cursor/commands/oxe-review-pr.md +5 -12
- package/.cursor/commands/oxe-scan.md +5 -12
- package/.cursor/commands/oxe-spec.md +5 -12
- package/.cursor/commands/oxe-update.md +9 -0
- package/.cursor/commands/oxe-verify.md +5 -12
- package/.github/copilot-instructions.md +7 -5
- package/.github/prompts/oxe-help.prompt.md +1 -1
- package/.github/prompts/oxe-update.prompt.md +11 -0
- package/.github/workflows/ci.yml +20 -18
- package/AGENTS.md +5 -5
- package/README.md +167 -32
- package/assets/oxe-ciclo-por-trilha.png +0 -0
- package/bin/banner.txt +1 -1
- package/bin/lib/oxe-agent-install.cjs +87 -36
- package/bin/lib/oxe-npm-version.cjs +64 -0
- package/bin/lib/oxe-project-health.cjs +29 -1
- package/bin/oxe-cc.js +689 -121
- package/lib/sdk/index.cjs +1 -0
- package/oxe/templates/CONFIG.md +12 -0
- package/oxe/templates/DOCS_BROWNFIELD_LAYOUT.md +55 -0
- package/oxe/templates/SPEC.template.md +18 -0
- package/oxe/templates/WORKFLOW_AUTHORING.md +2 -0
- package/oxe/workflows/execute.md +1 -0
- package/oxe/workflows/help.md +13 -7
- package/oxe/workflows/plan.md +4 -0
- package/oxe/workflows/references/legacy-brownfield.md +136 -0
- package/oxe/workflows/scan.md +5 -3
- package/oxe/workflows/spec.md +2 -0
- package/oxe/workflows/update.md +33 -0
- package/oxe/workflows/verify.md +1 -0
- package/package.json +15 -4
package/bin/oxe-cc.js
CHANGED
|
@@ -17,6 +17,7 @@ const oxeHealth = require(path.join(__dirname, 'lib', 'oxe-project-health.cjs'))
|
|
|
17
17
|
const oxeAgentInstall = require(path.join(__dirname, 'lib', 'oxe-agent-install.cjs'));
|
|
18
18
|
const oxeWorkflows = require(path.join(__dirname, 'lib', 'oxe-workflows.cjs'));
|
|
19
19
|
const oxeInstallResolve = require(path.join(__dirname, 'lib', 'oxe-install-resolve.cjs'));
|
|
20
|
+
const oxeNpmVersion = require(path.join(__dirname, 'lib', 'oxe-npm-version.cjs'));
|
|
20
21
|
|
|
21
22
|
/** Merge markers for ~/.copilot/copilot-instructions.md (bloco OXE). */
|
|
22
23
|
const OXE_INST_BEGIN = '<!-- oxe-cc:install-begin -->';
|
|
@@ -36,7 +37,7 @@ const RULE = '━━━━━━━━━━━━━━━━━━━━━━
|
|
|
36
37
|
/** Plain banner if banner.txt is missing (keep in sync with bin/banner.txt style). */
|
|
37
38
|
const DEFAULT_BANNER = ` .============================================.
|
|
38
39
|
| OXE · spec-driven workflow CLI |
|
|
39
|
-
|
|
|
40
|
+
| multi-IDE · Cursor · Copilot · +CLIs |
|
|
40
41
|
'============================================'
|
|
41
42
|
v{version}
|
|
42
43
|
`;
|
|
@@ -127,13 +128,14 @@ function printSummaryAndNextSteps(c, { bullets, nextSteps, dryRun = false }) {
|
|
|
127
128
|
/**
|
|
128
129
|
* @param {InstallOpts} opts
|
|
129
130
|
* @param {boolean} fullLayout
|
|
130
|
-
* @param {string} cursorBase
|
|
131
|
-
* @param {string} copilotRoot
|
|
132
|
-
* @param {string} claudeBase
|
|
133
131
|
*/
|
|
134
|
-
function buildInstallSummary(opts, fullLayout
|
|
132
|
+
function buildInstallSummary(opts, fullLayout) {
|
|
135
133
|
const bullets = [];
|
|
136
134
|
const prefix = opts.dryRun ? '[simulação] ' : '';
|
|
135
|
+
const cursorBase = installCursorBase(opts);
|
|
136
|
+
const copilotCliHome = installCopilotCliHome(opts);
|
|
137
|
+
const claudeBase = installClaudeBase(opts);
|
|
138
|
+
const agentPaths = oxeAgentInstall.buildAgentInstallPaths(!opts.ideLocal, opts.dir);
|
|
137
139
|
|
|
138
140
|
if (opts.oxeOnly) {
|
|
139
141
|
bullets.push(`${prefix}Repositório: .oxe/workflows/ e .oxe/templates/`);
|
|
@@ -157,18 +159,31 @@ function buildInstallSummary(opts, fullLayout, cursorBase, copilotRoot, claudeBa
|
|
|
157
159
|
}
|
|
158
160
|
if (opts.copilot) {
|
|
159
161
|
bullets.push(
|
|
160
|
-
`${prefix}Copilot (VS Code): trecho OXE em ${displayPathForUser(
|
|
162
|
+
`${prefix}Copilot (VS Code): trecho OXE em ${displayPathForUser(copilotInstructionsPath(opts))} e prompts em ${displayPathForUser(copilotPromptsDirPath(opts))}`
|
|
161
163
|
);
|
|
162
164
|
}
|
|
163
165
|
if (opts.copilotCli && !opts.allAgents) {
|
|
164
166
|
bullets.push(
|
|
165
|
-
`${prefix}CLI: skills Copilot em ${displayPathForUser(path.join(
|
|
167
|
+
`${prefix}CLI: skills Copilot em ${displayPathForUser(path.join(copilotCliHome, 'skills'))} (/oxe, /oxe-scan, …); cópia legado em ${displayPathForUser(path.join(claudeBase, 'commands'))} e ${displayPathForUser(path.join(copilotCliHome, 'commands'))}`
|
|
166
168
|
);
|
|
167
169
|
}
|
|
168
170
|
if (opts.allAgents) {
|
|
171
|
+
const oc = agentPaths.opencodeCommandDirs.map((d) => displayPathForUser(d)).join(' + ');
|
|
169
172
|
bullets.push(
|
|
170
|
-
`${prefix}Multi-agente
|
|
173
|
+
`${prefix}Multi-agente: OpenCode ${oc}; Gemini ${displayPathForUser(agentPaths.geminiCommandsBase)}; Codex ${displayPathForUser(agentPaths.codexAgentsSkillsRoot)} + ${displayPathForUser(agentPaths.codexPromptsDir)}; Windsurf ${displayPathForUser(agentPaths.windsurfWorkflowsDir)}; Antigravity ${displayPathForUser(agentPaths.antigravitySkillsRoot)}; + Claude/Copilot CLI como em --copilot-cli`
|
|
171
174
|
);
|
|
175
|
+
} else if (anyGranularAgent(opts)) {
|
|
176
|
+
const parts = [];
|
|
177
|
+
if (opts.agentOpenCode) parts.push(`OpenCode ${agentPaths.opencodeCommandDirs.map((d) => displayPathForUser(d)).join(' + ')}`);
|
|
178
|
+
if (opts.agentGemini) parts.push(`Gemini ${displayPathForUser(agentPaths.geminiCommandsBase)}`);
|
|
179
|
+
if (opts.agentCodex) {
|
|
180
|
+
parts.push(
|
|
181
|
+
`Codex ${displayPathForUser(agentPaths.codexAgentsSkillsRoot)} + ${displayPathForUser(agentPaths.codexPromptsDir)}`
|
|
182
|
+
);
|
|
183
|
+
}
|
|
184
|
+
if (opts.agentWindsurf) parts.push(`Windsurf ${displayPathForUser(agentPaths.windsurfWorkflowsDir)}`);
|
|
185
|
+
if (opts.agentAntigravity) parts.push(`Antigravity ${displayPathForUser(agentPaths.antigravitySkillsRoot)}`);
|
|
186
|
+
if (parts.length) bullets.push(`${prefix}Agentes (seleção): ${parts.join('; ')}`);
|
|
172
187
|
}
|
|
173
188
|
|
|
174
189
|
const nextSteps = [];
|
|
@@ -184,7 +199,7 @@ function buildInstallSummary(opts, fullLayout, cursorBase, copilotRoot, claudeBa
|
|
|
184
199
|
const agentHint = [];
|
|
185
200
|
if (opts.cursor) agentHint.push('Cursor');
|
|
186
201
|
if (opts.copilot) agentHint.push('Copilot no VS Code');
|
|
187
|
-
if (opts.copilotCli || opts.allAgents) agentHint.push('CLIs / multi-agente');
|
|
202
|
+
if (opts.copilotCli || opts.allAgents || anyGranularAgent(opts)) agentHint.push('CLIs / multi-agente');
|
|
188
203
|
if (agentHint.length) {
|
|
189
204
|
nextSteps.push({
|
|
190
205
|
desc: `Mapear o código no agente (${agentHint.join(', ')}):`,
|
|
@@ -192,7 +207,7 @@ function buildInstallSummary(opts, fullLayout, cursorBase, copilotRoot, claudeBa
|
|
|
192
207
|
});
|
|
193
208
|
} else if (opts.oxeOnly) {
|
|
194
209
|
nextSteps.push({
|
|
195
|
-
desc: 'Para ativar
|
|
210
|
+
desc: 'Para ativar integrações IDE/CLI neste repo, instale de novo sem --oxe-only:',
|
|
196
211
|
cmd: 'npx oxe-cc@latest',
|
|
197
212
|
});
|
|
198
213
|
} else {
|
|
@@ -208,7 +223,7 @@ function buildInstallSummary(opts, fullLayout, cursorBase, copilotRoot, claudeBa
|
|
|
208
223
|
cmd: '/skills list',
|
|
209
224
|
});
|
|
210
225
|
}
|
|
211
|
-
if (opts.allAgents) {
|
|
226
|
+
if (opts.allAgents || opts.agentGemini) {
|
|
212
227
|
nextSteps.push({
|
|
213
228
|
desc: 'No Gemini CLI: recarregar comandos personalizados (/oxe, /oxe:scan, …):',
|
|
214
229
|
cmd: '/commands reload',
|
|
@@ -239,6 +254,11 @@ function buildUninstallFooter(u) {
|
|
|
239
254
|
`${p}${rm} extensões multi-agente marcadas oxe-cc (OpenCode, Gemini TOML, Windsurf workflows, Codex prompts/skills, Antigravity), se existirem.`
|
|
240
255
|
);
|
|
241
256
|
}
|
|
257
|
+
if (u.ideLocal) {
|
|
258
|
+
bullets.push(
|
|
259
|
+
`${p}${rm} integrações OXE no repositório (.cursor, .github, .claude, .copilot, .opencode, … conforme flags).`
|
|
260
|
+
);
|
|
261
|
+
}
|
|
242
262
|
if (!u.noProject) {
|
|
243
263
|
bullets.push(
|
|
244
264
|
`${p}${u.dryRun ? 'Seriam removidas' : 'Removidas'} no repositório: .oxe/workflows, .oxe/templates, oxe/ e commands/oxe (o que existir).`
|
|
@@ -253,7 +273,17 @@ function buildUninstallFooter(u) {
|
|
|
253
273
|
return { bullets, nextSteps, dryRun: u.dryRun };
|
|
254
274
|
}
|
|
255
275
|
|
|
256
|
-
/** @typedef {{ help: boolean, version: boolean, cursor: boolean, copilot: boolean, copilotCli: boolean, allAgents: boolean, vscode: boolean, commands: boolean, agents: boolean, force: boolean, dryRun: boolean, dir: string, all: boolean, noInitOxe: boolean, oxeOnly: boolean, globalCli: boolean, noGlobalCli: boolean, installAssetsGlobal: boolean, explicitScope: boolean, integrationsUnset: boolean, explicitConfigDir: string | null, parseError: boolean, unknownFlag: string, conflictFlags: string, ignoreInstallConfig: boolean }} InstallOpts */
|
|
276
|
+
/** @typedef {{ help: boolean, version: boolean, cursor: boolean, copilot: boolean, copilotCli: boolean, allAgents: boolean, agentOpenCode: boolean, agentGemini: boolean, agentCodex: boolean, agentWindsurf: boolean, agentAntigravity: boolean, vscode: boolean, commands: boolean, agents: boolean, force: boolean, dryRun: boolean, dir: string, all: boolean, noInitOxe: boolean, oxeOnly: boolean, globalCli: boolean, noGlobalCli: boolean, installAssetsGlobal: boolean, explicitScope: boolean, integrationsUnset: boolean, ideLocal: boolean, explicitIdeScope: boolean, explicitConfigDir: string | null, parseError: boolean, unknownFlag: string, conflictFlags: string, ignoreInstallConfig: boolean }} InstallOpts */
|
|
277
|
+
|
|
278
|
+
/** @param {InstallOpts} o */
|
|
279
|
+
function anyGranularAgent(o) {
|
|
280
|
+
return !!(o.agentOpenCode || o.agentGemini || o.agentCodex || o.agentWindsurf || o.agentAntigravity);
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
/** @param {InstallOpts} o */
|
|
284
|
+
function anyIdeIntegration(o) {
|
|
285
|
+
return !!(o.cursor || o.copilot || o.copilotCli || o.allAgents || anyGranularAgent(o));
|
|
286
|
+
}
|
|
257
287
|
|
|
258
288
|
/**
|
|
259
289
|
* @param {string[]} argv
|
|
@@ -268,6 +298,11 @@ function parseInstallArgs(argv) {
|
|
|
268
298
|
copilot: false,
|
|
269
299
|
copilotCli: false,
|
|
270
300
|
allAgents: false,
|
|
301
|
+
agentOpenCode: false,
|
|
302
|
+
agentGemini: false,
|
|
303
|
+
agentCodex: false,
|
|
304
|
+
agentWindsurf: false,
|
|
305
|
+
agentAntigravity: false,
|
|
271
306
|
vscode: false,
|
|
272
307
|
commands: true,
|
|
273
308
|
agents: true,
|
|
@@ -282,11 +317,15 @@ function parseInstallArgs(argv) {
|
|
|
282
317
|
installAssetsGlobal: false,
|
|
283
318
|
explicitScope: false,
|
|
284
319
|
integrationsUnset: false,
|
|
320
|
+
ideLocal: false,
|
|
321
|
+
explicitIdeScope: false,
|
|
285
322
|
explicitConfigDir: null,
|
|
286
323
|
parseError: false,
|
|
287
324
|
unknownFlag: '',
|
|
288
325
|
conflictFlags: '',
|
|
289
326
|
ignoreInstallConfig: false,
|
|
327
|
+
/** Saída JSON em `status` (CI / agentes). */
|
|
328
|
+
jsonOutput: false,
|
|
290
329
|
restPositional: [],
|
|
291
330
|
};
|
|
292
331
|
for (let i = 0; i < argv.length; i++) {
|
|
@@ -302,10 +341,21 @@ function parseInstallArgs(argv) {
|
|
|
302
341
|
} else if (a === '--local') {
|
|
303
342
|
out.installAssetsGlobal = false;
|
|
304
343
|
out.explicitScope = true;
|
|
344
|
+
} else if (a === '--ide-global') {
|
|
345
|
+
out.ideLocal = false;
|
|
346
|
+
out.explicitIdeScope = true;
|
|
347
|
+
} else if (a === '--ide-local') {
|
|
348
|
+
out.ideLocal = true;
|
|
349
|
+
out.explicitIdeScope = true;
|
|
305
350
|
} else if (a === '--cursor') out.cursor = true;
|
|
306
351
|
else if (a === '--copilot') out.copilot = true;
|
|
307
352
|
else if (a === '--copilot-cli') out.copilotCli = true;
|
|
308
353
|
else if (a === '--all-agents') out.allAgents = true;
|
|
354
|
+
else if (a === '--opencode') out.agentOpenCode = true;
|
|
355
|
+
else if (a === '--gemini') out.agentGemini = true;
|
|
356
|
+
else if (a === '--codex') out.agentCodex = true;
|
|
357
|
+
else if (a === '--windsurf') out.agentWindsurf = true;
|
|
358
|
+
else if (a === '--antigravity') out.agentAntigravity = true;
|
|
309
359
|
else if (a === '--vscode') out.vscode = true;
|
|
310
360
|
else if (a === '--no-commands') out.commands = false;
|
|
311
361
|
else if (a === '--no-agents') out.agents = false;
|
|
@@ -318,7 +368,8 @@ function parseInstallArgs(argv) {
|
|
|
318
368
|
else if (a === '--no-global-cli' || a === '-l') out.noGlobalCli = true;
|
|
319
369
|
else if (a === '--dir' && argv[i + 1]) {
|
|
320
370
|
out.dir = path.resolve(argv[++i]);
|
|
321
|
-
} else if (
|
|
371
|
+
} else if (a === '--json') out.jsonOutput = true;
|
|
372
|
+
else if (!a.startsWith('-')) out.restPositional.push(a);
|
|
322
373
|
else {
|
|
323
374
|
out.parseError = true;
|
|
324
375
|
out.unknownFlag = a;
|
|
@@ -329,7 +380,13 @@ function parseInstallArgs(argv) {
|
|
|
329
380
|
out.conflictFlags = 'Não use --global-cli (-g) e --no-global-cli (-l) ao mesmo tempo';
|
|
330
381
|
}
|
|
331
382
|
if (!out.conflictFlags && argv.includes('--global') && argv.includes('--local')) {
|
|
332
|
-
out.conflictFlags = 'Não use --global e --local ao mesmo tempo';
|
|
383
|
+
out.conflictFlags = 'Não use --global e --local ao mesmo tempo (são o layout do repositório: oxe/ vs só .oxe/)';
|
|
384
|
+
}
|
|
385
|
+
if (!out.conflictFlags && argv.includes('--ide-global') && argv.includes('--ide-local')) {
|
|
386
|
+
out.conflictFlags = 'Não use --ide-global e --ide-local ao mesmo tempo';
|
|
387
|
+
}
|
|
388
|
+
if (!out.conflictFlags && out.ideLocal && out.explicitConfigDir) {
|
|
389
|
+
out.conflictFlags = '--ide-local não combina com --config-dir';
|
|
333
390
|
}
|
|
334
391
|
if (!out.conflictFlags && out.explicitConfigDir) {
|
|
335
392
|
if (out.oxeOnly || out.allAgents) {
|
|
@@ -345,12 +402,22 @@ function parseInstallArgs(argv) {
|
|
|
345
402
|
if (out.allAgents && !out.oxeOnly) {
|
|
346
403
|
out.cursor = true;
|
|
347
404
|
out.copilot = true;
|
|
405
|
+
out.agentOpenCode = true;
|
|
406
|
+
out.agentGemini = true;
|
|
407
|
+
out.agentCodex = true;
|
|
408
|
+
out.agentWindsurf = true;
|
|
409
|
+
out.agentAntigravity = true;
|
|
348
410
|
}
|
|
349
411
|
if (out.oxeOnly) {
|
|
350
412
|
out.cursor = false;
|
|
351
413
|
out.copilot = false;
|
|
352
414
|
out.copilotCli = false;
|
|
353
415
|
out.allAgents = false;
|
|
416
|
+
out.agentOpenCode = false;
|
|
417
|
+
out.agentGemini = false;
|
|
418
|
+
out.agentCodex = false;
|
|
419
|
+
out.agentWindsurf = false;
|
|
420
|
+
out.agentAntigravity = false;
|
|
354
421
|
out.vscode = false;
|
|
355
422
|
out.commands = false;
|
|
356
423
|
out.agents = false;
|
|
@@ -359,7 +426,7 @@ function parseInstallArgs(argv) {
|
|
|
359
426
|
out.cursor = true;
|
|
360
427
|
out.copilot = true;
|
|
361
428
|
out.integrationsUnset = false;
|
|
362
|
-
} else if (!out.cursor && !out.copilot && !out.copilotCli && !out.vscode) {
|
|
429
|
+
} else if (!out.cursor && !out.copilot && !out.copilotCli && !out.vscode && !anyGranularAgent(out)) {
|
|
363
430
|
out.integrationsUnset = true;
|
|
364
431
|
} else {
|
|
365
432
|
out.integrationsUnset = false;
|
|
@@ -453,6 +520,51 @@ function claudeUserDir(opts) {
|
|
|
453
520
|
return path.join(os.homedir(), '.claude');
|
|
454
521
|
}
|
|
455
522
|
|
|
523
|
+
/** Base Cursor: ~/.cursor ou <projeto>/.cursor (com --ide-local). */
|
|
524
|
+
function installCursorBase(opts) {
|
|
525
|
+
const target = path.resolve(opts.dir);
|
|
526
|
+
if (opts.ideLocal && opts.cursor) {
|
|
527
|
+
return path.join(target, '.cursor');
|
|
528
|
+
}
|
|
529
|
+
return cursorUserDir(opts);
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
/** Home Copilot CLI (skills, commands): ~/.copilot ou ./.copilot. */
|
|
533
|
+
function installCopilotCliHome(opts) {
|
|
534
|
+
const target = path.resolve(opts.dir);
|
|
535
|
+
if (opts.ideLocal && (opts.copilotCli || opts.allAgents)) {
|
|
536
|
+
return path.join(target, '.copilot');
|
|
537
|
+
}
|
|
538
|
+
return copilotUserDir(opts);
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
/** Pasta .claude (comandos) global ou no projeto. */
|
|
542
|
+
function installClaudeBase(opts) {
|
|
543
|
+
const target = path.resolve(opts.dir);
|
|
544
|
+
if (opts.ideLocal && (opts.copilotCli || opts.allAgents)) {
|
|
545
|
+
return path.join(target, '.claude');
|
|
546
|
+
}
|
|
547
|
+
return claudeUserDir(opts);
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
/** Ficheiro copilot-instructions (VS Code): ~/.copilot ou .github no repo. */
|
|
551
|
+
function copilotInstructionsPath(opts) {
|
|
552
|
+
const target = path.resolve(opts.dir);
|
|
553
|
+
if (opts.ideLocal && opts.copilot) {
|
|
554
|
+
return path.join(target, '.github', 'copilot-instructions.md');
|
|
555
|
+
}
|
|
556
|
+
return path.join(copilotUserDir(opts), 'copilot-instructions.md');
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
/** Pasta de prompts Copilot VS Code. */
|
|
560
|
+
function copilotPromptsDirPath(opts) {
|
|
561
|
+
const target = path.resolve(opts.dir);
|
|
562
|
+
if (opts.ideLocal && opts.copilot) {
|
|
563
|
+
return path.join(target, '.github', 'prompts');
|
|
564
|
+
}
|
|
565
|
+
return path.join(copilotUserDir(opts), 'prompts');
|
|
566
|
+
}
|
|
567
|
+
|
|
456
568
|
/** Layout “clássico”: pasta `oxe/` na raiz do repo. Caso contrário: só `.oxe/` (workflows em `.oxe/workflows`). */
|
|
457
569
|
function useFullRepoLayout(opts) {
|
|
458
570
|
return opts.installAssetsGlobal === true;
|
|
@@ -556,37 +668,118 @@ function applyInstallFromOxeConfig(opts, targetDir) {
|
|
|
556
668
|
}
|
|
557
669
|
}
|
|
558
670
|
|
|
559
|
-
/**
|
|
560
|
-
|
|
671
|
+
/**
|
|
672
|
+
* Mapa número da lista → chaves de runtime (estilo GSD).
|
|
673
|
+
* @param {string} input
|
|
674
|
+
* @returns {string[]}
|
|
675
|
+
*/
|
|
676
|
+
function parseRuntimeMultiselect(input) {
|
|
677
|
+
const runtimeMap = {
|
|
678
|
+
'1': 'claude',
|
|
679
|
+
'2': 'opencode',
|
|
680
|
+
'3': 'gemini',
|
|
681
|
+
'4': 'codex',
|
|
682
|
+
'5': 'copilot',
|
|
683
|
+
'6': 'antigravity',
|
|
684
|
+
'7': 'cursor',
|
|
685
|
+
'8': 'windsurf',
|
|
686
|
+
};
|
|
687
|
+
const trimmed = (input || '7').trim();
|
|
688
|
+
if (trimmed === '9') return ['all'];
|
|
689
|
+
const choices = trimmed.split(/[\s,]+/).filter(Boolean);
|
|
690
|
+
const selected = [];
|
|
691
|
+
for (const c of choices) {
|
|
692
|
+
const k = runtimeMap[c];
|
|
693
|
+
if (k && !selected.includes(k)) selected.push(k);
|
|
694
|
+
}
|
|
695
|
+
return selected.length > 0 ? selected : ['cursor', 'copilot'];
|
|
696
|
+
}
|
|
697
|
+
|
|
698
|
+
/**
|
|
699
|
+
* @param {InstallOpts} opts
|
|
700
|
+
* @param {string[]} keys
|
|
701
|
+
*/
|
|
702
|
+
function applyRuntimeKeysToOpts(opts, keys) {
|
|
703
|
+
if (keys.includes('all')) {
|
|
704
|
+
opts.cursor = true;
|
|
705
|
+
opts.copilot = true;
|
|
706
|
+
opts.copilotCli = true;
|
|
707
|
+
opts.allAgents = true;
|
|
708
|
+
opts.agentOpenCode = true;
|
|
709
|
+
opts.agentGemini = true;
|
|
710
|
+
opts.agentCodex = true;
|
|
711
|
+
opts.agentWindsurf = true;
|
|
712
|
+
opts.agentAntigravity = true;
|
|
713
|
+
opts.commands = true;
|
|
714
|
+
opts.agents = true;
|
|
715
|
+
return;
|
|
716
|
+
}
|
|
717
|
+
opts.cursor = keys.includes('cursor');
|
|
718
|
+
opts.copilot = keys.includes('copilot');
|
|
719
|
+
opts.copilotCli = keys.includes('claude');
|
|
720
|
+
opts.agentOpenCode = keys.includes('opencode');
|
|
721
|
+
opts.agentGemini = keys.includes('gemini');
|
|
722
|
+
opts.agentCodex = keys.includes('codex');
|
|
723
|
+
opts.agentWindsurf = keys.includes('windsurf');
|
|
724
|
+
opts.agentAntigravity = keys.includes('antigravity');
|
|
725
|
+
opts.allAgents =
|
|
726
|
+
opts.agentOpenCode &&
|
|
727
|
+
opts.agentGemini &&
|
|
728
|
+
opts.agentCodex &&
|
|
729
|
+
opts.agentWindsurf &&
|
|
730
|
+
opts.agentAntigravity;
|
|
731
|
+
const coreOnly = keys.length === 0;
|
|
732
|
+
if (coreOnly) {
|
|
733
|
+
opts.commands = true;
|
|
734
|
+
opts.agents = true;
|
|
735
|
+
return;
|
|
736
|
+
}
|
|
737
|
+
opts.commands = true;
|
|
738
|
+
opts.agents = true;
|
|
739
|
+
}
|
|
740
|
+
|
|
741
|
+
/** Multiselect de ambientes (1–8, 9=todos), em português. */
|
|
742
|
+
async function promptRuntimeSelection() {
|
|
743
|
+
const rl = readlinePromises.createInterface({ input: process.stdin, output: process.stdout });
|
|
744
|
+
const c = useAnsiColors();
|
|
745
|
+
try {
|
|
746
|
+
const home = displayPathForUser(os.homedir());
|
|
747
|
+
console.log(` ${c ? yellow : ''}Para quais ambientes deseja instalar o OXE?${c ? reset : ''}
|
|
748
|
+
|
|
749
|
+
${c ? cyan : ''}1${c ? reset : ''}) Claude Code ${c ? dim : ''}(${home}/.claude, CLI Copilot)${c ? reset : ''}
|
|
750
|
+
${c ? cyan : ''}2${c ? reset : ''}) OpenCode ${c ? dim : ''}(${home}/.config/opencode ou ${home}/.opencode)${c ? reset : ''}
|
|
751
|
+
${c ? cyan : ''}3${c ? reset : ''}) Gemini ${c ? dim : ''}(${home}/.gemini)${c ? reset : ''}
|
|
752
|
+
${c ? cyan : ''}4${c ? reset : ''}) Codex ${c ? dim : ''}(${home}/.codex, ${home}/.agents)${c ? reset : ''}
|
|
753
|
+
${c ? cyan : ''}5${c ? reset : ''}) Copilot ${c ? dim : ''}(${home}/.copilot — VS Code / instruções)${c ? reset : ''}
|
|
754
|
+
${c ? cyan : ''}6${c ? reset : ''}) Antigravity ${c ? dim : ''}(${home}/.gemini/antigravity)${c ? reset : ''}
|
|
755
|
+
${c ? cyan : ''}7${c ? reset : ''}) Cursor ${c ? dim : ''}(${home}/.cursor)${c ? reset : ''}
|
|
756
|
+
${c ? cyan : ''}8${c ? reset : ''}) Windsurf ${c ? dim : ''}(${home}/.codeium/windsurf)${c ? reset : ''}
|
|
757
|
+
${c ? cyan : ''}9${c ? reset : ''}) Todos os ambientes acima
|
|
758
|
+
|
|
759
|
+
${c ? dim : ''}Vários: 1,4,7 ou 1 4 7 (Enter = 7 Cursor + 5 Copilot, recomendado)${c ? reset : ''}
|
|
760
|
+
`);
|
|
761
|
+
const answer = await rl.question(` ${c ? dim : ''}Escolha${c ? reset : ''} ${c ? dim : ''}[7 5]${c ? reset : ''}: `);
|
|
762
|
+
return parseRuntimeMultiselect(answer || '7 5');
|
|
763
|
+
} finally {
|
|
764
|
+
rl.close();
|
|
765
|
+
}
|
|
766
|
+
}
|
|
767
|
+
|
|
768
|
+
/** Global vs local (pastas do projeto), estilo GSD. */
|
|
769
|
+
async function promptIdeLocation(opts) {
|
|
561
770
|
const rl = readlinePromises.createInterface({ input: process.stdin, output: process.stdout });
|
|
562
771
|
const c = useAnsiColors();
|
|
772
|
+
const target = path.resolve(opts.dir);
|
|
563
773
|
try {
|
|
564
|
-
console.log(` ${c ? yellow : ''}Onde
|
|
565
|
-
|
|
566
|
-
${c ? cyan : ''}
|
|
567
|
-
${c ? cyan : ''}
|
|
568
|
-
${c ? cyan : ''}4${c ? reset : ''}) ${c ? dim : ''}Cursor + Copilot + CLIs${c ? reset : ''} ${c ? dim : ''}(~/.claude, ~/.copilot, skills Copilot)${c ? reset : ''}
|
|
569
|
-
${c ? cyan : ''}5${c ? reset : ''}) ${c ? dim : ''}Só o núcleo${c ? reset : ''} ${c ? dim : ''}(apenas .oxe/ com workflows, sem IDE)${c ? reset : ''}
|
|
570
|
-
${c ? cyan : ''}6${c ? reset : ''}) ${c ? dim : ''}Tudo + multi-agente${c ? reset : ''} ${c ? dim : ''}— opção 4 + OpenCode, Gemini, Codex, Windsurf, Antigravity${c ? reset : ''}
|
|
774
|
+
console.log(` ${c ? yellow : ''}Onde instalar as integrações de IDE?${c ? reset : ''}
|
|
775
|
+
|
|
776
|
+
${c ? cyan : ''}1${c ? reset : ''}) Global ${c ? dim : ''}(~/.cursor, ~/.copilot, ~/.claude, … — disponível em todos os projetos)${c ? reset : ''}
|
|
777
|
+
${c ? cyan : ''}2${c ? reset : ''}) Local ${c ? dim : ''}(${displayPathForUser(path.join(target, '.cursor'))}, .github, .claude, .copilot, … — só este repositório)${c ? reset : ''}
|
|
571
778
|
`);
|
|
572
|
-
const answer = await rl.question(` ${c ?
|
|
779
|
+
const answer = await rl.question(` ${c ? dim : ''}Escolha${c ? reset : ''} ${c ? dim : ''}[1]${c ? reset : ''}: `);
|
|
573
780
|
const choice = (answer || '1').trim();
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
}
|
|
577
|
-
if (choice === '2') {
|
|
578
|
-
return { cursor: true, copilot: false, copilotCli: false, allAgents: false, vscode: false, commands: true, agents: true };
|
|
579
|
-
}
|
|
580
|
-
if (choice === '3') {
|
|
581
|
-
return { cursor: false, copilot: true, copilotCli: false, allAgents: false, vscode: false, commands: true, agents: true };
|
|
582
|
-
}
|
|
583
|
-
if (choice === '4') {
|
|
584
|
-
return { cursor: true, copilot: true, copilotCli: true, allAgents: false, vscode: false, commands: true, agents: true };
|
|
585
|
-
}
|
|
586
|
-
if (choice === '6') {
|
|
587
|
-
return { cursor: true, copilot: true, copilotCli: true, allAgents: true, vscode: false, commands: true, agents: true };
|
|
588
|
-
}
|
|
589
|
-
return { cursor: true, copilot: true, copilotCli: false, allAgents: false, vscode: false, commands: true, agents: true };
|
|
781
|
+
opts.ideLocal = choice === '2';
|
|
782
|
+
opts.explicitIdeScope = true;
|
|
590
783
|
} finally {
|
|
591
784
|
rl.close();
|
|
592
785
|
}
|
|
@@ -594,18 +787,21 @@ async function promptIntegrationProfile() {
|
|
|
594
787
|
|
|
595
788
|
/** @param {InstallOpts} opts */
|
|
596
789
|
async function promptInstallScope(opts) {
|
|
597
|
-
const hasIde = opts
|
|
790
|
+
const hasIde = anyIdeIntegration(opts);
|
|
598
791
|
if (!hasIde) return;
|
|
599
792
|
const rl = readlinePromises.createInterface({ input: process.stdin, output: process.stdout });
|
|
600
793
|
const c = useAnsiColors();
|
|
601
794
|
try {
|
|
602
|
-
|
|
603
|
-
|
|
795
|
+
const ideHint = opts.ideLocal
|
|
796
|
+
? `${c ? dim : ''}Integrações IDE ficam em pastas dentro deste repo (${c ? cyan : ''}.cursor${c ? dim : ''}, ${c ? cyan : ''}.github${c ? dim : ''}, …).${c ? reset : ''}`
|
|
797
|
+
: `${c ? dim : ''}Comandos Cursor/Copilot/CLI ficam no seu utilizador (${c ? cyan : ''}~/.cursor${c ? dim : ''}, …).${c ? reset : ''}`;
|
|
798
|
+
console.log(` ${c ? yellow : ''}Como organizar os ficheiros OXE na raiz do repositório?${c ? reset : ''}
|
|
799
|
+
${ideHint}
|
|
604
800
|
|
|
605
801
|
${c ? cyan : ''}1${c ? reset : ''}) ${c ? dim : ''}Clássico${c ? reset : ''} — ${c ? dim : ''}pasta ${c ? cyan : ''}oxe/${c ? dim : ''} na raiz + ${c ? cyan : ''}.oxe/${c ? dim : ''} (e, se aplicável, ${c ? cyan : ''}commands/oxe${c ? dim : ''}, ${c ? cyan : ''}AGENTS.md${c ? dim : ''})${c ? reset : ''}
|
|
606
802
|
${c ? cyan : ''}2${c ? reset : ''}) ${c ? dim : ''}Só ${c ? cyan : ''}.oxe/${c ? reset : ''} ${c ? dim : ''}— workflows em ${c ? cyan : ''}.oxe/workflows/${c ? dim : ''}; sem ${c ? cyan : ''}oxe/${c ? dim : ''} na raiz${c ? reset : ''}
|
|
607
803
|
`);
|
|
608
|
-
const answer = await rl.question(` ${c ?
|
|
804
|
+
const answer = await rl.question(` ${c ? dim : ''}Escolha${c ? reset : ''} ${c ? dim : ''}[1]${c ? reset : ''}: `);
|
|
609
805
|
const choice = (answer || '1').trim();
|
|
610
806
|
opts.installAssetsGlobal = choice !== '2';
|
|
611
807
|
} finally {
|
|
@@ -624,9 +820,14 @@ async function resolveInteractiveInstall(opts) {
|
|
|
624
820
|
opts.cursor = true;
|
|
625
821
|
opts.copilot = true;
|
|
626
822
|
opts.allAgents = false;
|
|
823
|
+
opts.agentOpenCode = false;
|
|
824
|
+
opts.agentGemini = false;
|
|
825
|
+
opts.agentCodex = false;
|
|
826
|
+
opts.agentWindsurf = false;
|
|
827
|
+
opts.agentAntigravity = false;
|
|
627
828
|
opts.integrationsUnset = false;
|
|
628
829
|
}
|
|
629
|
-
if (!opts.explicitScope && (opts
|
|
830
|
+
if (!opts.explicitScope && anyIdeIntegration(opts)) {
|
|
630
831
|
opts.installAssetsGlobal = false;
|
|
631
832
|
}
|
|
632
833
|
return;
|
|
@@ -636,8 +837,22 @@ async function resolveInteractiveInstall(opts) {
|
|
|
636
837
|
|
|
637
838
|
if (opts.integrationsUnset) {
|
|
638
839
|
if (can) {
|
|
639
|
-
const
|
|
640
|
-
|
|
840
|
+
const keys = await promptRuntimeSelection();
|
|
841
|
+
if (keys.length === 0) {
|
|
842
|
+
opts.cursor = false;
|
|
843
|
+
opts.copilot = false;
|
|
844
|
+
opts.copilotCli = false;
|
|
845
|
+
opts.allAgents = false;
|
|
846
|
+
opts.agentOpenCode = false;
|
|
847
|
+
opts.agentGemini = false;
|
|
848
|
+
opts.agentCodex = false;
|
|
849
|
+
opts.agentWindsurf = false;
|
|
850
|
+
opts.agentAntigravity = false;
|
|
851
|
+
opts.commands = false;
|
|
852
|
+
opts.agents = false;
|
|
853
|
+
} else {
|
|
854
|
+
applyRuntimeKeysToOpts(opts, keys);
|
|
855
|
+
}
|
|
641
856
|
opts.integrationsUnset = false;
|
|
642
857
|
} else {
|
|
643
858
|
opts.cursor = true;
|
|
@@ -646,13 +861,23 @@ async function resolveInteractiveInstall(opts) {
|
|
|
646
861
|
opts.integrationsUnset = false;
|
|
647
862
|
const c = useAnsiColors();
|
|
648
863
|
console.log(
|
|
649
|
-
`\n ${c ? yellow : ''}Terminal não interativo${c ? reset : ''} — layout mínimo: só ${c ? cyan : ''}.oxe/${c ? reset : ''}; integrações em ~/.cursor e ~/.copilot. Para ${c ? cyan : ''}oxe/${c ? reset : ''} na raiz use ${c ? cyan : ''}--global${c ? reset : ''}.
|
|
864
|
+
`\n ${c ? yellow : ''}Terminal não interativo${c ? reset : ''} — layout mínimo: só ${c ? cyan : ''}.oxe/${c ? reset : ''}; integrações em ~/.cursor e ~/.copilot. Para ${c ? cyan : ''}oxe/${c ? reset : ''} na raiz use ${c ? cyan : ''}--global${c ? reset : ''} (layout do repo). IDE no projeto: ${c ? cyan : ''}--ide-local${c ? reset : ''}. Flags: ${c ? cyan : ''}--cursor${c ? reset : ''}, ${c ? cyan : ''}--copilot${c ? reset : ''}, ${c ? cyan : ''}--all-agents${c ? reset : ''}, ${c ? cyan : ''}--opencode${c ? reset : ''}, … ${c ? cyan : ''}--oxe-only${c ? reset : ''}, ${c ? cyan : ''}OXE_NO_PROMPT=1${c ? reset : ''}.\n`
|
|
865
|
+
);
|
|
866
|
+
}
|
|
867
|
+
}
|
|
868
|
+
|
|
869
|
+
if (anyIdeIntegration(opts) && !opts.explicitIdeScope) {
|
|
870
|
+
if (can) await promptIdeLocation(opts);
|
|
871
|
+
else {
|
|
872
|
+
opts.ideLocal = false;
|
|
873
|
+
const c = useAnsiColors();
|
|
874
|
+
console.log(
|
|
875
|
+
`\n ${c ? yellow : ''}Terminal não interativo${c ? reset : ''} — integrações IDE em pastas globais (~/.cursor, …). Para instalar só neste repo: ${c ? cyan : ''}--ide-local${c ? reset : ''}.\n`
|
|
650
876
|
);
|
|
651
877
|
}
|
|
652
878
|
}
|
|
653
879
|
|
|
654
|
-
|
|
655
|
-
if (hasIde && !opts.explicitScope) {
|
|
880
|
+
if (anyIdeIntegration(opts) && !opts.explicitScope) {
|
|
656
881
|
if (can) await promptInstallScope(opts);
|
|
657
882
|
else {
|
|
658
883
|
opts.installAssetsGlobal = false;
|
|
@@ -735,6 +960,49 @@ function copyDir(srcDir, destDir, opts, pathRewriteNested = false) {
|
|
|
735
960
|
}
|
|
736
961
|
}
|
|
737
962
|
|
|
963
|
+
/**
|
|
964
|
+
* @param {string} content
|
|
965
|
+
* @returns {boolean}
|
|
966
|
+
*/
|
|
967
|
+
function gitignoreAlreadyIgnoresOxe(content) {
|
|
968
|
+
for (const line of content.split(/\r?\n/)) {
|
|
969
|
+
const t = line.replace(/#.*$/, '').trim();
|
|
970
|
+
if (!t || t.startsWith('!')) continue;
|
|
971
|
+
if (t === '.oxe' || t === '.oxe/') return true;
|
|
972
|
+
if (t === '.oxe/*' || t === '.oxe/**') return true;
|
|
973
|
+
}
|
|
974
|
+
return false;
|
|
975
|
+
}
|
|
976
|
+
|
|
977
|
+
/**
|
|
978
|
+
* Garante que o `.gitignore` na raiz do projeto inclui `.oxe/` (pasta de estado OXE, não versionar).
|
|
979
|
+
* @param {string} projectRoot
|
|
980
|
+
* @param {{ dryRun?: boolean }} opts
|
|
981
|
+
*/
|
|
982
|
+
function ensureGitignoreIgnoresOxeDir(projectRoot, opts = {}) {
|
|
983
|
+
const dryRun = opts.dryRun === true;
|
|
984
|
+
const giPath = path.join(projectRoot, '.gitignore');
|
|
985
|
+
const rel = path.relative(projectRoot, giPath) || '.gitignore';
|
|
986
|
+
const block =
|
|
987
|
+
'\n# OXE (oxe-cc) — pasta de estado local; não versionar\n' + '.oxe/\n';
|
|
988
|
+
|
|
989
|
+
if (dryRun) {
|
|
990
|
+
console.log(`${dim}gitignore${reset} garantir .oxe/ em ${rel}`);
|
|
991
|
+
return;
|
|
992
|
+
}
|
|
993
|
+
|
|
994
|
+
if (fs.existsSync(giPath)) {
|
|
995
|
+
const raw = fs.readFileSync(giPath, 'utf8');
|
|
996
|
+
if (gitignoreAlreadyIgnoresOxe(raw)) return;
|
|
997
|
+
let out = raw;
|
|
998
|
+
if (out.length > 0 && !/\n$/.test(out)) out += '\n';
|
|
999
|
+
fs.writeFileSync(giPath, out + block, 'utf8');
|
|
1000
|
+
} else {
|
|
1001
|
+
fs.writeFileSync(giPath, block.replace(/^\n/, ''), 'utf8');
|
|
1002
|
+
}
|
|
1003
|
+
console.log(`${green}gitignore${reset} ${rel} (+ .oxe/)`);
|
|
1004
|
+
}
|
|
1005
|
+
|
|
738
1006
|
/**
|
|
739
1007
|
* Create `.oxe/STATE.md` from template and ensure `.oxe/codebase/` exists.
|
|
740
1008
|
* @param {string} target
|
|
@@ -755,6 +1023,7 @@ function bootstrapOxe(target, opts) {
|
|
|
755
1023
|
|
|
756
1024
|
if (opts.dryRun) {
|
|
757
1025
|
console.log(`${dim}init${reset} ${oxeDir}/ (STATE.md, config.json, codebase/)`);
|
|
1026
|
+
ensureGitignoreIgnoresOxeDir(target, { dryRun: true });
|
|
758
1027
|
return;
|
|
759
1028
|
}
|
|
760
1029
|
|
|
@@ -775,6 +1044,8 @@ function bootstrapOxe(target, opts) {
|
|
|
775
1044
|
console.log(`${dim}omitido${reset} ${configDest} (já existe — use --force para substituir)`);
|
|
776
1045
|
}
|
|
777
1046
|
}
|
|
1047
|
+
|
|
1048
|
+
ensureGitignoreIgnoresOxeDir(target, { dryRun: false });
|
|
778
1049
|
}
|
|
779
1050
|
|
|
780
1051
|
/**
|
|
@@ -839,8 +1110,39 @@ function printOxeHealthDiagnostics(target, c) {
|
|
|
839
1110
|
|
|
840
1111
|
/**
|
|
841
1112
|
* @param {string} target
|
|
1113
|
+
* @param {{ json?: boolean }} [opts]
|
|
842
1114
|
*/
|
|
843
|
-
function runStatus(target) {
|
|
1115
|
+
function runStatus(target, opts = {}) {
|
|
1116
|
+
const { config } = oxeHealth.loadOxeConfigMerged(target);
|
|
1117
|
+
const next = oxeHealth.suggestNextStep(target, { discuss_before_plan: config.discuss_before_plan });
|
|
1118
|
+
const report = oxeHealth.buildHealthReport(target);
|
|
1119
|
+
|
|
1120
|
+
if (opts.json) {
|
|
1121
|
+
/** @type {Record<string, unknown>} */
|
|
1122
|
+
const payload = {
|
|
1123
|
+
oxeStatusSchema: 1,
|
|
1124
|
+
projectRoot: path.resolve(target),
|
|
1125
|
+
nextStep: report.next.step,
|
|
1126
|
+
cursorCmd: report.next.cursorCmd,
|
|
1127
|
+
reason: report.next.reason,
|
|
1128
|
+
artifacts: report.next.artifacts,
|
|
1129
|
+
phase: report.phase,
|
|
1130
|
+
scanDate: report.scanDate,
|
|
1131
|
+
staleScan: report.stale,
|
|
1132
|
+
diagnostics: {
|
|
1133
|
+
configParseError: report.configParseError,
|
|
1134
|
+
typeErrors: report.typeErrors,
|
|
1135
|
+
unknownConfigKeys: report.unknownConfigKeys,
|
|
1136
|
+
phaseWarnings: report.phaseWarn,
|
|
1137
|
+
summaryGapWarning: report.summaryGapWarn,
|
|
1138
|
+
specWarnings: report.specWarn,
|
|
1139
|
+
planWarnings: report.planWarn,
|
|
1140
|
+
},
|
|
1141
|
+
};
|
|
1142
|
+
console.log(JSON.stringify(payload));
|
|
1143
|
+
return;
|
|
1144
|
+
}
|
|
1145
|
+
|
|
844
1146
|
printSection('OXE ▸ status');
|
|
845
1147
|
const c = useAnsiColors();
|
|
846
1148
|
console.log(` ${c ? green : ''}Projeto:${c ? reset : ''} ${c ? cyan : ''}${target}${c ? reset : ''}`);
|
|
@@ -852,12 +1154,9 @@ function runStatus(target) {
|
|
|
852
1154
|
|
|
853
1155
|
printOxeHealthDiagnostics(target, c);
|
|
854
1156
|
|
|
855
|
-
const { config } = oxeHealth.loadOxeConfigMerged(target);
|
|
856
|
-
const next = oxeHealth.suggestNextStep(target, { discuss_before_plan: config.discuss_before_plan });
|
|
857
|
-
|
|
858
1157
|
console.log(`\n ${c ? yellow : ''}Próximo passo sugerido (único)${reset}`);
|
|
859
1158
|
console.log(` ${c ? dim : ''}Passo:${c ? reset : ''} ${c ? green : ''}${next.step}${reset}`);
|
|
860
|
-
console.log(` ${c ? dim : ''}No Cursor:${c ? reset : ''} ${c ? cyan : ''}${next.cursorCmd}${reset}`);
|
|
1159
|
+
console.log(` ${c ? dim : ''}No Cursor (referência):${c ? reset : ''} ${c ? cyan : ''}${next.cursorCmd}${reset}`);
|
|
861
1160
|
console.log(` ${c ? dim : ''}Motivo:${c ? reset : ''} ${next.reason}`);
|
|
862
1161
|
|
|
863
1162
|
printSummaryAndNextSteps(c, {
|
|
@@ -1053,17 +1352,31 @@ function maybePromptGlobalCli(opts) {
|
|
|
1053
1352
|
const c = useAnsiColors();
|
|
1054
1353
|
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
1055
1354
|
|
|
1355
|
+
const colW = 42;
|
|
1356
|
+
const row = (cmd, desc) => {
|
|
1357
|
+
const pad = ' '.repeat(Math.max(1, colW - cmd.length));
|
|
1358
|
+
console.log(` ${c ? dim : ''}${cmd}${c ? reset : ''}${pad}${desc}`);
|
|
1359
|
+
};
|
|
1360
|
+
|
|
1056
1361
|
console.log(
|
|
1057
1362
|
` ${c ? yellow : ''}Instalar o comando oxe-cc globalmente?${c ? reset : ''}
|
|
1058
|
-
(Os
|
|
1363
|
+
(Os ficheiros OXE já foram copiados para o projeto.)
|
|
1364
|
+
|
|
1365
|
+
O ${c ? cyan : ''}oxe-cc${c ? reset : ''} na linha de comandos permite validar e atualizar sem depender só do npx:
|
|
1059
1366
|
|
|
1060
|
-
|
|
1367
|
+
`
|
|
1368
|
+
);
|
|
1369
|
+
row('npx oxe-cc doctor', 'Validar workflows, config e mapa de scan');
|
|
1370
|
+
row('npx oxe-cc status', 'Estado .oxe/ e um próximo passo sugerido');
|
|
1371
|
+
row(`npx oxe-cc@latest --force`, 'Reinstalar/atualizar workflows no projeto');
|
|
1372
|
+
console.log(`
|
|
1373
|
+
${c ? cyan : ''}1${c ? reset : ''}) ${c ? dim : ''}Não — uso ${c ? reset : ''}${c ? cyan : ''}npx oxe-cc@latest${c ? reset : ''}${c ? dim : ''} (recomendado em CI)${c ? reset : ''}
|
|
1061
1374
|
${c ? cyan : ''}2${c ? reset : ''}) ${c ? dim : ''}Sim — ${c ? reset : ''}${c ? cyan : ''}npm install -g ${readPkgName()}@${readPkgVersion()}${c ? reset : ''}${c ? dim : ''} (${c ? reset : ''}${c ? cyan : ''}oxe-cc${c ? reset : ''}${c ? dim : ''} no PATH)${c ? reset : ''}
|
|
1062
1375
|
`
|
|
1063
1376
|
);
|
|
1064
1377
|
|
|
1065
1378
|
return new Promise((resolve) => {
|
|
1066
|
-
rl.question(` ${c ?
|
|
1379
|
+
rl.question(` ${c ? dim : ''}Escolha${c ? reset : ''} ${c ? dim : ''}[1]${c ? reset : ''}: `, (answer) => {
|
|
1067
1380
|
rl.close();
|
|
1068
1381
|
const choice = (answer || '1').trim();
|
|
1069
1382
|
if (choice === '2') installGlobalCliPackage();
|
|
@@ -1079,7 +1392,7 @@ function maybePromptGlobalCli(opts) {
|
|
|
1079
1392
|
|
|
1080
1393
|
function usage() {
|
|
1081
1394
|
console.log(`
|
|
1082
|
-
${cyan}oxe-cc${reset} — instala workflows OXE (
|
|
1395
|
+
${cyan}oxe-cc${reset} — instala workflows OXE (núcleo .oxe/ + integrações: Cursor, Copilot, Claude, OpenCode, Gemini, Codex, Windsurf, Antigravity, …)
|
|
1083
1396
|
|
|
1084
1397
|
${green}Uso:${reset}
|
|
1085
1398
|
npx oxe-cc@latest [opções] [pasta-do-projeto]
|
|
@@ -1094,30 +1407,37 @@ ${green}Uso:${reset}
|
|
|
1094
1407
|
${green}uninstall${reset} (remove OXE da pasta do usuário + pastas de workflows no repo)
|
|
1095
1408
|
--cursor / --copilot / --copilot-cli só essa integração (omissão = todas)
|
|
1096
1409
|
--all-agents também remove ficheiros multi-plataforma (com --copilot-cli implícito)
|
|
1410
|
+
--ide-local remove integrações IDE neste repositório (.cursor, .github, .claude, .copilot, …)
|
|
1097
1411
|
--ide-only não apagar .oxe/workflows, oxe/, etc. no projeto
|
|
1098
|
-
--config-dir <caminho> com exatamente uma flag IDE acima
|
|
1412
|
+
--config-dir <caminho> com exatamente uma flag IDE acima (não combina com --ide-local)
|
|
1099
1413
|
--dry-run
|
|
1100
1414
|
--dir <pasta> raiz do projeto (padrão: diretório atual)
|
|
1101
1415
|
|
|
1102
1416
|
${green}update${reset} (executa npx oxe-cc@latest --force na pasta do projeto)
|
|
1103
|
-
--
|
|
1417
|
+
--check só consulta npm: compara versão em execução com latest (saída 0=ok, 1=há mais nova, 2=erro; incompatível com --dry-run)
|
|
1418
|
+
--if-newer só executa o npx se existir versão mais nova no npm (falha de rede/registry: saída 2, sem npx)
|
|
1419
|
+
--dir <pasta> pasta em que o npx roda (padrão: atual; ignorada com --check)
|
|
1104
1420
|
--dry-run mostra o comando sem executar
|
|
1105
1421
|
[argumentos extras…] repassados ao oxe-cc (ex.: --cursor --global)
|
|
1422
|
+
${dim}CI / sem rede:${reset} OXE_UPDATE_SKIP_REGISTRY=1 desativa consultas (--check sai 2; --if-newer sai 2 sem npx)
|
|
1106
1423
|
|
|
1107
1424
|
${green}Opções da instalação:${reset}
|
|
1108
1425
|
--cursor Copia comandos e regras para ~/.cursor (padrão com --all)
|
|
1109
|
-
--copilot Mescla instruções + prompts em ~/.copilot (
|
|
1426
|
+
--copilot Mescla instruções + prompts em ~/.copilot (global) ou .github/ (com --ide-local)
|
|
1110
1427
|
--copilot-cli Skills em ~/.copilot/skills (/oxe, /oxe-scan, …) + cópia legado em ~/.claude/commands e ~/.copilot/commands
|
|
1111
1428
|
(subconjunto de --all-agents)
|
|
1112
|
-
--all-agents
|
|
1429
|
+
--all-agents Cursor+Copilot + CLIs + OpenCode, Gemini (TOML), Codex, Windsurf, Antigravity
|
|
1430
|
+
--opencode / --gemini / --codex / --windsurf / --antigravity só esse agente (sem os outros)
|
|
1431
|
+
--ide-global Instalar integrações IDE nas pastas do utilizador (~/.cursor, …) — predefinido
|
|
1432
|
+
--ide-local Instalar integrações IDE só neste repositório (.cursor, .github, .copilot, …)
|
|
1113
1433
|
--vscode Também copia .vscode/settings.json (chat.promptFiles)
|
|
1114
1434
|
--all, -a Cursor + Copilot (padrão se não passar --cursor nem --copilot)
|
|
1115
1435
|
--no-commands Não copia commands/oxe
|
|
1116
1436
|
--no-agents Não copia AGENTS.md
|
|
1117
1437
|
--no-init-oxe Não cria .oxe/STATE.md + .oxe/codebase/ após copiar workflows
|
|
1118
|
-
--oxe-only Só .oxe/workflows e templates (sem
|
|
1119
|
-
--global Layout
|
|
1120
|
-
--local Layout
|
|
1438
|
+
--oxe-only Só .oxe/workflows e templates (sem integrações IDE/CLI, commands, AGENTS.md)
|
|
1439
|
+
--global Layout do ${dim}repositório${reset}: pasta oxe/ na raiz + .oxe/ ${dim}(não confundir com --ide-global)${reset}
|
|
1440
|
+
--local Layout do ${dim}repositório${reset}: só .oxe/workflows ${dim}(não confundir com --ide-local)${reset}
|
|
1121
1441
|
--global-cli, -g Depois da cópia: npm install -g oxe-cc@<versão> (sem pergunta)
|
|
1122
1442
|
--no-global-cli, -l Não pergunta pelo CLI global (recomendado em CI)
|
|
1123
1443
|
--force, -f Sobrescreve arquivos existentes
|
|
@@ -1130,8 +1450,11 @@ ${green}Opções da instalação:${reset}
|
|
|
1130
1450
|
|
|
1131
1451
|
${green}status${reset} (coerência .oxe/ + um próximo passo sugerido; não exige pacote de workflows completo)
|
|
1132
1452
|
--dir <pasta> raiz do projeto (padrão: diretório atual)
|
|
1453
|
+
--json imprime um único objeto JSON (próximo passo + diagnósticos) em stdout; adequado a CI
|
|
1133
1454
|
|
|
1134
1455
|
${green}Atualizar (projeto já tem OXE):${reset}
|
|
1456
|
+
/oxe-update no Cursor (outras IDEs: mesmo fluxo pelo terminal)
|
|
1457
|
+
npx oxe-cc update --check só ver se há versão nova no npm
|
|
1135
1458
|
npx oxe-cc update
|
|
1136
1459
|
npx oxe-cc@latest --force
|
|
1137
1460
|
npm install -g oxe-cc@latest && oxe-cc --force
|
|
@@ -1143,6 +1466,7 @@ ${green}Exemplos:${reset}
|
|
|
1143
1466
|
npx oxe-cc@latest --cursor --dry-run
|
|
1144
1467
|
npx oxe-cc@latest --copilot --copilot-cli
|
|
1145
1468
|
npx oxe-cc@latest --all-agents
|
|
1469
|
+
npx oxe-cc@latest --cursor --ide-local
|
|
1146
1470
|
npx oxe-cc doctor
|
|
1147
1471
|
npx oxe-cc init-oxe --dir ./meu-app
|
|
1148
1472
|
npx oxe-cc uninstall --dir .
|
|
@@ -1178,14 +1502,21 @@ function runInstall(opts) {
|
|
|
1178
1502
|
` ${c ? dim : ''}Layout repo:${c ? reset : ''} ${c ? yellow : ''}só .oxe/${c ? reset : ''} ${c ? dim : ''}(${c ? cyan : ''}.oxe/workflows${c ? dim : ''})${c ? reset : ''}`
|
|
1179
1503
|
);
|
|
1180
1504
|
}
|
|
1181
|
-
const ideAny = opts
|
|
1505
|
+
const ideAny = anyIdeIntegration(opts);
|
|
1182
1506
|
if (ideAny) {
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1507
|
+
const scopeHint = opts.ideLocal
|
|
1508
|
+
? `${c ? dim : ''}pastas neste repositório (.cursor, .github, …)${c ? reset : ''}`
|
|
1509
|
+
: `${c ? dim : ''}pastas do utilizador (${c ? yellow : ''}~/.cursor${c ? dim : ''}, ${c ? yellow : ''}~/.copilot${c ? dim : ''}, …)${c ? reset : ''}`;
|
|
1510
|
+
const extra = opts.allAgents
|
|
1511
|
+
? `${c ? dim : ''} + OpenCode, Gemini, Codex, Windsurf, Antigravity${c ? reset : ''}`
|
|
1512
|
+
: anyGranularAgent(opts)
|
|
1513
|
+
? `${c ? dim : ''} + agentes selecionados${c ? reset : ''}`
|
|
1514
|
+
: '';
|
|
1515
|
+
console.log(` ${c ? dim : ''}Integrações IDE:${c ? reset : ''} ${scopeHint}${extra}`);
|
|
1186
1516
|
}
|
|
1187
1517
|
|
|
1188
1518
|
const copyOpts = { dryRun: opts.dryRun, force: opts.force };
|
|
1519
|
+
const agentPaths = oxeAgentInstall.buildAgentInstallPaths(!opts.ideLocal, target);
|
|
1189
1520
|
|
|
1190
1521
|
if (fullLayout) {
|
|
1191
1522
|
copyDir(path.join(PKG_ROOT, 'oxe'), path.join(target, 'oxe'), copyOpts, false);
|
|
@@ -1195,7 +1526,7 @@ function runInstall(opts) {
|
|
|
1195
1526
|
copyDir(path.join(PKG_ROOT, 'oxe', 'templates'), path.join(nested, 'templates'), copyOpts, true);
|
|
1196
1527
|
}
|
|
1197
1528
|
|
|
1198
|
-
const cursorBase =
|
|
1529
|
+
const cursorBase = installCursorBase(opts);
|
|
1199
1530
|
if (opts.cursor) {
|
|
1200
1531
|
const cCmd = path.join(PKG_ROOT, '.cursor', 'commands');
|
|
1201
1532
|
const cRules = path.join(PKG_ROOT, '.cursor', 'rules');
|
|
@@ -1206,8 +1537,9 @@ function runInstall(opts) {
|
|
|
1206
1537
|
const doAgentClis = opts.copilotCli || opts.allAgents;
|
|
1207
1538
|
if (doAgentClis) {
|
|
1208
1539
|
const cCmd = path.join(PKG_ROOT, '.cursor', 'commands');
|
|
1209
|
-
const
|
|
1210
|
-
const cpHome =
|
|
1540
|
+
const clBase = installClaudeBase(opts);
|
|
1541
|
+
const cpHome = installCopilotCliHome(opts);
|
|
1542
|
+
const clDest = path.join(clBase, 'commands');
|
|
1211
1543
|
const cpCmdDest = path.join(cpHome, 'commands');
|
|
1212
1544
|
const cpSkills = path.join(cpHome, 'skills');
|
|
1213
1545
|
if (fs.existsSync(cCmd)) {
|
|
@@ -1222,29 +1554,39 @@ function runInstall(opts) {
|
|
|
1222
1554
|
}
|
|
1223
1555
|
}
|
|
1224
1556
|
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
oxeAgentInstall.installOpenCodeCommands(
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
oxeAgentInstall.
|
|
1557
|
+
const cCmdAgents = path.join(PKG_ROOT, '.cursor', 'commands');
|
|
1558
|
+
if (fs.existsSync(cCmdAgents) && (opts.allAgents || anyGranularAgent(opts))) {
|
|
1559
|
+
const logO = (d) => console.log(`${dim}omitido${reset} ${d} (já existe — use --force)`);
|
|
1560
|
+
const logW = (msg) => console.log(`${dim}agents${reset} ${msg}`);
|
|
1561
|
+
console.log(
|
|
1562
|
+
` ${c ? green : ''}agents${c ? reset : ''} ${c ? dim : ''}OpenCode, Gemini (TOML), Windsurf, Codex (prompts + skills), Antigravity (conforme seleção)${c ? reset : ''}`
|
|
1563
|
+
);
|
|
1564
|
+
if (opts.agentOpenCode || opts.allAgents) {
|
|
1565
|
+
oxeAgentInstall.installOpenCodeCommands(cCmdAgents, agentPaths, copyOpts, idePathRewrite, logO, logW);
|
|
1566
|
+
}
|
|
1567
|
+
if (opts.agentGemini || opts.allAgents) {
|
|
1568
|
+
oxeAgentInstall.installGeminiTomlCommands(cCmdAgents, agentPaths, copyOpts, idePathRewrite, logO, logW);
|
|
1569
|
+
}
|
|
1570
|
+
if (opts.agentWindsurf || opts.allAgents) {
|
|
1571
|
+
oxeAgentInstall.installWindsurfGlobalWorkflows(cCmdAgents, agentPaths, copyOpts, idePathRewrite, logO, logW);
|
|
1572
|
+
}
|
|
1573
|
+
if (opts.agentCodex || opts.allAgents) {
|
|
1574
|
+
oxeAgentInstall.installCodexPrompts(cCmdAgents, agentPaths, copyOpts, idePathRewrite, logO, logW);
|
|
1575
|
+
}
|
|
1576
|
+
if (opts.agentAntigravity || opts.allAgents) {
|
|
1237
1577
|
oxeAgentInstall.installSkillTreeFromCursorCommands(
|
|
1238
|
-
|
|
1239
|
-
|
|
1578
|
+
cCmdAgents,
|
|
1579
|
+
agentPaths.antigravitySkillsRoot,
|
|
1240
1580
|
copyOpts,
|
|
1241
1581
|
idePathRewrite,
|
|
1242
1582
|
logO,
|
|
1243
1583
|
logW
|
|
1244
1584
|
);
|
|
1585
|
+
}
|
|
1586
|
+
if (opts.agentCodex || opts.allAgents) {
|
|
1245
1587
|
oxeAgentInstall.installSkillTreeFromCursorCommands(
|
|
1246
|
-
|
|
1247
|
-
|
|
1588
|
+
cCmdAgents,
|
|
1589
|
+
agentPaths.codexAgentsSkillsRoot,
|
|
1248
1590
|
copyOpts,
|
|
1249
1591
|
idePathRewrite,
|
|
1250
1592
|
logO,
|
|
@@ -1253,17 +1595,15 @@ function runInstall(opts) {
|
|
|
1253
1595
|
}
|
|
1254
1596
|
}
|
|
1255
1597
|
|
|
1256
|
-
const copilotRoot = copilotUserDir(opts);
|
|
1257
1598
|
if (opts.copilot) {
|
|
1258
1599
|
const gh = path.join(PKG_ROOT, '.github');
|
|
1259
1600
|
const inst = path.join(gh, 'copilot-instructions.md');
|
|
1260
1601
|
const prompts = path.join(gh, 'prompts');
|
|
1261
1602
|
if (fs.existsSync(inst)) {
|
|
1262
|
-
|
|
1263
|
-
installMergedCopilotInstructions(inst, dest, copyOpts, idePathRewrite);
|
|
1603
|
+
installMergedCopilotInstructions(inst, copilotInstructionsPath(opts), copyOpts, idePathRewrite);
|
|
1264
1604
|
}
|
|
1265
1605
|
if (fs.existsSync(prompts)) {
|
|
1266
|
-
copyDir(prompts,
|
|
1606
|
+
copyDir(prompts, copilotPromptsDirPath(opts), copyOpts, idePathRewrite);
|
|
1267
1607
|
}
|
|
1268
1608
|
}
|
|
1269
1609
|
|
|
@@ -1296,8 +1636,9 @@ function runInstall(opts) {
|
|
|
1296
1636
|
}
|
|
1297
1637
|
|
|
1298
1638
|
if (!opts.noInitOxe) bootstrapOxe(target, { dryRun: opts.dryRun, force: opts.force });
|
|
1639
|
+
else ensureGitignoreIgnoresOxeDir(target, { dryRun: opts.dryRun });
|
|
1299
1640
|
|
|
1300
|
-
if (!opts.dryRun && (opts.cursor || opts.copilot || opts.copilotCli || opts.allAgents)) {
|
|
1641
|
+
if (!opts.dryRun && (opts.cursor || opts.copilot || opts.copilotCli || opts.allAgents || anyGranularAgent(opts))) {
|
|
1301
1642
|
const nextFiles = {};
|
|
1302
1643
|
const addTracked = (root, nameFilter) => {
|
|
1303
1644
|
if (!fs.existsSync(root)) return;
|
|
@@ -1318,12 +1659,13 @@ function runInstall(opts) {
|
|
|
1318
1659
|
/* skip */
|
|
1319
1660
|
}
|
|
1320
1661
|
};
|
|
1662
|
+
const cpCliHome = installCopilotCliHome(opts);
|
|
1321
1663
|
if (opts.cursor) {
|
|
1322
1664
|
addTracked(path.join(cursorBase, 'commands'), (n) => n.startsWith('oxe-') && n.endsWith('.md'));
|
|
1323
1665
|
addTracked(path.join(cursorBase, 'rules'), (n) => n.includes('oxe') && (n.endsWith('.mdc') || n.endsWith('.md')));
|
|
1324
1666
|
}
|
|
1325
1667
|
if (opts.copilot) {
|
|
1326
|
-
const instP =
|
|
1668
|
+
const instP = copilotInstructionsPath(opts);
|
|
1327
1669
|
if (fs.existsSync(instP)) {
|
|
1328
1670
|
try {
|
|
1329
1671
|
nextFiles[instP] = oxeManifest.sha256File(instP);
|
|
@@ -1331,12 +1673,12 @@ function runInstall(opts) {
|
|
|
1331
1673
|
/* skip */
|
|
1332
1674
|
}
|
|
1333
1675
|
}
|
|
1334
|
-
addTracked(
|
|
1676
|
+
addTracked(copilotPromptsDirPath(opts), (n) => n.startsWith('oxe-'));
|
|
1335
1677
|
}
|
|
1336
1678
|
if (opts.copilotCli || opts.allAgents) {
|
|
1337
|
-
addTracked(path.join(
|
|
1338
|
-
addTracked(path.join(
|
|
1339
|
-
const skRoot = path.join(
|
|
1679
|
+
addTracked(path.join(installClaudeBase(opts), 'commands'), (n) => n.startsWith('oxe-') && n.endsWith('.md'));
|
|
1680
|
+
addTracked(path.join(cpCliHome, 'commands'), (n) => n.startsWith('oxe-') && n.endsWith('.md'));
|
|
1681
|
+
const skRoot = path.join(cpCliHome, 'skills');
|
|
1340
1682
|
if (fs.existsSync(skRoot)) {
|
|
1341
1683
|
for (const sub of fs.readdirSync(skRoot, { withFileTypes: true })) {
|
|
1342
1684
|
if (!sub.isDirectory() || !/^oxe($|-)/.test(sub.name)) continue;
|
|
@@ -1351,11 +1693,13 @@ function runInstall(opts) {
|
|
|
1351
1693
|
}
|
|
1352
1694
|
}
|
|
1353
1695
|
}
|
|
1354
|
-
if (opts.allAgents) {
|
|
1355
|
-
for (const d of
|
|
1696
|
+
if (opts.agentOpenCode || opts.allAgents) {
|
|
1697
|
+
for (const d of agentPaths.opencodeCommandDirs) {
|
|
1356
1698
|
addTracked(d, (n) => n.startsWith('oxe-') && n.endsWith('.md'));
|
|
1357
1699
|
}
|
|
1358
|
-
|
|
1700
|
+
}
|
|
1701
|
+
if (opts.agentGemini || opts.allAgents) {
|
|
1702
|
+
const gCmd = agentPaths.geminiCommandsBase;
|
|
1359
1703
|
trackFile(path.join(gCmd, 'oxe.toml'));
|
|
1360
1704
|
const oxeGem = path.join(gCmd, 'oxe');
|
|
1361
1705
|
if (fs.existsSync(oxeGem)) {
|
|
@@ -1363,14 +1707,28 @@ function runInstall(opts) {
|
|
|
1363
1707
|
if (n.endsWith('.toml')) trackFile(path.join(oxeGem, n));
|
|
1364
1708
|
}
|
|
1365
1709
|
}
|
|
1366
|
-
|
|
1367
|
-
|
|
1710
|
+
}
|
|
1711
|
+
if (opts.agentWindsurf || opts.allAgents) {
|
|
1712
|
+
addTracked(agentPaths.windsurfWorkflowsDir, (n) => n === 'oxe.md' || (n.startsWith('oxe-') && n.endsWith('.md')));
|
|
1713
|
+
}
|
|
1714
|
+
if (opts.agentCodex || opts.allAgents) {
|
|
1715
|
+
const cxPrompts = agentPaths.codexPromptsDir;
|
|
1368
1716
|
if (fs.existsSync(cxPrompts)) {
|
|
1369
1717
|
addTracked(cxPrompts, (n) => n.startsWith('oxe-') && n.endsWith('.md'));
|
|
1370
1718
|
}
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1719
|
+
}
|
|
1720
|
+
if (opts.agentAntigravity || opts.allAgents) {
|
|
1721
|
+
const root = agentPaths.antigravitySkillsRoot;
|
|
1722
|
+
if (fs.existsSync(root)) {
|
|
1723
|
+
for (const sub of fs.readdirSync(root, { withFileTypes: true })) {
|
|
1724
|
+
if (!sub.isDirectory() || !/^oxe($|-)/.test(sub.name)) continue;
|
|
1725
|
+
trackFile(path.join(root, sub.name, 'SKILL.md'));
|
|
1726
|
+
}
|
|
1727
|
+
}
|
|
1728
|
+
}
|
|
1729
|
+
if (opts.agentCodex || opts.allAgents) {
|
|
1730
|
+
const root = agentPaths.codexAgentsSkillsRoot;
|
|
1731
|
+
if (fs.existsSync(root)) {
|
|
1374
1732
|
for (const sub of fs.readdirSync(root, { withFileTypes: true })) {
|
|
1375
1733
|
if (!sub.isDirectory() || !/^oxe($|-)/.test(sub.name)) continue;
|
|
1376
1734
|
trackFile(path.join(root, sub.name, 'SKILL.md'));
|
|
@@ -1381,14 +1739,105 @@ function runInstall(opts) {
|
|
|
1381
1739
|
oxeManifest.writeFileManifest(home, mergedManifest, readPkgVersion());
|
|
1382
1740
|
}
|
|
1383
1741
|
|
|
1384
|
-
printSummaryAndNextSteps(
|
|
1385
|
-
c,
|
|
1386
|
-
buildInstallSummary(opts, fullLayout, cursorBase, copilotRoot, claudeUserDir(opts))
|
|
1387
|
-
);
|
|
1742
|
+
printSummaryAndNextSteps(c, buildInstallSummary(opts, fullLayout));
|
|
1388
1743
|
console.log(` ${c ? green : ''}✓${c ? reset : ''} Instalação concluída com sucesso.\n`);
|
|
1389
1744
|
}
|
|
1390
1745
|
|
|
1391
|
-
/** @typedef {{ help: boolean, dryRun: boolean, cursor: boolean, copilot: boolean, copilotCli: boolean, allAgents: boolean, ideExplicit: boolean, noProject: boolean, dir: string, explicitConfigDir: string | null, parseError: boolean, unknownFlag: string, conflictFlags: string }} UninstallOpts */
|
|
1746
|
+
/** @typedef {{ help: boolean, dryRun: boolean, cursor: boolean, copilot: boolean, copilotCli: boolean, allAgents: boolean, ideLocal: boolean, ideExplicit: boolean, noProject: boolean, dir: string, explicitConfigDir: string | null, parseError: boolean, unknownFlag: string, conflictFlags: string }} UninstallOpts */
|
|
1747
|
+
|
|
1748
|
+
/**
|
|
1749
|
+
* @param {UninstallOpts} u
|
|
1750
|
+
* @param {string[]} removedPaths
|
|
1751
|
+
*/
|
|
1752
|
+
function uninstallLocalIdeFromProject(u, removedPaths) {
|
|
1753
|
+
const proj = path.resolve(u.dir);
|
|
1754
|
+
const track = (p) => {
|
|
1755
|
+
if (removedPaths.indexOf(p) === -1) removedPaths.push(p);
|
|
1756
|
+
};
|
|
1757
|
+
|
|
1758
|
+
if (u.cursor) {
|
|
1759
|
+
const cmdDir = path.join(proj, '.cursor', 'commands');
|
|
1760
|
+
if (fs.existsSync(cmdDir)) {
|
|
1761
|
+
for (const name of fs.readdirSync(cmdDir)) {
|
|
1762
|
+
if (name.startsWith('oxe-') && name.endsWith('.md')) {
|
|
1763
|
+
const p = path.join(cmdDir, name);
|
|
1764
|
+
unlinkQuiet(p, u);
|
|
1765
|
+
track(p);
|
|
1766
|
+
}
|
|
1767
|
+
}
|
|
1768
|
+
}
|
|
1769
|
+
const ruleDir = path.join(proj, '.cursor', 'rules');
|
|
1770
|
+
if (fs.existsSync(ruleDir)) {
|
|
1771
|
+
for (const name of fs.readdirSync(ruleDir)) {
|
|
1772
|
+
if (name.includes('oxe') && (name.endsWith('.mdc') || name.endsWith('.md'))) {
|
|
1773
|
+
const p = path.join(ruleDir, name);
|
|
1774
|
+
unlinkQuiet(p, u);
|
|
1775
|
+
track(p);
|
|
1776
|
+
}
|
|
1777
|
+
}
|
|
1778
|
+
}
|
|
1779
|
+
}
|
|
1780
|
+
|
|
1781
|
+
if (u.copilot) {
|
|
1782
|
+
const inst = path.join(proj, '.github', 'copilot-instructions.md');
|
|
1783
|
+
stripOxeFromCopilotInstructions(inst, u);
|
|
1784
|
+
const pr = path.join(proj, '.github', 'prompts');
|
|
1785
|
+
if (fs.existsSync(pr)) {
|
|
1786
|
+
for (const name of fs.readdirSync(pr)) {
|
|
1787
|
+
if (name.startsWith('oxe-')) {
|
|
1788
|
+
const p = path.join(pr, name);
|
|
1789
|
+
unlinkQuiet(p, u);
|
|
1790
|
+
track(p);
|
|
1791
|
+
}
|
|
1792
|
+
}
|
|
1793
|
+
}
|
|
1794
|
+
}
|
|
1795
|
+
|
|
1796
|
+
if (u.copilotCli || u.allAgents) {
|
|
1797
|
+
for (const base of [path.join(proj, '.claude'), path.join(proj, '.copilot')]) {
|
|
1798
|
+
const cmdDir = path.join(base, 'commands');
|
|
1799
|
+
if (!fs.existsSync(cmdDir)) continue;
|
|
1800
|
+
for (const name of fs.readdirSync(cmdDir)) {
|
|
1801
|
+
if (name.startsWith('oxe-') && name.endsWith('.md')) {
|
|
1802
|
+
const p = path.join(cmdDir, name);
|
|
1803
|
+
unlinkQuiet(p, u);
|
|
1804
|
+
track(p);
|
|
1805
|
+
}
|
|
1806
|
+
}
|
|
1807
|
+
}
|
|
1808
|
+
const skillsRoot = path.join(proj, '.copilot', 'skills');
|
|
1809
|
+
if (fs.existsSync(skillsRoot)) {
|
|
1810
|
+
for (const ent of fs.readdirSync(skillsRoot, { withFileTypes: true })) {
|
|
1811
|
+
if (!ent.isDirectory() || !/^oxe($|-)/.test(ent.name)) continue;
|
|
1812
|
+
const skillFile = path.join(skillsRoot, ent.name, 'SKILL.md');
|
|
1813
|
+
if (!fs.existsSync(skillFile)) continue;
|
|
1814
|
+
let txt = '';
|
|
1815
|
+
try {
|
|
1816
|
+
txt = fs.readFileSync(skillFile, 'utf8');
|
|
1817
|
+
} catch {
|
|
1818
|
+
continue;
|
|
1819
|
+
}
|
|
1820
|
+
if (!txt.includes('<!-- oxe-cc managed -->')) continue;
|
|
1821
|
+
const dir = path.join(skillsRoot, ent.name);
|
|
1822
|
+
if (u.dryRun) {
|
|
1823
|
+
console.log(`${dim}rm -r${reset} ${dir}`);
|
|
1824
|
+
} else {
|
|
1825
|
+
fs.rmSync(dir, { recursive: true, force: true });
|
|
1826
|
+
}
|
|
1827
|
+
track(skillFile);
|
|
1828
|
+
}
|
|
1829
|
+
}
|
|
1830
|
+
}
|
|
1831
|
+
|
|
1832
|
+
if (u.copilotCli || u.allAgents) {
|
|
1833
|
+
const localPaths = oxeAgentInstall.buildAgentInstallPaths(false, proj);
|
|
1834
|
+
if (!u.dryRun) {
|
|
1835
|
+
oxeAgentInstall.cleanupMarkedUnifiedArtifacts(u, localPaths);
|
|
1836
|
+
} else {
|
|
1837
|
+
console.log(`${dim}agents${reset} (dry-run) limparia marcadores oxe-cc em pastas locais do projeto (OpenCode, Gemini, …)`);
|
|
1838
|
+
}
|
|
1839
|
+
}
|
|
1840
|
+
}
|
|
1392
1841
|
|
|
1393
1842
|
/**
|
|
1394
1843
|
* @param {string[]} argv
|
|
@@ -1403,6 +1852,7 @@ function parseUninstallArgs(argv) {
|
|
|
1403
1852
|
copilot: false,
|
|
1404
1853
|
copilotCli: false,
|
|
1405
1854
|
allAgents: false,
|
|
1855
|
+
ideLocal: false,
|
|
1406
1856
|
ideExplicit: false,
|
|
1407
1857
|
noProject: false,
|
|
1408
1858
|
dir: process.cwd(),
|
|
@@ -1431,7 +1881,8 @@ function parseUninstallArgs(argv) {
|
|
|
1431
1881
|
out.allAgents = true;
|
|
1432
1882
|
out.copilotCli = true;
|
|
1433
1883
|
out.ideExplicit = true;
|
|
1434
|
-
} else if (a === '--ide-
|
|
1884
|
+
} else if (a === '--ide-local') out.ideLocal = true;
|
|
1885
|
+
else if (a === '--ide-only') out.noProject = true;
|
|
1435
1886
|
else if (a === '--dir' && argv[i + 1]) out.dir = path.resolve(argv[++i]);
|
|
1436
1887
|
else if (!a.startsWith('-')) rest.push(path.resolve(a));
|
|
1437
1888
|
else {
|
|
@@ -1447,7 +1898,9 @@ function parseUninstallArgs(argv) {
|
|
|
1447
1898
|
out.copilotCli = true;
|
|
1448
1899
|
}
|
|
1449
1900
|
if (!out.conflictFlags && out.explicitConfigDir) {
|
|
1450
|
-
if (out.
|
|
1901
|
+
if (out.ideLocal) {
|
|
1902
|
+
out.conflictFlags = '--config-dir não combina com --ide-local';
|
|
1903
|
+
} else if (out.allAgents) {
|
|
1451
1904
|
out.conflictFlags = '--config-dir não combina com --all-agents';
|
|
1452
1905
|
} else {
|
|
1453
1906
|
const n = [out.cursor, out.copilot, out.copilotCli].filter(Boolean).length;
|
|
@@ -1539,6 +1992,14 @@ function runUninstall(u) {
|
|
|
1539
1992
|
parseError: false,
|
|
1540
1993
|
unknownFlag: '',
|
|
1541
1994
|
conflictFlags: '',
|
|
1995
|
+
ignoreInstallConfig: false,
|
|
1996
|
+
ideLocal: false,
|
|
1997
|
+
explicitIdeScope: true,
|
|
1998
|
+
agentOpenCode: false,
|
|
1999
|
+
agentGemini: false,
|
|
2000
|
+
agentCodex: false,
|
|
2001
|
+
agentWindsurf: false,
|
|
2002
|
+
agentAntigravity: false,
|
|
1542
2003
|
});
|
|
1543
2004
|
|
|
1544
2005
|
printSection('OXE ▸ uninstall');
|
|
@@ -1628,6 +2089,10 @@ function runUninstall(u) {
|
|
|
1628
2089
|
}
|
|
1629
2090
|
}
|
|
1630
2091
|
|
|
2092
|
+
if (u.ideLocal) {
|
|
2093
|
+
uninstallLocalIdeFromProject(u, removedPaths);
|
|
2094
|
+
}
|
|
2095
|
+
|
|
1631
2096
|
if (!u.noProject) {
|
|
1632
2097
|
const target = u.dir;
|
|
1633
2098
|
const nestedWf = path.join(target, '.oxe', 'workflows');
|
|
@@ -1678,7 +2143,7 @@ function runUninstall(u) {
|
|
|
1678
2143
|
console.log(` ${c ? green : ''}✓${c ? reset : ''} Desinstalação concluída com sucesso.\n`);
|
|
1679
2144
|
}
|
|
1680
2145
|
|
|
1681
|
-
/** @typedef {{ help: boolean, dryRun: boolean, dir: string, rest: string[], parseError: boolean, unknownFlag: string }} UpdateOpts */
|
|
2146
|
+
/** @typedef {{ help: boolean, dryRun: boolean, check: boolean, ifNewer: boolean, dir: string, rest: string[], parseError: boolean, unknownFlag: string, conflictFlags: string|null }} UpdateOpts */
|
|
1682
2147
|
|
|
1683
2148
|
/**
|
|
1684
2149
|
* @param {string[]} argv
|
|
@@ -1689,10 +2154,13 @@ function parseUpdateArgs(argv) {
|
|
|
1689
2154
|
const out = {
|
|
1690
2155
|
help: false,
|
|
1691
2156
|
dryRun: false,
|
|
2157
|
+
check: false,
|
|
2158
|
+
ifNewer: false,
|
|
1692
2159
|
dir: process.cwd(),
|
|
1693
2160
|
rest: [],
|
|
1694
2161
|
parseError: false,
|
|
1695
2162
|
unknownFlag: '',
|
|
2163
|
+
conflictFlags: null,
|
|
1696
2164
|
};
|
|
1697
2165
|
let dirExplicit = false;
|
|
1698
2166
|
let firstPositionalConsumed = false;
|
|
@@ -1700,6 +2168,8 @@ function parseUpdateArgs(argv) {
|
|
|
1700
2168
|
const a = argv[i];
|
|
1701
2169
|
if (a === '-h' || a === '--help') out.help = true;
|
|
1702
2170
|
else if (a === '--dry-run') out.dryRun = true;
|
|
2171
|
+
else if (a === '--check') out.check = true;
|
|
2172
|
+
else if (a === '--if-newer') out.ifNewer = true;
|
|
1703
2173
|
else if (a === '--dir' && argv[i + 1]) {
|
|
1704
2174
|
out.dir = path.resolve(argv[++i]);
|
|
1705
2175
|
dirExplicit = true;
|
|
@@ -1714,9 +2184,54 @@ function parseUpdateArgs(argv) {
|
|
|
1714
2184
|
break;
|
|
1715
2185
|
}
|
|
1716
2186
|
}
|
|
2187
|
+
if (!out.parseError) {
|
|
2188
|
+
if (out.check && out.dryRun) {
|
|
2189
|
+
out.conflictFlags = 'Combinação inválida: --check não pode ser usada com --dry-run.';
|
|
2190
|
+
} else if (out.check && out.ifNewer) {
|
|
2191
|
+
out.conflictFlags = 'Combinação inválida: use só --check ou só --if-newer.';
|
|
2192
|
+
}
|
|
2193
|
+
}
|
|
1717
2194
|
return out;
|
|
1718
2195
|
}
|
|
1719
2196
|
|
|
2197
|
+
/**
|
|
2198
|
+
* Compara versão em execução com `npm view`; termina o processo com 0 / 1 / 2.
|
|
2199
|
+
* @param {UpdateOpts} u
|
|
2200
|
+
*/
|
|
2201
|
+
function runUpdateVersionCheck(u) {
|
|
2202
|
+
assertNotWslWindowsNode();
|
|
2203
|
+
const c = useAnsiColors();
|
|
2204
|
+
const skip =
|
|
2205
|
+
process.env.OXE_UPDATE_SKIP_REGISTRY === '1' || process.env.OXE_UPDATE_SKIP_REGISTRY === 'true';
|
|
2206
|
+
printSection('OXE ▸ update — verificação no npm');
|
|
2207
|
+
if (skip) {
|
|
2208
|
+
console.error(
|
|
2209
|
+
`${yellow}Consulta ao registro desativada (OXE_UPDATE_SKIP_REGISTRY). Não foi possível verificar a versão.${reset}\n`
|
|
2210
|
+
);
|
|
2211
|
+
process.exit(2);
|
|
2212
|
+
}
|
|
2213
|
+
const pkgName = readPkgName();
|
|
2214
|
+
const current = readPkgVersion();
|
|
2215
|
+
const res = oxeNpmVersion.syncNpmViewVersion(pkgName);
|
|
2216
|
+
if (!res.ok) {
|
|
2217
|
+
console.error(`${red}Não foi possível obter a versão no npm:${reset} ${res.error}\n`);
|
|
2218
|
+
process.exit(2);
|
|
2219
|
+
}
|
|
2220
|
+
const latest = res.version;
|
|
2221
|
+
const newer = oxeNpmVersion.isNewerThan(latest, current);
|
|
2222
|
+
console.log(` ${dim}Pacote:${reset} ${pkgName}`);
|
|
2223
|
+
console.log(` ${dim}Versão em execução:${reset} ${current}`);
|
|
2224
|
+
console.log(` ${dim}Última no npm (tag latest):${reset} ${latest}`);
|
|
2225
|
+
if (newer) {
|
|
2226
|
+
console.log(
|
|
2227
|
+
`\n ${yellow}Existe uma versão mais recente no npm.${reset} Atualize com ${cyan}/oxe-update${reset} no Cursor ou, na raiz do projeto (qualquer IDE):\n ${cyan}npx oxe-cc update${reset} (ou ${cyan}npx oxe-cc update --if-newer${reset})\n`
|
|
2228
|
+
);
|
|
2229
|
+
process.exit(1);
|
|
2230
|
+
}
|
|
2231
|
+
console.log(`\n ${c ? green : ''}✓${c ? reset : ''} Já está na mesma ou numa versão mais recente que a publicada como latest.\n`);
|
|
2232
|
+
process.exit(0);
|
|
2233
|
+
}
|
|
2234
|
+
|
|
1720
2235
|
/**
|
|
1721
2236
|
* @param {UpdateOpts} u
|
|
1722
2237
|
*/
|
|
@@ -1725,12 +2240,21 @@ function runUpdate(u) {
|
|
|
1725
2240
|
const c = useAnsiColors();
|
|
1726
2241
|
if (u.dryRun) {
|
|
1727
2242
|
printSection('OXE ▸ update (simulação)');
|
|
1728
|
-
|
|
2243
|
+
if (u.ifNewer) {
|
|
2244
|
+
console.log(
|
|
2245
|
+
` ${dim}Com --if-newer, primeiro correria${reset} ${cyan}npm view ${readPkgName()} version${reset} ${dim}(salvo OXE_UPDATE_SKIP_REGISTRY). Só depois, se houver versão mais nova que a em execução, o npx abaixo.${reset}\n`
|
|
2246
|
+
);
|
|
2247
|
+
}
|
|
2248
|
+
console.log(` ${dim}Comando que seria executado (instalação):${reset}`);
|
|
1729
2249
|
console.log(` ${cyan}npx -y oxe-cc@latest --force --no-global-cli -l${reset} ${u.rest.join(' ')}`);
|
|
1730
2250
|
console.log(` ${dim}Diretório:${reset} ${u.dir}`);
|
|
1731
2251
|
printSummaryAndNextSteps(c, {
|
|
1732
|
-
bullets: [
|
|
2252
|
+
bullets: [
|
|
2253
|
+
'[simulação] O npx baixaria o pacote oxe-cc@latest e rodaria a instalação com --force.',
|
|
2254
|
+
u.ifNewer ? '[simulação] Com --if-newer, sem versão nova no npm não executaria o npx.' : '',
|
|
2255
|
+
].filter(Boolean),
|
|
1733
2256
|
nextSteps: [
|
|
2257
|
+
{ desc: 'Só ver versão no npm (sem instalar):', cmd: 'npx oxe-cc update --check' },
|
|
1734
2258
|
{ desc: 'Rodar de verdade (sem --dry-run), na pasta do projeto:', cmd: 'npx oxe-cc update' },
|
|
1735
2259
|
{ desc: 'Depois, validar:', cmd: 'npx oxe-cc doctor' },
|
|
1736
2260
|
],
|
|
@@ -1738,6 +2262,38 @@ function runUpdate(u) {
|
|
|
1738
2262
|
});
|
|
1739
2263
|
return;
|
|
1740
2264
|
}
|
|
2265
|
+
|
|
2266
|
+
if (u.ifNewer) {
|
|
2267
|
+
const skip =
|
|
2268
|
+
process.env.OXE_UPDATE_SKIP_REGISTRY === '1' || process.env.OXE_UPDATE_SKIP_REGISTRY === 'true';
|
|
2269
|
+
if (skip) {
|
|
2270
|
+
console.error(
|
|
2271
|
+
`${yellow}OXE_UPDATE_SKIP_REGISTRY está ativo: não é possível comparar versões; o update não foi executado.${reset}\n`
|
|
2272
|
+
);
|
|
2273
|
+
process.exit(2);
|
|
2274
|
+
}
|
|
2275
|
+
const res = oxeNpmVersion.syncNpmViewVersion(readPkgName());
|
|
2276
|
+
if (!res.ok) {
|
|
2277
|
+
console.error(`${red}Não foi possível obter a versão no npm:${reset} ${res.error}`);
|
|
2278
|
+
console.error(`${dim}O update não foi executado (use sem --if-newer para forçar).${reset}\n`);
|
|
2279
|
+
process.exit(2);
|
|
2280
|
+
}
|
|
2281
|
+
const current = readPkgVersion();
|
|
2282
|
+
if (!oxeNpmVersion.isNewerThan(res.version, current)) {
|
|
2283
|
+
printSection('OXE ▸ update');
|
|
2284
|
+
console.log(
|
|
2285
|
+
` ${dim}Versão em execução:${reset} ${current} ${dim}| npm latest:${reset} ${res.version}`
|
|
2286
|
+
);
|
|
2287
|
+
console.log(
|
|
2288
|
+
`\n ${c ? green : ''}✓${c ? reset : ''} Nenhuma versão mais nova no npm; nada a instalar. Use ${cyan}npx oxe-cc update --check${reset} para só consultar.\n`
|
|
2289
|
+
);
|
|
2290
|
+
return;
|
|
2291
|
+
}
|
|
2292
|
+
console.log(
|
|
2293
|
+
` ${dim}Há versão mais nova no npm (${res.version} > ${current}); a executar npx…${reset}\n`
|
|
2294
|
+
);
|
|
2295
|
+
}
|
|
2296
|
+
|
|
1741
2297
|
printSection('OXE ▸ update');
|
|
1742
2298
|
const args = ['-y', 'oxe-cc@latest', '--force', '--no-global-cli', '-l', ...u.rest];
|
|
1743
2299
|
const r = spawnSync('npx', args, {
|
|
@@ -1815,6 +2371,12 @@ async function main() {
|
|
|
1815
2371
|
usage();
|
|
1816
2372
|
process.exit(0);
|
|
1817
2373
|
}
|
|
2374
|
+
if (u.conflictFlags) {
|
|
2375
|
+
printBanner();
|
|
2376
|
+
console.error(`${red}${u.conflictFlags}${reset}`);
|
|
2377
|
+
usage();
|
|
2378
|
+
process.exit(1);
|
|
2379
|
+
}
|
|
1818
2380
|
if (u.parseError) {
|
|
1819
2381
|
printBanner();
|
|
1820
2382
|
console.error(`${red}Opção desconhecida:${reset} ${u.unknownFlag}`);
|
|
@@ -1822,6 +2384,10 @@ async function main() {
|
|
|
1822
2384
|
process.exit(1);
|
|
1823
2385
|
}
|
|
1824
2386
|
printBanner();
|
|
2387
|
+
if (u.check) {
|
|
2388
|
+
runUpdateVersionCheck(u);
|
|
2389
|
+
return;
|
|
2390
|
+
}
|
|
1825
2391
|
if (!u.dryRun && !fs.existsSync(u.dir)) {
|
|
1826
2392
|
console.error(`${yellow}Diretório não encontrado: ${u.dir}${reset}`);
|
|
1827
2393
|
process.exit(1);
|
|
@@ -1857,7 +2423,9 @@ async function main() {
|
|
|
1857
2423
|
process.exit(0);
|
|
1858
2424
|
}
|
|
1859
2425
|
|
|
1860
|
-
|
|
2426
|
+
if (!(command === 'status' && opts.jsonOutput)) {
|
|
2427
|
+
printBanner();
|
|
2428
|
+
}
|
|
1861
2429
|
|
|
1862
2430
|
const target = opts.dir;
|
|
1863
2431
|
if (command === 'doctor') {
|
|
@@ -1874,7 +2442,7 @@ async function main() {
|
|
|
1874
2442
|
console.error(`${yellow}Diretório não encontrado: ${target}${reset}`);
|
|
1875
2443
|
process.exit(1);
|
|
1876
2444
|
}
|
|
1877
|
-
runStatus(target);
|
|
2445
|
+
runStatus(target, { json: opts.jsonOutput });
|
|
1878
2446
|
return;
|
|
1879
2447
|
}
|
|
1880
2448
|
|
|
@@ -1894,7 +2462,7 @@ async function main() {
|
|
|
1894
2462
|
: ['.oxe/STATE.md, .oxe/config.json e pasta .oxe/codebase/ (criados ou atualizados conforme --force)'],
|
|
1895
2463
|
nextSteps: [
|
|
1896
2464
|
{ desc: 'Validar o projeto:', cmd: 'npx oxe-cc doctor' },
|
|
1897
|
-
{ desc: 'Instalar integrações
|
|
2465
|
+
{ desc: 'Instalar integrações IDE/CLI (se ainda não fez):', cmd: 'npx oxe-cc@latest' },
|
|
1898
2466
|
{ desc: 'Começar o fluxo no agente:', cmd: '/oxe-scan' },
|
|
1899
2467
|
],
|
|
1900
2468
|
dryRun: opts.dryRun,
|