prjct-cli 0.8.4 → 0.8.8

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/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) {