prjct-cli 0.5.1 → 0.6.0

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.
Files changed (45) hide show
  1. package/CHANGELOG.md +153 -1
  2. package/CLAUDE.md +43 -28
  3. package/README.md +1 -1
  4. package/bin/prjct +78 -63
  5. package/core/agent-generator.js +19 -10
  6. package/core/ascii-graphics.js +433 -0
  7. package/core/command-registry.js +553 -0
  8. package/core/commands.js +203 -4
  9. package/core/task-schema.js +342 -0
  10. package/package.json +2 -1
  11. package/templates/agents/AGENTS.md +79 -101
  12. package/templates/agents/be.template.md +14 -29
  13. package/templates/agents/coordinator.template.md +34 -0
  14. package/templates/agents/data.template.md +14 -28
  15. package/templates/agents/devops.template.md +14 -28
  16. package/templates/agents/fe.template.md +14 -29
  17. package/templates/agents/mobile.template.md +14 -28
  18. package/templates/agents/qa.template.md +14 -41
  19. package/templates/agents/scribe.template.md +15 -81
  20. package/templates/agents/security.template.md +14 -28
  21. package/templates/agents/ux.template.md +14 -36
  22. package/templates/commands/analyze.md +36 -239
  23. package/templates/commands/build.md +41 -0
  24. package/templates/commands/cleanup.md +24 -87
  25. package/templates/commands/context.md +24 -93
  26. package/templates/commands/design.md +20 -98
  27. package/templates/commands/done.md +16 -181
  28. package/templates/commands/fix.md +27 -66
  29. package/templates/commands/git.md +33 -60
  30. package/templates/commands/help.md +18 -52
  31. package/templates/commands/idea.md +11 -36
  32. package/templates/commands/init.md +30 -277
  33. package/templates/commands/next.md +20 -62
  34. package/templates/commands/now.md +18 -22
  35. package/templates/commands/progress.md +23 -78
  36. package/templates/commands/recap.md +22 -74
  37. package/templates/commands/roadmap.md +21 -90
  38. package/templates/commands/ship.md +26 -161
  39. package/templates/commands/status.md +40 -0
  40. package/templates/commands/stuck.md +21 -33
  41. package/templates/commands/sync.md +19 -209
  42. package/templates/commands/task.md +18 -80
  43. package/templates/commands/test.md +23 -72
  44. package/templates/commands/workflow.md +20 -212
  45. package/templates/agents/pm.template.md +0 -84
package/core/commands.js CHANGED
@@ -4,6 +4,7 @@ const { promisify } = require('util')
4
4
  const { exec: execCallback } = require('child_process')
5
5
  const exec = promisify(execCallback)
6
6
  const agentDetector = require('./agent-detector')
7
+ const agentGenerator = require('./agent-generator')
7
8
  const pathManager = require('./path-manager')
8
9
  const configManager = require('./config-manager')
9
10
  const authorDetector = require('./author-detector')
@@ -11,6 +12,8 @@ const migrator = require('./migrator')
11
12
  const commandInstaller = require('./command-installer')
12
13
  const sessionManager = require('./session-manager')
13
14
  const analyzer = require('./analyzer')
15
+ const workflowEngine = require('./workflow-engine')
16
+ const workflowPrompts = require('./workflow-prompts')
14
17
  const UpdateChecker = require('./update-checker')
15
18
  const { VERSION } = require('./version')
16
19
 
@@ -1130,6 +1133,9 @@ ${nextStep.agent}
1130
1133
  }
1131
1134
  }
1132
1135
 
1136
+ const config = await configManager.readConfig(projectPath)
1137
+ const globalProjectPath = pathManager.getProjectRoot(config.projectId)
1138
+
1133
1139
  const ideasFile = await this.getFilePath(projectPath, 'planning', 'ideas.md')
1134
1140
  const nextFile = await this.getFilePath(projectPath, 'core', 'next.md')
1135
1141
 
@@ -1138,17 +1144,41 @@ ${nextStep.agent}
1138
1144
  await this.agent.writeFile(ideasFile, ideasContent + entry)
1139
1145
 
1140
1146
  let addedToQueue = false
1147
+ let workflowCreated = false
1148
+ let workflowType = null
1149
+
1141
1150
  if (text.match(/^(implement|add|create|fix|update|build)/i)) {
1142
1151
  const nextContent = await this.agent.readFile(nextFile)
1143
1152
  await this.agent.writeFile(nextFile, nextContent + `- ${text}\n`)
1144
1153
  addedToQueue = true
1154
+
1155
+ // Auto-detect workflow type and initialize workflow
1156
+ workflowType = workflowEngine.classify(text)
1157
+ if (workflowType) {
1158
+ try {
1159
+ const workflow = await workflowEngine.init(text, workflowType, globalProjectPath)
1160
+ workflowCreated = !!workflow
1161
+ } catch (error) {
1162
+ console.warn('⚠️ Could not initialize workflow:', error.message)
1163
+ }
1164
+ }
1145
1165
  }
1146
1166
 
1147
- await this.logToMemory(projectPath, 'idea', { text, timestamp: this.agent.getTimestamp() })
1167
+ await this.logToMemory(projectPath, 'idea', {
1168
+ text,
1169
+ timestamp: this.agent.getTimestamp(),
1170
+ workflow: workflowCreated ? workflowType : null,
1171
+ })
1148
1172
 
1149
- const message =
1150
- `Idea captured: "${text}"` +
1151
- (addedToQueue ? `\nAlso added to ${this.agentInfo.config.commandPrefix}next queue` : '')
1173
+ let message = `Idea captured: "${text}"`
1174
+ if (addedToQueue) {
1175
+ message += `\nAlso added to ${this.agentInfo.config.commandPrefix}next queue`
1176
+ }
1177
+ if (workflowCreated) {
1178
+ message += `\n\n🔄 Workflow initialized: ${workflowType}`
1179
+ message += `\nUse ${this.agentInfo.config.commandPrefix}workflow to see steps`
1180
+ message += `\nStart with ${this.agentInfo.config.commandPrefix}now to begin working`
1181
+ }
1152
1182
 
1153
1183
  return {
1154
1184
  success: true,
@@ -2491,6 +2521,175 @@ ${syncResults.shippedMdUpdated ? `✅ Updated shipped.md (${syncResults.features
2491
2521
  `
2492
2522
  }
2493
2523
 
2524
+ /**
2525
+ * Sync project state and update AI agents
2526
+ * Re-analyzes the project and regenerates workflow agents in global project directory
2527
+ *
2528
+ * @param {string} [projectPath=process.cwd()] - Project directory path
2529
+ * @returns {Promise<Object>} Result with success status and message
2530
+ */
2531
+ async sync(projectPath = process.cwd()) {
2532
+ try {
2533
+ await this.initializeAgent()
2534
+
2535
+ // Get project ID from config
2536
+ const config = await configManager.readConfig(projectPath)
2537
+ if (!config || !config.projectId) {
2538
+ return {
2539
+ success: false,
2540
+ message: this.agent.formatResponse(
2541
+ 'Project not initialized. Run /p:init first.',
2542
+ 'error',
2543
+ ),
2544
+ }
2545
+ }
2546
+
2547
+ console.log('🔄 Syncing project state...\n')
2548
+
2549
+ // Step 1: Re-run project analysis
2550
+ const analysisResult = await this.analyze({ silent: true }, projectPath)
2551
+
2552
+ if (!analysisResult.success) {
2553
+ return {
2554
+ success: false,
2555
+ message: this.agent.formatResponse(`Sync failed: ${analysisResult.message}`, 'error'),
2556
+ }
2557
+ }
2558
+
2559
+ const { analysis } = analysisResult
2560
+ const summary = {
2561
+ commandsFound: analysis.commands.length,
2562
+ featuresFound: analysis.features.length,
2563
+ }
2564
+
2565
+ console.log('📊 Analysis Complete')
2566
+ console.log(` ✅ ${summary.commandsFound} commands detected`)
2567
+ console.log(` ✅ ${summary.featuresFound} features implemented\n`)
2568
+
2569
+ // Step 2: Generate/update all agents in project directory
2570
+ const AgentGenerator = agentGenerator
2571
+ const generator = new AgentGenerator(config.projectId)
2572
+
2573
+ const generatedAgents = await generator.generateAll(analysis)
2574
+
2575
+ // Step 3: Log sync action to memory
2576
+ await this.logAction(projectPath, 'sync', {
2577
+ commandsDetected: summary.commandsFound,
2578
+ featuresDetected: summary.featuresFound,
2579
+ agentsGenerated: generatedAgents.length,
2580
+ agents: generatedAgents,
2581
+ })
2582
+
2583
+ const agentsPath = path.join(
2584
+ pathManager.getProjectRoot(config.projectId),
2585
+ 'agents',
2586
+ )
2587
+
2588
+ const message = `
2589
+ 🔄 Sync Complete
2590
+
2591
+ 📊 Project State:
2592
+ ✅ ${summary.commandsFound} commands detected
2593
+ ✅ ${summary.featuresFound} features implemented
2594
+
2595
+ 🤖 Workflow Agents:
2596
+ ✨ Generated ${generatedAgents.length} agents in ${agentsPath}
2597
+ ${generatedAgents.map(a => `✅ ${a.toUpperCase()}`).join('\n ')}
2598
+
2599
+ 💡 Agents ready for workflow task assignment!
2600
+ `
2601
+
2602
+ return {
2603
+ success: true,
2604
+ message: this.agent.formatResponse(message, 'success'),
2605
+ generatedAgents,
2606
+ }
2607
+ } catch (error) {
2608
+ await this.initializeAgent()
2609
+ return {
2610
+ success: false,
2611
+ message: this.agent.formatResponse(`Sync failed: ${error.message}`, 'error'),
2612
+ }
2613
+ }
2614
+ }
2615
+
2616
+ /**
2617
+ * Show workflow status
2618
+ *
2619
+ * @param {string} [projectPath=process.cwd()] - Project path
2620
+ * @returns {Promise<Object>} Result object with success flag and message
2621
+ */
2622
+ async workflow(projectPath = process.cwd()) {
2623
+ try {
2624
+ await this.initializeAgent()
2625
+
2626
+ // Auto-init if not configured
2627
+ const initCheck = await this.ensureProjectInit(projectPath)
2628
+ if (!initCheck.success) {
2629
+ return initCheck
2630
+ }
2631
+
2632
+ const config = await configManager.readConfig(projectPath)
2633
+ const globalProjectPath = pathManager.getProjectRoot(config.projectId)
2634
+
2635
+ const workflow = await workflowEngine.load(globalProjectPath)
2636
+
2637
+ if (!workflow || !workflow.active) {
2638
+ return {
2639
+ success: true,
2640
+ message: this.agent.formatResponse(
2641
+ `No active workflow.\n\nStart one with ${this.agentInfo.config.commandPrefix}idea "implement [feature]"`,
2642
+ 'info',
2643
+ ),
2644
+ }
2645
+ }
2646
+
2647
+ const currentStep = workflow.steps[workflow.current]
2648
+ const completedSteps = workflow.steps.slice(0, workflow.current)
2649
+ const remainingSteps = workflow.steps.slice(workflow.current + 1)
2650
+
2651
+ let message = `🔄 Workflow: ${workflow.type}\n`
2652
+ message += `📋 Task: ${workflow.task}\n\n`
2653
+
2654
+ if (completedSteps.length > 0) {
2655
+ message += `✅ Completed:\n`
2656
+ completedSteps.forEach((step) => {
2657
+ message += ` - ${step.name}: ${step.action} (${step.agent})\n`
2658
+ })
2659
+ message += '\n'
2660
+ }
2661
+
2662
+ message += `🎯 Current Step:\n`
2663
+ message += ` - ${currentStep.name}: ${currentStep.action} (${currentStep.agent})\n`
2664
+ if (currentStep.required) {
2665
+ message += ` Required: Yes\n`
2666
+ }
2667
+ message += '\n'
2668
+
2669
+ if (remainingSteps.length > 0) {
2670
+ message += `⏳ Remaining:\n`
2671
+ remainingSteps.forEach((step) => {
2672
+ message += ` - ${step.name}: ${step.action} (${step.agent})\n`
2673
+ })
2674
+ message += '\n'
2675
+ }
2676
+
2677
+ message += `Progress: ${workflow.current + 1}/${workflow.steps.length} steps`
2678
+
2679
+ return {
2680
+ success: true,
2681
+ message: this.agent.formatResponse(message, 'info'),
2682
+ workflow,
2683
+ }
2684
+ } catch (error) {
2685
+ await this.initializeAgent()
2686
+ return {
2687
+ success: false,
2688
+ message: this.agent.formatResponse(`Workflow status failed: ${error.message}`, 'error'),
2689
+ }
2690
+ }
2691
+ }
2692
+
2494
2693
  /**
2495
2694
  * Detect if project has existing code (for auto-analyze during init)
2496
2695
  *
@@ -0,0 +1,342 @@
1
+ /**
2
+ * Task Metadata Schema and Agent Types
3
+ *
4
+ * Defines the structure for task tracking with agent assignment,
5
+ * time estimation, and complexity scoring.
6
+ *
7
+ * @version 0.6.0
8
+ */
9
+
10
+ /**
11
+ * Agent Types - Technical specialists for task assignment
12
+ */
13
+ const AGENT_TYPES = {
14
+ 'backend-architect': {
15
+ name: 'Backend Architect',
16
+ specialization: 'Server-side architecture, APIs, databases',
17
+ keywords: ['api', 'backend', 'server', 'database', 'endpoint', 'migration', 'schema'],
18
+ icon: '🏗️',
19
+ estimatedEfficiency: 1.0, // baseline
20
+ },
21
+ 'frontend-developer': {
22
+ name: 'Frontend Developer',
23
+ specialization: 'UI/UX, components, responsive design',
24
+ keywords: ['ui', 'frontend', 'component', 'design', 'layout', 'responsive', 'css', 'style'],
25
+ icon: '🎨',
26
+ estimatedEfficiency: 1.0,
27
+ },
28
+ 'fullstack-engineer': {
29
+ name: 'Fullstack Engineer',
30
+ specialization: 'End-to-end feature development',
31
+ keywords: ['fullstack', 'feature', 'integration', 'end-to-end', 'complete'],
32
+ icon: '⚡',
33
+ estimatedEfficiency: 0.9, // slightly slower due to context switching
34
+ },
35
+ 'devops-specialist': {
36
+ name: 'DevOps Specialist',
37
+ specialization: 'CI/CD, deployment, infrastructure',
38
+ keywords: ['deploy', 'ci/cd', 'docker', 'kubernetes', 'infrastructure', 'pipeline', 'build'],
39
+ icon: '🚀',
40
+ estimatedEfficiency: 1.1, // faster at automation
41
+ },
42
+ 'security-engineer': {
43
+ name: 'Security Engineer',
44
+ specialization: 'Authentication, authorization, security',
45
+ keywords: [
46
+ 'auth',
47
+ 'security',
48
+ 'authentication',
49
+ 'authorization',
50
+ 'encryption',
51
+ 'jwt',
52
+ 'oauth',
53
+ ],
54
+ icon: '🔒',
55
+ estimatedEfficiency: 0.8, // slower due to thorough security review
56
+ },
57
+ 'data-engineer': {
58
+ name: 'Data Engineer',
59
+ specialization: 'Data processing, analytics, ETL',
60
+ keywords: ['data', 'analytics', 'etl', 'pipeline', 'processing', 'warehouse', 'query'],
61
+ icon: '📊',
62
+ estimatedEfficiency: 0.9,
63
+ },
64
+ 'qa-engineer': {
65
+ name: 'QA Engineer',
66
+ specialization: 'Testing, quality assurance, automation',
67
+ keywords: ['test', 'testing', 'qa', 'quality', 'automation', 'e2e', 'integration'],
68
+ icon: '🧪',
69
+ estimatedEfficiency: 1.0,
70
+ },
71
+ 'performance-engineer': {
72
+ name: 'Performance Engineer',
73
+ specialization: 'Optimization, scaling, performance',
74
+ keywords: ['performance', 'optimize', 'scaling', 'cache', 'speed', 'bottleneck'],
75
+ icon: '⚡',
76
+ estimatedEfficiency: 0.85, // slower due to profiling and measurement
77
+ },
78
+ 'general-developer': {
79
+ name: 'General Developer',
80
+ specialization: 'General-purpose development',
81
+ keywords: ['fix', 'update', 'improve', 'refactor', 'cleanup'],
82
+ icon: '👨‍💻',
83
+ estimatedEfficiency: 1.0,
84
+ },
85
+ }
86
+
87
+ /**
88
+ * Complexity Levels
89
+ */
90
+ const COMPLEXITY = {
91
+ trivial: {
92
+ level: 1,
93
+ name: 'Trivial',
94
+ description: 'Simple changes, typos, configuration',
95
+ estimatedHours: 0.5,
96
+ multiplier: 0.5,
97
+ examples: ['Fix typo', 'Update config value', 'Change color'],
98
+ },
99
+ simple: {
100
+ level: 2,
101
+ name: 'Simple',
102
+ description: 'Straightforward implementation, single file',
103
+ estimatedHours: 2,
104
+ multiplier: 1.0,
105
+ examples: ['Add validation', 'Create simple component', 'Update documentation'],
106
+ },
107
+ moderate: {
108
+ level: 3,
109
+ name: 'Moderate',
110
+ description: 'Multiple files, some complexity',
111
+ estimatedHours: 4,
112
+ multiplier: 2.0,
113
+ examples: ['Implement new feature', 'Refactor module', 'Add API endpoint'],
114
+ },
115
+ complex: {
116
+ level: 4,
117
+ name: 'Complex',
118
+ description: 'System-wide changes, architecture',
119
+ estimatedHours: 8,
120
+ multiplier: 4.0,
121
+ examples: ['Authentication system', 'Database migration', 'Performance optimization'],
122
+ },
123
+ epic: {
124
+ level: 5,
125
+ name: 'Epic',
126
+ description: 'Major feature, multiple systems',
127
+ estimatedHours: 16,
128
+ multiplier: 8.0,
129
+ examples: ['Payment integration', 'Real-time messaging', 'Admin dashboard'],
130
+ },
131
+ }
132
+
133
+ /**
134
+ * Task Status
135
+ */
136
+ const TASK_STATUS = {
137
+ pending: 'Waiting to start',
138
+ active: 'Currently working on',
139
+ blocked: 'Blocked by dependency',
140
+ completed: 'Successfully finished',
141
+ cancelled: 'Cancelled/discarded',
142
+ }
143
+
144
+ /**
145
+ * Task Schema
146
+ */
147
+ class TaskSchema {
148
+ /**
149
+ * Create a new task
150
+ */
151
+ static create(data) {
152
+ const now = new Date().toISOString()
153
+
154
+ return {
155
+ id: data.id || this.generateId(),
156
+ title: data.title,
157
+ description: data.description || null,
158
+
159
+ // Agent & Developer
160
+ assignedAgent: data.assignedAgent || this.detectAgent(data.title),
161
+ githubDev: data.githubDev || null, // Will be populated from git config
162
+
163
+ // Complexity & Time
164
+ complexity: data.complexity || this.estimateComplexity(data.title, data.description),
165
+ estimatedTime: data.estimatedTime || this.estimateTime(data.complexity),
166
+ actualTime: null,
167
+
168
+ // Status & Tracking
169
+ status: data.status || 'pending',
170
+ priority: data.priority || 5,
171
+ blocked: data.blocked || false,
172
+ blockedBy: data.blockedBy || null,
173
+
174
+ // Timestamps
175
+ createdAt: data.createdAt || now,
176
+ startedAt: data.startedAt || null,
177
+ completedAt: data.completedAt || null,
178
+
179
+ // Metadata
180
+ tags: data.tags || [],
181
+ notes: data.notes || null,
182
+ }
183
+ }
184
+
185
+ /**
186
+ * Generate unique task ID
187
+ */
188
+ static generateId() {
189
+ return `task-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`
190
+ }
191
+
192
+ /**
193
+ * Auto-detect appropriate agent based on task description
194
+ */
195
+ static detectAgent(title, description = '') {
196
+ const text = `${title} ${description}`.toLowerCase()
197
+ let bestMatch = 'general-developer'
198
+ let highestScore = 0
199
+
200
+ for (const [agentType, config] of Object.entries(AGENT_TYPES)) {
201
+ const score = config.keywords.filter((keyword) => text.includes(keyword)).length
202
+
203
+ if (score > highestScore) {
204
+ highestScore = score
205
+ bestMatch = agentType
206
+ }
207
+ }
208
+
209
+ return bestMatch
210
+ }
211
+
212
+ /**
213
+ * Estimate complexity based on keywords and description
214
+ */
215
+ static estimateComplexity(title, description = '') {
216
+ const text = `${title} ${description}`.toLowerCase()
217
+
218
+ // Epic indicators
219
+ if (
220
+ text.match(
221
+ /system|payment|real-time|dashboard|integration|authentication|migration|architecture/i,
222
+ )
223
+ ) {
224
+ return 'epic'
225
+ }
226
+
227
+ // Complex indicators
228
+ if (text.match(/refactor|optimization|security|database|multiple|complex/i)) {
229
+ return 'complex'
230
+ }
231
+
232
+ // Moderate indicators
233
+ if (text.match(/feature|implement|api|endpoint|component|module/i)) {
234
+ return 'moderate'
235
+ }
236
+
237
+ // Simple indicators
238
+ if (text.match(/add|update|fix|change|simple|small/i)) {
239
+ return 'simple'
240
+ }
241
+
242
+ // Trivial indicators
243
+ if (text.match(/typo|config|color|text|minor|tiny/i)) {
244
+ return 'trivial'
245
+ }
246
+
247
+ return 'simple' // default
248
+ }
249
+
250
+ /**
251
+ * Estimate time based on complexity and agent efficiency
252
+ */
253
+ static estimateTime(complexity, agentType = 'general-developer') {
254
+ const complexityData = COMPLEXITY[complexity]
255
+ const agentData = AGENT_TYPES[agentType]
256
+
257
+ if (!complexityData || !agentData) {
258
+ return '2-4 hours'
259
+ }
260
+
261
+ const baseHours = complexityData.estimatedHours
262
+ const adjustedHours = baseHours * agentData.estimatedEfficiency
263
+
264
+ // Format as range
265
+ const low = Math.floor(adjustedHours * 0.75)
266
+ const high = Math.ceil(adjustedHours * 1.25)
267
+
268
+ if (adjustedHours < 1) {
269
+ return `${Math.round(adjustedHours * 60)} minutes`
270
+ }
271
+
272
+ return `${low}-${high} hours`
273
+ }
274
+
275
+ /**
276
+ * Start a task (move to active)
277
+ */
278
+ static start(task) {
279
+ return {
280
+ ...task,
281
+ status: 'active',
282
+ startedAt: new Date().toISOString(),
283
+ }
284
+ }
285
+
286
+ /**
287
+ * Complete a task
288
+ */
289
+ static complete(task) {
290
+ const completedAt = new Date().toISOString()
291
+ const actualTime = this.calculateActualTime(task.startedAt, completedAt)
292
+
293
+ return {
294
+ ...task,
295
+ status: 'completed',
296
+ completedAt,
297
+ actualTime,
298
+ }
299
+ }
300
+
301
+ /**
302
+ * Calculate actual time spent
303
+ */
304
+ static calculateActualTime(startedAt, completedAt) {
305
+ if (!startedAt) return null
306
+
307
+ const start = new Date(startedAt)
308
+ const end = new Date(completedAt)
309
+ const diffMs = end - start
310
+ const hours = Math.floor(diffMs / (1000 * 60 * 60))
311
+ const minutes = Math.floor((diffMs % (1000 * 60 * 60)) / (1000 * 60))
312
+
313
+ if (hours === 0) {
314
+ return `${minutes}m`
315
+ }
316
+ return `${hours}h ${minutes}m`
317
+ }
318
+
319
+ /**
320
+ * Validate task schema
321
+ */
322
+ static validate(task) {
323
+ const errors = []
324
+
325
+ if (!task.title) errors.push('Task title is required')
326
+ if (!AGENT_TYPES[task.assignedAgent]) errors.push('Invalid agent type')
327
+ if (!COMPLEXITY[task.complexity]) errors.push('Invalid complexity level')
328
+ if (!TASK_STATUS[task.status]) errors.push('Invalid task status')
329
+
330
+ return {
331
+ valid: errors.length === 0,
332
+ errors,
333
+ }
334
+ }
335
+ }
336
+
337
+ module.exports = {
338
+ TaskSchema,
339
+ AGENT_TYPES,
340
+ COMPLEXITY,
341
+ TASK_STATUS,
342
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "prjct-cli",
3
- "version": "0.5.1",
3
+ "version": "0.6.0",
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": {
@@ -15,6 +15,7 @@
15
15
  "preuninstall": "node scripts/preuninstall.js",
16
16
  "install-global": "./scripts/install.sh",
17
17
  "test": "echo 'No tests configured'",
18
+ "validate": "node scripts/validate-commands.js",
18
19
  "lint": "eslint \"**/*.js\" --ignore-pattern \"node_modules/**\" --ignore-pattern \"website/**\"",
19
20
  "lint:fix": "eslint \"**/*.js\" --fix --ignore-pattern \"node_modules/**\" --ignore-pattern \"website/**\"",
20
21
  "format": "prettier --write \"**/*.{js,jsx,ts,tsx,json,css,md}\" --config .config/.prettierrc --ignore-path .config/.prettierignore",