Skip to content

Commit 5d8fef1

Browse files
committed
refactor: address CodeRabbit review feedback
1 parent 50aef86 commit 5d8fef1

4 files changed

Lines changed: 32 additions & 46 deletions

File tree

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ Multi-file AI agent configuration manager with .agent directory support. Maintai
2626
| Windsurf | `.windsurfrules` | Plain Markdown | windsurf |
2727
| Zed | `.rules` | Plain Markdown | zed |
2828
| OpenAI Codex | `AGENTS.md` | Plain Markdown | codex |
29-
| OpenCode | `AGENTS.md` | Plain Markdown | opencode |
29+
| OpenCode | `AGENTS.md` | Plain Markdown | opencode |
3030
| Aider | `CONVENTIONS.md` | Plain Markdown | aider |
3131
| Gemini | `GEMINI.md` | Plain Markdown | gemini |
3232
| Qodo | `best_practices.md` | Plain Markdown | qodo |
@@ -219,7 +219,7 @@ Confidential requirements
219219
| Windsurf | `.windsurfrules` | `.windsurfrules.local` |
220220
| Zed | `.rules` | `.rules.local` |
221221
| Claude | `CLAUDE.md` | `CLAUDE.local.md` |
222-
| OpenCode | `AGENTS.md` | `AGENTS.local.md` |
222+
| OpenCode | `AGENTS.md` | `AGENTS.local.md` |
223223
| Gemini | `GEMINI.md` | `GEMINI.local.md` |
224224
| Junie | `.junie/guidelines.md` | `.junie/guidelines.local.md` |
225225
| Roo Code | `.roo/rules/*.md` | `.roo/rules/*.local.md` |

src/exporters.ts

Lines changed: 12 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -337,8 +337,11 @@ export function exportToZed(rules: RuleBlock[], outputPath: string, options?: Ex
337337
writeFileSync(outputPath, fullContent, 'utf-8')
338338
}
339339

340-
export function exportToCodex(rules: RuleBlock[], outputPath: string, options?: ExportOptions): void {
341-
// Filter out private rules unless includePrivate is true
340+
function exportSingleFileWithHeaders(
341+
rules: RuleBlock[],
342+
outputPath: string,
343+
options?: ExportOptions
344+
): void {
342345
const filteredRules = rules.filter(rule => !rule.metadata.private || options?.includePrivate)
343346

344347
const alwaysApplyRules = filteredRules.filter(r => r.metadata.alwaysApply !== false)
@@ -359,6 +362,10 @@ export function exportToCodex(rules: RuleBlock[], outputPath: string, options?:
359362
writeFileSync(outputPath, fullContent, 'utf-8')
360363
}
361364

365+
export function exportToCodex(rules: RuleBlock[], outputPath: string, options?: ExportOptions): void {
366+
exportSingleFileWithHeaders(rules, outputPath, options)
367+
}
368+
362369
export function exportToAider(rules: RuleBlock[], outputPath: string, options?: ExportOptions): void {
363370
// Filter out private rules unless includePrivate is true
364371
const filteredRules = rules.filter(rule => !rule.metadata.private || options?.includePrivate)
@@ -379,47 +386,11 @@ export function exportToAider(rules: RuleBlock[], outputPath: string, options?:
379386
}
380387

381388
export function exportToClaudeCode(rules: RuleBlock[], outputPath: string, options?: ExportOptions): void {
382-
// Filter out private rules unless includePrivate is true
383-
const filteredRules = rules.filter(rule => !rule.metadata.private || options?.includePrivate)
384-
385-
const alwaysApplyRules = filteredRules.filter(r => r.metadata.alwaysApply !== false)
386-
const conditionalSection = generateConditionalRulesSection(filteredRules, dirname(outputPath))
387-
388-
const mainContent = alwaysApplyRules
389-
.map(rule => {
390-
const header = rule.metadata.description ? `# ${rule.metadata.description}\n\n` : ''
391-
return header + rule.content
392-
})
393-
.join('\n\n')
394-
395-
const fullContent = conditionalSection
396-
? `${mainContent}\n\n${conditionalSection}`
397-
: mainContent
398-
399-
ensureDirectoryExists(outputPath)
400-
writeFileSync(outputPath, fullContent, 'utf-8')
389+
exportSingleFileWithHeaders(rules, outputPath, options)
401390
}
402391

403392
export function exportToOpenCode(rules: RuleBlock[], outputPath: string, options?: ExportOptions): void {
404-
// Filter out private rules unless includePrivate is true
405-
const filteredRules = rules.filter(rule => !rule.metadata.private || options?.includePrivate)
406-
407-
const alwaysApplyRules = filteredRules.filter(r => r.metadata.alwaysApply !== false)
408-
const conditionalSection = generateConditionalRulesSection(filteredRules, dirname(outputPath))
409-
410-
const mainContent = alwaysApplyRules
411-
.map(rule => {
412-
const header = rule.metadata.description ? `# ${rule.metadata.description}\n\n` : ''
413-
return header + rule.content
414-
})
415-
.join('\n\n')
416-
417-
const fullContent = conditionalSection
418-
? `${mainContent}\n\n${conditionalSection}`
419-
: mainContent
420-
421-
ensureDirectoryExists(outputPath)
422-
writeFileSync(outputPath, fullContent, 'utf-8')
393+
exportSingleFileWithHeaders(rules, outputPath, options)
423394
}
424395

425396
export function exportToGemini(rules: RuleBlock[], outputPath: string, options?: ExportOptions): void {
@@ -580,7 +551,7 @@ export function exportAll(rules: RuleBlock[], repoPath: string, dryRun = false,
580551
exportToCodex(rules, join(repoPath, 'AGENTS.md'), options)
581552
exportToAider(rules, join(repoPath, 'CONVENTIONS.md'), options)
582553
exportToClaudeCode(rules, join(repoPath, 'CLAUDE.md'), options)
583-
exportToOpenCode(rules, join(repoPath, 'AGENTS.opencode.md'), options)
554+
exportToOpenCode(rules, join(repoPath, 'AGENTS.md'), options)
584555
exportToGemini(rules, join(repoPath, 'GEMINI.md'), options)
585556
exportToQodo(rules, join(repoPath, 'best_practices.md'), options)
586557
exportToAmazonQ(rules, repoPath, options)

src/importers.ts

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -154,7 +154,17 @@ export async function importAll(repoPath: string): Promise<ImportResults> {
154154
}
155155
}
156156

157-
// Check for AGENTS.md (OpenCode) - Note: This conflicts with OpenAI Codex,
157+
// Check for AGENTS.md (OpenCode)
158+
const opencodeMd = join(repoPath, 'AGENTS.md')
159+
if (existsSync(opencodeMd)) {
160+
try {
161+
results.push(importOpenCode(opencodeMd))
162+
} catch (e) {
163+
errors.push({ file: opencodeMd, error: String(e) })
164+
}
165+
}
166+
167+
// Check for AGENTS.md (OpenAI Codex) - Note: This conflicts with OpenCode,
158168
// so we need to handle this carefully. For now, we'll prioritize OpenAI Codex
159169
// since it was implemented first, but this could be made configurable.
160170
// Users can explicitly specify the format using the CLI if needed.

test/opencode.test.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -121,8 +121,8 @@ Use Node.js 20+ and pnpm for package management.
121121

122122
const exported = readFileSync(agentsPath, 'utf8')
123123

124-
// Should not have a header if no description
125-
expect(exported).not.toContain('#')
124+
// Should not have a generated header if no description
125+
expect(exported.split('\n')[0]).not.toMatch(/^\s#/)
126126
expect(exported.trim()).toBe('Always use TypeScript')
127127
} finally {
128128
rmSync(tempDir, { recursive: true, force: true })
@@ -168,6 +168,11 @@ src/
168168
expect(exported).toContain('# OpenCode agents and instructions')
169169
expect(exported).toContain('Always validate inputs')
170170
expect(exported).toContain('Clean code is better than clever code')
171+
172+
// Verify Markdown structures are preserved
173+
expect(exported).toMatch(/```[\s\S]*?```/) // Code blocks
174+
expect(exported).toMatch(/^\d+\.\s/m) // Ordered lists
175+
expect(exported).toMatch(/^>\s/m) // Blockquotes
171176
} finally {
172177
rmSync(tempDir, { recursive: true, force: true })
173178
}

0 commit comments

Comments
 (0)