prjct-cli 0.4.8 → 0.4.9
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 +21 -0
- package/core/command-installer.js +97 -8
- package/core/commands.js +2 -16
- package/core/editors-config.js +29 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -15,6 +15,27 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
15
15
|
- Cross-platform file operations
|
|
16
16
|
- Windows Terminal integration
|
|
17
17
|
|
|
18
|
+
## [0.4.9] - 2025-10-02
|
|
19
|
+
|
|
20
|
+
### Added
|
|
21
|
+
- **Editor Uninstallation** - `prjct start` now allows removing editors
|
|
22
|
+
- Interactive checkbox selection shows currently installed editors
|
|
23
|
+
- Uncheck an editor to remove all prjct commands from it
|
|
24
|
+
- Automatically cleans up `~/.prjct-cli/config/installed-editors.json`
|
|
25
|
+
- User has full control over which editors have prjct commands
|
|
26
|
+
|
|
27
|
+
### Changed
|
|
28
|
+
- **`prjct start` Never Blocks** - Removed "already set up" error
|
|
29
|
+
- Can run `prjct start` anytime to reconfigure editors
|
|
30
|
+
- Shows beautiful ASCII art every time
|
|
31
|
+
- User decides which editors to keep/remove
|
|
32
|
+
|
|
33
|
+
### Fixed
|
|
34
|
+
- **Interactive Prompts** - Better UX for editor selection
|
|
35
|
+
- Pre-selects currently installed editors
|
|
36
|
+
- Clear message: "Select AI editors (uncheck to remove)"
|
|
37
|
+
- Allows selecting 0 editors (removes all)
|
|
38
|
+
|
|
18
39
|
## [0.4.8] - 2025-10-02
|
|
19
40
|
|
|
20
41
|
### Added
|
|
@@ -480,24 +480,93 @@ For detailed implementation, see prjct-cli documentation.
|
|
|
480
480
|
return await this.installToSelected(detectedEditors, forceUpdate)
|
|
481
481
|
}
|
|
482
482
|
|
|
483
|
+
/**
|
|
484
|
+
* Uninstall commands from a specific editor
|
|
485
|
+
* @param {string} editorKey - Editor key (claude, cursor, windsurf, codex)
|
|
486
|
+
* @returns {Promise<Object>} Uninstall result
|
|
487
|
+
*/
|
|
488
|
+
async uninstallFromEditor(editorKey) {
|
|
489
|
+
const chalk = require('chalk')
|
|
490
|
+
const editorsConfig = require('./editors-config')
|
|
491
|
+
|
|
492
|
+
const editor = this.editors[editorKey]
|
|
493
|
+
if (!editor) {
|
|
494
|
+
return {
|
|
495
|
+
success: false,
|
|
496
|
+
message: `Unknown editor: ${editorKey}`,
|
|
497
|
+
}
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
try {
|
|
501
|
+
// Delete commands from editor
|
|
502
|
+
const commandsPath = editor.path
|
|
503
|
+
const fs = require('fs').promises
|
|
504
|
+
|
|
505
|
+
// Get list of prjct commands
|
|
506
|
+
const commands = ['init', 'now', 'done', 'ship', 'next', 'idea', 'recap', 'progress', 'stuck', 'context',
|
|
507
|
+
'cleanup', 'design', 'task', 'git', 'test', 'roadmap', 'fix', 'analyze']
|
|
508
|
+
|
|
509
|
+
let deletedCount = 0
|
|
510
|
+
|
|
511
|
+
for (const command of commands) {
|
|
512
|
+
const commandFile = path.join(commandsPath, `p:${command}.md`)
|
|
513
|
+
try {
|
|
514
|
+
await fs.unlink(commandFile)
|
|
515
|
+
deletedCount++
|
|
516
|
+
} catch {
|
|
517
|
+
// Command file doesn't exist, skip
|
|
518
|
+
}
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
// Remove editor from tracked list
|
|
522
|
+
await editorsConfig.removeTrackedEditor(editorKey)
|
|
523
|
+
|
|
524
|
+
console.log(chalk.yellow(`🗑️ Removed ${deletedCount} commands from ${editor.name}`))
|
|
525
|
+
|
|
526
|
+
return {
|
|
527
|
+
success: true,
|
|
528
|
+
message: `Removed commands from ${editor.name}`,
|
|
529
|
+
deleted: deletedCount,
|
|
530
|
+
}
|
|
531
|
+
} catch (error) {
|
|
532
|
+
console.error(chalk.red(`❌ Error removing commands from ${editor.name}:`), error.message)
|
|
533
|
+
return {
|
|
534
|
+
success: false,
|
|
535
|
+
message: `Failed to remove commands: ${error.message}`,
|
|
536
|
+
}
|
|
537
|
+
}
|
|
538
|
+
}
|
|
539
|
+
|
|
483
540
|
/**
|
|
484
541
|
* Interactive installation with user selection using prompts
|
|
485
|
-
* @param {boolean}
|
|
542
|
+
* @param {boolean} allowUninstall - Allow uninstalling editors
|
|
486
543
|
* @returns {Promise<Object>} Installation results
|
|
487
544
|
*/
|
|
488
|
-
async interactiveInstall(
|
|
545
|
+
async interactiveInstall(allowUninstall = false) {
|
|
489
546
|
const prompts = require('prompts')
|
|
547
|
+
const editorsConfig = require('./editors-config')
|
|
490
548
|
|
|
491
549
|
// Detect all editors
|
|
492
550
|
const detected = await this.detectEditors(this.projectPath)
|
|
493
551
|
|
|
552
|
+
// Get currently installed editors if allowUninstall
|
|
553
|
+
let installedEditors = []
|
|
554
|
+
if (allowUninstall) {
|
|
555
|
+
try {
|
|
556
|
+
installedEditors = await editorsConfig.getTrackedEditors()
|
|
557
|
+
} catch {
|
|
558
|
+
// No editors installed yet
|
|
559
|
+
}
|
|
560
|
+
}
|
|
561
|
+
|
|
494
562
|
// Create choices for prompts
|
|
495
563
|
const availableEditors = Object.entries(detected)
|
|
496
564
|
.filter(([_, info]) => info.detected)
|
|
497
565
|
.map(([key, info]) => ({
|
|
498
566
|
title: `${this.editors[key].name} (${info.path})`,
|
|
499
567
|
value: key,
|
|
500
|
-
|
|
568
|
+
// Pre-select if allowUninstall and already installed, otherwise select all
|
|
569
|
+
selected: allowUninstall ? installedEditors.includes(key) : true,
|
|
501
570
|
}))
|
|
502
571
|
|
|
503
572
|
if (availableEditors.length === 0) {
|
|
@@ -513,15 +582,15 @@ For detailed implementation, see prjct-cli documentation.
|
|
|
513
582
|
const response = await prompts({
|
|
514
583
|
type: 'multiselect',
|
|
515
584
|
name: 'selectedEditors',
|
|
516
|
-
message: 'Select AI editors to install commands to:',
|
|
585
|
+
message: allowUninstall ? 'Select AI editors (uncheck to remove):' : 'Select AI editors to install commands to:',
|
|
517
586
|
choices: availableEditors,
|
|
518
|
-
min: 1,
|
|
587
|
+
min: allowUninstall ? 0 : 1, // Allow 0 selections if uninstalling
|
|
519
588
|
hint: '- Space to select. Return to submit',
|
|
520
589
|
instructions: false,
|
|
521
590
|
})
|
|
522
591
|
|
|
523
592
|
// Check if user cancelled
|
|
524
|
-
if (
|
|
593
|
+
if (response.selectedEditors === undefined) {
|
|
525
594
|
return {
|
|
526
595
|
success: false,
|
|
527
596
|
message: 'Installation cancelled by user',
|
|
@@ -530,8 +599,28 @@ For detailed implementation, see prjct-cli documentation.
|
|
|
530
599
|
}
|
|
531
600
|
}
|
|
532
601
|
|
|
533
|
-
|
|
534
|
-
|
|
602
|
+
const selectedEditors = response.selectedEditors || []
|
|
603
|
+
|
|
604
|
+
// If allowUninstall, handle editors that were deselected
|
|
605
|
+
if (allowUninstall) {
|
|
606
|
+
const deselectedEditors = installedEditors.filter(e => !selectedEditors.includes(e))
|
|
607
|
+
|
|
608
|
+
for (const editorKey of deselectedEditors) {
|
|
609
|
+
await this.uninstallFromEditor(editorKey)
|
|
610
|
+
}
|
|
611
|
+
}
|
|
612
|
+
|
|
613
|
+
// Install to selected editors (or return success if none selected)
|
|
614
|
+
if (selectedEditors.length === 0) {
|
|
615
|
+
return {
|
|
616
|
+
success: true,
|
|
617
|
+
message: 'All editors removed',
|
|
618
|
+
editors: [],
|
|
619
|
+
results: {},
|
|
620
|
+
}
|
|
621
|
+
}
|
|
622
|
+
|
|
623
|
+
return await this.installToSelected(selectedEditors, true) // Force update
|
|
535
624
|
}
|
|
536
625
|
|
|
537
626
|
/**
|
package/core/commands.js
CHANGED
|
@@ -2220,20 +2220,6 @@ ${diagram}
|
|
|
2220
2220
|
try {
|
|
2221
2221
|
await this.initializeAgent()
|
|
2222
2222
|
|
|
2223
|
-
// Check if already configured
|
|
2224
|
-
const editorsConfig = require('./editors-config')
|
|
2225
|
-
const configExists = await editorsConfig.configExists()
|
|
2226
|
-
|
|
2227
|
-
if (configExists) {
|
|
2228
|
-
return {
|
|
2229
|
-
success: false,
|
|
2230
|
-
message: this.agent.formatResponse(
|
|
2231
|
-
'prjct is already set up!\n\nTo reconfigure editors: prjct setup',
|
|
2232
|
-
'warning'
|
|
2233
|
-
),
|
|
2234
|
-
}
|
|
2235
|
-
}
|
|
2236
|
-
|
|
2237
2223
|
// ASCII Art
|
|
2238
2224
|
const chalk = require('chalk')
|
|
2239
2225
|
console.log('')
|
|
@@ -2277,8 +2263,8 @@ ${diagram}
|
|
|
2277
2263
|
})
|
|
2278
2264
|
console.log('')
|
|
2279
2265
|
|
|
2280
|
-
// Interactive selection
|
|
2281
|
-
const installResult = await commandInstaller.interactiveInstall(
|
|
2266
|
+
// Interactive selection (allow uninstall = true)
|
|
2267
|
+
const installResult = await commandInstaller.interactiveInstall(true)
|
|
2282
2268
|
|
|
2283
2269
|
if (!installResult.success) {
|
|
2284
2270
|
return {
|
package/core/editors-config.js
CHANGED
|
@@ -87,6 +87,35 @@ class EditorsConfig {
|
|
|
87
87
|
return config ? config.editors : []
|
|
88
88
|
}
|
|
89
89
|
|
|
90
|
+
/**
|
|
91
|
+
* Remove an editor from tracked list
|
|
92
|
+
* @param {string} editorKey - Editor key to remove
|
|
93
|
+
* @returns {Promise<boolean>} Success status
|
|
94
|
+
*/
|
|
95
|
+
async removeTrackedEditor(editorKey) {
|
|
96
|
+
try {
|
|
97
|
+
const config = await this.loadConfig()
|
|
98
|
+
if (!config) return false
|
|
99
|
+
|
|
100
|
+
// Remove from editors array
|
|
101
|
+
config.editors = config.editors.filter(e => e !== editorKey)
|
|
102
|
+
|
|
103
|
+
// Remove from paths object
|
|
104
|
+
if (config.paths && config.paths[editorKey]) {
|
|
105
|
+
delete config.paths[editorKey]
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// Save updated config
|
|
109
|
+
await fs.mkdir(this.configDir, { recursive: true })
|
|
110
|
+
await fs.writeFile(this.configPath, JSON.stringify(config, null, 2), 'utf-8')
|
|
111
|
+
|
|
112
|
+
return true
|
|
113
|
+
} catch (error) {
|
|
114
|
+
console.error('[editors-config] Error removing tracked editor:', error.message)
|
|
115
|
+
return false
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
90
119
|
/**
|
|
91
120
|
* Get editor paths from configuration
|
|
92
121
|
* @returns {Promise<Object>} Object mapping editor keys to paths
|