prjct-cli 0.5.0 → 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 +169 -1
  2. package/CLAUDE.md +43 -28
  3. package/README.md +4 -4
  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 +274 -62
  9. package/core/task-schema.js +342 -0
  10. package/package.json +4 -3
  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,
@@ -2224,54 +2254,55 @@ ${diagram}
2224
2254
  console.log(chalk.cyan(' 🤖 Perfect context for AI agents'))
2225
2255
  console.log('')
2226
2256
 
2227
- console.log(chalk.bold.magenta('📦 Setup - Install Commands to AI Editors\n'))
2257
+ console.log(chalk.bold.magenta('📦 Setup - Install Commands to Claude\n'))
2228
2258
 
2229
- // Detect editors
2259
+ // Detect Claude
2230
2260
  const commandInstaller = require('./command-installer')
2231
- const detection = await commandInstaller.detectEditors()
2232
- const detectedEditors = Object.entries(detection).filter(([_, info]) => info.detected)
2261
+ const claudeDetected = await commandInstaller.detectClaude()
2233
2262
 
2234
- if (detectedEditors.length === 0) {
2263
+ if (!claudeDetected) {
2235
2264
  return {
2236
2265
  success: false,
2237
2266
  message: this.agent.formatResponse(
2238
- 'No AI editors detected.\n\nSupported: Claude Code, Cursor, Windsurf',
2267
+ 'Claude not detected.\n\nPlease install Claude Code or Claude Desktop first.\n\nVisit: https://claude.ai/download',
2239
2268
  'error'
2240
2269
  ),
2241
2270
  }
2242
2271
  }
2243
2272
 
2244
- console.log(chalk.cyan('🔍 Detected AI editors:'))
2245
- detectedEditors.forEach(([key, info]) => {
2246
- const editorName = commandInstaller.editors[key]?.name || key
2247
- console.log(chalk.green(` [✓] ${editorName} (${info.path})`))
2248
- })
2273
+ console.log(chalk.green(' Claude detected'))
2249
2274
  console.log('')
2250
2275
 
2251
- // Interactive selection (allow uninstall = true)
2252
- const installResult = await commandInstaller.interactiveInstall(true)
2276
+ // Install commands
2277
+ console.log(chalk.cyan('Installing /p:* commands...'))
2278
+ const installResult = await commandInstaller.installCommands()
2253
2279
 
2254
2280
  if (!installResult.success) {
2255
2281
  return {
2256
2282
  success: false,
2257
- message: this.agent.formatResponse(installResult.message || 'Setup failed', 'error'),
2283
+ message: this.agent.formatResponse(
2284
+ `Installation failed: ${installResult.error || 'Unknown error'}`,
2285
+ 'error'
2286
+ ),
2258
2287
  }
2259
2288
  }
2260
2289
 
2261
- // Install Context7 MCP
2262
- const mcpResult = await commandInstaller.installContext7MCP()
2263
-
2264
2290
  // Success message
2265
- let message = `✅ Commands installed in: ${installResult.editors.join(', ')}\n`
2291
+ const installedCount = installResult.installed?.length || 0
2292
+ const errorCount = installResult.errors?.length || 0
2266
2293
 
2267
- if (mcpResult.success && mcpResult.editors.length > 0) {
2268
- message += `\n🔌 Context7 MCP enabled in: ${mcpResult.editors.join(', ')}`
2294
+ let message = `✅ Successfully installed ${installedCount} commands to Claude\n`
2295
+ message += ` Location: ${installResult.path}\n`
2296
+
2297
+ if (errorCount > 0) {
2298
+ message += `\n⚠️ ${errorCount} command(s) had issues during installation`
2269
2299
  }
2270
2300
 
2271
2301
  message += '\n\n✨ prjct is ready to use!'
2272
2302
  message += '\n\nNext steps:'
2273
2303
  message += '\n cd your-project/'
2274
2304
  message += '\n prjct init'
2305
+ message += '\n\nℹ️ Context7 MCP is automatically available in Claude'
2275
2306
 
2276
2307
  return {
2277
2308
  success: true,
@@ -2296,60 +2327,72 @@ ${diagram}
2296
2327
  try {
2297
2328
  await this.initializeAgent()
2298
2329
 
2299
- const {
2300
- force = false,
2301
- editor = null,
2302
- createTemplates = false,
2303
- interactive = true,
2304
- } = options
2330
+ const { force = false } = options
2305
2331
 
2306
- if (createTemplates) {
2307
- const templateResult = await commandInstaller.createTemplates()
2308
- if (!templateResult.success) {
2309
- return {
2310
- success: false,
2311
- message: this.agent.formatResponse(templateResult.message, 'error'),
2312
- }
2332
+ // Detect Claude
2333
+ const commandInstaller = require('./command-installer')
2334
+ const claudeDetected = await commandInstaller.detectClaude()
2335
+
2336
+ if (!claudeDetected) {
2337
+ return {
2338
+ success: false,
2339
+ message: this.agent.formatResponse(
2340
+ 'Claude not detected. Please install Claude Code or Claude Desktop first.',
2341
+ 'error'
2342
+ ),
2313
2343
  }
2314
2344
  }
2315
2345
 
2316
- const detection = await commandInstaller.detectEditors(process.cwd())
2317
- const detectedEditors = Object.entries(detection)
2318
- .filter(([_, info]) => info.detected)
2346
+ // Check current installation status
2347
+ const status = await commandInstaller.checkInstallation()
2348
+
2349
+ if (status.installed && !force) {
2350
+ const chalk = require('chalk')
2351
+ let report = chalk.green('✓ Commands already installed\n')
2352
+ report += chalk.dim(` Location: ${status.path}\n`)
2353
+ report += chalk.dim(` Commands: ${status.commands.length}\n`)
2354
+ report += '\nUse --force to reinstall'
2319
2355
 
2320
- if (detectedEditors.length === 0) {
2321
2356
  return {
2322
- success: false,
2323
- message: this.agent.formatResponse('No AI editors detected on this system', 'error'),
2357
+ success: true,
2358
+ message: this.agent.formatResponse(report, 'info'),
2324
2359
  }
2325
2360
  }
2326
2361
 
2327
- let installResult
2362
+ // Install or reinstall commands
2363
+ const installResult = force
2364
+ ? await commandInstaller.updateCommands()
2365
+ : await commandInstaller.installCommands()
2328
2366
 
2329
- if (editor) {
2330
- // Install to specific editor
2331
- installResult = await commandInstaller.installToEditor(editor, force)
2332
- } else if (interactive) {
2333
- // Interactive mode: use new interactiveInstall method
2334
- installResult = await commandInstaller.interactiveInstall(force)
2335
- } else {
2336
- // Non-interactive mode: install to all detected editors
2337
- installResult = await commandInstaller.installToAll(force)
2367
+ if (!installResult.success) {
2368
+ return {
2369
+ success: false,
2370
+ message: this.agent.formatResponse(
2371
+ `Installation failed: ${installResult.error || 'Unknown error'}`,
2372
+ 'error'
2373
+ ),
2374
+ }
2338
2375
  }
2339
2376
 
2340
- // Always install Context7 MCP after commands installation
2341
- const mcpResult = await commandInstaller.installContext7MCP()
2377
+ // Generate report
2378
+ const installedCount = installResult.installed?.length || 0
2379
+ const errorCount = installResult.errors?.length || 0
2342
2380
 
2343
- let report = commandInstaller.generateReport(installResult)
2344
- if (mcpResult.success && mcpResult.editors.length > 0) {
2345
- report += '\n\n🔌 Context7 MCP Enabled\n'
2346
- report += ` Editors: ${mcpResult.editors.join(', ')}\n`
2347
- report += ' 📚 Library documentation now available automatically'
2381
+ let report = `✅ Successfully ${force ? 'reinstalled' : 'installed'} ${installedCount} commands\n`
2382
+ report += ` Location: ${installResult.path}\n`
2383
+
2384
+ if (errorCount > 0) {
2385
+ report += `\n⚠️ ${errorCount} command(s) had issues\n`
2386
+ installResult.errors.forEach(err => {
2387
+ report += ` - ${err.file}: ${err.error}\n`
2388
+ })
2348
2389
  }
2349
2390
 
2391
+ report += '\n📚 Context7 MCP is automatically available in Claude'
2392
+
2350
2393
  return {
2351
- success: installResult.success,
2352
- message: this.agent.formatResponse(report, installResult.success ? 'celebrate' : 'error'),
2394
+ success: true,
2395
+ message: this.agent.formatResponse(report, 'celebrate'),
2353
2396
  }
2354
2397
  } catch (error) {
2355
2398
  await this.initializeAgent()
@@ -2478,6 +2521,175 @@ ${syncResults.shippedMdUpdated ? `✅ Updated shipped.md (${syncResults.features
2478
2521
  `
2479
2522
  }
2480
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
+
2481
2693
  /**
2482
2694
  * Detect if project has existing code (for auto-analyze during init)
2483
2695
  *