prjct-cli 0.6.0 → 0.7.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 (83) hide show
  1. package/CHANGELOG.md +67 -6
  2. package/CLAUDE.md +442 -36
  3. package/README.md +47 -54
  4. package/bin/prjct +170 -240
  5. package/core/agentic/command-executor.js +113 -0
  6. package/core/agentic/context-builder.js +85 -0
  7. package/core/agentic/prompt-builder.js +86 -0
  8. package/core/agentic/template-loader.js +104 -0
  9. package/core/agentic/tool-registry.js +117 -0
  10. package/core/command-registry.js +106 -62
  11. package/core/commands.js +2030 -2211
  12. package/core/domain/agent-generator.js +118 -0
  13. package/core/domain/analyzer.js +211 -0
  14. package/core/domain/architect-session.js +300 -0
  15. package/core/{agents → infrastructure/agents}/claude-agent.js +16 -13
  16. package/core/{author-detector.js → infrastructure/author-detector.js} +3 -1
  17. package/core/{capability-installer.js → infrastructure/capability-installer.js} +3 -6
  18. package/core/{command-installer.js → infrastructure/command-installer.js} +4 -2
  19. package/core/{config-manager.js → infrastructure/config-manager.js} +4 -4
  20. package/core/{editors-config.js → infrastructure/editors-config.js} +2 -10
  21. package/core/{migrator.js → infrastructure/migrator.js} +34 -19
  22. package/core/{path-manager.js → infrastructure/path-manager.js} +20 -44
  23. package/core/{session-manager.js → infrastructure/session-manager.js} +45 -105
  24. package/core/{update-checker.js → infrastructure/update-checker.js} +67 -67
  25. package/core/{animations-simple.js → utils/animations.js} +3 -23
  26. package/core/utils/date-helper.js +238 -0
  27. package/core/utils/file-helper.js +327 -0
  28. package/core/utils/jsonl-helper.js +206 -0
  29. package/core/{project-capabilities.js → utils/project-capabilities.js} +21 -22
  30. package/core/utils/session-helper.js +277 -0
  31. package/core/{version.js → utils/version.js} +1 -1
  32. package/package.json +4 -12
  33. package/templates/agents/AGENTS.md +101 -27
  34. package/templates/analysis/analyze.md +84 -0
  35. package/templates/commands/analyze.md +9 -2
  36. package/templates/commands/bug.md +79 -0
  37. package/templates/commands/build.md +5 -2
  38. package/templates/commands/cleanup.md +5 -2
  39. package/templates/commands/design.md +5 -2
  40. package/templates/commands/done.md +4 -2
  41. package/templates/commands/feature.md +113 -0
  42. package/templates/commands/fix.md +41 -10
  43. package/templates/commands/git.md +7 -2
  44. package/templates/commands/help.md +2 -2
  45. package/templates/commands/idea.md +14 -5
  46. package/templates/commands/init.md +62 -7
  47. package/templates/commands/next.md +4 -2
  48. package/templates/commands/now.md +4 -2
  49. package/templates/commands/progress.md +27 -5
  50. package/templates/commands/recap.md +39 -10
  51. package/templates/commands/roadmap.md +19 -5
  52. package/templates/commands/ship.md +118 -16
  53. package/templates/commands/status.md +4 -2
  54. package/templates/commands/sync.md +19 -15
  55. package/templates/commands/task.md +4 -2
  56. package/templates/commands/test.md +5 -2
  57. package/templates/commands/workflow.md +4 -2
  58. package/core/agent-generator.js +0 -525
  59. package/core/analyzer.js +0 -600
  60. package/core/animations.js +0 -277
  61. package/core/ascii-graphics.js +0 -433
  62. package/core/git-integration.js +0 -401
  63. package/core/task-schema.js +0 -342
  64. package/core/workflow-engine.js +0 -213
  65. package/core/workflow-prompts.js +0 -192
  66. package/core/workflow-rules.js +0 -147
  67. package/scripts/post-install.js +0 -121
  68. package/scripts/preuninstall.js +0 -94
  69. package/scripts/verify-installation.sh +0 -158
  70. package/templates/agents/be.template.md +0 -27
  71. package/templates/agents/coordinator.template.md +0 -34
  72. package/templates/agents/data.template.md +0 -27
  73. package/templates/agents/devops.template.md +0 -27
  74. package/templates/agents/fe.template.md +0 -27
  75. package/templates/agents/mobile.template.md +0 -27
  76. package/templates/agents/qa.template.md +0 -27
  77. package/templates/agents/scribe.template.md +0 -29
  78. package/templates/agents/security.template.md +0 -27
  79. package/templates/agents/ux.template.md +0 -27
  80. package/templates/commands/context.md +0 -36
  81. package/templates/commands/stuck.md +0 -36
  82. package/templates/examples/natural-language-examples.md +0 -532
  83. /package/core/{agent-detector.js → infrastructure/agent-detector.js} +0 -0
@@ -1,525 +0,0 @@
1
- const fs = require('fs').promises
2
- const path = require('path')
3
- const os = require('os')
4
-
5
- /**
6
- * AgentGenerator - Dynamic agent generation for prjct-cli
7
- *
8
- * Generates specialized AI agents based on project analysis,
9
- * customizing them with project-specific context and requirements.
10
- *
11
- * @version 0.5.0
12
- */
13
- class AgentGenerator {
14
- constructor(projectId = null) {
15
- this.templatesDir = path.join(__dirname, '..', 'templates', 'agents')
16
- this.projectId = projectId
17
-
18
- // Agents are stored in global project directory, not Claude's agents
19
- if (projectId) {
20
- this.outputDir = path.join(os.homedir(), '.prjct-cli', 'projects', projectId, 'agents')
21
- } else {
22
- // Fallback for backwards compatibility (will be deprecated)
23
- this.outputDir = path.join(os.homedir(), '.prjct-cli', 'agents')
24
- }
25
-
26
- // Base agents (always generated)
27
- this.baseAgents = ['coordinator', 'ux', 'fe', 'be', 'qa', 'scribe']
28
-
29
- // Agent colors for visual distinction
30
- this.agentColors = {
31
- coordinator: 'cyan',
32
- ux: 'purple',
33
- fe: 'orange',
34
- be: 'yellow',
35
- qa: 'green',
36
- scribe: 'blue',
37
- devops: 'red',
38
- security: 'magenta',
39
- mobile: 'pink',
40
- data: 'teal',
41
- }
42
- }
43
-
44
- /**
45
- * Generate all agents for a project
46
- * @param {Object} projectAnalysis - Analysis result from /p:analyze
47
- * @returns {Promise<Array>} List of generated agent types
48
- */
49
- async generateAll(projectAnalysis) {
50
- console.log('\n🤖 Generating AI agents...\n')
51
-
52
- // Ensure output directory exists
53
- await fs.mkdir(this.outputDir, { recursive: true })
54
-
55
- // Detect which agents to generate
56
- const conditionalAgents = this.detectConditionalAgents(projectAnalysis)
57
- const allAgents = [...this.baseAgents, ...conditionalAgents]
58
-
59
- // Generate each agent
60
- const generated = []
61
- for (const agentType of allAgents) {
62
- try {
63
- await this.generateAgent(agentType, projectAnalysis)
64
- generated.push(agentType)
65
- console.log(` ✅ ${agentType.toUpperCase()} agent created`)
66
- } catch (error) {
67
- console.error(` ❌ Failed to create ${agentType} agent:`, error.message)
68
- }
69
- }
70
-
71
- console.log(`\n✨ Generated ${generated.length} agents\n`)
72
-
73
- return generated
74
- }
75
-
76
- /**
77
- * Detect which conditional agents should be generated
78
- * @param {Object} analysis - Project analysis
79
- * @returns {Array<string>} List of conditional agent types
80
- */
81
- detectConditionalAgents(analysis) {
82
- const agents = []
83
-
84
- // Security agent for web apps or apps with auth
85
- if (
86
- analysis.type === 'web' ||
87
- analysis.isWebApp ||
88
- analysis.hasAuth ||
89
- analysis.hasAPI
90
- ) {
91
- agents.push('security')
92
- }
93
-
94
- // DevOps agent for projects with Docker/K8s/CI
95
- if (
96
- analysis.hasDocker ||
97
- analysis.hasKubernetes ||
98
- analysis.hasCI ||
99
- analysis.hasDeployment
100
- ) {
101
- agents.push('devops')
102
- }
103
-
104
- // Mobile agent for React Native/Flutter/mobile frameworks
105
- if (
106
- analysis.isMobile ||
107
- analysis.frameworks?.some(f =>
108
- ['react-native', 'flutter', 'ionic', 'expo'].includes(
109
- f.toLowerCase(),
110
- ),
111
- )
112
- ) {
113
- agents.push('mobile')
114
- }
115
-
116
- // Data Science agent for ML/data projects
117
- if (
118
- analysis.hasML ||
119
- analysis.hasDataScience ||
120
- analysis.dependencies?.some(d =>
121
- [
122
- 'tensorflow',
123
- 'pytorch',
124
- 'scikit-learn',
125
- 'pandas',
126
- 'numpy',
127
- 'jupyter',
128
- ].includes(d.toLowerCase()),
129
- )
130
- ) {
131
- agents.push('data')
132
- }
133
-
134
- return agents
135
- }
136
-
137
- /**
138
- * Generate a single agent file
139
- * @param {string} type - Agent type (pm, fe, be, etc.)
140
- * @param {Object} analysis - Project analysis
141
- * @returns {Promise<void>}
142
- */
143
- async generateAgent(type, analysis) {
144
- // Load template
145
- const templatePath = path.join(this.templatesDir, `${type}.template.md`)
146
- let template
147
-
148
- try {
149
- template = await fs.readFile(templatePath, 'utf-8')
150
- } catch (error) {
151
- throw new Error(`Template not found for ${type} agent`)
152
- }
153
-
154
- // Replace placeholders
155
- let content = this.replacePlaceholders(template, analysis)
156
-
157
- // Add project-specific context
158
- content += this.generateProjectContext(type, analysis)
159
-
160
- // Write agent file
161
- await fs.mkdir(this.outputDir, { recursive: true })
162
- const outputPath = path.join(this.outputDir, `${type}.md`)
163
- await fs.writeFile(outputPath, content, 'utf-8')
164
- }
165
-
166
- /**
167
- * Replace template placeholders with actual values
168
- * @param {string} template - Template content
169
- * @param {Object} analysis - Project analysis
170
- * @returns {string} Processed template
171
- */
172
- replacePlaceholders(template, analysis) {
173
- const replacements = {
174
- PROJECT_NAME: analysis.name || 'Unknown Project',
175
- DETECTED_STACK: this.formatStack(analysis),
176
- DETECTED_PATTERN: analysis.architecture || 'Not detected',
177
- DETECTED_ENTRY: analysis.entryPoint || 'Not detected',
178
- PROJECT_TYPE: analysis.type || 'application',
179
- PRIMARY_LANGUAGE: analysis.primaryLanguage || 'JavaScript',
180
- FRAMEWORK: this.formatFrameworks(analysis.frameworks),
181
- }
182
-
183
- let content = template
184
-
185
- for (const [key, value] of Object.entries(replacements)) {
186
- const regex = new RegExp(`\\[${key}\\]`, 'g')
187
- content = content.replace(regex, value)
188
- }
189
-
190
- return content
191
- }
192
-
193
- /**
194
- * Format stack information for display
195
- * @param {Object} analysis - Project analysis
196
- * @returns {string} Formatted stack string
197
- */
198
- formatStack(analysis) {
199
- const parts = []
200
-
201
- if (analysis.primaryLanguage) {
202
- parts.push(analysis.primaryLanguage)
203
- }
204
-
205
- if (analysis.frameworks && analysis.frameworks.length > 0) {
206
- parts.push(...analysis.frameworks.slice(0, 3))
207
- }
208
-
209
- if (analysis.tools && analysis.tools.length > 0) {
210
- parts.push(...analysis.tools.slice(0, 2))
211
- }
212
-
213
- return parts.length > 0 ? parts.join(' + ') : 'Not detected'
214
- }
215
-
216
- /**
217
- * Format frameworks list
218
- * @param {Array} frameworks - List of frameworks
219
- * @returns {string} Formatted string
220
- */
221
- formatFrameworks(frameworks) {
222
- if (!frameworks || frameworks.length === 0) {
223
- return 'None detected'
224
- }
225
- return frameworks.join(', ')
226
- }
227
-
228
- /**
229
- * Generate project-specific context section
230
- * @param {string} type - Agent type
231
- * @param {Object} analysis - Project analysis
232
- * @returns {string} Context markdown
233
- */
234
- generateProjectContext(type, analysis) {
235
- let context = '\n\n## Project-Specific Context\n\n'
236
-
237
- switch (type) {
238
- case 'fe':
239
- context += this.generateFrontendContext(analysis)
240
- break
241
- case 'be':
242
- context += this.generateBackendContext(analysis)
243
- break
244
- case 'ux':
245
- context += this.generateUXContext(analysis)
246
- break
247
- case 'qa':
248
- context += this.generateQAContext(analysis)
249
- break
250
- case 'devops':
251
- context += this.generateDevOpsContext(analysis)
252
- break
253
- case 'security':
254
- context += this.generateSecurityContext(analysis)
255
- break
256
- default:
257
- context += this.generateGenericContext(analysis)
258
- }
259
-
260
- return context
261
- }
262
-
263
- /**
264
- * Generate frontend-specific context
265
- */
266
- generateFrontendContext(analysis) {
267
- const context = []
268
-
269
- if (analysis.frontend) {
270
- const fe = analysis.frontend
271
-
272
- if (fe.framework) {
273
- context.push(`- **Framework**: ${fe.framework}${fe.version ? ` ${fe.version}` : ''}`)
274
- }
275
-
276
- if (fe.stateManagement) {
277
- context.push(`- **State Management**: ${fe.stateManagement}`)
278
- }
279
-
280
- if (fe.routing) {
281
- context.push(`- **Routing**: ${fe.routing}`)
282
- }
283
-
284
- if (fe.styling) {
285
- context.push(`- **Styling**: ${fe.styling}`)
286
- }
287
-
288
- if (fe.buildTool) {
289
- context.push(`- **Build Tool**: ${fe.buildTool}`)
290
- }
291
-
292
- if (fe.componentPattern) {
293
- context.push(`- **Component Pattern**: ${fe.componentPattern}`)
294
- }
295
- }
296
-
297
- if (analysis.directories?.components) {
298
- context.push(`- **Components Location**: ${analysis.directories.components}`)
299
- }
300
-
301
- return context.length > 0 ? context.join('\n') : '- No specific frontend context detected'
302
- }
303
-
304
- /**
305
- * Generate backend-specific context
306
- */
307
- generateBackendContext(analysis) {
308
- const context = []
309
-
310
- if (analysis.backend) {
311
- const be = analysis.backend
312
-
313
- if (be.framework) {
314
- context.push(`- **Framework**: ${be.framework}${be.version ? ` ${be.version}` : ''}`)
315
- }
316
-
317
- if (be.database) {
318
- context.push(`- **Database**: ${be.database}`)
319
- }
320
-
321
- if (be.orm) {
322
- context.push(`- **ORM/ODM**: ${be.orm}`)
323
- }
324
-
325
- if (be.auth) {
326
- context.push(`- **Authentication**: ${be.auth}`)
327
- }
328
-
329
- if (be.apiStyle) {
330
- context.push(`- **API Style**: ${be.apiStyle}`)
331
- }
332
- }
333
-
334
- if (analysis.hasAPI) {
335
- context.push(`- **API Detected**: Yes`)
336
- }
337
-
338
- return context.length > 0 ? context.join('\n') : '- No specific backend context detected'
339
- }
340
-
341
- /**
342
- * Generate UX-specific context
343
- */
344
- generateUXContext(analysis) {
345
- const context = []
346
-
347
- if (analysis.frontend?.styling) {
348
- context.push(`- **Design System**: ${analysis.frontend.styling}`)
349
- }
350
-
351
- if (analysis.hasDesignSystem) {
352
- context.push(`- **Design System Files**: Detected`)
353
- }
354
-
355
- if (analysis.accessibility) {
356
- context.push(`- **Accessibility**: ${analysis.accessibility}`)
357
- }
358
-
359
- context.push(`- **Focus**: User experience, visual design, interaction patterns`)
360
-
361
- return context.join('\n')
362
- }
363
-
364
- /**
365
- * Generate QA-specific context
366
- */
367
- generateQAContext(analysis) {
368
- const context = []
369
-
370
- if (analysis.testing) {
371
- if (analysis.testing.framework) {
372
- context.push(`- **Test Framework**: ${analysis.testing.framework}`)
373
- }
374
-
375
- if (analysis.testing.coverage) {
376
- context.push(`- **Coverage Tool**: ${analysis.testing.coverage}`)
377
- }
378
-
379
- if (analysis.testing.e2e) {
380
- context.push(`- **E2E Testing**: ${analysis.testing.e2e}`)
381
- }
382
- }
383
-
384
- if (analysis.hasTests) {
385
- context.push(`- **Tests Detected**: Yes`)
386
- }
387
-
388
- return context.length > 0 ? context.join('\n') : '- No specific testing context detected'
389
- }
390
-
391
- /**
392
- * Generate DevOps-specific context
393
- */
394
- generateDevOpsContext(analysis) {
395
- const context = []
396
-
397
- if (analysis.hasDocker) {
398
- context.push(`- **Docker**: Detected (Dockerfile, docker-compose)`)
399
- }
400
-
401
- if (analysis.hasKubernetes) {
402
- context.push(`- **Kubernetes**: Detected`)
403
- }
404
-
405
- if (analysis.hasCI) {
406
- context.push(`- **CI/CD**: ${analysis.ciProvider || 'Detected'}`)
407
- }
408
-
409
- if (analysis.deployment) {
410
- context.push(`- **Deployment**: ${analysis.deployment}`)
411
- }
412
-
413
- return context.length > 0 ? context.join('\n') : '- Infrastructure and deployment files detected'
414
- }
415
-
416
- /**
417
- * Generate security-specific context
418
- */
419
- generateSecurityContext(analysis) {
420
- const context = []
421
-
422
- if (analysis.hasAuth) {
423
- context.push(`- **Authentication**: Detected`)
424
- }
425
-
426
- if (analysis.hasAPI) {
427
- context.push(`- **API Security**: Focus on endpoint security`)
428
- }
429
-
430
- if (analysis.type === 'web' || analysis.isWebApp) {
431
- context.push(`- **Web Security**: OWASP Top 10 compliance`)
432
- }
433
-
434
- context.push(`- **Focus**: Security audits, vulnerability assessment, secure coding practices`)
435
-
436
- return context.join('\n')
437
- }
438
-
439
- /**
440
- * Generate generic project context
441
- */
442
- generateGenericContext(analysis) {
443
- const context = []
444
-
445
- if (analysis.description) {
446
- context.push(`- **Description**: ${analysis.description}`)
447
- }
448
-
449
- if (analysis.mainDirectories && analysis.mainDirectories.length > 0) {
450
- context.push(`- **Main Directories**: ${analysis.mainDirectories.join(', ')}`)
451
- }
452
-
453
- if (analysis.totalFiles) {
454
- context.push(`- **Total Files**: ${analysis.totalFiles}`)
455
- }
456
-
457
- return context.length > 0 ? context.join('\n') : '- Standard project setup'
458
- }
459
-
460
- /**
461
- * Update existing agents with new project context
462
- * @param {Object} analysis - Updated project analysis
463
- * @returns {Promise<Array>} List of updated agents
464
- */
465
- async updateExistingAgents(analysis) {
466
- console.log('\n🔄 Updating existing agents...\n')
467
-
468
- const updated = []
469
-
470
- // Check which agents currently exist
471
- try {
472
- const files = await fs.readdir(this.outputDir)
473
- const agentFiles = files.filter(f => f.endsWith('.md') && !f.startsWith('.'))
474
-
475
- for (const file of agentFiles) {
476
- const type = file.replace('.md', '')
477
-
478
- try {
479
- await this.generateAgent(type, analysis)
480
- updated.push(type)
481
- console.log(` ↻ ${type.toUpperCase()} agent updated`)
482
- } catch (error) {
483
- console.error(` ❌ Failed to update ${type} agent:`, error.message)
484
- }
485
- }
486
- } catch (error) {
487
- console.error('Error reading agents directory:', error.message)
488
- }
489
-
490
- console.log(`\n✨ Updated ${updated.length} agents\n`)
491
-
492
- return updated
493
- }
494
-
495
- /**
496
- * Remove agents that are no longer needed
497
- * @param {Array} requiredAgents - List of agents that should exist
498
- * @returns {Promise<Array>} List of removed agents
499
- */
500
- async cleanupObsoleteAgents(requiredAgents) {
501
- const removed = []
502
-
503
- try {
504
- const files = await fs.readdir(this.outputDir)
505
- const agentFiles = files.filter(f => f.endsWith('.md') && !f.startsWith('.'))
506
-
507
- for (const file of agentFiles) {
508
- const type = file.replace('.md', '')
509
-
510
- if (!requiredAgents.includes(type)) {
511
- const filePath = path.join(this.outputDir, file)
512
- await fs.unlink(filePath)
513
- removed.push(type)
514
- console.log(` 🗑️ ${type.toUpperCase()} agent removed (no longer needed)`)
515
- }
516
- }
517
- } catch (error) {
518
- console.error('Error during cleanup:', error.message)
519
- }
520
-
521
- return removed
522
- }
523
- }
524
-
525
- module.exports = AgentGenerator