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 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.2",
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
+ })