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.
- package/CHANGELOG.md +153 -1
- package/CLAUDE.md +43 -28
- package/README.md +1 -1
- package/bin/prjct +78 -63
- package/core/agent-generator.js +19 -10
- package/core/ascii-graphics.js +433 -0
- package/core/command-registry.js +553 -0
- package/core/commands.js +203 -4
- package/core/task-schema.js +342 -0
- package/package.json +2 -1
- package/templates/agents/AGENTS.md +79 -101
- package/templates/agents/be.template.md +14 -29
- package/templates/agents/coordinator.template.md +34 -0
- package/templates/agents/data.template.md +14 -28
- package/templates/agents/devops.template.md +14 -28
- package/templates/agents/fe.template.md +14 -29
- package/templates/agents/mobile.template.md +14 -28
- package/templates/agents/qa.template.md +14 -41
- package/templates/agents/scribe.template.md +15 -81
- package/templates/agents/security.template.md +14 -28
- package/templates/agents/ux.template.md +14 -36
- package/templates/commands/analyze.md +36 -239
- package/templates/commands/build.md +41 -0
- package/templates/commands/cleanup.md +24 -87
- package/templates/commands/context.md +24 -93
- package/templates/commands/design.md +20 -98
- package/templates/commands/done.md +16 -181
- package/templates/commands/fix.md +27 -66
- package/templates/commands/git.md +33 -60
- package/templates/commands/help.md +18 -52
- package/templates/commands/idea.md +11 -36
- package/templates/commands/init.md +30 -277
- package/templates/commands/next.md +20 -62
- package/templates/commands/now.md +18 -22
- package/templates/commands/progress.md +23 -78
- package/templates/commands/recap.md +22 -74
- package/templates/commands/roadmap.md +21 -90
- package/templates/commands/ship.md +26 -161
- package/templates/commands/status.md +40 -0
- package/templates/commands/stuck.md +21 -33
- package/templates/commands/sync.md +19 -209
- package/templates/commands/task.md +18 -80
- package/templates/commands/test.md +23 -72
- package/templates/commands/workflow.md +20 -212
- 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', {
|
|
1167
|
+
await this.logToMemory(projectPath, 'idea', {
|
|
1168
|
+
text,
|
|
1169
|
+
timestamp: this.agent.getTimestamp(),
|
|
1170
|
+
workflow: workflowCreated ? workflowType : null,
|
|
1171
|
+
})
|
|
1148
1172
|
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
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.
|
|
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",
|