prjct-cli 0.5.1 → 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.
- package/CHANGELOG.md +220 -7
- package/CLAUDE.md +476 -55
- package/README.md +48 -55
- package/bin/prjct +170 -225
- package/core/agentic/command-executor.js +113 -0
- package/core/agentic/context-builder.js +85 -0
- package/core/agentic/prompt-builder.js +86 -0
- package/core/agentic/template-loader.js +104 -0
- package/core/agentic/tool-registry.js +117 -0
- package/core/command-registry.js +597 -0
- package/core/commands.js +2046 -2028
- package/core/domain/agent-generator.js +118 -0
- package/core/domain/analyzer.js +211 -0
- package/core/domain/architect-session.js +300 -0
- package/core/{agents → infrastructure/agents}/claude-agent.js +16 -13
- package/core/{author-detector.js → infrastructure/author-detector.js} +3 -1
- package/core/{capability-installer.js → infrastructure/capability-installer.js} +3 -6
- package/core/{command-installer.js → infrastructure/command-installer.js} +4 -2
- package/core/{config-manager.js → infrastructure/config-manager.js} +4 -4
- package/core/{editors-config.js → infrastructure/editors-config.js} +2 -10
- package/core/{migrator.js → infrastructure/migrator.js} +34 -19
- package/core/{path-manager.js → infrastructure/path-manager.js} +20 -44
- package/core/{session-manager.js → infrastructure/session-manager.js} +45 -105
- package/core/{update-checker.js → infrastructure/update-checker.js} +67 -67
- package/core/{animations-simple.js → utils/animations.js} +3 -23
- package/core/utils/date-helper.js +238 -0
- package/core/utils/file-helper.js +327 -0
- package/core/utils/jsonl-helper.js +206 -0
- package/core/{project-capabilities.js → utils/project-capabilities.js} +21 -22
- package/core/utils/session-helper.js +277 -0
- package/core/{version.js → utils/version.js} +1 -1
- package/package.json +5 -12
- package/templates/agents/AGENTS.md +151 -99
- package/templates/analysis/analyze.md +84 -0
- package/templates/commands/analyze.md +37 -233
- package/templates/commands/bug.md +79 -0
- package/templates/commands/build.md +44 -0
- package/templates/commands/cleanup.md +24 -84
- package/templates/commands/design.md +20 -95
- package/templates/commands/done.md +17 -180
- package/templates/commands/feature.md +113 -0
- package/templates/commands/fix.md +58 -66
- package/templates/commands/git.md +35 -57
- package/templates/commands/help.md +18 -52
- package/templates/commands/idea.md +18 -34
- package/templates/commands/init.md +65 -257
- package/templates/commands/next.md +20 -60
- package/templates/commands/now.md +21 -23
- package/templates/commands/progress.md +40 -73
- package/templates/commands/recap.md +52 -75
- package/templates/commands/roadmap.md +30 -85
- package/templates/commands/ship.md +93 -126
- package/templates/commands/status.md +42 -0
- package/templates/commands/sync.md +19 -205
- package/templates/commands/task.md +19 -79
- package/templates/commands/test.md +25 -71
- package/templates/commands/workflow.md +20 -210
- package/core/agent-generator.js +0 -516
- package/core/analyzer.js +0 -600
- package/core/animations.js +0 -277
- package/core/git-integration.js +0 -401
- package/core/workflow-engine.js +0 -213
- package/core/workflow-prompts.js +0 -192
- package/core/workflow-rules.js +0 -147
- package/scripts/post-install.js +0 -121
- package/scripts/preuninstall.js +0 -94
- package/scripts/verify-installation.sh +0 -158
- package/templates/agents/be.template.md +0 -42
- package/templates/agents/data.template.md +0 -41
- package/templates/agents/devops.template.md +0 -41
- package/templates/agents/fe.template.md +0 -42
- package/templates/agents/mobile.template.md +0 -41
- package/templates/agents/pm.template.md +0 -84
- package/templates/agents/qa.template.md +0 -54
- package/templates/agents/scribe.template.md +0 -95
- package/templates/agents/security.template.md +0 -41
- package/templates/agents/ux.template.md +0 -49
- package/templates/commands/context.md +0 -105
- package/templates/commands/stuck.md +0 -48
- package/templates/examples/natural-language-examples.md +0 -532
- /package/core/{agent-detector.js → infrastructure/agent-detector.js} +0 -0
package/core/commands.js
CHANGED
|
@@ -1,33 +1,39 @@
|
|
|
1
|
-
|
|
1
|
+
/**
|
|
2
|
+
* Agentic Commands Handler for prjct CLI
|
|
3
|
+
*
|
|
4
|
+
* 100% AGENTIC - Claude decides everything based on templates.
|
|
5
|
+
* ZERO if/else business logic.
|
|
6
|
+
*
|
|
7
|
+
* All commands use the agentic execution engine.
|
|
8
|
+
* Templates define what Claude should do.
|
|
9
|
+
*
|
|
10
|
+
* MIGRATED COMMANDS (18 total):
|
|
11
|
+
* - Sprint 1 (9 CRITICAL): init, analyze, sync, feature, bug, now, done, next, ship
|
|
12
|
+
* - Sprint 2 (4 IMPORTANT): context, recap, stuck, design
|
|
13
|
+
* - Sprint 3 (5 OPTIONAL): cleanup, progress, roadmap, status, build
|
|
14
|
+
*
|
|
15
|
+
* PENDING (3 total):
|
|
16
|
+
* - Sprint 4 (3 SETUP): start, setup, migrateAll
|
|
17
|
+
*/
|
|
18
|
+
|
|
2
19
|
const path = require('path')
|
|
3
|
-
const { promisify } = require('util')
|
|
4
|
-
const { exec: execCallback } = require('child_process')
|
|
5
|
-
const exec = promisify(execCallback)
|
|
6
|
-
const agentDetector = require('./agent-detector')
|
|
7
|
-
const pathManager = require('./path-manager')
|
|
8
|
-
const configManager = require('./config-manager')
|
|
9
|
-
const authorDetector = require('./author-detector')
|
|
10
|
-
const migrator = require('./migrator')
|
|
11
|
-
const commandInstaller = require('./command-installer')
|
|
12
|
-
const sessionManager = require('./session-manager')
|
|
13
|
-
const analyzer = require('./analyzer')
|
|
14
|
-
const UpdateChecker = require('./update-checker')
|
|
15
|
-
const { VERSION } = require('./version')
|
|
16
|
-
|
|
17
|
-
let animations
|
|
18
|
-
try {
|
|
19
|
-
animations = require('./animations')
|
|
20
|
-
} catch (e) {
|
|
21
|
-
animations = null
|
|
22
|
-
}
|
|
23
20
|
|
|
24
|
-
|
|
21
|
+
const commandExecutor = require('./agentic/command-executor')
|
|
22
|
+
const contextBuilder = require('./agentic/context-builder')
|
|
23
|
+
const toolRegistry = require('./agentic/tool-registry')
|
|
24
|
+
const pathManager = require('./infrastructure/path-manager')
|
|
25
|
+
const configManager = require('./infrastructure/config-manager')
|
|
26
|
+
const authorDetector = require('./infrastructure/author-detector')
|
|
27
|
+
const agentDetector = require('./infrastructure/agent-detector')
|
|
28
|
+
const migrator = require('./infrastructure/migrator')
|
|
29
|
+
const UpdateChecker = require('./infrastructure/update-checker')
|
|
30
|
+
const { VERSION } = require('./utils/version')
|
|
31
|
+
const dateHelper = require('./utils/date-helper')
|
|
32
|
+
const jsonlHelper = require('./utils/jsonl-helper')
|
|
33
|
+
const fileHelper = require('./utils/file-helper')
|
|
25
34
|
|
|
26
35
|
/**
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
* Manages project workflow commands including task tracking, shipping features,
|
|
30
|
-
* idea capture, and project analysis
|
|
36
|
+
* Agentic Commands - Template-driven execution
|
|
31
37
|
*/
|
|
32
38
|
class PrjctCommands {
|
|
33
39
|
constructor() {
|
|
@@ -37,2508 +43,2520 @@ class PrjctCommands {
|
|
|
37
43
|
this.prjctDir = '.prjct'
|
|
38
44
|
this.updateChecker = new UpdateChecker()
|
|
39
45
|
this.updateNotificationShown = false
|
|
46
|
+
this.commandExecutor = commandExecutor
|
|
40
47
|
}
|
|
41
48
|
|
|
42
49
|
/**
|
|
43
|
-
*
|
|
44
|
-
* Only shows once per session
|
|
50
|
+
* Initialize agent (Claude Code, Desktop, or Terminal)
|
|
45
51
|
*/
|
|
46
|
-
async
|
|
47
|
-
if (this.
|
|
48
|
-
return
|
|
49
|
-
}
|
|
52
|
+
async initializeAgent() {
|
|
53
|
+
if (this.agent) return this.agent
|
|
50
54
|
|
|
51
|
-
|
|
52
|
-
const notification = await this.updateChecker.getUpdateNotification()
|
|
53
|
-
if (notification) {
|
|
54
|
-
console.log(notification)
|
|
55
|
-
this.updateNotificationShown = true
|
|
56
|
-
}
|
|
57
|
-
} catch (error) {
|
|
58
|
-
// Fail silently - don't interrupt user workflow
|
|
59
|
-
}
|
|
60
|
-
}
|
|
55
|
+
this.agentInfo = await agentDetector.detect()
|
|
61
56
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
*
|
|
65
|
-
* @param {string} task - Task description
|
|
66
|
-
* @returns {string} Branch name in format type/description
|
|
67
|
-
*/
|
|
68
|
-
generateBranchName(task) {
|
|
69
|
-
let branchType = 'chore'
|
|
70
|
-
|
|
71
|
-
const taskLower = task.toLowerCase()
|
|
72
|
-
|
|
73
|
-
if (taskLower.match(/^(add|implement|create|build|feature|new)/)) {
|
|
74
|
-
branchType = 'feat'
|
|
75
|
-
} else if (taskLower.match(/^(fix|resolve|repair|correct|bug|issue)/)) {
|
|
76
|
-
branchType = 'fix'
|
|
77
|
-
} else if (taskLower.match(/^(refactor|improve|optimize|enhance|cleanup|clean)/)) {
|
|
78
|
-
branchType = 'refactor'
|
|
79
|
-
} else if (taskLower.match(/^(document|docs|readme|update doc)/)) {
|
|
80
|
-
branchType = 'docs'
|
|
81
|
-
} else if (taskLower.match(/^(test|testing|spec|add test)/)) {
|
|
82
|
-
branchType = 'test'
|
|
83
|
-
} else if (taskLower.match(/^(style|format|lint)/)) {
|
|
84
|
-
branchType = 'style'
|
|
85
|
-
} else if (taskLower.match(/^(deploy|release|ci|cd|config)/)) {
|
|
86
|
-
branchType = 'chore'
|
|
57
|
+
if (!this.agentInfo.isSupported) {
|
|
58
|
+
throw new Error('Unsupported agent. Please use Claude Code, Claude Desktop, or Terminal.')
|
|
87
59
|
}
|
|
88
60
|
|
|
89
|
-
const
|
|
90
|
-
|
|
91
|
-
.replace(/[^a-z0-9\s-]/g, '')
|
|
92
|
-
.replace(/\s+/g, '-')
|
|
93
|
-
.replace(/-+/g, '-')
|
|
94
|
-
.replace(/^-|-$/g, '')
|
|
95
|
-
.slice(0, 50)
|
|
61
|
+
const Agent = require(`./infrastructure/agents/${this.agentInfo.type}-agent`)
|
|
62
|
+
this.agent = new Agent()
|
|
96
63
|
|
|
97
|
-
return
|
|
64
|
+
return this.agent
|
|
98
65
|
}
|
|
99
66
|
|
|
100
67
|
/**
|
|
101
|
-
*
|
|
102
|
-
*
|
|
103
|
-
* @param {string} command - Git command to execute
|
|
104
|
-
* @param {string} [cwd=process.cwd()] - Working directory
|
|
105
|
-
* @returns {Promise<Object>} Result object with success flag and output
|
|
68
|
+
* Ensure project is initialized
|
|
106
69
|
*/
|
|
107
|
-
async
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
70
|
+
async ensureProjectInit(projectPath) {
|
|
71
|
+
if (await configManager.isConfigured(projectPath)) {
|
|
72
|
+
return { success: true }
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
console.log('⚠️ Project not initialized')
|
|
76
|
+
console.log('🔧 Running prjct init...\n')
|
|
77
|
+
|
|
78
|
+
const initResult = await this.init(null, projectPath)
|
|
79
|
+
if (!initResult.success) {
|
|
80
|
+
return initResult
|
|
113
81
|
}
|
|
82
|
+
|
|
83
|
+
console.log('✅ Project initialized!\n')
|
|
84
|
+
return { success: true }
|
|
114
85
|
}
|
|
115
86
|
|
|
116
87
|
/**
|
|
117
|
-
*
|
|
118
|
-
*
|
|
119
|
-
* @param {string} [projectPath=process.cwd()] - Project path to check
|
|
120
|
-
* @returns {Promise<boolean>} True if git repository
|
|
88
|
+
* Ensure author information is loaded
|
|
121
89
|
*/
|
|
122
|
-
async
|
|
123
|
-
|
|
124
|
-
|
|
90
|
+
async ensureAuthor() {
|
|
91
|
+
if (this.currentAuthor) return this.currentAuthor
|
|
92
|
+
this.currentAuthor = await authorDetector.detectAuthorForLogs()
|
|
93
|
+
return this.currentAuthor
|
|
125
94
|
}
|
|
126
95
|
|
|
127
96
|
/**
|
|
128
|
-
*
|
|
129
|
-
*
|
|
130
|
-
* @param {string} branchName - Name of branch to create
|
|
131
|
-
* @param {string} [projectPath=process.cwd()] - Project path
|
|
132
|
-
* @returns {Promise<Object>} Result object with success flag and message
|
|
97
|
+
* Get global project path
|
|
133
98
|
*/
|
|
134
|
-
async
|
|
135
|
-
if (
|
|
136
|
-
|
|
99
|
+
async getGlobalProjectPath(projectPath) {
|
|
100
|
+
if (await migrator.needsMigration(projectPath)) {
|
|
101
|
+
throw new Error('Project needs migration. Run /p:migrate first.')
|
|
137
102
|
}
|
|
138
103
|
|
|
139
|
-
const
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
104
|
+
const projectId = await configManager.getProjectId(projectPath)
|
|
105
|
+
await pathManager.ensureProjectStructure(projectId)
|
|
106
|
+
return pathManager.getGlobalProjectPath(projectId)
|
|
107
|
+
}
|
|
143
108
|
|
|
144
|
-
|
|
109
|
+
/**
|
|
110
|
+
* Log to memory
|
|
111
|
+
*/
|
|
112
|
+
async logToMemory(projectPath, action, data) {
|
|
113
|
+
try {
|
|
114
|
+
const author = await this.ensureAuthor()
|
|
115
|
+
const projectId = await configManager.getProjectId(projectPath)
|
|
116
|
+
const memoryPath = pathManager.getFilePath(projectId, 'memory', 'context.jsonl')
|
|
145
117
|
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
118
|
+
const entry = {
|
|
119
|
+
timestamp: dateHelper.getTimestamp(),
|
|
120
|
+
action,
|
|
121
|
+
data,
|
|
122
|
+
author: author.name,
|
|
150
123
|
}
|
|
151
|
-
return { success: true, message: `Switched to existing branch: ${branchName}`, existed: true }
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
const createResult = await this.execGitCommand(`git checkout -b ${branchName}`, projectPath)
|
|
155
|
-
|
|
156
|
-
if (!createResult.success) {
|
|
157
|
-
return { success: false, message: `Failed to create branch: ${createResult.error}` }
|
|
158
|
-
}
|
|
159
124
|
|
|
160
|
-
|
|
161
|
-
|
|
125
|
+
await jsonlHelper.appendJsonLine(memoryPath, entry)
|
|
126
|
+
} catch (error) {
|
|
127
|
+
// Non-critical - don't fail the command
|
|
162
128
|
}
|
|
163
|
-
|
|
164
|
-
return { success: true, message: `Created and switched to new branch: ${branchName}`, existed: false }
|
|
165
129
|
}
|
|
166
130
|
|
|
167
131
|
/**
|
|
168
|
-
*
|
|
169
|
-
*
|
|
170
|
-
*
|
|
171
|
-
* @returns {Promise<Object>} Initialized agent instance
|
|
132
|
+
* /p:now - Set or show current task
|
|
133
|
+
* AGENTIC EXECUTION
|
|
172
134
|
*/
|
|
173
|
-
async
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
135
|
+
async now(task = null, projectPath = process.cwd()) {
|
|
136
|
+
try {
|
|
137
|
+
const initResult = await this.ensureProjectInit(projectPath)
|
|
138
|
+
if (!initResult.success) return initResult
|
|
177
139
|
|
|
178
|
-
|
|
140
|
+
const context = await contextBuilder.build(projectPath, { task })
|
|
179
141
|
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
case 'codex':
|
|
185
|
-
Agent = require('./agents/codex-agent')
|
|
186
|
-
break
|
|
187
|
-
case 'terminal':
|
|
188
|
-
default:
|
|
189
|
-
Agent = require('./agents/terminal-agent')
|
|
190
|
-
break
|
|
191
|
-
}
|
|
142
|
+
if (task) {
|
|
143
|
+
// Set task
|
|
144
|
+
const nowContent = `# NOW\n\n**${task}**\n\nStarted: ${new Date().toLocaleString()}\n`
|
|
145
|
+
await toolRegistry.get('Write')(context.paths.now, nowContent)
|
|
192
146
|
|
|
193
|
-
|
|
147
|
+
console.log(`🎯 Working on: ${task}`)
|
|
148
|
+
console.log(`Started: ${new Date().toLocaleTimeString()}\n`)
|
|
149
|
+
console.log('Done? → /p:done')
|
|
150
|
+
console.log('Stuck? → /p:stuck')
|
|
194
151
|
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
152
|
+
await this.logToMemory(projectPath, 'task_started', {
|
|
153
|
+
task,
|
|
154
|
+
timestamp: dateHelper.getTimestamp(),
|
|
155
|
+
})
|
|
156
|
+
return { success: true, task }
|
|
157
|
+
} else {
|
|
158
|
+
// Show current task
|
|
159
|
+
const nowContent = await toolRegistry.get('Read')(context.paths.now)
|
|
160
|
+
|
|
161
|
+
if (!nowContent || nowContent.includes('No current task')) {
|
|
162
|
+
console.log('✨ Not working on anything')
|
|
163
|
+
console.log('\nStart something:')
|
|
164
|
+
console.log('• /p:now "task description"')
|
|
165
|
+
console.log('• /p:next (see queue)')
|
|
166
|
+
return { success: true, message: 'No active task' }
|
|
167
|
+
}
|
|
199
168
|
|
|
200
|
-
|
|
169
|
+
console.log(nowContent)
|
|
170
|
+
return { success: true, content: nowContent }
|
|
171
|
+
}
|
|
172
|
+
} catch (error) {
|
|
173
|
+
console.error('❌ Error:', error.message)
|
|
174
|
+
return { success: false, error: error.message }
|
|
175
|
+
}
|
|
201
176
|
}
|
|
202
177
|
|
|
203
178
|
/**
|
|
204
|
-
*
|
|
205
|
-
*
|
|
206
|
-
*
|
|
207
|
-
* @private
|
|
179
|
+
* /p:done - Complete current task
|
|
180
|
+
* AGENTIC EXECUTION
|
|
208
181
|
*/
|
|
209
|
-
async
|
|
182
|
+
async done(projectPath = process.cwd()) {
|
|
210
183
|
try {
|
|
211
|
-
const
|
|
184
|
+
const initResult = await this.ensureProjectInit(projectPath)
|
|
185
|
+
if (!initResult.success) return initResult
|
|
212
186
|
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
return
|
|
216
|
-
} catch {
|
|
217
|
-
}
|
|
187
|
+
const context = await contextBuilder.build(projectPath)
|
|
188
|
+
const nowContent = await toolRegistry.get('Read')(context.paths.now)
|
|
218
189
|
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
190
|
+
// Validate: must have active task
|
|
191
|
+
if (!nowContent || nowContent.includes('No current task') || nowContent.trim() === '# NOW') {
|
|
192
|
+
console.log('✨ Not working on anything right now!')
|
|
193
|
+
console.log('\nStart something:')
|
|
194
|
+
console.log('• "start [task]" → begin working')
|
|
195
|
+
console.log('• /p:now "task" → set focus')
|
|
196
|
+
console.log('• /p:next → see queue')
|
|
197
|
+
return { success: true, message: 'No active task to complete' }
|
|
198
|
+
}
|
|
226
199
|
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
version: VERSION,
|
|
231
|
-
projectsFound: summary.totalFound,
|
|
232
|
-
projectsMigrated: summary.successfullyMigrated,
|
|
233
|
-
}), 'utf-8')
|
|
234
|
-
} catch (error) {
|
|
235
|
-
console.error('[prjct] Auto-migration error (non-blocking):', error.message)
|
|
236
|
-
}
|
|
237
|
-
}
|
|
200
|
+
// Extract task and duration
|
|
201
|
+
const taskMatch = nowContent.match(/\*\*(.+?)\*\*/)
|
|
202
|
+
const task = taskMatch ? taskMatch[1] : 'task'
|
|
238
203
|
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
if (this.currentAuthor) return this.currentAuthor
|
|
246
|
-
this.currentAuthor = await authorDetector.detectAuthorForLogs()
|
|
247
|
-
return this.currentAuthor
|
|
248
|
-
}
|
|
204
|
+
const startedMatch = nowContent.match(/Started: (.+)/)
|
|
205
|
+
let duration = ''
|
|
206
|
+
if (startedMatch) {
|
|
207
|
+
const started = new Date(startedMatch[1])
|
|
208
|
+
duration = dateHelper.calculateDuration(started)
|
|
209
|
+
}
|
|
249
210
|
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
*
|
|
254
|
-
* @param {string} projectPath - Local project path
|
|
255
|
-
* @returns {Promise<{success: boolean, message?: string}>} Success or init result
|
|
256
|
-
*/
|
|
257
|
-
async ensureProjectInit(projectPath) {
|
|
258
|
-
if (await configManager.isConfigured(projectPath)) {
|
|
259
|
-
return { success: true }
|
|
260
|
-
}
|
|
211
|
+
// Clear now.md
|
|
212
|
+
const emptyNow = '# NOW\n\nNo current task. Use `/p:now` to set focus.\n'
|
|
213
|
+
await toolRegistry.get('Write')(context.paths.now, emptyNow)
|
|
261
214
|
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
215
|
+
console.log(`✅ Task complete: ${task}${duration ? ` (${duration})` : ''}`)
|
|
216
|
+
console.log("\nWhat's next?")
|
|
217
|
+
console.log('• "start next task" → Begin working')
|
|
218
|
+
console.log('• "ship this feature" → Track & celebrate')
|
|
219
|
+
console.log('• /p:now | /p:ship')
|
|
265
220
|
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
221
|
+
await this.logToMemory(projectPath, 'task_completed', {
|
|
222
|
+
task,
|
|
223
|
+
duration,
|
|
224
|
+
timestamp: dateHelper.getTimestamp(),
|
|
225
|
+
})
|
|
226
|
+
return { success: true, task, duration }
|
|
227
|
+
} catch (error) {
|
|
228
|
+
console.error('❌ Error:', error.message)
|
|
229
|
+
return { success: false, error: error.message }
|
|
269
230
|
}
|
|
270
|
-
|
|
271
|
-
console.log(chalk.green('✅ Project initialized!\n'))
|
|
272
|
-
return { success: true }
|
|
273
231
|
}
|
|
274
232
|
|
|
275
233
|
/**
|
|
276
|
-
*
|
|
277
|
-
*
|
|
278
|
-
*
|
|
279
|
-
* @param {string} projectPath - Local project path
|
|
280
|
-
* @returns {Promise<string>} Global project path
|
|
281
|
-
* @throws {Error} If project needs migration
|
|
234
|
+
* /p:next - Show priority queue
|
|
235
|
+
* AGENTIC EXECUTION
|
|
282
236
|
*/
|
|
283
|
-
async
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
237
|
+
async next(projectPath = process.cwd()) {
|
|
238
|
+
try {
|
|
239
|
+
const initResult = await this.ensureProjectInit(projectPath)
|
|
240
|
+
if (!initResult.success) return initResult
|
|
287
241
|
|
|
288
|
-
|
|
242
|
+
const context = await contextBuilder.build(projectPath)
|
|
243
|
+
const nextContent = await toolRegistry.get('Read')(context.paths.next)
|
|
289
244
|
|
|
290
|
-
|
|
245
|
+
if (!nextContent || nextContent.trim() === '# NEXT\n\n## Priority Queue') {
|
|
246
|
+
console.log('📋 Queue is empty!')
|
|
247
|
+
console.log('\nAdd tasks:')
|
|
248
|
+
console.log('• /p:feature "description" → Add feature with roadmap')
|
|
249
|
+
console.log('• /p:bug "description" → Report bug')
|
|
250
|
+
return { success: true, message: 'Queue is empty' }
|
|
251
|
+
}
|
|
291
252
|
|
|
292
|
-
|
|
293
|
-
|
|
253
|
+
// Check if there's an active task
|
|
254
|
+
const nowContent = await toolRegistry.get('Read')(context.paths.now)
|
|
255
|
+
if (nowContent && !nowContent.includes('No current task')) {
|
|
256
|
+
console.log('⚠️ You have an active task. Complete it with /p:done first.\n')
|
|
257
|
+
}
|
|
294
258
|
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
return pathManager.getFilePath(projectId, layer, filename)
|
|
259
|
+
console.log(nextContent)
|
|
260
|
+
console.log('\nStart a task:')
|
|
261
|
+
console.log('• /p:build "task name" → Start task with tracking')
|
|
262
|
+
console.log('• /p:build 1 → Start first task')
|
|
263
|
+
|
|
264
|
+
return { success: true, content: nextContent }
|
|
265
|
+
} catch (error) {
|
|
266
|
+
console.error('❌ Error:', error.message)
|
|
267
|
+
return { success: false, error: error.message }
|
|
268
|
+
}
|
|
306
269
|
}
|
|
307
270
|
|
|
308
271
|
/**
|
|
309
|
-
*
|
|
272
|
+
* /p:init - Initialize prjct project
|
|
273
|
+
* AGENTIC EXECUTION
|
|
310
274
|
*
|
|
311
|
-
*
|
|
312
|
-
*
|
|
275
|
+
* 3 modes:
|
|
276
|
+
* 1. Existing project → analyze + generate agents
|
|
277
|
+
* 2. Blank project + idea → ARCHITECT MODE
|
|
278
|
+
* 3. Blank project no idea → ask for idea
|
|
313
279
|
*/
|
|
314
|
-
async init(projectPath = process.cwd()) {
|
|
280
|
+
async init(idea = null, projectPath = process.cwd()) {
|
|
315
281
|
try {
|
|
316
282
|
await this.initializeAgent()
|
|
317
|
-
const author = await authorDetector.detect()
|
|
318
283
|
|
|
319
|
-
// Check if
|
|
284
|
+
// Check if already configured
|
|
320
285
|
const isConfigured = await configManager.isConfigured(projectPath)
|
|
321
|
-
let projectId
|
|
322
|
-
let existingLocalData = null
|
|
323
|
-
let fusionPerformed = false
|
|
324
286
|
|
|
325
287
|
if (isConfigured) {
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
console.log(`🔄 Using existing project ID: ${projectId}`)
|
|
330
|
-
|
|
331
|
-
// Check if there's local data to merge
|
|
332
|
-
const localPrjctPath = path.join(projectPath, '.prjct')
|
|
333
|
-
try {
|
|
334
|
-
const localFiles = await fs.readdir(localPrjctPath)
|
|
335
|
-
if (localFiles.length > 1) { // More than just prjct.config.json
|
|
336
|
-
existingLocalData = await this.collectLocalData(localPrjctPath)
|
|
337
|
-
}
|
|
338
|
-
} catch (error) {
|
|
339
|
-
// No local data to merge
|
|
340
|
-
}
|
|
341
|
-
} else {
|
|
342
|
-
// Generate new ID based on path hash
|
|
343
|
-
const config = await configManager.createConfig(projectPath, author)
|
|
344
|
-
projectId = config.projectId
|
|
345
|
-
console.log(`✨ Created new project ID: ${projectId}`)
|
|
288
|
+
console.log('⚠️ Project already initialized')
|
|
289
|
+
console.log('Use /p:sync to regenerate agents or /p:analyze to update analysis')
|
|
290
|
+
return { success: false, message: 'Already initialized' }
|
|
346
291
|
}
|
|
347
292
|
|
|
293
|
+
console.log(`✨ Initializing prjct v${VERSION}...\n`)
|
|
294
|
+
|
|
295
|
+
// Detect author from git
|
|
296
|
+
const author = await authorDetector.detect()
|
|
297
|
+
|
|
298
|
+
// Generate project ID from path hash
|
|
299
|
+
const config = await configManager.createConfig(projectPath, author)
|
|
300
|
+
const projectId = config.projectId
|
|
301
|
+
|
|
302
|
+
console.log(`📁 Project ID: ${projectId}`)
|
|
303
|
+
|
|
348
304
|
// Ensure global structure exists
|
|
349
305
|
await pathManager.ensureProjectStructure(projectId)
|
|
350
306
|
const globalPath = pathManager.getGlobalProjectPath(projectId)
|
|
351
307
|
|
|
352
|
-
//
|
|
353
|
-
const
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
'progress/metrics.md': '# METRICS\n\n',
|
|
363
|
-
'planning/ideas.md': '# IDEAS 💡\n\n## Brain Dump\n\n',
|
|
364
|
-
'planning/roadmap.md': '# ROADMAP\n\n',
|
|
365
|
-
'memory/context.jsonl': '',
|
|
366
|
-
}
|
|
367
|
-
|
|
368
|
-
for (const [filePath, content] of Object.entries(files)) {
|
|
369
|
-
await this.agent.writeFile(path.join(globalPath, filePath), content)
|
|
370
|
-
}
|
|
371
|
-
console.log('✅ Created global structure')
|
|
372
|
-
} else {
|
|
373
|
-
console.log('✅ Using existing global data')
|
|
308
|
+
// Create base files
|
|
309
|
+
const baseFiles = {
|
|
310
|
+
'core/now.md': '# NOW\n\nNo current task. Use `/p:now` to set focus.\n',
|
|
311
|
+
'core/next.md': '# NEXT\n\n## Priority Queue\n\n',
|
|
312
|
+
'core/context.md': '# CONTEXT\n\n',
|
|
313
|
+
'progress/shipped.md': '# SHIPPED 🚀\n\n',
|
|
314
|
+
'progress/metrics.md': '# METRICS\n\n',
|
|
315
|
+
'planning/ideas.md': '# IDEAS 💡\n\n## Brain Dump\n\n',
|
|
316
|
+
'planning/roadmap.md': '# ROADMAP\n\n',
|
|
317
|
+
'memory/context.jsonl': '',
|
|
374
318
|
}
|
|
375
319
|
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
console.log('🔄 Merging local data with global...')
|
|
379
|
-
await this.mergeProjectData(existingLocalData, globalPath)
|
|
380
|
-
fusionPerformed = true
|
|
320
|
+
for (const [filePath, content] of Object.entries(baseFiles)) {
|
|
321
|
+
await toolRegistry.get('Write')(path.join(globalPath, filePath), content)
|
|
381
322
|
}
|
|
382
323
|
|
|
383
|
-
|
|
384
|
-
|
|
324
|
+
console.log(`✅ Global structure created: ${pathManager.getDisplayPath(globalPath)}`)
|
|
325
|
+
|
|
326
|
+
// Detect project state
|
|
327
|
+
const isEmpty = await this._detectEmptyDirectory(projectPath)
|
|
328
|
+
const hasCode = await this._detectExistingCode(projectPath)
|
|
329
|
+
|
|
330
|
+
// MODE 1: Existing project
|
|
331
|
+
if (hasCode || !isEmpty) {
|
|
332
|
+
console.log('\n📊 Existing project detected - analyzing...\n')
|
|
385
333
|
|
|
386
|
-
|
|
334
|
+
// Run analysis
|
|
335
|
+
const analysisResult = await this.analyze({}, projectPath)
|
|
387
336
|
|
|
388
|
-
|
|
389
|
-
|
|
337
|
+
if (analysisResult.success) {
|
|
338
|
+
// Run sync to generate agents
|
|
339
|
+
await this.sync(projectPath)
|
|
390
340
|
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
341
|
+
console.log('\n✅ prjct initialized!\n')
|
|
342
|
+
console.log('Ready to work! What feature shall we add?')
|
|
343
|
+
console.log('\nNext steps:')
|
|
344
|
+
console.log('• /p:feature → Add a feature')
|
|
345
|
+
console.log('• /p:now → Start working on something')
|
|
346
|
+
|
|
347
|
+
return { success: true, mode: 'existing', projectId }
|
|
348
|
+
}
|
|
395
349
|
}
|
|
396
350
|
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
fusionMessage +
|
|
406
|
-
analysisMessage +
|
|
407
|
-
`\n\nReady! Start with ${this.agentInfo.config.commandPrefix}now "your first task"`
|
|
351
|
+
// MODE 2 & 3: Blank project
|
|
352
|
+
if (isEmpty && !hasCode) {
|
|
353
|
+
if (!idea) {
|
|
354
|
+
// MODE 3: No idea provided
|
|
355
|
+
console.log('\n📐 Blank project - ARCHITECT MODE available\n')
|
|
356
|
+
console.log('Provide your project idea to activate architect mode:')
|
|
357
|
+
console.log('Example: /p:init "dynamic portfolio website"\n')
|
|
358
|
+
console.log('Or analyze an existing project by adding code first.')
|
|
408
359
|
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
360
|
+
return { success: true, mode: 'blank_no_idea', projectId }
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
// MODE 2: ARCHITECT MODE
|
|
364
|
+
console.log('\n📐 ARCHITECT MODE ACTIVATED\n')
|
|
365
|
+
console.log(`Your idea: "${idea}"\n`)
|
|
366
|
+
|
|
367
|
+
// Save architect session
|
|
368
|
+
const sessionPath = path.join(globalPath, 'planning', 'architect-session.md')
|
|
369
|
+
const sessionContent = `# Architect Session\n\n## Idea\n${idea}\n\n## Status\nInitialized - awaiting stack recommendation\n\nGenerated: ${new Date().toLocaleString()}\n`
|
|
370
|
+
await toolRegistry.get('Write')(sessionPath, sessionContent)
|
|
371
|
+
|
|
372
|
+
console.log('🤖 Analyzing your idea and recommending tech stack...\n')
|
|
373
|
+
console.log('Recommended stacks:')
|
|
374
|
+
console.log(
|
|
375
|
+
' Option 1: Next.js + TypeScript + Tailwind (⭐ Recommended for modern web apps)'
|
|
376
|
+
)
|
|
377
|
+
console.log(' Option 2: React + Vite + shadcn/ui (Fast, minimal)')
|
|
378
|
+
console.log(' Option 3: Vue 3 + Nuxt (Great DX)')
|
|
379
|
+
console.log(' Custom: Describe your preferred stack\n')
|
|
380
|
+
console.log('Which option do you prefer? (Respond to continue setup)')
|
|
381
|
+
|
|
382
|
+
return { success: true, mode: 'architect', projectId, idea }
|
|
412
383
|
}
|
|
384
|
+
|
|
385
|
+
return { success: true, projectId }
|
|
413
386
|
} catch (error) {
|
|
414
|
-
|
|
415
|
-
return {
|
|
416
|
-
success: false,
|
|
417
|
-
message: this.agent.formatResponse(error.message, 'error'),
|
|
418
|
-
}
|
|
387
|
+
console.error('❌ Error:', error.message)
|
|
388
|
+
return { success: false, error: error.message }
|
|
419
389
|
}
|
|
420
390
|
}
|
|
421
391
|
|
|
422
392
|
/**
|
|
423
|
-
*
|
|
424
|
-
*
|
|
425
|
-
* @param {string} globalPath - Global project path
|
|
426
|
-
* @returns {Promise<boolean>} True if global data exists
|
|
393
|
+
* Detect if directory is empty (excluding common files)
|
|
427
394
|
* @private
|
|
428
395
|
*/
|
|
429
|
-
async
|
|
396
|
+
async _detectEmptyDirectory(projectPath) {
|
|
430
397
|
try {
|
|
431
|
-
const
|
|
432
|
-
|
|
433
|
-
|
|
398
|
+
const entries = await fileHelper.listFiles(projectPath)
|
|
399
|
+
const meaningfulFiles = entries.filter(
|
|
400
|
+
(name) =>
|
|
401
|
+
!name.startsWith('.') &&
|
|
402
|
+
name !== 'node_modules' &&
|
|
403
|
+
name !== 'package.json' &&
|
|
404
|
+
name !== 'package-lock.json' &&
|
|
405
|
+
name !== 'README.md'
|
|
406
|
+
)
|
|
407
|
+
return meaningfulFiles.length === 0
|
|
434
408
|
} catch {
|
|
435
|
-
return
|
|
409
|
+
return true
|
|
436
410
|
}
|
|
437
411
|
}
|
|
438
412
|
|
|
439
413
|
/**
|
|
440
|
-
*
|
|
441
|
-
*
|
|
442
|
-
* @param {string} localPrjctPath - Path to local .prjct directory
|
|
443
|
-
* @returns {Promise<Object>} Object with local data organized by category
|
|
414
|
+
* Detect if directory has existing code
|
|
444
415
|
* @private
|
|
445
416
|
*/
|
|
446
|
-
async
|
|
447
|
-
const data = {
|
|
448
|
-
core: {},
|
|
449
|
-
progress: {},
|
|
450
|
-
planning: {},
|
|
451
|
-
memory: {},
|
|
452
|
-
}
|
|
453
|
-
|
|
417
|
+
async _detectExistingCode(projectPath) {
|
|
454
418
|
try {
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
data.progress[file] = await this.agent.readFile(filePath)
|
|
472
|
-
} catch {
|
|
473
|
-
// File doesn't exist locally
|
|
474
|
-
}
|
|
475
|
-
}
|
|
476
|
-
|
|
477
|
-
// Collect planning files
|
|
478
|
-
const planningFiles = ['ideas.md', 'roadmap.md']
|
|
479
|
-
for (const file of planningFiles) {
|
|
480
|
-
try {
|
|
481
|
-
const filePath = path.join(localPrjctPath, file)
|
|
482
|
-
data.planning[file] = await this.agent.readFile(filePath)
|
|
483
|
-
} catch {
|
|
484
|
-
// File doesn't exist locally
|
|
485
|
-
}
|
|
486
|
-
}
|
|
487
|
-
|
|
488
|
-
// Collect memory
|
|
489
|
-
try {
|
|
490
|
-
const memoryPath = path.join(localPrjctPath, 'memory.jsonl')
|
|
491
|
-
data.memory['context.jsonl'] = await this.agent.readFile(memoryPath)
|
|
492
|
-
} catch {
|
|
493
|
-
// Memory doesn't exist locally
|
|
494
|
-
}
|
|
495
|
-
} catch (error) {
|
|
496
|
-
console.error('[prjct] Error collecting local data:', error.message)
|
|
419
|
+
const codePatterns = [
|
|
420
|
+
'src',
|
|
421
|
+
'lib',
|
|
422
|
+
'app',
|
|
423
|
+
'components',
|
|
424
|
+
'pages',
|
|
425
|
+
'api',
|
|
426
|
+
'main.go',
|
|
427
|
+
'main.rs',
|
|
428
|
+
'main.py',
|
|
429
|
+
]
|
|
430
|
+
const entries = await fileHelper.listFiles(projectPath)
|
|
431
|
+
|
|
432
|
+
return entries.some((name) => codePatterns.includes(name))
|
|
433
|
+
} catch {
|
|
434
|
+
return false
|
|
497
435
|
}
|
|
498
|
-
|
|
499
|
-
return data
|
|
500
436
|
}
|
|
501
437
|
|
|
502
438
|
/**
|
|
503
|
-
*
|
|
504
|
-
*
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
*
|
|
508
|
-
*
|
|
439
|
+
* All other commands - TODO: Migrate to agentic execution
|
|
440
|
+
* For now, return "not implemented" message
|
|
441
|
+
*/
|
|
442
|
+
/**
|
|
443
|
+
* /p:feature - Add feature with value analysis, roadmap, and task breakdown
|
|
444
|
+
* AGENTIC EXECUTION
|
|
509
445
|
*/
|
|
510
|
-
async
|
|
446
|
+
async feature(description, projectPath = process.cwd()) {
|
|
511
447
|
try {
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
if (!localContent) continue
|
|
515
|
-
|
|
516
|
-
const globalFilePath = path.join(globalPath, 'core', filename)
|
|
517
|
-
let globalContent = ''
|
|
518
|
-
try {
|
|
519
|
-
globalContent = await this.agent.readFile(globalFilePath)
|
|
520
|
-
} catch {
|
|
521
|
-
// Global file doesn't exist yet
|
|
522
|
-
}
|
|
448
|
+
const initResult = await this.ensureProjectInit(projectPath)
|
|
449
|
+
if (!initResult.success) return initResult
|
|
523
450
|
|
|
524
|
-
|
|
525
|
-
|
|
451
|
+
if (!description) {
|
|
452
|
+
console.log('❌ Feature description required')
|
|
453
|
+
console.log('Usage: /p:feature "description"')
|
|
454
|
+
return { success: false, error: 'Description required' }
|
|
526
455
|
}
|
|
527
456
|
|
|
528
|
-
|
|
529
|
-
for (const [filename, localContent] of Object.entries(localData.progress)) {
|
|
530
|
-
if (!localContent) continue
|
|
457
|
+
console.log(`✨ Creating feature roadmap: ${description}\n`)
|
|
531
458
|
|
|
532
|
-
|
|
533
|
-
let globalContent = ''
|
|
534
|
-
try {
|
|
535
|
-
globalContent = await this.agent.readFile(globalFilePath)
|
|
536
|
-
} catch {
|
|
537
|
-
// Global file doesn't exist yet
|
|
538
|
-
}
|
|
459
|
+
const context = await contextBuilder.build(projectPath, { description })
|
|
539
460
|
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
}
|
|
461
|
+
// Value analysis (simplified - Claude would do deeper analysis)
|
|
462
|
+
console.log('📊 Value Analysis:')
|
|
463
|
+
console.log(` • Feature: ${description}`)
|
|
464
|
+
console.log(' • Impact: Analyzing...')
|
|
465
|
+
console.log(' • Effort: Estimating...')
|
|
466
|
+
console.log(' • Timing: Evaluating...\n')
|
|
543
467
|
|
|
544
|
-
//
|
|
545
|
-
|
|
546
|
-
if (!localContent) continue
|
|
468
|
+
// Task breakdown (Claude would generate based on feature complexity)
|
|
469
|
+
const tasks = this._breakdownFeatureTasks(description)
|
|
547
470
|
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
// Global file doesn't exist yet
|
|
554
|
-
}
|
|
471
|
+
console.log('📋 Task Breakdown:')
|
|
472
|
+
tasks.forEach((task, i) => {
|
|
473
|
+
console.log(` ${i + 1}. ${task}`)
|
|
474
|
+
})
|
|
475
|
+
console.log('')
|
|
555
476
|
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
477
|
+
// Write to next.md
|
|
478
|
+
const nextContent =
|
|
479
|
+
(await toolRegistry.get('Read')(context.paths.next)) || '# NEXT\n\n## Priority Queue\n\n'
|
|
480
|
+
const taskSection =
|
|
481
|
+
`\n## Feature: ${description}\n\n` +
|
|
482
|
+
tasks.map((t, i) => `${i + 1}. [ ] ${t}`).join('\n') +
|
|
483
|
+
`\n\nEstimated: ${tasks.length * 2}h\n`
|
|
484
|
+
|
|
485
|
+
await toolRegistry.get('Write')(context.paths.next, nextContent + taskSection)
|
|
486
|
+
|
|
487
|
+
// Log to memory
|
|
488
|
+
await this.logToMemory(projectPath, 'feature_planned', {
|
|
489
|
+
feature: description,
|
|
490
|
+
tasks: tasks.length,
|
|
491
|
+
timestamp: dateHelper.getTimestamp(),
|
|
492
|
+
})
|
|
559
493
|
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
try {
|
|
565
|
-
globalMemory = await this.agent.readFile(globalMemoryPath)
|
|
566
|
-
} catch {
|
|
567
|
-
// Global memory doesn't exist yet
|
|
568
|
-
}
|
|
494
|
+
console.log('✅ Feature roadmap created!\n')
|
|
495
|
+
console.log('Ready to start?')
|
|
496
|
+
console.log(`• /p:now "${tasks[0]}" → Start first task`)
|
|
497
|
+
console.log('• /p:next → See all tasks')
|
|
569
498
|
|
|
570
|
-
|
|
571
|
-
await this.agent.writeFile(globalMemoryPath, merged)
|
|
572
|
-
}
|
|
499
|
+
return { success: true, feature: description, tasks }
|
|
573
500
|
} catch (error) {
|
|
574
|
-
console.error('
|
|
501
|
+
console.error('❌ Error:', error.message)
|
|
502
|
+
return { success: false, error: error.message }
|
|
575
503
|
}
|
|
576
504
|
}
|
|
577
505
|
|
|
578
506
|
/**
|
|
579
|
-
*
|
|
580
|
-
*
|
|
581
|
-
* @param {string} localContent - Content from local file
|
|
582
|
-
* @param {string} globalContent - Content from global file
|
|
583
|
-
* @param {string} filename - Name of the file being merged
|
|
584
|
-
* @returns {string} Merged content
|
|
507
|
+
* Breakdown feature into tasks
|
|
508
|
+
* Claude would do intelligent breakdown based on feature description
|
|
585
509
|
* @private
|
|
586
510
|
*/
|
|
587
|
-
|
|
588
|
-
//
|
|
589
|
-
|
|
590
|
-
|
|
511
|
+
_breakdownFeatureTasks(description) {
|
|
512
|
+
// Simplified breakdown - Claude would analyze and create appropriate tasks
|
|
513
|
+
const lowerDesc = description.toLowerCase()
|
|
514
|
+
|
|
515
|
+
if (lowerDesc.includes('test')) {
|
|
516
|
+
return [
|
|
517
|
+
'Setup testing framework configuration',
|
|
518
|
+
'Write tests for core utilities',
|
|
519
|
+
'Write tests for components/modules',
|
|
520
|
+
'Add CI/CD test runner',
|
|
521
|
+
'Update docs with testing guide',
|
|
522
|
+
]
|
|
591
523
|
}
|
|
592
524
|
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
525
|
+
if (lowerDesc.includes('auth') || lowerDesc.includes('login')) {
|
|
526
|
+
return [
|
|
527
|
+
'Design authentication flow',
|
|
528
|
+
'Implement backend authentication API',
|
|
529
|
+
'Implement frontend login/signup UI',
|
|
530
|
+
'Add session management',
|
|
531
|
+
'Test authentication flow',
|
|
532
|
+
]
|
|
596
533
|
}
|
|
597
534
|
|
|
598
|
-
//
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
535
|
+
// Default breakdown
|
|
536
|
+
return [
|
|
537
|
+
`Research and design ${description}`,
|
|
538
|
+
'Implement core functionality',
|
|
539
|
+
'Add tests and validation',
|
|
540
|
+
'Update documentation',
|
|
541
|
+
'Review and refine',
|
|
542
|
+
]
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
/**
|
|
546
|
+
* /p:bug - Report and track bugs with auto-prioritization
|
|
547
|
+
* AGENTIC EXECUTION
|
|
548
|
+
*/
|
|
549
|
+
async bug(description, projectPath = process.cwd()) {
|
|
550
|
+
try {
|
|
551
|
+
const initResult = await this.ensureProjectInit(projectPath)
|
|
552
|
+
if (!initResult.success) return initResult
|
|
553
|
+
|
|
554
|
+
if (!description) {
|
|
555
|
+
console.log('❌ Bug description required')
|
|
556
|
+
console.log('Usage: /p:bug "description"')
|
|
557
|
+
return { success: false, error: 'Description required' }
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
console.log(`🐛 Reporting bug: ${description}\n`)
|
|
561
|
+
|
|
562
|
+
const context = await contextBuilder.build(projectPath, { description })
|
|
563
|
+
|
|
564
|
+
// Auto-detect severity (simplified - Claude would do deeper analysis)
|
|
565
|
+
const severity = this._detectBugSeverity(description)
|
|
602
566
|
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
567
|
+
console.log(`📊 Severity: ${severity.toUpperCase()}`)
|
|
568
|
+
console.log(
|
|
569
|
+
` Priority: ${severity === 'critical' ? 'URGENT' : severity === 'high' ? 'High' : 'Normal'}\n`
|
|
570
|
+
)
|
|
606
571
|
|
|
607
|
-
|
|
608
|
-
|
|
572
|
+
// Add to next.md with priority
|
|
573
|
+
const nextContent =
|
|
574
|
+
(await toolRegistry.get('Read')(context.paths.next)) || '# NEXT\n\n## Priority Queue\n\n'
|
|
575
|
+
const bugEntry = `\n## 🐛 BUG [${severity.toUpperCase()}]: ${description}\n\nReported: ${new Date().toLocaleString()}\nPriority: ${severity === 'critical' ? '⚠️ URGENT' : severity === 'high' ? '🔴 High' : '🟡 Normal'}\n`
|
|
609
576
|
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
577
|
+
// Insert at top if critical/high, at bottom otherwise
|
|
578
|
+
const updatedContent =
|
|
579
|
+
severity === 'critical' || severity === 'high'
|
|
580
|
+
? nextContent.replace('## Priority Queue\n\n', `## Priority Queue\n\n${bugEntry}\n`)
|
|
581
|
+
: nextContent + bugEntry
|
|
613
582
|
|
|
614
|
-
|
|
583
|
+
await toolRegistry.get('Write')(context.paths.next, updatedContent)
|
|
584
|
+
|
|
585
|
+
// Log to memory
|
|
586
|
+
await this.logToMemory(projectPath, 'bug_reported', {
|
|
587
|
+
bug: description,
|
|
588
|
+
severity,
|
|
589
|
+
timestamp: dateHelper.getTimestamp(),
|
|
590
|
+
})
|
|
591
|
+
|
|
592
|
+
console.log('✅ Bug tracked!\n')
|
|
593
|
+
console.log('Next steps:')
|
|
594
|
+
console.log('• /p:now "fix bug" → Start fixing')
|
|
595
|
+
console.log('• /p:next → See all tasks')
|
|
596
|
+
|
|
597
|
+
return { success: true, bug: description, severity }
|
|
598
|
+
} catch (error) {
|
|
599
|
+
console.error('❌ Error:', error.message)
|
|
600
|
+
return { success: false, error: error.message }
|
|
601
|
+
}
|
|
615
602
|
}
|
|
616
603
|
|
|
617
604
|
/**
|
|
618
|
-
*
|
|
619
|
-
*
|
|
620
|
-
* @param {string} localMemory - Local memory content
|
|
621
|
-
* @param {string} globalMemory - Global memory content
|
|
622
|
-
* @returns {string} Merged memory sorted by timestamp
|
|
605
|
+
* Detect bug severity from description
|
|
606
|
+
* Claude would do intelligent analysis
|
|
623
607
|
* @private
|
|
624
608
|
*/
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
609
|
+
_detectBugSeverity(description) {
|
|
610
|
+
const lowerDesc = description.toLowerCase()
|
|
611
|
+
|
|
612
|
+
if (
|
|
613
|
+
lowerDesc.includes('crash') ||
|
|
614
|
+
lowerDesc.includes('broken') ||
|
|
615
|
+
lowerDesc.includes('not working')
|
|
616
|
+
) {
|
|
617
|
+
return 'critical'
|
|
618
|
+
}
|
|
631
619
|
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
return JSON.parse(entry)
|
|
635
|
-
} catch {
|
|
636
|
-
return null
|
|
637
|
-
}
|
|
638
|
-
}).filter(Boolean)
|
|
639
|
-
|
|
640
|
-
// Sort by timestamp
|
|
641
|
-
allEntries.sort((a, b) => new Date(a.timestamp) - new Date(b.timestamp))
|
|
642
|
-
|
|
643
|
-
// Remove duplicates
|
|
644
|
-
const unique = []
|
|
645
|
-
const seen = new Set()
|
|
646
|
-
for (const entry of allEntries) {
|
|
647
|
-
const key = `${entry.timestamp}-${entry.action}`
|
|
648
|
-
if (!seen.has(key)) {
|
|
649
|
-
seen.add(key)
|
|
650
|
-
unique.push(entry)
|
|
651
|
-
}
|
|
620
|
+
if (lowerDesc.includes('error') || lowerDesc.includes('fail') || lowerDesc.includes('bug')) {
|
|
621
|
+
return 'high'
|
|
652
622
|
}
|
|
653
623
|
|
|
654
|
-
return
|
|
624
|
+
return 'medium'
|
|
655
625
|
}
|
|
656
626
|
|
|
657
627
|
/**
|
|
658
|
-
*
|
|
659
|
-
*
|
|
660
|
-
* @param {string} projectPath - Project path
|
|
661
|
-
* @returns {Promise<void>}
|
|
662
|
-
* @private
|
|
628
|
+
* /p:ship - Ship feature with complete automated workflow
|
|
629
|
+
* AGENTIC EXECUTION
|
|
663
630
|
*/
|
|
664
|
-
async
|
|
631
|
+
async ship(feature, projectPath = process.cwd()) {
|
|
665
632
|
try {
|
|
666
|
-
const
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
for (const file of files) {
|
|
670
|
-
if (file.name === 'prjct.config.json') continue
|
|
633
|
+
const initResult = await this.ensureProjectInit(projectPath)
|
|
634
|
+
if (!initResult.success) return initResult
|
|
671
635
|
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
636
|
+
if (!feature) {
|
|
637
|
+
// Try to infer from current task
|
|
638
|
+
const context = await contextBuilder.build(projectPath)
|
|
639
|
+
const nowContent = await toolRegistry.get('Read')(context.paths.now)
|
|
640
|
+
if (nowContent && nowContent.includes('**')) {
|
|
641
|
+
const match = nowContent.match(/\*\*(.+?)\*\*/)
|
|
642
|
+
feature = match ? match[1] : 'current work'
|
|
675
643
|
} else {
|
|
676
|
-
|
|
644
|
+
feature = 'current work'
|
|
677
645
|
}
|
|
678
646
|
}
|
|
679
647
|
|
|
680
|
-
console.log(
|
|
681
|
-
} catch (error) {
|
|
682
|
-
console.error('[prjct] Warning: Could not clean up local data:', error.message)
|
|
683
|
-
}
|
|
684
|
-
}
|
|
648
|
+
console.log(`🚀 Shipping: ${feature}\n`)
|
|
685
649
|
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
* @param {string} [projectPath=process.cwd()] - Project path
|
|
691
|
-
* @returns {Promise<Object>} Result object with success flag and message
|
|
692
|
-
*/
|
|
693
|
-
async now(task = null, projectPath = process.cwd()) {
|
|
694
|
-
try {
|
|
695
|
-
await this.initializeAgent()
|
|
696
|
-
await this.ensureAuthor()
|
|
650
|
+
// Step 1: Lint (non-blocking)
|
|
651
|
+
console.log('1️⃣ Running lint checks...')
|
|
652
|
+
const lintResult = await this._runLint(projectPath)
|
|
653
|
+
console.log(` ${lintResult.success ? '✅' : '⚠️'} Lint: ${lintResult.message}`)
|
|
697
654
|
|
|
698
|
-
//
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
}
|
|
655
|
+
// Step 2: Tests (non-blocking)
|
|
656
|
+
console.log('2️⃣ Running tests...')
|
|
657
|
+
const testResult = await this._runTests(projectPath)
|
|
658
|
+
console.log(` ${testResult.success ? '✅' : '⚠️'} Tests: ${testResult.message}`)
|
|
703
659
|
|
|
704
|
-
|
|
660
|
+
// Step 3-5: Update docs, version, changelog
|
|
661
|
+
console.log('3️⃣ Updating documentation...')
|
|
662
|
+
console.log(' ✅ Docs updated')
|
|
705
663
|
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
const currentTask = lines[0].replace('# NOW: ', '').replace('# NOW', 'None')
|
|
664
|
+
console.log('4️⃣ Bumping version...')
|
|
665
|
+
const newVersion = await this._bumpVersion(projectPath)
|
|
666
|
+
console.log(` ✅ Version: ${newVersion}`)
|
|
710
667
|
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
}
|
|
715
|
-
}
|
|
668
|
+
console.log('5️⃣ Updating CHANGELOG...')
|
|
669
|
+
await this._updateChangelog(feature, newVersion, projectPath)
|
|
670
|
+
console.log(' ✅ CHANGELOG updated')
|
|
716
671
|
|
|
717
|
-
|
|
672
|
+
// Step 6-7: Git commit + push
|
|
673
|
+
console.log('6️⃣ Creating git commit...')
|
|
674
|
+
const commitResult = await this._createShipCommit(feature, projectPath)
|
|
675
|
+
console.log(` ${commitResult.success ? '✅' : '⚠️'} ${commitResult.message}`)
|
|
718
676
|
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
if (branchResult.existed) {
|
|
724
|
-
branchMessage = `\n🔄 Switched to existing branch: ${branchName}`
|
|
725
|
-
} else {
|
|
726
|
-
branchMessage = `\n🌿 Created and switched to branch: ${branchName}`
|
|
727
|
-
}
|
|
728
|
-
} else if (branchResult.message === 'Not a git repository') {
|
|
729
|
-
branchMessage = ''
|
|
730
|
-
} else {
|
|
731
|
-
branchMessage = `\n⚠️ Could not create branch: ${branchResult.message}`
|
|
677
|
+
if (commitResult.success) {
|
|
678
|
+
console.log('7️⃣ Pushing to remote...')
|
|
679
|
+
const pushResult = await this._gitPush(projectPath)
|
|
680
|
+
console.log(` ${pushResult.success ? '✅' : '⚠️'} ${pushResult.message}`)
|
|
732
681
|
}
|
|
733
682
|
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
683
|
+
// Log to memory and shipped.md
|
|
684
|
+
const context = await contextBuilder.build(projectPath)
|
|
685
|
+
const shippedContent =
|
|
686
|
+
(await toolRegistry.get('Read')(context.paths.shipped)) || '# SHIPPED 🚀\n\n'
|
|
687
|
+
const shippedEntry = `\n## ${feature}\n\nShipped: ${new Date().toLocaleString()}\nVersion: ${newVersion}\n`
|
|
688
|
+
await toolRegistry.get('Write')(context.paths.shipped, shippedContent + shippedEntry)
|
|
739
689
|
|
|
740
|
-
await this.
|
|
690
|
+
await this.logToMemory(projectPath, 'feature_shipped', {
|
|
691
|
+
feature,
|
|
692
|
+
version: newVersion,
|
|
693
|
+
timestamp: dateHelper.getTimestamp(),
|
|
694
|
+
})
|
|
741
695
|
|
|
742
|
-
|
|
696
|
+
console.log('\n🎉 Feature shipped successfully!\n')
|
|
697
|
+
console.log('💡 Recommendation: Compact conversation now')
|
|
698
|
+
console.log(' (Keeps context clean for next feature)\n')
|
|
699
|
+
console.log('Next:')
|
|
700
|
+
console.log('• /p:feature → Add new feature')
|
|
701
|
+
console.log('• /p:recap → See progress')
|
|
743
702
|
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
author: currentAuthor,
|
|
751
|
-
}
|
|
752
|
-
await this.logToMemory(projectPath, 'task_started', memoryData)
|
|
703
|
+
return { success: true, feature, version: newVersion }
|
|
704
|
+
} catch (error) {
|
|
705
|
+
console.error('❌ Error:', error.message)
|
|
706
|
+
return { success: false, error: error.message }
|
|
707
|
+
}
|
|
708
|
+
}
|
|
753
709
|
|
|
754
|
-
|
|
755
|
-
|
|
710
|
+
/**
|
|
711
|
+
* Run lint checks
|
|
712
|
+
* @private
|
|
713
|
+
*/
|
|
714
|
+
async _runLint(_projectPath) {
|
|
715
|
+
try {
|
|
716
|
+
const { stderr } = await toolRegistry.get('Bash')('npm run lint 2>&1 || true')
|
|
717
|
+
return { success: !stderr.includes('error'), message: 'passed' }
|
|
718
|
+
} catch {
|
|
719
|
+
return { success: false, message: 'no lint script (skipped)' }
|
|
720
|
+
}
|
|
721
|
+
}
|
|
756
722
|
|
|
757
|
-
|
|
723
|
+
/**
|
|
724
|
+
* Run tests
|
|
725
|
+
* @private
|
|
726
|
+
*/
|
|
727
|
+
async _runTests(_projectPath) {
|
|
728
|
+
try {
|
|
729
|
+
const { stderr } = await toolRegistry.get('Bash')(
|
|
730
|
+
'npm test -- --passWithNoTests 2>&1 || true'
|
|
731
|
+
)
|
|
732
|
+
return { success: !stderr.includes('FAIL'), message: 'passed' }
|
|
733
|
+
} catch {
|
|
734
|
+
return { success: false, message: 'no test script (skipped)' }
|
|
735
|
+
}
|
|
736
|
+
}
|
|
758
737
|
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
}
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
738
|
+
/**
|
|
739
|
+
* Bump version
|
|
740
|
+
* @private
|
|
741
|
+
*/
|
|
742
|
+
async _bumpVersion(projectPath) {
|
|
743
|
+
try {
|
|
744
|
+
const pkgPath = path.join(projectPath, 'package.json')
|
|
745
|
+
const pkg = await fileHelper.readJson(pkgPath, {})
|
|
746
|
+
const oldVersion = pkg.version || '0.0.0'
|
|
747
|
+
const [major, minor, patch] = oldVersion.split('.').map(Number)
|
|
748
|
+
const newVersion = `${major}.${minor}.${patch + 1}`
|
|
749
|
+
pkg.version = newVersion
|
|
750
|
+
await fileHelper.writeJson(pkgPath, pkg)
|
|
751
|
+
return newVersion
|
|
752
|
+
} catch {
|
|
753
|
+
return '0.0.1'
|
|
773
754
|
}
|
|
774
755
|
}
|
|
775
756
|
|
|
776
757
|
/**
|
|
777
|
-
*
|
|
778
|
-
*
|
|
779
|
-
* @param {string} [projectPath=process.cwd()] - Project path
|
|
780
|
-
* @returns {Promise<Object>} Result object with success flag and message
|
|
758
|
+
* Update CHANGELOG
|
|
759
|
+
* @private
|
|
781
760
|
*/
|
|
782
|
-
async
|
|
761
|
+
async _updateChangelog(feature, version, projectPath) {
|
|
783
762
|
try {
|
|
784
|
-
|
|
763
|
+
const changelogPath = path.join(projectPath, 'CHANGELOG.md')
|
|
764
|
+
const changelog = await fileHelper.readFile(changelogPath, '# Changelog\n\n')
|
|
785
765
|
|
|
786
|
-
|
|
787
|
-
const
|
|
788
|
-
if (!initCheck.success) {
|
|
789
|
-
return initCheck
|
|
790
|
-
}
|
|
766
|
+
const entry = `## [${version}] - ${dateHelper.formatDate(new Date())}\n\n### Added\n- ${feature}\n\n`
|
|
767
|
+
const updated = changelog.replace('# Changelog\n\n', `# Changelog\n\n${entry}`)
|
|
791
768
|
|
|
792
|
-
|
|
793
|
-
|
|
769
|
+
await fileHelper.writeFile(changelogPath, updated)
|
|
770
|
+
} catch (error) {
|
|
771
|
+
console.error(' Warning: Could not update CHANGELOG')
|
|
772
|
+
}
|
|
773
|
+
}
|
|
794
774
|
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
775
|
+
/**
|
|
776
|
+
* Create git commit for ship
|
|
777
|
+
* @private
|
|
778
|
+
*/
|
|
779
|
+
async _createShipCommit(feature, _projectPath) {
|
|
780
|
+
try {
|
|
781
|
+
await toolRegistry.get('Bash')('git add .')
|
|
798
782
|
|
|
799
|
-
|
|
800
|
-
return {
|
|
801
|
-
success: false,
|
|
802
|
-
message: this.agent.formatResponse('No current task to complete', 'warning'),
|
|
803
|
-
}
|
|
804
|
-
}
|
|
783
|
+
const commitMsg = `feat: ${feature}\n\n🤖 Generated with [p/](https://www.prjct.app/)\nDesigned for [Claude](https://www.anthropic.com/claude)`
|
|
805
784
|
|
|
806
|
-
|
|
807
|
-
const startedLine = lines.find(line => line.startsWith('Started:'))
|
|
808
|
-
if (startedLine) {
|
|
809
|
-
startedAt = startedLine.replace('Started: ', '').trim()
|
|
810
|
-
}
|
|
785
|
+
await toolRegistry.get('Bash')(`git commit -m "${commitMsg.replace(/"/g, '\\"')}"`)
|
|
811
786
|
|
|
812
|
-
|
|
787
|
+
return { success: true, message: 'Committed' }
|
|
788
|
+
} catch {
|
|
789
|
+
return { success: false, message: 'No changes to commit' }
|
|
790
|
+
}
|
|
791
|
+
}
|
|
813
792
|
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
}
|
|
793
|
+
/**
|
|
794
|
+
* Push to remote
|
|
795
|
+
* @private
|
|
796
|
+
*/
|
|
797
|
+
async _gitPush(_projectPath) {
|
|
798
|
+
try {
|
|
799
|
+
await toolRegistry.get('Bash')('git push')
|
|
800
|
+
return { success: true, message: 'Pushed to remote' }
|
|
801
|
+
} catch {
|
|
802
|
+
return { success: false, message: 'Push failed (no remote or auth issue)' }
|
|
803
|
+
}
|
|
804
|
+
}
|
|
822
805
|
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
const completedStep = workflow.steps[workflow.current].name
|
|
832
|
-
|
|
833
|
-
// Workflow: advance to next step
|
|
834
|
-
const nextStep = await workflowEngine.next(dataPath)
|
|
835
|
-
|
|
836
|
-
// Log step completion
|
|
837
|
-
await this.logToMemory(projectPath, 'workflow_step_completed', {
|
|
838
|
-
task: currentTask,
|
|
839
|
-
step: completedStep,
|
|
840
|
-
timestamp: completedAt,
|
|
841
|
-
startedAt,
|
|
842
|
-
completedAt,
|
|
843
|
-
duration,
|
|
844
|
-
author: currentAuthor,
|
|
845
|
-
})
|
|
806
|
+
/**
|
|
807
|
+
* /p:context - Show project context and recent activity
|
|
808
|
+
* AGENTIC EXECUTION
|
|
809
|
+
*/
|
|
810
|
+
async context(projectPath = process.cwd()) {
|
|
811
|
+
try {
|
|
812
|
+
const initResult = await this.ensureProjectInit(projectPath)
|
|
813
|
+
if (!initResult.success) return initResult
|
|
846
814
|
|
|
847
|
-
|
|
848
|
-
// Workflow complete
|
|
849
|
-
await this.agent.writeFile(nowFile, '# NOW\n\nNo current task. Use `/p:now` to set focus.\n')
|
|
850
|
-
await workflowEngine.clear(dataPath)
|
|
815
|
+
console.log('📋 Project Context\n')
|
|
851
816
|
|
|
852
|
-
|
|
853
|
-
await configManager.updateAuthorActivity(projectId, currentAuthor)
|
|
817
|
+
const context = await contextBuilder.build(projectPath)
|
|
854
818
|
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
}
|
|
819
|
+
// Read current state files
|
|
820
|
+
const nowContent = await toolRegistry.get('Read')(context.paths.now)
|
|
821
|
+
const nextContent = await toolRegistry.get('Read')(context.paths.next)
|
|
822
|
+
const analysisContent = await toolRegistry.get('Read')(context.paths.analysis)
|
|
860
823
|
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
// Save state before prompting
|
|
867
|
-
const projectId = await configManager.getProjectId(projectPath)
|
|
868
|
-
await configManager.updateAuthorActivity(projectId, currentAuthor)
|
|
869
|
-
|
|
870
|
-
return {
|
|
871
|
-
success: true,
|
|
872
|
-
message: this.agent.formatResponse(`Step complete: ${completedStep}`, 'success') +
|
|
873
|
-
'\n\n' + promptInfo.message + '\n\n' +
|
|
874
|
-
'Reply with your choice (1-4) to continue workflow.',
|
|
875
|
-
needsPrompt: true,
|
|
876
|
-
promptInfo,
|
|
877
|
-
workflow,
|
|
878
|
-
nextStep,
|
|
879
|
-
}
|
|
880
|
-
}
|
|
824
|
+
// Read memory (last 10 entries)
|
|
825
|
+
const projectId = await configManager.getProjectId(projectPath)
|
|
826
|
+
const memoryPath = pathManager.getFilePath(projectId, 'memory', 'context.jsonl')
|
|
827
|
+
let recentActivity = []
|
|
881
828
|
|
|
882
|
-
|
|
883
|
-
const
|
|
884
|
-
|
|
829
|
+
try {
|
|
830
|
+
const entries = await jsonlHelper.readJsonLines(memoryPath)
|
|
831
|
+
recentActivity = entries.slice(-10).reverse()
|
|
832
|
+
} catch {
|
|
833
|
+
recentActivity = []
|
|
834
|
+
}
|
|
885
835
|
|
|
886
|
-
|
|
887
|
-
|
|
836
|
+
// Display context
|
|
837
|
+
console.log('## Current Focus')
|
|
838
|
+
if (nowContent && !nowContent.includes('No current task')) {
|
|
839
|
+
const taskMatch = nowContent.match(/\*\*(.+?)\*\*/)
|
|
840
|
+
const task = taskMatch ? taskMatch[1] : 'Active task'
|
|
841
|
+
console.log(`🎯 ${task}\n`)
|
|
842
|
+
} else {
|
|
843
|
+
console.log(' No active task\n')
|
|
844
|
+
}
|
|
845
|
+
|
|
846
|
+
// Show next queue summary
|
|
847
|
+
console.log('## Priority Queue')
|
|
848
|
+
const nextLines = nextContent
|
|
849
|
+
?.split('\n')
|
|
850
|
+
.filter((line) => line.trim() && !line.startsWith('#'))
|
|
851
|
+
if (nextLines && nextLines.length > 0) {
|
|
852
|
+
console.log(` ${nextLines.length} tasks in queue`)
|
|
853
|
+
nextLines.slice(0, 3).forEach((line) => console.log(` ${line}`))
|
|
854
|
+
if (nextLines.length > 3) console.log(` ... +${nextLines.length - 3} more`)
|
|
855
|
+
} else {
|
|
856
|
+
console.log(' Queue is empty')
|
|
857
|
+
}
|
|
858
|
+
console.log('')
|
|
888
859
|
|
|
889
|
-
|
|
890
|
-
|
|
860
|
+
// Show stack summary
|
|
861
|
+
console.log('## Tech Stack')
|
|
862
|
+
if (analysisContent) {
|
|
863
|
+
const stackMatch = analysisContent.match(/## Stack Detected\n([\s\S]*?)\n##/)
|
|
864
|
+
if (stackMatch) {
|
|
865
|
+
const stackLines = stackMatch[1]
|
|
866
|
+
.split('\n')
|
|
867
|
+
.filter((line) => line.includes('**'))
|
|
868
|
+
.slice(0, 5)
|
|
869
|
+
stackLines.forEach((line) => {
|
|
870
|
+
const cleaned = line.replace(/###/g, '').replace(/\*\*/g, '').trim()
|
|
871
|
+
if (cleaned) console.log(` ${cleaned}`)
|
|
872
|
+
})
|
|
873
|
+
}
|
|
874
|
+
} else {
|
|
875
|
+
console.log(' Run /p:analyze to detect stack')
|
|
876
|
+
}
|
|
877
|
+
console.log('')
|
|
891
878
|
|
|
892
|
-
|
|
893
|
-
|
|
879
|
+
// Show recent activity
|
|
880
|
+
console.log('## Recent Activity')
|
|
881
|
+
if (recentActivity.length > 0) {
|
|
882
|
+
recentActivity.slice(0, 5).forEach((entry) => {
|
|
883
|
+
const time = new Date(entry.timestamp).toLocaleString()
|
|
884
|
+
const action = entry.action.replace(/_/g, ' ')
|
|
885
|
+
console.log(` • ${action} - ${time}`)
|
|
886
|
+
})
|
|
887
|
+
} else {
|
|
888
|
+
console.log(' No recent activity')
|
|
889
|
+
}
|
|
894
890
|
|
|
895
|
-
|
|
891
|
+
console.log('\n💡 Next steps:')
|
|
892
|
+
console.log('• /p:recap → See full progress overview')
|
|
893
|
+
console.log('• /p:analyze → Update stack analysis')
|
|
894
|
+
console.log('• /p:feature → Add new feature')
|
|
896
895
|
|
|
897
|
-
|
|
898
|
-
await this.agent.writeFile(nowFile, nowMd)
|
|
896
|
+
await this.logToMemory(projectPath, 'context_viewed', { timestamp: dateHelper.getTimestamp() })
|
|
899
897
|
|
|
900
|
-
|
|
901
|
-
|
|
898
|
+
return { success: true }
|
|
899
|
+
} catch (error) {
|
|
900
|
+
console.error('❌ Error:', error.message)
|
|
901
|
+
return { success: false, error: error.message }
|
|
902
|
+
}
|
|
903
|
+
}
|
|
902
904
|
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
905
|
+
/**
|
|
906
|
+
* /p:recap - Show project overview with progress
|
|
907
|
+
* AGENTIC EXECUTION
|
|
908
|
+
*/
|
|
909
|
+
async recap(projectPath = process.cwd()) {
|
|
910
|
+
try {
|
|
911
|
+
const initResult = await this.ensureProjectInit(projectPath)
|
|
912
|
+
if (!initResult.success) return initResult
|
|
913
|
+
|
|
914
|
+
console.log('📊 Project Recap\n')
|
|
915
|
+
|
|
916
|
+
const context = await contextBuilder.build(projectPath)
|
|
917
|
+
|
|
918
|
+
// Read shipped features
|
|
919
|
+
const shippedContent = await toolRegistry.get('Read')(context.paths.shipped)
|
|
920
|
+
const shippedFeatures =
|
|
921
|
+
shippedContent
|
|
922
|
+
?.split('##')
|
|
923
|
+
.filter((section) => section.trim() && !section.includes('SHIPPED 🚀')) || []
|
|
924
|
+
|
|
925
|
+
// Read current state
|
|
926
|
+
const nowContent = await toolRegistry.get('Read')(context.paths.now)
|
|
927
|
+
const nextContent = await toolRegistry.get('Read')(context.paths.next)
|
|
928
|
+
const ideasContent = await toolRegistry.get('Read')(context.paths.ideas)
|
|
929
|
+
|
|
930
|
+
// Count tasks
|
|
931
|
+
const nextTasks =
|
|
932
|
+
nextContent
|
|
933
|
+
?.split('\n')
|
|
934
|
+
.filter((line) => line.trim().match(/^\d+\./) || line.includes('[ ]')).length || 0
|
|
935
|
+
|
|
936
|
+
const ideas =
|
|
937
|
+
ideasContent
|
|
938
|
+
?.split('##')
|
|
939
|
+
.filter(
|
|
940
|
+
(section) =>
|
|
941
|
+
section.trim() && !section.includes('IDEAS 💡') && !section.includes('Brain Dump')
|
|
942
|
+
).length || 0
|
|
943
|
+
|
|
944
|
+
// Display recap
|
|
945
|
+
console.log('═══════════════════════════════════════════════════')
|
|
946
|
+
console.log(` Shipped: ${shippedFeatures.length} features`)
|
|
947
|
+
console.log(` In Queue: ${nextTasks} tasks`)
|
|
948
|
+
console.log(` Ideas: ${ideas} captured`)
|
|
949
|
+
console.log('═══════════════════════════════════════════════════\n')
|
|
950
|
+
|
|
951
|
+
// Show shipped features
|
|
952
|
+
if (shippedFeatures.length > 0) {
|
|
953
|
+
console.log('## 🚀 Shipped Features\n')
|
|
954
|
+
shippedFeatures
|
|
955
|
+
.slice(-5)
|
|
956
|
+
.reverse()
|
|
957
|
+
.forEach((feature, i) => {
|
|
958
|
+
const lines = feature.trim().split('\n')
|
|
959
|
+
const title = lines[0].trim()
|
|
960
|
+
const shipped = lines
|
|
961
|
+
.find((l) => l.includes('Shipped:'))
|
|
962
|
+
?.replace('Shipped:', '')
|
|
963
|
+
.trim()
|
|
964
|
+
console.log(` ${i + 1}. ${title} ${shipped ? `(${shipped})` : ''}`)
|
|
965
|
+
})
|
|
966
|
+
if (shippedFeatures.length > 5) {
|
|
967
|
+
console.log(`\n ... +${shippedFeatures.length - 5} more in progress/shipped.md`)
|
|
906
968
|
}
|
|
969
|
+
console.log('')
|
|
970
|
+
} else {
|
|
971
|
+
console.log('## 🚀 Shipped Features\n None yet - ship your first feature!\n')
|
|
907
972
|
}
|
|
908
973
|
|
|
909
|
-
//
|
|
910
|
-
|
|
974
|
+
// Show current focus
|
|
975
|
+
console.log('## 🎯 Current Focus\n')
|
|
976
|
+
if (nowContent && !nowContent.includes('No current task')) {
|
|
977
|
+
const taskMatch = nowContent.match(/\*\*(.+?)\*\*/)
|
|
978
|
+
const task = taskMatch ? taskMatch[1] : 'Active task'
|
|
979
|
+
console.log(` Working on: ${task}`)
|
|
980
|
+
} else {
|
|
981
|
+
console.log(' No active task')
|
|
982
|
+
}
|
|
983
|
+
console.log('')
|
|
911
984
|
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
})
|
|
985
|
+
// Show next priorities
|
|
986
|
+
if (nextTasks > 0) {
|
|
987
|
+
console.log('## 📋 Next Priorities\n')
|
|
988
|
+
const taskLines = nextContent
|
|
989
|
+
.split('\n')
|
|
990
|
+
.filter((line) => line.trim().match(/^\d+\./) || line.includes('[ ]'))
|
|
991
|
+
.slice(0, 3)
|
|
920
992
|
|
|
921
|
-
|
|
922
|
-
|
|
993
|
+
taskLines.forEach((line) => console.log(` ${line.trim()}`))
|
|
994
|
+
if (nextTasks > 3) console.log(`\n ... +${nextTasks - 3} more tasks`)
|
|
995
|
+
console.log('')
|
|
996
|
+
}
|
|
923
997
|
|
|
924
|
-
|
|
998
|
+
console.log('💡 Next steps:')
|
|
999
|
+
console.log('• /p:feature → Add new feature')
|
|
1000
|
+
console.log('• /p:now → Start working on something')
|
|
1001
|
+
console.log('• /p:ship → Ship completed work')
|
|
925
1002
|
|
|
926
|
-
|
|
927
|
-
|
|
1003
|
+
await this.logToMemory(projectPath, 'recap_viewed', {
|
|
1004
|
+
shipped: shippedFeatures.length,
|
|
1005
|
+
tasks: nextTasks,
|
|
1006
|
+
timestamp: dateHelper.getTimestamp(),
|
|
1007
|
+
})
|
|
928
1008
|
|
|
929
1009
|
return {
|
|
930
1010
|
success: true,
|
|
931
|
-
|
|
1011
|
+
stats: { shipped: shippedFeatures.length, tasks: nextTasks, ideas },
|
|
932
1012
|
}
|
|
933
1013
|
} catch (error) {
|
|
934
|
-
|
|
935
|
-
return {
|
|
936
|
-
success: false,
|
|
937
|
-
message: this.agent.formatResponse(error.message, 'error'),
|
|
938
|
-
}
|
|
1014
|
+
console.error('❌ Error:', error.message)
|
|
1015
|
+
return { success: false, error: error.message }
|
|
939
1016
|
}
|
|
940
1017
|
}
|
|
941
1018
|
|
|
942
1019
|
/**
|
|
943
|
-
*
|
|
944
|
-
*
|
|
945
|
-
* @param {string} feature - Feature description
|
|
946
|
-
* @param {string} [projectPath=process.cwd()] - Project path
|
|
947
|
-
* @returns {Promise<Object>} Result object with success flag and message
|
|
1020
|
+
* /p:stuck - Get contextual help with problems
|
|
1021
|
+
* AGENTIC EXECUTION
|
|
948
1022
|
*/
|
|
949
|
-
async
|
|
1023
|
+
async stuck(issue, projectPath = process.cwd()) {
|
|
950
1024
|
try {
|
|
951
|
-
await this.
|
|
952
|
-
|
|
953
|
-
// Auto-init if not configured
|
|
954
|
-
const initCheck = await this.ensureProjectInit(projectPath)
|
|
955
|
-
if (!initCheck.success) {
|
|
956
|
-
return initCheck
|
|
957
|
-
}
|
|
1025
|
+
const initResult = await this.ensureProjectInit(projectPath)
|
|
1026
|
+
if (!initResult.success) return initResult
|
|
958
1027
|
|
|
959
|
-
if (!
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
'warning',
|
|
965
|
-
),
|
|
966
|
-
}
|
|
1028
|
+
if (!issue) {
|
|
1029
|
+
console.log('❌ Issue description required')
|
|
1030
|
+
console.log('Usage: /p:stuck "description of problem"')
|
|
1031
|
+
console.log('\nExample: /p:stuck "CORS error in API calls"')
|
|
1032
|
+
return { success: false, error: 'Issue description required' }
|
|
967
1033
|
}
|
|
968
1034
|
|
|
969
|
-
|
|
1035
|
+
console.log(`🆘 Getting help: ${issue}\n`)
|
|
970
1036
|
|
|
971
|
-
|
|
972
|
-
const week = this.getWeekNumber(new Date())
|
|
973
|
-
const year = new Date().getFullYear()
|
|
974
|
-
const weekHeader = `## Week ${week}, ${year}`
|
|
1037
|
+
const context = await contextBuilder.build(projectPath, { issue })
|
|
975
1038
|
|
|
976
|
-
|
|
1039
|
+
// Read analysis to understand stack
|
|
1040
|
+
const analysisContent = await toolRegistry.get('Read')(context.paths.analysis)
|
|
1041
|
+
let detectedStack = 'your project'
|
|
977
1042
|
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
const recentShips = await sessionManager.getRecentLogs(config.projectId, 30, 'shipped.md')
|
|
986
|
-
const totalShipped = recentShips.match(/✅/g)?.length || 1
|
|
987
|
-
|
|
988
|
-
await this.logToMemory(projectPath, 'ship', { feature, timestamp: this.agent.getTimestamp() })
|
|
1043
|
+
if (analysisContent) {
|
|
1044
|
+
if (analysisContent.includes('Next.js')) detectedStack = 'Next.js'
|
|
1045
|
+
else if (analysisContent.includes('React')) detectedStack = 'React'
|
|
1046
|
+
else if (analysisContent.includes('Rust')) detectedStack = 'Rust'
|
|
1047
|
+
else if (analysisContent.includes('Go')) detectedStack = 'Go'
|
|
1048
|
+
else if (analysisContent.includes('Python')) detectedStack = 'Python'
|
|
1049
|
+
}
|
|
989
1050
|
|
|
990
|
-
|
|
991
|
-
|
|
1051
|
+
// Provide contextual help based on issue type
|
|
1052
|
+
console.log('💡 Contextual Help:\n')
|
|
992
1053
|
|
|
993
|
-
|
|
1054
|
+
const issueLower = issue.toLowerCase()
|
|
994
1055
|
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1056
|
+
// Common issue patterns
|
|
1057
|
+
if (issueLower.includes('cors')) {
|
|
1058
|
+
console.log('## CORS Issue Detected\n')
|
|
1059
|
+
console.log('Common solutions for CORS errors:')
|
|
1060
|
+
console.log('1. Add CORS headers in your backend')
|
|
1061
|
+
if (detectedStack === 'Next.js') {
|
|
1062
|
+
console.log('2. Use Next.js API routes as proxy')
|
|
1063
|
+
console.log('3. Configure next.config.js rewrites')
|
|
1001
1064
|
}
|
|
1065
|
+
console.log('4. Check if credentials are being sent')
|
|
1066
|
+
console.log('5. Verify allowed origins match exactly\n')
|
|
1067
|
+
} else if (issueLower.includes('test') || issueLower.includes('failing')) {
|
|
1068
|
+
console.log('## Test Issues\n')
|
|
1069
|
+
console.log('Debug steps:')
|
|
1070
|
+
console.log('1. Run tests in watch mode: npm test -- --watch')
|
|
1071
|
+
console.log('2. Check test environment setup')
|
|
1072
|
+
console.log('3. Verify mocks are correct')
|
|
1073
|
+
console.log('4. Check async handling\n')
|
|
1074
|
+
} else if (issueLower.includes('build') || issueLower.includes('compile')) {
|
|
1075
|
+
console.log('## Build/Compile Issues\n')
|
|
1076
|
+
console.log('Debug steps:')
|
|
1077
|
+
console.log('1. Clear cache and node_modules')
|
|
1078
|
+
console.log('2. Check TypeScript errors: npm run type-check')
|
|
1079
|
+
console.log('3. Verify all dependencies are installed')
|
|
1080
|
+
console.log('4. Check for circular dependencies\n')
|
|
1081
|
+
} else if (issueLower.includes('deploy') || issueLower.includes('production')) {
|
|
1082
|
+
console.log('## Deployment Issues\n')
|
|
1083
|
+
console.log('Debug steps:')
|
|
1084
|
+
console.log('1. Check environment variables')
|
|
1085
|
+
console.log('2. Verify build succeeds locally')
|
|
1086
|
+
console.log('3. Check logs in deployment platform')
|
|
1087
|
+
console.log('4. Verify node version matches\n')
|
|
1002
1088
|
} else {
|
|
1003
|
-
|
|
1004
|
-
|
|
1089
|
+
console.log('## General Debugging Steps\n')
|
|
1090
|
+
console.log(`For ${detectedStack}:`)
|
|
1091
|
+
console.log('1. Check error logs and stack traces')
|
|
1092
|
+
console.log('2. Search error message in docs')
|
|
1093
|
+
console.log('3. Verify configuration files')
|
|
1094
|
+
console.log('4. Test in isolation (minimal reproduction)')
|
|
1095
|
+
console.log('5. Check recent changes (git diff)\n')
|
|
1096
|
+
}
|
|
1097
|
+
|
|
1098
|
+
console.log('📚 Resources:')
|
|
1099
|
+
console.log(`• Stack Overflow: Search "${issue}"`)
|
|
1100
|
+
console.log(`• GitHub Issues: Search in ${detectedStack} repo`)
|
|
1101
|
+
if (detectedStack !== 'your project') {
|
|
1102
|
+
console.log(`• Official Docs: ${detectedStack} documentation`)
|
|
1103
|
+
}
|
|
1104
|
+
console.log('• Claude Code: Ask Claude for specific help with code\n')
|
|
1105
|
+
|
|
1106
|
+
console.log('💬 Still stuck?')
|
|
1107
|
+
console.log('• Share error logs with Claude')
|
|
1108
|
+
console.log('• Create minimal reproduction')
|
|
1109
|
+
console.log('• /p:context → Review project state')
|
|
1110
|
+
|
|
1111
|
+
// Log to memory
|
|
1112
|
+
await this.logToMemory(projectPath, 'help_requested', {
|
|
1113
|
+
issue,
|
|
1114
|
+
stack: detectedStack,
|
|
1115
|
+
timestamp: dateHelper.getTimestamp(),
|
|
1116
|
+
})
|
|
1117
|
+
|
|
1118
|
+
return { success: true, issue, stack: detectedStack }
|
|
1005
1119
|
} catch (error) {
|
|
1006
|
-
|
|
1007
|
-
return {
|
|
1008
|
-
success: false,
|
|
1009
|
-
message: this.agent.formatResponse(error.message, 'error'),
|
|
1010
|
-
}
|
|
1120
|
+
console.error('❌ Error:', error.message)
|
|
1121
|
+
return { success: false, error: error.message }
|
|
1011
1122
|
}
|
|
1012
1123
|
}
|
|
1013
1124
|
|
|
1014
1125
|
/**
|
|
1015
|
-
*
|
|
1016
|
-
*
|
|
1017
|
-
* @private
|
|
1018
|
-
* @param {string} feature - Feature description
|
|
1019
|
-
* @param {string} projectPath - Project path
|
|
1020
|
-
* @returns {Promise<Object>} Result object
|
|
1126
|
+
* /p:design - Design system architecture, APIs, and components
|
|
1127
|
+
* AGENTIC EXECUTION
|
|
1021
1128
|
*/
|
|
1022
|
-
async
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
const week = this.getWeekNumber(new Date())
|
|
1028
|
-
const year = new Date().getFullYear()
|
|
1029
|
-
const weekHeader = `## Week ${week}, ${year}`
|
|
1030
|
-
|
|
1031
|
-
if (!content.includes(weekHeader)) {
|
|
1032
|
-
content += `\n${weekHeader}\n`
|
|
1033
|
-
}
|
|
1034
|
-
|
|
1035
|
-
const entry = `- ✅ **${feature}** _(${new Date().toLocaleString()})_\n`
|
|
1036
|
-
const insertIndex = content.indexOf(weekHeader) + weekHeader.length + 1
|
|
1037
|
-
content = content.slice(0, insertIndex) + entry + content.slice(insertIndex)
|
|
1129
|
+
async design(target = null, options = {}, projectPath = process.cwd()) {
|
|
1130
|
+
try {
|
|
1131
|
+
const initResult = await this.ensureProjectInit(projectPath)
|
|
1132
|
+
if (!initResult.success) return initResult
|
|
1038
1133
|
|
|
1039
|
-
|
|
1134
|
+
const designType = options.type || 'architecture'
|
|
1135
|
+
const validTypes = ['architecture', 'api', 'component', 'database', 'flow']
|
|
1040
1136
|
|
|
1041
|
-
|
|
1137
|
+
if (!validTypes.includes(designType)) {
|
|
1138
|
+
console.log(`❌ Invalid design type: ${designType}`)
|
|
1139
|
+
console.log(`Valid types: ${validTypes.join(', ')}`)
|
|
1140
|
+
return { success: false, error: 'Invalid design type' }
|
|
1141
|
+
}
|
|
1042
1142
|
|
|
1043
|
-
|
|
1143
|
+
const designTarget = target || 'system'
|
|
1044
1144
|
|
|
1045
|
-
|
|
1046
|
-
const velocityMsg = daysSinceLastShip > 3 ? 'Keep the momentum going!' : "You're on fire! 🔥"
|
|
1145
|
+
console.log(`🎨 Designing ${designType}: ${designTarget}\n`)
|
|
1047
1146
|
|
|
1048
|
-
|
|
1147
|
+
// Create designs directory if it doesn't exist
|
|
1148
|
+
const projectId = await configManager.getProjectId(projectPath)
|
|
1149
|
+
const designsPath = path.join(
|
|
1150
|
+
pathManager.getGlobalProjectPath(projectId),
|
|
1151
|
+
'planning',
|
|
1152
|
+
'designs'
|
|
1153
|
+
)
|
|
1154
|
+
await fileHelper.ensureDir(designsPath)
|
|
1155
|
+
|
|
1156
|
+
// Generate design document based on type
|
|
1157
|
+
let designContent = ''
|
|
1049
1158
|
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
'
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1159
|
+
switch (designType) {
|
|
1160
|
+
case 'architecture':
|
|
1161
|
+
designContent = this._generateArchitectureDesign(designTarget, projectPath)
|
|
1162
|
+
break
|
|
1163
|
+
case 'api':
|
|
1164
|
+
designContent = this._generateApiDesign(designTarget)
|
|
1165
|
+
break
|
|
1166
|
+
case 'component':
|
|
1167
|
+
designContent = this._generateComponentDesign(designTarget)
|
|
1168
|
+
break
|
|
1169
|
+
case 'database':
|
|
1170
|
+
designContent = this._generateDatabaseDesign(designTarget)
|
|
1171
|
+
break
|
|
1172
|
+
case 'flow':
|
|
1173
|
+
designContent = this._generateFlowDesign(designTarget)
|
|
1174
|
+
break
|
|
1175
|
+
}
|
|
1058
1176
|
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
* @returns {Promise<Object>} Result object with success flag and message
|
|
1064
|
-
*/
|
|
1065
|
-
async next(projectPath = process.cwd()) {
|
|
1066
|
-
try {
|
|
1067
|
-
await this.initializeAgent()
|
|
1177
|
+
// Save design document
|
|
1178
|
+
const designFileName = `${designType}-${designTarget.toLowerCase().replace(/\s+/g, '-')}.md`
|
|
1179
|
+
const designFilePath = path.join(designsPath, designFileName)
|
|
1180
|
+
await fileHelper.writeFile(designFilePath, designContent)
|
|
1068
1181
|
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1182
|
+
console.log('✅ Design document created!\n')
|
|
1183
|
+
console.log(`📄 Location: planning/designs/${designFileName}\n`)
|
|
1184
|
+
console.log('💡 Next steps:')
|
|
1185
|
+
console.log('• Review and refine the design')
|
|
1186
|
+
console.log('• /p:feature → Implement the design')
|
|
1187
|
+
console.log('• Share with team for feedback')
|
|
1074
1188
|
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
.filter((line) => line.startsWith('- '))
|
|
1081
|
-
.map((line) => line.replace('- ', ''))
|
|
1082
|
-
|
|
1083
|
-
if (tasks.length === 0) {
|
|
1084
|
-
return {
|
|
1085
|
-
success: true,
|
|
1086
|
-
message: this.agent.formatResponse(
|
|
1087
|
-
`Queue is empty. Add tasks with ${this.agentInfo.config.commandPrefix}idea or focus on shipping!`,
|
|
1088
|
-
'info',
|
|
1089
|
-
),
|
|
1090
|
-
}
|
|
1091
|
-
}
|
|
1189
|
+
await this.logToMemory(projectPath, 'design_created', {
|
|
1190
|
+
type: designType,
|
|
1191
|
+
target: designTarget,
|
|
1192
|
+
timestamp: dateHelper.getTimestamp(),
|
|
1193
|
+
})
|
|
1092
1194
|
|
|
1093
1195
|
return {
|
|
1094
1196
|
success: true,
|
|
1095
|
-
|
|
1197
|
+
designPath: designFilePath,
|
|
1198
|
+
type: designType,
|
|
1199
|
+
target: designTarget,
|
|
1096
1200
|
}
|
|
1097
1201
|
} catch (error) {
|
|
1098
|
-
|
|
1099
|
-
return {
|
|
1100
|
-
success: false,
|
|
1101
|
-
message: this.agent.formatResponse(error.message, 'error'),
|
|
1102
|
-
}
|
|
1202
|
+
console.error('❌ Error:', error.message)
|
|
1203
|
+
return { success: false, error: error.message }
|
|
1103
1204
|
}
|
|
1104
1205
|
}
|
|
1105
1206
|
|
|
1106
1207
|
/**
|
|
1107
|
-
*
|
|
1108
|
-
*
|
|
1109
|
-
* @param {string} text - Idea text
|
|
1110
|
-
* @param {string} [projectPath=process.cwd()] - Project path
|
|
1111
|
-
* @returns {Promise<Object>} Result object with success flag and message
|
|
1208
|
+
* Generate architecture design document
|
|
1209
|
+
* @private
|
|
1112
1210
|
*/
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1211
|
+
_generateArchitectureDesign(target, projectPath) {
|
|
1212
|
+
const projectName = path.basename(projectPath)
|
|
1213
|
+
return `# Architecture Design: ${target}
|
|
1116
1214
|
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
return initCheck
|
|
1121
|
-
}
|
|
1215
|
+
**Project**: ${projectName}
|
|
1216
|
+
**Created**: ${new Date().toLocaleString()}
|
|
1217
|
+
**Type**: System Architecture
|
|
1122
1218
|
|
|
1123
|
-
|
|
1124
|
-
return {
|
|
1125
|
-
success: false,
|
|
1126
|
-
message: this.agent.formatResponse(
|
|
1127
|
-
`Please provide an idea: ${this.agentInfo.config.commandPrefix}idea "your idea"`,
|
|
1128
|
-
'warning',
|
|
1129
|
-
),
|
|
1130
|
-
}
|
|
1131
|
-
}
|
|
1132
|
-
|
|
1133
|
-
const ideasFile = await this.getFilePath(projectPath, 'planning', 'ideas.md')
|
|
1134
|
-
const nextFile = await this.getFilePath(projectPath, 'core', 'next.md')
|
|
1219
|
+
## Overview
|
|
1135
1220
|
|
|
1136
|
-
|
|
1137
|
-
const ideasContent = await this.agent.readFile(ideasFile)
|
|
1138
|
-
await this.agent.writeFile(ideasFile, ideasContent + entry)
|
|
1221
|
+
High-level architecture design for ${target}.
|
|
1139
1222
|
|
|
1140
|
-
|
|
1141
|
-
if (text.match(/^(implement|add|create|fix|update|build)/i)) {
|
|
1142
|
-
const nextContent = await this.agent.readFile(nextFile)
|
|
1143
|
-
await this.agent.writeFile(nextFile, nextContent + `- ${text}\n`)
|
|
1144
|
-
addedToQueue = true
|
|
1145
|
-
}
|
|
1146
|
-
|
|
1147
|
-
await this.logToMemory(projectPath, 'idea', { text, timestamp: this.agent.getTimestamp() })
|
|
1223
|
+
## Components
|
|
1148
1224
|
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1225
|
+
### Core Components
|
|
1226
|
+
1. **Component A**
|
|
1227
|
+
- Responsibility: [Define responsibility]
|
|
1228
|
+
- Dependencies: [List dependencies]
|
|
1229
|
+
- Interfaces: [Define interfaces]
|
|
1152
1230
|
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
'\n' +
|
|
1158
|
-
this.agent.suggestNextAction('ideaCaptured'),
|
|
1159
|
-
}
|
|
1160
|
-
} catch (error) {
|
|
1161
|
-
await this.initializeAgent()
|
|
1162
|
-
return {
|
|
1163
|
-
success: false,
|
|
1164
|
-
message: this.agent.formatResponse(error.message, 'error'),
|
|
1165
|
-
}
|
|
1166
|
-
}
|
|
1167
|
-
}
|
|
1231
|
+
2. **Component B**
|
|
1232
|
+
- Responsibility: [Define responsibility]
|
|
1233
|
+
- Dependencies: [List dependencies]
|
|
1234
|
+
- Interfaces: [Define interfaces]
|
|
1168
1235
|
|
|
1169
|
-
|
|
1170
|
-
* Show project recap with progress overview
|
|
1171
|
-
*
|
|
1172
|
-
* @param {string} [projectPath=process.cwd()] - Project path
|
|
1173
|
-
* @returns {Promise<Object>} Result object with success flag and message
|
|
1174
|
-
*/
|
|
1175
|
-
async recap(projectPath = process.cwd()) {
|
|
1176
|
-
try {
|
|
1177
|
-
await this.initializeAgent()
|
|
1236
|
+
## Data Flow
|
|
1178
1237
|
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1238
|
+
\`\`\`
|
|
1239
|
+
[User] → [Frontend] → [API Gateway] → [Backend Services] → [Database]
|
|
1240
|
+
↓
|
|
1241
|
+
[Cache Layer]
|
|
1242
|
+
\`\`\`
|
|
1184
1243
|
|
|
1185
|
-
|
|
1186
|
-
const nextFilePath = await this.getFilePath(projectPath, 'core', 'next.md')
|
|
1187
|
-
const ideasFilePath = await this.getFilePath(projectPath, 'planning', 'ideas.md')
|
|
1244
|
+
## Technology Stack
|
|
1188
1245
|
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1246
|
+
- **Frontend**: [Framework/Library]
|
|
1247
|
+
- **Backend**: [Framework/Runtime]
|
|
1248
|
+
- **Database**: [Type/System]
|
|
1249
|
+
- **Deployment**: [Platform/Method]
|
|
1192
1250
|
|
|
1193
|
-
|
|
1251
|
+
## Design Decisions
|
|
1194
1252
|
|
|
1195
|
-
|
|
1196
|
-
|
|
1253
|
+
### Decision 1: [Title]
|
|
1254
|
+
- **Context**: [Why this decision is needed]
|
|
1255
|
+
- **Options**: [Alternatives considered]
|
|
1256
|
+
- **Choice**: [What was chosen]
|
|
1257
|
+
- **Rationale**: [Why this choice]
|
|
1197
1258
|
|
|
1198
|
-
|
|
1199
|
-
let shippedCount = 0
|
|
1200
|
-
let recentActivity = ''
|
|
1259
|
+
## Implementation Plan
|
|
1201
1260
|
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1261
|
+
1. [ ] Setup project structure
|
|
1262
|
+
2. [ ] Implement core components
|
|
1263
|
+
3. [ ] Add integration layer
|
|
1264
|
+
4. [ ] Testing and validation
|
|
1265
|
+
5. [ ] Documentation
|
|
1205
1266
|
|
|
1206
|
-
|
|
1207
|
-
recentActivity = recentLogs
|
|
1208
|
-
.slice(-3)
|
|
1209
|
-
.map((entry) => {
|
|
1210
|
-
return `• ${entry.action}: ${entry.data.task || entry.data.feature || entry.data.text || ''}`
|
|
1211
|
-
})
|
|
1212
|
-
.join('\n')
|
|
1213
|
-
} else {
|
|
1214
|
-
const shippedFilePath = await this.getFilePath(projectPath, 'progress', 'shipped.md')
|
|
1215
|
-
const shippedFile = await this.agent.readFile(shippedFilePath)
|
|
1216
|
-
shippedCount = (shippedFile.match(/✅/g) || []).length
|
|
1217
|
-
|
|
1218
|
-
const memoryFile = await this.getFilePath(projectPath, 'memory', 'memory.jsonl')
|
|
1219
|
-
try {
|
|
1220
|
-
const memory = await this.agent.readFile(memoryFile)
|
|
1221
|
-
const lines = memory
|
|
1222
|
-
.trim()
|
|
1223
|
-
.split('\n')
|
|
1224
|
-
.filter((l) => l)
|
|
1225
|
-
recentActivity = lines
|
|
1226
|
-
.slice(-3)
|
|
1227
|
-
.map((l) => {
|
|
1228
|
-
const entry = JSON.parse(l)
|
|
1229
|
-
return `• ${entry.action}: ${entry.data.task || entry.data.feature || entry.data.text || ''}`
|
|
1230
|
-
})
|
|
1231
|
-
.join('\n')
|
|
1232
|
-
} catch (e) {
|
|
1233
|
-
}
|
|
1234
|
-
}
|
|
1267
|
+
## Notes
|
|
1235
1268
|
|
|
1236
|
-
|
|
1237
|
-
currentTask,
|
|
1238
|
-
shippedCount,
|
|
1239
|
-
queuedCount,
|
|
1240
|
-
ideasCount,
|
|
1241
|
-
recentActivity,
|
|
1242
|
-
}
|
|
1269
|
+
[Additional notes, constraints, assumptions]
|
|
1243
1270
|
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
}
|
|
1248
|
-
} catch (error) {
|
|
1249
|
-
await this.initializeAgent()
|
|
1250
|
-
return {
|
|
1251
|
-
success: false,
|
|
1252
|
-
message: this.agent.formatResponse(error.message, 'error'),
|
|
1253
|
-
}
|
|
1254
|
-
}
|
|
1271
|
+
---
|
|
1272
|
+
*This is a living document. Update as design evolves.*
|
|
1273
|
+
`
|
|
1255
1274
|
}
|
|
1256
1275
|
|
|
1257
1276
|
/**
|
|
1258
|
-
*
|
|
1259
|
-
*
|
|
1260
|
-
* @param {string} [period='week'] - Time period: 'day', 'week', or 'month'
|
|
1261
|
-
* @param {string} [projectPath=process.cwd()] - Project path
|
|
1262
|
-
* @returns {Promise<Object>} Result object with success flag and message
|
|
1277
|
+
* Generate API design document
|
|
1278
|
+
* @private
|
|
1263
1279
|
*/
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
await this.initializeAgent()
|
|
1280
|
+
_generateApiDesign(target) {
|
|
1281
|
+
return `# API Design: ${target}
|
|
1267
1282
|
|
|
1268
|
-
|
|
1283
|
+
**Created**: ${new Date().toLocaleString()}
|
|
1284
|
+
**Type**: API Specification
|
|
1269
1285
|
|
|
1270
|
-
|
|
1271
|
-
const lines = shippedData.split('\n')
|
|
1286
|
+
## Endpoints
|
|
1272
1287
|
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
const match = line.match(/\*\*(.*?)\*\*.*?\((.*?)\)/)
|
|
1276
|
-
if (match) {
|
|
1277
|
-
features.push({
|
|
1278
|
-
name: match[1],
|
|
1279
|
-
date: new Date(match[2]),
|
|
1280
|
-
})
|
|
1281
|
-
}
|
|
1282
|
-
}
|
|
1283
|
-
}
|
|
1288
|
+
### GET /api/${target.toLowerCase()}
|
|
1289
|
+
**Description**: Retrieve ${target}
|
|
1284
1290
|
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1291
|
+
**Request**:
|
|
1292
|
+
\`\`\`
|
|
1293
|
+
GET /api/${target.toLowerCase()}?limit=10&offset=0
|
|
1294
|
+
\`\`\`
|
|
1288
1295
|
|
|
1289
|
-
|
|
1296
|
+
**Response** (200 OK):
|
|
1297
|
+
\`\`\`json
|
|
1298
|
+
{
|
|
1299
|
+
"data": [],
|
|
1300
|
+
"meta": {
|
|
1301
|
+
"total": 0,
|
|
1302
|
+
"limit": 10,
|
|
1303
|
+
"offset": 0
|
|
1304
|
+
}
|
|
1305
|
+
}
|
|
1306
|
+
\`\`\`
|
|
1290
1307
|
|
|
1291
|
-
|
|
1308
|
+
### POST /api/${target.toLowerCase()}
|
|
1309
|
+
**Description**: Create new ${target}
|
|
1292
1310
|
|
|
1293
|
-
|
|
1294
|
-
|
|
1311
|
+
**Request**:
|
|
1312
|
+
\`\`\`json
|
|
1313
|
+
{
|
|
1314
|
+
"name": "string",
|
|
1315
|
+
"description": "string"
|
|
1316
|
+
}
|
|
1317
|
+
\`\`\`
|
|
1295
1318
|
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1319
|
+
**Response** (201 Created):
|
|
1320
|
+
\`\`\`json
|
|
1321
|
+
{
|
|
1322
|
+
"id": "string",
|
|
1323
|
+
"name": "string",
|
|
1324
|
+
"description": "string",
|
|
1325
|
+
"createdAt": "ISO8601"
|
|
1326
|
+
}
|
|
1327
|
+
\`\`\`
|
|
1302
1328
|
|
|
1303
|
-
|
|
1304
|
-
period,
|
|
1305
|
-
count: periodFeatures.length,
|
|
1306
|
-
velocity,
|
|
1307
|
-
previousVelocity,
|
|
1308
|
-
recentFeatures: periodFeatures
|
|
1309
|
-
.slice(0, 3)
|
|
1310
|
-
.map((f) => `• ${f.name}`)
|
|
1311
|
-
.join('\n'),
|
|
1312
|
-
motivationalMessage,
|
|
1313
|
-
timeMetrics,
|
|
1314
|
-
}
|
|
1329
|
+
## Error Handling
|
|
1315
1330
|
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1331
|
+
\`\`\`json
|
|
1332
|
+
{
|
|
1333
|
+
"error": {
|
|
1334
|
+
"code": "ERROR_CODE",
|
|
1335
|
+
"message": "Human-readable message",
|
|
1336
|
+
"details": {}
|
|
1337
|
+
}
|
|
1338
|
+
}
|
|
1339
|
+
\`\`\`
|
|
1340
|
+
|
|
1341
|
+
## Authentication
|
|
1342
|
+
|
|
1343
|
+
- **Method**: Bearer Token
|
|
1344
|
+
- **Header**: \`Authorization: Bearer <token>\`
|
|
1345
|
+
|
|
1346
|
+
## Rate Limiting
|
|
1347
|
+
|
|
1348
|
+
- **Limit**: 100 requests/minute
|
|
1349
|
+
- **Headers**: \`X-RateLimit-Limit\`, \`X-RateLimit-Remaining\`
|
|
1350
|
+
|
|
1351
|
+
---
|
|
1352
|
+
*Update this specification as API evolves.*
|
|
1353
|
+
`
|
|
1327
1354
|
}
|
|
1328
1355
|
|
|
1329
1356
|
/**
|
|
1330
|
-
*
|
|
1331
|
-
*
|
|
1332
|
-
* @param {string} projectPath - Path to the project
|
|
1333
|
-
* @param {string} period - Period ('day', 'week', 'month')
|
|
1334
|
-
* @returns {Promise<Object>} Time metrics object
|
|
1357
|
+
* Generate component design document
|
|
1358
|
+
* @private
|
|
1335
1359
|
*/
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
const periodDays = period === 'day' ? 1 : period === 'week' ? 7 : period === 'month' ? 30 : 7
|
|
1339
|
-
const logs = await sessionManager.getRecentLogs(await configManager.getProjectId(projectPath), periodDays, 'context.jsonl')
|
|
1340
|
-
|
|
1341
|
-
const completedTasks = logs.filter(log => log.type === 'task_completed' && log.data?.duration)
|
|
1342
|
-
|
|
1343
|
-
if (completedTasks.length === 0) {
|
|
1344
|
-
return {
|
|
1345
|
-
totalTime: 'N/A',
|
|
1346
|
-
avgDuration: 'N/A',
|
|
1347
|
-
tasksCompleted: 0,
|
|
1348
|
-
longestTask: 'N/A',
|
|
1349
|
-
shortestTask: 'N/A',
|
|
1350
|
-
byAuthor: {},
|
|
1351
|
-
}
|
|
1352
|
-
}
|
|
1360
|
+
_generateComponentDesign(target) {
|
|
1361
|
+
return `# Component Design: ${target}
|
|
1353
1362
|
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
if (!match) return 0
|
|
1357
|
-
return parseInt(match[1]) * 60 + parseInt(match[2])
|
|
1358
|
-
}
|
|
1363
|
+
**Created**: ${new Date().toLocaleString()}
|
|
1364
|
+
**Type**: Component Specification
|
|
1359
1365
|
|
|
1360
|
-
|
|
1361
|
-
const totalMinutes = durations.reduce((sum, d) => sum + d, 0)
|
|
1362
|
-
const avgMinutes = Math.round(totalMinutes / durations.length)
|
|
1366
|
+
## Overview
|
|
1363
1367
|
|
|
1364
|
-
|
|
1365
|
-
const longestMinutes = sortedDurations[0]
|
|
1366
|
-
const shortestMinutes = sortedDurations[sortedDurations.length - 1]
|
|
1368
|
+
Component for ${target} functionality.
|
|
1367
1369
|
|
|
1368
|
-
|
|
1369
|
-
const h = Math.floor(minutes / 60)
|
|
1370
|
-
const m = minutes % 60
|
|
1371
|
-
return `${h}h ${m}m`
|
|
1372
|
-
}
|
|
1370
|
+
## Props/Interface
|
|
1373
1371
|
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
}
|
|
1383
|
-
byAuthor[author].tasks++
|
|
1384
|
-
byAuthor[author].totalMinutes += parseDuration(task.data.duration)
|
|
1385
|
-
})
|
|
1372
|
+
\`\`\`typescript
|
|
1373
|
+
interface ${target}Props {
|
|
1374
|
+
// Define props
|
|
1375
|
+
id?: string
|
|
1376
|
+
className?: string
|
|
1377
|
+
onAction?: (data: any) => void
|
|
1378
|
+
}
|
|
1379
|
+
\`\`\`
|
|
1386
1380
|
|
|
1387
|
-
|
|
1388
|
-
byAuthor[author].totalTime = formatTime(byAuthor[author].totalMinutes)
|
|
1389
|
-
byAuthor[author].avgTime = formatTime(Math.round(byAuthor[author].totalMinutes / byAuthor[author].tasks))
|
|
1390
|
-
})
|
|
1381
|
+
## State
|
|
1391
1382
|
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
} catch (error) {
|
|
1401
|
-
return {
|
|
1402
|
-
totalTime: 'N/A',
|
|
1403
|
-
avgDuration: 'N/A',
|
|
1404
|
-
tasksCompleted: 0,
|
|
1405
|
-
longestTask: 'N/A',
|
|
1406
|
-
shortestTask: 'N/A',
|
|
1407
|
-
byAuthor: {},
|
|
1408
|
-
}
|
|
1409
|
-
}
|
|
1410
|
-
}
|
|
1383
|
+
\`\`\`typescript
|
|
1384
|
+
interface ${target}State {
|
|
1385
|
+
// Define internal state
|
|
1386
|
+
loading: boolean
|
|
1387
|
+
data: any[]
|
|
1388
|
+
error: Error | null
|
|
1389
|
+
}
|
|
1390
|
+
\`\`\`
|
|
1411
1391
|
|
|
1412
|
-
|
|
1413
|
-
* Get help when stuck on a problem
|
|
1414
|
-
*
|
|
1415
|
-
* @param {string} issue - Issue description
|
|
1416
|
-
* @param {string} [projectPath=process.cwd()] - Project path
|
|
1417
|
-
* @returns {Promise<Object>} Result object with success flag and message
|
|
1418
|
-
*/
|
|
1419
|
-
async stuck(issue, projectPath = process.cwd()) {
|
|
1420
|
-
try {
|
|
1421
|
-
await this.initializeAgent()
|
|
1392
|
+
## Component Structure
|
|
1422
1393
|
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1394
|
+
\`\`\`
|
|
1395
|
+
${target}/
|
|
1396
|
+
├── index.ts # Barrel export
|
|
1397
|
+
├── ${target}.tsx # Main component
|
|
1398
|
+
├── ${target}.test.tsx # Tests
|
|
1399
|
+
├── ${target}.styles.ts # Styles
|
|
1400
|
+
└── types.ts # Type definitions
|
|
1401
|
+
\`\`\`
|
|
1428
1402
|
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
`Please describe what you're stuck on: ${this.agentInfo.config.commandPrefix}stuck "issue description"`,
|
|
1434
|
-
'warning',
|
|
1435
|
-
),
|
|
1436
|
-
}
|
|
1437
|
-
}
|
|
1403
|
+
## Usage Example
|
|
1404
|
+
|
|
1405
|
+
\`\`\`tsx
|
|
1406
|
+
import { ${target} } from '@/components/${target}'
|
|
1438
1407
|
|
|
1439
|
-
|
|
1408
|
+
function App() {
|
|
1409
|
+
return (
|
|
1410
|
+
<${target}
|
|
1411
|
+
id="example"
|
|
1412
|
+
onAction={(data) => console.log(data)}
|
|
1413
|
+
/>
|
|
1414
|
+
)
|
|
1415
|
+
}
|
|
1416
|
+
\`\`\`
|
|
1440
1417
|
|
|
1441
|
-
|
|
1418
|
+
## Dependencies
|
|
1442
1419
|
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
1420
|
+
- React
|
|
1421
|
+
- [Other libraries]
|
|
1422
|
+
|
|
1423
|
+
## Implementation Notes
|
|
1424
|
+
|
|
1425
|
+
1. [ ] Setup component structure
|
|
1426
|
+
2. [ ] Implement core logic
|
|
1427
|
+
3. [ ] Add styling
|
|
1428
|
+
4. [ ] Write tests
|
|
1429
|
+
5. [ ] Document usage
|
|
1430
|
+
|
|
1431
|
+
---
|
|
1432
|
+
*Component design is iterative. Update as needed.*
|
|
1433
|
+
`
|
|
1454
1434
|
}
|
|
1455
1435
|
|
|
1456
1436
|
/**
|
|
1457
|
-
*
|
|
1458
|
-
*
|
|
1459
|
-
* @param {string} [target='.'] - Target directory
|
|
1460
|
-
* @param {Object} [options={}] - Cleanup options
|
|
1461
|
-
* @param {string} [projectPath=process.cwd()] - Project path
|
|
1462
|
-
* @returns {Promise<Object>} Result object with success flag and message
|
|
1437
|
+
* Generate database design document
|
|
1438
|
+
* @private
|
|
1463
1439
|
*/
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
await this.initializeAgent()
|
|
1440
|
+
_generateDatabaseDesign(target) {
|
|
1441
|
+
return `# Database Design: ${target}
|
|
1467
1442
|
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
const dryRun = options.dryRun || false
|
|
1443
|
+
**Created**: ${new Date().toLocaleString()}
|
|
1444
|
+
**Type**: Database Schema
|
|
1471
1445
|
|
|
1472
|
-
|
|
1473
|
-
deadCode: { consoleLogs: 0, commented: 0, unused: 0 },
|
|
1474
|
-
imports: { removed: 0, organized: 0 },
|
|
1475
|
-
files: { temp: 0, empty: 0, spaceFeed: 0 },
|
|
1476
|
-
deps: { removed: 0, sizeSaved: 0 },
|
|
1477
|
-
}
|
|
1446
|
+
## Schema
|
|
1478
1447
|
|
|
1479
|
-
|
|
1480
|
-
results.deadCode.consoleLogs = Math.floor(Math.random() * 20)
|
|
1481
|
-
results.deadCode.commented = Math.floor(Math.random() * 10)
|
|
1482
|
-
if (mode === 'aggressive') {
|
|
1483
|
-
results.deadCode.unused = Math.floor(Math.random() * 5)
|
|
1484
|
-
}
|
|
1485
|
-
}
|
|
1448
|
+
### Table: ${target.toLowerCase()}
|
|
1486
1449
|
|
|
1487
|
-
|
|
1488
|
-
|
|
1489
|
-
|
|
1490
|
-
|
|
1450
|
+
\`\`\`sql
|
|
1451
|
+
CREATE TABLE ${target.toLowerCase()} (
|
|
1452
|
+
id SERIAL PRIMARY KEY,
|
|
1453
|
+
name VARCHAR(255) NOT NULL,
|
|
1454
|
+
description TEXT,
|
|
1455
|
+
status VARCHAR(50) DEFAULT 'active',
|
|
1456
|
+
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
1457
|
+
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
|
1458
|
+
);
|
|
1459
|
+
\`\`\`
|
|
1491
1460
|
|
|
1492
|
-
|
|
1493
|
-
results.files.temp = Math.floor(Math.random() * 10)
|
|
1494
|
-
results.files.empty = Math.floor(Math.random() * 5)
|
|
1495
|
-
results.files.spaceFeed = (Math.random() * 5).toFixed(1)
|
|
1496
|
-
}
|
|
1461
|
+
## Indexes
|
|
1497
1462
|
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
|
|
1463
|
+
\`\`\`sql
|
|
1464
|
+
CREATE INDEX idx_${target.toLowerCase()}_status ON ${target.toLowerCase()}(status);
|
|
1465
|
+
CREATE INDEX idx_${target.toLowerCase()}_created_at ON ${target.toLowerCase()}(created_at);
|
|
1466
|
+
\`\`\`
|
|
1502
1467
|
|
|
1503
|
-
|
|
1504
|
-
const message = `
|
|
1505
|
-
🧹 ✨ Advanced Cleanup Complete! ✨ 🧹
|
|
1468
|
+
## Relationships
|
|
1506
1469
|
|
|
1507
|
-
|
|
1508
|
-
|
|
1470
|
+
- **Related Tables**: [List related tables]
|
|
1471
|
+
- **Foreign Keys**: [Define foreign keys]
|
|
1509
1472
|
|
|
1510
|
-
|
|
1511
|
-
• Console.logs: ${results.deadCode.consoleLogs} statements
|
|
1512
|
-
• Commented code: ${results.deadCode.commented} blocks
|
|
1513
|
-
${mode === 'aggressive' ? `• Unused functions: ${results.deadCode.unused}` : ''}
|
|
1473
|
+
## Queries
|
|
1514
1474
|
|
|
1515
|
-
|
|
1516
|
-
• Unused imports: ${results.imports.removed} removed
|
|
1517
|
-
• Files organized: ${results.imports.organized}
|
|
1475
|
+
### Common Queries
|
|
1518
1476
|
|
|
1519
|
-
|
|
1520
|
-
|
|
1521
|
-
|
|
1522
|
-
• Space freed: ${results.files.spaceFeed} MB
|
|
1477
|
+
\`\`\`sql
|
|
1478
|
+
-- Get active records
|
|
1479
|
+
SELECT * FROM ${target.toLowerCase()} WHERE status = 'active';
|
|
1523
1480
|
|
|
1524
|
-
|
|
1525
|
-
|
|
1526
|
-
|
|
1481
|
+
-- Get recent records
|
|
1482
|
+
SELECT * FROM ${target.toLowerCase()}
|
|
1483
|
+
ORDER BY created_at DESC
|
|
1484
|
+
LIMIT 10;
|
|
1485
|
+
\`\`\`
|
|
1527
1486
|
|
|
1528
|
-
|
|
1529
|
-
✨ Your code is clean and optimized!
|
|
1487
|
+
## Migrations
|
|
1530
1488
|
|
|
1531
|
-
|
|
1532
|
-
|
|
1489
|
+
1. [ ] Create initial schema
|
|
1490
|
+
2. [ ] Add indexes
|
|
1491
|
+
3. [ ] Setup relationships
|
|
1492
|
+
4. [ ] Add constraints
|
|
1533
1493
|
|
|
1534
|
-
|
|
1535
|
-
success: true,
|
|
1536
|
-
message,
|
|
1537
|
-
}
|
|
1538
|
-
}
|
|
1494
|
+
## Notes
|
|
1539
1495
|
|
|
1540
|
-
|
|
1541
|
-
|
|
1542
|
-
|
|
1543
|
-
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
|
|
1547
|
-
success: false,
|
|
1548
|
-
message: this.agent.formatResponse(error.message, 'error'),
|
|
1549
|
-
}
|
|
1550
|
-
}
|
|
1496
|
+
- Consider partitioning for large datasets
|
|
1497
|
+
- Add audit logging if needed
|
|
1498
|
+
- Implement soft deletes
|
|
1499
|
+
|
|
1500
|
+
---
|
|
1501
|
+
*Database design should evolve with requirements.*
|
|
1502
|
+
`
|
|
1551
1503
|
}
|
|
1552
1504
|
|
|
1553
1505
|
/**
|
|
1554
|
-
* Generate design
|
|
1555
|
-
*
|
|
1556
|
-
* @param {string} target - Design target name
|
|
1557
|
-
* @param {Object} [options={}] - Design options
|
|
1558
|
-
* @param {string} [projectPath=process.cwd()] - Project path
|
|
1559
|
-
* @returns {Promise<Object>} Result object with success flag and message
|
|
1506
|
+
* Generate flow design document
|
|
1507
|
+
* @private
|
|
1560
1508
|
*/
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
await this.initializeAgent()
|
|
1509
|
+
_generateFlowDesign(target) {
|
|
1510
|
+
return `# Flow Design: ${target}
|
|
1564
1511
|
|
|
1565
|
-
|
|
1566
|
-
|
|
1567
|
-
return {
|
|
1568
|
-
success: false,
|
|
1569
|
-
message: this.agent.formatResponse(
|
|
1570
|
-
`Project not initialized. Run ${this.agentInfo.config.commandPrefix}init first.`,
|
|
1571
|
-
'warning'
|
|
1572
|
-
),
|
|
1573
|
-
}
|
|
1574
|
-
}
|
|
1512
|
+
**Created**: ${new Date().toLocaleString()}
|
|
1513
|
+
**Type**: Process Flow
|
|
1575
1514
|
|
|
1576
|
-
|
|
1515
|
+
## Flow Overview
|
|
1577
1516
|
|
|
1578
|
-
|
|
1579
|
-
const projectId = await configManager.getProjectId(projectPath)
|
|
1580
|
-
const globalPath = pathManager.getGlobalProjectPath(projectId)
|
|
1581
|
-
const designDir = path.join(globalPath, 'analysis', 'designs')
|
|
1582
|
-
await this.agent.createDirectory(designDir)
|
|
1583
|
-
|
|
1584
|
-
let designContent = ''
|
|
1585
|
-
let diagram = ''
|
|
1517
|
+
Process flow for ${target}.
|
|
1586
1518
|
|
|
1587
|
-
|
|
1588
|
-
case 'architecture':
|
|
1589
|
-
diagram = `
|
|
1590
|
-
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
|
|
1591
|
-
│ Frontend │────▶│ Backend │────▶│ Database │
|
|
1592
|
-
│ React │ │ Node.js │ │ PostgreSQL │
|
|
1593
|
-
└─────────────┘ └─────────────┘ └─────────────┘
|
|
1594
|
-
│ │ │
|
|
1595
|
-
▼ ▼ ▼
|
|
1596
|
-
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
|
|
1597
|
-
│ Redux │ │ Express │ │ Redis │
|
|
1598
|
-
│ Store │ │ Routes │ │ Cache │
|
|
1599
|
-
└─────────────┘ └─────────────┘ └─────────────┘`
|
|
1600
|
-
break
|
|
1519
|
+
## Steps
|
|
1601
1520
|
|
|
1602
|
-
case 'api':
|
|
1603
|
-
diagram = `
|
|
1604
|
-
REST API Endpoints:
|
|
1605
|
-
POST /api/auth/register
|
|
1606
|
-
POST /api/auth/login
|
|
1607
|
-
GET /api/users/:id
|
|
1608
|
-
PUT /api/users/:id
|
|
1609
|
-
DELETE /api/users/:id`
|
|
1610
|
-
break
|
|
1611
|
-
|
|
1612
|
-
case 'component':
|
|
1613
|
-
diagram = `
|
|
1614
|
-
<App>
|
|
1615
|
-
├── <Header>
|
|
1616
|
-
│ ├── <Logo />
|
|
1617
|
-
│ ├── <Navigation />
|
|
1618
|
-
│ └── <UserMenu />
|
|
1619
|
-
├── <Main>
|
|
1620
|
-
│ ├── <Sidebar />
|
|
1621
|
-
│ └── <Content>
|
|
1622
|
-
│ ├── <Dashboard />
|
|
1623
|
-
│ └── <Routes />
|
|
1624
|
-
└── <Footer>`
|
|
1625
|
-
break
|
|
1626
|
-
|
|
1627
|
-
case 'database':
|
|
1628
|
-
diagram = `
|
|
1629
|
-
┌─────────────┐ ┌─────────────┐
|
|
1630
|
-
│ users │────▶│ profiles │
|
|
1631
|
-
├─────────────┤ ├─────────────┤
|
|
1632
|
-
│ id (PK) │ │ id (PK) │
|
|
1633
|
-
│ email │ │ user_id(FK) │
|
|
1634
|
-
│ password │ │ bio │
|
|
1635
|
-
│ created_at │ │ avatar_url │
|
|
1636
|
-
└─────────────┘ └─────────────┘`
|
|
1637
|
-
break
|
|
1638
|
-
|
|
1639
|
-
default:
|
|
1640
|
-
diagram = 'Custom design diagram'
|
|
1641
|
-
}
|
|
1642
|
-
|
|
1643
|
-
const timestamp = new Date().toISOString().split('T')[0]
|
|
1644
|
-
const designFile = path.join(designDir, `${target.replace(/\s+/g, '-')}-${type}-${timestamp}.md`)
|
|
1645
|
-
|
|
1646
|
-
designContent = `# Design: ${target}
|
|
1647
|
-
Type: ${type}
|
|
1648
|
-
Date: ${timestamp}
|
|
1649
|
-
|
|
1650
|
-
## Architecture Diagram
|
|
1651
1521
|
\`\`\`
|
|
1652
|
-
|
|
1522
|
+
1. [User Action/Trigger]
|
|
1523
|
+
↓
|
|
1524
|
+
2. [Validation]
|
|
1525
|
+
↓
|
|
1526
|
+
3. [Processing]
|
|
1527
|
+
↓
|
|
1528
|
+
4. [Side Effects]
|
|
1529
|
+
↓
|
|
1530
|
+
5. [Response/Completion]
|
|
1653
1531
|
\`\`\`
|
|
1654
1532
|
|
|
1655
|
-
##
|
|
1656
|
-
- Technology Stack: Modern web stack
|
|
1657
|
-
- Design Patterns: MVC, Repository, Observer
|
|
1658
|
-
- Key Components: Authentication, API, Database
|
|
1659
|
-
- Data Flow: Request → Controller → Service → Database
|
|
1660
|
-
|
|
1661
|
-
## Implementation Guide
|
|
1662
|
-
1. Set up project structure
|
|
1663
|
-
2. Implement core models
|
|
1664
|
-
3. Build API endpoints
|
|
1665
|
-
4. Create UI components
|
|
1666
|
-
5. Add tests and documentation
|
|
1667
|
-
`
|
|
1533
|
+
## Detailed Flow
|
|
1668
1534
|
|
|
1669
|
-
|
|
1535
|
+
### Step 1: Initial Action
|
|
1536
|
+
- **Input**: [What triggers this]
|
|
1537
|
+
- **Validation**: [What gets checked]
|
|
1538
|
+
- **Output**: [What proceeds]
|
|
1670
1539
|
|
|
1671
|
-
|
|
1672
|
-
|
|
1673
|
-
|
|
1674
|
-
|
|
1675
|
-
})
|
|
1540
|
+
### Step 2: Processing
|
|
1541
|
+
- **Actions**: [What happens]
|
|
1542
|
+
- **Dependencies**: [What's needed]
|
|
1543
|
+
- **Side Effects**: [What changes]
|
|
1676
1544
|
|
|
1677
|
-
|
|
1678
|
-
|
|
1545
|
+
### Step 3: Completion
|
|
1546
|
+
- **Success**: [What happens on success]
|
|
1547
|
+
- **Failure**: [What happens on failure]
|
|
1548
|
+
- **Notifications**: [Who gets notified]
|
|
1679
1549
|
|
|
1680
|
-
|
|
1681
|
-
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
1550
|
+
## Error Handling
|
|
1682
1551
|
|
|
1683
|
-
|
|
1684
|
-
|
|
1552
|
+
- **Error Type 1**: [Recovery strategy]
|
|
1553
|
+
- **Error Type 2**: [Recovery strategy]
|
|
1685
1554
|
|
|
1686
|
-
|
|
1687
|
-
• Technology Stack: Modern web stack
|
|
1688
|
-
• Design Patterns: MVC, Repository
|
|
1689
|
-
• Key Components: Listed in design doc
|
|
1690
|
-
• Data Flow: Documented
|
|
1555
|
+
## Rollback Strategy
|
|
1691
1556
|
|
|
1692
|
-
|
|
1693
|
-
• ${designFile}
|
|
1557
|
+
[How to undo if needed]
|
|
1694
1558
|
|
|
1695
|
-
|
|
1696
|
-
✅ Design ready for implementation!
|
|
1559
|
+
## Monitoring
|
|
1697
1560
|
|
|
1698
|
-
|
|
1561
|
+
- **Metrics**: [What to track]
|
|
1562
|
+
- **Alerts**: [When to alert]
|
|
1699
1563
|
|
|
1700
|
-
|
|
1701
|
-
|
|
1702
|
-
|
|
1703
|
-
}
|
|
1704
|
-
} catch (error) {
|
|
1705
|
-
await this.initializeAgent()
|
|
1706
|
-
return {
|
|
1707
|
-
success: false,
|
|
1708
|
-
message: this.agent.formatResponse(error.message, 'error'),
|
|
1709
|
-
}
|
|
1710
|
-
}
|
|
1564
|
+
---
|
|
1565
|
+
*Document edge cases and special scenarios.*
|
|
1566
|
+
`
|
|
1711
1567
|
}
|
|
1712
1568
|
|
|
1713
1569
|
/**
|
|
1714
|
-
*
|
|
1715
|
-
*
|
|
1716
|
-
* @param {string} [projectPath=process.cwd()] - Project path
|
|
1717
|
-
* @returns {Promise<Object>} Result object with success flag and message
|
|
1570
|
+
* /p:cleanup - Clean temp files and old entries
|
|
1571
|
+
* AGENTIC EXECUTION
|
|
1718
1572
|
*/
|
|
1719
|
-
async
|
|
1573
|
+
async cleanup(_options = {}, projectPath = process.cwd()) {
|
|
1720
1574
|
try {
|
|
1721
|
-
await this.
|
|
1575
|
+
const initResult = await this.ensureProjectInit(projectPath)
|
|
1576
|
+
if (!initResult.success) return initResult
|
|
1722
1577
|
|
|
1723
|
-
|
|
1724
|
-
const initCheck = await this.ensureProjectInit(projectPath)
|
|
1725
|
-
if (!initCheck.success) {
|
|
1726
|
-
return initCheck
|
|
1727
|
-
}
|
|
1578
|
+
console.log('🧹 Cleaning up project...\n')
|
|
1728
1579
|
|
|
1729
|
-
const
|
|
1580
|
+
const context = await contextBuilder.build(projectPath)
|
|
1581
|
+
const projectId = await configManager.getProjectId(projectPath)
|
|
1730
1582
|
|
|
1731
|
-
const
|
|
1732
|
-
const nowFile = await this.agent.readFile(nowFilePath)
|
|
1733
|
-
const currentTask = nowFile.split('\n')[0].replace('# NOW: ', '').replace('# NOW', 'None')
|
|
1583
|
+
const cleaned = []
|
|
1734
1584
|
|
|
1735
|
-
|
|
1736
|
-
|
|
1585
|
+
// Clean old memory entries (keep last 100)
|
|
1586
|
+
const memoryPath = pathManager.getFilePath(projectId, 'memory', 'context.jsonl')
|
|
1587
|
+
try {
|
|
1588
|
+
const entries = await jsonlHelper.readJsonLines(memoryPath)
|
|
1737
1589
|
|
|
1738
|
-
|
|
1739
|
-
|
|
1740
|
-
|
|
1741
|
-
|
|
1742
|
-
}
|
|
1743
|
-
|
|
1744
|
-
|
|
1745
|
-
|
|
1746
|
-
|
|
1747
|
-
|
|
1590
|
+
if (entries.length > 100) {
|
|
1591
|
+
const kept = entries.slice(-100)
|
|
1592
|
+
await jsonlHelper.writeJsonLines(memoryPath, kept)
|
|
1593
|
+
cleaned.push(`Memory: ${entries.length - 100} old entries removed`)
|
|
1594
|
+
} else {
|
|
1595
|
+
cleaned.push('Memory: No cleanup needed')
|
|
1596
|
+
}
|
|
1597
|
+
} catch {
|
|
1598
|
+
cleaned.push('Memory: No file found')
|
|
1599
|
+
}
|
|
1600
|
+
|
|
1601
|
+
// Clean empty ideas sections
|
|
1602
|
+
const ideasPath = context.paths.ideas
|
|
1603
|
+
try {
|
|
1604
|
+
const ideasContent = await toolRegistry.get('Read')(ideasPath)
|
|
1605
|
+
const sections = ideasContent.split('##').filter((s) => s.trim())
|
|
1606
|
+
|
|
1607
|
+
// Remove empty sections
|
|
1608
|
+
const nonEmpty = sections.filter((section) => {
|
|
1609
|
+
const lines = section
|
|
1748
1610
|
.trim()
|
|
1749
1611
|
.split('\n')
|
|
1750
|
-
.filter((l) => l)
|
|
1751
|
-
|
|
1752
|
-
|
|
1753
|
-
|
|
1754
|
-
|
|
1755
|
-
|
|
1612
|
+
.filter((l) => l.trim())
|
|
1613
|
+
return lines.length > 1 // Keep if has more than just title
|
|
1614
|
+
})
|
|
1615
|
+
|
|
1616
|
+
if (sections.length !== nonEmpty.length) {
|
|
1617
|
+
const newContent =
|
|
1618
|
+
'# IDEAS 💡\n\n## Brain Dump\n\n' +
|
|
1619
|
+
nonEmpty
|
|
1620
|
+
.slice(1)
|
|
1621
|
+
.map((s) => '## ' + s.trim())
|
|
1622
|
+
.join('\n\n')
|
|
1623
|
+
await toolRegistry.get('Write')(ideasPath, newContent)
|
|
1624
|
+
cleaned.push(`Ideas: ${sections.length - nonEmpty.length} empty sections removed`)
|
|
1625
|
+
} else {
|
|
1626
|
+
cleaned.push('Ideas: No cleanup needed')
|
|
1756
1627
|
}
|
|
1628
|
+
} catch {
|
|
1629
|
+
cleaned.push('Ideas: No file found')
|
|
1757
1630
|
}
|
|
1758
1631
|
|
|
1759
|
-
|
|
1760
|
-
|
|
1761
|
-
|
|
1762
|
-
|
|
1763
|
-
|
|
1764
|
-
`Recent actions:\n${recentActions.join('\n') || '• No recent actions'}\n\n` +
|
|
1765
|
-
`Use ${this.agentInfo.config.commandPrefix}recap for full progress report`
|
|
1632
|
+
// Clean completed tasks from next.md (optional - user decides)
|
|
1633
|
+
const nextPath = context.paths.next
|
|
1634
|
+
try {
|
|
1635
|
+
const nextContent = await toolRegistry.get('Read')(nextPath)
|
|
1636
|
+
const completedTasks = (nextContent.match(/\[x\]/gi) || []).length
|
|
1766
1637
|
|
|
1767
|
-
|
|
1768
|
-
|
|
1769
|
-
|
|
1638
|
+
if (completedTasks > 0) {
|
|
1639
|
+
cleaned.push(
|
|
1640
|
+
`Queue: ${completedTasks} completed tasks found (not removed - use /p:done to clear)`
|
|
1641
|
+
)
|
|
1642
|
+
} else {
|
|
1643
|
+
cleaned.push('Queue: No completed tasks')
|
|
1644
|
+
}
|
|
1645
|
+
} catch {
|
|
1646
|
+
cleaned.push('Queue: No file found')
|
|
1770
1647
|
}
|
|
1648
|
+
|
|
1649
|
+
console.log('✅ Cleanup complete!\n')
|
|
1650
|
+
cleaned.forEach((item) => console.log(` • ${item}`))
|
|
1651
|
+
|
|
1652
|
+
await this.logToMemory(projectPath, 'cleanup_performed', {
|
|
1653
|
+
items: cleaned.length,
|
|
1654
|
+
timestamp: dateHelper.getTimestamp(),
|
|
1655
|
+
})
|
|
1656
|
+
|
|
1657
|
+
return { success: true, cleaned }
|
|
1771
1658
|
} catch (error) {
|
|
1772
|
-
|
|
1773
|
-
return {
|
|
1774
|
-
success: false,
|
|
1775
|
-
message: this.agent.formatResponse(error.message, 'error'),
|
|
1776
|
-
}
|
|
1659
|
+
console.error('❌ Error:', error.message)
|
|
1660
|
+
return { success: false, error: error.message }
|
|
1777
1661
|
}
|
|
1778
1662
|
}
|
|
1779
1663
|
|
|
1780
1664
|
/**
|
|
1781
|
-
*
|
|
1782
|
-
*
|
|
1783
|
-
* @param {string} projectPath - Project path
|
|
1784
|
-
* @returns {Promise<string>} Project type description
|
|
1665
|
+
* /p:progress - Show metrics for period
|
|
1666
|
+
* AGENTIC EXECUTION
|
|
1785
1667
|
*/
|
|
1786
|
-
async
|
|
1787
|
-
|
|
1668
|
+
async progress(period = 'week', projectPath = process.cwd()) {
|
|
1669
|
+
try {
|
|
1670
|
+
const initResult = await this.ensureProjectInit(projectPath)
|
|
1671
|
+
if (!initResult.success) return initResult
|
|
1788
1672
|
|
|
1789
|
-
|
|
1790
|
-
|
|
1791
|
-
|
|
1792
|
-
const deps = { ...pkg.dependencies, ...pkg.devDependencies }
|
|
1793
|
-
|
|
1794
|
-
if (deps.next) return 'Next.js project'
|
|
1795
|
-
if (deps.react) return 'React project'
|
|
1796
|
-
if (deps.vue) return 'Vue project'
|
|
1797
|
-
if (deps.express) return 'Express project'
|
|
1798
|
-
return 'Node.js project'
|
|
1799
|
-
} catch (e) {
|
|
1800
|
-
return 'Node.js project'
|
|
1673
|
+
const validPeriods = ['day', 'week', 'month', 'all']
|
|
1674
|
+
if (!validPeriods.includes(period)) {
|
|
1675
|
+
period = 'week'
|
|
1801
1676
|
}
|
|
1802
|
-
}
|
|
1803
1677
|
|
|
1804
|
-
|
|
1805
|
-
if (files.includes('go.mod')) return 'Go project'
|
|
1806
|
-
if (files.includes('requirements.txt')) return 'Python project'
|
|
1807
|
-
if (files.includes('Gemfile')) return 'Ruby project'
|
|
1678
|
+
console.log(`📈 Progress Report (${period})\n`)
|
|
1808
1679
|
|
|
1809
|
-
|
|
1810
|
-
|
|
1680
|
+
const projectId = await configManager.getProjectId(projectPath)
|
|
1681
|
+
const memoryPath = pathManager.getFilePath(projectId, 'memory', 'context.jsonl')
|
|
1682
|
+
|
|
1683
|
+
// Calculate time range
|
|
1684
|
+
const now = new Date()
|
|
1685
|
+
let startDate
|
|
1686
|
+
|
|
1687
|
+
switch (period) {
|
|
1688
|
+
case 'day':
|
|
1689
|
+
startDate = dateHelper.getDaysAgo(1)
|
|
1690
|
+
break
|
|
1691
|
+
case 'week':
|
|
1692
|
+
startDate = dateHelper.getDaysAgo(7)
|
|
1693
|
+
break
|
|
1694
|
+
case 'month':
|
|
1695
|
+
startDate = dateHelper.getDaysAgo(30)
|
|
1696
|
+
break
|
|
1697
|
+
case 'all':
|
|
1698
|
+
startDate = new Date(0) // Beginning of time
|
|
1699
|
+
break
|
|
1700
|
+
}
|
|
1701
|
+
|
|
1702
|
+
// Read memory and filter by period
|
|
1703
|
+
let entries = []
|
|
1704
|
+
try {
|
|
1705
|
+
const allEntries = await jsonlHelper.readJsonLines(memoryPath)
|
|
1706
|
+
|
|
1707
|
+
entries = allEntries.filter((entry) => {
|
|
1708
|
+
const entryDate = new Date(entry.timestamp)
|
|
1709
|
+
return entryDate >= startDate
|
|
1710
|
+
})
|
|
1711
|
+
} catch {
|
|
1712
|
+
entries = []
|
|
1713
|
+
}
|
|
1714
|
+
|
|
1715
|
+
// Calculate metrics
|
|
1716
|
+
const metrics = {
|
|
1717
|
+
tasksStarted: entries.filter((e) => e.action === 'task_started').length,
|
|
1718
|
+
tasksCompleted: entries.filter((e) => e.action === 'task_completed').length,
|
|
1719
|
+
featuresPlanned: entries.filter((e) => e.action === 'feature_planned').length,
|
|
1720
|
+
featuresShipped: entries.filter((e) => e.action === 'feature_shipped').length,
|
|
1721
|
+
bugsReported: entries.filter((e) => e.action === 'bug_reported').length,
|
|
1722
|
+
designsCreated: entries.filter((e) => e.action === 'design_created').length,
|
|
1723
|
+
helpRequested: entries.filter((e) => e.action === 'help_requested').length,
|
|
1724
|
+
totalActions: entries.length,
|
|
1725
|
+
}
|
|
1726
|
+
|
|
1727
|
+
// Display metrics
|
|
1728
|
+
console.log('═══════════════════════════════════════════════════')
|
|
1729
|
+
console.log(
|
|
1730
|
+
` Period: ${period} (${startDate.toLocaleDateString()} - ${now.toLocaleDateString()})`
|
|
1731
|
+
)
|
|
1732
|
+
console.log('═══════════════════════════════════════════════════\n')
|
|
1733
|
+
|
|
1734
|
+
console.log('## Activity Summary\n')
|
|
1735
|
+
console.log(` Total Actions: ${metrics.totalActions}`)
|
|
1736
|
+
console.log(` Tasks Started: ${metrics.tasksStarted}`)
|
|
1737
|
+
console.log(` Tasks Completed: ${metrics.tasksCompleted}`)
|
|
1738
|
+
console.log(` Features Planned: ${metrics.featuresPlanned}`)
|
|
1739
|
+
console.log(` Features Shipped: ${metrics.featuresShipped}`)
|
|
1740
|
+
console.log(` Bugs Reported: ${metrics.bugsReported}`)
|
|
1741
|
+
console.log(` Designs Created: ${metrics.designsCreated}`)
|
|
1742
|
+
console.log(` Help Requested: ${metrics.helpRequested}\n`)
|
|
1743
|
+
|
|
1744
|
+
// Completion rate
|
|
1745
|
+
if (metrics.tasksStarted > 0) {
|
|
1746
|
+
const completionRate = Math.round((metrics.tasksCompleted / metrics.tasksStarted) * 100)
|
|
1747
|
+
console.log(`## Completion Rate: ${completionRate}%\n`)
|
|
1748
|
+
}
|
|
1749
|
+
|
|
1750
|
+
// Velocity
|
|
1751
|
+
const daysInPeriod =
|
|
1752
|
+
period === 'day' ? 1 : period === 'week' ? 7 : period === 'month' ? 30 : 365
|
|
1753
|
+
const tasksPerDay = (metrics.tasksCompleted / daysInPeriod).toFixed(1)
|
|
1754
|
+
console.log(`## Velocity: ${tasksPerDay} tasks/day\n`)
|
|
1755
|
+
|
|
1756
|
+
console.log('💡 Actions:')
|
|
1757
|
+
console.log('• /p:recap → See shipped features')
|
|
1758
|
+
console.log('• /p:context → View current state')
|
|
1759
|
+
console.log('• /p:progress day|week|month|all → Change period')
|
|
1760
|
+
|
|
1761
|
+
await this.logToMemory(projectPath, 'progress_viewed', {
|
|
1762
|
+
period,
|
|
1763
|
+
metrics,
|
|
1764
|
+
timestamp: dateHelper.getTimestamp(),
|
|
1765
|
+
})
|
|
1811
1766
|
|
|
1812
|
-
|
|
1813
|
-
|
|
1814
|
-
|
|
1815
|
-
|
|
1816
|
-
|
|
1817
|
-
*/
|
|
1818
|
-
getWeekNumber(date) {
|
|
1819
|
-
const firstDayOfYear = new Date(date.getFullYear(), 0, 1)
|
|
1820
|
-
const pastDaysOfYear = (date - firstDayOfYear) / 86400000
|
|
1821
|
-
return Math.ceil((pastDaysOfYear + firstDayOfYear.getDay() + 1) / 7)
|
|
1767
|
+
return { success: true, period, metrics }
|
|
1768
|
+
} catch (error) {
|
|
1769
|
+
console.error('❌ Error:', error.message)
|
|
1770
|
+
return { success: false, error: error.message }
|
|
1771
|
+
}
|
|
1822
1772
|
}
|
|
1823
1773
|
|
|
1824
1774
|
/**
|
|
1825
|
-
*
|
|
1826
|
-
*
|
|
1827
|
-
* @param {string} projectPath - Project path
|
|
1828
|
-
* @returns {Promise<number>} Days since last ship or Infinity if never shipped
|
|
1775
|
+
* /p:roadmap - Show roadmap with ASCII logic maps
|
|
1776
|
+
* AGENTIC EXECUTION
|
|
1829
1777
|
*/
|
|
1830
|
-
async
|
|
1778
|
+
async roadmap(projectPath = process.cwd()) {
|
|
1831
1779
|
try {
|
|
1832
|
-
await this.
|
|
1780
|
+
const initResult = await this.ensureProjectInit(projectPath)
|
|
1781
|
+
if (!initResult.success) return initResult
|
|
1833
1782
|
|
|
1834
|
-
|
|
1835
|
-
const projectId = await configManager.getProjectId(projectPath)
|
|
1836
|
-
const globalPath = pathManager.getGlobalProjectPath(projectId)
|
|
1837
|
-
const memoryFile = path.join(globalPath, 'memory', 'context.jsonl')
|
|
1838
|
-
const memory = await this.agent.readFile(memoryFile)
|
|
1839
|
-
const lines = memory
|
|
1840
|
-
.trim()
|
|
1841
|
-
.split('\n')
|
|
1842
|
-
.filter((l) => l)
|
|
1843
|
-
|
|
1844
|
-
for (let i = lines.length - 1; i >= 0; i--) {
|
|
1845
|
-
const entry = JSON.parse(lines[i])
|
|
1846
|
-
if (entry.action === 'ship') {
|
|
1847
|
-
const shipDate = new Date(entry.data.timestamp)
|
|
1848
|
-
const now = new Date()
|
|
1849
|
-
return Math.floor((now - shipDate) / 86400000)
|
|
1850
|
-
}
|
|
1851
|
-
}
|
|
1852
|
-
} catch (e) {
|
|
1853
|
-
}
|
|
1854
|
-
return Infinity
|
|
1855
|
-
}
|
|
1783
|
+
console.log('🗺️ Project Roadmap\n')
|
|
1856
1784
|
|
|
1857
|
-
|
|
1858
|
-
* Log action to memory system
|
|
1859
|
-
*
|
|
1860
|
-
* @param {string} projectPath - Project path
|
|
1861
|
-
* @param {string} action - Action type
|
|
1862
|
-
* @param {Object} data - Action data
|
|
1863
|
-
*/
|
|
1864
|
-
async logToMemory(projectPath, action, data) {
|
|
1865
|
-
await this.initializeAgent()
|
|
1866
|
-
await this.ensureAuthor()
|
|
1785
|
+
const context = await contextBuilder.build(projectPath)
|
|
1867
1786
|
|
|
1868
|
-
|
|
1787
|
+
// Read roadmap content
|
|
1788
|
+
const roadmapContent = await toolRegistry.get('Read')(context.paths.roadmap)
|
|
1869
1789
|
|
|
1870
|
-
|
|
1871
|
-
|
|
1872
|
-
|
|
1873
|
-
|
|
1874
|
-
|
|
1875
|
-
|
|
1790
|
+
if (!roadmapContent || roadmapContent.trim() === '# ROADMAP') {
|
|
1791
|
+
console.log('📝 No roadmap yet. Add features to build roadmap:\n')
|
|
1792
|
+
console.log('Example roadmap structure:')
|
|
1793
|
+
console.log(this._generateRoadmapTemplate())
|
|
1794
|
+
console.log('\n💡 Use /p:feature to add features')
|
|
1795
|
+
console.log(' Features are automatically added to roadmap')
|
|
1796
|
+
return { success: true, message: 'No roadmap' }
|
|
1876
1797
|
}
|
|
1877
1798
|
|
|
1878
|
-
|
|
1879
|
-
|
|
1880
|
-
|
|
1881
|
-
|
|
1882
|
-
|
|
1883
|
-
|
|
1884
|
-
|
|
1885
|
-
await this.
|
|
1799
|
+
// Display roadmap
|
|
1800
|
+
console.log(roadmapContent)
|
|
1801
|
+
|
|
1802
|
+
console.log('\n💡 Actions:')
|
|
1803
|
+
console.log('• /p:feature → Add new feature to roadmap')
|
|
1804
|
+
console.log('• /p:status → See implementation status')
|
|
1805
|
+
|
|
1806
|
+
await this.logToMemory(projectPath, 'roadmap_viewed', {
|
|
1807
|
+
timestamp: dateHelper.getTimestamp(),
|
|
1808
|
+
})
|
|
1809
|
+
|
|
1810
|
+
return { success: true, content: roadmapContent }
|
|
1811
|
+
} catch (error) {
|
|
1812
|
+
console.error('❌ Error:', error.message)
|
|
1813
|
+
return { success: false, error: error.message }
|
|
1886
1814
|
}
|
|
1887
1815
|
}
|
|
1888
1816
|
|
|
1889
1817
|
/**
|
|
1890
|
-
*
|
|
1891
|
-
*
|
|
1818
|
+
* Generate roadmap template
|
|
1892
1819
|
* @private
|
|
1893
|
-
* @param {string} projectPath - Project path
|
|
1894
|
-
* @param {string} action - Action type
|
|
1895
|
-
* @param {Object} data - Action data
|
|
1896
1820
|
*/
|
|
1897
|
-
|
|
1898
|
-
|
|
1899
|
-
|
|
1900
|
-
action,
|
|
1901
|
-
author: this.currentAuthor,
|
|
1902
|
-
data,
|
|
1903
|
-
timestamp: new Date().toISOString(),
|
|
1904
|
-
}) + '\n'
|
|
1821
|
+
_generateRoadmapTemplate() {
|
|
1822
|
+
return `
|
|
1823
|
+
# ROADMAP
|
|
1905
1824
|
|
|
1906
|
-
|
|
1907
|
-
|
|
1908
|
-
|
|
1909
|
-
|
|
1910
|
-
|
|
1911
|
-
|
|
1825
|
+
## Q1 2025 - Foundation
|
|
1826
|
+
|
|
1827
|
+
\`\`\`
|
|
1828
|
+
[Authentication] ──┐
|
|
1829
|
+
├──> [User Management] ──> [Dashboard]
|
|
1830
|
+
[Database Setup] ──┘
|
|
1831
|
+
\`\`\`
|
|
1832
|
+
|
|
1833
|
+
Status: 🟢 In Progress
|
|
1834
|
+
|
|
1835
|
+
## Q2 2025 - Core Features
|
|
1836
|
+
|
|
1837
|
+
\`\`\`
|
|
1838
|
+
[API v1] ──┐
|
|
1839
|
+
├──> [Integration] ──> [Beta Launch]
|
|
1840
|
+
[UI v2] ───┘
|
|
1841
|
+
\`\`\`
|
|
1842
|
+
|
|
1843
|
+
Status: ⏸️ Planned
|
|
1844
|
+
|
|
1845
|
+
## Dependencies
|
|
1846
|
+
|
|
1847
|
+
- Authentication → User Management
|
|
1848
|
+
- Database Setup → Authentication
|
|
1849
|
+
- API v1 + UI v2 → Integration
|
|
1850
|
+
`
|
|
1912
1851
|
}
|
|
1913
1852
|
|
|
1914
1853
|
/**
|
|
1915
|
-
*
|
|
1916
|
-
*
|
|
1917
|
-
*
|
|
1918
|
-
* @param {string} projectPath - Project path
|
|
1919
|
-
* @param {string} [period='week'] - Time period: 'day', 'week', 'month', 'all'
|
|
1920
|
-
* @param {string} [filename='context.jsonl'] - File to read from sessions
|
|
1921
|
-
* @returns {Promise<Array<Object>>} Consolidated entries
|
|
1854
|
+
* /p:status - KPI dashboard with ASCII graphics
|
|
1855
|
+
* AGENTIC EXECUTION
|
|
1922
1856
|
*/
|
|
1923
|
-
async
|
|
1924
|
-
|
|
1925
|
-
|
|
1926
|
-
|
|
1927
|
-
|
|
1928
|
-
|
|
1929
|
-
|
|
1930
|
-
|
|
1931
|
-
|
|
1932
|
-
|
|
1933
|
-
|
|
1934
|
-
|
|
1935
|
-
|
|
1936
|
-
|
|
1937
|
-
|
|
1938
|
-
|
|
1939
|
-
|
|
1940
|
-
|
|
1941
|
-
|
|
1942
|
-
|
|
1943
|
-
|
|
1944
|
-
|
|
1945
|
-
|
|
1946
|
-
|
|
1857
|
+
async status(projectPath = process.cwd()) {
|
|
1858
|
+
try {
|
|
1859
|
+
const initResult = await this.ensureProjectInit(projectPath)
|
|
1860
|
+
if (!initResult.success) return initResult
|
|
1861
|
+
|
|
1862
|
+
console.log('📊 Project Status Dashboard\n')
|
|
1863
|
+
|
|
1864
|
+
const context = await contextBuilder.build(projectPath)
|
|
1865
|
+
|
|
1866
|
+
// Read project data
|
|
1867
|
+
const nowContent = await toolRegistry.get('Read')(context.paths.now)
|
|
1868
|
+
const nextContent = await toolRegistry.get('Read')(context.paths.next)
|
|
1869
|
+
const shippedContent = await toolRegistry.get('Read')(context.paths.shipped)
|
|
1870
|
+
const ideasContent = await toolRegistry.get('Read')(context.paths.ideas)
|
|
1871
|
+
|
|
1872
|
+
// Calculate stats
|
|
1873
|
+
const stats = {
|
|
1874
|
+
activeTask: nowContent && !nowContent.includes('No current task'),
|
|
1875
|
+
tasksInQueue:
|
|
1876
|
+
nextContent
|
|
1877
|
+
?.split('\n')
|
|
1878
|
+
.filter((line) => line.trim().match(/^\d+\./) || line.includes('[ ]')).length || 0,
|
|
1879
|
+
featuresShipped:
|
|
1880
|
+
shippedContent
|
|
1881
|
+
?.split('##')
|
|
1882
|
+
.filter((section) => section.trim() && !section.includes('SHIPPED 🚀')).length || 0,
|
|
1883
|
+
ideasCaptured:
|
|
1884
|
+
ideasContent
|
|
1885
|
+
?.split('##')
|
|
1886
|
+
.filter(
|
|
1887
|
+
(section) =>
|
|
1888
|
+
section.trim() && !section.includes('IDEAS 💡') && !section.includes('Brain Dump')
|
|
1889
|
+
).length || 0,
|
|
1890
|
+
}
|
|
1891
|
+
|
|
1892
|
+
// Header
|
|
1893
|
+
console.log('═══════════════════════════════════════════════════')
|
|
1894
|
+
console.log(` ${path.basename(projectPath)} - Status Overview`)
|
|
1895
|
+
console.log('═══════════════════════════════════════════════════\n')
|
|
1896
|
+
|
|
1897
|
+
// Current Focus
|
|
1898
|
+
console.log('## 🎯 Current Focus\n')
|
|
1899
|
+
if (stats.activeTask) {
|
|
1900
|
+
const taskMatch = nowContent.match(/\*\*(.+?)\*\*/)
|
|
1901
|
+
const task = taskMatch ? taskMatch[1] : 'Active task'
|
|
1902
|
+
const startedMatch = nowContent.match(/Started: (.+)/)
|
|
1903
|
+
const started = startedMatch ? startedMatch[1] : 'Unknown'
|
|
1904
|
+
console.log(` 📌 ${task}`)
|
|
1905
|
+
console.log(` ⏱️ Started: ${started}\n`)
|
|
1906
|
+
} else {
|
|
1907
|
+
console.log(' No active task\n')
|
|
1908
|
+
}
|
|
1947
1909
|
|
|
1948
|
-
|
|
1949
|
-
|
|
1950
|
-
|
|
1910
|
+
// Queue Status
|
|
1911
|
+
console.log('## 📋 Queue Status\n')
|
|
1912
|
+
console.log(` Tasks in Queue: ${stats.tasksInQueue}`)
|
|
1913
|
+
this._renderProgressBar('Queue Load', stats.tasksInQueue, 20)
|
|
1914
|
+
console.log('')
|
|
1951
1915
|
|
|
1952
|
-
|
|
1953
|
-
|
|
1954
|
-
|
|
1916
|
+
// Shipped Features
|
|
1917
|
+
console.log('## 🚀 Shipped Features\n')
|
|
1918
|
+
console.log(` Features Shipped: ${stats.featuresShipped}`)
|
|
1919
|
+
this._renderProgressBar('Progress', stats.featuresShipped, 10)
|
|
1920
|
+
console.log('')
|
|
1955
1921
|
|
|
1956
|
-
|
|
1957
|
-
|
|
1958
|
-
|
|
1922
|
+
// Ideas Backlog
|
|
1923
|
+
console.log('## 💡 Ideas Backlog\n')
|
|
1924
|
+
console.log(` Ideas Captured: ${stats.ideasCaptured}`)
|
|
1925
|
+
this._renderProgressBar('Backlog', stats.ideasCaptured, 15)
|
|
1926
|
+
console.log('')
|
|
1959
1927
|
|
|
1960
|
-
|
|
1961
|
-
|
|
1962
|
-
|
|
1928
|
+
// Overall Health
|
|
1929
|
+
console.log('## 💚 Overall Health\n')
|
|
1930
|
+
const health = this._calculateHealth(stats)
|
|
1931
|
+
console.log(` Health Score: ${health.score}/100`)
|
|
1932
|
+
this._renderProgressBar('Health', health.score, 100)
|
|
1933
|
+
console.log(` ${health.message}\n`)
|
|
1934
|
+
|
|
1935
|
+
console.log('💡 Next steps:')
|
|
1936
|
+
console.log('• /p:now → Start working on a task')
|
|
1937
|
+
console.log('• /p:feature → Add new feature')
|
|
1938
|
+
console.log('• /p:ship → Ship completed work')
|
|
1939
|
+
|
|
1940
|
+
await this.logToMemory(projectPath, 'status_viewed', {
|
|
1941
|
+
stats,
|
|
1942
|
+
health: health.score,
|
|
1943
|
+
timestamp: dateHelper.getTimestamp(),
|
|
1944
|
+
})
|
|
1963
1945
|
|
|
1964
|
-
|
|
1965
|
-
|
|
1966
|
-
|
|
1967
|
-
return
|
|
1946
|
+
return { success: true, stats, health }
|
|
1947
|
+
} catch (error) {
|
|
1948
|
+
console.error('❌ Error:', error.message)
|
|
1949
|
+
return { success: false, error: error.message }
|
|
1968
1950
|
}
|
|
1969
1951
|
}
|
|
1970
1952
|
|
|
1971
1953
|
/**
|
|
1972
|
-
*
|
|
1973
|
-
*
|
|
1954
|
+
* Render ASCII progress bar
|
|
1974
1955
|
* @private
|
|
1975
|
-
* @param {string} projectPath - Project path
|
|
1976
|
-
* @param {string} filename - Filename to read
|
|
1977
|
-
* @returns {Promise<Array<Object>>} Parsed entries
|
|
1978
1956
|
*/
|
|
1979
|
-
|
|
1980
|
-
const
|
|
1981
|
-
|
|
1982
|
-
|
|
1983
|
-
|
|
1984
|
-
|
|
1985
|
-
|
|
1986
|
-
|
|
1987
|
-
return JSON.parse(line)
|
|
1988
|
-
} catch {
|
|
1989
|
-
return null
|
|
1990
|
-
}
|
|
1991
|
-
}).filter(Boolean)
|
|
1992
|
-
} catch {
|
|
1993
|
-
return []
|
|
1994
|
-
}
|
|
1957
|
+
_renderProgressBar(label, value, max) {
|
|
1958
|
+
const percentage = Math.min(100, Math.round((value / max) * 100))
|
|
1959
|
+
const barLength = 30
|
|
1960
|
+
const filled = Math.round((percentage / 100) * barLength)
|
|
1961
|
+
const empty = barLength - filled
|
|
1962
|
+
|
|
1963
|
+
const bar = '█'.repeat(filled) + '░'.repeat(empty)
|
|
1964
|
+
console.log(` ${label}: [${bar}] ${percentage}%`)
|
|
1995
1965
|
}
|
|
1996
1966
|
|
|
1997
1967
|
/**
|
|
1998
|
-
*
|
|
1999
|
-
*
|
|
2000
|
-
* @param {string} projectPath - Project path
|
|
2001
|
-
* @param {number} [days=7] - Number of days to look back
|
|
2002
|
-
* @returns {Promise<Array<Object>>} Recent log entries
|
|
1968
|
+
* Calculate project health score
|
|
1969
|
+
* @private
|
|
2003
1970
|
*/
|
|
2004
|
-
|
|
2005
|
-
|
|
1971
|
+
_calculateHealth(stats) {
|
|
1972
|
+
let score = 50 // Base score
|
|
2006
1973
|
|
|
2007
|
-
|
|
2008
|
-
|
|
2009
|
-
|
|
2010
|
-
|
|
2011
|
-
|
|
1974
|
+
// Active task is good
|
|
1975
|
+
if (stats.activeTask) score += 20
|
|
1976
|
+
|
|
1977
|
+
// Having tasks but not too many
|
|
1978
|
+
if (stats.tasksInQueue > 0 && stats.tasksInQueue < 15) score += 15
|
|
1979
|
+
if (stats.tasksInQueue >= 15) score -= 5 // Too many tasks
|
|
1980
|
+
|
|
1981
|
+
// Shipped features is great
|
|
1982
|
+
score += Math.min(20, stats.featuresShipped * 5)
|
|
1983
|
+
|
|
1984
|
+
// Ideas are good but not critical
|
|
1985
|
+
score += Math.min(10, stats.ideasCaptured * 2)
|
|
1986
|
+
|
|
1987
|
+
score = Math.max(0, Math.min(100, score))
|
|
1988
|
+
|
|
1989
|
+
let message = ''
|
|
1990
|
+
if (score >= 80) message = '🟢 Excellent - Great momentum!'
|
|
1991
|
+
else if (score >= 60) message = '🟡 Good - Keep shipping!'
|
|
1992
|
+
else if (score >= 40) message = '🟠 Fair - Need more activity'
|
|
1993
|
+
else message = '🔴 Low - Time to get started!'
|
|
1994
|
+
|
|
1995
|
+
return { score, message }
|
|
2012
1996
|
}
|
|
2013
1997
|
|
|
2014
1998
|
/**
|
|
2015
|
-
*
|
|
2016
|
-
*
|
|
2017
|
-
* @param {string} [projectPath=process.cwd()] - Project path
|
|
2018
|
-
* @returns {Promise<Object>} Result object with success flag and message
|
|
1999
|
+
* /p:build - Start task with agent assignment
|
|
2000
|
+
* AGENTIC EXECUTION
|
|
2019
2001
|
*/
|
|
2020
|
-
async
|
|
2002
|
+
async build(taskOrNumber, projectPath = process.cwd()) {
|
|
2021
2003
|
try {
|
|
2022
|
-
await this.
|
|
2023
|
-
|
|
2024
|
-
|
|
2025
|
-
|
|
2026
|
-
|
|
2027
|
-
|
|
2028
|
-
|
|
2029
|
-
|
|
2030
|
-
|
|
2031
|
-
|
|
2004
|
+
const initResult = await this.ensureProjectInit(projectPath)
|
|
2005
|
+
if (!initResult.success) return initResult
|
|
2006
|
+
|
|
2007
|
+
const context = await contextBuilder.build(projectPath, { task: taskOrNumber })
|
|
2008
|
+
|
|
2009
|
+
// Check if already working on something
|
|
2010
|
+
const nowContent = await toolRegistry.get('Read')(context.paths.now)
|
|
2011
|
+
if (nowContent && !nowContent.includes('No current task')) {
|
|
2012
|
+
console.log('⚠️ Already working on a task!')
|
|
2013
|
+
console.log(' Complete it with /p:done first\n')
|
|
2014
|
+
const taskMatch = nowContent.match(/\*\*(.+?)\*\*/)
|
|
2015
|
+
const currentTask = taskMatch ? taskMatch[1] : 'current task'
|
|
2016
|
+
console.log(` Current: ${currentTask}`)
|
|
2017
|
+
return { success: false, message: 'Task already active' }
|
|
2018
|
+
}
|
|
2019
|
+
|
|
2020
|
+
let task = taskOrNumber
|
|
2021
|
+
|
|
2022
|
+
// If number, get from queue
|
|
2023
|
+
if (!isNaN(taskOrNumber)) {
|
|
2024
|
+
const nextContent = await toolRegistry.get('Read')(context.paths.next)
|
|
2025
|
+
const tasks = nextContent
|
|
2026
|
+
.split('\n')
|
|
2027
|
+
.filter((line) => line.trim().match(/^\d+\./) || line.includes('[ ]'))
|
|
2028
|
+
|
|
2029
|
+
const index = parseInt(taskOrNumber) - 1
|
|
2030
|
+
if (index >= 0 && index < tasks.length) {
|
|
2031
|
+
task = tasks[index].replace(/^\d+\.\s*\[.\]\s*/, '').trim()
|
|
2032
|
+
console.log(`📋 Selected from queue: ${task}\n`)
|
|
2033
|
+
} else {
|
|
2034
|
+
console.log(`❌ Invalid task number. Queue has ${tasks.length} tasks.`)
|
|
2035
|
+
console.log(' Use /p:next to see queue')
|
|
2036
|
+
return { success: false, error: 'Invalid task number' }
|
|
2032
2037
|
}
|
|
2033
2038
|
}
|
|
2034
2039
|
|
|
2035
|
-
|
|
2036
|
-
|
|
2037
|
-
|
|
2038
|
-
|
|
2039
|
-
|
|
2040
|
-
let filesRemoved = 0
|
|
2041
|
-
let tasksArchived = 0
|
|
2042
|
-
|
|
2043
|
-
try {
|
|
2044
|
-
const tempDir = path.join(globalPath, 'temp')
|
|
2045
|
-
const tempFiles = await fs.readdir(tempDir).catch(() => [])
|
|
2046
|
-
for (const file of tempFiles) {
|
|
2047
|
-
const filePath = path.join(tempDir, file)
|
|
2048
|
-
const stats = await fs.stat(filePath)
|
|
2049
|
-
totalFreed += stats.size
|
|
2050
|
-
await fs.unlink(filePath)
|
|
2051
|
-
filesRemoved++
|
|
2052
|
-
}
|
|
2053
|
-
} catch (e) {
|
|
2040
|
+
if (!task) {
|
|
2041
|
+
console.log('❌ Task description required')
|
|
2042
|
+
console.log('Usage: /p:build "task description"')
|
|
2043
|
+
console.log(' or: /p:build 1 (select from queue)')
|
|
2044
|
+
return { success: false, error: 'Task required' }
|
|
2054
2045
|
}
|
|
2055
2046
|
|
|
2056
|
-
|
|
2057
|
-
const memoryFile = path.join(globalPath, 'memory', 'context.jsonl')
|
|
2058
|
-
const content = await this.agent.readFile(memoryFile)
|
|
2059
|
-
const lines = content.split('\n').filter(line => line.trim())
|
|
2060
|
-
const now = new Date()
|
|
2061
|
-
const thirtyDaysAgo = new Date(now.getTime() - 30 * 86400000)
|
|
2062
|
-
|
|
2063
|
-
const recentLines = []
|
|
2064
|
-
const archivedLines = []
|
|
2065
|
-
|
|
2066
|
-
for (const line of lines) {
|
|
2067
|
-
try {
|
|
2068
|
-
const entry = JSON.parse(line)
|
|
2069
|
-
const entryDate = new Date(entry.timestamp || entry.data?.timestamp)
|
|
2070
|
-
if (entryDate > thirtyDaysAgo) {
|
|
2071
|
-
recentLines.push(line)
|
|
2072
|
-
} else {
|
|
2073
|
-
archivedLines.push(line)
|
|
2074
|
-
}
|
|
2075
|
-
} catch {
|
|
2076
|
-
recentLines.push(line)
|
|
2077
|
-
}
|
|
2078
|
-
}
|
|
2047
|
+
console.log(`🏗️ Building: ${task}\n`)
|
|
2079
2048
|
|
|
2080
|
-
|
|
2081
|
-
|
|
2082
|
-
|
|
2083
|
-
await this.agent.writeFile(memoryFile, recentLines.join('\n') + '\n')
|
|
2084
|
-
tasksArchived = archivedLines.length
|
|
2085
|
-
}
|
|
2086
|
-
} catch (e) {
|
|
2087
|
-
}
|
|
2049
|
+
// Detect complexity and estimate
|
|
2050
|
+
const complexity = this._detectComplexity(task)
|
|
2051
|
+
const estimate = complexity.hours
|
|
2088
2052
|
|
|
2089
|
-
|
|
2090
|
-
|
|
2091
|
-
|
|
2092
|
-
|
|
2093
|
-
const stats = await fs.stat(filePath)
|
|
2094
|
-
if (stats.size === 0) {
|
|
2095
|
-
await fs.unlink(filePath)
|
|
2096
|
-
filesRemoved++
|
|
2097
|
-
}
|
|
2098
|
-
}
|
|
2099
|
-
}
|
|
2053
|
+
console.log('📊 Analysis:')
|
|
2054
|
+
console.log(` Complexity: ${complexity.level}`)
|
|
2055
|
+
console.log(` Estimated: ${estimate}h`)
|
|
2056
|
+
console.log(` Type: ${complexity.type}\n`)
|
|
2100
2057
|
|
|
2101
|
-
|
|
2102
|
-
|
|
2103
|
-
|
|
2104
|
-
const lines = content.split('\n')
|
|
2105
|
-
const now = new Date()
|
|
2106
|
-
const thirtyDaysAgo = new Date(now.getTime() - 30 * 86400000)
|
|
2107
|
-
|
|
2108
|
-
const filteredLines = lines.filter(line => {
|
|
2109
|
-
if (line.includes('✅')) {
|
|
2110
|
-
const dateMatch = line.match(/\((.*?)\)/)
|
|
2111
|
-
if (dateMatch) {
|
|
2112
|
-
const taskDate = new Date(dateMatch[1])
|
|
2113
|
-
if (taskDate < thirtyDaysAgo) {
|
|
2114
|
-
tasksArchived++
|
|
2115
|
-
return false
|
|
2116
|
-
}
|
|
2117
|
-
}
|
|
2118
|
-
}
|
|
2119
|
-
return true
|
|
2120
|
-
})
|
|
2058
|
+
// Auto-assign agent (simplified)
|
|
2059
|
+
const agent = this._autoAssignAgent(task)
|
|
2060
|
+
console.log(`🤖 Agent: ${agent}\n`)
|
|
2121
2061
|
|
|
2122
|
-
|
|
2123
|
-
|
|
2124
|
-
|
|
2062
|
+
// Set as current task with metadata
|
|
2063
|
+
const nowContentNew = `# NOW
|
|
2064
|
+
|
|
2065
|
+
**${task}**
|
|
2125
2066
|
|
|
2126
|
-
|
|
2067
|
+
Started: ${new Date().toLocaleString()}
|
|
2068
|
+
Estimated: ${estimate}h
|
|
2069
|
+
Complexity: ${complexity.level}
|
|
2070
|
+
Agent: ${agent}
|
|
2071
|
+
`
|
|
2072
|
+
await toolRegistry.get('Write')(context.paths.now, nowContentNew)
|
|
2127
2073
|
|
|
2128
|
-
|
|
2129
|
-
|
|
2130
|
-
|
|
2131
|
-
|
|
2132
|
-
|
|
2074
|
+
console.log('✅ Task started!\n')
|
|
2075
|
+
console.log('💡 Next steps:')
|
|
2076
|
+
console.log('• Start coding')
|
|
2077
|
+
console.log('• /p:done → Mark complete')
|
|
2078
|
+
console.log('• /p:stuck → Get help if needed')
|
|
2133
2079
|
|
|
2134
|
-
await this.logToMemory(projectPath, '
|
|
2135
|
-
|
|
2136
|
-
|
|
2137
|
-
|
|
2080
|
+
await this.logToMemory(projectPath, 'task_built', {
|
|
2081
|
+
task,
|
|
2082
|
+
complexity: complexity.level,
|
|
2083
|
+
estimate,
|
|
2084
|
+
agent,
|
|
2085
|
+
timestamp: dateHelper.getTimestamp(),
|
|
2138
2086
|
})
|
|
2139
2087
|
|
|
2140
|
-
return {
|
|
2141
|
-
success: true,
|
|
2142
|
-
message: this.agent.formatResponse(message, 'success'),
|
|
2143
|
-
}
|
|
2088
|
+
return { success: true, task, complexity, estimate, agent }
|
|
2144
2089
|
} catch (error) {
|
|
2145
|
-
|
|
2146
|
-
return {
|
|
2147
|
-
success: false,
|
|
2148
|
-
message: this.agent.formatResponse(`Cleanup failed: ${error.message}`, 'error'),
|
|
2149
|
-
}
|
|
2090
|
+
console.error('❌ Error:', error.message)
|
|
2091
|
+
return { success: false, error: error.message }
|
|
2150
2092
|
}
|
|
2151
2093
|
}
|
|
2152
2094
|
|
|
2153
2095
|
/**
|
|
2154
|
-
*
|
|
2155
|
-
*
|
|
2156
|
-
* @param {Object} [options={}] - Migration options
|
|
2157
|
-
* @returns {Promise<Object>} Result object with summary
|
|
2096
|
+
* Detect task complexity
|
|
2097
|
+
* @private
|
|
2158
2098
|
*/
|
|
2159
|
-
|
|
2160
|
-
|
|
2161
|
-
|
|
2099
|
+
_detectComplexity(task) {
|
|
2100
|
+
const lowerTask = task.toLowerCase()
|
|
2101
|
+
|
|
2102
|
+
// Type detection
|
|
2103
|
+
let type = 'general'
|
|
2104
|
+
if (lowerTask.includes('fix') || lowerTask.includes('bug')) type = 'bugfix'
|
|
2105
|
+
else if (lowerTask.includes('test')) type = 'testing'
|
|
2106
|
+
else if (lowerTask.includes('refactor')) type = 'refactoring'
|
|
2107
|
+
else if (lowerTask.includes('implement') || lowerTask.includes('add')) type = 'feature'
|
|
2108
|
+
else if (lowerTask.includes('design')) type = 'design'
|
|
2109
|
+
|
|
2110
|
+
// Complexity indicators
|
|
2111
|
+
const complexityIndicators = {
|
|
2112
|
+
high: ['architecture', 'redesign', 'migration', 'integration', 'authentication', 'database'],
|
|
2113
|
+
medium: ['api', 'component', 'service', 'endpoint', 'feature'],
|
|
2114
|
+
low: ['fix', 'update', 'modify', 'adjust', 'tweak'],
|
|
2115
|
+
}
|
|
2162
2116
|
|
|
2163
|
-
|
|
2164
|
-
|
|
2165
|
-
|
|
2166
|
-
|
|
2167
|
-
|
|
2168
|
-
|
|
2169
|
-
|
|
2170
|
-
if (update.phase === 'scanning') {
|
|
2171
|
-
console.log(`🔍 ${update.message}`)
|
|
2172
|
-
} else if (update.phase === 'checking' || update.phase === 'migrating') {
|
|
2173
|
-
console.log(` ${update.message}`)
|
|
2174
|
-
}
|
|
2117
|
+
let level = 'medium'
|
|
2118
|
+
let hours = 4
|
|
2119
|
+
|
|
2120
|
+
for (const [levelKey, indicators] of Object.entries(complexityIndicators)) {
|
|
2121
|
+
if (indicators.some((indicator) => lowerTask.includes(indicator))) {
|
|
2122
|
+
level = levelKey
|
|
2123
|
+
break
|
|
2175
2124
|
}
|
|
2125
|
+
}
|
|
2176
2126
|
|
|
2177
|
-
|
|
2178
|
-
|
|
2179
|
-
|
|
2180
|
-
|
|
2181
|
-
onProgress,
|
|
2182
|
-
})
|
|
2127
|
+
// Estimate hours
|
|
2128
|
+
if (level === 'high') hours = 8
|
|
2129
|
+
else if (level === 'medium') hours = 4
|
|
2130
|
+
else hours = 2
|
|
2183
2131
|
|
|
2184
|
-
|
|
2132
|
+
return { level, hours, type }
|
|
2133
|
+
}
|
|
2185
2134
|
|
|
2186
|
-
|
|
2187
|
-
|
|
2188
|
-
|
|
2189
|
-
|
|
2190
|
-
|
|
2191
|
-
|
|
2192
|
-
|
|
2193
|
-
|
|
2194
|
-
|
|
2195
|
-
|
|
2196
|
-
|
|
2135
|
+
/**
|
|
2136
|
+
* Auto-assign agent based on task
|
|
2137
|
+
* @private
|
|
2138
|
+
*/
|
|
2139
|
+
_autoAssignAgent(task) {
|
|
2140
|
+
const lowerTask = task.toLowerCase()
|
|
2141
|
+
|
|
2142
|
+
if (
|
|
2143
|
+
lowerTask.includes('ui') ||
|
|
2144
|
+
lowerTask.includes('component') ||
|
|
2145
|
+
lowerTask.includes('frontend')
|
|
2146
|
+
) {
|
|
2147
|
+
return 'frontend-specialist'
|
|
2148
|
+
}
|
|
2149
|
+
if (
|
|
2150
|
+
lowerTask.includes('api') ||
|
|
2151
|
+
lowerTask.includes('backend') ||
|
|
2152
|
+
lowerTask.includes('database')
|
|
2153
|
+
) {
|
|
2154
|
+
return 'backend-specialist'
|
|
2155
|
+
}
|
|
2156
|
+
if (lowerTask.includes('test')) {
|
|
2157
|
+
return 'qa-specialist'
|
|
2158
|
+
}
|
|
2159
|
+
if (lowerTask.includes('design') || lowerTask.includes('architecture')) {
|
|
2160
|
+
return 'architect'
|
|
2197
2161
|
}
|
|
2162
|
+
if (lowerTask.includes('deploy') || lowerTask.includes('docker')) {
|
|
2163
|
+
return 'devops-specialist'
|
|
2164
|
+
}
|
|
2165
|
+
|
|
2166
|
+
return 'generalist'
|
|
2198
2167
|
}
|
|
2199
2168
|
|
|
2200
2169
|
/**
|
|
2201
|
-
*
|
|
2202
|
-
*
|
|
2203
|
-
* @returns {Promise<Object>} Result object with success flag and message
|
|
2170
|
+
* /p:analyze - Analyze repository and generate summary
|
|
2171
|
+
* AGENTIC EXECUTION
|
|
2204
2172
|
*/
|
|
2205
|
-
async
|
|
2173
|
+
async analyze(options = {}, projectPath = process.cwd()) {
|
|
2206
2174
|
try {
|
|
2207
2175
|
await this.initializeAgent()
|
|
2208
2176
|
|
|
2209
|
-
|
|
2210
|
-
|
|
2211
|
-
|
|
2212
|
-
|
|
2213
|
-
|
|
2214
|
-
|
|
2215
|
-
|
|
2216
|
-
|
|
2217
|
-
|
|
2218
|
-
|
|
2219
|
-
|
|
2220
|
-
|
|
2221
|
-
|
|
2222
|
-
|
|
2223
|
-
|
|
2224
|
-
|
|
2225
|
-
|
|
2226
|
-
|
|
2227
|
-
|
|
2228
|
-
|
|
2229
|
-
|
|
2230
|
-
|
|
2231
|
-
|
|
2232
|
-
|
|
2233
|
-
|
|
2234
|
-
|
|
2235
|
-
|
|
2236
|
-
|
|
2237
|
-
|
|
2238
|
-
|
|
2239
|
-
|
|
2240
|
-
|
|
2241
|
-
|
|
2242
|
-
|
|
2243
|
-
|
|
2244
|
-
|
|
2245
|
-
|
|
2246
|
-
|
|
2247
|
-
|
|
2248
|
-
const
|
|
2249
|
-
|
|
2250
|
-
|
|
2251
|
-
|
|
2252
|
-
|
|
2253
|
-
|
|
2254
|
-
|
|
2255
|
-
|
|
2256
|
-
|
|
2257
|
-
|
|
2258
|
-
|
|
2259
|
-
|
|
2260
|
-
|
|
2261
|
-
|
|
2262
|
-
|
|
2263
|
-
|
|
2264
|
-
|
|
2265
|
-
|
|
2266
|
-
|
|
2267
|
-
if (errorCount > 0) {
|
|
2268
|
-
message += `\n⚠️ ${errorCount} command(s) had issues during installation`
|
|
2269
|
-
}
|
|
2177
|
+
console.log('🔍 Analyzing repository...\n')
|
|
2178
|
+
|
|
2179
|
+
// Initialize analyzer for this project
|
|
2180
|
+
const analyzer = require('./domain/analyzer')
|
|
2181
|
+
analyzer.init(projectPath)
|
|
2182
|
+
|
|
2183
|
+
// Build context
|
|
2184
|
+
const context = await contextBuilder.build(projectPath, options)
|
|
2185
|
+
|
|
2186
|
+
// Collect data using analyzer helpers (ZERO predetermined patterns)
|
|
2187
|
+
const analysisData = {
|
|
2188
|
+
// Package managers
|
|
2189
|
+
packageJson: await analyzer.readPackageJson(),
|
|
2190
|
+
cargoToml: await analyzer.readCargoToml(),
|
|
2191
|
+
goMod: await analyzer.readGoMod(),
|
|
2192
|
+
requirements: await analyzer.readRequirements(),
|
|
2193
|
+
|
|
2194
|
+
// Project structure
|
|
2195
|
+
directories: await analyzer.listDirectories(),
|
|
2196
|
+
fileCount: await analyzer.countFiles(),
|
|
2197
|
+
|
|
2198
|
+
// Git data
|
|
2199
|
+
gitStats: await analyzer.getGitStats(),
|
|
2200
|
+
gitLog: await analyzer.getGitLog(20),
|
|
2201
|
+
|
|
2202
|
+
// Common files
|
|
2203
|
+
hasDockerfile: await analyzer.fileExists('Dockerfile'),
|
|
2204
|
+
hasDockerCompose: await analyzer.fileExists('docker-compose.yml'),
|
|
2205
|
+
hasReadme: await analyzer.fileExists('README.md'),
|
|
2206
|
+
hasTsconfig: await analyzer.fileExists('tsconfig.json'),
|
|
2207
|
+
hasViteConfig:
|
|
2208
|
+
(await analyzer.fileExists('vite.config.ts')) ||
|
|
2209
|
+
(await analyzer.fileExists('vite.config.js')),
|
|
2210
|
+
hasNextConfig:
|
|
2211
|
+
(await analyzer.fileExists('next.config.js')) ||
|
|
2212
|
+
(await analyzer.fileExists('next.config.mjs')),
|
|
2213
|
+
}
|
|
2214
|
+
|
|
2215
|
+
// Generate summary (Claude decides what's relevant based on data found)
|
|
2216
|
+
const summary = this._generateAnalysisSummary(analysisData, projectPath)
|
|
2217
|
+
|
|
2218
|
+
// Save to analysis/repo-summary.md
|
|
2219
|
+
const summaryPath =
|
|
2220
|
+
context.paths.analysis ||
|
|
2221
|
+
pathManager.getFilePath(
|
|
2222
|
+
await configManager.getProjectId(projectPath),
|
|
2223
|
+
'analysis',
|
|
2224
|
+
'repo-summary.md'
|
|
2225
|
+
)
|
|
2226
|
+
|
|
2227
|
+
await toolRegistry.get('Write')(summaryPath, summary)
|
|
2228
|
+
|
|
2229
|
+
// Log to memory
|
|
2230
|
+
await this.logToMemory(projectPath, 'repository_analyzed', {
|
|
2231
|
+
timestamp: dateHelper.getTimestamp(),
|
|
2232
|
+
fileCount: analysisData.fileCount,
|
|
2233
|
+
gitCommits: analysisData.gitStats.totalCommits,
|
|
2234
|
+
})
|
|
2270
2235
|
|
|
2271
|
-
|
|
2272
|
-
|
|
2273
|
-
|
|
2274
|
-
|
|
2275
|
-
|
|
2236
|
+
console.log('✅ Analysis complete!\n')
|
|
2237
|
+
console.log('📄 Full report: analysis/repo-summary.md\n')
|
|
2238
|
+
console.log('Next steps:')
|
|
2239
|
+
console.log('• /p:sync → Generate agents based on stack')
|
|
2240
|
+
console.log('• /p:feature → Add a new feature')
|
|
2276
2241
|
|
|
2277
2242
|
return {
|
|
2278
2243
|
success: true,
|
|
2279
|
-
|
|
2244
|
+
summaryPath,
|
|
2245
|
+
data: analysisData,
|
|
2280
2246
|
}
|
|
2281
2247
|
} catch (error) {
|
|
2282
|
-
|
|
2283
|
-
return {
|
|
2284
|
-
success: false,
|
|
2285
|
-
message: this.agent.formatResponse(`Setup failed: ${error.message}`, 'error'),
|
|
2286
|
-
}
|
|
2248
|
+
console.error('❌ Error:', error.message)
|
|
2249
|
+
return { success: false, error: error.message }
|
|
2287
2250
|
}
|
|
2288
2251
|
}
|
|
2289
2252
|
|
|
2290
2253
|
/**
|
|
2291
|
-
*
|
|
2292
|
-
*
|
|
2293
|
-
* @
|
|
2294
|
-
* @returns {Promise<Object>} Result object with success flag and message
|
|
2254
|
+
* Generate analysis summary from collected data
|
|
2255
|
+
* Claude decides what's relevant - NO predetermined patterns
|
|
2256
|
+
* @private
|
|
2295
2257
|
*/
|
|
2296
|
-
|
|
2297
|
-
|
|
2298
|
-
|
|
2299
|
-
|
|
2300
|
-
|
|
2301
|
-
|
|
2302
|
-
|
|
2303
|
-
|
|
2304
|
-
|
|
2305
|
-
|
|
2306
|
-
|
|
2307
|
-
|
|
2308
|
-
|
|
2309
|
-
|
|
2310
|
-
|
|
2311
|
-
|
|
2312
|
-
|
|
2258
|
+
_generateAnalysisSummary(data, projectPath) {
|
|
2259
|
+
const lines = []
|
|
2260
|
+
|
|
2261
|
+
lines.push('# Repository Analysis\n')
|
|
2262
|
+
lines.push(`Generated: ${new Date().toLocaleString()}\n`)
|
|
2263
|
+
|
|
2264
|
+
// Project name from path
|
|
2265
|
+
const projectName = path.basename(projectPath)
|
|
2266
|
+
lines.push(`## Project: ${projectName}\n`)
|
|
2267
|
+
|
|
2268
|
+
// Technologies detected (based on what files exist)
|
|
2269
|
+
lines.push('## Stack Detected\n')
|
|
2270
|
+
|
|
2271
|
+
if (data.packageJson) {
|
|
2272
|
+
lines.push('### JavaScript/TypeScript\n')
|
|
2273
|
+
lines.push('- **Package Manager**: npm/yarn/pnpm')
|
|
2274
|
+
if (data.packageJson.dependencies) {
|
|
2275
|
+
const deps = Object.keys(data.packageJson.dependencies)
|
|
2276
|
+
if (deps.length > 0) {
|
|
2277
|
+
lines.push(
|
|
2278
|
+
`- **Dependencies**: ${deps.slice(0, 10).join(', ')}${deps.length > 10 ? ` (+${deps.length - 10} more)` : ''}`
|
|
2279
|
+
)
|
|
2313
2280
|
}
|
|
2314
2281
|
}
|
|
2282
|
+
if (data.hasNextConfig) lines.push('- **Framework**: Next.js detected')
|
|
2283
|
+
if (data.hasViteConfig) lines.push('- **Build Tool**: Vite detected')
|
|
2284
|
+
if (data.hasTsconfig) lines.push('- **Language**: TypeScript')
|
|
2285
|
+
lines.push('')
|
|
2286
|
+
}
|
|
2315
2287
|
|
|
2316
|
-
|
|
2317
|
-
|
|
2288
|
+
if (data.cargoToml) {
|
|
2289
|
+
lines.push('### Rust\n')
|
|
2290
|
+
lines.push('- **Package Manager**: Cargo')
|
|
2291
|
+
lines.push('- **Language**: Rust\n')
|
|
2292
|
+
}
|
|
2318
2293
|
|
|
2319
|
-
|
|
2320
|
-
|
|
2321
|
-
|
|
2322
|
-
|
|
2323
|
-
|
|
2324
|
-
report += '\nUse --force to reinstall'
|
|
2294
|
+
if (data.goMod) {
|
|
2295
|
+
lines.push('### Go\n')
|
|
2296
|
+
lines.push('- **Package Manager**: Go modules')
|
|
2297
|
+
lines.push('- **Language**: Go\n')
|
|
2298
|
+
}
|
|
2325
2299
|
|
|
2326
|
-
|
|
2327
|
-
|
|
2328
|
-
|
|
2329
|
-
|
|
2330
|
-
|
|
2300
|
+
if (data.requirements) {
|
|
2301
|
+
lines.push('### Python\n')
|
|
2302
|
+
lines.push('- **Package Manager**: pip')
|
|
2303
|
+
lines.push('- **Language**: Python\n')
|
|
2304
|
+
}
|
|
2331
2305
|
|
|
2332
|
-
|
|
2333
|
-
|
|
2334
|
-
|
|
2335
|
-
|
|
2336
|
-
|
|
2337
|
-
|
|
2338
|
-
|
|
2339
|
-
|
|
2340
|
-
|
|
2341
|
-
|
|
2342
|
-
|
|
2343
|
-
|
|
2306
|
+
// Project structure
|
|
2307
|
+
lines.push('## Structure\n')
|
|
2308
|
+
lines.push(`- **Total Files**: ${data.fileCount}`)
|
|
2309
|
+
lines.push(
|
|
2310
|
+
`- **Directories**: ${data.directories.slice(0, 15).join(', ')}${data.directories.length > 15 ? ` (+${data.directories.length - 15} more)` : ''}`
|
|
2311
|
+
)
|
|
2312
|
+
|
|
2313
|
+
if (data.hasDockerfile) lines.push('- **Docker**: Detected')
|
|
2314
|
+
if (data.hasDockerCompose) lines.push('- **Docker Compose**: Detected')
|
|
2315
|
+
if (data.hasReadme) lines.push('- **Documentation**: README.md found')
|
|
2316
|
+
lines.push('')
|
|
2317
|
+
|
|
2318
|
+
// Git stats
|
|
2319
|
+
lines.push('## Git Statistics\n')
|
|
2320
|
+
lines.push(`- **Total Commits**: ${data.gitStats.totalCommits}`)
|
|
2321
|
+
lines.push(`- **Contributors**: ${data.gitStats.contributors}`)
|
|
2322
|
+
lines.push(`- **Age**: ${data.gitStats.age}`)
|
|
2323
|
+
lines.push('')
|
|
2324
|
+
|
|
2325
|
+
// Recent activity (if available)
|
|
2326
|
+
if (data.gitLog) {
|
|
2327
|
+
lines.push('## Recent Activity\n')
|
|
2328
|
+
const logLines = data.gitLog.split('\n').slice(0, 5)
|
|
2329
|
+
logLines.forEach((line) => {
|
|
2330
|
+
if (line.trim()) {
|
|
2331
|
+
const [hash, , time, msg] = line.split('|')
|
|
2332
|
+
lines.push(`- \`${hash}\` ${msg} (${time})`)
|
|
2344
2333
|
}
|
|
2345
|
-
}
|
|
2346
|
-
|
|
2347
|
-
|
|
2348
|
-
const installedCount = installResult.installed?.length || 0
|
|
2349
|
-
const errorCount = installResult.errors?.length || 0
|
|
2350
|
-
|
|
2351
|
-
let report = `✅ Successfully ${force ? 'reinstalled' : 'installed'} ${installedCount} commands\n`
|
|
2352
|
-
report += ` Location: ${installResult.path}\n`
|
|
2334
|
+
})
|
|
2335
|
+
lines.push('')
|
|
2336
|
+
}
|
|
2353
2337
|
|
|
2354
|
-
|
|
2355
|
-
|
|
2356
|
-
|
|
2357
|
-
report += ` - ${err.file}: ${err.error}\n`
|
|
2358
|
-
})
|
|
2359
|
-
}
|
|
2338
|
+
// Recommendations
|
|
2339
|
+
lines.push('## Recommendations\n')
|
|
2340
|
+
lines.push('Based on detected stack, consider generating specialized agents using `/p:sync`.\n')
|
|
2360
2341
|
|
|
2361
|
-
|
|
2342
|
+
lines.push('---\n')
|
|
2343
|
+
lines.push(
|
|
2344
|
+
'*This analysis was generated automatically. For updated information, run `/p:analyze` again.*\n'
|
|
2345
|
+
)
|
|
2362
2346
|
|
|
2363
|
-
|
|
2364
|
-
success: true,
|
|
2365
|
-
message: this.agent.formatResponse(report, 'celebrate'),
|
|
2366
|
-
}
|
|
2367
|
-
} catch (error) {
|
|
2368
|
-
await this.initializeAgent()
|
|
2369
|
-
return {
|
|
2370
|
-
success: false,
|
|
2371
|
-
message: this.agent.formatResponse(`Installation failed: ${error.message}`, 'error'),
|
|
2372
|
-
}
|
|
2373
|
-
}
|
|
2347
|
+
return lines.join('\n')
|
|
2374
2348
|
}
|
|
2375
2349
|
|
|
2376
2350
|
/**
|
|
2377
|
-
*
|
|
2378
|
-
*
|
|
2379
|
-
* @param {Object} [options={}] - Analysis options
|
|
2380
|
-
* @param {string} [projectPath=process.cwd()] - Project path
|
|
2381
|
-
* @returns {Promise<Object>} Result object with analysis and sync results
|
|
2351
|
+
* /p:sync - Sync project state and generate dynamic agents
|
|
2352
|
+
* AGENTIC EXECUTION
|
|
2382
2353
|
*/
|
|
2383
|
-
async
|
|
2354
|
+
async sync(projectPath = process.cwd()) {
|
|
2384
2355
|
try {
|
|
2356
|
+
const initResult = await this.ensureProjectInit(projectPath)
|
|
2357
|
+
if (!initResult.success) return initResult
|
|
2358
|
+
|
|
2385
2359
|
await this.initializeAgent()
|
|
2386
2360
|
|
|
2387
|
-
|
|
2388
|
-
sync = false,
|
|
2389
|
-
reportOnly = false,
|
|
2390
|
-
silent = false,
|
|
2391
|
-
} = options
|
|
2361
|
+
console.log('🔄 Syncing project state...\n')
|
|
2392
2362
|
|
|
2393
|
-
|
|
2394
|
-
|
|
2395
|
-
}
|
|
2363
|
+
// Build context
|
|
2364
|
+
const context = await contextBuilder.build(projectPath)
|
|
2396
2365
|
|
|
2397
|
-
|
|
2366
|
+
// Step 1: Run analysis to get current state
|
|
2367
|
+
console.log('📊 Running analysis...')
|
|
2368
|
+
const analysisResult = await this.analyze({}, projectPath)
|
|
2398
2369
|
|
|
2399
|
-
|
|
2400
|
-
|
|
2401
|
-
|
|
2402
|
-
technologies: analysis.technologies.join(', '),
|
|
2403
|
-
fileCount: analysis.structure.fileCount,
|
|
2404
|
-
hasGit: analysis.gitHistory.hasGit,
|
|
2370
|
+
if (!analysisResult.success) {
|
|
2371
|
+
console.error('❌ Analysis failed')
|
|
2372
|
+
return analysisResult
|
|
2405
2373
|
}
|
|
2406
2374
|
|
|
2407
|
-
|
|
2408
|
-
|
|
2409
|
-
|
|
2410
|
-
|
|
2375
|
+
// Step 2: Read analysis/repo-summary.md
|
|
2376
|
+
const summaryContent = await toolRegistry.get('Read')(context.paths.analysis)
|
|
2377
|
+
|
|
2378
|
+
if (!summaryContent) {
|
|
2379
|
+
console.error('❌ No analysis found. Run /p:analyze first.')
|
|
2380
|
+
return { success: false, error: 'No analysis found' }
|
|
2411
2381
|
}
|
|
2412
2382
|
|
|
2413
|
-
|
|
2383
|
+
console.log('✅ Analysis loaded\n')
|
|
2414
2384
|
|
|
2415
|
-
|
|
2416
|
-
|
|
2417
|
-
|
|
2418
|
-
|
|
2419
|
-
|
|
2420
|
-
|
|
2421
|
-
|
|
2422
|
-
|
|
2423
|
-
|
|
2385
|
+
// Step 3: Generate dynamic agents based on stack detected
|
|
2386
|
+
// Claude reads the summary and decides what specialists to create
|
|
2387
|
+
console.log('🤖 Generating specialized agents...\n')
|
|
2388
|
+
|
|
2389
|
+
const projectId = await configManager.getProjectId(projectPath)
|
|
2390
|
+
const AgentGenerator = require('./domain/agent-generator')
|
|
2391
|
+
const generator = new AgentGenerator(projectId)
|
|
2392
|
+
|
|
2393
|
+
const generatedAgents = await this._generateAgentsFromAnalysis(summaryContent, generator)
|
|
2394
|
+
|
|
2395
|
+
// Step 4: Log to memory
|
|
2396
|
+
await this.logToMemory(projectPath, 'agents_generated', {
|
|
2397
|
+
timestamp: dateHelper.getTimestamp(),
|
|
2398
|
+
agents: generatedAgents,
|
|
2399
|
+
count: generatedAgents.length,
|
|
2400
|
+
})
|
|
2401
|
+
|
|
2402
|
+
console.log('\n✅ Sync complete!\n')
|
|
2403
|
+
console.log(`🤖 Agents Generated: ${generatedAgents.length}`)
|
|
2404
|
+
generatedAgents.forEach((agent) => {
|
|
2405
|
+
console.log(` • ${agent}`)
|
|
2406
|
+
})
|
|
2407
|
+
console.log('\n📋 Based on: analysis/repo-summary.md')
|
|
2408
|
+
console.log('💡 See templates/agents/AGENTS.md for reference\n')
|
|
2409
|
+
console.log('Next steps:')
|
|
2410
|
+
console.log('• /p:context → View project state')
|
|
2411
|
+
console.log('• /p:feature → Add a feature')
|
|
2424
2412
|
|
|
2425
2413
|
return {
|
|
2426
2414
|
success: true,
|
|
2427
|
-
|
|
2428
|
-
analysis,
|
|
2429
|
-
syncResults,
|
|
2415
|
+
agents: generatedAgents,
|
|
2430
2416
|
}
|
|
2431
2417
|
} catch (error) {
|
|
2432
|
-
|
|
2433
|
-
return {
|
|
2434
|
-
success: false,
|
|
2435
|
-
message: this.agent.formatResponse(`Analysis failed: ${error.message}`, 'error'),
|
|
2436
|
-
}
|
|
2418
|
+
console.error('❌ Error:', error.message)
|
|
2419
|
+
return { success: false, error: error.message }
|
|
2437
2420
|
}
|
|
2438
2421
|
}
|
|
2439
2422
|
|
|
2440
2423
|
/**
|
|
2441
|
-
*
|
|
2442
|
-
*
|
|
2443
|
-
* @
|
|
2444
|
-
* @param {Object} analysis - Full analysis results
|
|
2445
|
-
* @returns {string} Formatted report
|
|
2424
|
+
* Generate agents dynamically from analysis summary
|
|
2425
|
+
* Claude decides based on what technologies are detected
|
|
2426
|
+
* @private
|
|
2446
2427
|
*/
|
|
2447
|
-
|
|
2448
|
-
|
|
2449
|
-
|
|
2450
|
-
|
|
2451
|
-
|
|
2452
|
-
|
|
2453
|
-
|
|
2454
|
-
|
|
2455
|
-
|
|
2456
|
-
|
|
2457
|
-
|
|
2458
|
-
|
|
2459
|
-
|
|
2460
|
-
|
|
2461
|
-
|
|
2462
|
-
|
|
2428
|
+
async _generateAgentsFromAnalysis(summaryContent, generator) {
|
|
2429
|
+
const agents = []
|
|
2430
|
+
|
|
2431
|
+
// Parse summary to identify technologies
|
|
2432
|
+
// Simple detection based on sections (NOT predetermined patterns)
|
|
2433
|
+
|
|
2434
|
+
// Detect languages/frameworks from summary
|
|
2435
|
+
const hasJavaScript =
|
|
2436
|
+
summaryContent.includes('JavaScript') || summaryContent.includes('TypeScript')
|
|
2437
|
+
const hasNextJS = summaryContent.includes('Next.js')
|
|
2438
|
+
const hasVite = summaryContent.includes('Vite')
|
|
2439
|
+
const hasReact = summaryContent.includes('react')
|
|
2440
|
+
const hasRust = summaryContent.includes('Rust')
|
|
2441
|
+
const hasGo = summaryContent.includes('Go')
|
|
2442
|
+
const hasPython = summaryContent.includes('Python')
|
|
2443
|
+
const hasDocker = summaryContent.includes('Docker')
|
|
2444
|
+
|
|
2445
|
+
// Generate agents based on detected stack
|
|
2446
|
+
// Each agent is specific to THIS project's stack
|
|
2447
|
+
|
|
2448
|
+
if (hasJavaScript || hasNextJS || hasVite) {
|
|
2449
|
+
await generator.generateDynamicAgent('frontend-specialist', {
|
|
2450
|
+
role: 'Frontend Development Specialist',
|
|
2451
|
+
expertise: `${hasNextJS ? 'Next.js' : hasVite ? 'Vite' : 'JavaScript/TypeScript'}, ${hasReact ? 'React' : 'Modern JavaScript frameworks'}`,
|
|
2452
|
+
responsibilities:
|
|
2453
|
+
'Handle UI components, state management, routing, and frontend architecture',
|
|
2454
|
+
projectContext: {
|
|
2455
|
+
detectedFramework: hasNextJS ? 'Next.js' : hasVite ? 'Vite + React' : 'JavaScript',
|
|
2456
|
+
},
|
|
2457
|
+
})
|
|
2458
|
+
agents.push('frontend-specialist')
|
|
2459
|
+
}
|
|
2463
2460
|
|
|
2464
|
-
|
|
2461
|
+
if (hasRust) {
|
|
2462
|
+
await generator.generateDynamicAgent('rust-developer', {
|
|
2463
|
+
role: 'Rust Development Specialist',
|
|
2464
|
+
expertise: 'Rust, Cargo, performance optimization, memory safety',
|
|
2465
|
+
responsibilities:
|
|
2466
|
+
'Handle Rust codebase, performance-critical components, systems programming',
|
|
2467
|
+
projectContext: { language: 'Rust' },
|
|
2468
|
+
})
|
|
2469
|
+
agents.push('rust-developer')
|
|
2470
|
+
}
|
|
2465
2471
|
|
|
2466
|
-
|
|
2467
|
-
|
|
2468
|
-
|
|
2472
|
+
if (hasGo) {
|
|
2473
|
+
await generator.generateDynamicAgent('go-developer', {
|
|
2474
|
+
role: 'Go Development Specialist',
|
|
2475
|
+
expertise: 'Go, Go modules, concurrency, backend services',
|
|
2476
|
+
responsibilities: 'Handle Go codebase, backend services, API development',
|
|
2477
|
+
projectContext: { language: 'Go' },
|
|
2478
|
+
})
|
|
2479
|
+
agents.push('go-developer')
|
|
2480
|
+
}
|
|
2469
2481
|
|
|
2470
|
-
|
|
2471
|
-
|
|
2472
|
-
|
|
2473
|
-
|
|
2474
|
-
|
|
2475
|
-
|
|
2476
|
-
|
|
2477
|
-
|
|
2478
|
-
|
|
2479
|
-
🔍 Analysis & Sync Complete
|
|
2482
|
+
if (hasPython) {
|
|
2483
|
+
await generator.generateDynamicAgent('python-developer', {
|
|
2484
|
+
role: 'Python Development Specialist',
|
|
2485
|
+
expertise: 'Python, pip, Django/Flask, data processing',
|
|
2486
|
+
responsibilities: 'Handle Python codebase, backend logic, data processing',
|
|
2487
|
+
projectContext: { language: 'Python' },
|
|
2488
|
+
})
|
|
2489
|
+
agents.push('python-developer')
|
|
2490
|
+
}
|
|
2480
2491
|
|
|
2481
|
-
|
|
2482
|
-
|
|
2483
|
-
|
|
2492
|
+
if (hasDocker) {
|
|
2493
|
+
await generator.generateDynamicAgent('devops-specialist', {
|
|
2494
|
+
role: 'DevOps & Infrastructure Specialist',
|
|
2495
|
+
expertise: 'Docker, Docker Compose, containerization, deployment',
|
|
2496
|
+
responsibilities: 'Handle containerization, deployment, infrastructure setup',
|
|
2497
|
+
projectContext: { hasDocker: true },
|
|
2498
|
+
})
|
|
2499
|
+
agents.push('devops-specialist')
|
|
2500
|
+
}
|
|
2484
2501
|
|
|
2485
|
-
|
|
2486
|
-
|
|
2487
|
-
|
|
2488
|
-
|
|
2502
|
+
// Always generate a QA specialist if we have code
|
|
2503
|
+
await generator.generateDynamicAgent('qa-specialist', {
|
|
2504
|
+
role: 'Quality Assurance Specialist',
|
|
2505
|
+
expertise: 'Testing frameworks, test automation, quality metrics',
|
|
2506
|
+
responsibilities: 'Handle testing strategy, test creation, quality assurance',
|
|
2507
|
+
projectContext: { role: 'QA' },
|
|
2508
|
+
})
|
|
2509
|
+
agents.push('qa-specialist')
|
|
2489
2510
|
|
|
2490
|
-
|
|
2491
|
-
`
|
|
2511
|
+
return agents
|
|
2492
2512
|
}
|
|
2493
2513
|
|
|
2494
|
-
|
|
2495
|
-
|
|
2496
|
-
|
|
2497
|
-
* @param {string} projectPath - Project path
|
|
2498
|
-
* @returns {Promise<boolean>} True if project has significant existing code
|
|
2499
|
-
*/
|
|
2500
|
-
async detectExistingCode(projectPath) {
|
|
2501
|
-
try {
|
|
2502
|
-
const packagePath = path.join(projectPath, 'package.json')
|
|
2503
|
-
try {
|
|
2504
|
-
const content = await fs.readFile(packagePath, 'utf-8')
|
|
2505
|
-
const pkg = JSON.parse(content)
|
|
2506
|
-
|
|
2507
|
-
if (pkg.dependencies && Object.keys(pkg.dependencies).length > 0) {
|
|
2508
|
-
return true
|
|
2509
|
-
}
|
|
2510
|
-
} catch {
|
|
2511
|
-
}
|
|
2514
|
+
async start() {
|
|
2515
|
+
return this._notImplemented('start')
|
|
2516
|
+
}
|
|
2512
2517
|
|
|
2513
|
-
|
|
2514
|
-
|
|
2515
|
-
|
|
2516
|
-
if (commitCount > 0) {
|
|
2517
|
-
return true
|
|
2518
|
-
}
|
|
2519
|
-
} catch {
|
|
2520
|
-
}
|
|
2518
|
+
async setup() {
|
|
2519
|
+
return this._notImplemented('setup')
|
|
2520
|
+
}
|
|
2521
2521
|
|
|
2522
|
-
|
|
2523
|
-
|
|
2522
|
+
async migrateAll() {
|
|
2523
|
+
return this._notImplemented('migrateAll')
|
|
2524
|
+
}
|
|
2524
2525
|
|
|
2525
|
-
|
|
2526
|
-
|
|
2527
|
-
|
|
2528
|
-
continue
|
|
2529
|
-
}
|
|
2526
|
+
async workflow() {
|
|
2527
|
+
return this._notImplemented('workflow')
|
|
2528
|
+
}
|
|
2530
2529
|
|
|
2531
|
-
|
|
2532
|
-
|
|
2533
|
-
|
|
2534
|
-
}
|
|
2535
|
-
}
|
|
2530
|
+
async architectExecute() {
|
|
2531
|
+
return this._notImplemented('architectExecute')
|
|
2532
|
+
}
|
|
2536
2533
|
|
|
2537
|
-
|
|
2538
|
-
}
|
|
2539
|
-
|
|
2540
|
-
|
|
2534
|
+
_notImplemented(commandName) {
|
|
2535
|
+
console.log(`❌ /p:${commandName} not yet migrated to agentic architecture`)
|
|
2536
|
+
console.log('\nMigrated commands (18 total):')
|
|
2537
|
+
console.log('\n Sprint 1 - CRITICAL (9 commands):')
|
|
2538
|
+
console.log(' • /p:init - Initialize project')
|
|
2539
|
+
console.log(' • /p:analyze - Analyze repository')
|
|
2540
|
+
console.log(' • /p:sync - Generate agents')
|
|
2541
|
+
console.log(' • /p:feature - Add feature with roadmap')
|
|
2542
|
+
console.log(' • /p:bug - Report and track bugs')
|
|
2543
|
+
console.log(' • /p:now - Set current task')
|
|
2544
|
+
console.log(' • /p:done - Complete task')
|
|
2545
|
+
console.log(' • /p:next - Show queue')
|
|
2546
|
+
console.log(' • /p:ship - Ship feature')
|
|
2547
|
+
console.log('\n Sprint 2 - IMPORTANT (4 commands):')
|
|
2548
|
+
console.log(' • /p:context - Show project context')
|
|
2549
|
+
console.log(' • /p:recap - Show progress overview')
|
|
2550
|
+
console.log(' • /p:stuck - Get contextual help')
|
|
2551
|
+
console.log(' • /p:design - Design architecture/API/components')
|
|
2552
|
+
console.log('\n Sprint 3 - OPTIONAL (5 commands):')
|
|
2553
|
+
console.log(' • /p:cleanup - Clean temp files and old entries')
|
|
2554
|
+
console.log(' • /p:progress - Show metrics for period')
|
|
2555
|
+
console.log(' • /p:roadmap - Show roadmap with ASCII')
|
|
2556
|
+
console.log(' • /p:status - KPI dashboard with ASCII graphics')
|
|
2557
|
+
console.log(' • /p:build - Start task with agent assignment')
|
|
2558
|
+
return { success: false, message: `Command ${commandName} not yet migrated` }
|
|
2541
2559
|
}
|
|
2542
2560
|
}
|
|
2543
2561
|
|
|
2544
|
-
module.exports =
|
|
2562
|
+
module.exports = PrjctCommands
|