@korl3one/ccode 2.3.0 → 3.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.
package/dist/cli/index.js CHANGED
@@ -10,7 +10,9 @@ import { FileUtils } from '../utils/files.js';
10
10
  import { ContextEngine } from '../core/context.js';
11
11
  import { TaskEngine } from '../core/tasks.js';
12
12
  import { PromptBuilder } from '../core/prompt-builder.js';
13
- import { AIManager } from '../ai/manager.js';
13
+ import { AIManager, PROVIDER_INFO } from '../ai/manager.js';
14
+ import { ContextExporter } from '../core/exports.js';
15
+ import { exec } from 'child_process';
14
16
  import { FileWatcher, displayChanges } from './watcher.js';
15
17
  // ─── Estado global de sesión ────────────────────────────────────────
16
18
  const watcher = new FileWatcher();
@@ -33,84 +35,118 @@ async function requireAI() {
33
35
  }
34
36
  return config;
35
37
  }
38
+ function openBrowser(url) {
39
+ const cmd = process.platform === 'darwin' ? 'open' : process.platform === 'win32' ? 'start' : 'xdg-open';
40
+ exec(`${cmd} "${url}"`);
41
+ }
36
42
  async function promptAIConfig() {
37
- const { provider } = await inquirer.prompt([{
43
+ // 1. Auto-detect zero config if possible
44
+ const detected = AIManager.autoDetect();
45
+ if (detected) {
46
+ const info = PROVIDER_INFO[detected.provider];
47
+ showSuccess(`${info.name} detectado (${detected.source})`);
48
+ const testSpinner = ora({ text: 'Verificando conexion...', color: 'cyan', spinner: 'dots' }).start();
49
+ const defaultModel = info.models[0].value;
50
+ const config = {
51
+ provider: detected.provider,
52
+ apiKey: detected.apiKey,
53
+ model: defaultModel,
54
+ authType: detected.authType,
55
+ };
56
+ const testResult = await AIManager.testConnection(config);
57
+ if (testResult.ok) {
58
+ testSpinner.succeed(c.success(`Conectado a ${info.name}`));
59
+ const { model } = await inquirer.prompt([{
60
+ type: 'select',
61
+ name: 'model',
62
+ message: 'Modelo:',
63
+ choices: info.models,
64
+ }]);
65
+ config.model = model;
66
+ return config;
67
+ }
68
+ testSpinner.fail(c.error('Sesion expirada'));
69
+ if (detected.authType === 'oauth') {
70
+ showWarning('El token de Gemini CLI expiro.');
71
+ showInfo('Ejecuta "gemini" en otra terminal para refrescar tu sesion.');
72
+ showInfo('Luego vuelve a correr "ccode init".');
73
+ console.log('');
74
+ const { continueManual } = await inquirer.prompt([{
75
+ type: 'confirm', name: 'continueManual',
76
+ message: 'Configurar manualmente mientras tanto?', default: true,
77
+ }]);
78
+ if (!continueManual) {
79
+ throw new Error('Ejecuta "gemini" para refrescar tu sesion y vuelve a intentar.');
80
+ }
81
+ }
82
+ else {
83
+ showError(testResult.error || 'No se pudo conectar.');
84
+ }
85
+ console.log('');
86
+ }
87
+ // 2. Nothing detected — guide setup
88
+ showInfo('No se detecto ningun proveedor de IA.');
89
+ console.log('');
90
+ console.log(c.white(' Opciones para configurar:'));
91
+ console.log(c.accent(' 1. Gemini CLI') + c.dim(' — Instala: npm i -g @anthropic-ai/gemini-cli'));
92
+ console.log(c.dim(' Luego ejecuta "gemini" una vez para autenticarte con Google.'));
93
+ console.log(c.accent(' 2. Variable de entorno') + c.dim(' — export GOOGLE_API_KEY="tu-key"'));
94
+ console.log(c.accent(' 3. Variable de entorno') + c.dim(' — export ANTHROPIC_API_KEY="tu-key"'));
95
+ console.log('');
96
+ const { method } = await inquirer.prompt([{
38
97
  type: 'select',
39
- name: 'provider',
40
- message: 'Proveedor de IA:',
98
+ name: 'method',
99
+ message: 'Como quieres configurar?',
41
100
  choices: [
42
- { name: ' Claude (Anthropic) — Recomendado', value: 'claude' },
43
- { name: ' OpenAI (ChatGPT)', value: 'openai' },
44
- { name: ' Google Gemini', value: 'gemini' },
45
- { name: ' DeepSeek', value: 'deepseek' },
46
- { name: ' Groq (ultra-rápido)', value: 'groq' },
47
- { name: ' Ollama (local, sin API key)', value: 'ollama' },
101
+ { name: ' Tengo una API Key de Gemini (Google)', value: 'gemini-key' },
102
+ { name: ' Tengo una API Key de Claude (Anthropic)', value: 'claude-key' },
103
+ { name: ' Obtener API Key de Gemini gratis (abre navegador)', value: 'gemini-browser' },
104
+ { name: ' Obtener API Key de Claude (abre navegador)', value: 'claude-browser' },
48
105
  ],
49
106
  }]);
50
- const config = { provider };
51
- // Modelos por proveedor
52
- const modelChoices = {
53
- claude: [
54
- { name: 'Claude Sonnet 4 (recomendado)', value: 'claude-sonnet-4-20250514' },
55
- { name: 'Claude Haiku 3.5 (rápido)', value: 'claude-haiku-4-5-20251001' },
56
- { name: 'Claude Opus 4 (máxima calidad)', value: 'claude-opus-4-20250514' },
57
- ],
58
- openai: [
59
- { name: 'GPT-4o (recomendado)', value: 'gpt-4o' },
60
- { name: 'GPT-4o mini (rápido)', value: 'gpt-4o-mini' },
61
- { name: 'GPT-4.1 (último)', value: 'gpt-4.1' },
62
- { name: 'o3-mini (razonamiento)', value: 'o3-mini' },
63
- ],
64
- gemini: [
65
- { name: 'Gemini 2.5 Flash (recomendado)', value: 'gemini-2.5-flash' },
66
- { name: 'Gemini 2.5 Pro (máxima calidad)', value: 'gemini-2.5-pro' },
67
- { name: 'Gemini 2.0 Flash (rápido)', value: 'gemini-2.0-flash' },
68
- ],
69
- deepseek: [
70
- { name: 'DeepSeek Chat (recomendado)', value: 'deepseek-chat' },
71
- { name: 'DeepSeek Reasoner', value: 'deepseek-reasoner' },
72
- ],
73
- groq: [
74
- { name: 'Llama 3.3 70B (recomendado)', value: 'llama-3.3-70b-versatile' },
75
- { name: 'Llama 3.1 8B (rápido)', value: 'llama-3.1-8b-instant' },
76
- { name: 'Mixtral 8x7B', value: 'mixtral-8x7b-32768' },
77
- ],
78
- };
79
- // API Key (todos excepto Ollama)
80
- if (provider !== 'ollama') {
81
- const providerNames = {
82
- claude: 'Anthropic', openai: 'OpenAI', gemini: 'Google AI',
83
- deepseek: 'DeepSeek', groq: 'Groq',
84
- };
85
- const { apiKey } = await inquirer.prompt([{
86
- type: 'password',
87
- name: 'apiKey',
88
- message: `API Key de ${providerNames[provider]}:`,
89
- mask: '*',
90
- validate: (v) => v.length > 10 || 'Ingresa una API Key válida',
91
- }]);
92
- config.apiKey = apiKey;
93
- }
94
- // Selección de modelo
95
- if (provider === 'ollama') {
96
- const { model } = await inquirer.prompt([{
97
- type: 'input',
98
- name: 'model',
99
- message: 'Modelo de Ollama:',
100
- default: 'llama3',
101
- }]);
102
- config.model = model;
107
+ const isGemini = method.startsWith('gemini');
108
+ const provider = isGemini ? 'gemini' : 'claude';
109
+ const info = PROVIDER_INFO[provider];
110
+ // Open browser if needed
111
+ if (method.endsWith('-browser')) {
112
+ openBrowser(info.keyUrl);
113
+ showInfo('Se abrio el navegador. Copia tu API Key y pegala aqui.');
114
+ console.log('');
103
115
  }
104
- else {
105
- const { model } = await inquirer.prompt([{
106
- type: 'select',
107
- name: 'model',
108
- message: 'Modelo:',
109
- choices: modelChoices[provider],
110
- }]);
111
- config.model = model;
116
+ const { apiKey } = await inquirer.prompt([{
117
+ type: 'password',
118
+ name: 'apiKey',
119
+ message: `API Key de ${info.name}:`,
120
+ mask: '*',
121
+ validate: (v) => v.length > 10 || 'API Key muy corta',
122
+ }]);
123
+ const { model } = await inquirer.prompt([{
124
+ type: 'select',
125
+ name: 'model',
126
+ message: 'Modelo:',
127
+ choices: info.models,
128
+ }]);
129
+ const config = { provider, apiKey, model, authType: 'api-key' };
130
+ const testSpinner = ora({ text: 'Verificando conexion...', color: 'cyan', spinner: 'dots' }).start();
131
+ const result = await AIManager.testConnection(config);
132
+ if (result.ok) {
133
+ testSpinner.succeed(c.success('Conexion verificada'));
134
+ const envVar = info.envVars[0];
135
+ showInfo(`Tip: Para que sea automatico la proxima vez:`);
136
+ console.log(c.accent(` export ${envVar}="tu-api-key"`));
137
+ console.log(c.dim(' Agrega esa linea a tu ~/.zshrc o ~/.bashrc'));
138
+ console.log('');
139
+ return config;
112
140
  }
113
- return config;
141
+ testSpinner.fail(c.error('Error de conexion'));
142
+ showError(result.error || 'No se pudo conectar.');
143
+ const { retry } = await inquirer.prompt([{
144
+ type: 'confirm', name: 'retry',
145
+ message: 'Intentar de nuevo?', default: true,
146
+ }]);
147
+ if (retry)
148
+ return promptAIConfig();
149
+ throw new Error('Configuracion de IA cancelada.');
114
150
  }
115
151
  function listProjectFiles(dir, prefix = '') {
116
152
  const results = [];
@@ -180,7 +216,7 @@ async function startSession() {
180
216
  if (!hasConfig) {
181
217
  choices.push({ name: ' 🔌 Conectar proveedor de IA', value: 'connect' });
182
218
  }
183
- choices.push({ name: ' 📋 Generar / actualizar plan de tareas', value: 'plan' }, { name: ` 📊 Ver estado completo`, value: 'status' }, { name: ' 📄 Ver contexto generado', value: 'context' }, { name: ' 🔄 Actualizar contexto (re-analizar proyecto)', value: 'update' }, { name: ' 📤 Exportar contexto para otra IA', value: 'export' }, { name: ' 💡 Explicar proyecto (resumen rápido)', value: 'explain' }, { name: ' 🩺 Doctor (diagnóstico de salud)', value: 'doctor' });
219
+ choices.push({ name: ' 📋 Generar / actualizar plan de tareas', value: 'plan' }, { name: ` 📊 Ver estado completo`, value: 'status' }, { name: ' 📄 Ver contexto generado', value: 'context' }, { name: ' 🔄 Actualizar contexto (re-analizar proyecto)', value: 'update' }, { name: ' 🔗 Sincronizar contexto (AGENTS.md, CLAUDE.md, ...)', value: 'sync' }, { name: ' 📤 Exportar contexto para otra IA', value: 'export' }, { name: ' 💡 Explicar proyecto (resumen rápido)', value: 'explain' }, { name: ' 🩺 Doctor (diagnóstico de salud)', value: 'doctor' });
184
220
  if (hasConfig) {
185
221
  choices.push({ name: ' 🔌 Reconfigurar IA', value: 'connect' });
186
222
  }
@@ -213,6 +249,7 @@ async function startSession() {
213
249
  status: handleStatus,
214
250
  context: handleContext,
215
251
  update: handleUpdate,
252
+ sync: handleSync,
216
253
  export: handleExport,
217
254
  explain: handleExplain,
218
255
  doctor: handleDoctor,
@@ -342,6 +379,19 @@ async function handleInit() {
342
379
  { name: 'config.json ', desc: `IA: ${aiConfig.provider}` },
343
380
  ]);
344
381
  console.log('');
382
+ // ─── Sincronizar contexto a todas las herramientas AI ───
383
+ const syncSpinner = ora({ text: 'Sincronizando contexto con herramientas AI...', color: 'cyan', spinner: 'dots' }).start();
384
+ try {
385
+ const exporter = new ContextExporter();
386
+ const exported = await exporter.exportAll();
387
+ syncSpinner.succeed(c.success('Contexto sincronizado'));
388
+ console.log(c.dim('\n Archivos de contexto generados:\n'));
389
+ showFileTree(exported.map(e => ({ name: e.file, desc: e.label.split('(')[1]?.replace(')', '') || '' })));
390
+ console.log('');
391
+ }
392
+ catch {
393
+ syncSpinner.warn(c.warning('No se pudo sincronizar (los archivos .ccode/ se crearon correctamente)'));
394
+ }
345
395
  showSuccess('CCODE se queda activo observando tu proyecto.');
346
396
  showInfo('Abre otra terminal y empieza a desarrollar.');
347
397
  showInfo('CCODE detectará los cambios automáticamente.');
@@ -385,17 +435,9 @@ async function handleConnect() {
385
435
  return;
386
436
  }
387
437
  const config = await promptAIConfig();
388
- const spinner = ora({ text: 'Verificando conexión...', color: 'cyan', spinner: 'dots' }).start();
389
- const success = await AIManager.testConnection(config);
390
- if (success) {
391
- spinner.succeed(c.success('Conexión verificada'));
392
- await AIManager.saveConfig(config);
393
- showSuccess('Configuración guardada.');
394
- }
395
- else {
396
- spinner.fail(c.error('No se pudo conectar'));
397
- showError('Verifica tu API Key, conexión a internet, o que Ollama esté corriendo.');
398
- }
438
+ // promptAIConfig already verifies connection, just save
439
+ await AIManager.saveConfig(config);
440
+ showSuccess('Configuracion guardada.');
399
441
  }
400
442
  // ─── PLAN ───────────────────────────────────────────────────────────
401
443
  async function handlePlan() {
@@ -781,6 +823,50 @@ Responde ÚNICAMENTE con JSON válido:
781
823
  console.log(c.accent(' Cambios detectados:'));
782
824
  result.changes.forEach(change => console.log(c.dim(` • ${change}`)));
783
825
  }
826
+ // Sincronizar exports después de actualizar contexto
827
+ const syncSpinner = ora({ text: 'Sincronizando exports...', color: 'cyan', spinner: 'dots' }).start();
828
+ try {
829
+ const exporter = new ContextExporter();
830
+ const existing = await exporter.detectExisting();
831
+ if (existing.length > 0) {
832
+ for (const format of existing) {
833
+ await exporter.exportFormat(format);
834
+ }
835
+ syncSpinner.succeed(c.success(`${existing.length} export${existing.length > 1 ? 's' : ''} actualizado${existing.length > 1 ? 's' : ''}`));
836
+ }
837
+ else {
838
+ syncSpinner.info(c.dim('Sin exports previos. Usa "export" para generarlos.'));
839
+ }
840
+ }
841
+ catch {
842
+ syncSpinner.warn(c.warning('No se pudieron actualizar los exports'));
843
+ }
844
+ console.log('');
845
+ }
846
+ catch (error) {
847
+ spinner.fail('Error');
848
+ showError(error instanceof Error ? error.message : String(error));
849
+ }
850
+ }
851
+ // ─── SYNC ──────────────────────────────────────────────────────────
852
+ async function handleSync() {
853
+ if (!(await requireInit()))
854
+ return;
855
+ showHeader('Sincronizar Contexto', 'Genera archivos de contexto para cada herramienta AI');
856
+ const exporter = new ContextExporter();
857
+ const existing = await exporter.detectExisting();
858
+ if (existing.length > 0) {
859
+ showInfo(`Exports existentes: ${existing.map(f => ContextExporter.FORMATS[f].file).join(', ')}`);
860
+ }
861
+ const spinner = ora({ text: 'Generando archivos de contexto...', color: 'cyan', spinner: 'dots' }).start();
862
+ try {
863
+ const exported = await exporter.exportAll();
864
+ spinner.succeed(c.success('Contexto sincronizado con todas las herramientas'));
865
+ console.log('');
866
+ showFileTree(exported.map(e => ({ name: e.file, desc: e.label.split('(')[1]?.replace(')', '') || '' })));
867
+ console.log('');
868
+ showInfo('Cada herramienta AI leera su archivo automaticamente.');
869
+ showInfo('Los archivos se actualizan con cada "sync", "update" o "init".');
784
870
  console.log('');
785
871
  }
786
872
  catch (error) {
@@ -793,12 +879,61 @@ async function handleExport() {
793
879
  if (!(await requireInit()))
794
880
  return;
795
881
  showHeader('Exportar Contexto');
796
- const pb = new PromptBuilder();
797
- const fullContext = await pb.buildContextPrompt();
798
- const exportPath = path.join(process.cwd(), '.ccode', 'context-export.md');
799
- await FileUtils.writeFile(exportPath, `# CCODE — Project Context Export\n\n${fullContext}`);
800
- showSuccess('Contexto exportado a .ccode/context-export.md');
801
- showInfo('Copia el contenido y pégalo en cualquier chat de IA (ChatGPT, Claude, Gemini, etc.).');
882
+ const { mode } = await inquirer.prompt([{
883
+ type: 'select',
884
+ name: 'mode',
885
+ message: 'Tipo de export:',
886
+ choices: [
887
+ { name: ' Sincronizar con todas las herramientas AI', value: 'all' },
888
+ { name: ' Solo export universal (.md para copiar/pegar)', value: 'universal' },
889
+ { name: ' Elegir herramientas específicas', value: 'pick' },
890
+ ],
891
+ }]);
892
+ const exporter = new ContextExporter();
893
+ if (mode === 'universal') {
894
+ const content = await exporter.generateUniversalExport();
895
+ const exportPath = path.join(process.cwd(), '.ccode', 'context-export.md');
896
+ await FileUtils.writeFile(exportPath, content);
897
+ showSuccess('Contexto exportado a .ccode/context-export.md');
898
+ showInfo('Copia el contenido y pegalo en cualquier chat de IA.');
899
+ console.log('');
900
+ return;
901
+ }
902
+ if (mode === 'pick') {
903
+ const formats = Object.entries(ContextExporter.FORMATS);
904
+ const existing = await exporter.detectExisting();
905
+ const { selected } = await inquirer.prompt([{
906
+ type: 'checkbox',
907
+ name: 'selected',
908
+ message: 'Selecciona los formatos:',
909
+ choices: formats.map(([key, info]) => ({
910
+ name: ` ${info.label}${existing.includes(key) ? c.dim(' (existe)') : ''}`,
911
+ value: key,
912
+ checked: true,
913
+ })),
914
+ }]);
915
+ if (selected.length === 0) {
916
+ showWarning('No se selecciono ningun formato.');
917
+ return;
918
+ }
919
+ const spinner = ora({ text: 'Generando exports...', color: 'cyan', spinner: 'dots' }).start();
920
+ for (const format of selected) {
921
+ await exporter.exportFormat(format);
922
+ }
923
+ spinner.succeed(c.success(`${selected.length} formato${selected.length > 1 ? 's' : ''} exportado${selected.length > 1 ? 's' : ''}`));
924
+ for (const format of selected) {
925
+ const info = ContextExporter.FORMATS[format];
926
+ console.log(c.dim(` ${info.file}`));
927
+ }
928
+ console.log('');
929
+ return;
930
+ }
931
+ // mode === 'all'
932
+ const spinner = ora({ text: 'Sincronizando con todas las herramientas AI...', color: 'cyan', spinner: 'dots' }).start();
933
+ const exported = await exporter.exportAll();
934
+ spinner.succeed(c.success('Contexto sincronizado'));
935
+ console.log(c.dim('\n Archivos generados:\n'));
936
+ showFileTree(exported.map(e => ({ name: e.file, desc: e.label.split('(')[1]?.replace(')', '') || '' })));
802
937
  console.log('');
803
938
  }
804
939
  // ─── EXPLAIN ────────────────────────────────────────────────────────
@@ -878,14 +1013,15 @@ async function handleDoctor() {
878
1013
  const config = await AIManager.loadConfig();
879
1014
  if (config) {
880
1015
  console.log(c.success(` ✓ Configurado: ${config.provider} (${config.model || 'default'})`));
881
- // Test de conexión
882
- const spinner = ora({ text: ' Probando conexión...', color: 'cyan', spinner: 'dots' }).start();
883
- const connected = await AIManager.testConnection(config);
884
- if (connected) {
885
- spinner.succeed(c.success('Conexión activa'));
1016
+ // Test de conexion
1017
+ const spinner = ora({ text: ' Probando conexion...', color: 'cyan', spinner: 'dots' }).start();
1018
+ const connResult = await AIManager.testConnection(config);
1019
+ if (connResult.ok) {
1020
+ spinner.succeed(c.success('Conexion activa'));
886
1021
  }
887
1022
  else {
888
1023
  spinner.fail(c.error('No se pudo conectar'));
1024
+ showError(` ${connResult.error}`);
889
1025
  issues++;
890
1026
  }
891
1027
  }
@@ -916,7 +1052,26 @@ async function handleDoctor() {
916
1052
  issues++;
917
1053
  }
918
1054
  }
919
- // 4. Archivos del proyecto
1055
+ // 4. Exports de contexto
1056
+ console.log('');
1057
+ console.log(c.bold(' Exports de contexto'));
1058
+ const exporter = new ContextExporter();
1059
+ const existingExports = await exporter.detectExisting();
1060
+ if (existingExports.length > 0) {
1061
+ for (const format of existingExports) {
1062
+ const info = ContextExporter.FORMATS[format];
1063
+ console.log(c.success(` ✓ ${info.file}`));
1064
+ }
1065
+ const missing = Object.keys(ContextExporter.FORMATS).length - existingExports.length;
1066
+ if (missing > 0) {
1067
+ console.log(c.dim(` ${missing} formato${missing > 1 ? 's' : ''} disponible${missing > 1 ? 's' : ''} sin generar`));
1068
+ }
1069
+ }
1070
+ else {
1071
+ console.log(c.warning(' ⚠ Sin exports — ejecuta "Sincronizar contexto" para generarlos'));
1072
+ issues++;
1073
+ }
1074
+ // 5. Archivos del proyecto
920
1075
  console.log('');
921
1076
  console.log(c.bold(' Proyecto'));
922
1077
  const projectFiles = listProjectFiles(process.cwd());
@@ -959,6 +1114,7 @@ async function main() {
959
1114
  program.command('status').description('Estado del proyecto').action(handleStatus);
960
1115
  program.command('context').description('Ver contexto generado').action(handleContext);
961
1116
  program.command('update').description('Re-analiza y actualiza el contexto').action(handleUpdate);
1117
+ program.command('sync').description('Sincroniza contexto con todas las herramientas AI').action(handleSync);
962
1118
  program.command('export').description('Exporta contexto como .md para cualquier IA').action(handleExport);
963
1119
  program.command('explain').description('Resumen rápido del proyecto').action(handleExplain);
964
1120
  program.command('doctor').description('Diagnóstico de salud del proyecto').action(handleDoctor);
@@ -0,0 +1,74 @@
1
+ /**
2
+ * ContextExporter: genera archivos de contexto para cada herramienta de IA.
3
+ *
4
+ * Lee la fuente de verdad (.ccode/) y produce exports específicos:
5
+ * - AGENTS.md → Estándar abierto (Linux Foundation)
6
+ * - CLAUDE.md → Claude Code
7
+ * - GEMINI.md → Gemini CLI
8
+ * - .cursorrules → Cursor
9
+ * - copilot-instructions.md → GitHub Copilot
10
+ *
11
+ * Cada export se adapta al formato y expectativas de la herramienta destino.
12
+ */
13
+ export declare class ContextExporter {
14
+ private readonly ccodePath;
15
+ private readonly projectRoot;
16
+ static readonly FORMATS: Record<string, {
17
+ file: string;
18
+ label: string;
19
+ }>;
20
+ constructor(projectRoot?: string);
21
+ /**
22
+ * Lee todos los archivos de contexto de .ccode/
23
+ */
24
+ private readSources;
25
+ /**
26
+ * Formatea las tareas como lista legible.
27
+ */
28
+ private formatTasks;
29
+ /**
30
+ * Genera AGENTS.md — Estándar abierto compatible con múltiples herramientas.
31
+ * Sigue la convención de AGENTS.md (Linux Foundation / AAIF).
32
+ */
33
+ generateAgentsMd(): Promise<string>;
34
+ /**
35
+ * Genera CLAUDE.md — Optimizado para Claude Code.
36
+ * Claude Code lee este archivo automáticamente del root del proyecto.
37
+ */
38
+ generateClaudeMd(): Promise<string>;
39
+ /**
40
+ * Genera GEMINI.md — Para Gemini CLI.
41
+ */
42
+ generateGeminiMd(): Promise<string>;
43
+ /**
44
+ * Genera .cursorrules — Instrucciones para Cursor IDE.
45
+ * Cursor lee este archivo del root del proyecto automáticamente.
46
+ */
47
+ generateCursorRules(): Promise<string>;
48
+ /**
49
+ * Genera copilot-instructions.md — Para GitHub Copilot.
50
+ * Se ubica en .github/copilot-instructions.md
51
+ */
52
+ generateCopilotInstructions(): Promise<string>;
53
+ /**
54
+ * Genera el export universal (.ccode/context-export.md).
55
+ */
56
+ generateUniversalExport(): Promise<string>;
57
+ /**
58
+ * Genera y escribe un formato específico.
59
+ */
60
+ exportFormat(format: string): Promise<string>;
61
+ /**
62
+ * Exporta todos los formatos de una vez.
63
+ * Retorna la lista de archivos generados.
64
+ */
65
+ exportAll(): Promise<Array<{
66
+ format: string;
67
+ file: string;
68
+ label: string;
69
+ }>>;
70
+ /**
71
+ * Verifica qué formatos ya existen en el proyecto.
72
+ */
73
+ detectExisting(): Promise<string[]>;
74
+ }