prjct-cli 0.37.0 → 0.39.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/CHANGELOG.md +108 -0
- package/README.md +84 -37
- package/bin/prjct.ts +11 -1
- package/core/index.ts +53 -26
- package/core/infrastructure/ai-provider.ts +157 -1
- package/core/infrastructure/setup.ts +225 -7
- package/core/types/provider.ts +18 -1
- package/dist/bin/prjct.mjs +3607 -1050
- package/dist/core/infrastructure/command-installer.js +458 -47
- package/dist/core/infrastructure/setup.js +728 -211
- package/package.json +1 -1
- package/templates/antigravity/SKILL.md +39 -0
- package/templates/cursor/commands/bug.md +8 -0
- package/templates/cursor/commands/done.md +4 -0
- package/templates/cursor/commands/pause.md +6 -0
- package/templates/cursor/commands/resume.md +4 -0
- package/templates/cursor/commands/ship.md +8 -0
- package/templates/cursor/commands/sync.md +4 -0
- package/templates/cursor/commands/task.md +8 -0
- package/templates/cursor/router.mdc +6 -6
- package/templates/global/ANTIGRAVITY.md +256 -0
- package/templates/global/CLAUDE.md +30 -0
- package/templates/global/CURSOR.mdc +60 -25
- package/templates/global/GEMINI.md +30 -0
- package/templates/global/WINDSURF.md +268 -0
- package/templates/windsurf/router.md +28 -0
- package/templates/windsurf/workflows/bug.md +8 -0
- package/templates/windsurf/workflows/done.md +4 -0
- package/templates/windsurf/workflows/pause.md +4 -0
- package/templates/windsurf/workflows/resume.md +4 -0
- package/templates/windsurf/workflows/ship.md +8 -0
- package/templates/windsurf/workflows/sync.md +4 -0
- package/templates/windsurf/workflows/task.md +8 -0
|
@@ -29,7 +29,9 @@ import {
|
|
|
29
29
|
selectProvider,
|
|
30
30
|
detectProvider,
|
|
31
31
|
detectAllProviders,
|
|
32
|
+
detectAntigravity,
|
|
32
33
|
Providers,
|
|
34
|
+
AntigravityProvider,
|
|
33
35
|
} from './ai-provider'
|
|
34
36
|
import type { AIProviderName, AIProviderConfig } from '../types/provider'
|
|
35
37
|
|
|
@@ -185,6 +187,15 @@ export async function run(): Promise<SetupResults> {
|
|
|
185
187
|
results.providers.push(providerResult)
|
|
186
188
|
}
|
|
187
189
|
|
|
190
|
+
// Step 2b: Install for Antigravity if detected (separate from CLI providers)
|
|
191
|
+
const antigravityDetection = detectAntigravity()
|
|
192
|
+
if (antigravityDetection.installed) {
|
|
193
|
+
const antigravityResult = await installAntigravitySkill()
|
|
194
|
+
if (antigravityResult.success) {
|
|
195
|
+
console.log(` ${GREEN}✓${NC} Antigravity skill installed`)
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
188
199
|
// Step 3: Save version in editors-config
|
|
189
200
|
await editorsConfig.saveConfig(VERSION, installer.getInstallPath(), selection.provider)
|
|
190
201
|
|
|
@@ -296,6 +307,55 @@ async function installGeminiGlobalConfig(): Promise<{ success: boolean; action:
|
|
|
296
307
|
}
|
|
297
308
|
}
|
|
298
309
|
|
|
310
|
+
// =============================================================================
|
|
311
|
+
// Antigravity Installation (Skills-based)
|
|
312
|
+
// =============================================================================
|
|
313
|
+
|
|
314
|
+
/**
|
|
315
|
+
* Install prjct as a skill for Google Antigravity
|
|
316
|
+
*
|
|
317
|
+
* Antigravity uses SKILL.md files in ~/.gemini/antigravity/skills/
|
|
318
|
+
* This is the recommended integration method (not MCP).
|
|
319
|
+
*/
|
|
320
|
+
export async function installAntigravitySkill(): Promise<{ success: boolean; action: string | null }> {
|
|
321
|
+
try {
|
|
322
|
+
const antigravitySkillsDir = path.join(os.homedir(), '.gemini', 'antigravity', 'skills')
|
|
323
|
+
const prjctSkillDir = path.join(antigravitySkillsDir, 'prjct')
|
|
324
|
+
const skillMdPath = path.join(prjctSkillDir, 'SKILL.md')
|
|
325
|
+
const templatePath = path.join(getPackageRoot(), 'templates', 'antigravity', 'SKILL.md')
|
|
326
|
+
|
|
327
|
+
// Ensure skills directory exists
|
|
328
|
+
fs.mkdirSync(prjctSkillDir, { recursive: true })
|
|
329
|
+
|
|
330
|
+
// Check if SKILL.md already exists
|
|
331
|
+
const fileExists = fs.existsSync(skillMdPath)
|
|
332
|
+
|
|
333
|
+
// Read template content
|
|
334
|
+
if (!fs.existsSync(templatePath)) {
|
|
335
|
+
console.error('Antigravity SKILL.md template not found')
|
|
336
|
+
return { success: false, action: null }
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
const templateContent = fs.readFileSync(templatePath, 'utf-8')
|
|
340
|
+
|
|
341
|
+
// Write SKILL.md
|
|
342
|
+
fs.writeFileSync(skillMdPath, templateContent, 'utf-8')
|
|
343
|
+
|
|
344
|
+
return { success: true, action: fileExists ? 'updated' : 'created' }
|
|
345
|
+
} catch (error) {
|
|
346
|
+
console.error(`Antigravity skill warning: ${(error as Error).message}`)
|
|
347
|
+
return { success: false, action: null }
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
/**
|
|
352
|
+
* Check if Antigravity skill needs installation or update
|
|
353
|
+
*/
|
|
354
|
+
export function needsAntigravityInstallation(): boolean {
|
|
355
|
+
const detection = detectAntigravity()
|
|
356
|
+
return detection.installed && !detection.skillInstalled
|
|
357
|
+
}
|
|
358
|
+
|
|
299
359
|
// =============================================================================
|
|
300
360
|
// Cursor IDE Installation (Project-Level)
|
|
301
361
|
// =============================================================================
|
|
@@ -307,6 +367,7 @@ async function installGeminiGlobalConfig(): Promise<{ success: boolean; action:
|
|
|
307
367
|
* configuration in .cursor/rules/ and .cursor/commands/.
|
|
308
368
|
*
|
|
309
369
|
* Creates minimal routers that point to the npm package for real instructions.
|
|
370
|
+
* Installs individual command files for better Cursor UX (/sync, /task, etc.)
|
|
310
371
|
*
|
|
311
372
|
* @param projectRoot - The project root directory
|
|
312
373
|
* @returns Object with success status and files created
|
|
@@ -330,10 +391,9 @@ export async function installCursorProject(projectRoot: string): Promise<{
|
|
|
330
391
|
const commandsDir = path.join(cursorDir, 'commands')
|
|
331
392
|
|
|
332
393
|
const routerMdcDest = path.join(rulesDir, 'prjct.mdc')
|
|
333
|
-
const commandRouterDest = path.join(commandsDir, 'p.md')
|
|
334
394
|
|
|
335
395
|
const routerMdcSource = path.join(getPackageRoot(), 'templates', 'cursor', 'router.mdc')
|
|
336
|
-
const
|
|
396
|
+
const cursorCommandsSource = path.join(getPackageRoot(), 'templates', 'cursor', 'commands')
|
|
337
397
|
|
|
338
398
|
// Ensure directories exist
|
|
339
399
|
fs.mkdirSync(rulesDir, { recursive: true })
|
|
@@ -345,10 +405,18 @@ export async function installCursorProject(projectRoot: string): Promise<{
|
|
|
345
405
|
result.rulesCreated = true
|
|
346
406
|
}
|
|
347
407
|
|
|
348
|
-
// Copy
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
408
|
+
// Copy individual command files → .cursor/commands/
|
|
409
|
+
// This enables /sync, /task, /done, /ship, etc. syntax in Cursor
|
|
410
|
+
if (fs.existsSync(cursorCommandsSource)) {
|
|
411
|
+
const commandFiles = fs.readdirSync(cursorCommandsSource)
|
|
412
|
+
.filter(f => f.endsWith('.md'))
|
|
413
|
+
|
|
414
|
+
for (const file of commandFiles) {
|
|
415
|
+
const src = path.join(cursorCommandsSource, file)
|
|
416
|
+
const dest = path.join(commandsDir, file)
|
|
417
|
+
fs.copyFileSync(src, dest)
|
|
418
|
+
}
|
|
419
|
+
result.commandsCreated = commandFiles.length > 0
|
|
352
420
|
}
|
|
353
421
|
|
|
354
422
|
// Update .gitignore to exclude prjct Cursor routers
|
|
@@ -373,7 +441,13 @@ async function addCursorToGitignore(projectRoot: string): Promise<boolean> {
|
|
|
373
441
|
const entriesToAdd = [
|
|
374
442
|
'# prjct Cursor routers (regenerated per-developer)',
|
|
375
443
|
'.cursor/rules/prjct.mdc',
|
|
376
|
-
'.cursor/commands/
|
|
444
|
+
'.cursor/commands/sync.md',
|
|
445
|
+
'.cursor/commands/task.md',
|
|
446
|
+
'.cursor/commands/done.md',
|
|
447
|
+
'.cursor/commands/ship.md',
|
|
448
|
+
'.cursor/commands/bug.md',
|
|
449
|
+
'.cursor/commands/pause.md',
|
|
450
|
+
'.cursor/commands/resume.md',
|
|
377
451
|
]
|
|
378
452
|
|
|
379
453
|
let content = ''
|
|
@@ -424,6 +498,150 @@ export function needsCursorRegeneration(projectRoot: string): boolean {
|
|
|
424
498
|
return fs.existsSync(cursorDir) && !fs.existsSync(routerPath)
|
|
425
499
|
}
|
|
426
500
|
|
|
501
|
+
// =============================================================================
|
|
502
|
+
// Windsurf IDE Installation (Project-Level)
|
|
503
|
+
// =============================================================================
|
|
504
|
+
|
|
505
|
+
/**
|
|
506
|
+
* Install prjct routers for Windsurf IDE in a project
|
|
507
|
+
*
|
|
508
|
+
* Unlike Claude/Gemini which have global config, Windsurf uses project-level
|
|
509
|
+
* configuration in .windsurf/rules/ and .windsurf/workflows/.
|
|
510
|
+
*
|
|
511
|
+
* Key differences from Cursor:
|
|
512
|
+
* - Uses .md files (not .mdc) with YAML frontmatter
|
|
513
|
+
* - Uses "workflows" directory instead of "commands"
|
|
514
|
+
* - Frontmatter uses `trigger: always_on` instead of `alwaysApply: true`
|
|
515
|
+
*
|
|
516
|
+
* @param projectRoot - The project root directory
|
|
517
|
+
* @returns Object with success status and files created
|
|
518
|
+
*/
|
|
519
|
+
export async function installWindsurfProject(projectRoot: string): Promise<{
|
|
520
|
+
success: boolean
|
|
521
|
+
rulesCreated: boolean
|
|
522
|
+
workflowsCreated: boolean
|
|
523
|
+
gitignoreUpdated: boolean
|
|
524
|
+
}> {
|
|
525
|
+
const result = {
|
|
526
|
+
success: false,
|
|
527
|
+
rulesCreated: false,
|
|
528
|
+
workflowsCreated: false,
|
|
529
|
+
gitignoreUpdated: false,
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
try {
|
|
533
|
+
const windsurfDir = path.join(projectRoot, '.windsurf')
|
|
534
|
+
const rulesDir = path.join(windsurfDir, 'rules')
|
|
535
|
+
const workflowsDir = path.join(windsurfDir, 'workflows')
|
|
536
|
+
|
|
537
|
+
const routerDest = path.join(rulesDir, 'prjct.md')
|
|
538
|
+
|
|
539
|
+
const routerSource = path.join(getPackageRoot(), 'templates', 'windsurf', 'router.md')
|
|
540
|
+
const windsurfWorkflowsSource = path.join(getPackageRoot(), 'templates', 'windsurf', 'workflows')
|
|
541
|
+
|
|
542
|
+
// Ensure directories exist
|
|
543
|
+
fs.mkdirSync(rulesDir, { recursive: true })
|
|
544
|
+
fs.mkdirSync(workflowsDir, { recursive: true })
|
|
545
|
+
|
|
546
|
+
// Copy router.md → .windsurf/rules/prjct.md
|
|
547
|
+
if (fs.existsSync(routerSource)) {
|
|
548
|
+
fs.copyFileSync(routerSource, routerDest)
|
|
549
|
+
result.rulesCreated = true
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
// Copy individual workflow files → .windsurf/workflows/
|
|
553
|
+
// This enables /sync, /task, /done, /ship, etc. syntax in Windsurf
|
|
554
|
+
if (fs.existsSync(windsurfWorkflowsSource)) {
|
|
555
|
+
const workflowFiles = fs.readdirSync(windsurfWorkflowsSource)
|
|
556
|
+
.filter(f => f.endsWith('.md'))
|
|
557
|
+
|
|
558
|
+
for (const file of workflowFiles) {
|
|
559
|
+
const src = path.join(windsurfWorkflowsSource, file)
|
|
560
|
+
const dest = path.join(workflowsDir, file)
|
|
561
|
+
fs.copyFileSync(src, dest)
|
|
562
|
+
}
|
|
563
|
+
result.workflowsCreated = workflowFiles.length > 0
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
// Update .gitignore to exclude prjct Windsurf routers
|
|
567
|
+
result.gitignoreUpdated = await addWindsurfToGitignore(projectRoot)
|
|
568
|
+
|
|
569
|
+
result.success = result.rulesCreated || result.workflowsCreated
|
|
570
|
+
return result
|
|
571
|
+
} catch (error) {
|
|
572
|
+
console.error(`Windsurf installation warning: ${(error as Error).message}`)
|
|
573
|
+
return result
|
|
574
|
+
}
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
/**
|
|
578
|
+
* Add Windsurf prjct routers to .gitignore
|
|
579
|
+
*
|
|
580
|
+
* These files are per-developer and regenerated automatically.
|
|
581
|
+
*/
|
|
582
|
+
async function addWindsurfToGitignore(projectRoot: string): Promise<boolean> {
|
|
583
|
+
try {
|
|
584
|
+
const gitignorePath = path.join(projectRoot, '.gitignore')
|
|
585
|
+
const entriesToAdd = [
|
|
586
|
+
'# prjct Windsurf routers (regenerated per-developer)',
|
|
587
|
+
'.windsurf/rules/prjct.md',
|
|
588
|
+
'.windsurf/workflows/sync.md',
|
|
589
|
+
'.windsurf/workflows/task.md',
|
|
590
|
+
'.windsurf/workflows/done.md',
|
|
591
|
+
'.windsurf/workflows/ship.md',
|
|
592
|
+
'.windsurf/workflows/bug.md',
|
|
593
|
+
'.windsurf/workflows/pause.md',
|
|
594
|
+
'.windsurf/workflows/resume.md',
|
|
595
|
+
]
|
|
596
|
+
|
|
597
|
+
let content = ''
|
|
598
|
+
let fileExists = false
|
|
599
|
+
|
|
600
|
+
try {
|
|
601
|
+
content = fs.readFileSync(gitignorePath, 'utf-8')
|
|
602
|
+
fileExists = true
|
|
603
|
+
} catch (error) {
|
|
604
|
+
if (!isNotFoundError(error)) {
|
|
605
|
+
throw error
|
|
606
|
+
}
|
|
607
|
+
}
|
|
608
|
+
|
|
609
|
+
// Check if already added
|
|
610
|
+
if (content.includes('.windsurf/rules/prjct.md')) {
|
|
611
|
+
return false // Already added
|
|
612
|
+
}
|
|
613
|
+
|
|
614
|
+
// Append to .gitignore
|
|
615
|
+
const newContent = fileExists
|
|
616
|
+
? content.trimEnd() + '\n\n' + entriesToAdd.join('\n') + '\n'
|
|
617
|
+
: entriesToAdd.join('\n') + '\n'
|
|
618
|
+
|
|
619
|
+
fs.writeFileSync(gitignorePath, newContent, 'utf-8')
|
|
620
|
+
return true
|
|
621
|
+
} catch (error) {
|
|
622
|
+
console.error(`Gitignore update warning: ${(error as Error).message}`)
|
|
623
|
+
return false
|
|
624
|
+
}
|
|
625
|
+
}
|
|
626
|
+
|
|
627
|
+
/**
|
|
628
|
+
* Check if a project has Windsurf configured (has .windsurf/ directory)
|
|
629
|
+
*/
|
|
630
|
+
export function hasWindsurfProject(projectRoot: string): boolean {
|
|
631
|
+
return fs.existsSync(path.join(projectRoot, '.windsurf'))
|
|
632
|
+
}
|
|
633
|
+
|
|
634
|
+
/**
|
|
635
|
+
* Check if Windsurf routers need regeneration
|
|
636
|
+
*/
|
|
637
|
+
export function needsWindsurfRegeneration(projectRoot: string): boolean {
|
|
638
|
+
const windsurfDir = path.join(projectRoot, '.windsurf')
|
|
639
|
+
const routerPath = path.join(windsurfDir, 'rules', 'prjct.md')
|
|
640
|
+
|
|
641
|
+
// Only check if .windsurf/ exists (project uses Windsurf)
|
|
642
|
+
return fs.existsSync(windsurfDir) && !fs.existsSync(routerPath)
|
|
643
|
+
}
|
|
644
|
+
|
|
427
645
|
/**
|
|
428
646
|
* Migrate existing projects to add cliVersion field
|
|
429
647
|
* This clears the status line warning after npm update
|
package/core/types/provider.ts
CHANGED
|
@@ -5,19 +5,22 @@
|
|
|
5
5
|
* - Claude Code (CLI)
|
|
6
6
|
* - Gemini CLI (CLI)
|
|
7
7
|
* - Cursor IDE (GUI, project-level config)
|
|
8
|
+
* - Windsurf IDE (GUI, project-level config)
|
|
8
9
|
*
|
|
9
10
|
* Key discovery: Skills use identical SKILL.md format for CLI providers.
|
|
10
11
|
* Cursor uses .mdc files with frontmatter for rules.
|
|
12
|
+
* Windsurf uses .md files with YAML frontmatter for rules.
|
|
11
13
|
*
|
|
12
14
|
* @see https://geminicli.com/docs/cli/gemini-md/
|
|
13
15
|
* @see https://geminicli.com/docs/cli/skills/
|
|
14
16
|
* @see https://cursor.com/docs/context/rules
|
|
17
|
+
* @see https://docs.windsurf.com/windsurf/cascade/memories
|
|
15
18
|
*/
|
|
16
19
|
|
|
17
20
|
/**
|
|
18
21
|
* Supported AI provider names
|
|
19
22
|
*/
|
|
20
|
-
export type AIProviderName = 'claude' | 'gemini' | 'cursor'
|
|
23
|
+
export type AIProviderName = 'claude' | 'gemini' | 'cursor' | 'antigravity' | 'windsurf'
|
|
21
24
|
|
|
22
25
|
/**
|
|
23
26
|
* Command format for each provider
|
|
@@ -122,6 +125,20 @@ export interface CursorProjectDetection {
|
|
|
122
125
|
projectRoot?: string
|
|
123
126
|
}
|
|
124
127
|
|
|
128
|
+
/**
|
|
129
|
+
* Result of Windsurf project detection
|
|
130
|
+
*/
|
|
131
|
+
export interface WindsurfProjectDetection {
|
|
132
|
+
/** Whether .windsurf/ directory exists in project */
|
|
133
|
+
detected: boolean
|
|
134
|
+
|
|
135
|
+
/** Whether prjct router is installed */
|
|
136
|
+
routerInstalled: boolean
|
|
137
|
+
|
|
138
|
+
/** Project root path */
|
|
139
|
+
projectRoot?: string
|
|
140
|
+
}
|
|
141
|
+
|
|
125
142
|
/**
|
|
126
143
|
* Provider-aware branding configuration
|
|
127
144
|
*/
|