prjct-cli 0.8.4 → 0.8.6

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,45 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.8.6] - 2025-10-05
11
+
12
+ ### Fixed
13
+
14
+ - **Critical: Command update system** - Fixed command synchronization to ALWAYS update to latest version
15
+ - ✅ Removed mtime comparison (was causing commands to stay outdated)
16
+ - ✅ ALWAYS overwrites existing commands with latest templates
17
+ - ✅ Guarantees all clients get updated commands on `npm update`
18
+ - ✅ Preserves legacy commands (removed: always 0)
19
+ - 🐛 **Bug fixed**: `now.md`, `done.md` and other commands now update correctly across all client installations
20
+
21
+ - **Setup architecture redesigned** - Dual-defense strategy for reliable setup
22
+ - ✅ First-use setup check in `bin/prjct` (guaranteed execution)
23
+ - ✅ Optional postinstall optimization (when scripts enabled)
24
+ - ✅ Works in CI/CD environments with `--ignore-scripts`
25
+ - ✅ Version tracking via `editors-config.js`
26
+ - ✅ Zero manual steps required
27
+
28
+ ### Changed
29
+
30
+ - **Command sync behavior** (`syncCommands()`)
31
+ - Previous: Compared mtime, only updated if source newer → ❌ Unreliable
32
+ - Now: ALWAYS overwrites existing commands → ✅ Always latest version
33
+ - Preserves legacy/custom commands not in templates
34
+ - Result: `{ added: X, updated: Y, removed: 0 }`
35
+
36
+ - **Setup flow**
37
+ - Centralized all logic in `core/infrastructure/setup.js`
38
+ - `bin/prjct` checks version on every execution
39
+ - `scripts/postinstall.js` simplified to optional helper
40
+ - Auto-setup triggers on version mismatch or first use
41
+
42
+ ### Technical
43
+
44
+ - Reduced `scripts/postinstall.js` from 250 → 69 lines
45
+ - Created `core/infrastructure/setup.js` (167 lines) - single source of truth
46
+ - Modified `bin/prjct` to include version check (34 lines)
47
+ - Removed `.setup-complete` marker (using JSON version tracking instead)
48
+
10
49
  ## [0.8.2] - 2025-10-05
11
50
 
12
51
  ### Changed
package/bin/prjct CHANGED
@@ -1,181 +1,34 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  /**
4
- * prjct CLI - 100% Agentic Entry Point
4
+ * prjct CLI entry point
5
5
  *
6
- * Uses command-registry as single source of truth.
7
- * Zero hardcoded logic - all commands resolved dynamically.
6
+ * Auto-setup on first use (like Astro, Vite, etc.)
8
7
  */
9
8
 
10
- const PrjctCommands = require('../core/commands')
11
- const registry = require('../core/command-registry')
12
-
13
- async function main() {
14
- const [commandName, ...rawArgs] = process.argv.slice(2)
15
-
16
- // === SPECIAL COMMANDS (version, help) ===
17
-
18
- if (['-v', '--version', 'version'].includes(commandName)) {
19
- const packageJson = require('../package.json')
20
- console.log(`prjct-cli v${packageJson.version}`)
21
- process.exit(0)
22
- }
23
-
24
- if (['-h', '--help', 'help', undefined].includes(commandName)) {
25
- displayHelp()
26
- process.exit(0)
27
- }
28
-
29
- // === DYNAMIC COMMAND EXECUTION ===
9
+ const { VERSION } = require('../core/utils/version')
10
+ const editorsConfig = require('../core/infrastructure/editors-config')
30
11
 
12
+ // Ensure setup has run for this version
13
+ ;(async function ensureSetup() {
31
14
  try {
32
- // 1. Find command in registry
33
- const cmd = registry.getByName(commandName)
34
-
35
- if (!cmd) {
36
- console.error(`Unknown command: ${commandName}`)
37
- console.error(`\nUse 'prjct --help' to see available commands.`)
38
- process.exit(1)
39
- }
40
-
41
- // 2. Check if deprecated (before checking implemented)
42
- if (cmd.deprecated) {
43
- console.error(`Command '${commandName}' is deprecated.`)
44
- if (cmd.replacedBy) {
45
- console.error(`Use 'prjct ${cmd.replacedBy}' instead.`)
46
- }
47
- process.exit(1)
48
- }
49
-
50
- // 3. Check if implemented
51
- if (!cmd.implemented) {
52
- console.error(`Command '${commandName}' exists but is not yet implemented.`)
53
- console.error(`Check the roadmap or contribute: https://github.com/jlopezlira/prjct-cli`)
54
- console.error(`\nUse 'prjct --help' to see available commands.`)
55
- process.exit(1)
56
- }
57
-
58
- // 4. Parse arguments based on command definition
59
- const { parsedArgs, options } = parseCommandArgs(cmd, rawArgs)
60
-
61
- // 5. Instantiate commands handler
62
- const commands = new PrjctCommands()
15
+ const lastVersion = await editorsConfig.getLastVersion()
63
16
 
64
- // 6. Execute command
65
- let result
17
+ if (!lastVersion || lastVersion !== VERSION) {
18
+ console.log('\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━')
19
+ console.log('🔧 One-time setup (v' + VERSION + ')...')
20
+ console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n')
66
21
 
67
- // Commands with special option handling
68
- if (commandName === 'design') {
69
- const target = parsedArgs.join(' ')
70
- result = await commands.design(target, options)
71
- } else if (commandName === 'analyze') {
72
- result = await commands.analyze(options)
73
- } else if (commandName === 'cleanup') {
74
- result = await commands.cleanup(options)
75
- } else if (commandName === 'setup') {
76
- result = await commands.setup(options)
77
- } else if (commandName === 'migrate-all') {
78
- result = await commands.migrateAll(options)
79
- } else if (commandName === 'progress') {
80
- const period = parsedArgs[0] || 'week'
81
- result = await commands.progress(period)
82
- } else if (commandName === 'build') {
83
- const taskOrNumber = parsedArgs.join(' ')
84
- result = await commands.build(taskOrNumber)
85
- } else {
86
- // Standard commands (most common case)
87
- const param = parsedArgs.join(' ') || null
88
- result = await commands[commandName](param)
89
- }
22
+ const setup = require('../core/infrastructure/setup')
23
+ await setup.run()
90
24
 
91
- // 7. Display result
92
- if (result && result.message) {
93
- console.log(result.message)
25
+ console.log('✓ Setup complete!\n')
94
26
  }
95
-
96
- process.exit(result && result.success ? 0 : 1)
97
27
  } catch (error) {
98
- console.error('Error:', error.message)
99
- if (process.env.DEBUG) {
100
- console.error(error.stack)
101
- }
102
- process.exit(1)
28
+ console.error('\n⚠️ Setup warning:', error.message)
29
+ console.error('You can run setup manually: prjct setup\n')
103
30
  }
104
- }
105
31
 
106
- /**
107
- * Parse command arguments dynamically
108
- */
109
- function parseCommandArgs(cmd, rawArgs) {
110
- const parsedArgs = []
111
- const options = {}
112
-
113
- for (let i = 0; i < rawArgs.length; i++) {
114
- const arg = rawArgs[i]
115
-
116
- if (arg.startsWith('--')) {
117
- // Handle flags
118
- const flagName = arg.slice(2)
119
-
120
- // Check if next arg is a value (doesn't start with --)
121
- if (i + 1 < rawArgs.length && !rawArgs[i + 1].startsWith('--')) {
122
- options[flagName] = rawArgs[++i]
123
- } else {
124
- options[flagName] = true
125
- }
126
- } else {
127
- parsedArgs.push(arg)
128
- }
129
- }
130
-
131
- return { parsedArgs, options }
132
- }
133
-
134
- /**
135
- * Display help using registry
136
- */
137
- function displayHelp() {
138
- const categories = registry.getCategories()
139
- const categorizedCommands = {}
140
-
141
- // Group commands by category (exclude deprecated)
142
- registry.getTerminalCommands().forEach((cmd) => {
143
- if (cmd.deprecated) return
144
-
145
- if (!categorizedCommands[cmd.category]) {
146
- categorizedCommands[cmd.category] = []
147
- }
148
- categorizedCommands[cmd.category].push(cmd)
149
- })
150
-
151
- console.log('prjct - Developer momentum tool for solo builders')
152
- console.log('\nAvailable commands:\n')
153
-
154
- // Display commands by category
155
- Object.entries(categorizedCommands).forEach(([categoryKey, cmds]) => {
156
- const categoryInfo = categories[categoryKey]
157
- console.log(` ${categoryInfo.title}:`)
158
-
159
- cmds.forEach((cmd) => {
160
- const params = cmd.params ? ` ${cmd.params}` : ''
161
- const spacing = ' '.repeat(Math.max(20 - cmd.name.length - params.length, 1))
162
- const impl = cmd.implemented ? '' : ' (not implemented)'
163
- console.log(` ${cmd.name}${params}${spacing}${cmd.description}${impl}`)
164
- })
165
-
166
- console.log('')
167
- })
168
-
169
- const stats = registry.getStats()
170
- console.log(`Total: ${stats.implemented} implemented / ${stats.total} commands`)
171
- console.log('\nFor more info: https://prjct.app')
172
- }
173
-
174
- // Run CLI
175
- main().catch((error) => {
176
- console.error('Fatal error:', error.message)
177
- if (process.env.DEBUG) {
178
- console.error(error.stack)
179
- }
180
- process.exit(1)
181
- })
32
+ // Continue to main CLI logic
33
+ require('../core/index')
34
+ })()
package/core/index.js ADDED
@@ -0,0 +1,178 @@
1
+ /**
2
+ * prjct CLI - Main Entry Point
3
+ *
4
+ * This file is required by bin/prjct after setup verification
5
+ */
6
+
7
+ const PrjctCommands = require('./commands')
8
+ const registry = require('./command-registry')
9
+
10
+ async function main() {
11
+ const [commandName, ...rawArgs] = process.argv.slice(2)
12
+
13
+ // === SPECIAL COMMANDS (version, help) ===
14
+
15
+ if (['-v', '--version', 'version'].includes(commandName)) {
16
+ const packageJson = require('../package.json')
17
+ console.log(`prjct-cli v${packageJson.version}`)
18
+ process.exit(0)
19
+ }
20
+
21
+ if (['-h', '--help', 'help', undefined].includes(commandName)) {
22
+ displayHelp()
23
+ process.exit(0)
24
+ }
25
+
26
+ // === DYNAMIC COMMAND EXECUTION ===
27
+
28
+ try {
29
+ // 1. Find command in registry
30
+ const cmd = registry.getByName(commandName)
31
+
32
+ if (!cmd) {
33
+ console.error(`Unknown command: ${commandName}`)
34
+ console.error(`\nUse 'prjct --help' to see available commands.`)
35
+ process.exit(1)
36
+ }
37
+
38
+ // 2. Check if deprecated
39
+ if (cmd.deprecated) {
40
+ console.error(`Command '${commandName}' is deprecated.`)
41
+ if (cmd.replacedBy) {
42
+ console.error(`Use 'prjct ${cmd.replacedBy}' instead.`)
43
+ }
44
+ process.exit(1)
45
+ }
46
+
47
+ // 3. Check if implemented
48
+ if (!cmd.implemented) {
49
+ console.error(`Command '${commandName}' exists but is not yet implemented.`)
50
+ console.error(`Check the roadmap or contribute: https://github.com/jlopezlira/prjct-cli`)
51
+ console.error(`\nUse 'prjct --help' to see available commands.`)
52
+ process.exit(1)
53
+ }
54
+
55
+ // 4. Parse arguments
56
+ const { parsedArgs, options } = parseCommandArgs(cmd, rawArgs)
57
+
58
+ // 5. Instantiate commands handler
59
+ const commands = new PrjctCommands()
60
+
61
+ // 6. Execute command
62
+ let result
63
+
64
+ // Commands with special option handling
65
+ if (commandName === 'design') {
66
+ const target = parsedArgs.join(' ')
67
+ result = await commands.design(target, options)
68
+ } else if (commandName === 'analyze') {
69
+ result = await commands.analyze(options)
70
+ } else if (commandName === 'cleanup') {
71
+ result = await commands.cleanup(options)
72
+ } else if (commandName === 'setup') {
73
+ result = await commands.setup(options)
74
+ } else if (commandName === 'migrate-all') {
75
+ result = await commands.migrateAll(options)
76
+ } else if (commandName === 'progress') {
77
+ const period = parsedArgs[0] || 'week'
78
+ result = await commands.progress(period)
79
+ } else if (commandName === 'build') {
80
+ const taskOrNumber = parsedArgs.join(' ')
81
+ result = await commands.build(taskOrNumber)
82
+ } else {
83
+ // Standard commands
84
+ const param = parsedArgs.join(' ') || null
85
+ result = await commands[commandName](param)
86
+ }
87
+
88
+ // 7. Display result
89
+ if (result && result.message) {
90
+ console.log(result.message)
91
+ }
92
+
93
+ process.exit(result && result.success ? 0 : 1)
94
+ } catch (error) {
95
+ console.error('Error:', error.message)
96
+ if (process.env.DEBUG) {
97
+ console.error(error.stack)
98
+ }
99
+ process.exit(1)
100
+ }
101
+ }
102
+
103
+ /**
104
+ * Parse command arguments dynamically
105
+ */
106
+ function parseCommandArgs(cmd, rawArgs) {
107
+ const parsedArgs = []
108
+ const options = {}
109
+
110
+ for (let i = 0; i < rawArgs.length; i++) {
111
+ const arg = rawArgs[i]
112
+
113
+ if (arg.startsWith('--')) {
114
+ // Handle flags
115
+ const flagName = arg.slice(2)
116
+
117
+ // Check if next arg is a value
118
+ if (i + 1 < rawArgs.length && !rawArgs[i + 1].startsWith('--')) {
119
+ options[flagName] = rawArgs[++i]
120
+ } else {
121
+ options[flagName] = true
122
+ }
123
+ } else {
124
+ parsedArgs.push(arg)
125
+ }
126
+ }
127
+
128
+ return { parsedArgs, options }
129
+ }
130
+
131
+ /**
132
+ * Display help using registry
133
+ */
134
+ function displayHelp() {
135
+ const categories = registry.getCategories()
136
+ const categorizedCommands = {}
137
+
138
+ // Group commands by category (exclude deprecated)
139
+ registry.getTerminalCommands().forEach((cmd) => {
140
+ if (cmd.deprecated) return
141
+
142
+ if (!categorizedCommands[cmd.category]) {
143
+ categorizedCommands[cmd.category] = []
144
+ }
145
+ categorizedCommands[cmd.category].push(cmd)
146
+ })
147
+
148
+ console.log('prjct - Developer momentum tool for solo builders')
149
+ console.log('\nAvailable commands:\n')
150
+
151
+ // Display commands by category
152
+ Object.entries(categorizedCommands).forEach(([categoryKey, cmds]) => {
153
+ const categoryInfo = categories[categoryKey]
154
+ console.log(` ${categoryInfo.title}:`)
155
+
156
+ cmds.forEach((cmd) => {
157
+ const params = cmd.params ? ` ${cmd.params}` : ''
158
+ const spacing = ' '.repeat(Math.max(20 - cmd.name.length - params.length, 1))
159
+ const impl = cmd.implemented ? '' : ' (not implemented)'
160
+ console.log(` ${cmd.name}${params}${spacing}${cmd.description}${impl}`)
161
+ })
162
+
163
+ console.log('')
164
+ })
165
+
166
+ const stats = registry.getStats()
167
+ console.log(`Total: ${stats.implemented} implemented / ${stats.total} commands`)
168
+ console.log('\nFor more info: https://prjct.app')
169
+ }
170
+
171
+ // Run CLI
172
+ main().catch((error) => {
173
+ console.error('Fatal error:', error.message)
174
+ if (process.env.DEBUG) {
175
+ console.error(error.stack)
176
+ }
177
+ process.exit(1)
178
+ })
@@ -280,7 +280,7 @@ class CommandInstaller {
280
280
  errors: [],
281
281
  }
282
282
 
283
- // Detect new and updated files
283
+ // Install/update all template files (always overwrite)
284
284
  for (const file of templateFiles) {
285
285
  try {
286
286
  const sourcePath = path.join(this.templatesDir, file)
@@ -289,40 +289,23 @@ class CommandInstaller {
289
289
  // Check if file exists in installed location
290
290
  const exists = installedFiles.includes(file)
291
291
 
292
+ // Read and write (always overwrite to ensure latest version)
293
+ const content = await fs.readFile(sourcePath, 'utf-8')
294
+ await fs.writeFile(destPath, content, 'utf-8')
295
+
292
296
  if (!exists) {
293
- // New file
294
- const content = await fs.readFile(sourcePath, 'utf-8')
295
- await fs.writeFile(destPath, content, 'utf-8')
296
297
  results.added++
297
298
  } else {
298
- // Check if updated (compare modification time or content)
299
- const sourceStats = await fs.stat(sourcePath)
300
- const destStats = await fs.stat(destPath)
301
-
302
- if (sourceStats.mtime > destStats.mtime) {
303
- // Updated file
304
- const content = await fs.readFile(sourcePath, 'utf-8')
305
- await fs.writeFile(destPath, content, 'utf-8')
306
- results.updated++
307
- }
299
+ results.updated++
308
300
  }
309
301
  } catch (error) {
310
302
  results.errors.push({ file, error: error.message })
311
303
  }
312
304
  }
313
305
 
314
- // Detect and remove orphaned files
315
- const orphans = installedFiles.filter((file) => !templateFiles.includes(file))
316
-
317
- for (const orphan of orphans) {
318
- try {
319
- const orphanPath = path.join(this.claudeCommandsPath, orphan)
320
- await fs.unlink(orphanPath)
321
- results.removed++
322
- } catch (error) {
323
- results.errors.push({ file: orphan, error: error.message })
324
- }
325
- }
306
+ // Note: We do NOT remove orphaned files
307
+ // Legacy commands from older versions are preserved
308
+ // to avoid breaking existing workflows
326
309
 
327
310
  return results
328
311
  } catch (error) {
@@ -0,0 +1,167 @@
1
+ const { execSync } = require('child_process')
2
+ const installer = require('./command-installer')
3
+ const migrator = require('./migrator')
4
+ const editorsConfig = require('./editors-config')
5
+ const { VERSION } = require('../utils/version')
6
+
7
+ /**
8
+ * Setup Module - Core installation logic
9
+ *
10
+ * Ejecuta TODO el setup necesario para prjct-cli:
11
+ * 1. Instalar Claude Code CLI si falta
12
+ * 2. Sincronizar comandos a ~/.claude/commands/p/
13
+ * 3. Instalar configuración global ~/.claude/CLAUDE.md
14
+ * 4. Migrar proyectos legacy automáticamente
15
+ * 5. Guardar versión en editors-config
16
+ *
17
+ * Este módulo es llamado desde:
18
+ * - core/index.js (en primer uso del CLI)
19
+ * - scripts/postinstall.js (si npm scripts están habilitados)
20
+ *
21
+ * @version 0.8.5
22
+ */
23
+
24
+ // Colors
25
+ const CYAN = '\x1b[36m'
26
+ const GREEN = '\x1b[32m'
27
+ const YELLOW = '\x1b[33m'
28
+ const MAGENTA = '\x1b[35m'
29
+ const BOLD = '\x1b[1m'
30
+ const DIM = '\x1b[2m'
31
+ const NC = '\x1b[0m'
32
+
33
+ /**
34
+ * Check if Claude Code CLI is installed
35
+ */
36
+ async function hasClaudeCodeCLI() {
37
+ try {
38
+ execSync('which claude', { stdio: 'ignore' })
39
+ return true
40
+ } catch {
41
+ return false
42
+ }
43
+ }
44
+
45
+ /**
46
+ * Install Claude Code CLI
47
+ */
48
+ async function installClaudeCode() {
49
+ try {
50
+ console.log(`${YELLOW}📦 Claude Code not found. Installing...${NC}`)
51
+ console.log('')
52
+ execSync('npm install -g @anthropic-ai/claude-code', { stdio: 'inherit' })
53
+ console.log('')
54
+ console.log(`${GREEN}✓${NC} Claude Code installed successfully`)
55
+ console.log('')
56
+ return true
57
+ } catch (error) {
58
+ console.log(`${YELLOW}⚠️ Failed to install Claude Code: ${error.message}${NC}`)
59
+ console.log(`${DIM}Please install manually: npm install -g @anthropic-ai/claude-code${NC}`)
60
+ console.log('')
61
+ return false
62
+ }
63
+ }
64
+
65
+ /**
66
+ * Main setup function
67
+ */
68
+ async function run() {
69
+ const results = {
70
+ claudeInstalled: false,
71
+ commandsAdded: 0,
72
+ commandsUpdated: 0,
73
+ configAction: null,
74
+ projectsMigrated: 0
75
+ }
76
+
77
+ // Step 1: Ensure Claude Code CLI is installed
78
+ const hasClaude = await hasClaudeCodeCLI()
79
+
80
+ if (!hasClaude) {
81
+ const installed = await installClaudeCode()
82
+ if (installed) {
83
+ results.claudeInstalled = true
84
+ } else {
85
+ throw new Error('Claude Code installation failed')
86
+ }
87
+ }
88
+
89
+ // Step 2: Detect Claude directory (for commands)
90
+ const claudeDetected = await installer.detectClaude()
91
+
92
+ if (claudeDetected) {
93
+ // Step 3: Sync commands
94
+ const syncResult = await installer.syncCommands()
95
+
96
+ if (syncResult.success) {
97
+ results.commandsAdded = syncResult.added
98
+ results.commandsUpdated = syncResult.updated
99
+ }
100
+
101
+ // Step 4: Install global configuration
102
+ const configResult = await installer.installGlobalConfig()
103
+
104
+ if (configResult.success) {
105
+ results.configAction = configResult.action
106
+ }
107
+
108
+ // Step 5: Migrate legacy projects automatically
109
+ const migrationResult = await migrator.migrateAll({
110
+ deepScan: false,
111
+ cleanupLegacy: true,
112
+ dryRun: false
113
+ })
114
+
115
+ if (migrationResult.successfullyMigrated > 0) {
116
+ results.projectsMigrated = migrationResult.successfullyMigrated
117
+ }
118
+ }
119
+
120
+ // Step 6: Save version in editors-config
121
+ await editorsConfig.saveConfig(VERSION, installer.getInstallPath())
122
+
123
+ // Show results
124
+ showResults(results)
125
+
126
+ return results
127
+ }
128
+
129
+ /**
130
+ * Show setup results
131
+ */
132
+ function showResults(results) {
133
+ console.log('')
134
+
135
+ // Show what was done
136
+ if (results.claudeInstalled) {
137
+ console.log(` ${GREEN}✓${NC} Claude Code CLI installed`)
138
+ } else {
139
+ console.log(` ${GREEN}✓${NC} Claude Code CLI found`)
140
+ }
141
+
142
+ const totalCommands = results.commandsAdded + results.commandsUpdated
143
+ if (totalCommands > 0) {
144
+ const parts = []
145
+ if (results.commandsAdded > 0) parts.push(`${results.commandsAdded} new`)
146
+ if (results.commandsUpdated > 0) parts.push(`${results.commandsUpdated} updated`)
147
+ console.log(` ${GREEN}✓${NC} Commands synced (${parts.join(', ')})`)
148
+ } else {
149
+ console.log(` ${GREEN}✓${NC} Commands up to date`)
150
+ }
151
+
152
+ if (results.configAction === 'created') {
153
+ console.log(` ${GREEN}✓${NC} Global config created`)
154
+ } else if (results.configAction === 'updated') {
155
+ console.log(` ${GREEN}✓${NC} Global config updated`)
156
+ } else if (results.configAction === 'appended') {
157
+ console.log(` ${GREEN}✓${NC} Global config merged`)
158
+ }
159
+
160
+ if (results.projectsMigrated > 0) {
161
+ console.log(` ${GREEN}✓${NC} ${results.projectsMigrated} projects migrated to global storage`)
162
+ }
163
+
164
+ console.log('')
165
+ }
166
+
167
+ module.exports = { run }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "prjct-cli",
3
- "version": "0.8.4",
3
+ "version": "0.8.6",
4
4
  "description": "Built for Claude - Ship fast, track progress, stay focused. Developer momentum tool for indie hackers.",
5
5
  "main": "core/index.js",
6
6
  "bin": {
@@ -1,114 +1,35 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  /**
4
- * Post-install hook for prjct-cli
4
+ * Post-install hook for prjct-cli (OPTIONAL)
5
5
  *
6
- * Runs automatically after npm install -g prjct-cli
6
+ * Intenta ejecutar setup si npm scripts están habilitados.
7
+ * Si falla o no se ejecuta, no hay problema - el setup se ejecutará
8
+ * automáticamente en el primer uso del CLI (como Astro, Vite, etc.)
7
9
  *
8
- * 1. Detects if global install
9
- * 2. Installs Claude Code CLI if not found (automatic)
10
- * 3. Installs/syncs commands to ~/.claude/commands/p/
11
- * 4. Installs/updates global config to ~/.claude/CLAUDE.md
12
- * 5. Migrates all legacy projects automatically
13
- * 6. Shows success message with results
10
+ * Este hook es una optimización pero NO es crítico.
14
11
  *
15
- * ZERO MANUAL STEPS - Everything is automatic
16
- *
17
- * @version 0.8.4
12
+ * @version 0.8.5
18
13
  */
19
14
 
20
15
  const path = require('path')
21
- const { execSync } = require('child_process')
22
- const installer = require('../core/infrastructure/command-installer')
23
- const migrator = require('../core/infrastructure/migrator')
24
- const { VERSION } = require('../core/utils/version')
25
-
26
- // Colors for terminal output
27
- const CYAN = '\x1b[36m'
28
- const GREEN = '\x1b[32m'
29
- const YELLOW = '\x1b[33m'
30
- const MAGENTA = '\x1b[35m'
31
- const WHITE = '\x1b[37m'
32
- const BOLD = '\x1b[1m'
33
- const DIM = '\x1b[2m'
34
- const NC = '\x1b[0m' // No Color
16
+ const setup = require('../core/infrastructure/setup')
35
17
 
36
18
  async function main() {
37
19
  try {
38
20
  // Detect if this is a global install
39
- const isGlobal = await detectGlobalInstall()
21
+ const isGlobal = detectGlobalInstall()
40
22
 
41
23
  if (!isGlobal) {
42
24
  // Skip postinstall for local development installs
43
- console.log(`${DIM}Skipping post-install (local development)${NC}`)
44
25
  return
45
26
  }
46
27
 
47
- console.log('')
48
- console.log(`${CYAN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}`)
49
- console.log(`${BOLD}${CYAN}🚀 Setting up prjct-cli v${VERSION}${NC}`)
50
- console.log(`${CYAN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}`)
51
- console.log('')
52
-
53
- const results = {
54
- claudeInstalled: false,
55
- commandsAdded: 0,
56
- commandsUpdated: 0,
57
- configAction: null,
58
- projectsMigrated: 0
59
- }
60
-
61
- // Step 1: Ensure Claude Code CLI is installed
62
- const hasClaude = await hasClaudeCodeCLI()
63
-
64
- if (!hasClaude) {
65
- const installed = await installClaudeCode()
66
- if (installed) {
67
- results.claudeInstalled = true
68
- } else {
69
- // Cannot continue without Claude Code
70
- showFailureMessage()
71
- return
72
- }
73
- }
74
-
75
- // Step 2: Detect Claude directory (for commands)
76
- const claudeDetected = await installer.detectClaude()
77
-
78
- if (claudeDetected) {
79
- // Step 3: Sync commands
80
- const syncResult = await installer.syncCommands()
81
-
82
- if (syncResult.success) {
83
- results.commandsAdded = syncResult.added
84
- results.commandsUpdated = syncResult.updated
85
- }
86
-
87
- // Step 4: Install global configuration
88
- const configResult = await installer.installGlobalConfig()
89
-
90
- if (configResult.success) {
91
- results.configAction = configResult.action
92
- }
93
-
94
- // Step 5: Migrate legacy projects automatically
95
- const migrationResult = await migrator.migrateAll({
96
- deepScan: false,
97
- cleanupLegacy: true,
98
- dryRun: false
99
- })
100
-
101
- if (migrationResult.successfullyMigrated > 0) {
102
- results.projectsMigrated = migrationResult.successfullyMigrated
103
- }
104
- }
105
-
106
- // Show final success message
107
- showSuccessMessage(results)
28
+ // Run setup (all logic is in core/infrastructure/setup.js)
29
+ await setup.run()
108
30
 
109
31
  } catch (error) {
110
- console.error(`${YELLOW}⚠️ Post-install error: ${error.message}${NC}`)
111
- console.error(`${DIM}You can run 'prjct setup' manually later${NC}`)
32
+ // Silent failure - setup will run on first use anyway
112
33
  // Don't fail the install if post-install has issues
113
34
  process.exit(0)
114
35
  }
@@ -117,7 +38,7 @@ async function main() {
117
38
  /**
118
39
  * Detect if this is a global npm install
119
40
  */
120
- async function detectGlobalInstall() {
41
+ function detectGlobalInstall() {
121
42
  // Check if we're being installed globally
122
43
  const npmConfig = process.env.npm_config_global
123
44
  if (npmConfig === 'true') {
@@ -137,118 +58,6 @@ async function detectGlobalInstall() {
137
58
  return globalPaths.some(globalPath => installPath.includes(globalPath))
138
59
  }
139
60
 
140
- /**
141
- * Check if Claude Code CLI is installed
142
- */
143
- async function hasClaudeCodeCLI() {
144
- try {
145
- execSync('which claude', { stdio: 'ignore' })
146
- return true
147
- } catch {
148
- return false
149
- }
150
- }
151
-
152
- /**
153
- * Install Claude Code CLI
154
- */
155
- async function installClaudeCode() {
156
- try {
157
- console.log(`${YELLOW}📦 Claude Code not found. Installing...${NC}`)
158
- console.log('')
159
- execSync('npm install -g @anthropic-ai/claude-code', { stdio: 'inherit' })
160
- console.log('')
161
- console.log(`${GREEN}✓${NC} Claude Code installed successfully`)
162
- console.log('')
163
- return true
164
- } catch (error) {
165
- console.log(`${YELLOW}⚠️ Failed to install Claude Code: ${error.message}${NC}`)
166
- console.log(`${DIM}Please install manually: npm install -g @anthropic-ai/claude-code${NC}`)
167
- console.log('')
168
- return false
169
- }
170
- }
171
-
172
- /**
173
- * Show success message with results
174
- */
175
- function showSuccessMessage(results) {
176
- console.log('')
177
- console.log(`${CYAN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}`)
178
- console.log('')
179
- console.log(` ${BOLD}${CYAN}██████╗ ██████╗ ██╗ ██████╗████████╗${NC}`)
180
- console.log(` ${BOLD}${CYAN}██╔══██╗██╔══██╗ ██║██╔════╝╚══██╔══╝${NC}`)
181
- console.log(` ${BOLD}${CYAN}██████╔╝██████╔╝ ██║██║ ██║${NC}`)
182
- console.log(` ${BOLD}${CYAN}██╔═══╝ ██╔══██╗██ ██║██║ ██║${NC}`)
183
- console.log(` ${BOLD}${CYAN}██║ ██║ ██║╚█████╔╝╚██████╗ ██║${NC}`)
184
- console.log(` ${BOLD}${CYAN}╚═╝ ╚═╝ ╚═╝ ╚════╝ ╚═════╝ ╚═╝${NC}`)
185
- console.log('')
186
- console.log(` ${BOLD}${GREEN}v${VERSION} READY!${NC}`)
187
- console.log('')
188
- console.log(`${CYAN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}`)
189
- console.log('')
190
-
191
- // Show what was done
192
- if (results.claudeInstalled) {
193
- console.log(` ${GREEN}✓${NC} Claude Code CLI installed`)
194
- } else {
195
- console.log(` ${GREEN}✓${NC} Claude Code CLI found`)
196
- }
197
-
198
- const totalCommands = results.commandsAdded + results.commandsUpdated
199
- if (totalCommands > 0) {
200
- const parts = []
201
- if (results.commandsAdded > 0) parts.push(`${results.commandsAdded} new`)
202
- if (results.commandsUpdated > 0) parts.push(`${results.commandsUpdated} updated`)
203
- console.log(` ${GREEN}✓${NC} Commands synced (${parts.join(', ')})`)
204
- } else {
205
- console.log(` ${GREEN}✓${NC} Commands up to date`)
206
- }
207
-
208
- if (results.configAction === 'created') {
209
- console.log(` ${GREEN}✓${NC} Global config created`)
210
- } else if (results.configAction === 'updated') {
211
- console.log(` ${GREEN}✓${NC} Global config updated`)
212
- } else if (results.configAction === 'appended') {
213
- console.log(` ${GREEN}✓${NC} Global config merged`)
214
- }
215
-
216
- if (results.projectsMigrated > 0) {
217
- console.log(` ${GREEN}✓${NC} ${results.projectsMigrated} projects migrated to global storage`)
218
- }
219
-
220
- console.log('')
221
- console.log(`${CYAN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}`)
222
- console.log('')
223
- console.log(`${BOLD}Next steps:${NC}`)
224
- console.log(` ${DIM}cd your-project${NC}`)
225
- console.log(` ${GREEN}/p:init${NC}`)
226
- console.log('')
227
- console.log(`${BOLD}${MAGENTA}Happy shipping! 🚀${NC}`)
228
- console.log('')
229
- }
230
-
231
- /**
232
- * Show failure message
233
- */
234
- function showFailureMessage() {
235
- console.log('')
236
- console.log(`${CYAN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}`)
237
- console.log('')
238
- console.log(`${YELLOW}⚠️ Setup incomplete${NC}`)
239
- console.log('')
240
- console.log(`Claude Code is required for prjct-cli.`)
241
- console.log('')
242
- console.log(`Please install manually:`)
243
- console.log(` ${GREEN}npm install -g @anthropic-ai/claude-code${NC}`)
244
- console.log('')
245
- console.log(`Then run:`)
246
- console.log(` ${GREEN}prjct setup${NC}`)
247
- console.log('')
248
- console.log(`${CYAN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}`)
249
- console.log('')
250
- }
251
-
252
61
  // Run if executed directly
253
62
  if (require.main === module) {
254
63
  main().catch(error => {