sinapse-ai 7.0.5 → 7.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (32) hide show
  1. package/.sinapse-ai/core-config.yaml +2 -26
  2. package/.sinapse-ai/data/entity-registry.yaml +759 -926
  3. package/.sinapse-ai/data/registry-update-log.jsonl +22 -0
  4. package/.sinapse-ai/infrastructure/scripts/ide-sync/index.js +1 -49
  5. package/.sinapse-ai/infrastructure/scripts/validate-parity.js +0 -7
  6. package/.sinapse-ai/install-manifest.yaml +11 -43
  7. package/README.en.md +6 -11
  8. package/README.md +6 -11
  9. package/bin/modules/env-config.js +1 -2
  10. package/bin/sinapse-init.js +23 -188
  11. package/docs/ide-integration.md +22 -263
  12. package/docs/installation/README.md +4 -6
  13. package/docs/installation/faq.md +10 -33
  14. package/docs/installation/linux.md +0 -23
  15. package/docs/installation/macos.md +0 -10
  16. package/docs/installation/troubleshooting.md +5 -9
  17. package/docs/installation/v4-quick-start.md +1 -1
  18. package/docs/installation/windows.md +0 -18
  19. package/package.json +1 -9
  20. package/packages/installer/src/config/ide-configs.js +3 -49
  21. package/.sinapse-ai/infrastructure/scripts/ide-sync/transformers/antigravity.js +0 -105
  22. package/.sinapse-ai/infrastructure/scripts/ide-sync/transformers/cursor.js +0 -94
  23. package/.sinapse-ai/infrastructure/scripts/ide-sync/transformers/github-copilot.js +0 -184
  24. package/.sinapse-ai/infrastructure/scripts/validate-gemini-integration.js +0 -151
  25. package/.sinapse-ai/product/templates/ide-rules/antigravity-rules.md +0 -115
  26. package/.sinapse-ai/product/templates/ide-rules/copilot-rules.md +0 -92
  27. package/.sinapse-ai/product/templates/ide-rules/cursor-rules.md +0 -115
  28. package/.sinapse-ai/product/templates/ide-rules/gemini-rules.md +0 -87
  29. package/docs/pt/platforms/antigravity.md +0 -508
  30. package/docs/pt/platforms/cursor.md +0 -633
  31. package/docs/pt/platforms/gemini-cli.md +0 -481
  32. package/docs/pt/platforms/github-copilot.md +0 -478
@@ -25,13 +25,9 @@ const path = require('path');
25
25
  /**
26
26
  * IDE Configuration Metadata
27
27
  *
28
- * SINAPSE v4 supports 6 main IDEs:
28
+ * SINAPSE v4 supports 2 main IDEs:
29
29
  * - Claude Code (Anthropic's official CLI) - Recommended
30
30
  * - Codex CLI (OpenAI coding CLI)
31
- * - Gemini CLI (Google AI coding CLI)
32
- * - Cursor (AI-first code editor)
33
- * - GitHub Copilot (GitHub's AI pair programmer)
34
- * - AntiGravity (Google agentic platform)
35
31
  */
36
32
  const IDE_CONFIGS = {
37
33
  'claude-code': {
@@ -47,55 +43,13 @@ const IDE_CONFIGS = {
47
43
  codex: {
48
44
  name: 'Codex CLI',
49
45
  description: '',
50
- configFile: 'AGENTS.md',
46
+ configFile: '.codex/instructions.md',
51
47
  template: 'ide-rules/codex-rules.md',
52
- requiresDirectory: false,
48
+ requiresDirectory: true,
53
49
  format: 'text',
54
50
  recommended: true,
55
51
  agentFolder: path.join('.codex', 'agents'),
56
52
  },
57
- gemini: {
58
- name: 'Gemini CLI',
59
- description: '',
60
- configFile: path.join('.gemini', 'rules.md'),
61
- template: 'ide-rules/gemini-rules.md',
62
- requiresDirectory: true,
63
- format: 'text',
64
- agentFolder: path.join('.gemini', 'rules', 'SINAPSE', 'agents'),
65
- },
66
- cursor: {
67
- name: 'Cursor',
68
- description: '',
69
- configFile: path.join('.cursor', 'rules.md'),
70
- template: 'ide-rules/cursor-rules.md',
71
- requiresDirectory: true,
72
- format: 'text',
73
- agentFolder: path.join('.cursor', 'rules'),
74
- },
75
- 'github-copilot': {
76
- name: 'GitHub Copilot',
77
- description: '',
78
- configFile: path.join('.github', 'copilot-instructions.md'),
79
- template: 'ide-rules/copilot-rules.md',
80
- requiresDirectory: true,
81
- format: 'text',
82
- agentFolder: path.join('.github', 'agents'),
83
- },
84
- antigravity: {
85
- name: 'AntiGravity',
86
- description: '',
87
- configFile: path.join('.antigravity', 'rules.md'),
88
- template: 'ide-rules/antigravity-rules.md',
89
- requiresDirectory: true,
90
- format: 'text',
91
- agentFolder: path.join('.agent', 'workflows'),
92
- specialConfig: {
93
- type: 'antigravity',
94
- configJsonPath: path.join('.antigravity', 'antigravity.json'),
95
- workflowsFolder: path.join('.agent', 'workflows'),
96
- agentsFolder: path.join('.antigravity', 'agents'),
97
- },
98
- },
99
53
  };
100
54
 
101
55
  /**
@@ -1,105 +0,0 @@
1
- /**
2
- * Antigravity Transformer - Cursor-style format
3
- * @story 6.19 - IDE Command Auto-Sync System
4
- *
5
- * Format: Similar to Cursor, condensed rules format
6
- * Target: .antigravity/rules/agents/*.md
7
- */
8
-
9
- const { getVisibleCommands, normalizeCommands } = require('../agent-parser');
10
-
11
- /**
12
- * Transform agent data to Antigravity format
13
- * @param {object} agentData - Parsed agent data from agent-parser
14
- * @returns {string} - Transformed content
15
- */
16
- function transform(agentData) {
17
- const agent = agentData.agent || {};
18
- const persona = agentData.persona_profile || {};
19
-
20
- const icon = agent.icon || '🤖';
21
- const name = agent.name || agentData.id;
22
- const title = agent.title || 'SINAPSE Agent';
23
- const whenToUse = agent.whenToUse || 'Use this agent for specific tasks';
24
- const archetype = persona.archetype || '';
25
-
26
- // Get quick visibility commands (normalized to consistent format)
27
- const allCommands = normalizeCommands(agentData.commands || []);
28
- const quickCommands = getVisibleCommands(allCommands, 'quick');
29
- const keyCommands = getVisibleCommands(allCommands, 'key');
30
-
31
- // Build content (similar to Cursor)
32
- let content = `# ${name} (@${agentData.id})
33
-
34
- ${icon} **${title}**${archetype ? ` | ${archetype}` : ''}
35
-
36
- > ${whenToUse}
37
-
38
- `;
39
-
40
- // Add quick commands section
41
- if (quickCommands.length > 0) {
42
- content += `## Quick Commands
43
-
44
- `;
45
- for (const cmd of quickCommands) {
46
- content += `- \`*${cmd.name}\` - ${cmd.description || 'No description'}\n`;
47
- }
48
- content += '\n';
49
- }
50
-
51
- // Add key commands if different from quick
52
- const keyOnlyCommands = keyCommands.filter(
53
- k => !quickCommands.some(q => q.name === k.name)
54
- );
55
- if (keyOnlyCommands.length > 0) {
56
- content += `## Key Commands
57
-
58
- `;
59
- for (const cmd of keyOnlyCommands) {
60
- content += `- \`*${cmd.name}\` - ${cmd.description || 'No description'}\n`;
61
- }
62
- content += '\n';
63
- }
64
-
65
- // Add all commands for reference (allCommands already normalized above)
66
- if (allCommands.length > quickCommands.length + keyOnlyCommands.length) {
67
- content += `## All Commands
68
-
69
- `;
70
- for (const cmd of allCommands) {
71
- content += `- \`*${cmd.name}\` - ${cmd.description || 'No description'}\n`;
72
- }
73
- content += '\n';
74
- }
75
-
76
- // Add collaboration section if available
77
- if (agentData.sections.collaboration) {
78
- content += `## Collaboration
79
-
80
- ${agentData.sections.collaboration}
81
-
82
- `;
83
- }
84
-
85
- content += `---
86
- *SINAPSE Agent - Synced from .sinapse-ai/development/agents/${agentData.filename}*
87
- `;
88
-
89
- return content;
90
- }
91
-
92
- /**
93
- * Get the target filename for this agent
94
- * @param {object} agentData - Parsed agent data
95
- * @returns {string} - Target filename
96
- */
97
- function getFilename(agentData) {
98
- return agentData.filename;
99
- }
100
-
101
- module.exports = {
102
- transform,
103
- getFilename,
104
- format: 'cursor-style',
105
- };
@@ -1,94 +0,0 @@
1
- /**
2
- * Cursor Transformer - Condensed rules format
3
- * @story 6.19 - IDE Command Auto-Sync System
4
- *
5
- * Format: Condensed markdown with icon, title, quick commands
6
- * Target: .cursor/rules/agents/*.md
7
- */
8
-
9
- const { getVisibleCommands, normalizeCommands } = require('../agent-parser');
10
-
11
- /**
12
- * Transform agent data to Cursor format
13
- * @param {object} agentData - Parsed agent data from agent-parser
14
- * @returns {string} - Transformed content
15
- */
16
- function transform(agentData) {
17
- const agent = agentData.agent || {};
18
- const persona = agentData.persona_profile || {};
19
-
20
- const icon = agent.icon || '🤖';
21
- const name = agent.name || agentData.id;
22
- const title = agent.title || 'SINAPSE Agent';
23
- const whenToUse = agent.whenToUse || 'Use this agent for specific tasks';
24
- const archetype = persona.archetype || '';
25
-
26
- // Get quick visibility commands (normalized to consistent format)
27
- const allCommands = normalizeCommands(agentData.commands || []);
28
- const quickCommands = getVisibleCommands(allCommands, 'quick');
29
- const keyCommands = getVisibleCommands(allCommands, 'key');
30
-
31
- // Build content
32
- let content = `# ${name} (@${agentData.id})
33
-
34
- ${icon} **${title}**${archetype ? ` | ${archetype}` : ''}
35
-
36
- > ${whenToUse}
37
-
38
- `;
39
-
40
- // Add quick commands section
41
- if (quickCommands.length > 0) {
42
- content += `## Quick Commands
43
-
44
- `;
45
- for (const cmd of quickCommands) {
46
- content += `- \`*${cmd.name}\` - ${cmd.description || 'No description'}\n`;
47
- }
48
- content += '\n';
49
- }
50
-
51
- // Add key commands if different from quick
52
- const keyOnlyCommands = keyCommands.filter(
53
- k => !quickCommands.some(q => q.name === k.name)
54
- );
55
- if (keyOnlyCommands.length > 0) {
56
- content += `## Key Commands
57
-
58
- `;
59
- for (const cmd of keyOnlyCommands) {
60
- content += `- \`*${cmd.name}\` - ${cmd.description || 'No description'}\n`;
61
- }
62
- content += '\n';
63
- }
64
-
65
- // Add collaboration section if available
66
- if (agentData.sections.collaboration) {
67
- content += `## Collaboration
68
-
69
- ${agentData.sections.collaboration}
70
-
71
- `;
72
- }
73
-
74
- content += `---
75
- *SINAPSE Agent - Synced from .sinapse-ai/development/agents/${agentData.filename}*
76
- `;
77
-
78
- return content;
79
- }
80
-
81
- /**
82
- * Get the target filename for this agent
83
- * @param {object} agentData - Parsed agent data
84
- * @returns {string} - Target filename
85
- */
86
- function getFilename(agentData) {
87
- return agentData.filename;
88
- }
89
-
90
- module.exports = {
91
- transform,
92
- getFilename,
93
- format: 'condensed-rules',
94
- };
@@ -1,184 +0,0 @@
1
- /**
2
- * GitHub Copilot Transformer - YAML frontmatter + condensed markdown
3
- * @story 6.19 - IDE Command Auto-Sync System
4
- * @issue #138 - Agent files not compatible with GitHub Copilot
5
- *
6
- * Format: .agent.md files with YAML frontmatter (--- delimiters)
7
- * Target: .github/agents/*.agent.md
8
- *
9
- * GitHub Copilot custom agents require:
10
- * - YAML frontmatter with `description` (required), `name`, `tools`
11
- * - Markdown body under 30,000 characters
12
- * - File extension: .agent.md
13
- *
14
- * @see https://docs.github.com/en/copilot/reference/custom-agents-configuration
15
- */
16
-
17
- const { normalizeCommands, getVisibleCommands } = require('../agent-parser');
18
-
19
- /**
20
- * Transform agent data to GitHub Copilot custom agent format
21
- * @param {object} agentData - Parsed agent data from agent-parser
22
- * @returns {string} - Transformed content with YAML frontmatter
23
- */
24
- function transform(agentData) {
25
- const agent = agentData.agent || {};
26
- const persona = agentData.persona_profile || {};
27
- const yamlData = agentData.yaml || {};
28
- const personaBlock = yamlData.persona || {};
29
-
30
- const id = agent.id || agentData.id;
31
- const name = agent.name || id;
32
- const title = agent.title || 'SINAPSE Agent';
33
- const icon = agent.icon || '';
34
- const description = escapeYamlString(agent.whenToUse || `${title} agent for development tasks`);
35
-
36
- // Build YAML frontmatter
37
- const frontmatter = [
38
- '---',
39
- `name: ${id}`,
40
- `description: '${description}'`,
41
- `tools: ['read', 'edit', 'search', 'execute']`,
42
- '---',
43
- ].join('\n');
44
-
45
- // Build markdown body
46
- const body = buildMarkdownBody({
47
- id,
48
- name,
49
- title,
50
- icon,
51
- personaBlock,
52
- persona,
53
- commands: agentData.commands || [],
54
- sections: agentData.sections || {},
55
- corePrinciples: yamlData.core_principles,
56
- filename: agentData.filename,
57
- });
58
-
59
- const content = `${frontmatter}\n\n${body}`;
60
-
61
- // Enforce 30K character limit
62
- if (content.length > 30000) {
63
- return truncateContent(content, 30000);
64
- }
65
-
66
- return content;
67
- }
68
-
69
- /**
70
- * Build the markdown body for the Copilot agent prompt
71
- * @param {object} params - Agent parameters
72
- * @returns {string} - Markdown body
73
- */
74
- function buildMarkdownBody(params) {
75
- const { id, name, title, icon, personaBlock, persona, commands, sections, filename } = params;
76
-
77
- const parts = [];
78
-
79
- // Header
80
- const headerIcon = icon ? `${icon} ` : '';
81
- parts.push(`# ${headerIcon}${name} Agent (@${id})\n`);
82
-
83
- // Role description
84
- if (personaBlock.role) {
85
- parts.push(`You are an expert ${personaBlock.role}.\n`);
86
- } else {
87
- parts.push(`You are an expert ${title}.\n`);
88
- }
89
-
90
- // Style
91
- if (personaBlock.style) {
92
- parts.push(`## Style\n\n${personaBlock.style}\n`);
93
- }
94
-
95
- // Core principles (may be in persona block or at root level of YAML)
96
- const corePrinciples = personaBlock.core_principles || params.corePrinciples;
97
- if (corePrinciples && Array.isArray(corePrinciples)) {
98
- parts.push('## Core Principles\n');
99
- for (const principle of corePrinciples) {
100
- // Handle both string and object formats (YAML may parse "KEY: value" as {KEY: value})
101
- if (typeof principle === 'string') {
102
- parts.push(`- ${principle}`);
103
- } else if (typeof principle === 'object' && principle !== null) {
104
- const entries = Object.entries(principle);
105
- for (const [key, value] of entries) {
106
- parts.push(`- ${key}: ${value}`);
107
- }
108
- }
109
- }
110
- parts.push('');
111
- }
112
-
113
- // Commands reference
114
- const allCommands = normalizeCommands(commands);
115
- const keyCommands = getVisibleCommands(allCommands, 'key');
116
- const quickCommands = getVisibleCommands(allCommands, 'quick');
117
- const displayCommands = keyCommands.length > 0 ? keyCommands : quickCommands.slice(0, 10);
118
-
119
- if (displayCommands.length > 0) {
120
- parts.push('## Commands\n');
121
- parts.push('Use `*` prefix for commands:\n');
122
- for (const cmd of displayCommands) {
123
- parts.push(`- \`*${cmd.name}\` - ${cmd.description || 'No description'}`);
124
- }
125
- parts.push('');
126
- }
127
-
128
- // Collaboration section (condensed)
129
- if (sections.collaboration) {
130
- parts.push(`## Collaboration\n\n${sections.collaboration}\n`);
131
- }
132
-
133
- // Sync footer
134
- parts.push('---');
135
- parts.push(`*SINAPSE Agent - Synced from .sinapse-ai/development/agents/${filename}*`);
136
- parts.push('');
137
-
138
- return parts.join('\n');
139
- }
140
-
141
- /**
142
- * Escape a string for use as a YAML single-quoted value
143
- * Single quotes inside the string must be doubled
144
- * @param {string} str - Input string
145
- * @returns {string} - Escaped string
146
- */
147
- function escapeYamlString(str) {
148
- if (!str) return '';
149
- // In YAML single-quoted strings, single quotes are escaped by doubling them
150
- return str.replace(/'/g, "''");
151
- }
152
-
153
- /**
154
- * Truncate content to fit within character limit while keeping structure valid
155
- * @param {string} content - Full content
156
- * @param {number} maxChars - Maximum characters
157
- * @returns {string} - Truncated content
158
- */
159
- function truncateContent(content, maxChars) {
160
- // Find the last complete section before the limit
161
- const truncated = content.substring(0, maxChars - 100);
162
- const lastNewline = truncated.lastIndexOf('\n\n');
163
-
164
- if (lastNewline > 0) {
165
- return truncated.substring(0, lastNewline) + '\n\n---\n*Content truncated to fit 30K limit*\n';
166
- }
167
- return truncated + '\n\n---\n*Content truncated to fit 30K limit*\n';
168
- }
169
-
170
- /**
171
- * Get the target filename for this agent (with .agent.md extension)
172
- * @param {object} agentData - Parsed agent data
173
- * @returns {string} - Target filename (e.g., "dev.agent.md")
174
- */
175
- function getFilename(agentData) {
176
- const id = (agentData.agent && agentData.agent.id) || agentData.id;
177
- return `${id}.agent.md`;
178
- }
179
-
180
- module.exports = {
181
- transform,
182
- getFilename,
183
- format: 'github-copilot',
184
- };
@@ -1,151 +0,0 @@
1
- #!/usr/bin/env node
2
- 'use strict';
3
-
4
- const fs = require('fs');
5
- const path = require('path');
6
-
7
- function getDefaultOptions() {
8
- const projectRoot = process.cwd();
9
- return {
10
- projectRoot,
11
- rulesFile: path.join(projectRoot, '.gemini', 'rules.md'),
12
- agentsDir: path.join(projectRoot, '.gemini', 'rules', 'SINAPSE', 'agents'),
13
- commandsDir: path.join(projectRoot, '.gemini', 'commands'),
14
- extensionDir: path.join(projectRoot, 'packages', 'gemini-sinapse-extension'),
15
- sourceAgentsDir: path.join(projectRoot, '.sinapse-ai', 'development', 'agents'),
16
- quiet: false,
17
- json: false,
18
- };
19
- }
20
-
21
- function parseArgs(argv = process.argv.slice(2)) {
22
- const args = new Set(argv);
23
- return {
24
- quiet: args.has('--quiet') || args.has('-q'),
25
- json: args.has('--json'),
26
- };
27
- }
28
-
29
- function countMarkdownFiles(dirPath) {
30
- if (!fs.existsSync(dirPath)) return 0;
31
- return fs.readdirSync(dirPath).filter((f) => f.endsWith('.md')).length;
32
- }
33
-
34
- function validateGeminiIntegration(options = {}) {
35
- const projectRoot = options.projectRoot || process.cwd();
36
- const resolved = {
37
- ...getDefaultOptions(),
38
- ...options,
39
- projectRoot,
40
- rulesFile: options.rulesFile || path.join(projectRoot, '.gemini', 'rules.md'),
41
- agentsDir: options.agentsDir || path.join(projectRoot, '.gemini', 'rules', 'SINAPSE', 'agents'),
42
- commandsDir: options.commandsDir || path.join(projectRoot, '.gemini', 'commands'),
43
- extensionDir: options.extensionDir || path.join(projectRoot, 'packages', 'gemini-sinapse-extension'),
44
- sourceAgentsDir: options.sourceAgentsDir || path.join(projectRoot, '.sinapse-ai', 'development', 'agents'),
45
- };
46
- const errors = [];
47
- const warnings = [];
48
-
49
- if (!fs.existsSync(resolved.rulesFile)) {
50
- warnings.push(`Gemini rules file not found yet: ${path.relative(resolved.projectRoot, resolved.rulesFile)}`);
51
- }
52
-
53
- if (!fs.existsSync(resolved.agentsDir)) {
54
- errors.push(`Missing Gemini agents dir: ${path.relative(resolved.projectRoot, resolved.agentsDir)}`);
55
- }
56
- if (!fs.existsSync(resolved.commandsDir)) {
57
- errors.push(`Missing Gemini commands dir: ${path.relative(resolved.projectRoot, resolved.commandsDir)}`);
58
- }
59
-
60
- const sourceCount = countMarkdownFiles(resolved.sourceAgentsDir);
61
- const geminiCount = countMarkdownFiles(resolved.agentsDir);
62
- const commandFiles = fs.existsSync(resolved.commandsDir)
63
- ? fs.readdirSync(resolved.commandsDir).filter((f) => f.endsWith('.toml'))
64
- : [];
65
- const expectedCommandCount = sourceCount > 0 ? sourceCount + 1 : 0;
66
-
67
- if (sourceCount > 0 && commandFiles.length !== expectedCommandCount) {
68
- warnings.push(`Gemini command count differs from source (${commandFiles.length}/${expectedCommandCount})`);
69
- }
70
- if (!commandFiles.includes('sinapse-menu.toml')) {
71
- errors.push(`Missing Gemini command file: ${path.relative(resolved.projectRoot, path.join(resolved.commandsDir, 'sinapse-menu.toml'))}`);
72
- }
73
- if (sourceCount > 0 && geminiCount !== sourceCount) {
74
- warnings.push(`Gemini agent count differs from source (${geminiCount}/${sourceCount})`);
75
- }
76
-
77
- const requiredExtensionFiles = [
78
- 'extension.json',
79
- 'README.md',
80
- path.join('commands', 'sinapse-status.js'),
81
- path.join('commands', 'sinapse-agents.js'),
82
- path.join('commands', 'sinapse-validate.js'),
83
- path.join('hooks', 'hooks.json'),
84
- ];
85
-
86
- for (const rel of requiredExtensionFiles) {
87
- const abs = path.join(resolved.extensionDir, rel);
88
- if (!fs.existsSync(abs)) {
89
- errors.push(`Missing Gemini extension file: ${path.relative(resolved.projectRoot, abs)}`);
90
- }
91
- }
92
-
93
- return {
94
- ok: errors.length === 0,
95
- errors,
96
- warnings,
97
- metrics: {
98
- sourceAgents: sourceCount,
99
- geminiAgents: geminiCount,
100
- geminiCommands: commandFiles.length,
101
- },
102
- };
103
- }
104
-
105
- function formatHumanReport(result) {
106
- if (result.ok) {
107
- const lines = [
108
- `✅ Gemini integration validation passed (agents: ${result.metrics.geminiAgents}, commands: ${result.metrics.geminiCommands})`,
109
- ];
110
- if (result.warnings.length > 0) {
111
- lines.push(...result.warnings.map((w) => `⚠️ ${w}`));
112
- }
113
- return lines.join('\n');
114
- }
115
- const lines = [
116
- `❌ Gemini integration validation failed (${result.errors.length} issue(s))`,
117
- ...result.errors.map((e) => `- ${e}`),
118
- ];
119
- if (result.warnings.length > 0) {
120
- lines.push(...result.warnings.map((w) => `⚠️ ${w}`));
121
- }
122
- return lines.join('\n');
123
- }
124
-
125
- function main() {
126
- const args = parseArgs();
127
- const result = validateGeminiIntegration(args);
128
-
129
- if (!args.quiet) {
130
- if (args.json) {
131
- console.log(JSON.stringify(result, null, 2));
132
- } else {
133
- console.log(formatHumanReport(result));
134
- }
135
- }
136
-
137
- if (!result.ok) {
138
- process.exitCode = 1;
139
- }
140
- }
141
-
142
- if (require.main === module) {
143
- main();
144
- }
145
-
146
- module.exports = {
147
- validateGeminiIntegration,
148
- parseArgs,
149
- getDefaultOptions,
150
- countMarkdownFiles,
151
- };