prjct-cli 0.4.3 → 0.4.5
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 +115 -2
- package/bin/prjct +9 -0
- package/core/command-installer.js +115 -2
- package/core/commands.js +41 -9
- package/core/editors-config.js +18 -0
- package/core/migrator.js +1 -1
- package/package.json +3 -1
- package/scripts/post-install.js +71 -7
- package/scripts/preuninstall.js +94 -0
package/CHANGELOG.md
CHANGED
|
@@ -15,6 +15,96 @@ 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.5] - 2025-10-02
|
|
19
|
+
|
|
20
|
+
### Fixed
|
|
21
|
+
- **Critical Bug Fixes** - All commands now enforce global architecture correctly
|
|
22
|
+
- `/p:design` now creates designs in global `analysis/designs/` instead of local `.prjct/designs/`
|
|
23
|
+
- `/p:cleanup` now operates on global project data instead of local `.prjct/`
|
|
24
|
+
- `getDaysSinceLastShip()` now reads from global `memory/context.jsonl` instead of local
|
|
25
|
+
- All commands verify project is initialized before execution
|
|
26
|
+
- Prevents creation of invalid local `.prjct/` structures
|
|
27
|
+
- **Fixed post-install.js** - `currentVersion` now declared before use (prevents ReferenceError)
|
|
28
|
+
- **Added 'Apps' directory** to migration scanner for better project detection
|
|
29
|
+
|
|
30
|
+
### Technical Details
|
|
31
|
+
- **Global Architecture**: All commands now use `pathManager.getGlobalProjectPath(projectId)`
|
|
32
|
+
- design(), cleanup(), getDaysSinceLastShip() fully migrated
|
|
33
|
+
- Prevents LOCAL .prjct/ creation
|
|
34
|
+
- Requires project initialization before command execution
|
|
35
|
+
- **Migration Enhancement**: Added ~/Apps to common directories scan
|
|
36
|
+
- **Post-Install Fix**: currentVersion variable properly declared at function start
|
|
37
|
+
|
|
38
|
+
## [0.4.4] - 2025-10-02
|
|
39
|
+
|
|
40
|
+
### Added
|
|
41
|
+
- **Automatic Editor Command Updates** - Commands auto-update when npm package is updated
|
|
42
|
+
- New `core/editors-config.js` tracks which editors user has installed commands to
|
|
43
|
+
- Stores editor selections in `~/.prjct-cli/config/installed-editors.json`
|
|
44
|
+
- Post-install hook (`scripts/post-install.js`) auto-updates commands after `npm update -g prjct-cli`
|
|
45
|
+
- **Auto-installation on first install** - Detects AI editors and installs commands automatically
|
|
46
|
+
- Ensures version consistency across all configured editors (Claude, Cursor, Windsurf, Codex)
|
|
47
|
+
- No manual reinstallation needed - updates happen automatically
|
|
48
|
+
- Respects user's original editor choices from initial setup
|
|
49
|
+
|
|
50
|
+
- **Automatic Cleanup on Uninstall** - Clean uninstallation removes all traces
|
|
51
|
+
- New `scripts/preuninstall.js` runs before `npm uninstall -g prjct-cli`
|
|
52
|
+
- Automatically removes commands from all tracked editors
|
|
53
|
+
- Deletes tracking configuration from `~/.prjct-cli/config/`
|
|
54
|
+
- Added `uninstallFromEditor()` and `uninstallFromAll()` methods to command installer
|
|
55
|
+
- Added `deleteConfig()` method to editors config
|
|
56
|
+
- Prevents orphaned commands when package is uninstalled
|
|
57
|
+
- Clean exit even if cleanup fails (doesn't block uninstall)
|
|
58
|
+
|
|
59
|
+
- **Automatic Data Migration** - Seamless upgrade from v0.1.0 to v0.4.4
|
|
60
|
+
- Post-install hook automatically detects legacy `.prjct/` projects
|
|
61
|
+
- Migrates data from local structure to new global architecture (`~/.prjct-cli/projects/{id}/`)
|
|
62
|
+
- Scans common project directories (Projects, Documents, Developer, Code, **Apps**)
|
|
63
|
+
- Preserves all project data during migration (now, next, shipped, ideas, memory)
|
|
64
|
+
- Cleans up legacy directories while keeping config for compatibility
|
|
65
|
+
- No user intervention required - migration happens automatically on update
|
|
66
|
+
- Uses existing battle-tested `core/migrator.js` system
|
|
67
|
+
|
|
68
|
+
### Changed
|
|
69
|
+
- **Improved Command Tracking** - Always track editor installations for better update management
|
|
70
|
+
- Modified `installToSelected()` to always save editor config (removed `!forceUpdate` restriction)
|
|
71
|
+
- Ensures tracking happens even during force updates
|
|
72
|
+
- Enables reliable auto-updates across all configured editors
|
|
73
|
+
|
|
74
|
+
### Fixed
|
|
75
|
+
- **Global Architecture Enforcement** - All commands now enforce global architecture correctly
|
|
76
|
+
- `/p:design` now creates designs in global `analysis/designs/` instead of local `.prjct/designs/`
|
|
77
|
+
- `/p:cleanup` now operates on global project data instead of local `.prjct/`
|
|
78
|
+
- `getDaysSinceLastShip()` now reads from global `memory/context.jsonl` instead of local
|
|
79
|
+
- All commands verify project is initialized before execution
|
|
80
|
+
- Prevents creation of invalid local `.prjct/` structures
|
|
81
|
+
- **Fixed post-install.js** - `currentVersion` now declared before use (prevents ReferenceError)
|
|
82
|
+
- **Added 'Apps' directory** to migration scanner for better project detection
|
|
83
|
+
|
|
84
|
+
### Technical Details
|
|
85
|
+
- **Post-Install Hook**: Runs after `npm install -g prjct-cli` or `npm update -g prjct-cli`
|
|
86
|
+
- First install: Auto-detects editors and installs commands
|
|
87
|
+
- Updates: Checks version change and auto-updates commands in tracked editors
|
|
88
|
+
- **Migration**: Automatically detects and migrates legacy projects from v0.1.0
|
|
89
|
+
- Silent operation with debug mode via `DEBUG=1` environment variable
|
|
90
|
+
- **Fixed**: currentVersion variable now properly declared at function start
|
|
91
|
+
- **Enhanced**: Better error handling with debug logging
|
|
92
|
+
- **Pre-Uninstall Hook**: Runs before `npm uninstall -g prjct-cli`
|
|
93
|
+
- Reads tracking config to find all installed editors
|
|
94
|
+
- Removes command directories from each editor
|
|
95
|
+
- Cleans up tracking configuration
|
|
96
|
+
- Fails gracefully to not block uninstall
|
|
97
|
+
- **Migration System**: Automatic upgrade path from v0.1.0 to v0.4.4
|
|
98
|
+
- Scans common project directories including **~/Apps** (fast, non-intrusive)
|
|
99
|
+
- Migrates local `.prjct/` to global `~/.prjct-cli/projects/{id}/`
|
|
100
|
+
- Preserves all data: core, progress, planning, analysis, memory layers
|
|
101
|
+
- Cleans legacy directories while keeping compatibility config
|
|
102
|
+
- Zero data loss, seamless upgrade experience
|
|
103
|
+
- **Global Architecture**: All commands now use `pathManager.getGlobalProjectPath(projectId)`
|
|
104
|
+
- design(), cleanup(), getDaysSinceLastShip() fully migrated
|
|
105
|
+
- Prevents LOCAL .prjct/ creation
|
|
106
|
+
- Requires project initialization before command execution
|
|
107
|
+
|
|
18
108
|
## [0.4.3] - 2025-10-02
|
|
19
109
|
|
|
20
110
|
### Added
|
|
@@ -22,10 +112,20 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
22
112
|
- New `core/editors-config.js` tracks which editors user has installed commands to
|
|
23
113
|
- Stores editor selections in `~/.prjct-cli/config/installed-editors.json`
|
|
24
114
|
- Post-install hook (`scripts/post-install.js`) auto-updates commands after `npm update -g prjct-cli`
|
|
115
|
+
- **Auto-installation on first install** - Detects AI editors and installs commands automatically
|
|
25
116
|
- Ensures version consistency across all configured editors (Claude, Cursor, Windsurf, Codex)
|
|
26
117
|
- No manual reinstallation needed - updates happen automatically
|
|
27
118
|
- Respects user's original editor choices from initial setup
|
|
28
119
|
|
|
120
|
+
- **Automatic Cleanup on Uninstall** - Clean uninstallation removes all traces
|
|
121
|
+
- New `scripts/preuninstall.js` runs before `npm uninstall -g prjct-cli`
|
|
122
|
+
- Automatically removes commands from all tracked editors
|
|
123
|
+
- Deletes tracking configuration from `~/.prjct-cli/config/`
|
|
124
|
+
- Added `uninstallFromEditor()` and `uninstallFromAll()` methods to command installer
|
|
125
|
+
- Added `deleteConfig()` method to editors config
|
|
126
|
+
- Prevents orphaned commands when package is uninstalled
|
|
127
|
+
- Clean exit even if cleanup fails (doesn't block uninstall)
|
|
128
|
+
|
|
29
129
|
- **GitHub Packages Support** - Dual registry publication for better reliability
|
|
30
130
|
- Package now published to both npm and GitHub Packages automatically
|
|
31
131
|
- GitHub Actions workflow updated to publish to both registries in parallel
|
|
@@ -40,9 +140,22 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
40
140
|
- Alternative: GitHub Packages (for advanced users or npm fallback)
|
|
41
141
|
- Clear instructions for both installation methods
|
|
42
142
|
|
|
143
|
+
- **Command Installer Improvements**
|
|
144
|
+
- `installToSelected()` now always saves editor config (removed force-update restriction)
|
|
145
|
+
- Better tracking ensures updates work correctly even for force updates
|
|
146
|
+
- Improved error handling and reporting
|
|
147
|
+
|
|
43
148
|
### Technical Details
|
|
44
|
-
- **Editor Tracking**: Configuration saved after successful command
|
|
45
|
-
- **Post-Install Hook**:
|
|
149
|
+
- **Editor Tracking**: Configuration saved after all successful command installations
|
|
150
|
+
- **Post-Install Hook**:
|
|
151
|
+
- Runs only for global installations, skips for local/dev installs
|
|
152
|
+
- Auto-detects and installs to all AI editors on first install
|
|
153
|
+
- Auto-updates commands when version changes
|
|
154
|
+
- **Pre-Uninstall Hook**:
|
|
155
|
+
- Runs only for global uninstallations
|
|
156
|
+
- Removes slash commands from `~/.claude/`, `~/.cursor/`, `~/.windsurf/`
|
|
157
|
+
- Removes AGENTS.md from `~/.codex/` if present
|
|
158
|
+
- Deletes tracking configuration
|
|
46
159
|
- **Version Detection**: Compares current version with last installed version
|
|
47
160
|
- **Force Update**: Automatically updates commands when version changes
|
|
48
161
|
- **Parallel Publication**: npm and GitHub Packages jobs run simultaneously for faster releases
|
package/bin/prjct
CHANGED
|
@@ -139,6 +139,15 @@ async function main() {
|
|
|
139
139
|
|
|
140
140
|
result = await commands.analyze(analyzeOptions);
|
|
141
141
|
break;
|
|
142
|
+
case '--version':
|
|
143
|
+
case '-v':
|
|
144
|
+
case 'version':
|
|
145
|
+
const packageJson = require('../package.json');
|
|
146
|
+
result = {
|
|
147
|
+
success: true,
|
|
148
|
+
message: `prjct-cli v${packageJson.version}`
|
|
149
|
+
};
|
|
150
|
+
break;
|
|
142
151
|
case '--help':
|
|
143
152
|
case '-h':
|
|
144
153
|
case 'help':
|
|
@@ -444,8 +444,8 @@ For detailed implementation, see prjct-cli documentation.
|
|
|
444
444
|
const totalUpdated = Object.values(results)
|
|
445
445
|
.reduce((sum, r) => sum + (r.updated || 0), 0)
|
|
446
446
|
|
|
447
|
-
//
|
|
448
|
-
if (
|
|
447
|
+
// Always save editor selections to config for tracking updates
|
|
448
|
+
if (successfulEditors.length > 0) {
|
|
449
449
|
await this.saveEditorConfig(successfulEditors)
|
|
450
450
|
}
|
|
451
451
|
|
|
@@ -807,6 +807,119 @@ This command uses the global prjct architecture:
|
|
|
807
807
|
|
|
808
808
|
return results
|
|
809
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
|
+
}
|
|
810
923
|
}
|
|
811
924
|
|
|
812
925
|
module.exports = new CommandInstaller()
|
package/core/commands.js
CHANGED
|
@@ -1236,9 +1236,23 @@ ${dryRun ? '⚠️ DRY RUN - No changes were made' : '✅ All changes applied su
|
|
|
1236
1236
|
try {
|
|
1237
1237
|
await this.initializeAgent()
|
|
1238
1238
|
|
|
1239
|
+
// Verify project is initialized
|
|
1240
|
+
if (!await configManager.isConfigured(projectPath)) {
|
|
1241
|
+
return {
|
|
1242
|
+
success: false,
|
|
1243
|
+
message: this.agent.formatResponse(
|
|
1244
|
+
`Project not initialized. Run ${this.agentInfo.config.commandPrefix}init first.`,
|
|
1245
|
+
'warning'
|
|
1246
|
+
),
|
|
1247
|
+
}
|
|
1248
|
+
}
|
|
1249
|
+
|
|
1239
1250
|
const type = options.type || 'architecture'
|
|
1240
1251
|
|
|
1241
|
-
|
|
1252
|
+
// Use global architecture
|
|
1253
|
+
const projectId = await configManager.getProjectId(projectPath)
|
|
1254
|
+
const globalPath = pathManager.getGlobalProjectPath(projectId)
|
|
1255
|
+
const designDir = path.join(globalPath, 'analysis', 'designs')
|
|
1242
1256
|
await this.agent.createDirectory(designDir)
|
|
1243
1257
|
|
|
1244
1258
|
let designContent = ''
|
|
@@ -1484,7 +1498,11 @@ ${diagram}
|
|
|
1484
1498
|
async getDaysSinceLastShip(projectPath) {
|
|
1485
1499
|
try {
|
|
1486
1500
|
await this.initializeAgent()
|
|
1487
|
-
|
|
1501
|
+
|
|
1502
|
+
// Use global architecture
|
|
1503
|
+
const projectId = await configManager.getProjectId(projectPath)
|
|
1504
|
+
const globalPath = pathManager.getGlobalProjectPath(projectId)
|
|
1505
|
+
const memoryFile = path.join(globalPath, 'memory', 'context.jsonl')
|
|
1488
1506
|
const memory = await this.agent.readFile(memoryFile)
|
|
1489
1507
|
const lines = memory
|
|
1490
1508
|
.trim()
|
|
@@ -1670,14 +1688,28 @@ ${diagram}
|
|
|
1670
1688
|
async cleanup(projectPath = process.cwd()) {
|
|
1671
1689
|
try {
|
|
1672
1690
|
await this.initializeAgent()
|
|
1673
|
-
|
|
1691
|
+
|
|
1692
|
+
// Verify project is initialized
|
|
1693
|
+
if (!await configManager.isConfigured(projectPath)) {
|
|
1694
|
+
return {
|
|
1695
|
+
success: false,
|
|
1696
|
+
message: this.agent.formatResponse(
|
|
1697
|
+
`Project not initialized. Run ${this.agentInfo.config.commandPrefix}init first.`,
|
|
1698
|
+
'warning'
|
|
1699
|
+
),
|
|
1700
|
+
}
|
|
1701
|
+
}
|
|
1702
|
+
|
|
1703
|
+
// Use global architecture
|
|
1704
|
+
const projectId = await configManager.getProjectId(projectPath)
|
|
1705
|
+
const globalPath = pathManager.getGlobalProjectPath(projectId)
|
|
1674
1706
|
|
|
1675
1707
|
let totalFreed = 0
|
|
1676
1708
|
let filesRemoved = 0
|
|
1677
1709
|
let tasksArchived = 0
|
|
1678
1710
|
|
|
1679
1711
|
try {
|
|
1680
|
-
const tempDir = path.join(
|
|
1712
|
+
const tempDir = path.join(globalPath, 'temp')
|
|
1681
1713
|
const tempFiles = await fs.readdir(tempDir).catch(() => [])
|
|
1682
1714
|
for (const file of tempFiles) {
|
|
1683
1715
|
const filePath = path.join(tempDir, file)
|
|
@@ -1690,7 +1722,7 @@ ${diagram}
|
|
|
1690
1722
|
}
|
|
1691
1723
|
|
|
1692
1724
|
try {
|
|
1693
|
-
const memoryFile = path.join(
|
|
1725
|
+
const memoryFile = path.join(globalPath, 'memory', 'context.jsonl')
|
|
1694
1726
|
const content = await this.agent.readFile(memoryFile)
|
|
1695
1727
|
const lines = content.split('\n').filter(line => line.trim())
|
|
1696
1728
|
const now = new Date()
|
|
@@ -1714,7 +1746,7 @@ ${diagram}
|
|
|
1714
1746
|
}
|
|
1715
1747
|
|
|
1716
1748
|
if (archivedLines.length > 0) {
|
|
1717
|
-
const archiveFile = path.join(
|
|
1749
|
+
const archiveFile = path.join(globalPath, 'memory', `archive-${now.toISOString().split('T')[0]}.jsonl`)
|
|
1718
1750
|
await this.agent.writeFile(archiveFile, archivedLines.join('\n') + '\n')
|
|
1719
1751
|
await this.agent.writeFile(memoryFile, recentLines.join('\n') + '\n')
|
|
1720
1752
|
tasksArchived = archivedLines.length
|
|
@@ -1722,10 +1754,10 @@ ${diagram}
|
|
|
1722
1754
|
} catch (e) {
|
|
1723
1755
|
}
|
|
1724
1756
|
|
|
1725
|
-
const files = await fs.readdir(
|
|
1757
|
+
const files = await fs.readdir(globalPath)
|
|
1726
1758
|
for (const file of files) {
|
|
1727
1759
|
if (file.endsWith('.md') || file.endsWith('.txt')) {
|
|
1728
|
-
const filePath = path.join(
|
|
1760
|
+
const filePath = path.join(globalPath, file)
|
|
1729
1761
|
const stats = await fs.stat(filePath)
|
|
1730
1762
|
if (stats.size === 0) {
|
|
1731
1763
|
await fs.unlink(filePath)
|
|
@@ -1735,7 +1767,7 @@ ${diagram}
|
|
|
1735
1767
|
}
|
|
1736
1768
|
|
|
1737
1769
|
try {
|
|
1738
|
-
const shippedFile = path.join(
|
|
1770
|
+
const shippedFile = path.join(globalPath, 'progress', 'shipped.md')
|
|
1739
1771
|
const content = await this.agent.readFile(shippedFile)
|
|
1740
1772
|
const lines = content.split('\n')
|
|
1741
1773
|
const now = new Date()
|
package/core/editors-config.js
CHANGED
|
@@ -155,6 +155,24 @@ class EditorsConfig {
|
|
|
155
155
|
return false
|
|
156
156
|
}
|
|
157
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
|
+
}
|
|
158
176
|
}
|
|
159
177
|
|
|
160
178
|
module.exports = new EditorsConfig()
|
package/core/migrator.js
CHANGED
|
@@ -510,7 +510,7 @@ class Migrator {
|
|
|
510
510
|
if (deepScan) {
|
|
511
511
|
searchPaths = [os.homedir()]
|
|
512
512
|
} else {
|
|
513
|
-
const commonDirs = ['Projects', 'Documents', 'Developer', 'Code', 'dev', 'workspace', 'repos', 'src']
|
|
513
|
+
const commonDirs = ['Projects', 'Documents', 'Developer', 'Code', 'dev', 'workspace', 'repos', 'src', 'Apps']
|
|
514
514
|
searchPaths = commonDirs
|
|
515
515
|
.map(dir => path.join(os.homedir(), dir))
|
|
516
516
|
.filter(dirPath => {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "prjct-cli",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.5",
|
|
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": {
|
|
@@ -12,6 +12,7 @@
|
|
|
12
12
|
},
|
|
13
13
|
"scripts": {
|
|
14
14
|
"postinstall": "node scripts/post-install.js",
|
|
15
|
+
"preuninstall": "node scripts/preuninstall.js",
|
|
15
16
|
"install-global": "./scripts/install.sh",
|
|
16
17
|
"test": "echo 'No tests configured'",
|
|
17
18
|
"lint": "eslint \"**/*.js\" --ignore-pattern \"node_modules/**\" --ignore-pattern \"website/**\"",
|
|
@@ -69,6 +70,7 @@
|
|
|
69
70
|
"scripts/install.sh",
|
|
70
71
|
"scripts/verify-installation.sh",
|
|
71
72
|
"scripts/post-install.js",
|
|
73
|
+
"scripts/preuninstall.js",
|
|
72
74
|
"LICENSE",
|
|
73
75
|
"README.md",
|
|
74
76
|
"CHANGELOG.md",
|
package/scripts/post-install.js
CHANGED
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
* 3. If config exists and version changed, force-update commands in tracked editors
|
|
13
13
|
* 4. Update version in config
|
|
14
14
|
*
|
|
15
|
-
* @version 0.4.
|
|
15
|
+
* @version 0.4.4
|
|
16
16
|
*/
|
|
17
17
|
|
|
18
18
|
const path = require('path')
|
|
@@ -21,11 +21,18 @@ const { execSync } = require('child_process')
|
|
|
21
21
|
|
|
22
22
|
async function main() {
|
|
23
23
|
try {
|
|
24
|
+
// Get current package version (needed by multiple code paths)
|
|
25
|
+
const packageJson = require('../package.json')
|
|
26
|
+
const currentVersion = packageJson.version
|
|
27
|
+
|
|
24
28
|
// Check if this is a global installation
|
|
25
29
|
const isGlobal = await checkIfGlobalInstall()
|
|
26
30
|
|
|
27
31
|
if (!isGlobal) {
|
|
28
32
|
// Skip post-install for local/dev installations
|
|
33
|
+
if (process.env.DEBUG) {
|
|
34
|
+
console.log(chalk.gray('[post-install] Skipping - not a global install'))
|
|
35
|
+
}
|
|
29
36
|
return
|
|
30
37
|
}
|
|
31
38
|
|
|
@@ -34,15 +41,44 @@ async function main() {
|
|
|
34
41
|
const configExists = await editorsConfig.configExists()
|
|
35
42
|
|
|
36
43
|
if (!configExists) {
|
|
37
|
-
// First-time install
|
|
38
|
-
|
|
44
|
+
// First-time install - auto-detect and install to all editors
|
|
45
|
+
console.log(chalk.cyan('\n🔍 First-time installation detected...\n'))
|
|
46
|
+
|
|
47
|
+
// Load command installer
|
|
48
|
+
const commandInstaller = require('../core/command-installer')
|
|
49
|
+
|
|
50
|
+
// Detect available editors
|
|
51
|
+
const detected = await commandInstaller.detectEditors()
|
|
52
|
+
const detectedEditors = Object.entries(detected)
|
|
53
|
+
.filter(([_, info]) => info.detected)
|
|
54
|
+
.map(([key, _]) => key)
|
|
55
|
+
|
|
56
|
+
if (detectedEditors.length === 0) {
|
|
57
|
+
// No editors detected, user will install manually later
|
|
58
|
+
console.log(chalk.yellow('ℹ️ No AI editors detected'))
|
|
59
|
+
console.log(chalk.gray(' Run `prjct install` when you set up Claude Code, Cursor, or Windsurf\n'))
|
|
60
|
+
return
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
console.log(chalk.cyan(`📦 Installing commands to: ${detectedEditors.map(k => commandInstaller.editors[k]?.name || k).join(', ')}\n`))
|
|
64
|
+
|
|
65
|
+
// Install to all detected editors
|
|
66
|
+
const results = await commandInstaller.installToAll(false)
|
|
67
|
+
|
|
68
|
+
if (results.success) {
|
|
69
|
+
console.log(chalk.green(`✅ Commands installed in: ${results.editors.join(', ')}`))
|
|
70
|
+
console.log(chalk.gray(` Commands installed: ${results.totalInstalled}`))
|
|
71
|
+
console.log(chalk.cyan(`\n✨ prjct-cli ${currentVersion} is ready!\n`))
|
|
72
|
+
} else {
|
|
73
|
+
console.log(chalk.yellow('⚠️ Some editors could not be configured'))
|
|
74
|
+
if (process.env.DEBUG) {
|
|
75
|
+
console.log(chalk.gray(` Error: ${results.message || 'Unknown error'}`))
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
39
79
|
return
|
|
40
80
|
}
|
|
41
81
|
|
|
42
|
-
// Get current package version
|
|
43
|
-
const packageJson = require('../package.json')
|
|
44
|
-
const currentVersion = packageJson.version
|
|
45
|
-
|
|
46
82
|
// Check if version has changed
|
|
47
83
|
const versionChanged = await editorsConfig.hasVersionChanged(currentVersion)
|
|
48
84
|
|
|
@@ -78,6 +114,34 @@ async function main() {
|
|
|
78
114
|
// Update version in config
|
|
79
115
|
await editorsConfig.updateVersion(currentVersion)
|
|
80
116
|
|
|
117
|
+
// Auto-migrate legacy projects (v0.1.0 → v0.4.4)
|
|
118
|
+
try {
|
|
119
|
+
console.log(chalk.cyan('\n🔍 Checking for legacy projects to migrate...\n'))
|
|
120
|
+
|
|
121
|
+
const migrator = require('../core/migrator')
|
|
122
|
+
|
|
123
|
+
// Quick scan of common directories (not entire home directory)
|
|
124
|
+
const summary = await migrator.migrateAll({
|
|
125
|
+
deepScan: false, // Only scan common dirs (fast)
|
|
126
|
+
removeLegacy: false, // Keep legacy dirs for safety
|
|
127
|
+
cleanupLegacy: true, // Remove old dirs but keep config
|
|
128
|
+
dryRun: false, // Actually perform migration
|
|
129
|
+
interactive: false // No user prompts during npm install
|
|
130
|
+
})
|
|
131
|
+
|
|
132
|
+
if (summary.successfullyMigrated > 0) {
|
|
133
|
+
console.log(chalk.green(`✅ Migrated ${summary.successfullyMigrated} legacy project(s) to new structure`))
|
|
134
|
+
console.log(chalk.gray(` Data location: ~/.prjct-cli/projects/`))
|
|
135
|
+
} else if (summary.totalFound > 0 && summary.alreadyMigrated === summary.totalFound) {
|
|
136
|
+
console.log(chalk.gray('ℹ️ All projects already using new structure'))
|
|
137
|
+
}
|
|
138
|
+
} catch (migrationError) {
|
|
139
|
+
// Don't block install if migration fails
|
|
140
|
+
if (process.env.DEBUG) {
|
|
141
|
+
console.error(chalk.yellow('[post-install] Migration warning:'), migrationError.message)
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
81
145
|
console.log(chalk.cyan(`\n✨ prjct-cli ${currentVersion} is ready!\n`))
|
|
82
146
|
|
|
83
147
|
} catch (error) {
|
|
@@ -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
|
+
})
|