prjct-cli 0.64.0 → 0.64.1

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/CHANGELOG.md CHANGED
@@ -1,5 +1,43 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.64.1] - 2026-02-05
4
+
5
+ ### Bug Fixes
6
+
7
+ - generate IDE context files with correct paths and formats (PRJ-122) (#107)
8
+
9
+
10
+ ## [0.64.1] - 2026-02-05
11
+
12
+ ### Bug Fixes
13
+
14
+ - **IDE context file generation (PRJ-122)**: Fixed Cursor and Windsurf context file paths and formats
15
+
16
+ ### Implementation Details
17
+
18
+ Fixed misalignment between `ai-provider.ts` (correct paths) and `ai-tools/registry.ts` (incorrect paths). Updated Cursor output to `.cursor/rules/prjct.mdc` with MDC format and `alwaysApply: true` frontmatter. Updated Windsurf output to `.windsurf/rules/prjct.md` with `trigger: always_on` frontmatter. Modified sync-service default behavior to auto-detect IDE tools when `.cursor/` or `.windsurf/` directories exist.
19
+
20
+ ### Learnings
21
+
22
+ - Two separate implementations existed (ai-provider vs ai-tools) that needed synchronization
23
+ - IDE formats differ: Cursor uses `alwaysApply: true`, Windsurf uses `trigger: always_on`
24
+ - Auto-detection pattern: check for directory existence to enable optional features
25
+
26
+ ### Test Plan
27
+
28
+ #### For QA
29
+ 1. Run `prjct sync` in project with `.cursor/` dir - verify `.cursor/rules/prjct.mdc` generated
30
+ 2. Run `prjct sync` in project with `.windsurf/` dir - verify `.windsurf/rules/prjct.md` generated
31
+ 3. Verify Cursor file has `alwaysApply: true` in frontmatter
32
+ 4. Verify Windsurf file has `trigger: always_on` in frontmatter
33
+ 5. Run `bun test` - all 405 tests pass
34
+
35
+ #### For Users
36
+ - `prjct sync` now auto-generates IDE context files when `.cursor/` or `.windsurf/` directories exist
37
+ - No manual flags needed - detection is automatic
38
+ - No breaking changes
39
+
40
+
3
41
  ## [0.64.0] - 2026-02-05
4
42
 
5
43
  ### Features
@@ -0,0 +1,358 @@
1
+ /**
2
+ * Tests for AI Tools Formatters
3
+ *
4
+ * @see PRJ-122
5
+ */
6
+
7
+ import { describe, expect, test } from 'bun:test'
8
+ import {
9
+ formatForClaude,
10
+ formatForContinue,
11
+ formatForCopilot,
12
+ formatForCursor,
13
+ formatForWindsurf,
14
+ getFormatter,
15
+ type ProjectContext,
16
+ } from '../../ai-tools/formatters'
17
+ import { AI_TOOLS, getAIToolConfig } from '../../ai-tools/registry'
18
+
19
+ // =============================================================================
20
+ // Test Fixtures
21
+ // =============================================================================
22
+
23
+ const mockContext: ProjectContext = {
24
+ projectId: 'test-project-id',
25
+ name: 'test-project',
26
+ version: '1.0.0',
27
+ ecosystem: 'Node.js',
28
+ projectType: 'library',
29
+ languages: ['TypeScript'],
30
+ frameworks: ['Hono'],
31
+ repoPath: '/Users/test/project',
32
+ branch: 'main',
33
+ fileCount: 100,
34
+ commits: 50,
35
+ hasChanges: false,
36
+ commands: {
37
+ install: 'bun install',
38
+ dev: 'bun run dev',
39
+ test: 'bun test',
40
+ build: 'bun run build',
41
+ lint: 'bun run lint',
42
+ format: 'bun run format',
43
+ },
44
+ agents: {
45
+ workflow: ['prjct-planner', 'prjct-shipper'],
46
+ domain: ['backend'],
47
+ },
48
+ }
49
+
50
+ // =============================================================================
51
+ // Registry Tests
52
+ // =============================================================================
53
+
54
+ describe('AI Tools Registry', () => {
55
+ test('cursor uses correct output file path', () => {
56
+ expect(AI_TOOLS.cursor.outputFile).toBe('.cursor/rules/prjct.mdc')
57
+ })
58
+
59
+ test('windsurf uses correct output file path', () => {
60
+ expect(AI_TOOLS.windsurf.outputFile).toBe('.windsurf/rules/prjct.md')
61
+ })
62
+
63
+ test('copilot uses correct output file path', () => {
64
+ expect(AI_TOOLS.copilot.outputFile).toBe('.github/copilot-instructions.md')
65
+ })
66
+
67
+ test('claude uses correct output file path', () => {
68
+ expect(AI_TOOLS.claude.outputFile).toBe('CLAUDE.md')
69
+ })
70
+
71
+ test('continue uses correct output file path', () => {
72
+ expect(AI_TOOLS.continue.outputFile).toBe('.continue/config.json')
73
+ })
74
+
75
+ test('getAIToolConfig returns config for valid tool', () => {
76
+ const config = getAIToolConfig('cursor')
77
+ expect(config).not.toBeNull()
78
+ expect(config?.id).toBe('cursor')
79
+ })
80
+
81
+ test('getAIToolConfig returns null for invalid tool', () => {
82
+ const config = getAIToolConfig('invalid-tool')
83
+ expect(config).toBeNull()
84
+ })
85
+ })
86
+
87
+ // =============================================================================
88
+ // Formatter Tests
89
+ // =============================================================================
90
+
91
+ describe('formatForCursor', () => {
92
+ const config = AI_TOOLS.cursor
93
+
94
+ test('generates MDC format with YAML frontmatter', () => {
95
+ const result = formatForCursor(mockContext, config)
96
+
97
+ // Should start with YAML frontmatter
98
+ expect(result.startsWith('---\n')).toBe(true)
99
+ expect(result).toContain('description: prjct context for test-project')
100
+ expect(result).toContain('alwaysApply: true')
101
+ expect(result).toContain('---')
102
+ })
103
+
104
+ test('includes project info', () => {
105
+ const result = formatForCursor(mockContext, config)
106
+
107
+ expect(result).toContain('test-project')
108
+ expect(result).toContain('library')
109
+ expect(result).toContain('Node.js')
110
+ })
111
+
112
+ test('includes tech stack', () => {
113
+ const result = formatForCursor(mockContext, config)
114
+
115
+ expect(result).toContain('TypeScript')
116
+ expect(result).toContain('Hono')
117
+ })
118
+
119
+ test('includes commands', () => {
120
+ const result = formatForCursor(mockContext, config)
121
+
122
+ expect(result).toContain('bun install')
123
+ expect(result).toContain('bun run dev')
124
+ expect(result).toContain('bun test')
125
+ expect(result).toContain('bun run build')
126
+ })
127
+ })
128
+
129
+ describe('formatForWindsurf', () => {
130
+ const config = AI_TOOLS.windsurf
131
+
132
+ test('generates MD format with YAML frontmatter', () => {
133
+ const result = formatForWindsurf(mockContext, config)
134
+
135
+ // Should start with YAML frontmatter
136
+ expect(result.startsWith('---\n')).toBe(true)
137
+ expect(result).toContain('description: prjct context for test-project')
138
+ expect(result).toContain('trigger: always_on')
139
+ expect(result).toContain('---')
140
+ })
141
+
142
+ test('uses trigger: always_on (not alwaysApply)', () => {
143
+ const result = formatForWindsurf(mockContext, config)
144
+
145
+ expect(result).toContain('trigger: always_on')
146
+ expect(result).not.toContain('alwaysApply')
147
+ })
148
+
149
+ test('includes project info', () => {
150
+ const result = formatForWindsurf(mockContext, config)
151
+
152
+ expect(result).toContain('test-project')
153
+ expect(result).toContain('library')
154
+ expect(result).toContain('Node.js')
155
+ })
156
+
157
+ test('includes commands in bash block', () => {
158
+ const result = formatForWindsurf(mockContext, config)
159
+
160
+ expect(result).toContain('```bash')
161
+ expect(result).toContain('bun install')
162
+ expect(result).toContain('bun run dev')
163
+ expect(result).toContain('bun test')
164
+ expect(result).toContain('bun run build')
165
+ expect(result).toContain('```')
166
+ })
167
+ })
168
+
169
+ describe('formatForCopilot', () => {
170
+ const config = AI_TOOLS.copilot
171
+
172
+ test('generates minimal format without frontmatter', () => {
173
+ const result = formatForCopilot(mockContext, config)
174
+
175
+ // Copilot uses plain markdown, no YAML frontmatter
176
+ expect(result.startsWith('# Copilot Instructions')).toBe(true)
177
+ expect(result).not.toContain('---')
178
+ })
179
+
180
+ test('includes project info', () => {
181
+ const result = formatForCopilot(mockContext, config)
182
+
183
+ expect(result).toContain('test-project')
184
+ expect(result).toContain('library')
185
+ expect(result).toContain('Node.js')
186
+ })
187
+
188
+ test('includes essential commands only', () => {
189
+ const result = formatForCopilot(mockContext, config)
190
+
191
+ expect(result).toContain('bun test')
192
+ expect(result).toContain('bun run build')
193
+ })
194
+ })
195
+
196
+ describe('formatForClaude', () => {
197
+ const config = AI_TOOLS.claude
198
+
199
+ test('generates detailed markdown format', () => {
200
+ const result = formatForClaude(mockContext, config)
201
+
202
+ expect(result).toContain('# test-project - Project Rules')
203
+ expect(result).toContain('<!-- projectId: test-project-id -->')
204
+ })
205
+
206
+ test('includes prjct workflow reference', () => {
207
+ const result = formatForClaude(mockContext, config)
208
+
209
+ expect(result).toContain('## PRJCT RULES')
210
+ expect(result).toContain('p. sync')
211
+ expect(result).toContain('p. task')
212
+ expect(result).toContain('p. done')
213
+ expect(result).toContain('p. ship')
214
+ })
215
+
216
+ test('includes commands table', () => {
217
+ const result = formatForClaude(mockContext, config)
218
+
219
+ expect(result).toContain('| Action | Command |')
220
+ expect(result).toContain('bun install')
221
+ expect(result).toContain('bun run dev')
222
+ })
223
+
224
+ test('includes agent references', () => {
225
+ const result = formatForClaude(mockContext, config)
226
+
227
+ expect(result).toContain('prjct-planner')
228
+ expect(result).toContain('prjct-shipper')
229
+ expect(result).toContain('backend')
230
+ })
231
+ })
232
+
233
+ describe('formatForContinue', () => {
234
+ const config = AI_TOOLS.continue
235
+
236
+ test('generates valid JSON format', () => {
237
+ const result = formatForContinue(mockContext, config)
238
+
239
+ const parsed = JSON.parse(result)
240
+ expect(parsed).toBeDefined()
241
+ })
242
+
243
+ test('includes system message with project info', () => {
244
+ const result = formatForContinue(mockContext, config)
245
+ const parsed = JSON.parse(result)
246
+
247
+ expect(parsed.systemMessage).toContain('test-project')
248
+ expect(parsed.systemMessage).toContain('library')
249
+ expect(parsed.systemMessage).toContain('Node.js')
250
+ })
251
+
252
+ test('includes context providers', () => {
253
+ const result = formatForContinue(mockContext, config)
254
+ const parsed = JSON.parse(result)
255
+
256
+ expect(parsed.contextProviders).toBeArray()
257
+ expect(parsed.contextProviders.length).toBeGreaterThan(0)
258
+ expect(parsed.contextProviders).toContainEqual({ name: 'code' })
259
+ expect(parsed.contextProviders).toContainEqual({ name: 'diff' })
260
+ })
261
+
262
+ test('includes slash commands', () => {
263
+ const result = formatForContinue(mockContext, config)
264
+ const parsed = JSON.parse(result)
265
+
266
+ expect(parsed.slashCommands).toBeArray()
267
+ expect(parsed.slashCommands.length).toBeGreaterThan(0)
268
+ })
269
+
270
+ test('includes custom test command with project test command', () => {
271
+ const result = formatForContinue(mockContext, config)
272
+ const parsed = JSON.parse(result)
273
+
274
+ expect(parsed.customCommands).toBeArray()
275
+ const testCmd = parsed.customCommands.find((c: { name: string }) => c.name === 'test')
276
+ expect(testCmd).toBeDefined()
277
+ expect(testCmd.prompt).toContain('bun test')
278
+ })
279
+ })
280
+
281
+ // =============================================================================
282
+ // getFormatter Tests
283
+ // =============================================================================
284
+
285
+ describe('getFormatter', () => {
286
+ test('returns formatter for claude', () => {
287
+ const formatter = getFormatter('claude')
288
+ expect(formatter).toBe(formatForClaude)
289
+ })
290
+
291
+ test('returns formatter for cursor', () => {
292
+ const formatter = getFormatter('cursor')
293
+ expect(formatter).toBe(formatForCursor)
294
+ })
295
+
296
+ test('returns formatter for windsurf', () => {
297
+ const formatter = getFormatter('windsurf')
298
+ expect(formatter).toBe(formatForWindsurf)
299
+ })
300
+
301
+ test('returns formatter for copilot', () => {
302
+ const formatter = getFormatter('copilot')
303
+ expect(formatter).toBe(formatForCopilot)
304
+ })
305
+
306
+ test('returns formatter for continue', () => {
307
+ const formatter = getFormatter('continue')
308
+ expect(formatter).toBe(formatForContinue)
309
+ })
310
+
311
+ test('returns null for unknown tool', () => {
312
+ const formatter = getFormatter('unknown-tool')
313
+ expect(formatter).toBeNull()
314
+ })
315
+ })
316
+
317
+ // =============================================================================
318
+ // Edge Cases
319
+ // =============================================================================
320
+
321
+ describe('Formatter Edge Cases', () => {
322
+ test('handles empty languages array', () => {
323
+ const ctx = { ...mockContext, languages: [] }
324
+
325
+ const cursorResult = formatForCursor(ctx, AI_TOOLS.cursor)
326
+ expect(cursorResult).not.toContain('Languages:')
327
+
328
+ const windsurfResult = formatForWindsurf(ctx, AI_TOOLS.windsurf)
329
+ expect(windsurfResult).toContain('## Stack')
330
+ })
331
+
332
+ test('handles empty frameworks array', () => {
333
+ const ctx = { ...mockContext, frameworks: [] }
334
+
335
+ const cursorResult = formatForCursor(ctx, AI_TOOLS.cursor)
336
+ expect(cursorResult).not.toContain('Frameworks:')
337
+
338
+ const windsurfResult = formatForWindsurf(ctx, AI_TOOLS.windsurf)
339
+ expect(windsurfResult).toContain('## Stack')
340
+ })
341
+
342
+ test('handles empty agents', () => {
343
+ const ctx = { ...mockContext, agents: { workflow: [], domain: [] } }
344
+
345
+ const claudeResult = formatForClaude(ctx, AI_TOOLS.claude)
346
+ expect(claudeResult).toContain('**Domain**: none')
347
+ })
348
+
349
+ test('handles special characters in project name', () => {
350
+ const ctx = { ...mockContext, name: '@scope/my-project' }
351
+
352
+ const cursorResult = formatForCursor(ctx, AI_TOOLS.cursor)
353
+ expect(cursorResult).toContain('@scope/my-project')
354
+
355
+ const claudeResult = formatForClaude(ctx, AI_TOOLS.claude)
356
+ expect(claudeResult).toContain('@scope/my-project')
357
+ })
358
+ })
@@ -114,50 +114,60 @@ Load from \`~/.prjct-cli/projects/${ctx.projectId}/agents/\`:
114
114
  }
115
115
 
116
116
  /**
117
- * Format context for Cursor (.cursorrules)
118
- * Concise rules format, optimized for inline suggestions
117
+ * Format context for Cursor (.cursor/rules/prjct.mdc)
118
+ * MDC format with YAML frontmatter, optimized for inline suggestions
119
+ *
120
+ * @see https://cursor.com/docs/context/rules
119
121
  */
120
122
  export function formatForCursor(ctx: ProjectContext, _config: AIToolConfig): string {
121
- const rules: string[] = []
123
+ const lines: string[] = []
124
+
125
+ // MDC format with YAML frontmatter
126
+ lines.push('---')
127
+ lines.push(`description: prjct context for ${ctx.name}`)
128
+ lines.push('globs:')
129
+ lines.push('alwaysApply: true')
130
+ lines.push('---')
131
+ lines.push('')
122
132
 
123
133
  // Project identity
124
- rules.push(`You are working on ${ctx.name}, a ${ctx.projectType} ${ctx.ecosystem} project.`)
125
- rules.push('')
134
+ lines.push(`You are working on ${ctx.name}, a ${ctx.projectType} ${ctx.ecosystem} project.`)
135
+ lines.push('')
126
136
 
127
137
  // Tech stack
128
- rules.push('## Tech Stack')
138
+ lines.push('## Tech Stack')
129
139
  if (ctx.languages.length > 0) {
130
- rules.push(`- Languages: ${ctx.languages.join(', ')}`)
140
+ lines.push(`- Languages: ${ctx.languages.join(', ')}`)
131
141
  }
132
142
  if (ctx.frameworks.length > 0) {
133
- rules.push(`- Frameworks: ${ctx.frameworks.join(', ')}`)
143
+ lines.push(`- Frameworks: ${ctx.frameworks.join(', ')}`)
134
144
  }
135
- rules.push('')
145
+ lines.push('')
136
146
 
137
147
  // Commands
138
- rules.push('## Commands')
139
- rules.push(`- Install: \`${ctx.commands.install}\``)
140
- rules.push(`- Dev: \`${ctx.commands.dev}\``)
141
- rules.push(`- Test: \`${ctx.commands.test}\``)
142
- rules.push(`- Build: \`${ctx.commands.build}\``)
143
- rules.push('')
148
+ lines.push('## Commands')
149
+ lines.push(`- Install: \`${ctx.commands.install}\``)
150
+ lines.push(`- Dev: \`${ctx.commands.dev}\``)
151
+ lines.push(`- Test: \`${ctx.commands.test}\``)
152
+ lines.push(`- Build: \`${ctx.commands.build}\``)
153
+ lines.push('')
144
154
 
145
155
  // Code style - language agnostic
146
- rules.push('## Code Style')
147
- rules.push(`- Follow ${ctx.ecosystem} conventions`)
148
- rules.push('- Match existing code patterns in this project')
149
- rules.push('- Use idiomatic constructs for the language')
150
- rules.push('')
156
+ lines.push('## Code Style')
157
+ lines.push(`- Follow ${ctx.ecosystem} conventions`)
158
+ lines.push('- Match existing code patterns in this project')
159
+ lines.push('- Use idiomatic constructs for the language')
160
+ lines.push('')
151
161
 
152
162
  // Best practices
153
- rules.push('## Best Practices')
154
- rules.push('- Write clean, readable code')
155
- rules.push('- Add comments only for complex logic')
156
- rules.push('- Keep functions small and focused')
157
- rules.push('- Handle errors appropriately')
158
- rules.push('- Write tests for new functionality')
159
-
160
- return rules.join('\n')
163
+ lines.push('## Best Practices')
164
+ lines.push('- Write clean, readable code')
165
+ lines.push('- Add comments only for complex logic')
166
+ lines.push('- Keep functions small and focused')
167
+ lines.push('- Handle errors appropriately')
168
+ lines.push('- Write tests for new functionality')
169
+
170
+ return lines.join('\n')
161
171
  }
162
172
 
163
173
  /**
@@ -194,48 +204,57 @@ export function formatForCopilot(ctx: ProjectContext, _config: AIToolConfig): st
194
204
  }
195
205
 
196
206
  /**
197
- * Format context for Windsurf (.windsurfrules)
198
- * Optimized for Cascade AI with flow-based suggestions
207
+ * Format context for Windsurf (.windsurf/rules/prjct.md)
208
+ * MD format with YAML frontmatter, optimized for Cascade AI
209
+ *
210
+ * @see https://docs.windsurf.com/windsurf/cascade/memories
199
211
  */
200
212
  export function formatForWindsurf(ctx: ProjectContext, _config: AIToolConfig): string {
201
- const rules: string[] = []
213
+ const lines: string[] = []
214
+
215
+ // YAML frontmatter (Windsurf uses trigger: always_on instead of alwaysApply)
216
+ lines.push('---')
217
+ lines.push(`description: prjct context for ${ctx.name}`)
218
+ lines.push('trigger: always_on')
219
+ lines.push('---')
220
+ lines.push('')
202
221
 
203
222
  // Project identity
204
- rules.push(`# ${ctx.name}`)
205
- rules.push('')
206
- rules.push(`${ctx.projectType} project using ${ctx.ecosystem}.`)
207
- rules.push('')
223
+ lines.push(`# ${ctx.name}`)
224
+ lines.push('')
225
+ lines.push(`${ctx.projectType} project using ${ctx.ecosystem}.`)
226
+ lines.push('')
208
227
 
209
228
  // Tech stack (concise)
210
- rules.push('## Stack')
211
- rules.push(`- ${ctx.languages.join(', ')}`)
229
+ lines.push('## Stack')
230
+ lines.push(`- ${ctx.languages.join(', ')}`)
212
231
  if (ctx.frameworks.length > 0) {
213
- rules.push(`- ${ctx.frameworks.join(', ')}`)
232
+ lines.push(`- ${ctx.frameworks.join(', ')}`)
214
233
  }
215
- rules.push('')
234
+ lines.push('')
216
235
 
217
236
  // Commands (essential only)
218
- rules.push('## Commands')
219
- rules.push(`\`\`\`bash`)
220
- rules.push(`# Install`)
221
- rules.push(ctx.commands.install)
222
- rules.push(`# Dev`)
223
- rules.push(ctx.commands.dev)
224
- rules.push(`# Test`)
225
- rules.push(ctx.commands.test)
226
- rules.push(`# Build`)
227
- rules.push(ctx.commands.build)
228
- rules.push(`\`\`\``)
229
- rules.push('')
237
+ lines.push('## Commands')
238
+ lines.push('```bash')
239
+ lines.push(`# Install`)
240
+ lines.push(ctx.commands.install)
241
+ lines.push(`# Dev`)
242
+ lines.push(ctx.commands.dev)
243
+ lines.push(`# Test`)
244
+ lines.push(ctx.commands.test)
245
+ lines.push(`# Build`)
246
+ lines.push(ctx.commands.build)
247
+ lines.push('```')
248
+ lines.push('')
230
249
 
231
250
  // Code style - language agnostic
232
- rules.push('## Rules')
233
- rules.push(`- Follow ${ctx.ecosystem} conventions`)
234
- rules.push('- Match existing project patterns')
235
- rules.push('- Clean code, minimal comments')
236
- rules.push('- Test new functionality')
251
+ lines.push('## Rules')
252
+ lines.push(`- Follow ${ctx.ecosystem} conventions`)
253
+ lines.push('- Match existing project patterns')
254
+ lines.push('- Clean code, minimal comments')
255
+ lines.push('- Test new functionality')
237
256
 
238
- return rules.join('\n')
257
+ return lines.join('\n')
239
258
  }
240
259
 
241
260
  /**
@@ -40,7 +40,7 @@ export const AI_TOOLS: Record<string, AIToolConfig> = {
40
40
  cursor: {
41
41
  id: 'cursor',
42
42
  name: 'Cursor',
43
- outputFile: '.cursorrules',
43
+ outputFile: '.cursor/rules/prjct.mdc',
44
44
  outputPath: 'repo',
45
45
  maxTokens: 2000,
46
46
  format: 'concise',
@@ -58,7 +58,7 @@ export const AI_TOOLS: Record<string, AIToolConfig> = {
58
58
  windsurf: {
59
59
  id: 'windsurf',
60
60
  name: 'Windsurf',
61
- outputFile: '.windsurfrules',
61
+ outputFile: '.windsurf/rules/prjct.md',
62
62
  outputPath: 'repo',
63
63
  maxTokens: 2000,
64
64
  format: 'concise',
@@ -142,9 +142,14 @@ class SyncService {
142
142
  const startTime = Date.now()
143
143
 
144
144
  // Resolve AI tools: supports 'auto', 'all', or specific list
145
+ // Default behavior: claude + any detected IDE tools (.cursor/, .windsurf/)
145
146
  let aiToolIds: string[]
146
147
  if (!options.aiTools || options.aiTools.length === 0) {
147
- aiToolIds = DEFAULT_AI_TOOLS
148
+ // Start with default CLI tools and add detected IDE tools
149
+ const detectedIdeTools = detectInstalledTools(projectPath).filter(
150
+ (id) => !DEFAULT_AI_TOOLS.includes(id)
151
+ )
152
+ aiToolIds = [...DEFAULT_AI_TOOLS, ...detectedIdeTools]
148
153
  } else if (options.aiTools[0] === 'auto') {
149
154
  aiToolIds = detectInstalledTools(projectPath)
150
155
  if (aiToolIds.length === 0) aiToolIds = ['claude'] // fallback
@@ -19701,35 +19701,41 @@ Load from \`~/.prjct-cli/projects/${ctx.projectId}/agents/\`:
19701
19701
  `;
19702
19702
  }
19703
19703
  function formatForCursor(ctx, _config) {
19704
- const rules = [];
19705
- rules.push(`You are working on ${ctx.name}, a ${ctx.projectType} ${ctx.ecosystem} project.`);
19706
- rules.push("");
19707
- rules.push("## Tech Stack");
19704
+ const lines = [];
19705
+ lines.push("---");
19706
+ lines.push(`description: prjct context for ${ctx.name}`);
19707
+ lines.push("globs:");
19708
+ lines.push("alwaysApply: true");
19709
+ lines.push("---");
19710
+ lines.push("");
19711
+ lines.push(`You are working on ${ctx.name}, a ${ctx.projectType} ${ctx.ecosystem} project.`);
19712
+ lines.push("");
19713
+ lines.push("## Tech Stack");
19708
19714
  if (ctx.languages.length > 0) {
19709
- rules.push(`- Languages: ${ctx.languages.join(", ")}`);
19715
+ lines.push(`- Languages: ${ctx.languages.join(", ")}`);
19710
19716
  }
19711
19717
  if (ctx.frameworks.length > 0) {
19712
- rules.push(`- Frameworks: ${ctx.frameworks.join(", ")}`);
19713
- }
19714
- rules.push("");
19715
- rules.push("## Commands");
19716
- rules.push(`- Install: \`${ctx.commands.install}\``);
19717
- rules.push(`- Dev: \`${ctx.commands.dev}\``);
19718
- rules.push(`- Test: \`${ctx.commands.test}\``);
19719
- rules.push(`- Build: \`${ctx.commands.build}\``);
19720
- rules.push("");
19721
- rules.push("## Code Style");
19722
- rules.push(`- Follow ${ctx.ecosystem} conventions`);
19723
- rules.push("- Match existing code patterns in this project");
19724
- rules.push("- Use idiomatic constructs for the language");
19725
- rules.push("");
19726
- rules.push("## Best Practices");
19727
- rules.push("- Write clean, readable code");
19728
- rules.push("- Add comments only for complex logic");
19729
- rules.push("- Keep functions small and focused");
19730
- rules.push("- Handle errors appropriately");
19731
- rules.push("- Write tests for new functionality");
19732
- return rules.join("\n");
19718
+ lines.push(`- Frameworks: ${ctx.frameworks.join(", ")}`);
19719
+ }
19720
+ lines.push("");
19721
+ lines.push("## Commands");
19722
+ lines.push(`- Install: \`${ctx.commands.install}\``);
19723
+ lines.push(`- Dev: \`${ctx.commands.dev}\``);
19724
+ lines.push(`- Test: \`${ctx.commands.test}\``);
19725
+ lines.push(`- Build: \`${ctx.commands.build}\``);
19726
+ lines.push("");
19727
+ lines.push("## Code Style");
19728
+ lines.push(`- Follow ${ctx.ecosystem} conventions`);
19729
+ lines.push("- Match existing code patterns in this project");
19730
+ lines.push("- Use idiomatic constructs for the language");
19731
+ lines.push("");
19732
+ lines.push("## Best Practices");
19733
+ lines.push("- Write clean, readable code");
19734
+ lines.push("- Add comments only for complex logic");
19735
+ lines.push("- Keep functions small and focused");
19736
+ lines.push("- Handle errors appropriately");
19737
+ lines.push("- Write tests for new functionality");
19738
+ return lines.join("\n");
19733
19739
  }
19734
19740
  function formatForCopilot(ctx, _config) {
19735
19741
  const lines = [];
@@ -19752,35 +19758,40 @@ function formatForCopilot(ctx, _config) {
19752
19758
  return lines.join("\n");
19753
19759
  }
19754
19760
  function formatForWindsurf(ctx, _config) {
19755
- const rules = [];
19756
- rules.push(`# ${ctx.name}`);
19757
- rules.push("");
19758
- rules.push(`${ctx.projectType} project using ${ctx.ecosystem}.`);
19759
- rules.push("");
19760
- rules.push("## Stack");
19761
- rules.push(`- ${ctx.languages.join(", ")}`);
19761
+ const lines = [];
19762
+ lines.push("---");
19763
+ lines.push(`description: prjct context for ${ctx.name}`);
19764
+ lines.push("trigger: always_on");
19765
+ lines.push("---");
19766
+ lines.push("");
19767
+ lines.push(`# ${ctx.name}`);
19768
+ lines.push("");
19769
+ lines.push(`${ctx.projectType} project using ${ctx.ecosystem}.`);
19770
+ lines.push("");
19771
+ lines.push("## Stack");
19772
+ lines.push(`- ${ctx.languages.join(", ")}`);
19762
19773
  if (ctx.frameworks.length > 0) {
19763
- rules.push(`- ${ctx.frameworks.join(", ")}`);
19764
- }
19765
- rules.push("");
19766
- rules.push("## Commands");
19767
- rules.push(`\`\`\`bash`);
19768
- rules.push(`# Install`);
19769
- rules.push(ctx.commands.install);
19770
- rules.push(`# Dev`);
19771
- rules.push(ctx.commands.dev);
19772
- rules.push(`# Test`);
19773
- rules.push(ctx.commands.test);
19774
- rules.push(`# Build`);
19775
- rules.push(ctx.commands.build);
19776
- rules.push(`\`\`\``);
19777
- rules.push("");
19778
- rules.push("## Rules");
19779
- rules.push(`- Follow ${ctx.ecosystem} conventions`);
19780
- rules.push("- Match existing project patterns");
19781
- rules.push("- Clean code, minimal comments");
19782
- rules.push("- Test new functionality");
19783
- return rules.join("\n");
19774
+ lines.push(`- ${ctx.frameworks.join(", ")}`);
19775
+ }
19776
+ lines.push("");
19777
+ lines.push("## Commands");
19778
+ lines.push("```bash");
19779
+ lines.push(`# Install`);
19780
+ lines.push(ctx.commands.install);
19781
+ lines.push(`# Dev`);
19782
+ lines.push(ctx.commands.dev);
19783
+ lines.push(`# Test`);
19784
+ lines.push(ctx.commands.test);
19785
+ lines.push(`# Build`);
19786
+ lines.push(ctx.commands.build);
19787
+ lines.push("```");
19788
+ lines.push("");
19789
+ lines.push("## Rules");
19790
+ lines.push(`- Follow ${ctx.ecosystem} conventions`);
19791
+ lines.push("- Match existing project patterns");
19792
+ lines.push("- Clean code, minimal comments");
19793
+ lines.push("- Test new functionality");
19794
+ return lines.join("\n");
19784
19795
  }
19785
19796
  function formatForContinue(ctx, _config) {
19786
19797
  const systemMessage = [
@@ -19907,7 +19918,7 @@ var init_registry = __esm({
19907
19918
  cursor: {
19908
19919
  id: "cursor",
19909
19920
  name: "Cursor",
19910
- outputFile: ".cursorrules",
19921
+ outputFile: ".cursor/rules/prjct.mdc",
19911
19922
  outputPath: "repo",
19912
19923
  maxTokens: 2e3,
19913
19924
  format: "concise",
@@ -19925,7 +19936,7 @@ var init_registry = __esm({
19925
19936
  windsurf: {
19926
19937
  id: "windsurf",
19927
19938
  name: "Windsurf",
19928
- outputFile: ".windsurfrules",
19939
+ outputFile: ".windsurf/rules/prjct.md",
19929
19940
  outputPath: "repo",
19930
19941
  maxTokens: 2e3,
19931
19942
  format: "concise",
@@ -20704,7 +20715,10 @@ var init_sync_service = __esm({
20704
20715
  const startTime = Date.now();
20705
20716
  let aiToolIds;
20706
20717
  if (!options.aiTools || options.aiTools.length === 0) {
20707
- aiToolIds = DEFAULT_AI_TOOLS;
20718
+ const detectedIdeTools = detectInstalledTools(projectPath).filter(
20719
+ (id) => !DEFAULT_AI_TOOLS.includes(id)
20720
+ );
20721
+ aiToolIds = [...DEFAULT_AI_TOOLS, ...detectedIdeTools];
20708
20722
  } else if (options.aiTools[0] === "auto") {
20709
20723
  aiToolIds = detectInstalledTools(projectPath);
20710
20724
  if (aiToolIds.length === 0) aiToolIds = ["claude"];
@@ -26845,7 +26859,7 @@ var require_package = __commonJS({
26845
26859
  "package.json"(exports, module) {
26846
26860
  module.exports = {
26847
26861
  name: "prjct-cli",
26848
- version: "0.64.0",
26862
+ version: "0.64.1",
26849
26863
  description: "Context layer for AI agents. Project context for Claude Code, Gemini CLI, and more.",
26850
26864
  main: "core/index.ts",
26851
26865
  bin: {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "prjct-cli",
3
- "version": "0.64.0",
3
+ "version": "0.64.1",
4
4
  "description": "Context layer for AI agents. Project context for Claude Code, Gemini CLI, and more.",
5
5
  "main": "core/index.ts",
6
6
  "bin": {