prjct-cli 0.4.2 → 0.4.4
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 +117 -8
- package/README.md +12 -0
- package/core/command-installer.js +150 -0
- package/core/editors-config.js +178 -0
- package/package.json +7 -2
- package/scripts/post-install.js +174 -0
- package/scripts/preuninstall.js +94 -0
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,123 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
### Coming Soon
|
|
11
|
+
- **Windows Compatibility** - Native Windows support
|
|
12
|
+
- PowerShell and CMD command execution
|
|
13
|
+
- Windows path handling (`%USERPROFILE%\.prjct-cli\`)
|
|
14
|
+
- Windows-specific installation scripts
|
|
15
|
+
- Cross-platform file operations
|
|
16
|
+
- Windows Terminal integration
|
|
17
|
+
|
|
18
|
+
## [0.4.4] - 2025-10-02
|
|
19
|
+
|
|
20
|
+
### Added
|
|
21
|
+
- **Automatic Editor Command Updates** - Commands auto-update when npm package is updated
|
|
22
|
+
- New `core/editors-config.js` tracks which editors user has installed commands to
|
|
23
|
+
- Stores editor selections in `~/.prjct-cli/config/installed-editors.json`
|
|
24
|
+
- Post-install hook (`scripts/post-install.js`) auto-updates commands after `npm update -g prjct-cli`
|
|
25
|
+
- **Auto-installation on first install** - Detects AI editors and installs commands automatically
|
|
26
|
+
- Ensures version consistency across all configured editors (Claude, Cursor, Windsurf, Codex)
|
|
27
|
+
- No manual reinstallation needed - updates happen automatically
|
|
28
|
+
- Respects user's original editor choices from initial setup
|
|
29
|
+
|
|
30
|
+
- **Automatic Cleanup on Uninstall** - Clean uninstallation removes all traces
|
|
31
|
+
- New `scripts/preuninstall.js` runs before `npm uninstall -g prjct-cli`
|
|
32
|
+
- Automatically removes commands from all tracked editors
|
|
33
|
+
- Deletes tracking configuration from `~/.prjct-cli/config/`
|
|
34
|
+
- Added `uninstallFromEditor()` and `uninstallFromAll()` methods to command installer
|
|
35
|
+
- Added `deleteConfig()` method to editors config
|
|
36
|
+
- Prevents orphaned commands when package is uninstalled
|
|
37
|
+
- Clean exit even if cleanup fails (doesn't block uninstall)
|
|
38
|
+
|
|
39
|
+
- **Automatic Data Migration** - Seamless upgrade from v0.1.0 to v0.4.4
|
|
40
|
+
- Post-install hook automatically detects legacy `.prjct/` projects
|
|
41
|
+
- Migrates data from local structure to new global architecture (`~/.prjct-cli/projects/{id}/`)
|
|
42
|
+
- Scans common project directories (Projects, Documents, Developer, Code, etc.)
|
|
43
|
+
- Preserves all project data during migration (now, next, shipped, ideas, memory)
|
|
44
|
+
- Cleans up legacy directories while keeping config for compatibility
|
|
45
|
+
- No user intervention required - migration happens automatically on update
|
|
46
|
+
- Uses existing battle-tested `core/migrator.js` system
|
|
47
|
+
|
|
48
|
+
### Changed
|
|
49
|
+
- **Improved Command Tracking** - Always track editor installations for better update management
|
|
50
|
+
- Modified `installToSelected()` to always save editor config (removed `!forceUpdate` restriction)
|
|
51
|
+
- Ensures tracking happens even during force updates
|
|
52
|
+
- Enables reliable auto-updates across all configured editors
|
|
53
|
+
|
|
54
|
+
### Technical Details
|
|
55
|
+
- **Post-Install Hook**: Runs after `npm install -g prjct-cli` or `npm update -g prjct-cli`
|
|
56
|
+
- First install: Auto-detects editors and installs commands
|
|
57
|
+
- Updates: Checks version change and auto-updates commands in tracked editors
|
|
58
|
+
- **Migration**: Automatically detects and migrates legacy projects from v0.1.0
|
|
59
|
+
- Silent operation with debug mode via `DEBUG=1` environment variable
|
|
60
|
+
- **Pre-Uninstall Hook**: Runs before `npm uninstall -g prjct-cli`
|
|
61
|
+
- Reads tracking config to find all installed editors
|
|
62
|
+
- Removes command directories from each editor
|
|
63
|
+
- Cleans up tracking configuration
|
|
64
|
+
- Fails gracefully to not block uninstall
|
|
65
|
+
- **Migration System**: Automatic upgrade path from v0.1.0 to v0.4.4
|
|
66
|
+
- Scans common project directories (fast, non-intrusive)
|
|
67
|
+
- Migrates local `.prjct/` to global `~/.prjct-cli/projects/{id}/`
|
|
68
|
+
- Preserves all data: core, progress, planning, analysis, memory layers
|
|
69
|
+
- Cleans legacy directories while keeping compatibility config
|
|
70
|
+
- Zero data loss, seamless upgrade experience
|
|
71
|
+
|
|
72
|
+
## [0.4.3] - 2025-10-02
|
|
73
|
+
|
|
74
|
+
### Added
|
|
75
|
+
- **Automatic Editor Command Updates** - Commands auto-update when npm package is updated
|
|
76
|
+
- New `core/editors-config.js` tracks which editors user has installed commands to
|
|
77
|
+
- Stores editor selections in `~/.prjct-cli/config/installed-editors.json`
|
|
78
|
+
- Post-install hook (`scripts/post-install.js`) auto-updates commands after `npm update -g prjct-cli`
|
|
79
|
+
- **Auto-installation on first install** - Detects AI editors and installs commands automatically
|
|
80
|
+
- Ensures version consistency across all configured editors (Claude, Cursor, Windsurf, Codex)
|
|
81
|
+
- No manual reinstallation needed - updates happen automatically
|
|
82
|
+
- Respects user's original editor choices from initial setup
|
|
83
|
+
|
|
84
|
+
- **Automatic Cleanup on Uninstall** - Clean uninstallation removes all traces
|
|
85
|
+
- New `scripts/preuninstall.js` runs before `npm uninstall -g prjct-cli`
|
|
86
|
+
- Automatically removes commands from all tracked editors
|
|
87
|
+
- Deletes tracking configuration from `~/.prjct-cli/config/`
|
|
88
|
+
- Added `uninstallFromEditor()` and `uninstallFromAll()` methods to command installer
|
|
89
|
+
- Added `deleteConfig()` method to editors config
|
|
90
|
+
- Prevents orphaned commands when package is uninstalled
|
|
91
|
+
- Clean exit even if cleanup fails (doesn't block uninstall)
|
|
92
|
+
|
|
93
|
+
- **GitHub Packages Support** - Dual registry publication for better reliability
|
|
94
|
+
- Package now published to both npm and GitHub Packages automatically
|
|
95
|
+
- GitHub Actions workflow updated to publish to both registries in parallel
|
|
96
|
+
- Added comprehensive GitHub Packages documentation (`docs/GITHUB_PACKAGES.md`)
|
|
97
|
+
- Includes `.npmrc.example` for easy local configuration
|
|
98
|
+
- Provides fallback option if npm registry is unavailable
|
|
99
|
+
- Free hosting for public repositories with automatic authentication
|
|
100
|
+
|
|
101
|
+
### Changed
|
|
102
|
+
- **Installation Documentation** - Updated README with dual installation options
|
|
103
|
+
- Primary: npm registry (recommended for most users)
|
|
104
|
+
- Alternative: GitHub Packages (for advanced users or npm fallback)
|
|
105
|
+
- Clear instructions for both installation methods
|
|
106
|
+
|
|
107
|
+
- **Command Installer Improvements**
|
|
108
|
+
- `installToSelected()` now always saves editor config (removed force-update restriction)
|
|
109
|
+
- Better tracking ensures updates work correctly even for force updates
|
|
110
|
+
- Improved error handling and reporting
|
|
111
|
+
|
|
112
|
+
### Technical Details
|
|
113
|
+
- **Editor Tracking**: Configuration saved after all successful command installations
|
|
114
|
+
- **Post-Install Hook**:
|
|
115
|
+
- Runs only for global installations, skips for local/dev installs
|
|
116
|
+
- Auto-detects and installs to all AI editors on first install
|
|
117
|
+
- Auto-updates commands when version changes
|
|
118
|
+
- **Pre-Uninstall Hook**:
|
|
119
|
+
- Runs only for global uninstallations
|
|
120
|
+
- Removes slash commands from `~/.claude/`, `~/.cursor/`, `~/.windsurf/`
|
|
121
|
+
- Removes AGENTS.md from `~/.codex/` if present
|
|
122
|
+
- Deletes tracking configuration
|
|
123
|
+
- **Version Detection**: Compares current version with last installed version
|
|
124
|
+
- **Force Update**: Automatically updates commands when version changes
|
|
125
|
+
- **Parallel Publication**: npm and GitHub Packages jobs run simultaneously for faster releases
|
|
126
|
+
|
|
10
127
|
## [0.4.2] - 2025-10-02
|
|
11
128
|
|
|
12
129
|
### Fixed
|
|
@@ -21,14 +138,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
21
138
|
- Removed obsolete install.sh and setup.sh copying from build script
|
|
22
139
|
- Cleaner and faster website builds
|
|
23
140
|
|
|
24
|
-
### Coming Soon
|
|
25
|
-
- **Windows Compatibility** - Native Windows support
|
|
26
|
-
- PowerShell and CMD command execution
|
|
27
|
-
- Windows path handling (`%USERPROFILE%\.prjct-cli\`)
|
|
28
|
-
- Windows-specific installation scripts
|
|
29
|
-
- Cross-platform file operations
|
|
30
|
-
- Windows Terminal integration
|
|
31
|
-
|
|
32
141
|
## [0.4.1] - 2025-10-01
|
|
33
142
|
|
|
34
143
|
### Added
|
package/README.md
CHANGED
|
@@ -46,6 +46,8 @@ Each agent gets optimized output:
|
|
|
46
46
|
|
|
47
47
|
## ⚡ Installation
|
|
48
48
|
|
|
49
|
+
### From npm (Recommended)
|
|
50
|
+
|
|
49
51
|
Install prjct-cli globally using npm:
|
|
50
52
|
|
|
51
53
|
```bash
|
|
@@ -65,6 +67,16 @@ pnpm add -g prjct-cli
|
|
|
65
67
|
bun install -g prjct-cli
|
|
66
68
|
```
|
|
67
69
|
|
|
70
|
+
### From GitHub Packages
|
|
71
|
+
|
|
72
|
+
You can also install from GitHub Packages:
|
|
73
|
+
|
|
74
|
+
```bash
|
|
75
|
+
npm install -g @jlopezlira/prjct-cli --registry=https://npm.pkg.github.com
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
For easier installation from GitHub Packages, see [GitHub Packages Setup](docs/GITHUB_PACKAGES.md).
|
|
79
|
+
|
|
68
80
|
**Requirements**: Node.js 18 or higher
|
|
69
81
|
|
|
70
82
|
> **Note**: The CLI automatically detects updates and notifies you when a new version is available. Simply run `npm update -g prjct-cli` to upgrade.
|
|
@@ -403,6 +403,7 @@ For detailed implementation, see prjct-cli documentation.
|
|
|
403
403
|
|
|
404
404
|
const results = {}
|
|
405
405
|
const installedTo = []
|
|
406
|
+
const successfulEditors = []
|
|
406
407
|
|
|
407
408
|
for (const editorKey of selectedEditors) {
|
|
408
409
|
const editor = this.editors[editorKey]
|
|
@@ -426,6 +427,7 @@ For detailed implementation, see prjct-cli documentation.
|
|
|
426
427
|
results[editorKey] = await this.installToEditor(editorKey, forceUpdate)
|
|
427
428
|
if (results[editorKey].success) {
|
|
428
429
|
installedTo.push(editor.name)
|
|
430
|
+
successfulEditors.push(editorKey)
|
|
429
431
|
}
|
|
430
432
|
}
|
|
431
433
|
|
|
@@ -442,6 +444,11 @@ For detailed implementation, see prjct-cli documentation.
|
|
|
442
444
|
const totalUpdated = Object.values(results)
|
|
443
445
|
.reduce((sum, r) => sum + (r.updated || 0), 0)
|
|
444
446
|
|
|
447
|
+
// Always save editor selections to config for tracking updates
|
|
448
|
+
if (successfulEditors.length > 0) {
|
|
449
|
+
await this.saveEditorConfig(successfulEditors)
|
|
450
|
+
}
|
|
451
|
+
|
|
445
452
|
return {
|
|
446
453
|
success: true,
|
|
447
454
|
editors: installedTo,
|
|
@@ -667,6 +674,36 @@ This command uses the global prjct architecture:
|
|
|
667
674
|
return lines.join('\n')
|
|
668
675
|
}
|
|
669
676
|
|
|
677
|
+
/**
|
|
678
|
+
* Save editor configuration to track installed editors
|
|
679
|
+
* @param {string[]} editors - Array of successfully installed editor keys
|
|
680
|
+
* @returns {Promise<void>}
|
|
681
|
+
*/
|
|
682
|
+
async saveEditorConfig(editors) {
|
|
683
|
+
try {
|
|
684
|
+
const editorsConfig = require('./editors-config')
|
|
685
|
+
const packageJson = require('../package.json')
|
|
686
|
+
|
|
687
|
+
// Build paths object
|
|
688
|
+
const paths = {}
|
|
689
|
+
for (const editorKey of editors) {
|
|
690
|
+
const editor = this.editors[editorKey]
|
|
691
|
+
if (editor) {
|
|
692
|
+
paths[editorKey] = editor.commandsPath
|
|
693
|
+
}
|
|
694
|
+
}
|
|
695
|
+
|
|
696
|
+
await editorsConfig.saveConfig(
|
|
697
|
+
editors,
|
|
698
|
+
paths,
|
|
699
|
+
packageJson.version,
|
|
700
|
+
)
|
|
701
|
+
} catch (error) {
|
|
702
|
+
// Don't fail installation if config save fails
|
|
703
|
+
console.error('[command-installer] Error saving editor config:', error.message)
|
|
704
|
+
}
|
|
705
|
+
}
|
|
706
|
+
|
|
670
707
|
/**
|
|
671
708
|
* Install Context7 MCP configuration for all detected editors
|
|
672
709
|
* @returns {Promise<Object>} Installation results
|
|
@@ -770,6 +807,119 @@ This command uses the global prjct architecture:
|
|
|
770
807
|
|
|
771
808
|
return results
|
|
772
809
|
}
|
|
810
|
+
|
|
811
|
+
/**
|
|
812
|
+
* Uninstall commands from a specific editor
|
|
813
|
+
* @param {string} editorKey - Editor identifier (claude, cursor, codex, windsurf)
|
|
814
|
+
* @returns {Promise<Object>} Uninstallation result
|
|
815
|
+
*/
|
|
816
|
+
async uninstallFromEditor(editorKey) {
|
|
817
|
+
const editor = this.editors[editorKey]
|
|
818
|
+
|
|
819
|
+
if (!editor) {
|
|
820
|
+
return { success: false, editor: editorKey, message: `Unknown editor: ${editorKey}` }
|
|
821
|
+
}
|
|
822
|
+
|
|
823
|
+
try {
|
|
824
|
+
switch (editor.format) {
|
|
825
|
+
case 'slash-commands':
|
|
826
|
+
// Remove the /p commands directory
|
|
827
|
+
await fs.rm(editor.commandsPath, { recursive: true, force: true })
|
|
828
|
+
return {
|
|
829
|
+
success: true,
|
|
830
|
+
editor: editor.name,
|
|
831
|
+
path: editor.commandsPath,
|
|
832
|
+
message: `Removed slash commands from ${editor.name}`,
|
|
833
|
+
}
|
|
834
|
+
|
|
835
|
+
case 'agents-md':
|
|
836
|
+
// Remove AGENTS.md if it exists
|
|
837
|
+
const exists = await this.fileExists(editor.commandsPath)
|
|
838
|
+
if (exists) {
|
|
839
|
+
await fs.unlink(editor.commandsPath)
|
|
840
|
+
}
|
|
841
|
+
return {
|
|
842
|
+
success: true,
|
|
843
|
+
editor: editor.name,
|
|
844
|
+
path: editor.commandsPath,
|
|
845
|
+
message: `Removed AGENTS.md from ${editor.name}`,
|
|
846
|
+
}
|
|
847
|
+
|
|
848
|
+
case 'workflows':
|
|
849
|
+
// Remove all p_*.md workflow files
|
|
850
|
+
try {
|
|
851
|
+
const files = await fs.readdir(editor.commandsPath)
|
|
852
|
+
const prjctWorkflows = files.filter(f => f.startsWith('p_') && f.endsWith('.md'))
|
|
853
|
+
|
|
854
|
+
for (const file of prjctWorkflows) {
|
|
855
|
+
await fs.unlink(path.join(editor.commandsPath, file))
|
|
856
|
+
}
|
|
857
|
+
|
|
858
|
+
return {
|
|
859
|
+
success: true,
|
|
860
|
+
editor: editor.name,
|
|
861
|
+
path: editor.commandsPath,
|
|
862
|
+
removed: prjctWorkflows.length,
|
|
863
|
+
message: `Removed ${prjctWorkflows.length} workflows from ${editor.name}`,
|
|
864
|
+
}
|
|
865
|
+
} catch (error) {
|
|
866
|
+
// Directory might not exist, that's ok
|
|
867
|
+
return {
|
|
868
|
+
success: true,
|
|
869
|
+
editor: editor.name,
|
|
870
|
+
message: `No workflows found in ${editor.name}`,
|
|
871
|
+
}
|
|
872
|
+
}
|
|
873
|
+
|
|
874
|
+
default:
|
|
875
|
+
return {
|
|
876
|
+
success: false,
|
|
877
|
+
editor: editor.name,
|
|
878
|
+
message: `Unknown format: ${editor.format}`,
|
|
879
|
+
}
|
|
880
|
+
}
|
|
881
|
+
} catch (error) {
|
|
882
|
+
return {
|
|
883
|
+
success: false,
|
|
884
|
+
editor: editor.name,
|
|
885
|
+
message: `Uninstall failed: ${error.message}`,
|
|
886
|
+
}
|
|
887
|
+
}
|
|
888
|
+
}
|
|
889
|
+
|
|
890
|
+
/**
|
|
891
|
+
* Uninstall commands from all tracked editors
|
|
892
|
+
* @returns {Promise<Object>} Uninstallation results
|
|
893
|
+
*/
|
|
894
|
+
async uninstallFromAll() {
|
|
895
|
+
const editorsConfig = require('./editors-config')
|
|
896
|
+
const trackedEditors = await editorsConfig.getTrackedEditors()
|
|
897
|
+
|
|
898
|
+
if (trackedEditors.length === 0) {
|
|
899
|
+
return {
|
|
900
|
+
success: true,
|
|
901
|
+
message: 'No editors tracked, nothing to uninstall',
|
|
902
|
+
results: {},
|
|
903
|
+
}
|
|
904
|
+
}
|
|
905
|
+
|
|
906
|
+
const results = {}
|
|
907
|
+
const successfulEditors = []
|
|
908
|
+
|
|
909
|
+
for (const editorKey of trackedEditors) {
|
|
910
|
+
results[editorKey] = await this.uninstallFromEditor(editorKey)
|
|
911
|
+
if (results[editorKey].success) {
|
|
912
|
+
successfulEditors.push(this.editors[editorKey]?.name || editorKey)
|
|
913
|
+
}
|
|
914
|
+
}
|
|
915
|
+
|
|
916
|
+
return {
|
|
917
|
+
success: true,
|
|
918
|
+
editors: successfulEditors,
|
|
919
|
+
totalRemoved: successfulEditors.length,
|
|
920
|
+
results,
|
|
921
|
+
}
|
|
922
|
+
}
|
|
773
923
|
}
|
|
774
924
|
|
|
775
925
|
module.exports = new CommandInstaller()
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
const fs = require('fs').promises
|
|
2
|
+
const path = require('path')
|
|
3
|
+
const os = require('os')
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* EditorsConfig - Manages installed editors tracking configuration
|
|
7
|
+
*
|
|
8
|
+
* Tracks which AI editors user has installed prjct commands to,
|
|
9
|
+
* enabling automatic updates when npm package is updated.
|
|
10
|
+
*
|
|
11
|
+
* Config location: ~/.prjct-cli/config/installed-editors.json
|
|
12
|
+
*
|
|
13
|
+
* @version 0.4.2
|
|
14
|
+
*/
|
|
15
|
+
class EditorsConfig {
|
|
16
|
+
constructor() {
|
|
17
|
+
this.homeDir = os.homedir()
|
|
18
|
+
this.configDir = path.join(this.homeDir, '.prjct-cli', 'config')
|
|
19
|
+
this.configFile = path.join(this.configDir, 'installed-editors.json')
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Ensure config directory exists
|
|
24
|
+
*/
|
|
25
|
+
async ensureConfigDir() {
|
|
26
|
+
try {
|
|
27
|
+
await fs.mkdir(this.configDir, { recursive: true })
|
|
28
|
+
} catch (error) {
|
|
29
|
+
console.error('[editors-config] Error creating config directory:', error.message)
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Load installed editors configuration
|
|
35
|
+
* @returns {Promise<Object|null>} Configuration object or null if not found
|
|
36
|
+
*/
|
|
37
|
+
async loadConfig() {
|
|
38
|
+
try {
|
|
39
|
+
const content = await fs.readFile(this.configFile, 'utf-8')
|
|
40
|
+
return JSON.parse(content)
|
|
41
|
+
} catch (error) {
|
|
42
|
+
if (error.code === 'ENOENT') {
|
|
43
|
+
return null
|
|
44
|
+
}
|
|
45
|
+
console.error('[editors-config] Error loading config:', error.message)
|
|
46
|
+
return null
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Save installed editors configuration
|
|
52
|
+
* @param {string[]} editors - Array of editor keys (e.g., ['claude', 'cursor'])
|
|
53
|
+
* @param {Object} paths - Object mapping editor keys to installation paths
|
|
54
|
+
* @param {string} version - Current prjct-cli version
|
|
55
|
+
* @returns {Promise<boolean>} Success status
|
|
56
|
+
*/
|
|
57
|
+
async saveConfig(editors, paths, version) {
|
|
58
|
+
try {
|
|
59
|
+
await this.ensureConfigDir()
|
|
60
|
+
|
|
61
|
+
const config = {
|
|
62
|
+
version,
|
|
63
|
+
editors,
|
|
64
|
+
lastInstall: new Date().toISOString(),
|
|
65
|
+
paths,
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
await fs.writeFile(
|
|
69
|
+
this.configFile,
|
|
70
|
+
JSON.stringify(config, null, 2),
|
|
71
|
+
'utf-8',
|
|
72
|
+
)
|
|
73
|
+
|
|
74
|
+
return true
|
|
75
|
+
} catch (error) {
|
|
76
|
+
console.error('[editors-config] Error saving config:', error.message)
|
|
77
|
+
return false
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Get tracked editors from configuration
|
|
83
|
+
* @returns {Promise<string[]>} Array of editor keys
|
|
84
|
+
*/
|
|
85
|
+
async getTrackedEditors() {
|
|
86
|
+
const config = await this.loadConfig()
|
|
87
|
+
return config ? config.editors : []
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Get editor paths from configuration
|
|
92
|
+
* @returns {Promise<Object>} Object mapping editor keys to paths
|
|
93
|
+
*/
|
|
94
|
+
async getEditorPaths() {
|
|
95
|
+
const config = await this.loadConfig()
|
|
96
|
+
return config ? config.paths : {}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Get last installed version
|
|
101
|
+
* @returns {Promise<string|null>} Version string or null
|
|
102
|
+
*/
|
|
103
|
+
async getLastVersion() {
|
|
104
|
+
const config = await this.loadConfig()
|
|
105
|
+
return config ? config.version : null
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Check if version has changed since last install
|
|
110
|
+
* @param {string} currentVersion - Current version to compare
|
|
111
|
+
* @returns {Promise<boolean>} True if version has changed
|
|
112
|
+
*/
|
|
113
|
+
async hasVersionChanged(currentVersion) {
|
|
114
|
+
const lastVersion = await this.getLastVersion()
|
|
115
|
+
return lastVersion !== null && lastVersion !== currentVersion
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Update version in configuration
|
|
120
|
+
* @param {string} version - New version to save
|
|
121
|
+
* @returns {Promise<boolean>} Success status
|
|
122
|
+
*/
|
|
123
|
+
async updateVersion(version) {
|
|
124
|
+
try {
|
|
125
|
+
const config = await this.loadConfig()
|
|
126
|
+
if (!config) {
|
|
127
|
+
return false
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
config.version = version
|
|
131
|
+
config.lastInstall = new Date().toISOString()
|
|
132
|
+
|
|
133
|
+
await fs.writeFile(
|
|
134
|
+
this.configFile,
|
|
135
|
+
JSON.stringify(config, null, 2),
|
|
136
|
+
'utf-8',
|
|
137
|
+
)
|
|
138
|
+
|
|
139
|
+
return true
|
|
140
|
+
} catch (error) {
|
|
141
|
+
console.error('[editors-config] Error updating version:', error.message)
|
|
142
|
+
return false
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Check if config file exists
|
|
148
|
+
* @returns {Promise<boolean>} True if config exists
|
|
149
|
+
*/
|
|
150
|
+
async configExists() {
|
|
151
|
+
try {
|
|
152
|
+
await fs.access(this.configFile)
|
|
153
|
+
return true
|
|
154
|
+
} catch {
|
|
155
|
+
return false
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* Delete configuration file
|
|
161
|
+
* Used during uninstallation to clean up tracking data
|
|
162
|
+
* @returns {Promise<boolean>} Success status
|
|
163
|
+
*/
|
|
164
|
+
async deleteConfig() {
|
|
165
|
+
try {
|
|
166
|
+
const exists = await this.configExists()
|
|
167
|
+
if (exists) {
|
|
168
|
+
await fs.unlink(this.configFile)
|
|
169
|
+
}
|
|
170
|
+
return true
|
|
171
|
+
} catch (error) {
|
|
172
|
+
console.error('[editors-config] Error deleting config:', error.message)
|
|
173
|
+
return false
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
module.exports = new EditorsConfig()
|
package/package.json
CHANGED
|
@@ -1,15 +1,18 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "prjct-cli",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.4",
|
|
4
4
|
"description": "AI-integrated project management for indie hackers - works with Claude Code, Cursor, and Warp",
|
|
5
5
|
"main": "core/index.js",
|
|
6
6
|
"bin": {
|
|
7
7
|
"prjct": "./bin/prjct"
|
|
8
8
|
},
|
|
9
9
|
"publishConfig": {
|
|
10
|
-
"access": "public"
|
|
10
|
+
"access": "public",
|
|
11
|
+
"registry": "https://registry.npmjs.org"
|
|
11
12
|
},
|
|
12
13
|
"scripts": {
|
|
14
|
+
"postinstall": "node scripts/post-install.js",
|
|
15
|
+
"preuninstall": "node scripts/preuninstall.js",
|
|
13
16
|
"install-global": "./scripts/install.sh",
|
|
14
17
|
"test": "echo 'No tests configured'",
|
|
15
18
|
"lint": "eslint \"**/*.js\" --ignore-pattern \"node_modules/**\" --ignore-pattern \"website/**\"",
|
|
@@ -66,6 +69,8 @@
|
|
|
66
69
|
"templates/",
|
|
67
70
|
"scripts/install.sh",
|
|
68
71
|
"scripts/verify-installation.sh",
|
|
72
|
+
"scripts/post-install.js",
|
|
73
|
+
"scripts/preuninstall.js",
|
|
69
74
|
"LICENSE",
|
|
70
75
|
"README.md",
|
|
71
76
|
"CHANGELOG.md",
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Post-Install Script
|
|
5
|
+
*
|
|
6
|
+
* Runs automatically after `npm install -g prjct-cli` or `npm update -g prjct-cli`.
|
|
7
|
+
* Auto-updates slash commands in all previously configured editors.
|
|
8
|
+
*
|
|
9
|
+
* Flow:
|
|
10
|
+
* 1. Check if running as global install
|
|
11
|
+
* 2. Read tracked editors from ~/.prjct-cli/config/installed-editors.json
|
|
12
|
+
* 3. If config exists and version changed, force-update commands in tracked editors
|
|
13
|
+
* 4. Update version in config
|
|
14
|
+
*
|
|
15
|
+
* @version 0.4.4
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
const path = require('path')
|
|
19
|
+
const chalk = require('chalk')
|
|
20
|
+
const { execSync } = require('child_process')
|
|
21
|
+
|
|
22
|
+
async function main() {
|
|
23
|
+
try {
|
|
24
|
+
// Check if this is a global installation
|
|
25
|
+
const isGlobal = await checkIfGlobalInstall()
|
|
26
|
+
|
|
27
|
+
if (!isGlobal) {
|
|
28
|
+
// Skip post-install for local/dev installations
|
|
29
|
+
return
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// Load editors config
|
|
33
|
+
const editorsConfig = require('../core/editors-config')
|
|
34
|
+
const configExists = await editorsConfig.configExists()
|
|
35
|
+
|
|
36
|
+
if (!configExists) {
|
|
37
|
+
// First-time install - auto-detect and install to all editors
|
|
38
|
+
console.log(chalk.cyan('\n🔍 First-time installation detected...\n'))
|
|
39
|
+
|
|
40
|
+
// Load command installer
|
|
41
|
+
const commandInstaller = require('../core/command-installer')
|
|
42
|
+
|
|
43
|
+
// Detect available editors
|
|
44
|
+
const detected = await commandInstaller.detectEditors()
|
|
45
|
+
const detectedEditors = Object.entries(detected)
|
|
46
|
+
.filter(([_, info]) => info.detected)
|
|
47
|
+
.map(([key, _]) => key)
|
|
48
|
+
|
|
49
|
+
if (detectedEditors.length === 0) {
|
|
50
|
+
// No editors detected, user will install manually later
|
|
51
|
+
console.log(chalk.yellow('ℹ️ No AI editors detected'))
|
|
52
|
+
console.log(chalk.gray(' Run `prjct install` when you set up Claude Code, Cursor, or Windsurf\n'))
|
|
53
|
+
return
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
console.log(chalk.cyan(`📦 Installing commands to: ${detectedEditors.map(k => commandInstaller.editors[k]?.name || k).join(', ')}\n`))
|
|
57
|
+
|
|
58
|
+
// Install to all detected editors
|
|
59
|
+
const results = await commandInstaller.installToAll(false)
|
|
60
|
+
|
|
61
|
+
if (results.success) {
|
|
62
|
+
console.log(chalk.green(`✅ Commands installed in: ${results.editors.join(', ')}`))
|
|
63
|
+
console.log(chalk.gray(` Commands installed: ${results.totalInstalled}`))
|
|
64
|
+
console.log(chalk.cyan(`\n✨ prjct-cli ${currentVersion} is ready!\n`))
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
return
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// Get current package version
|
|
71
|
+
const packageJson = require('../package.json')
|
|
72
|
+
const currentVersion = packageJson.version
|
|
73
|
+
|
|
74
|
+
// Check if version has changed
|
|
75
|
+
const versionChanged = await editorsConfig.hasVersionChanged(currentVersion)
|
|
76
|
+
|
|
77
|
+
if (!versionChanged) {
|
|
78
|
+
// Same version, no update needed
|
|
79
|
+
return
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// Get tracked editors and paths
|
|
83
|
+
const trackedEditors = await editorsConfig.getTrackedEditors()
|
|
84
|
+
const editorPaths = await editorsConfig.getEditorPaths()
|
|
85
|
+
|
|
86
|
+
if (trackedEditors.length === 0) {
|
|
87
|
+
// No editors tracked yet
|
|
88
|
+
return
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
console.log(chalk.cyan('\n🔄 Updating prjct commands in configured editors...\n'))
|
|
92
|
+
|
|
93
|
+
// Load command installer
|
|
94
|
+
const commandInstaller = require('../core/command-installer')
|
|
95
|
+
|
|
96
|
+
// Force-update commands in all tracked editors
|
|
97
|
+
const results = await commandInstaller.installToSelected(trackedEditors, true)
|
|
98
|
+
|
|
99
|
+
if (results.success) {
|
|
100
|
+
console.log(chalk.green(`✅ Updated commands in: ${results.editors.join(', ')}`))
|
|
101
|
+
console.log(chalk.gray(` Commands updated: ${results.totalUpdated}`))
|
|
102
|
+
} else {
|
|
103
|
+
console.log(chalk.yellow('⚠️ Some editors could not be updated'))
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// Update version in config
|
|
107
|
+
await editorsConfig.updateVersion(currentVersion)
|
|
108
|
+
|
|
109
|
+
// Auto-migrate legacy projects (v0.1.0 → v0.4.4)
|
|
110
|
+
try {
|
|
111
|
+
console.log(chalk.cyan('\n🔍 Checking for legacy projects to migrate...\n'))
|
|
112
|
+
|
|
113
|
+
const migrator = require('../core/migrator')
|
|
114
|
+
|
|
115
|
+
// Quick scan of common directories (not entire home directory)
|
|
116
|
+
const summary = await migrator.migrateAll({
|
|
117
|
+
deepScan: false, // Only scan common dirs (fast)
|
|
118
|
+
removeLegacy: false, // Keep legacy dirs for safety
|
|
119
|
+
cleanupLegacy: true, // Remove old dirs but keep config
|
|
120
|
+
dryRun: false, // Actually perform migration
|
|
121
|
+
interactive: false // No user prompts during npm install
|
|
122
|
+
})
|
|
123
|
+
|
|
124
|
+
if (summary.successfullyMigrated > 0) {
|
|
125
|
+
console.log(chalk.green(`✅ Migrated ${summary.successfullyMigrated} legacy project(s) to new structure`))
|
|
126
|
+
console.log(chalk.gray(` Data location: ~/.prjct-cli/projects/`))
|
|
127
|
+
} else if (summary.totalFound > 0 && summary.alreadyMigrated === summary.totalFound) {
|
|
128
|
+
console.log(chalk.gray('ℹ️ All projects already using new structure'))
|
|
129
|
+
}
|
|
130
|
+
} catch (migrationError) {
|
|
131
|
+
// Don't block install if migration fails
|
|
132
|
+
if (process.env.DEBUG) {
|
|
133
|
+
console.error(chalk.yellow('[post-install] Migration warning:'), migrationError.message)
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
console.log(chalk.cyan(`\n✨ prjct-cli ${currentVersion} is ready!\n`))
|
|
138
|
+
|
|
139
|
+
} catch (error) {
|
|
140
|
+
// Silently fail - don't block npm install
|
|
141
|
+
// Only log if explicitly debugging
|
|
142
|
+
if (process.env.DEBUG) {
|
|
143
|
+
console.error(chalk.red('[post-install] Error:'), error.message)
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Check if package is being installed globally
|
|
150
|
+
* @returns {Promise<boolean>} True if global install
|
|
151
|
+
*/
|
|
152
|
+
async function checkIfGlobalInstall() {
|
|
153
|
+
try {
|
|
154
|
+
// Get npm global root directory
|
|
155
|
+
const globalRoot = execSync('npm root -g', { encoding: 'utf-8' }).trim()
|
|
156
|
+
|
|
157
|
+
// Get current package directory
|
|
158
|
+
const currentDir = path.resolve(__dirname, '..')
|
|
159
|
+
|
|
160
|
+
// Check if current directory is under global node_modules
|
|
161
|
+
return currentDir.startsWith(globalRoot)
|
|
162
|
+
} catch {
|
|
163
|
+
return false
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
// Run main function
|
|
168
|
+
main().catch(error => {
|
|
169
|
+
// Silently fail - don't block npm install
|
|
170
|
+
if (process.env.DEBUG) {
|
|
171
|
+
console.error('[post-install] Fatal error:', error)
|
|
172
|
+
}
|
|
173
|
+
process.exit(0) // Exit with success to not block npm install
|
|
174
|
+
})
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Pre-Uninstall Script
|
|
5
|
+
*
|
|
6
|
+
* Runs automatically BEFORE `npm uninstall -g prjct-cli`.
|
|
7
|
+
* Cleans up slash commands from all tracked AI editors.
|
|
8
|
+
*
|
|
9
|
+
* Flow:
|
|
10
|
+
* 1. Check if running as global uninstall
|
|
11
|
+
* 2. Read tracked editors from ~/.prjct-cli/config/installed-editors.json
|
|
12
|
+
* 3. Remove all prjct commands from tracked editors
|
|
13
|
+
* 4. Delete tracking configuration
|
|
14
|
+
* 5. Clean exit (don't block uninstall)
|
|
15
|
+
*
|
|
16
|
+
* @version 0.4.4
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
const chalk = require('chalk')
|
|
20
|
+
const { execSync } = require('child_process')
|
|
21
|
+
const path = require('path')
|
|
22
|
+
|
|
23
|
+
async function main() {
|
|
24
|
+
try {
|
|
25
|
+
// Check if this is a global uninstallation
|
|
26
|
+
const isGlobal = await checkIfGlobalInstall()
|
|
27
|
+
|
|
28
|
+
if (!isGlobal) {
|
|
29
|
+
// Skip cleanup for local/dev uninstalls
|
|
30
|
+
return
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// Load editors config
|
|
34
|
+
const editorsConfig = require('../core/editors-config')
|
|
35
|
+
const configExists = await editorsConfig.configExists()
|
|
36
|
+
|
|
37
|
+
if (!configExists) {
|
|
38
|
+
// No config, nothing to clean up
|
|
39
|
+
return
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
console.log(chalk.cyan('\n🧹 Cleaning up prjct commands from AI editors...\n'))
|
|
43
|
+
|
|
44
|
+
// Load command installer
|
|
45
|
+
const commandInstaller = require('../core/command-installer')
|
|
46
|
+
|
|
47
|
+
// Uninstall from all tracked editors
|
|
48
|
+
const results = await commandInstaller.uninstallFromAll()
|
|
49
|
+
|
|
50
|
+
if (results.success && results.editors.length > 0) {
|
|
51
|
+
console.log(chalk.green(`✅ Removed from: ${results.editors.join(', ')}`))
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// Delete tracking config
|
|
55
|
+
await editorsConfig.deleteConfig()
|
|
56
|
+
|
|
57
|
+
console.log(chalk.green('\n✨ prjct-cli uninstalled cleanly\n'))
|
|
58
|
+
|
|
59
|
+
} catch (error) {
|
|
60
|
+
// Silently fail - don't block npm uninstall
|
|
61
|
+
// Only log if explicitly debugging
|
|
62
|
+
if (process.env.DEBUG) {
|
|
63
|
+
console.error(chalk.red('[preuninstall] Error:'), error.message)
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Check if package is being uninstalled globally
|
|
70
|
+
* @returns {Promise<boolean>} True if global uninstall
|
|
71
|
+
*/
|
|
72
|
+
async function checkIfGlobalInstall() {
|
|
73
|
+
try {
|
|
74
|
+
// Get npm global root directory
|
|
75
|
+
const globalRoot = execSync('npm root -g', { encoding: 'utf-8' }).trim()
|
|
76
|
+
|
|
77
|
+
// Get current package directory
|
|
78
|
+
const currentDir = path.resolve(__dirname, '..')
|
|
79
|
+
|
|
80
|
+
// Check if current directory is under global node_modules
|
|
81
|
+
return currentDir.startsWith(globalRoot)
|
|
82
|
+
} catch {
|
|
83
|
+
return false
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// Run main function
|
|
88
|
+
main().catch(error => {
|
|
89
|
+
// Silently fail - don't block npm uninstall
|
|
90
|
+
if (process.env.DEBUG) {
|
|
91
|
+
console.error('[preuninstall] Fatal error:', error)
|
|
92
|
+
}
|
|
93
|
+
process.exit(0) // Exit with success to not block npm uninstall
|
|
94
|
+
})
|