Skip to content

Commit 6f50f09

Browse files
author
JosXa
committed
Add Warp support and merge with OpenCode support
- Merge opencode and warp format support from rebase - Add project documentation (.agent/guidelines.md, AGENTS.md) - Update .gitignore to exclude exported AI rule files - Fix path normalization in Amazon Q importer - Improve Warp test coverage for private rules - Update README to avoid hardcoding format count
1 parent 005c192 commit 6f50f09

7 files changed

Lines changed: 122 additions & 30 deletions

File tree

.agent/guidelines.md

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
---
2+
alwaysApply: true
3+
---
4+
## Project Overview
5+
6+
dotagent is a multi-file AI agent configuration manager that maintains a single source of truth for AI coding assistant rules across multiple IDEs and tools (including VS Code Copilot, Cursor, Claude Code, OpenCode, Windsurf, and more). It converts between a unified `.agent/` directory format and tool-specific formats, supporting import/export operations, nested folders, private rules, and both CLI and TypeScript API usage.
7+
8+
## Package Manager
9+
10+
This project uses **pnpm** for dependency management.
11+
12+
## Available Scripts
13+
14+
```bash
15+
# Install dependencies
16+
pnpm install
17+
18+
# Build the project
19+
pnpm build
20+
21+
# Development mode (watch)
22+
pnpm dev
23+
24+
# Run tests (recommended for AI agents - non-interactive)
25+
pnpm test:ci
26+
27+
# Type checking
28+
pnpm typecheck
29+
```

.gitignore

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,4 +9,19 @@ coverage/
99
*.tsbuildinfo
1010
.vitest/
1111
.cursor
12-
.claude
12+
.claude
13+
# Added by dotagent: ignore exported AI rule files
14+
.github/copilot-instructions.md
15+
.cursor/rules/**
16+
.clinerules
17+
.windsurfrules
18+
.rules
19+
AGENTS.md
20+
CONVENTIONS.md
21+
CLAUDE.md
22+
GEMINI.md
23+
best_practices.md
24+
.amazonq/
25+
.junie/
26+
.roo/
27+
WARP.md

AGENTS.md

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
## Project Overview
2+
3+
dotagent is a multi-file AI agent configuration manager that maintains a single source of truth for AI coding assistant rules across multiple IDEs and tools (including VS Code Copilot, Cursor, Claude Code, OpenCode, Windsurf, and more). It converts between a unified `.agent/` directory format and tool-specific formats, supporting import/export operations, nested folders, private rules, and both CLI and TypeScript API usage.
4+
5+
## Package Manager
6+
7+
This project uses **pnpm** for dependency management.
8+
9+
## Available Scripts
10+
11+
```bash
12+
# Install dependencies
13+
pnpm install
14+
15+
# Build the project
16+
pnpm build
17+
18+
# Development mode (watch)
19+
pnpm dev
20+
21+
# Run tests (recommended for AI agents - non-interactive)
22+
pnpm test:ci
23+
24+
# Type checking
25+
pnpm typecheck
26+
```

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# dotagent
22

3-
Multi-file AI agent configuration manager with .agent directory support. Maintain a single source of truth for AI coding assistant rules across Claude Code, VS Code Copilot, Cursor, Cline, Windsurf, Zed, Amazon Q Developer, and more.
3+
Multi-file AI agent configuration manager with .agent directory support. Maintain a single source of truth for AI coding assistant rules across multiple IDEs and tools including VS Code Copilot, Cursor, Claude Code, OpenCode, and more.
44

55
## Features
66

src/cli.ts

Lines changed: 26 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,7 @@
33
import { existsSync, readFileSync, writeFileSync, appendFileSync, rmSync } from 'fs'
44
import { join, resolve, dirname } from 'path'
55
import { parseArgs } from 'util'
6-
<<<<<<< HEAD
7-
import { importAll, importAgent, exportToAgent, exportAll, exportToCopilot, exportToCursor, exportToCline, exportToWindsurf, exportToZed, exportToCodex, exportToAider, exportToClaudeCode, exportToGemini, exportToQodo, importRoo, exportToRoo, exportToJunie, importOpenCode, exportToOpenCode } from './index.js'
8-
=======
9-
import { importAll, importAgent, exportToAgent, exportAll, exportToCopilot, exportToCursor, exportToCline, exportToWindsurf, exportToZed, exportToCodex, exportToAider, exportToClaudeCode, exportToGemini, exportToQodo, importRoo, exportToRoo, exportToJunie, importWarp, exportToWarp } from './index.js'
10-
>>>>>>> d5e19dd (feat: Add Warp.dev terminal support)
6+
import { importAll, importAgent, exportToAgent, exportAll, exportToCopilot, exportToCursor, exportToCline, exportToWindsurf, exportToZed, exportToCodex, exportToAider, exportToClaudeCode, exportToGemini, exportToQodo, importRoo, exportToRoo, exportToJunie, importOpenCode, exportToOpenCode, importWarp, exportToWarp } from './index.js'
117
import { color, header, formatList } from './utils/colors.js'
128
import { select, confirm } from './utils/prompt.js'
139

@@ -46,11 +42,7 @@ ${color.bold('Usage:')}
4642
${color.bold('Options:')}
4743
${color.yellow('-h, --help')} Show this help message
4844
${color.yellow('-o, --output')} Output file path (for convert command)
49-
<<<<<<< HEAD
50-
${color.yellow('-f, --format')} Specify format (copilot|cursor|cline|windsurf|zed|codex|aider|claude|gemini|qodo|roo|junie|opencode)
51-
=======
52-
${color.yellow('-f, --format')} Specify format (copilot|cursor|cline|windsurf|zed|codex|aider|claude|gemini|qodo|roo|junie|warp)
53-
>>>>>>> d5e19dd (feat: Add Warp.dev terminal support)
45+
${color.yellow('-f, --format')} Specify format (copilot|cursor|cline|windsurf|zed|codex|aider|claude|gemini|qodo|roo|junie|opencode|warp)
5446
${color.yellow('--formats')} Specify multiple formats (comma-separated)
5547
${color.yellow('-w, --overwrite')} Overwrite existing files
5648
${color.yellow('-d, --dry-run')} Preview operations without making changes
@@ -216,6 +208,9 @@ async function main() {
216208
} else if (values.format) {
217209
// Single format from -f flag
218210
selectedFormats = [values.format]
211+
} else if (isDryRun) {
212+
// In dry-run mode, default to 'all' to avoid hanging on prompt
213+
selectedFormats = ['all']
219214
} else {
220215
// Interactive menu
221216
console.log()
@@ -224,11 +219,7 @@ async function main() {
224219
}
225220

226221
// Validate formats
227-
<<<<<<< HEAD
228-
const validFormats = ['all', 'copilot', 'cursor', 'cline', 'windsurf', 'zed', 'codex', 'aider', 'claude', 'gemini', 'qodo', 'roo', 'junie', 'opencode']
229-
=======
230-
const validFormats = ['all', 'copilot', 'cursor', 'cline', 'windsurf', 'zed', 'codex', 'aider', 'claude', 'gemini', 'qodo', 'roo', 'junie', 'warp']
231-
>>>>>>> d5e19dd (feat: Add Warp.dev terminal support)
222+
const validFormats = ['all', 'copilot', 'cursor', 'cline', 'windsurf', 'zed', 'codex', 'aider', 'claude', 'gemini', 'qodo', 'roo', 'junie', 'opencode', 'warp']
232223
const invalidFormats = selectedFormats.filter(f => !validFormats.includes(f))
233224
if (invalidFormats.length > 0) {
234225
console.error(color.error(`Invalid format(s): ${invalidFormats.join(', ')}`))
@@ -248,8 +239,21 @@ async function main() {
248239
if (selectedFormat === 'all') {
249240
if (!isDryRun) {
250241
exportAll(rules, outputDir, false, options)
242+
console.log(color.success('Exported to all formats'))
243+
} else {
244+
console.log(color.info('Would export to:'))
245+
console.log(color.dim(' - .github/copilot-instructions.md'))
246+
console.log(color.dim(' - .cursor/rules/'))
247+
console.log(color.dim(' - .clinerules'))
248+
console.log(color.dim(' - .windsurfrules'))
249+
console.log(color.dim(' - .rules'))
250+
console.log(color.dim(' - AGENTS.md'))
251+
console.log(color.dim(' - CONVENTIONS.md'))
252+
console.log(color.dim(' - CLAUDE.md'))
253+
console.log(color.dim(' - GEMINI.md'))
254+
console.log(color.dim(' - best_practices.md'))
255+
console.log(color.dim(' - WARP.md'))
251256
}
252-
console.log(color.success('Exported to all formats'))
253257
exportedPaths.push(
254258
'.github/copilot-instructions.md',
255259
'.cursor/rules/',
@@ -341,7 +345,11 @@ async function main() {
341345
}
342346

343347
if (exportPath) {
344-
console.log(color.success(`Exported to: ${color.path(exportPath)}`))
348+
if (isDryRun) {
349+
console.log(color.info(`Would export to: ${color.path(exportPath)}`))
350+
} else {
351+
console.log(color.success(`Exported to: ${color.path(exportPath)}`))
352+
}
345353
}
346354
}
347355
}
@@ -409,11 +417,7 @@ async function main() {
409417
else if (inputPath.endsWith('WARP.md')) format = 'warp'
410418
else {
411419
console.error(color.error('Cannot auto-detect format'))
412-
<<<<<<< HEAD
413-
console.error(color.dim('Hint: Specify format with -f (copilot|cursor|cline|windsurf|zed|codex|aider|claude|gemini|qodo|roo|opencode)'))
414-
=======
415-
console.error(color.dim('Hint: Specify format with -f (copilot|cursor|cline|windsurf|zed|codex|aider|claude|gemini|qodo|roo|warp)'))
416-
>>>>>>> d5e19dd (feat: Add Warp.dev terminal support)
420+
console.error(color.dim('Hint: Specify format with -f (copilot|cursor|cline|windsurf|zed|codex|aider|claude|gemini|qodo|roo|opencode|warp)'))
417421
process.exit(1)
418422
}
419423
}

src/importers.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -797,10 +797,13 @@ export function importAmazonQ(rulesDir: string): ImportResult {
797797
if (segments[0] === 'private') segments = segments.slice(1)
798798
const defaultId = segments.join('/')
799799

800+
// Normalize path for description (use forward slashes)
801+
const normalizedRelPath = relPath.replace(/\\/g, '/')
802+
800803
const metadata: any = {
801804
id: `amazonq-${defaultId}`,
802805
alwaysApply: true,
803-
description: `Amazon Q rules from ${relPath}`
806+
description: `Amazon Q rules from ${normalizedRelPath}`
804807
}
805808

806809
if (isPrivateFile) {

test/warp.test.ts

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -156,11 +156,23 @@ npm run build # Build for production
156156
}
157157
})
158158

159-
it('should export to WARP.local.md format', () => {
160-
const tempDir = mkdtempSync(join(tmpdir(), 'warp-local-export-'))
161-
const warpLocalPath = join(tempDir, 'WARP.local.md')
159+
it('should export private rules when includePrivate option is set', () => {
160+
const tempDir = mkdtempSync(join(tmpdir(), 'warp-private-export-'))
161+
const warpPath = join(tempDir, 'WARP.md')
162162

163163
const rules: RuleBlock[] = [
164+
{
165+
metadata: {
166+
id: 'public-config',
167+
description: 'Public Configuration',
168+
alwaysApply: true,
169+
private: false
170+
},
171+
content: `## Public Environment
172+
173+
Production server runs on port 443.
174+
Database connection uses production credentials.`
175+
},
164176
{
165177
metadata: {
166178
id: 'private-config',
@@ -176,12 +188,15 @@ Database connection uses local credentials.`
176188
]
177189

178190
try {
179-
exportToWarp(rules, warpLocalPath, { includePrivate: true })
191+
// Export with private rules included
192+
exportToWarp(rules, warpPath, { includePrivate: true })
180193

181-
const exported = readFileSync(warpLocalPath, 'utf8')
194+
const exported = readFileSync(warpPath, 'utf8')
182195

196+
expect(exported).toContain('# Public Configuration')
183197
expect(exported).toContain('# Private Configuration')
184198
expect(exported).toContain('Development server runs on port 8000')
199+
expect(exported).toContain('Production server runs on port 443')
185200
} finally {
186201
rmSync(tempDir, { recursive: true, force: true })
187202
}

0 commit comments

Comments
 (0)