prjct-cli 0.6.0 → 0.7.1
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 +67 -6
- package/CLAUDE.md +442 -36
- package/README.md +47 -54
- package/bin/prjct +174 -240
- 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 +109 -65
- package/core/commands.js +2213 -2173
- 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} +5 -3
- 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 +4 -12
- package/templates/agents/AGENTS.md +101 -27
- package/templates/analysis/analyze.md +84 -0
- package/templates/commands/analyze.md +9 -2
- package/templates/commands/bug.md +79 -0
- package/templates/commands/build.md +5 -2
- package/templates/commands/cleanup.md +5 -2
- package/templates/commands/design.md +5 -2
- package/templates/commands/done.md +4 -2
- package/templates/commands/feature.md +113 -0
- package/templates/commands/fix.md +41 -10
- package/templates/commands/git.md +7 -2
- package/templates/commands/help.md +2 -2
- package/templates/commands/idea.md +14 -5
- package/templates/commands/init.md +62 -7
- package/templates/commands/next.md +4 -2
- package/templates/commands/now.md +4 -2
- package/templates/commands/progress.md +27 -5
- package/templates/commands/recap.md +39 -10
- package/templates/commands/roadmap.md +19 -5
- package/templates/commands/ship.md +118 -16
- package/templates/commands/status.md +4 -2
- package/templates/commands/sync.md +19 -15
- package/templates/commands/task.md +4 -2
- package/templates/commands/test.md +5 -2
- package/templates/commands/workflow.md +4 -2
- package/core/agent-generator.js +0 -525
- package/core/analyzer.js +0 -600
- package/core/animations.js +0 -277
- package/core/ascii-graphics.js +0 -433
- package/core/git-integration.js +0 -401
- package/core/task-schema.js +0 -342
- 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 -27
- package/templates/agents/coordinator.template.md +0 -34
- package/templates/agents/data.template.md +0 -27
- package/templates/agents/devops.template.md +0 -27
- package/templates/agents/fe.template.md +0 -27
- package/templates/agents/mobile.template.md +0 -27
- package/templates/agents/qa.template.md +0 -27
- package/templates/agents/scribe.template.md +0 -29
- package/templates/agents/security.template.md +0 -27
- package/templates/agents/ux.template.md +0 -27
- package/templates/commands/context.md +0 -36
- package/templates/commands/stuck.md +0 -36
- 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,36 +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 agentGenerator = require('./agent-generator')
|
|
8
|
-
const pathManager = require('./path-manager')
|
|
9
|
-
const configManager = require('./config-manager')
|
|
10
|
-
const authorDetector = require('./author-detector')
|
|
11
|
-
const migrator = require('./migrator')
|
|
12
|
-
const commandInstaller = require('./command-installer')
|
|
13
|
-
const sessionManager = require('./session-manager')
|
|
14
|
-
const analyzer = require('./analyzer')
|
|
15
|
-
const workflowEngine = require('./workflow-engine')
|
|
16
|
-
const workflowPrompts = require('./workflow-prompts')
|
|
17
|
-
const UpdateChecker = require('./update-checker')
|
|
18
|
-
const { VERSION } = require('./version')
|
|
19
|
-
|
|
20
|
-
let animations
|
|
21
|
-
try {
|
|
22
|
-
animations = require('./animations')
|
|
23
|
-
} catch (e) {
|
|
24
|
-
animations = null
|
|
25
|
-
}
|
|
26
20
|
|
|
27
|
-
|
|
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')
|
|
28
34
|
|
|
29
35
|
/**
|
|
30
|
-
*
|
|
31
|
-
*
|
|
32
|
-
* Manages project workflow commands including task tracking, shipping features,
|
|
33
|
-
* idea capture, and project analysis
|
|
36
|
+
* Agentic Commands - Template-driven execution
|
|
34
37
|
*/
|
|
35
38
|
class PrjctCommands {
|
|
36
39
|
constructor() {
|
|
@@ -40,2704 +43,2741 @@ class PrjctCommands {
|
|
|
40
43
|
this.prjctDir = '.prjct'
|
|
41
44
|
this.updateChecker = new UpdateChecker()
|
|
42
45
|
this.updateNotificationShown = false
|
|
46
|
+
this.commandExecutor = commandExecutor
|
|
43
47
|
}
|
|
44
48
|
|
|
45
49
|
/**
|
|
46
|
-
*
|
|
47
|
-
* Only shows once per session
|
|
50
|
+
* Initialize agent (Claude Code, Desktop, or Terminal)
|
|
48
51
|
*/
|
|
49
|
-
async
|
|
50
|
-
if (this.
|
|
51
|
-
return
|
|
52
|
-
}
|
|
52
|
+
async initializeAgent() {
|
|
53
|
+
if (this.agent) return this.agent
|
|
53
54
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
this.updateNotificationShown = true
|
|
59
|
-
}
|
|
60
|
-
} catch (error) {
|
|
61
|
-
// Fail silently - don't interrupt user workflow
|
|
55
|
+
this.agentInfo = await agentDetector.detect()
|
|
56
|
+
|
|
57
|
+
if (!this.agentInfo.isSupported) {
|
|
58
|
+
throw new Error('Unsupported agent. Please use Claude Code, Claude Desktop, or Terminal.')
|
|
62
59
|
}
|
|
63
|
-
}
|
|
64
60
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
* @returns {string} Branch name in format type/description
|
|
70
|
-
*/
|
|
71
|
-
generateBranchName(task) {
|
|
72
|
-
let branchType = 'chore'
|
|
73
|
-
|
|
74
|
-
const taskLower = task.toLowerCase()
|
|
75
|
-
|
|
76
|
-
if (taskLower.match(/^(add|implement|create|build|feature|new)/)) {
|
|
77
|
-
branchType = 'feat'
|
|
78
|
-
} else if (taskLower.match(/^(fix|resolve|repair|correct|bug|issue)/)) {
|
|
79
|
-
branchType = 'fix'
|
|
80
|
-
} else if (taskLower.match(/^(refactor|improve|optimize|enhance|cleanup|clean)/)) {
|
|
81
|
-
branchType = 'refactor'
|
|
82
|
-
} else if (taskLower.match(/^(document|docs|readme|update doc)/)) {
|
|
83
|
-
branchType = 'docs'
|
|
84
|
-
} else if (taskLower.match(/^(test|testing|spec|add test)/)) {
|
|
85
|
-
branchType = 'test'
|
|
86
|
-
} else if (taskLower.match(/^(style|format|lint)/)) {
|
|
87
|
-
branchType = 'style'
|
|
88
|
-
} else if (taskLower.match(/^(deploy|release|ci|cd|config)/)) {
|
|
89
|
-
branchType = 'chore'
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
const cleanDescription = task
|
|
93
|
-
.toLowerCase()
|
|
94
|
-
.replace(/[^a-z0-9\s-]/g, '')
|
|
95
|
-
.replace(/\s+/g, '-')
|
|
96
|
-
.replace(/-+/g, '-')
|
|
97
|
-
.replace(/^-|-$/g, '')
|
|
98
|
-
.slice(0, 50)
|
|
99
|
-
|
|
100
|
-
return `${branchType}/${cleanDescription}`
|
|
61
|
+
const Agent = require(`./infrastructure/agents/${this.agentInfo.type}-agent`)
|
|
62
|
+
this.agent = new Agent()
|
|
63
|
+
|
|
64
|
+
return this.agent
|
|
101
65
|
}
|
|
102
66
|
|
|
103
67
|
/**
|
|
104
|
-
*
|
|
105
|
-
*
|
|
106
|
-
* @param {string} command - Git command to execute
|
|
107
|
-
* @param {string} [cwd=process.cwd()] - Working directory
|
|
108
|
-
* @returns {Promise<Object>} Result object with success flag and output
|
|
68
|
+
* Ensure project is initialized
|
|
109
69
|
*/
|
|
110
|
-
async
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
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
|
|
116
81
|
}
|
|
82
|
+
|
|
83
|
+
console.log('✅ Project initialized!\n')
|
|
84
|
+
return { success: true }
|
|
117
85
|
}
|
|
118
86
|
|
|
119
87
|
/**
|
|
120
|
-
*
|
|
121
|
-
*
|
|
122
|
-
* @param {string} [projectPath=process.cwd()] - Project path to check
|
|
123
|
-
* @returns {Promise<boolean>} True if git repository
|
|
88
|
+
* Ensure author information is loaded
|
|
124
89
|
*/
|
|
125
|
-
async
|
|
126
|
-
|
|
127
|
-
|
|
90
|
+
async ensureAuthor() {
|
|
91
|
+
if (this.currentAuthor) return this.currentAuthor
|
|
92
|
+
this.currentAuthor = await authorDetector.detectAuthorForLogs()
|
|
93
|
+
return this.currentAuthor
|
|
128
94
|
}
|
|
129
95
|
|
|
130
96
|
/**
|
|
131
|
-
*
|
|
132
|
-
*
|
|
133
|
-
* @param {string} branchName - Name of branch to create
|
|
134
|
-
* @param {string} [projectPath=process.cwd()] - Project path
|
|
135
|
-
* @returns {Promise<Object>} Result object with success flag and message
|
|
97
|
+
* Get global project path
|
|
136
98
|
*/
|
|
137
|
-
async
|
|
138
|
-
if (
|
|
139
|
-
|
|
99
|
+
async getGlobalProjectPath(projectPath) {
|
|
100
|
+
if (await migrator.needsMigration(projectPath)) {
|
|
101
|
+
throw new Error('Project needs migration. Run /p:migrate first.')
|
|
140
102
|
}
|
|
141
103
|
|
|
142
|
-
const
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
104
|
+
const projectId = await configManager.getProjectId(projectPath)
|
|
105
|
+
await pathManager.ensureProjectStructure(projectId)
|
|
106
|
+
return pathManager.getGlobalProjectPath(projectId)
|
|
107
|
+
}
|
|
146
108
|
|
|
147
|
-
|
|
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')
|
|
148
117
|
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
118
|
+
const entry = {
|
|
119
|
+
timestamp: dateHelper.getTimestamp(),
|
|
120
|
+
action,
|
|
121
|
+
data,
|
|
122
|
+
author: author.name,
|
|
153
123
|
}
|
|
154
|
-
return { success: true, message: `Switched to existing branch: ${branchName}`, existed: true }
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
const createResult = await this.execGitCommand(`git checkout -b ${branchName}`, projectPath)
|
|
158
|
-
|
|
159
|
-
if (!createResult.success) {
|
|
160
|
-
return { success: false, message: `Failed to create branch: ${createResult.error}` }
|
|
161
|
-
}
|
|
162
124
|
|
|
163
|
-
|
|
164
|
-
|
|
125
|
+
await jsonlHelper.appendJsonLine(memoryPath, entry)
|
|
126
|
+
} catch (error) {
|
|
127
|
+
// Non-critical - don't fail the command
|
|
165
128
|
}
|
|
166
|
-
|
|
167
|
-
return { success: true, message: `Created and switched to new branch: ${branchName}`, existed: false }
|
|
168
129
|
}
|
|
169
130
|
|
|
170
131
|
/**
|
|
171
|
-
*
|
|
172
|
-
*
|
|
173
|
-
*
|
|
174
|
-
* @returns {Promise<Object>} Initialized agent instance
|
|
132
|
+
* /p:now - Set or show current task
|
|
133
|
+
* AGENTIC EXECUTION
|
|
175
134
|
*/
|
|
176
|
-
async
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
135
|
+
async now(task = null, projectPath = process.cwd()) {
|
|
136
|
+
try {
|
|
137
|
+
const initResult = await this.ensureProjectInit(projectPath)
|
|
138
|
+
if (!initResult.success) return initResult
|
|
180
139
|
|
|
181
|
-
|
|
140
|
+
const context = await contextBuilder.build(projectPath, { task })
|
|
182
141
|
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
case 'codex':
|
|
188
|
-
Agent = require('./agents/codex-agent')
|
|
189
|
-
break
|
|
190
|
-
case 'terminal':
|
|
191
|
-
default:
|
|
192
|
-
Agent = require('./agents/terminal-agent')
|
|
193
|
-
break
|
|
194
|
-
}
|
|
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)
|
|
195
146
|
|
|
196
|
-
|
|
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')
|
|
197
151
|
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
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
|
+
}
|
|
202
168
|
|
|
203
|
-
|
|
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
|
+
}
|
|
204
176
|
}
|
|
205
177
|
|
|
206
178
|
/**
|
|
207
|
-
*
|
|
208
|
-
*
|
|
209
|
-
*
|
|
210
|
-
* @private
|
|
179
|
+
* /p:done - Complete current task
|
|
180
|
+
* AGENTIC EXECUTION
|
|
211
181
|
*/
|
|
212
|
-
async
|
|
182
|
+
async done(projectPath = process.cwd()) {
|
|
213
183
|
try {
|
|
214
|
-
const
|
|
184
|
+
const initResult = await this.ensureProjectInit(projectPath)
|
|
185
|
+
if (!initResult.success) return initResult
|
|
215
186
|
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
return
|
|
219
|
-
} catch {
|
|
220
|
-
}
|
|
187
|
+
const context = await contextBuilder.build(projectPath)
|
|
188
|
+
const nowContent = await toolRegistry.get('Read')(context.paths.now)
|
|
221
189
|
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
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
|
+
}
|
|
229
199
|
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
version: VERSION,
|
|
234
|
-
projectsFound: summary.totalFound,
|
|
235
|
-
projectsMigrated: summary.successfullyMigrated,
|
|
236
|
-
}), 'utf-8')
|
|
237
|
-
} catch (error) {
|
|
238
|
-
console.error('[prjct] Auto-migration error (non-blocking):', error.message)
|
|
239
|
-
}
|
|
240
|
-
}
|
|
200
|
+
// Extract task and duration
|
|
201
|
+
const taskMatch = nowContent.match(/\*\*(.+?)\*\*/)
|
|
202
|
+
const task = taskMatch ? taskMatch[1] : 'task'
|
|
241
203
|
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
if (this.currentAuthor) return this.currentAuthor
|
|
249
|
-
this.currentAuthor = await authorDetector.detectAuthorForLogs()
|
|
250
|
-
return this.currentAuthor
|
|
251
|
-
}
|
|
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
|
+
}
|
|
252
210
|
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
*
|
|
257
|
-
* @param {string} projectPath - Local project path
|
|
258
|
-
* @returns {Promise<{success: boolean, message?: string}>} Success or init result
|
|
259
|
-
*/
|
|
260
|
-
async ensureProjectInit(projectPath) {
|
|
261
|
-
if (await configManager.isConfigured(projectPath)) {
|
|
262
|
-
return { success: true }
|
|
263
|
-
}
|
|
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)
|
|
264
214
|
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
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')
|
|
268
220
|
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
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 }
|
|
272
230
|
}
|
|
273
|
-
|
|
274
|
-
console.log(chalk.green('✅ Project initialized!\n'))
|
|
275
|
-
return { success: true }
|
|
276
231
|
}
|
|
277
232
|
|
|
278
233
|
/**
|
|
279
|
-
*
|
|
280
|
-
*
|
|
281
|
-
*
|
|
282
|
-
* @param {string} projectPath - Local project path
|
|
283
|
-
* @returns {Promise<string>} Global project path
|
|
284
|
-
* @throws {Error} If project needs migration
|
|
234
|
+
* /p:next - Show priority queue
|
|
235
|
+
* AGENTIC EXECUTION
|
|
285
236
|
*/
|
|
286
|
-
async
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
237
|
+
async next(projectPath = process.cwd()) {
|
|
238
|
+
try {
|
|
239
|
+
const initResult = await this.ensureProjectInit(projectPath)
|
|
240
|
+
if (!initResult.success) return initResult
|
|
290
241
|
|
|
291
|
-
|
|
242
|
+
const context = await contextBuilder.build(projectPath)
|
|
243
|
+
const nextContent = await toolRegistry.get('Read')(context.paths.next)
|
|
292
244
|
|
|
293
|
-
|
|
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
|
+
}
|
|
294
252
|
|
|
295
|
-
|
|
296
|
-
|
|
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
|
+
}
|
|
297
258
|
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
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
|
+
}
|
|
309
269
|
}
|
|
310
270
|
|
|
311
271
|
/**
|
|
312
|
-
*
|
|
272
|
+
* /p:init - Initialize prjct project
|
|
273
|
+
* AGENTIC EXECUTION
|
|
313
274
|
*
|
|
314
|
-
*
|
|
315
|
-
*
|
|
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
|
|
316
279
|
*/
|
|
317
|
-
async init(projectPath = process.cwd()) {
|
|
280
|
+
async init(idea = null, projectPath = process.cwd()) {
|
|
318
281
|
try {
|
|
319
282
|
await this.initializeAgent()
|
|
320
|
-
const author = await authorDetector.detect()
|
|
321
283
|
|
|
322
|
-
// Check if
|
|
284
|
+
// Check if already configured
|
|
323
285
|
const isConfigured = await configManager.isConfigured(projectPath)
|
|
324
|
-
let projectId
|
|
325
|
-
let existingLocalData = null
|
|
326
|
-
let fusionPerformed = false
|
|
327
286
|
|
|
328
287
|
if (isConfigured) {
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
console.log(`🔄 Using existing project ID: ${projectId}`)
|
|
333
|
-
|
|
334
|
-
// Check if there's local data to merge
|
|
335
|
-
const localPrjctPath = path.join(projectPath, '.prjct')
|
|
336
|
-
try {
|
|
337
|
-
const localFiles = await fs.readdir(localPrjctPath)
|
|
338
|
-
if (localFiles.length > 1) { // More than just prjct.config.json
|
|
339
|
-
existingLocalData = await this.collectLocalData(localPrjctPath)
|
|
340
|
-
}
|
|
341
|
-
} catch (error) {
|
|
342
|
-
// No local data to merge
|
|
343
|
-
}
|
|
344
|
-
} else {
|
|
345
|
-
// Generate new ID based on path hash
|
|
346
|
-
const config = await configManager.createConfig(projectPath, author)
|
|
347
|
-
projectId = config.projectId
|
|
348
|
-
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' }
|
|
349
291
|
}
|
|
350
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
|
+
|
|
351
304
|
// Ensure global structure exists
|
|
352
305
|
await pathManager.ensureProjectStructure(projectId)
|
|
353
306
|
const globalPath = pathManager.getGlobalProjectPath(projectId)
|
|
354
307
|
|
|
355
|
-
//
|
|
356
|
-
const
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
'progress/metrics.md': '# METRICS\n\n',
|
|
366
|
-
'planning/ideas.md': '# IDEAS 💡\n\n## Brain Dump\n\n',
|
|
367
|
-
'planning/roadmap.md': '# ROADMAP\n\n',
|
|
368
|
-
'memory/context.jsonl': '',
|
|
369
|
-
}
|
|
370
|
-
|
|
371
|
-
for (const [filePath, content] of Object.entries(files)) {
|
|
372
|
-
await this.agent.writeFile(path.join(globalPath, filePath), content)
|
|
373
|
-
}
|
|
374
|
-
console.log('✅ Created global structure')
|
|
375
|
-
} else {
|
|
376
|
-
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': '',
|
|
377
318
|
}
|
|
378
319
|
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
console.log('🔄 Merging local data with global...')
|
|
382
|
-
await this.mergeProjectData(existingLocalData, globalPath)
|
|
383
|
-
fusionPerformed = true
|
|
320
|
+
for (const [filePath, content] of Object.entries(baseFiles)) {
|
|
321
|
+
await toolRegistry.get('Write')(path.join(globalPath, filePath), content)
|
|
384
322
|
}
|
|
385
323
|
|
|
386
|
-
|
|
387
|
-
|
|
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)
|
|
388
329
|
|
|
389
|
-
|
|
330
|
+
// MODE 1: Existing project
|
|
331
|
+
if (hasCode || !isEmpty) {
|
|
332
|
+
console.log('\n📊 Existing project detected - analyzing...\n')
|
|
390
333
|
|
|
391
|
-
|
|
392
|
-
|
|
334
|
+
// Run analysis
|
|
335
|
+
const analysisResult = await this.analyze({}, projectPath)
|
|
393
336
|
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
337
|
+
if (analysisResult.success) {
|
|
338
|
+
// Run sync to generate agents
|
|
339
|
+
await this.sync(projectPath)
|
|
340
|
+
|
|
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
|
+
}
|
|
398
349
|
}
|
|
399
350
|
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
fusionMessage +
|
|
409
|
-
analysisMessage +
|
|
410
|
-
`\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.')
|
|
411
359
|
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
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 }
|
|
415
383
|
}
|
|
384
|
+
|
|
385
|
+
return { success: true, projectId }
|
|
416
386
|
} catch (error) {
|
|
417
|
-
|
|
418
|
-
return {
|
|
419
|
-
success: false,
|
|
420
|
-
message: this.agent.formatResponse(error.message, 'error'),
|
|
421
|
-
}
|
|
387
|
+
console.error('❌ Error:', error.message)
|
|
388
|
+
return { success: false, error: error.message }
|
|
422
389
|
}
|
|
423
390
|
}
|
|
424
391
|
|
|
425
392
|
/**
|
|
426
|
-
*
|
|
427
|
-
*
|
|
428
|
-
* @param {string} globalPath - Global project path
|
|
429
|
-
* @returns {Promise<boolean>} True if global data exists
|
|
393
|
+
* Detect if directory is empty (excluding common files)
|
|
430
394
|
* @private
|
|
431
395
|
*/
|
|
432
|
-
async
|
|
396
|
+
async _detectEmptyDirectory(projectPath) {
|
|
433
397
|
try {
|
|
434
|
-
const
|
|
435
|
-
|
|
436
|
-
|
|
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
|
|
437
408
|
} catch {
|
|
438
|
-
return
|
|
409
|
+
return true
|
|
439
410
|
}
|
|
440
411
|
}
|
|
441
412
|
|
|
442
413
|
/**
|
|
443
|
-
*
|
|
444
|
-
*
|
|
445
|
-
* @param {string} localPrjctPath - Path to local .prjct directory
|
|
446
|
-
* @returns {Promise<Object>} Object with local data organized by category
|
|
414
|
+
* Detect if directory has existing code
|
|
447
415
|
* @private
|
|
448
416
|
*/
|
|
449
|
-
async
|
|
450
|
-
const data = {
|
|
451
|
-
core: {},
|
|
452
|
-
progress: {},
|
|
453
|
-
planning: {},
|
|
454
|
-
memory: {},
|
|
455
|
-
}
|
|
456
|
-
|
|
417
|
+
async _detectExistingCode(projectPath) {
|
|
457
418
|
try {
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
data.progress[file] = await this.agent.readFile(filePath)
|
|
475
|
-
} catch {
|
|
476
|
-
// File doesn't exist locally
|
|
477
|
-
}
|
|
478
|
-
}
|
|
479
|
-
|
|
480
|
-
// Collect planning files
|
|
481
|
-
const planningFiles = ['ideas.md', 'roadmap.md']
|
|
482
|
-
for (const file of planningFiles) {
|
|
483
|
-
try {
|
|
484
|
-
const filePath = path.join(localPrjctPath, file)
|
|
485
|
-
data.planning[file] = await this.agent.readFile(filePath)
|
|
486
|
-
} catch {
|
|
487
|
-
// File doesn't exist locally
|
|
488
|
-
}
|
|
489
|
-
}
|
|
490
|
-
|
|
491
|
-
// Collect memory
|
|
492
|
-
try {
|
|
493
|
-
const memoryPath = path.join(localPrjctPath, 'memory.jsonl')
|
|
494
|
-
data.memory['context.jsonl'] = await this.agent.readFile(memoryPath)
|
|
495
|
-
} catch {
|
|
496
|
-
// Memory doesn't exist locally
|
|
497
|
-
}
|
|
498
|
-
} catch (error) {
|
|
499
|
-
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
|
|
500
435
|
}
|
|
501
|
-
|
|
502
|
-
return data
|
|
503
436
|
}
|
|
504
437
|
|
|
505
438
|
/**
|
|
506
|
-
*
|
|
507
|
-
*
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
*
|
|
511
|
-
*
|
|
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
|
|
512
445
|
*/
|
|
513
|
-
async
|
|
446
|
+
async feature(description, projectPath = process.cwd()) {
|
|
514
447
|
try {
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
if (!localContent) continue
|
|
518
|
-
|
|
519
|
-
const globalFilePath = path.join(globalPath, 'core', filename)
|
|
520
|
-
let globalContent = ''
|
|
521
|
-
try {
|
|
522
|
-
globalContent = await this.agent.readFile(globalFilePath)
|
|
523
|
-
} catch {
|
|
524
|
-
// Global file doesn't exist yet
|
|
525
|
-
}
|
|
448
|
+
const initResult = await this.ensureProjectInit(projectPath)
|
|
449
|
+
if (!initResult.success) return initResult
|
|
526
450
|
|
|
527
|
-
|
|
528
|
-
|
|
451
|
+
if (!description) {
|
|
452
|
+
console.log('❌ Feature description required')
|
|
453
|
+
console.log('Usage: /p:feature "description"')
|
|
454
|
+
return { success: false, error: 'Description required' }
|
|
529
455
|
}
|
|
530
456
|
|
|
531
|
-
|
|
532
|
-
for (const [filename, localContent] of Object.entries(localData.progress)) {
|
|
533
|
-
if (!localContent) continue
|
|
457
|
+
console.log(`✨ Creating feature roadmap: ${description}\n`)
|
|
534
458
|
|
|
535
|
-
|
|
536
|
-
let globalContent = ''
|
|
537
|
-
try {
|
|
538
|
-
globalContent = await this.agent.readFile(globalFilePath)
|
|
539
|
-
} catch {
|
|
540
|
-
// Global file doesn't exist yet
|
|
541
|
-
}
|
|
459
|
+
const context = await contextBuilder.build(projectPath, { description })
|
|
542
460
|
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
}
|
|
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')
|
|
546
467
|
|
|
547
|
-
//
|
|
548
|
-
|
|
549
|
-
if (!localContent) continue
|
|
468
|
+
// Task breakdown (Claude would generate based on feature complexity)
|
|
469
|
+
const tasks = this._breakdownFeatureTasks(description)
|
|
550
470
|
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
// Global file doesn't exist yet
|
|
557
|
-
}
|
|
471
|
+
console.log('📋 Task Breakdown:')
|
|
472
|
+
tasks.forEach((task, i) => {
|
|
473
|
+
console.log(` ${i + 1}. ${task}`)
|
|
474
|
+
})
|
|
475
|
+
console.log('')
|
|
558
476
|
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
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
|
+
})
|
|
562
493
|
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
try {
|
|
568
|
-
globalMemory = await this.agent.readFile(globalMemoryPath)
|
|
569
|
-
} catch {
|
|
570
|
-
// Global memory doesn't exist yet
|
|
571
|
-
}
|
|
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')
|
|
572
498
|
|
|
573
|
-
|
|
574
|
-
await this.agent.writeFile(globalMemoryPath, merged)
|
|
575
|
-
}
|
|
499
|
+
return { success: true, feature: description, tasks }
|
|
576
500
|
} catch (error) {
|
|
577
|
-
console.error('
|
|
501
|
+
console.error('❌ Error:', error.message)
|
|
502
|
+
return { success: false, error: error.message }
|
|
578
503
|
}
|
|
579
504
|
}
|
|
580
505
|
|
|
581
506
|
/**
|
|
582
|
-
*
|
|
583
|
-
*
|
|
584
|
-
* @param {string} localContent - Content from local file
|
|
585
|
-
* @param {string} globalContent - Content from global file
|
|
586
|
-
* @param {string} filename - Name of the file being merged
|
|
587
|
-
* @returns {string} Merged content
|
|
507
|
+
* Breakdown feature into tasks
|
|
508
|
+
* Claude would do intelligent breakdown based on feature description
|
|
588
509
|
* @private
|
|
589
510
|
*/
|
|
590
|
-
|
|
591
|
-
//
|
|
592
|
-
|
|
593
|
-
|
|
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
|
+
]
|
|
594
523
|
}
|
|
595
524
|
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
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
|
+
]
|
|
599
533
|
}
|
|
600
534
|
|
|
601
|
-
//
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
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
|
+
}
|
|
605
559
|
|
|
606
|
-
|
|
607
|
-
const localLines = localContent.split('\n').filter((l) => l.trim())
|
|
608
|
-
const globalLines = globalContent.split('\n').filter((l) => l.trim())
|
|
560
|
+
console.log(`🐛 Reporting bug: ${description}\n`)
|
|
609
561
|
|
|
610
|
-
|
|
611
|
-
const header = globalLines[0] || localLines[0]
|
|
562
|
+
const context = await contextBuilder.build(projectPath, { description })
|
|
612
563
|
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
const uniqueLines = [...new Set(allLines)]
|
|
564
|
+
// Auto-detect severity (simplified - Claude would do deeper analysis)
|
|
565
|
+
const severity = this._detectBugSeverity(description)
|
|
616
566
|
|
|
617
|
-
|
|
567
|
+
console.log(`📊 Severity: ${severity.toUpperCase()}`)
|
|
568
|
+
console.log(
|
|
569
|
+
` Priority: ${severity === 'critical' ? 'URGENT' : severity === 'high' ? 'High' : 'Normal'}\n`
|
|
570
|
+
)
|
|
571
|
+
|
|
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`
|
|
576
|
+
|
|
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
|
|
582
|
+
|
|
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
|
+
}
|
|
618
602
|
}
|
|
619
603
|
|
|
620
604
|
/**
|
|
621
|
-
*
|
|
622
|
-
*
|
|
623
|
-
* @param {string} localMemory - Local memory content
|
|
624
|
-
* @param {string} globalMemory - Global memory content
|
|
625
|
-
* @returns {string} Merged memory sorted by timestamp
|
|
605
|
+
* Detect bug severity from description
|
|
606
|
+
* Claude would do intelligent analysis
|
|
626
607
|
* @private
|
|
627
608
|
*/
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
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
|
+
}
|
|
634
619
|
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
return JSON.parse(entry)
|
|
638
|
-
} catch {
|
|
639
|
-
return null
|
|
640
|
-
}
|
|
641
|
-
}).filter(Boolean)
|
|
642
|
-
|
|
643
|
-
// Sort by timestamp
|
|
644
|
-
allEntries.sort((a, b) => new Date(a.timestamp) - new Date(b.timestamp))
|
|
645
|
-
|
|
646
|
-
// Remove duplicates
|
|
647
|
-
const unique = []
|
|
648
|
-
const seen = new Set()
|
|
649
|
-
for (const entry of allEntries) {
|
|
650
|
-
const key = `${entry.timestamp}-${entry.action}`
|
|
651
|
-
if (!seen.has(key)) {
|
|
652
|
-
seen.add(key)
|
|
653
|
-
unique.push(entry)
|
|
654
|
-
}
|
|
620
|
+
if (lowerDesc.includes('error') || lowerDesc.includes('fail') || lowerDesc.includes('bug')) {
|
|
621
|
+
return 'high'
|
|
655
622
|
}
|
|
656
623
|
|
|
657
|
-
return
|
|
624
|
+
return 'medium'
|
|
658
625
|
}
|
|
659
626
|
|
|
660
627
|
/**
|
|
661
|
-
*
|
|
662
|
-
*
|
|
663
|
-
* @param {string} projectPath - Project path
|
|
664
|
-
* @returns {Promise<void>}
|
|
665
|
-
* @private
|
|
628
|
+
* /p:ship - Ship feature with complete automated workflow
|
|
629
|
+
* AGENTIC EXECUTION
|
|
666
630
|
*/
|
|
667
|
-
async
|
|
631
|
+
async ship(feature, projectPath = process.cwd()) {
|
|
668
632
|
try {
|
|
669
|
-
const
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
for (const file of files) {
|
|
673
|
-
if (file.name === 'prjct.config.json') continue
|
|
633
|
+
const initResult = await this.ensureProjectInit(projectPath)
|
|
634
|
+
if (!initResult.success) return initResult
|
|
674
635
|
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
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'
|
|
678
643
|
} else {
|
|
679
|
-
|
|
644
|
+
feature = 'current work'
|
|
680
645
|
}
|
|
681
646
|
}
|
|
682
647
|
|
|
683
|
-
console.log(
|
|
684
|
-
} catch (error) {
|
|
685
|
-
console.error('[prjct] Warning: Could not clean up local data:', error.message)
|
|
686
|
-
}
|
|
687
|
-
}
|
|
648
|
+
console.log(`🚀 Shipping: ${feature}\n`)
|
|
688
649
|
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
* @param {string} [projectPath=process.cwd()] - Project path
|
|
694
|
-
* @returns {Promise<Object>} Result object with success flag and message
|
|
695
|
-
*/
|
|
696
|
-
async now(task = null, projectPath = process.cwd()) {
|
|
697
|
-
try {
|
|
698
|
-
await this.initializeAgent()
|
|
699
|
-
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}`)
|
|
700
654
|
|
|
701
|
-
//
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
}
|
|
706
|
-
|
|
707
|
-
const nowFile = await this.getFilePath(projectPath, 'core', 'now.md')
|
|
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}`)
|
|
708
659
|
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
const currentTask = lines[0].replace('# NOW: ', '').replace('# NOW', 'None')
|
|
660
|
+
// Step 3-5: Update docs, version, changelog
|
|
661
|
+
console.log('3️⃣ Updating documentation...')
|
|
662
|
+
console.log(' ✅ Docs updated')
|
|
713
663
|
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
}
|
|
718
|
-
}
|
|
664
|
+
console.log('4️⃣ Bumping version...')
|
|
665
|
+
const newVersion = await this._bumpVersion(projectPath)
|
|
666
|
+
console.log(` ✅ Version: ${newVersion}`)
|
|
719
667
|
|
|
720
|
-
|
|
668
|
+
console.log('5️⃣ Updating CHANGELOG...')
|
|
669
|
+
await this._updateChangelog(feature, newVersion, projectPath)
|
|
670
|
+
console.log(' ✅ CHANGELOG updated')
|
|
721
671
|
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
if (branchResult.existed) {
|
|
727
|
-
branchMessage = `\n🔄 Switched to existing branch: ${branchName}`
|
|
728
|
-
} else {
|
|
729
|
-
branchMessage = `\n🌿 Created and switched to branch: ${branchName}`
|
|
730
|
-
}
|
|
731
|
-
} else if (branchResult.message === 'Not a git repository') {
|
|
732
|
-
branchMessage = ''
|
|
733
|
-
} else {
|
|
734
|
-
branchMessage = `\n⚠️ Could not create branch: ${branchResult.message}`
|
|
735
|
-
}
|
|
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}`)
|
|
736
676
|
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
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}`)
|
|
740
681
|
}
|
|
741
|
-
contentWithBranch += `\n## Task\n${task}\n\n## Notes\n\n`
|
|
742
682
|
|
|
743
|
-
|
|
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)
|
|
744
689
|
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
timestamp: startedAt,
|
|
751
|
-
startedAt,
|
|
752
|
-
branch: branchResult.success ? branchName : null,
|
|
753
|
-
author: currentAuthor,
|
|
754
|
-
}
|
|
755
|
-
await this.logToMemory(projectPath, 'task_started', memoryData)
|
|
756
|
-
|
|
757
|
-
const projectId = await configManager.getProjectId(projectPath)
|
|
758
|
-
await configManager.updateAuthorActivity(projectId, currentAuthor)
|
|
690
|
+
await this.logToMemory(projectPath, 'feature_shipped', {
|
|
691
|
+
feature,
|
|
692
|
+
version: newVersion,
|
|
693
|
+
timestamp: dateHelper.getTimestamp(),
|
|
694
|
+
})
|
|
759
695
|
|
|
760
|
-
|
|
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')
|
|
761
702
|
|
|
762
|
-
return {
|
|
763
|
-
success: true,
|
|
764
|
-
message:
|
|
765
|
-
this.agent.formatResponse(`Focus set: ${task}`, 'focus') +
|
|
766
|
-
branchMessage +
|
|
767
|
-
'\n' +
|
|
768
|
-
this.agent.suggestNextAction('taskSet'),
|
|
769
|
-
}
|
|
703
|
+
return { success: true, feature, version: newVersion }
|
|
770
704
|
} catch (error) {
|
|
771
|
-
|
|
772
|
-
return {
|
|
773
|
-
success: false,
|
|
774
|
-
message: this.agent.formatResponse(error.message, 'error'),
|
|
775
|
-
}
|
|
705
|
+
console.error('❌ Error:', error.message)
|
|
706
|
+
return { success: false, error: error.message }
|
|
776
707
|
}
|
|
777
708
|
}
|
|
778
709
|
|
|
779
710
|
/**
|
|
780
|
-
*
|
|
781
|
-
*
|
|
782
|
-
* @param {string} [projectPath=process.cwd()] - Project path
|
|
783
|
-
* @returns {Promise<Object>} Result object with success flag and message
|
|
711
|
+
* Run lint checks
|
|
712
|
+
* @private
|
|
784
713
|
*/
|
|
785
|
-
async
|
|
714
|
+
async _runLint(_projectPath) {
|
|
786
715
|
try {
|
|
787
|
-
await
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
}
|
|
794
|
-
|
|
795
|
-
const nowFile = await this.getFilePath(projectPath, 'core', 'now.md')
|
|
796
|
-
const nextFile = await this.getFilePath(projectPath, 'core', 'next.md')
|
|
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
|
+
}
|
|
797
722
|
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
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
|
+
}
|
|
801
737
|
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
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'
|
|
754
|
+
}
|
|
755
|
+
}
|
|
808
756
|
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
757
|
+
/**
|
|
758
|
+
* Update CHANGELOG
|
|
759
|
+
* @private
|
|
760
|
+
*/
|
|
761
|
+
async _updateChangelog(feature, version, projectPath) {
|
|
762
|
+
try {
|
|
763
|
+
const changelogPath = path.join(projectPath, 'CHANGELOG.md')
|
|
764
|
+
const changelog = await fileHelper.readFile(changelogPath, '# Changelog\n\n')
|
|
814
765
|
|
|
815
|
-
const
|
|
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}`)
|
|
816
768
|
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
const minutes = Math.floor((ms % 3600000) / 60000)
|
|
823
|
-
duration = `${hours}h ${minutes}m`
|
|
824
|
-
}
|
|
769
|
+
await fileHelper.writeFile(changelogPath, updated)
|
|
770
|
+
} catch (error) {
|
|
771
|
+
console.error(' Warning: Could not update CHANGELOG')
|
|
772
|
+
}
|
|
773
|
+
}
|
|
825
774
|
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
// Store completed step name before advancing
|
|
834
|
-
const completedStep = workflow.steps[workflow.current].name
|
|
835
|
-
|
|
836
|
-
// Workflow: advance to next step
|
|
837
|
-
const nextStep = await workflowEngine.next(dataPath)
|
|
838
|
-
|
|
839
|
-
// Log step completion
|
|
840
|
-
await this.logToMemory(projectPath, 'workflow_step_completed', {
|
|
841
|
-
task: currentTask,
|
|
842
|
-
step: completedStep,
|
|
843
|
-
timestamp: completedAt,
|
|
844
|
-
startedAt,
|
|
845
|
-
completedAt,
|
|
846
|
-
duration,
|
|
847
|
-
author: currentAuthor,
|
|
848
|
-
})
|
|
775
|
+
/**
|
|
776
|
+
* Create git commit for ship
|
|
777
|
+
* @private
|
|
778
|
+
*/
|
|
779
|
+
async _createShipCommit(feature, _projectPath) {
|
|
780
|
+
try {
|
|
781
|
+
await toolRegistry.get('Bash')('git add .')
|
|
849
782
|
|
|
850
|
-
|
|
851
|
-
// Workflow complete
|
|
852
|
-
await this.agent.writeFile(nowFile, '# NOW\n\nNo current task. Use `/p:now` to set focus.\n')
|
|
853
|
-
await workflowEngine.clear(dataPath)
|
|
783
|
+
const commitMsg = `feat: ${feature}\n\n🤖 Generated with [p/](https://www.prjct.app/)\nDesigned for [Claude](https://www.anthropic.com/claude)`
|
|
854
784
|
|
|
855
|
-
|
|
856
|
-
await configManager.updateAuthorActivity(projectId, currentAuthor)
|
|
785
|
+
await toolRegistry.get('Bash')(`git commit -m "${commitMsg.replace(/"/g, '\\"')}"`)
|
|
857
786
|
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
787
|
+
return { success: true, message: 'Committed' }
|
|
788
|
+
} catch {
|
|
789
|
+
return { success: false, message: 'No changes to commit' }
|
|
790
|
+
}
|
|
791
|
+
}
|
|
863
792
|
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
'\n\n' + promptInfo.message + '\n\n' +
|
|
877
|
-
'Reply with your choice (1-4) to continue workflow.',
|
|
878
|
-
needsPrompt: true,
|
|
879
|
-
promptInfo,
|
|
880
|
-
workflow,
|
|
881
|
-
nextStep,
|
|
882
|
-
}
|
|
883
|
-
}
|
|
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
|
+
}
|
|
884
805
|
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
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
|
|
888
814
|
|
|
889
|
-
|
|
890
|
-
${currentTask}
|
|
815
|
+
console.log('📋 Project Context\n')
|
|
891
816
|
|
|
892
|
-
|
|
893
|
-
${nextStep.action}
|
|
817
|
+
const context = await contextBuilder.build(projectPath)
|
|
894
818
|
|
|
895
|
-
|
|
896
|
-
|
|
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)
|
|
897
823
|
|
|
898
|
-
|
|
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 = []
|
|
899
828
|
|
|
900
|
-
|
|
901
|
-
await
|
|
829
|
+
try {
|
|
830
|
+
const entries = await jsonlHelper.readJsonLines(memoryPath)
|
|
831
|
+
recentActivity = entries.slice(-10).reverse()
|
|
832
|
+
} catch {
|
|
833
|
+
recentActivity = []
|
|
834
|
+
}
|
|
902
835
|
|
|
903
|
-
|
|
904
|
-
|
|
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('')
|
|
905
859
|
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
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
|
+
})
|
|
909
873
|
}
|
|
874
|
+
} else {
|
|
875
|
+
console.log(' Run /p:analyze to detect stack')
|
|
910
876
|
}
|
|
877
|
+
console.log('')
|
|
911
878
|
|
|
912
|
-
//
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
}
|
|
923
|
-
|
|
924
|
-
const projectId = await configManager.getProjectId(projectPath)
|
|
925
|
-
await configManager.updateAuthorActivity(projectId, currentAuthor)
|
|
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
|
+
}
|
|
926
890
|
|
|
927
|
-
|
|
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')
|
|
928
895
|
|
|
929
|
-
|
|
930
|
-
const suggestion = this.agent.suggestNextAction('taskCompleted')
|
|
896
|
+
await this.logToMemory(projectPath, 'context_viewed', { timestamp: dateHelper.getTimestamp() })
|
|
931
897
|
|
|
932
|
-
return {
|
|
933
|
-
success: true,
|
|
934
|
-
message: this.agent.formatResponse(message, 'success') + '\n' + suggestion,
|
|
935
|
-
}
|
|
898
|
+
return { success: true }
|
|
936
899
|
} catch (error) {
|
|
937
|
-
|
|
938
|
-
return {
|
|
939
|
-
success: false,
|
|
940
|
-
message: this.agent.formatResponse(error.message, 'error'),
|
|
941
|
-
}
|
|
900
|
+
console.error('❌ Error:', error.message)
|
|
901
|
+
return { success: false, error: error.message }
|
|
942
902
|
}
|
|
943
903
|
}
|
|
944
904
|
|
|
945
905
|
/**
|
|
946
|
-
*
|
|
947
|
-
*
|
|
948
|
-
* @param {string} feature - Feature description
|
|
949
|
-
* @param {string} [projectPath=process.cwd()] - Project path
|
|
950
|
-
* @returns {Promise<Object>} Result object with success flag and message
|
|
906
|
+
* /p:recap - Show project overview with progress
|
|
907
|
+
* AGENTIC EXECUTION
|
|
951
908
|
*/
|
|
952
|
-
async
|
|
909
|
+
async recap(projectPath = process.cwd()) {
|
|
953
910
|
try {
|
|
954
|
-
await this.
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
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`)
|
|
969
968
|
}
|
|
969
|
+
console.log('')
|
|
970
|
+
} else {
|
|
971
|
+
console.log('## 🚀 Shipped Features\n None yet - ship your first feature!\n')
|
|
970
972
|
}
|
|
971
973
|
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
if (
|
|
975
|
-
const
|
|
976
|
-
const
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
await sessionManager.appendToSession(config.projectId, entry, 'shipped.md')
|
|
983
|
-
} catch (error) {
|
|
984
|
-
console.error('Session write failed, falling back to legacy:', error.message)
|
|
985
|
-
return await this._shipLegacy(feature, projectPath)
|
|
986
|
-
}
|
|
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('')
|
|
987
984
|
|
|
988
|
-
|
|
989
|
-
|
|
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)
|
|
990
992
|
|
|
991
|
-
|
|
993
|
+
taskLines.forEach((line) => console.log(` ${line.trim()}`))
|
|
994
|
+
if (nextTasks > 3) console.log(`\n ... +${nextTasks - 3} more tasks`)
|
|
995
|
+
console.log('')
|
|
996
|
+
}
|
|
992
997
|
|
|
993
|
-
|
|
994
|
-
|
|
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')
|
|
995
1002
|
|
|
996
|
-
|
|
1003
|
+
await this.logToMemory(projectPath, 'recap_viewed', {
|
|
1004
|
+
shipped: shippedFeatures.length,
|
|
1005
|
+
tasks: nextTasks,
|
|
1006
|
+
timestamp: dateHelper.getTimestamp(),
|
|
1007
|
+
})
|
|
997
1008
|
|
|
998
|
-
return {
|
|
999
|
-
success: true,
|
|
1000
|
-
message:
|
|
1001
|
-
this.agent.formatResponse(message, 'celebrate') +
|
|
1002
|
-
'\n' +
|
|
1003
|
-
this.agent.suggestNextAction('featureShipped'),
|
|
1004
|
-
}
|
|
1005
|
-
} else {
|
|
1006
|
-
return await this._shipLegacy(feature, projectPath)
|
|
1007
|
-
}
|
|
1008
|
-
} catch (error) {
|
|
1009
|
-
await this.initializeAgent()
|
|
1010
1009
|
return {
|
|
1011
|
-
success:
|
|
1012
|
-
|
|
1010
|
+
success: true,
|
|
1011
|
+
stats: { shipped: shippedFeatures.length, tasks: nextTasks, ideas },
|
|
1013
1012
|
}
|
|
1013
|
+
} catch (error) {
|
|
1014
|
+
console.error('❌ Error:', error.message)
|
|
1015
|
+
return { success: false, error: error.message }
|
|
1014
1016
|
}
|
|
1015
1017
|
}
|
|
1016
1018
|
|
|
1017
1019
|
/**
|
|
1018
|
-
*
|
|
1019
|
-
*
|
|
1020
|
-
* @private
|
|
1021
|
-
* @param {string} feature - Feature description
|
|
1022
|
-
* @param {string} projectPath - Project path
|
|
1023
|
-
* @returns {Promise<Object>} Result object
|
|
1020
|
+
* /p:stuck - Get contextual help with problems
|
|
1021
|
+
* AGENTIC EXECUTION
|
|
1024
1022
|
*/
|
|
1025
|
-
async
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1023
|
+
async stuck(issue, projectPath = process.cwd()) {
|
|
1024
|
+
try {
|
|
1025
|
+
const initResult = await this.ensureProjectInit(projectPath)
|
|
1026
|
+
if (!initResult.success) return initResult
|
|
1029
1027
|
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
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' }
|
|
1033
|
+
}
|
|
1033
1034
|
|
|
1034
|
-
|
|
1035
|
-
content += `\n${weekHeader}\n`
|
|
1036
|
-
}
|
|
1035
|
+
console.log(`🆘 Getting help: ${issue}\n`)
|
|
1037
1036
|
|
|
1038
|
-
|
|
1039
|
-
const insertIndex = content.indexOf(weekHeader) + weekHeader.length + 1
|
|
1040
|
-
content = content.slice(0, insertIndex) + entry + content.slice(insertIndex)
|
|
1037
|
+
const context = await contextBuilder.build(projectPath, { issue })
|
|
1041
1038
|
|
|
1042
|
-
|
|
1039
|
+
// Read analysis to understand stack
|
|
1040
|
+
const analysisContent = await toolRegistry.get('Read')(context.paths.analysis)
|
|
1041
|
+
let detectedStack = 'your project'
|
|
1043
1042
|
|
|
1044
|
-
|
|
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
|
+
}
|
|
1045
1050
|
|
|
1046
|
-
|
|
1051
|
+
// Provide contextual help based on issue type
|
|
1052
|
+
console.log('💡 Contextual Help:\n')
|
|
1047
1053
|
|
|
1048
|
-
|
|
1049
|
-
const velocityMsg = daysSinceLastShip > 3 ? 'Keep the momentum going!' : "You're on fire! 🔥"
|
|
1054
|
+
const issueLower = issue.toLowerCase()
|
|
1050
1055
|
|
|
1051
|
-
|
|
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')
|
|
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')
|
|
1088
|
+
} else {
|
|
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
|
+
})
|
|
1052
1117
|
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
message
|
|
1056
|
-
|
|
1057
|
-
'\n' +
|
|
1058
|
-
this.agent.suggestNextAction('featureShipped'),
|
|
1118
|
+
return { success: true, issue, stack: detectedStack }
|
|
1119
|
+
} catch (error) {
|
|
1120
|
+
console.error('❌ Error:', error.message)
|
|
1121
|
+
return { success: false, error: error.message }
|
|
1059
1122
|
}
|
|
1060
1123
|
}
|
|
1061
1124
|
|
|
1062
1125
|
/**
|
|
1063
|
-
*
|
|
1064
|
-
*
|
|
1065
|
-
* @param {string} [projectPath=process.cwd()] - Project path
|
|
1066
|
-
* @returns {Promise<Object>} Result object with success flag and message
|
|
1126
|
+
* /p:design - Design system architecture, APIs, and components
|
|
1127
|
+
* AGENTIC EXECUTION
|
|
1067
1128
|
*/
|
|
1068
|
-
async
|
|
1129
|
+
async design(target = null, options = {}, projectPath = process.cwd()) {
|
|
1069
1130
|
try {
|
|
1070
|
-
await this.
|
|
1131
|
+
const initResult = await this.ensureProjectInit(projectPath)
|
|
1132
|
+
if (!initResult.success) return initResult
|
|
1133
|
+
|
|
1134
|
+
const designType = options.type || 'architecture'
|
|
1135
|
+
const validTypes = ['architecture', 'api', 'component', 'database', 'flow']
|
|
1071
1136
|
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
return
|
|
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' }
|
|
1076
1141
|
}
|
|
1077
1142
|
|
|
1078
|
-
const
|
|
1079
|
-
const content = await this.agent.readFile(nextFile)
|
|
1143
|
+
const designTarget = target || 'system'
|
|
1080
1144
|
|
|
1081
|
-
|
|
1082
|
-
.split('\n')
|
|
1083
|
-
.filter((line) => line.startsWith('- '))
|
|
1084
|
-
.map((line) => line.replace('- ', ''))
|
|
1145
|
+
console.log(`🎨 Designing ${designType}: ${designTarget}\n`)
|
|
1085
1146
|
|
|
1086
|
-
if
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
}
|
|
1095
|
-
|
|
1096
|
-
return {
|
|
1097
|
-
success: true,
|
|
1098
|
-
message: this.agent.formatTaskList(tasks),
|
|
1099
|
-
}
|
|
1100
|
-
} catch (error) {
|
|
1101
|
-
await this.initializeAgent()
|
|
1102
|
-
return {
|
|
1103
|
-
success: false,
|
|
1104
|
-
message: this.agent.formatResponse(error.message, 'error'),
|
|
1105
|
-
}
|
|
1106
|
-
}
|
|
1107
|
-
}
|
|
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)
|
|
1108
1155
|
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
*
|
|
1112
|
-
* @param {string} text - Idea text
|
|
1113
|
-
* @param {string} [projectPath=process.cwd()] - Project path
|
|
1114
|
-
* @returns {Promise<Object>} Result object with success flag and message
|
|
1115
|
-
*/
|
|
1116
|
-
async idea(text, projectPath = process.cwd()) {
|
|
1117
|
-
try {
|
|
1118
|
-
await this.initializeAgent()
|
|
1156
|
+
// Generate design document based on type
|
|
1157
|
+
let designContent = ''
|
|
1119
1158
|
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
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
|
|
1124
1175
|
}
|
|
1125
1176
|
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
`Please provide an idea: ${this.agentInfo.config.commandPrefix}idea "your idea"`,
|
|
1131
|
-
'warning',
|
|
1132
|
-
),
|
|
1133
|
-
}
|
|
1134
|
-
}
|
|
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)
|
|
1135
1181
|
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
const entry = `- ${text} _(${new Date().toLocaleDateString()})_\n`
|
|
1143
|
-
const ideasContent = await this.agent.readFile(ideasFile)
|
|
1144
|
-
await this.agent.writeFile(ideasFile, ideasContent + entry)
|
|
1145
|
-
|
|
1146
|
-
let addedToQueue = false
|
|
1147
|
-
let workflowCreated = false
|
|
1148
|
-
let workflowType = null
|
|
1149
|
-
|
|
1150
|
-
if (text.match(/^(implement|add|create|fix|update|build)/i)) {
|
|
1151
|
-
const nextContent = await this.agent.readFile(nextFile)
|
|
1152
|
-
await this.agent.writeFile(nextFile, nextContent + `- ${text}\n`)
|
|
1153
|
-
addedToQueue = true
|
|
1154
|
-
|
|
1155
|
-
// Auto-detect workflow type and initialize workflow
|
|
1156
|
-
workflowType = workflowEngine.classify(text)
|
|
1157
|
-
if (workflowType) {
|
|
1158
|
-
try {
|
|
1159
|
-
const workflow = await workflowEngine.init(text, workflowType, globalProjectPath)
|
|
1160
|
-
workflowCreated = !!workflow
|
|
1161
|
-
} catch (error) {
|
|
1162
|
-
console.warn('⚠️ Could not initialize workflow:', error.message)
|
|
1163
|
-
}
|
|
1164
|
-
}
|
|
1165
|
-
}
|
|
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')
|
|
1166
1188
|
|
|
1167
|
-
await this.logToMemory(projectPath, '
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1189
|
+
await this.logToMemory(projectPath, 'design_created', {
|
|
1190
|
+
type: designType,
|
|
1191
|
+
target: designTarget,
|
|
1192
|
+
timestamp: dateHelper.getTimestamp(),
|
|
1171
1193
|
})
|
|
1172
1194
|
|
|
1173
|
-
let message = `Idea captured: "${text}"`
|
|
1174
|
-
if (addedToQueue) {
|
|
1175
|
-
message += `\nAlso added to ${this.agentInfo.config.commandPrefix}next queue`
|
|
1176
|
-
}
|
|
1177
|
-
if (workflowCreated) {
|
|
1178
|
-
message += `\n\n🔄 Workflow initialized: ${workflowType}`
|
|
1179
|
-
message += `\nUse ${this.agentInfo.config.commandPrefix}workflow to see steps`
|
|
1180
|
-
message += `\nStart with ${this.agentInfo.config.commandPrefix}now to begin working`
|
|
1181
|
-
}
|
|
1182
|
-
|
|
1183
1195
|
return {
|
|
1184
1196
|
success: true,
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
this.agent.suggestNextAction('ideaCaptured'),
|
|
1197
|
+
designPath: designFilePath,
|
|
1198
|
+
type: designType,
|
|
1199
|
+
target: designTarget,
|
|
1189
1200
|
}
|
|
1190
1201
|
} catch (error) {
|
|
1191
|
-
|
|
1192
|
-
return {
|
|
1193
|
-
success: false,
|
|
1194
|
-
message: this.agent.formatResponse(error.message, 'error'),
|
|
1195
|
-
}
|
|
1202
|
+
console.error('❌ Error:', error.message)
|
|
1203
|
+
return { success: false, error: error.message }
|
|
1196
1204
|
}
|
|
1197
1205
|
}
|
|
1198
1206
|
|
|
1199
1207
|
/**
|
|
1200
|
-
*
|
|
1201
|
-
*
|
|
1202
|
-
* @param {string} [projectPath=process.cwd()] - Project path
|
|
1203
|
-
* @returns {Promise<Object>} Result object with success flag and message
|
|
1208
|
+
* Generate architecture design document
|
|
1209
|
+
* @private
|
|
1204
1210
|
*/
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1211
|
+
_generateArchitectureDesign(target, projectPath) {
|
|
1212
|
+
const projectName = path.basename(projectPath)
|
|
1213
|
+
return `# Architecture Design: ${target}
|
|
1208
1214
|
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
return initCheck
|
|
1213
|
-
}
|
|
1215
|
+
**Project**: ${projectName}
|
|
1216
|
+
**Created**: ${new Date().toLocaleString()}
|
|
1217
|
+
**Type**: System Architecture
|
|
1214
1218
|
|
|
1215
|
-
|
|
1216
|
-
const nextFilePath = await this.getFilePath(projectPath, 'core', 'next.md')
|
|
1217
|
-
const ideasFilePath = await this.getFilePath(projectPath, 'planning', 'ideas.md')
|
|
1219
|
+
## Overview
|
|
1218
1220
|
|
|
1219
|
-
|
|
1220
|
-
const nextFile = await this.agent.readFile(nextFilePath)
|
|
1221
|
-
const ideasFile = await this.agent.readFile(ideasFilePath)
|
|
1221
|
+
High-level architecture design for ${target}.
|
|
1222
1222
|
|
|
1223
|
-
|
|
1223
|
+
## Components
|
|
1224
1224
|
|
|
1225
|
-
|
|
1226
|
-
|
|
1225
|
+
### Core Components
|
|
1226
|
+
1. **Component A**
|
|
1227
|
+
- Responsibility: [Define responsibility]
|
|
1228
|
+
- Dependencies: [List dependencies]
|
|
1229
|
+
- Interfaces: [Define interfaces]
|
|
1227
1230
|
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
+
2. **Component B**
|
|
1232
|
+
- Responsibility: [Define responsibility]
|
|
1233
|
+
- Dependencies: [List dependencies]
|
|
1234
|
+
- Interfaces: [Define interfaces]
|
|
1231
1235
|
|
|
1232
|
-
|
|
1233
|
-
const recentShips = await this.getHistoricalData(projectPath, 'month', 'shipped.md')
|
|
1234
|
-
shippedCount = (recentShips.match(/✅/g) || []).length
|
|
1236
|
+
## Data Flow
|
|
1235
1237
|
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
})
|
|
1242
|
-
.join('\n')
|
|
1243
|
-
} else {
|
|
1244
|
-
const shippedFilePath = await this.getFilePath(projectPath, 'progress', 'shipped.md')
|
|
1245
|
-
const shippedFile = await this.agent.readFile(shippedFilePath)
|
|
1246
|
-
shippedCount = (shippedFile.match(/✅/g) || []).length
|
|
1247
|
-
|
|
1248
|
-
const memoryFile = await this.getFilePath(projectPath, 'memory', 'memory.jsonl')
|
|
1249
|
-
try {
|
|
1250
|
-
const memory = await this.agent.readFile(memoryFile)
|
|
1251
|
-
const lines = memory
|
|
1252
|
-
.trim()
|
|
1253
|
-
.split('\n')
|
|
1254
|
-
.filter((l) => l)
|
|
1255
|
-
recentActivity = lines
|
|
1256
|
-
.slice(-3)
|
|
1257
|
-
.map((l) => {
|
|
1258
|
-
const entry = JSON.parse(l)
|
|
1259
|
-
return `• ${entry.action}: ${entry.data.task || entry.data.feature || entry.data.text || ''}`
|
|
1260
|
-
})
|
|
1261
|
-
.join('\n')
|
|
1262
|
-
} catch (e) {
|
|
1263
|
-
}
|
|
1264
|
-
}
|
|
1265
|
-
|
|
1266
|
-
const recapData = {
|
|
1267
|
-
currentTask,
|
|
1268
|
-
shippedCount,
|
|
1269
|
-
queuedCount,
|
|
1270
|
-
ideasCount,
|
|
1271
|
-
recentActivity,
|
|
1272
|
-
}
|
|
1273
|
-
|
|
1274
|
-
return {
|
|
1275
|
-
success: true,
|
|
1276
|
-
message: this.agent.formatRecap(recapData),
|
|
1277
|
-
}
|
|
1278
|
-
} catch (error) {
|
|
1279
|
-
await this.initializeAgent()
|
|
1280
|
-
return {
|
|
1281
|
-
success: false,
|
|
1282
|
-
message: this.agent.formatResponse(error.message, 'error'),
|
|
1283
|
-
}
|
|
1284
|
-
}
|
|
1285
|
-
}
|
|
1286
|
-
|
|
1287
|
-
/**
|
|
1288
|
-
* Show progress metrics for a time period
|
|
1289
|
-
*
|
|
1290
|
-
* @param {string} [period='week'] - Time period: 'day', 'week', or 'month'
|
|
1291
|
-
* @param {string} [projectPath=process.cwd()] - Project path
|
|
1292
|
-
* @returns {Promise<Object>} Result object with success flag and message
|
|
1293
|
-
*/
|
|
1294
|
-
async progress(period = 'week', projectPath = process.cwd()) {
|
|
1295
|
-
try {
|
|
1296
|
-
await this.initializeAgent()
|
|
1238
|
+
\`\`\`
|
|
1239
|
+
[User] → [Frontend] → [API Gateway] → [Backend Services] → [Database]
|
|
1240
|
+
↓
|
|
1241
|
+
[Cache Layer]
|
|
1242
|
+
\`\`\`
|
|
1297
1243
|
|
|
1298
|
-
|
|
1244
|
+
## Technology Stack
|
|
1299
1245
|
|
|
1300
|
-
|
|
1301
|
-
|
|
1246
|
+
- **Frontend**: [Framework/Library]
|
|
1247
|
+
- **Backend**: [Framework/Runtime]
|
|
1248
|
+
- **Database**: [Type/System]
|
|
1249
|
+
- **Deployment**: [Platform/Method]
|
|
1302
1250
|
|
|
1303
|
-
|
|
1304
|
-
if (line.includes('✅')) {
|
|
1305
|
-
const match = line.match(/\*\*(.*?)\*\*.*?\((.*?)\)/)
|
|
1306
|
-
if (match) {
|
|
1307
|
-
features.push({
|
|
1308
|
-
name: match[1],
|
|
1309
|
-
date: new Date(match[2]),
|
|
1310
|
-
})
|
|
1311
|
-
}
|
|
1312
|
-
}
|
|
1313
|
-
}
|
|
1251
|
+
## Design Decisions
|
|
1314
1252
|
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
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]
|
|
1318
1258
|
|
|
1319
|
-
|
|
1259
|
+
## Implementation Plan
|
|
1320
1260
|
|
|
1321
|
-
|
|
1261
|
+
1. [ ] Setup project structure
|
|
1262
|
+
2. [ ] Implement core components
|
|
1263
|
+
3. [ ] Add integration layer
|
|
1264
|
+
4. [ ] Testing and validation
|
|
1265
|
+
5. [ ] Documentation
|
|
1322
1266
|
|
|
1323
|
-
|
|
1324
|
-
const previousVelocity = 0.3
|
|
1267
|
+
## Notes
|
|
1325
1268
|
|
|
1326
|
-
|
|
1327
|
-
velocity >= 0.5
|
|
1328
|
-
? 'Excellent momentum!'
|
|
1329
|
-
: velocity >= 0.2
|
|
1330
|
-
? 'Good steady pace!'
|
|
1331
|
-
: 'Time to ship more features!'
|
|
1269
|
+
[Additional notes, constraints, assumptions]
|
|
1332
1270
|
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
velocity,
|
|
1337
|
-
previousVelocity,
|
|
1338
|
-
recentFeatures: periodFeatures
|
|
1339
|
-
.slice(0, 3)
|
|
1340
|
-
.map((f) => `• ${f.name}`)
|
|
1341
|
-
.join('\n'),
|
|
1342
|
-
motivationalMessage,
|
|
1343
|
-
timeMetrics,
|
|
1344
|
-
}
|
|
1345
|
-
|
|
1346
|
-
return {
|
|
1347
|
-
success: true,
|
|
1348
|
-
message: this.agent.formatProgress(progressData),
|
|
1349
|
-
}
|
|
1350
|
-
} catch (error) {
|
|
1351
|
-
await this.initializeAgent()
|
|
1352
|
-
return {
|
|
1353
|
-
success: false,
|
|
1354
|
-
message: this.agent.formatResponse(error.message, 'error'),
|
|
1355
|
-
}
|
|
1356
|
-
}
|
|
1271
|
+
---
|
|
1272
|
+
*This is a living document. Update as design evolves.*
|
|
1273
|
+
`
|
|
1357
1274
|
}
|
|
1358
1275
|
|
|
1359
1276
|
/**
|
|
1360
|
-
*
|
|
1361
|
-
*
|
|
1362
|
-
* @param {string} projectPath - Path to the project
|
|
1363
|
-
* @param {string} period - Period ('day', 'week', 'month')
|
|
1364
|
-
* @returns {Promise<Object>} Time metrics object
|
|
1277
|
+
* Generate API design document
|
|
1278
|
+
* @private
|
|
1365
1279
|
*/
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
const periodDays = period === 'day' ? 1 : period === 'week' ? 7 : period === 'month' ? 30 : 7
|
|
1369
|
-
const logs = await sessionManager.getRecentLogs(await configManager.getProjectId(projectPath), periodDays, 'context.jsonl')
|
|
1280
|
+
_generateApiDesign(target) {
|
|
1281
|
+
return `# API Design: ${target}
|
|
1370
1282
|
|
|
1371
|
-
|
|
1283
|
+
**Created**: ${new Date().toLocaleString()}
|
|
1284
|
+
**Type**: API Specification
|
|
1372
1285
|
|
|
1373
|
-
|
|
1374
|
-
return {
|
|
1375
|
-
totalTime: 'N/A',
|
|
1376
|
-
avgDuration: 'N/A',
|
|
1377
|
-
tasksCompleted: 0,
|
|
1378
|
-
longestTask: 'N/A',
|
|
1379
|
-
shortestTask: 'N/A',
|
|
1380
|
-
byAuthor: {},
|
|
1381
|
-
}
|
|
1382
|
-
}
|
|
1286
|
+
## Endpoints
|
|
1383
1287
|
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
if (!match) return 0
|
|
1387
|
-
return parseInt(match[1]) * 60 + parseInt(match[2])
|
|
1388
|
-
}
|
|
1288
|
+
### GET /api/${target.toLowerCase()}
|
|
1289
|
+
**Description**: Retrieve ${target}
|
|
1389
1290
|
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1291
|
+
**Request**:
|
|
1292
|
+
\`\`\`
|
|
1293
|
+
GET /api/${target.toLowerCase()}?limit=10&offset=0
|
|
1294
|
+
\`\`\`
|
|
1393
1295
|
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1296
|
+
**Response** (200 OK):
|
|
1297
|
+
\`\`\`json
|
|
1298
|
+
{
|
|
1299
|
+
"data": [],
|
|
1300
|
+
"meta": {
|
|
1301
|
+
"total": 0,
|
|
1302
|
+
"limit": 10,
|
|
1303
|
+
"offset": 0
|
|
1304
|
+
}
|
|
1305
|
+
}
|
|
1306
|
+
\`\`\`
|
|
1397
1307
|
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
const m = minutes % 60
|
|
1401
|
-
return `${h}h ${m}m`
|
|
1402
|
-
}
|
|
1308
|
+
### POST /api/${target.toLowerCase()}
|
|
1309
|
+
**Description**: Create new ${target}
|
|
1403
1310
|
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
}
|
|
1412
|
-
}
|
|
1413
|
-
byAuthor[author].tasks++
|
|
1414
|
-
byAuthor[author].totalMinutes += parseDuration(task.data.duration)
|
|
1415
|
-
})
|
|
1311
|
+
**Request**:
|
|
1312
|
+
\`\`\`json
|
|
1313
|
+
{
|
|
1314
|
+
"name": "string",
|
|
1315
|
+
"description": "string"
|
|
1316
|
+
}
|
|
1317
|
+
\`\`\`
|
|
1416
1318
|
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1319
|
+
**Response** (201 Created):
|
|
1320
|
+
\`\`\`json
|
|
1321
|
+
{
|
|
1322
|
+
"id": "string",
|
|
1323
|
+
"name": "string",
|
|
1324
|
+
"description": "string",
|
|
1325
|
+
"createdAt": "ISO8601"
|
|
1326
|
+
}
|
|
1327
|
+
\`\`\`
|
|
1421
1328
|
|
|
1422
|
-
|
|
1423
|
-
totalTime: formatTime(totalMinutes),
|
|
1424
|
-
avgDuration: formatTime(avgMinutes),
|
|
1425
|
-
tasksCompleted: completedTasks.length,
|
|
1426
|
-
longestTask: formatTime(longestMinutes),
|
|
1427
|
-
shortestTask: formatTime(shortestMinutes),
|
|
1428
|
-
byAuthor,
|
|
1429
|
-
}
|
|
1430
|
-
} catch (error) {
|
|
1431
|
-
return {
|
|
1432
|
-
totalTime: 'N/A',
|
|
1433
|
-
avgDuration: 'N/A',
|
|
1434
|
-
tasksCompleted: 0,
|
|
1435
|
-
longestTask: 'N/A',
|
|
1436
|
-
shortestTask: 'N/A',
|
|
1437
|
-
byAuthor: {},
|
|
1438
|
-
}
|
|
1439
|
-
}
|
|
1440
|
-
}
|
|
1329
|
+
## Error Handling
|
|
1441
1330
|
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
await this.initializeAgent()
|
|
1331
|
+
\`\`\`json
|
|
1332
|
+
{
|
|
1333
|
+
"error": {
|
|
1334
|
+
"code": "ERROR_CODE",
|
|
1335
|
+
"message": "Human-readable message",
|
|
1336
|
+
"details": {}
|
|
1337
|
+
}
|
|
1338
|
+
}
|
|
1339
|
+
\`\`\`
|
|
1452
1340
|
|
|
1453
|
-
|
|
1454
|
-
const initCheck = await this.ensureProjectInit(projectPath)
|
|
1455
|
-
if (!initCheck.success) {
|
|
1456
|
-
return initCheck
|
|
1457
|
-
}
|
|
1341
|
+
## Authentication
|
|
1458
1342
|
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
success: false,
|
|
1462
|
-
message: this.agent.formatResponse(
|
|
1463
|
-
`Please describe what you're stuck on: ${this.agentInfo.config.commandPrefix}stuck "issue description"`,
|
|
1464
|
-
'warning',
|
|
1465
|
-
),
|
|
1466
|
-
}
|
|
1467
|
-
}
|
|
1343
|
+
- **Method**: Bearer Token
|
|
1344
|
+
- **Header**: \`Authorization: Bearer <token>\`
|
|
1468
1345
|
|
|
1469
|
-
|
|
1346
|
+
## Rate Limiting
|
|
1470
1347
|
|
|
1471
|
-
|
|
1348
|
+
- **Limit**: 100 requests/minute
|
|
1349
|
+
- **Headers**: \`X-RateLimit-Limit\`, \`X-RateLimit-Remaining\`
|
|
1472
1350
|
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
}
|
|
1477
|
-
} catch (error) {
|
|
1478
|
-
await this.initializeAgent()
|
|
1479
|
-
return {
|
|
1480
|
-
success: false,
|
|
1481
|
-
message: this.agent.formatResponse(error.message, 'error'),
|
|
1482
|
-
}
|
|
1483
|
-
}
|
|
1351
|
+
---
|
|
1352
|
+
*Update this specification as API evolves.*
|
|
1353
|
+
`
|
|
1484
1354
|
}
|
|
1485
1355
|
|
|
1486
1356
|
/**
|
|
1487
|
-
*
|
|
1488
|
-
*
|
|
1489
|
-
* @param {string} [target='.'] - Target directory
|
|
1490
|
-
* @param {Object} [options={}] - Cleanup options
|
|
1491
|
-
* @param {string} [projectPath=process.cwd()] - Project path
|
|
1492
|
-
* @returns {Promise<Object>} Result object with success flag and message
|
|
1357
|
+
* Generate component design document
|
|
1358
|
+
* @private
|
|
1493
1359
|
*/
|
|
1494
|
-
|
|
1495
|
-
|
|
1496
|
-
|
|
1360
|
+
_generateComponentDesign(target) {
|
|
1361
|
+
return `# Component Design: ${target}
|
|
1362
|
+
|
|
1363
|
+
**Created**: ${new Date().toLocaleString()}
|
|
1364
|
+
**Type**: Component Specification
|
|
1497
1365
|
|
|
1498
|
-
|
|
1499
|
-
const mode = options.aggressive ? 'aggressive' : 'safe'
|
|
1500
|
-
const dryRun = options.dryRun || false
|
|
1366
|
+
## Overview
|
|
1501
1367
|
|
|
1502
|
-
|
|
1503
|
-
deadCode: { consoleLogs: 0, commented: 0, unused: 0 },
|
|
1504
|
-
imports: { removed: 0, organized: 0 },
|
|
1505
|
-
files: { temp: 0, empty: 0, spaceFeed: 0 },
|
|
1506
|
-
deps: { removed: 0, sizeSaved: 0 },
|
|
1507
|
-
}
|
|
1368
|
+
Component for ${target} functionality.
|
|
1508
1369
|
|
|
1509
|
-
|
|
1510
|
-
results.deadCode.consoleLogs = Math.floor(Math.random() * 20)
|
|
1511
|
-
results.deadCode.commented = Math.floor(Math.random() * 10)
|
|
1512
|
-
if (mode === 'aggressive') {
|
|
1513
|
-
results.deadCode.unused = Math.floor(Math.random() * 5)
|
|
1514
|
-
}
|
|
1515
|
-
}
|
|
1370
|
+
## Props/Interface
|
|
1516
1371
|
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
|
|
1520
|
-
|
|
1372
|
+
\`\`\`typescript
|
|
1373
|
+
interface ${target}Props {
|
|
1374
|
+
// Define props
|
|
1375
|
+
id?: string
|
|
1376
|
+
className?: string
|
|
1377
|
+
onAction?: (data: any) => void
|
|
1378
|
+
}
|
|
1379
|
+
\`\`\`
|
|
1521
1380
|
|
|
1522
|
-
|
|
1523
|
-
results.files.temp = Math.floor(Math.random() * 10)
|
|
1524
|
-
results.files.empty = Math.floor(Math.random() * 5)
|
|
1525
|
-
results.files.spaceFeed = (Math.random() * 5).toFixed(1)
|
|
1526
|
-
}
|
|
1381
|
+
## State
|
|
1527
1382
|
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
|
|
1383
|
+
\`\`\`typescript
|
|
1384
|
+
interface ${target}State {
|
|
1385
|
+
// Define internal state
|
|
1386
|
+
loading: boolean
|
|
1387
|
+
data: any[]
|
|
1388
|
+
error: Error | null
|
|
1389
|
+
}
|
|
1390
|
+
\`\`\`
|
|
1532
1391
|
|
|
1533
|
-
|
|
1534
|
-
const message = `
|
|
1535
|
-
🧹 ✨ Advanced Cleanup Complete! ✨ 🧹
|
|
1392
|
+
## Component Structure
|
|
1536
1393
|
|
|
1537
|
-
|
|
1538
|
-
|
|
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
|
+
\`\`\`
|
|
1539
1402
|
|
|
1540
|
-
|
|
1541
|
-
• Console.logs: ${results.deadCode.consoleLogs} statements
|
|
1542
|
-
• Commented code: ${results.deadCode.commented} blocks
|
|
1543
|
-
${mode === 'aggressive' ? `• Unused functions: ${results.deadCode.unused}` : ''}
|
|
1403
|
+
## Usage Example
|
|
1544
1404
|
|
|
1545
|
-
|
|
1546
|
-
|
|
1547
|
-
• Files organized: ${results.imports.organized}
|
|
1405
|
+
\`\`\`tsx
|
|
1406
|
+
import { ${target} } from '@/components/${target}'
|
|
1548
1407
|
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
|
|
1408
|
+
function App() {
|
|
1409
|
+
return (
|
|
1410
|
+
<${target}
|
|
1411
|
+
id="example"
|
|
1412
|
+
onAction={(data) => console.log(data)}
|
|
1413
|
+
/>
|
|
1414
|
+
)
|
|
1415
|
+
}
|
|
1416
|
+
\`\`\`
|
|
1553
1417
|
|
|
1554
|
-
|
|
1555
|
-
• Unused packages: ${results.deps.removed} removed
|
|
1556
|
-
• Size reduced: ${results.deps.sizeSaved} MB
|
|
1418
|
+
## Dependencies
|
|
1557
1419
|
|
|
1558
|
-
|
|
1559
|
-
|
|
1420
|
+
- React
|
|
1421
|
+
- [Other libraries]
|
|
1560
1422
|
|
|
1561
|
-
|
|
1562
|
-
💡 Tip: Run with --dry-run first to preview changes`
|
|
1423
|
+
## Implementation Notes
|
|
1563
1424
|
|
|
1564
|
-
|
|
1565
|
-
|
|
1566
|
-
|
|
1567
|
-
|
|
1568
|
-
|
|
1425
|
+
1. [ ] Setup component structure
|
|
1426
|
+
2. [ ] Implement core logic
|
|
1427
|
+
3. [ ] Add styling
|
|
1428
|
+
4. [ ] Write tests
|
|
1429
|
+
5. [ ] Document usage
|
|
1569
1430
|
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
}
|
|
1574
|
-
} catch (error) {
|
|
1575
|
-
await this.initializeAgent()
|
|
1576
|
-
return {
|
|
1577
|
-
success: false,
|
|
1578
|
-
message: this.agent.formatResponse(error.message, 'error'),
|
|
1579
|
-
}
|
|
1580
|
-
}
|
|
1431
|
+
---
|
|
1432
|
+
*Component design is iterative. Update as needed.*
|
|
1433
|
+
`
|
|
1581
1434
|
}
|
|
1582
1435
|
|
|
1583
1436
|
/**
|
|
1584
|
-
* Generate design
|
|
1585
|
-
*
|
|
1586
|
-
* @param {string} target - Design target name
|
|
1587
|
-
* @param {Object} [options={}] - Design options
|
|
1588
|
-
* @param {string} [projectPath=process.cwd()] - Project path
|
|
1589
|
-
* @returns {Promise<Object>} Result object with success flag and message
|
|
1437
|
+
* Generate database design document
|
|
1438
|
+
* @private
|
|
1590
1439
|
*/
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
await this.initializeAgent()
|
|
1440
|
+
_generateDatabaseDesign(target) {
|
|
1441
|
+
return `# Database Design: ${target}
|
|
1594
1442
|
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
return {
|
|
1598
|
-
success: false,
|
|
1599
|
-
message: this.agent.formatResponse(
|
|
1600
|
-
`Project not initialized. Run ${this.agentInfo.config.commandPrefix}init first.`,
|
|
1601
|
-
'warning'
|
|
1602
|
-
),
|
|
1603
|
-
}
|
|
1604
|
-
}
|
|
1443
|
+
**Created**: ${new Date().toLocaleString()}
|
|
1444
|
+
**Type**: Database Schema
|
|
1605
1445
|
|
|
1606
|
-
|
|
1446
|
+
## Schema
|
|
1607
1447
|
|
|
1608
|
-
|
|
1609
|
-
const projectId = await configManager.getProjectId(projectPath)
|
|
1610
|
-
const globalPath = pathManager.getGlobalProjectPath(projectId)
|
|
1611
|
-
const designDir = path.join(globalPath, 'analysis', 'designs')
|
|
1612
|
-
await this.agent.createDirectory(designDir)
|
|
1448
|
+
### Table: ${target.toLowerCase()}
|
|
1613
1449
|
|
|
1614
|
-
|
|
1615
|
-
|
|
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
|
+
\`\`\`
|
|
1616
1460
|
|
|
1617
|
-
|
|
1618
|
-
case 'architecture':
|
|
1619
|
-
diagram = `
|
|
1620
|
-
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
|
|
1621
|
-
│ Frontend │────▶│ Backend │────▶│ Database │
|
|
1622
|
-
│ React │ │ Node.js │ │ PostgreSQL │
|
|
1623
|
-
└─────────────┘ └─────────────┘ └─────────────┘
|
|
1624
|
-
│ │ │
|
|
1625
|
-
▼ ▼ ▼
|
|
1626
|
-
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
|
|
1627
|
-
│ Redux │ │ Express │ │ Redis │
|
|
1628
|
-
│ Store │ │ Routes │ │ Cache │
|
|
1629
|
-
└─────────────┘ └─────────────┘ └─────────────┘`
|
|
1630
|
-
break
|
|
1461
|
+
## Indexes
|
|
1631
1462
|
|
|
1632
|
-
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
|
|
1636
|
-
POST /api/auth/login
|
|
1637
|
-
GET /api/users/:id
|
|
1638
|
-
PUT /api/users/:id
|
|
1639
|
-
DELETE /api/users/:id`
|
|
1640
|
-
break
|
|
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
|
+
\`\`\`
|
|
1641
1467
|
|
|
1642
|
-
|
|
1643
|
-
diagram = `
|
|
1644
|
-
<App>
|
|
1645
|
-
├── <Header>
|
|
1646
|
-
│ ├── <Logo />
|
|
1647
|
-
│ ├── <Navigation />
|
|
1648
|
-
│ └── <UserMenu />
|
|
1649
|
-
├── <Main>
|
|
1650
|
-
│ ├── <Sidebar />
|
|
1651
|
-
│ └── <Content>
|
|
1652
|
-
│ ├── <Dashboard />
|
|
1653
|
-
│ └── <Routes />
|
|
1654
|
-
└── <Footer>`
|
|
1655
|
-
break
|
|
1468
|
+
## Relationships
|
|
1656
1469
|
|
|
1657
|
-
|
|
1658
|
-
|
|
1659
|
-
┌─────────────┐ ┌─────────────┐
|
|
1660
|
-
│ users │────▶│ profiles │
|
|
1661
|
-
├─────────────┤ ├─────────────┤
|
|
1662
|
-
│ id (PK) │ │ id (PK) │
|
|
1663
|
-
│ email │ │ user_id(FK) │
|
|
1664
|
-
│ password │ │ bio │
|
|
1665
|
-
│ created_at │ │ avatar_url │
|
|
1666
|
-
└─────────────┘ └─────────────┘`
|
|
1667
|
-
break
|
|
1470
|
+
- **Related Tables**: [List related tables]
|
|
1471
|
+
- **Foreign Keys**: [Define foreign keys]
|
|
1668
1472
|
|
|
1669
|
-
|
|
1670
|
-
diagram = 'Custom design diagram'
|
|
1671
|
-
}
|
|
1473
|
+
## Queries
|
|
1672
1474
|
|
|
1673
|
-
|
|
1674
|
-
const designFile = path.join(designDir, `${target.replace(/\s+/g, '-')}-${type}-${timestamp}.md`)
|
|
1475
|
+
### Common Queries
|
|
1675
1476
|
|
|
1676
|
-
|
|
1677
|
-
|
|
1678
|
-
|
|
1477
|
+
\`\`\`sql
|
|
1478
|
+
-- Get active records
|
|
1479
|
+
SELECT * FROM ${target.toLowerCase()} WHERE status = 'active';
|
|
1679
1480
|
|
|
1680
|
-
|
|
1681
|
-
|
|
1682
|
-
|
|
1481
|
+
-- Get recent records
|
|
1482
|
+
SELECT * FROM ${target.toLowerCase()}
|
|
1483
|
+
ORDER BY created_at DESC
|
|
1484
|
+
LIMIT 10;
|
|
1683
1485
|
\`\`\`
|
|
1684
1486
|
|
|
1685
|
-
##
|
|
1686
|
-
|
|
1687
|
-
|
|
1688
|
-
|
|
1689
|
-
|
|
1690
|
-
|
|
1691
|
-
|
|
1692
|
-
|
|
1693
|
-
|
|
1694
|
-
|
|
1695
|
-
|
|
1696
|
-
|
|
1487
|
+
## Migrations
|
|
1488
|
+
|
|
1489
|
+
1. [ ] Create initial schema
|
|
1490
|
+
2. [ ] Add indexes
|
|
1491
|
+
3. [ ] Setup relationships
|
|
1492
|
+
4. [ ] Add constraints
|
|
1493
|
+
|
|
1494
|
+
## Notes
|
|
1495
|
+
|
|
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.*
|
|
1697
1502
|
`
|
|
1503
|
+
}
|
|
1698
1504
|
|
|
1699
|
-
|
|
1505
|
+
/**
|
|
1506
|
+
* Generate flow design document
|
|
1507
|
+
* @private
|
|
1508
|
+
*/
|
|
1509
|
+
_generateFlowDesign(target) {
|
|
1510
|
+
return `# Flow Design: ${target}
|
|
1700
1511
|
|
|
1701
|
-
|
|
1702
|
-
|
|
1703
|
-
type,
|
|
1704
|
-
file: designFile,
|
|
1705
|
-
})
|
|
1512
|
+
**Created**: ${new Date().toLocaleString()}
|
|
1513
|
+
**Type**: Process Flow
|
|
1706
1514
|
|
|
1707
|
-
|
|
1708
|
-
🎨 ✨ Design Complete! ✨ 🎨
|
|
1515
|
+
## Flow Overview
|
|
1709
1516
|
|
|
1710
|
-
|
|
1711
|
-
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
1517
|
+
Process flow for ${target}.
|
|
1712
1518
|
|
|
1713
|
-
|
|
1714
|
-
${diagram}
|
|
1519
|
+
## Steps
|
|
1715
1520
|
|
|
1716
|
-
|
|
1717
|
-
|
|
1718
|
-
|
|
1719
|
-
|
|
1720
|
-
|
|
1521
|
+
\`\`\`
|
|
1522
|
+
1. [User Action/Trigger]
|
|
1523
|
+
↓
|
|
1524
|
+
2. [Validation]
|
|
1525
|
+
↓
|
|
1526
|
+
3. [Processing]
|
|
1527
|
+
↓
|
|
1528
|
+
4. [Side Effects]
|
|
1529
|
+
↓
|
|
1530
|
+
5. [Response/Completion]
|
|
1531
|
+
\`\`\`
|
|
1721
1532
|
|
|
1722
|
-
|
|
1723
|
-
• ${designFile}
|
|
1533
|
+
## Detailed Flow
|
|
1724
1534
|
|
|
1725
|
-
|
|
1726
|
-
|
|
1535
|
+
### Step 1: Initial Action
|
|
1536
|
+
- **Input**: [What triggers this]
|
|
1537
|
+
- **Validation**: [What gets checked]
|
|
1538
|
+
- **Output**: [What proceeds]
|
|
1727
1539
|
|
|
1728
|
-
|
|
1540
|
+
### Step 2: Processing
|
|
1541
|
+
- **Actions**: [What happens]
|
|
1542
|
+
- **Dependencies**: [What's needed]
|
|
1543
|
+
- **Side Effects**: [What changes]
|
|
1729
1544
|
|
|
1730
|
-
|
|
1731
|
-
|
|
1732
|
-
|
|
1733
|
-
|
|
1734
|
-
|
|
1735
|
-
|
|
1736
|
-
|
|
1737
|
-
|
|
1738
|
-
|
|
1739
|
-
|
|
1740
|
-
|
|
1545
|
+
### Step 3: Completion
|
|
1546
|
+
- **Success**: [What happens on success]
|
|
1547
|
+
- **Failure**: [What happens on failure]
|
|
1548
|
+
- **Notifications**: [Who gets notified]
|
|
1549
|
+
|
|
1550
|
+
## Error Handling
|
|
1551
|
+
|
|
1552
|
+
- **Error Type 1**: [Recovery strategy]
|
|
1553
|
+
- **Error Type 2**: [Recovery strategy]
|
|
1554
|
+
|
|
1555
|
+
## Rollback Strategy
|
|
1556
|
+
|
|
1557
|
+
[How to undo if needed]
|
|
1558
|
+
|
|
1559
|
+
## Monitoring
|
|
1560
|
+
|
|
1561
|
+
- **Metrics**: [What to track]
|
|
1562
|
+
- **Alerts**: [When to alert]
|
|
1563
|
+
|
|
1564
|
+
---
|
|
1565
|
+
*Document edge cases and special scenarios.*
|
|
1566
|
+
`
|
|
1741
1567
|
}
|
|
1742
1568
|
|
|
1743
1569
|
/**
|
|
1744
|
-
*
|
|
1745
|
-
*
|
|
1746
|
-
* @param {string} [projectPath=process.cwd()] - Project path
|
|
1747
|
-
* @returns {Promise<Object>} Result object with success flag and message
|
|
1570
|
+
* /p:cleanup - Clean temp files and old entries
|
|
1571
|
+
* AGENTIC EXECUTION
|
|
1748
1572
|
*/
|
|
1749
|
-
async
|
|
1573
|
+
async cleanup(_options = {}, projectPath = process.cwd()) {
|
|
1750
1574
|
try {
|
|
1751
|
-
await this.
|
|
1575
|
+
const initResult = await this.ensureProjectInit(projectPath)
|
|
1576
|
+
if (!initResult.success) return initResult
|
|
1752
1577
|
|
|
1753
|
-
|
|
1754
|
-
const initCheck = await this.ensureProjectInit(projectPath)
|
|
1755
|
-
if (!initCheck.success) {
|
|
1756
|
-
return initCheck
|
|
1757
|
-
}
|
|
1578
|
+
console.log('🧹 Cleaning up project...\n')
|
|
1758
1579
|
|
|
1759
|
-
const
|
|
1580
|
+
const context = await contextBuilder.build(projectPath)
|
|
1581
|
+
const projectId = await configManager.getProjectId(projectPath)
|
|
1760
1582
|
|
|
1761
|
-
const
|
|
1762
|
-
const nowFile = await this.agent.readFile(nowFilePath)
|
|
1763
|
-
const currentTask = nowFile.split('\n')[0].replace('# NOW: ', '').replace('# NOW', 'None')
|
|
1583
|
+
const cleaned = []
|
|
1764
1584
|
|
|
1765
|
-
|
|
1766
|
-
|
|
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)
|
|
1767
1589
|
|
|
1768
|
-
|
|
1769
|
-
|
|
1770
|
-
|
|
1771
|
-
|
|
1772
|
-
}
|
|
1773
|
-
|
|
1774
|
-
|
|
1775
|
-
|
|
1776
|
-
|
|
1777
|
-
|
|
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
|
|
1778
1610
|
.trim()
|
|
1779
1611
|
.split('\n')
|
|
1780
|
-
.filter((l) => l)
|
|
1781
|
-
|
|
1782
|
-
|
|
1783
|
-
|
|
1784
|
-
|
|
1785
|
-
|
|
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')
|
|
1786
1627
|
}
|
|
1628
|
+
} catch {
|
|
1629
|
+
cleaned.push('Ideas: No file found')
|
|
1787
1630
|
}
|
|
1788
1631
|
|
|
1789
|
-
|
|
1790
|
-
|
|
1791
|
-
|
|
1792
|
-
|
|
1793
|
-
|
|
1794
|
-
`Recent actions:\n${recentActions.join('\n') || '• No recent actions'}\n\n` +
|
|
1795
|
-
`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
|
|
1796
1637
|
|
|
1797
|
-
|
|
1798
|
-
|
|
1799
|
-
|
|
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')
|
|
1800
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 }
|
|
1801
1658
|
} catch (error) {
|
|
1802
|
-
|
|
1803
|
-
return {
|
|
1804
|
-
success: false,
|
|
1805
|
-
message: this.agent.formatResponse(error.message, 'error'),
|
|
1806
|
-
}
|
|
1659
|
+
console.error('❌ Error:', error.message)
|
|
1660
|
+
return { success: false, error: error.message }
|
|
1807
1661
|
}
|
|
1808
1662
|
}
|
|
1809
1663
|
|
|
1810
1664
|
/**
|
|
1811
|
-
*
|
|
1812
|
-
*
|
|
1813
|
-
* @param {string} projectPath - Project path
|
|
1814
|
-
* @returns {Promise<string>} Project type description
|
|
1665
|
+
* /p:progress - Show metrics for period
|
|
1666
|
+
* AGENTIC EXECUTION
|
|
1815
1667
|
*/
|
|
1816
|
-
async
|
|
1817
|
-
|
|
1668
|
+
async progress(period = 'week', projectPath = process.cwd()) {
|
|
1669
|
+
try {
|
|
1670
|
+
const initResult = await this.ensureProjectInit(projectPath)
|
|
1671
|
+
if (!initResult.success) return initResult
|
|
1818
1672
|
|
|
1819
|
-
|
|
1820
|
-
|
|
1821
|
-
|
|
1822
|
-
const deps = { ...pkg.dependencies, ...pkg.devDependencies }
|
|
1823
|
-
|
|
1824
|
-
if (deps.next) return 'Next.js project'
|
|
1825
|
-
if (deps.react) return 'React project'
|
|
1826
|
-
if (deps.vue) return 'Vue project'
|
|
1827
|
-
if (deps.express) return 'Express project'
|
|
1828
|
-
return 'Node.js project'
|
|
1829
|
-
} catch (e) {
|
|
1830
|
-
return 'Node.js project'
|
|
1673
|
+
const validPeriods = ['day', 'week', 'month', 'all']
|
|
1674
|
+
if (!validPeriods.includes(period)) {
|
|
1675
|
+
period = 'week'
|
|
1831
1676
|
}
|
|
1832
|
-
}
|
|
1833
1677
|
|
|
1834
|
-
|
|
1835
|
-
if (files.includes('go.mod')) return 'Go project'
|
|
1836
|
-
if (files.includes('requirements.txt')) return 'Python project'
|
|
1837
|
-
if (files.includes('Gemfile')) return 'Ruby project'
|
|
1678
|
+
console.log(`📈 Progress Report (${period})\n`)
|
|
1838
1679
|
|
|
1839
|
-
|
|
1840
|
-
|
|
1680
|
+
const projectId = await configManager.getProjectId(projectPath)
|
|
1681
|
+
const memoryPath = pathManager.getFilePath(projectId, 'memory', 'context.jsonl')
|
|
1841
1682
|
|
|
1842
|
-
|
|
1843
|
-
|
|
1844
|
-
|
|
1845
|
-
* @param {Date} date - Date to get week number for
|
|
1846
|
-
* @returns {number} Week number
|
|
1847
|
-
*/
|
|
1848
|
-
getWeekNumber(date) {
|
|
1849
|
-
const firstDayOfYear = new Date(date.getFullYear(), 0, 1)
|
|
1850
|
-
const pastDaysOfYear = (date - firstDayOfYear) / 86400000
|
|
1851
|
-
return Math.ceil((pastDaysOfYear + firstDayOfYear.getDay() + 1) / 7)
|
|
1852
|
-
}
|
|
1683
|
+
// Calculate time range
|
|
1684
|
+
const now = new Date()
|
|
1685
|
+
let startDate
|
|
1853
1686
|
|
|
1854
|
-
|
|
1855
|
-
|
|
1856
|
-
|
|
1857
|
-
|
|
1858
|
-
|
|
1859
|
-
|
|
1860
|
-
|
|
1861
|
-
|
|
1862
|
-
|
|
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
|
+
}
|
|
1863
1701
|
|
|
1864
|
-
//
|
|
1865
|
-
|
|
1866
|
-
|
|
1867
|
-
|
|
1868
|
-
|
|
1869
|
-
|
|
1870
|
-
|
|
1871
|
-
|
|
1872
|
-
|
|
1873
|
-
|
|
1874
|
-
|
|
1875
|
-
|
|
1876
|
-
|
|
1877
|
-
|
|
1878
|
-
|
|
1879
|
-
|
|
1880
|
-
|
|
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,
|
|
1881
1725
|
}
|
|
1882
|
-
|
|
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
|
+
})
|
|
1766
|
+
|
|
1767
|
+
return { success: true, period, metrics }
|
|
1768
|
+
} catch (error) {
|
|
1769
|
+
console.error('❌ Error:', error.message)
|
|
1770
|
+
return { success: false, error: error.message }
|
|
1883
1771
|
}
|
|
1884
|
-
return Infinity
|
|
1885
1772
|
}
|
|
1886
1773
|
|
|
1887
1774
|
/**
|
|
1888
|
-
*
|
|
1889
|
-
*
|
|
1890
|
-
* @param {string} projectPath - Project path
|
|
1891
|
-
* @param {string} action - Action type
|
|
1892
|
-
* @param {Object} data - Action data
|
|
1775
|
+
* /p:roadmap - Show roadmap with ASCII logic maps
|
|
1776
|
+
* AGENTIC EXECUTION
|
|
1893
1777
|
*/
|
|
1894
|
-
async
|
|
1895
|
-
|
|
1896
|
-
|
|
1778
|
+
async roadmap(projectPath = process.cwd()) {
|
|
1779
|
+
try {
|
|
1780
|
+
const initResult = await this.ensureProjectInit(projectPath)
|
|
1781
|
+
if (!initResult.success) return initResult
|
|
1897
1782
|
|
|
1898
|
-
|
|
1783
|
+
console.log('🗺️ Project Roadmap\n')
|
|
1899
1784
|
|
|
1900
|
-
|
|
1901
|
-
const entry = {
|
|
1902
|
-
action,
|
|
1903
|
-
author: this.currentAuthor,
|
|
1904
|
-
data,
|
|
1905
|
-
timestamp: new Date().toISOString(),
|
|
1906
|
-
}
|
|
1785
|
+
const context = await contextBuilder.build(projectPath)
|
|
1907
1786
|
|
|
1908
|
-
|
|
1909
|
-
|
|
1910
|
-
|
|
1911
|
-
|
|
1912
|
-
|
|
1787
|
+
// Read roadmap content
|
|
1788
|
+
const roadmapContent = await toolRegistry.get('Read')(context.paths.roadmap)
|
|
1789
|
+
|
|
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' }
|
|
1913
1797
|
}
|
|
1914
|
-
|
|
1915
|
-
|
|
1798
|
+
|
|
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 }
|
|
1916
1814
|
}
|
|
1917
1815
|
}
|
|
1918
1816
|
|
|
1919
1817
|
/**
|
|
1920
|
-
*
|
|
1921
|
-
*
|
|
1818
|
+
* Generate roadmap template
|
|
1922
1819
|
* @private
|
|
1923
|
-
* @param {string} projectPath - Project path
|
|
1924
|
-
* @param {string} action - Action type
|
|
1925
|
-
* @param {Object} data - Action data
|
|
1926
1820
|
*/
|
|
1927
|
-
|
|
1928
|
-
|
|
1929
|
-
|
|
1930
|
-
action,
|
|
1931
|
-
author: this.currentAuthor,
|
|
1932
|
-
data,
|
|
1933
|
-
timestamp: new Date().toISOString(),
|
|
1934
|
-
}) + '\n'
|
|
1821
|
+
_generateRoadmapTemplate() {
|
|
1822
|
+
return `
|
|
1823
|
+
# ROADMAP
|
|
1935
1824
|
|
|
1936
|
-
|
|
1937
|
-
|
|
1938
|
-
|
|
1939
|
-
|
|
1940
|
-
|
|
1941
|
-
|
|
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
|
+
`
|
|
1942
1851
|
}
|
|
1943
1852
|
|
|
1944
1853
|
/**
|
|
1945
|
-
*
|
|
1946
|
-
*
|
|
1947
|
-
*
|
|
1948
|
-
* @param {string} projectPath - Project path
|
|
1949
|
-
* @param {string} [period='week'] - Time period: 'day', 'week', 'month', 'all'
|
|
1950
|
-
* @param {string} [filename='context.jsonl'] - File to read from sessions
|
|
1951
|
-
* @returns {Promise<Array<Object>>} Consolidated entries
|
|
1854
|
+
* /p:status - KPI dashboard with ASCII graphics
|
|
1855
|
+
* AGENTIC EXECUTION
|
|
1952
1856
|
*/
|
|
1953
|
-
async
|
|
1954
|
-
|
|
1955
|
-
|
|
1956
|
-
|
|
1957
|
-
|
|
1958
|
-
|
|
1959
|
-
|
|
1960
|
-
|
|
1961
|
-
|
|
1962
|
-
|
|
1963
|
-
|
|
1964
|
-
|
|
1965
|
-
|
|
1966
|
-
|
|
1967
|
-
|
|
1968
|
-
|
|
1969
|
-
|
|
1970
|
-
|
|
1971
|
-
|
|
1972
|
-
|
|
1973
|
-
|
|
1974
|
-
|
|
1975
|
-
|
|
1976
|
-
|
|
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
|
+
}
|
|
1977
1909
|
|
|
1978
|
-
|
|
1979
|
-
|
|
1980
|
-
|
|
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('')
|
|
1981
1915
|
|
|
1982
|
-
|
|
1983
|
-
|
|
1984
|
-
|
|
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('')
|
|
1985
1921
|
|
|
1986
|
-
|
|
1987
|
-
|
|
1988
|
-
|
|
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('')
|
|
1989
1927
|
|
|
1990
|
-
|
|
1991
|
-
|
|
1992
|
-
|
|
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
|
+
})
|
|
1993
1945
|
|
|
1994
|
-
|
|
1995
|
-
|
|
1996
|
-
|
|
1997
|
-
return
|
|
1946
|
+
return { success: true, stats, health }
|
|
1947
|
+
} catch (error) {
|
|
1948
|
+
console.error('❌ Error:', error.message)
|
|
1949
|
+
return { success: false, error: error.message }
|
|
1998
1950
|
}
|
|
1999
1951
|
}
|
|
2000
1952
|
|
|
2001
1953
|
/**
|
|
2002
|
-
*
|
|
2003
|
-
*
|
|
1954
|
+
* Render ASCII progress bar
|
|
2004
1955
|
* @private
|
|
2005
|
-
* @param {string} projectPath - Project path
|
|
2006
|
-
* @param {string} filename - Filename to read
|
|
2007
|
-
* @returns {Promise<Array<Object>>} Parsed entries
|
|
2008
1956
|
*/
|
|
2009
|
-
|
|
2010
|
-
const
|
|
2011
|
-
|
|
2012
|
-
|
|
2013
|
-
|
|
2014
|
-
|
|
2015
|
-
|
|
2016
|
-
|
|
2017
|
-
return JSON.parse(line)
|
|
2018
|
-
} catch {
|
|
2019
|
-
return null
|
|
2020
|
-
}
|
|
2021
|
-
}).filter(Boolean)
|
|
2022
|
-
} catch {
|
|
2023
|
-
return []
|
|
2024
|
-
}
|
|
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}%`)
|
|
2025
1965
|
}
|
|
2026
1966
|
|
|
2027
1967
|
/**
|
|
2028
|
-
*
|
|
2029
|
-
*
|
|
2030
|
-
* @param {string} projectPath - Project path
|
|
2031
|
-
* @param {number} [days=7] - Number of days to look back
|
|
2032
|
-
* @returns {Promise<Array<Object>>} Recent log entries
|
|
1968
|
+
* Calculate project health score
|
|
1969
|
+
* @private
|
|
2033
1970
|
*/
|
|
2034
|
-
|
|
2035
|
-
|
|
1971
|
+
_calculateHealth(stats) {
|
|
1972
|
+
let score = 50 // Base score
|
|
2036
1973
|
|
|
2037
|
-
|
|
2038
|
-
|
|
2039
|
-
|
|
2040
|
-
|
|
2041
|
-
|
|
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 }
|
|
2042
1996
|
}
|
|
2043
1997
|
|
|
2044
1998
|
/**
|
|
2045
|
-
*
|
|
2046
|
-
*
|
|
2047
|
-
* @param {string} [projectPath=process.cwd()] - Project path
|
|
2048
|
-
* @returns {Promise<Object>} Result object with success flag and message
|
|
1999
|
+
* /p:build - Start task with agent assignment
|
|
2000
|
+
* AGENTIC EXECUTION
|
|
2049
2001
|
*/
|
|
2050
|
-
async
|
|
2002
|
+
async build(taskOrNumber, projectPath = process.cwd()) {
|
|
2051
2003
|
try {
|
|
2052
|
-
await this.
|
|
2053
|
-
|
|
2054
|
-
|
|
2055
|
-
|
|
2056
|
-
|
|
2057
|
-
|
|
2058
|
-
|
|
2059
|
-
|
|
2060
|
-
|
|
2061
|
-
|
|
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' }
|
|
2062
2037
|
}
|
|
2063
2038
|
}
|
|
2064
2039
|
|
|
2065
|
-
|
|
2066
|
-
|
|
2067
|
-
|
|
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' }
|
|
2045
|
+
}
|
|
2068
2046
|
|
|
2069
|
-
|
|
2070
|
-
let filesRemoved = 0
|
|
2071
|
-
let tasksArchived = 0
|
|
2047
|
+
console.log(`🏗️ Building: ${task}\n`)
|
|
2072
2048
|
|
|
2073
|
-
|
|
2074
|
-
|
|
2075
|
-
|
|
2076
|
-
for (const file of tempFiles) {
|
|
2077
|
-
const filePath = path.join(tempDir, file)
|
|
2078
|
-
const stats = await fs.stat(filePath)
|
|
2079
|
-
totalFreed += stats.size
|
|
2080
|
-
await fs.unlink(filePath)
|
|
2081
|
-
filesRemoved++
|
|
2082
|
-
}
|
|
2083
|
-
} catch (e) {
|
|
2084
|
-
}
|
|
2049
|
+
// Detect complexity and estimate
|
|
2050
|
+
const complexity = this._detectComplexity(task)
|
|
2051
|
+
const estimate = complexity.hours
|
|
2085
2052
|
|
|
2086
|
-
|
|
2087
|
-
|
|
2088
|
-
|
|
2089
|
-
|
|
2090
|
-
const now = new Date()
|
|
2091
|
-
const thirtyDaysAgo = new Date(now.getTime() - 30 * 86400000)
|
|
2092
|
-
|
|
2093
|
-
const recentLines = []
|
|
2094
|
-
const archivedLines = []
|
|
2095
|
-
|
|
2096
|
-
for (const line of lines) {
|
|
2097
|
-
try {
|
|
2098
|
-
const entry = JSON.parse(line)
|
|
2099
|
-
const entryDate = new Date(entry.timestamp || entry.data?.timestamp)
|
|
2100
|
-
if (entryDate > thirtyDaysAgo) {
|
|
2101
|
-
recentLines.push(line)
|
|
2102
|
-
} else {
|
|
2103
|
-
archivedLines.push(line)
|
|
2104
|
-
}
|
|
2105
|
-
} catch {
|
|
2106
|
-
recentLines.push(line)
|
|
2107
|
-
}
|
|
2108
|
-
}
|
|
2053
|
+
console.log('📊 Analysis:')
|
|
2054
|
+
console.log(` Complexity: ${complexity.level}`)
|
|
2055
|
+
console.log(` Estimated: ${estimate}h`)
|
|
2056
|
+
console.log(` Type: ${complexity.type}\n`)
|
|
2109
2057
|
|
|
2110
|
-
|
|
2111
|
-
|
|
2112
|
-
|
|
2113
|
-
await this.agent.writeFile(memoryFile, recentLines.join('\n') + '\n')
|
|
2114
|
-
tasksArchived = archivedLines.length
|
|
2115
|
-
}
|
|
2116
|
-
} catch (e) {
|
|
2117
|
-
}
|
|
2058
|
+
// Auto-assign agent (simplified)
|
|
2059
|
+
const agent = this._autoAssignAgent(task)
|
|
2060
|
+
console.log(`🤖 Agent: ${agent}\n`)
|
|
2118
2061
|
|
|
2119
|
-
|
|
2120
|
-
|
|
2121
|
-
if (file.endsWith('.md') || file.endsWith('.txt')) {
|
|
2122
|
-
const filePath = path.join(globalPath, file)
|
|
2123
|
-
const stats = await fs.stat(filePath)
|
|
2124
|
-
if (stats.size === 0) {
|
|
2125
|
-
await fs.unlink(filePath)
|
|
2126
|
-
filesRemoved++
|
|
2127
|
-
}
|
|
2128
|
-
}
|
|
2129
|
-
}
|
|
2062
|
+
// Set as current task with metadata
|
|
2063
|
+
const nowContentNew = `# NOW
|
|
2130
2064
|
|
|
2131
|
-
|
|
2132
|
-
const shippedFile = path.join(globalPath, 'progress', 'shipped.md')
|
|
2133
|
-
const content = await this.agent.readFile(shippedFile)
|
|
2134
|
-
const lines = content.split('\n')
|
|
2135
|
-
const now = new Date()
|
|
2136
|
-
const thirtyDaysAgo = new Date(now.getTime() - 30 * 86400000)
|
|
2137
|
-
|
|
2138
|
-
const filteredLines = lines.filter(line => {
|
|
2139
|
-
if (line.includes('✅')) {
|
|
2140
|
-
const dateMatch = line.match(/\((.*?)\)/)
|
|
2141
|
-
if (dateMatch) {
|
|
2142
|
-
const taskDate = new Date(dateMatch[1])
|
|
2143
|
-
if (taskDate < thirtyDaysAgo) {
|
|
2144
|
-
tasksArchived++
|
|
2145
|
-
return false
|
|
2146
|
-
}
|
|
2147
|
-
}
|
|
2148
|
-
}
|
|
2149
|
-
return true
|
|
2150
|
-
})
|
|
2065
|
+
**${task}**
|
|
2151
2066
|
|
|
2152
|
-
|
|
2153
|
-
|
|
2154
|
-
|
|
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)
|
|
2073
|
+
|
|
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')
|
|
2079
|
+
|
|
2080
|
+
await this.logToMemory(projectPath, 'task_built', {
|
|
2081
|
+
task,
|
|
2082
|
+
complexity: complexity.level,
|
|
2083
|
+
estimate,
|
|
2084
|
+
agent,
|
|
2085
|
+
timestamp: dateHelper.getTimestamp(),
|
|
2086
|
+
})
|
|
2087
|
+
|
|
2088
|
+
return { success: true, task, complexity, estimate, agent }
|
|
2089
|
+
} catch (error) {
|
|
2090
|
+
console.error('❌ Error:', error.message)
|
|
2091
|
+
return { success: false, error: error.message }
|
|
2092
|
+
}
|
|
2093
|
+
}
|
|
2094
|
+
|
|
2095
|
+
/**
|
|
2096
|
+
* Detect task complexity
|
|
2097
|
+
* @private
|
|
2098
|
+
*/
|
|
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
|
+
}
|
|
2155
2116
|
|
|
2156
|
-
|
|
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
|
|
2124
|
+
}
|
|
2125
|
+
}
|
|
2157
2126
|
|
|
2158
|
-
|
|
2159
|
-
|
|
2160
|
-
|
|
2161
|
-
|
|
2162
|
-
'\n✨ Your project is clean and lean!'
|
|
2127
|
+
// Estimate hours
|
|
2128
|
+
if (level === 'high') hours = 8
|
|
2129
|
+
else if (level === 'medium') hours = 4
|
|
2130
|
+
else hours = 2
|
|
2163
2131
|
|
|
2164
|
-
|
|
2165
|
-
|
|
2166
|
-
tasksArchived,
|
|
2167
|
-
spaceFeed: freedMB,
|
|
2168
|
-
})
|
|
2132
|
+
return { level, hours, type }
|
|
2133
|
+
}
|
|
2169
2134
|
|
|
2170
|
-
|
|
2171
|
-
|
|
2172
|
-
|
|
2173
|
-
|
|
2174
|
-
|
|
2175
|
-
|
|
2176
|
-
|
|
2177
|
-
|
|
2178
|
-
|
|
2179
|
-
|
|
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'
|
|
2180
2161
|
}
|
|
2162
|
+
if (lowerTask.includes('deploy') || lowerTask.includes('docker')) {
|
|
2163
|
+
return 'devops-specialist'
|
|
2164
|
+
}
|
|
2165
|
+
|
|
2166
|
+
return 'generalist'
|
|
2181
2167
|
}
|
|
2182
2168
|
|
|
2183
2169
|
/**
|
|
2184
|
-
*
|
|
2185
|
-
*
|
|
2186
|
-
* @param {Object} [options={}] - Migration options
|
|
2187
|
-
* @returns {Promise<Object>} Result object with summary
|
|
2170
|
+
* /p:analyze - Analyze repository and generate summary
|
|
2171
|
+
* AGENTIC EXECUTION
|
|
2188
2172
|
*/
|
|
2189
|
-
async
|
|
2173
|
+
async analyze(options = {}, projectPath = process.cwd()) {
|
|
2190
2174
|
try {
|
|
2191
2175
|
await this.initializeAgent()
|
|
2192
2176
|
|
|
2193
|
-
|
|
2194
|
-
|
|
2195
|
-
|
|
2196
|
-
|
|
2197
|
-
|
|
2198
|
-
|
|
2199
|
-
|
|
2200
|
-
|
|
2201
|
-
|
|
2202
|
-
|
|
2203
|
-
|
|
2204
|
-
|
|
2205
|
-
|
|
2206
|
-
|
|
2207
|
-
|
|
2208
|
-
|
|
2209
|
-
|
|
2210
|
-
|
|
2211
|
-
|
|
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,
|
|
2212
2234
|
})
|
|
2213
2235
|
|
|
2214
|
-
|
|
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')
|
|
2215
2241
|
|
|
2216
2242
|
return {
|
|
2217
|
-
success:
|
|
2218
|
-
|
|
2219
|
-
|
|
2243
|
+
success: true,
|
|
2244
|
+
summaryPath,
|
|
2245
|
+
data: analysisData,
|
|
2220
2246
|
}
|
|
2221
2247
|
} catch (error) {
|
|
2222
|
-
|
|
2223
|
-
return {
|
|
2224
|
-
success: false,
|
|
2225
|
-
message: this.agent.formatResponse(`Global migration failed: ${error.message}`, 'error'),
|
|
2226
|
-
}
|
|
2248
|
+
console.error('❌ Error:', error.message)
|
|
2249
|
+
return { success: false, error: error.message }
|
|
2227
2250
|
}
|
|
2228
2251
|
}
|
|
2229
2252
|
|
|
2230
2253
|
/**
|
|
2231
|
-
*
|
|
2232
|
-
*
|
|
2233
|
-
* @
|
|
2254
|
+
* Generate analysis summary from collected data
|
|
2255
|
+
* Claude decides what's relevant - NO predetermined patterns
|
|
2256
|
+
* @private
|
|
2234
2257
|
*/
|
|
2235
|
-
|
|
2236
|
-
|
|
2237
|
-
|
|
2238
|
-
|
|
2239
|
-
|
|
2240
|
-
|
|
2241
|
-
|
|
2242
|
-
|
|
2243
|
-
|
|
2244
|
-
|
|
2245
|
-
|
|
2246
|
-
|
|
2247
|
-
|
|
2248
|
-
|
|
2249
|
-
|
|
2250
|
-
|
|
2251
|
-
|
|
2252
|
-
|
|
2253
|
-
|
|
2254
|
-
|
|
2255
|
-
|
|
2256
|
-
|
|
2257
|
-
console.log(chalk.bold.magenta('📦 Setup - Install Commands to Claude\n'))
|
|
2258
|
-
|
|
2259
|
-
// Detect Claude
|
|
2260
|
-
const commandInstaller = require('./command-installer')
|
|
2261
|
-
const claudeDetected = await commandInstaller.detectClaude()
|
|
2262
|
-
|
|
2263
|
-
if (!claudeDetected) {
|
|
2264
|
-
return {
|
|
2265
|
-
success: false,
|
|
2266
|
-
message: this.agent.formatResponse(
|
|
2267
|
-
'Claude not detected.\n\nPlease install Claude Code or Claude Desktop first.\n\nVisit: https://claude.ai/download',
|
|
2268
|
-
'error'
|
|
2269
|
-
),
|
|
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
|
+
)
|
|
2270
2280
|
}
|
|
2271
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
|
+
}
|
|
2272
2287
|
|
|
2273
|
-
|
|
2274
|
-
|
|
2275
|
-
|
|
2276
|
-
|
|
2277
|
-
|
|
2278
|
-
const installResult = await commandInstaller.installCommands()
|
|
2288
|
+
if (data.cargoToml) {
|
|
2289
|
+
lines.push('### Rust\n')
|
|
2290
|
+
lines.push('- **Package Manager**: Cargo')
|
|
2291
|
+
lines.push('- **Language**: Rust\n')
|
|
2292
|
+
}
|
|
2279
2293
|
|
|
2280
|
-
|
|
2281
|
-
|
|
2282
|
-
|
|
2283
|
-
|
|
2284
|
-
|
|
2285
|
-
'error'
|
|
2286
|
-
),
|
|
2287
|
-
}
|
|
2288
|
-
}
|
|
2294
|
+
if (data.goMod) {
|
|
2295
|
+
lines.push('### Go\n')
|
|
2296
|
+
lines.push('- **Package Manager**: Go modules')
|
|
2297
|
+
lines.push('- **Language**: Go\n')
|
|
2298
|
+
}
|
|
2289
2299
|
|
|
2290
|
-
|
|
2291
|
-
|
|
2292
|
-
|
|
2300
|
+
if (data.requirements) {
|
|
2301
|
+
lines.push('### Python\n')
|
|
2302
|
+
lines.push('- **Package Manager**: pip')
|
|
2303
|
+
lines.push('- **Language**: Python\n')
|
|
2304
|
+
}
|
|
2293
2305
|
|
|
2294
|
-
|
|
2295
|
-
|
|
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})`)
|
|
2333
|
+
}
|
|
2334
|
+
})
|
|
2335
|
+
lines.push('')
|
|
2336
|
+
}
|
|
2296
2337
|
|
|
2297
|
-
|
|
2298
|
-
|
|
2299
|
-
|
|
2338
|
+
// Recommendations
|
|
2339
|
+
lines.push('## Recommendations\n')
|
|
2340
|
+
lines.push('Based on detected stack, consider generating specialized agents using `/p:sync`.\n')
|
|
2300
2341
|
|
|
2301
|
-
|
|
2302
|
-
|
|
2303
|
-
|
|
2304
|
-
|
|
2305
|
-
message += '\n\nℹ️ Context7 MCP is automatically available in Claude'
|
|
2342
|
+
lines.push('---\n')
|
|
2343
|
+
lines.push(
|
|
2344
|
+
'*This analysis was generated automatically. For updated information, run `/p:analyze` again.*\n'
|
|
2345
|
+
)
|
|
2306
2346
|
|
|
2307
|
-
|
|
2308
|
-
success: true,
|
|
2309
|
-
message: this.agent.formatResponse(message, 'celebrate'),
|
|
2310
|
-
}
|
|
2311
|
-
} catch (error) {
|
|
2312
|
-
await this.initializeAgent()
|
|
2313
|
-
return {
|
|
2314
|
-
success: false,
|
|
2315
|
-
message: this.agent.formatResponse(`Setup failed: ${error.message}`, 'error'),
|
|
2316
|
-
}
|
|
2317
|
-
}
|
|
2347
|
+
return lines.join('\n')
|
|
2318
2348
|
}
|
|
2319
2349
|
|
|
2320
2350
|
/**
|
|
2321
|
-
*
|
|
2322
|
-
*
|
|
2323
|
-
* @param {Object} [options={}] - Installation options
|
|
2324
|
-
* @returns {Promise<Object>} Result object with success flag and message
|
|
2351
|
+
* /p:sync - Sync project state and generate dynamic agents
|
|
2352
|
+
* AGENTIC EXECUTION
|
|
2325
2353
|
*/
|
|
2326
|
-
async
|
|
2354
|
+
async sync(projectPath = process.cwd()) {
|
|
2327
2355
|
try {
|
|
2356
|
+
const initResult = await this.ensureProjectInit(projectPath)
|
|
2357
|
+
if (!initResult.success) return initResult
|
|
2358
|
+
|
|
2328
2359
|
await this.initializeAgent()
|
|
2329
2360
|
|
|
2330
|
-
|
|
2361
|
+
console.log('🔄 Syncing project state...\n')
|
|
2331
2362
|
|
|
2332
|
-
//
|
|
2333
|
-
const
|
|
2334
|
-
const claudeDetected = await commandInstaller.detectClaude()
|
|
2363
|
+
// Build context
|
|
2364
|
+
const context = await contextBuilder.build(projectPath)
|
|
2335
2365
|
|
|
2336
|
-
|
|
2337
|
-
|
|
2338
|
-
|
|
2339
|
-
message: this.agent.formatResponse(
|
|
2340
|
-
'Claude not detected. Please install Claude Code or Claude Desktop first.',
|
|
2341
|
-
'error'
|
|
2342
|
-
),
|
|
2343
|
-
}
|
|
2344
|
-
}
|
|
2366
|
+
// Step 1: Run analysis to get current state
|
|
2367
|
+
console.log('📊 Running analysis...')
|
|
2368
|
+
const analysisResult = await this.analyze({}, projectPath)
|
|
2345
2369
|
|
|
2346
|
-
|
|
2347
|
-
|
|
2370
|
+
if (!analysisResult.success) {
|
|
2371
|
+
console.error('❌ Analysis failed')
|
|
2372
|
+
return analysisResult
|
|
2373
|
+
}
|
|
2348
2374
|
|
|
2349
|
-
|
|
2350
|
-
|
|
2351
|
-
let report = chalk.green('✓ Commands already installed\n')
|
|
2352
|
-
report += chalk.dim(` Location: ${status.path}\n`)
|
|
2353
|
-
report += chalk.dim(` Commands: ${status.commands.length}\n`)
|
|
2354
|
-
report += '\nUse --force to reinstall'
|
|
2375
|
+
// Step 2: Read analysis/repo-summary.md
|
|
2376
|
+
const summaryContent = await toolRegistry.get('Read')(context.paths.analysis)
|
|
2355
2377
|
|
|
2356
|
-
|
|
2357
|
-
|
|
2358
|
-
|
|
2359
|
-
}
|
|
2378
|
+
if (!summaryContent) {
|
|
2379
|
+
console.error('❌ No analysis found. Run /p:analyze first.')
|
|
2380
|
+
return { success: false, error: 'No analysis found' }
|
|
2360
2381
|
}
|
|
2361
2382
|
|
|
2362
|
-
|
|
2363
|
-
const installResult = force
|
|
2364
|
-
? await commandInstaller.updateCommands()
|
|
2365
|
-
: await commandInstaller.installCommands()
|
|
2383
|
+
console.log('✅ Analysis loaded\n')
|
|
2366
2384
|
|
|
2367
|
-
|
|
2368
|
-
|
|
2369
|
-
|
|
2370
|
-
message: this.agent.formatResponse(
|
|
2371
|
-
`Installation failed: ${installResult.error || 'Unknown error'}`,
|
|
2372
|
-
'error'
|
|
2373
|
-
),
|
|
2374
|
-
}
|
|
2375
|
-
}
|
|
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')
|
|
2376
2388
|
|
|
2377
|
-
|
|
2378
|
-
const
|
|
2379
|
-
const
|
|
2389
|
+
const projectId = await configManager.getProjectId(projectPath)
|
|
2390
|
+
const AgentGenerator = require('./domain/agent-generator')
|
|
2391
|
+
const generator = new AgentGenerator(projectId)
|
|
2380
2392
|
|
|
2381
|
-
|
|
2382
|
-
report += ` Location: ${installResult.path}\n`
|
|
2393
|
+
const generatedAgents = await this._generateAgentsFromAnalysis(summaryContent, generator)
|
|
2383
2394
|
|
|
2384
|
-
|
|
2385
|
-
|
|
2386
|
-
|
|
2387
|
-
|
|
2388
|
-
|
|
2389
|
-
}
|
|
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
|
+
})
|
|
2390
2401
|
|
|
2391
|
-
|
|
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')
|
|
2392
2412
|
|
|
2393
2413
|
return {
|
|
2394
2414
|
success: true,
|
|
2395
|
-
|
|
2415
|
+
agents: generatedAgents,
|
|
2396
2416
|
}
|
|
2397
2417
|
} catch (error) {
|
|
2398
|
-
|
|
2399
|
-
return {
|
|
2400
|
-
success: false,
|
|
2401
|
-
message: this.agent.formatResponse(`Installation failed: ${error.message}`, 'error'),
|
|
2402
|
-
}
|
|
2418
|
+
console.error('❌ Error:', error.message)
|
|
2419
|
+
return { success: false, error: error.message }
|
|
2403
2420
|
}
|
|
2404
2421
|
}
|
|
2405
2422
|
|
|
2406
2423
|
/**
|
|
2407
|
-
*
|
|
2408
|
-
*
|
|
2409
|
-
* @
|
|
2410
|
-
* @param {string} [projectPath=process.cwd()] - Project path
|
|
2411
|
-
* @returns {Promise<Object>} Result object with analysis and sync results
|
|
2424
|
+
* Generate agents dynamically from analysis summary
|
|
2425
|
+
* Claude decides based on what technologies are detected
|
|
2426
|
+
* @private
|
|
2412
2427
|
*/
|
|
2413
|
-
async
|
|
2414
|
-
|
|
2415
|
-
|
|
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
|
+
}
|
|
2416
2460
|
|
|
2417
|
-
|
|
2418
|
-
|
|
2419
|
-
|
|
2420
|
-
|
|
2421
|
-
|
|
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
|
+
}
|
|
2422
2471
|
|
|
2423
|
-
|
|
2424
|
-
|
|
2425
|
-
|
|
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
|
+
}
|
|
2481
|
+
|
|
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
|
+
}
|
|
2426
2491
|
|
|
2427
|
-
|
|
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
|
+
}
|
|
2428
2501
|
|
|
2429
|
-
|
|
2430
|
-
|
|
2431
|
-
|
|
2432
|
-
|
|
2433
|
-
|
|
2434
|
-
|
|
2435
|
-
|
|
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')
|
|
2436
2510
|
|
|
2437
|
-
|
|
2438
|
-
|
|
2439
|
-
|
|
2440
|
-
|
|
2441
|
-
|
|
2511
|
+
return agents
|
|
2512
|
+
}
|
|
2513
|
+
|
|
2514
|
+
/**
|
|
2515
|
+
* First-time setup - Install commands to editors
|
|
2516
|
+
*/
|
|
2517
|
+
async start() {
|
|
2518
|
+
const commandInstaller = require('./infrastructure/command-installer')
|
|
2442
2519
|
|
|
2443
|
-
|
|
2520
|
+
console.log('🚀 Setting up prjct for Claude...\n')
|
|
2444
2521
|
|
|
2445
|
-
|
|
2446
|
-
|
|
2447
|
-
} else if (reportOnly) {
|
|
2448
|
-
message = this.formatAnalysisReport(summary, analysis)
|
|
2449
|
-
} else if (sync) {
|
|
2450
|
-
message = this.formatAnalysisWithSync(summary, syncResults)
|
|
2451
|
-
} else {
|
|
2452
|
-
message = this.formatAnalysisReport(summary, analysis)
|
|
2453
|
-
}
|
|
2522
|
+
// Check if Claude is installed
|
|
2523
|
+
const status = await commandInstaller.checkInstallation()
|
|
2454
2524
|
|
|
2525
|
+
if (!status.claudeDetected) {
|
|
2455
2526
|
return {
|
|
2456
|
-
success:
|
|
2457
|
-
message:
|
|
2458
|
-
|
|
2459
|
-
|
|
2527
|
+
success: false,
|
|
2528
|
+
message:
|
|
2529
|
+
'❌ Claude not detected.\n\nPlease install Claude Code or Claude Desktop first:\n' +
|
|
2530
|
+
' - Claude Code: https://claude.com/code\n' +
|
|
2531
|
+
' - Claude Desktop: https://claude.com/desktop',
|
|
2460
2532
|
}
|
|
2461
|
-
}
|
|
2462
|
-
|
|
2533
|
+
}
|
|
2534
|
+
|
|
2535
|
+
// Install commands
|
|
2536
|
+
console.log('📦 Installing /p:* commands...')
|
|
2537
|
+
const result = await commandInstaller.installCommands()
|
|
2538
|
+
|
|
2539
|
+
if (!result.success) {
|
|
2463
2540
|
return {
|
|
2464
2541
|
success: false,
|
|
2465
|
-
message:
|
|
2542
|
+
message: `❌ Installation failed: ${result.error}`,
|
|
2466
2543
|
}
|
|
2467
2544
|
}
|
|
2545
|
+
|
|
2546
|
+
console.log(`\n✅ Installed ${result.installed.length} commands to:\n ${result.path}`)
|
|
2547
|
+
|
|
2548
|
+
if (result.errors.length > 0) {
|
|
2549
|
+
console.log(`\n⚠️ ${result.errors.length} errors:`)
|
|
2550
|
+
result.errors.forEach((e) => console.log(` - ${e.file}: ${e.error}`))
|
|
2551
|
+
}
|
|
2552
|
+
|
|
2553
|
+
console.log('\n🎉 Setup complete!')
|
|
2554
|
+
console.log('\nNext steps:')
|
|
2555
|
+
console.log(' 1. Open Claude Code or Claude Desktop')
|
|
2556
|
+
console.log(' 2. Navigate to your project')
|
|
2557
|
+
console.log(' 3. Run: /p:init')
|
|
2558
|
+
|
|
2559
|
+
return {
|
|
2560
|
+
success: true,
|
|
2561
|
+
message: '',
|
|
2562
|
+
}
|
|
2468
2563
|
}
|
|
2469
2564
|
|
|
2470
2565
|
/**
|
|
2471
|
-
*
|
|
2472
|
-
*
|
|
2473
|
-
* @param {Object} summary - Analysis summary
|
|
2474
|
-
* @param {Object} analysis - Full analysis results
|
|
2475
|
-
* @returns {string} Formatted report
|
|
2566
|
+
* Reconfigure editor installations
|
|
2476
2567
|
*/
|
|
2477
|
-
|
|
2478
|
-
|
|
2479
|
-
🔍 Codebase Analysis Complete
|
|
2480
|
-
|
|
2481
|
-
📊 Project Overview:
|
|
2482
|
-
• Technologies: ${summary.technologies || 'Not detected'}
|
|
2483
|
-
• Total Files: ~${summary.fileCount}
|
|
2484
|
-
• Git Repository: ${summary.hasGit ? '✅ Yes' : '❌ No'}
|
|
2568
|
+
async setup(options = {}) {
|
|
2569
|
+
const commandInstaller = require('./infrastructure/command-installer')
|
|
2485
2570
|
|
|
2486
|
-
|
|
2487
|
-
${analysis.commands.slice(0, 10).map(cmd => ` • /p:${cmd}`).join('\n')}
|
|
2488
|
-
${analysis.commands.length > 10 ? ` ... and ${analysis.commands.length - 10} more` : ''}
|
|
2571
|
+
console.log('🔧 Reconfiguring prjct...\n')
|
|
2489
2572
|
|
|
2490
|
-
|
|
2491
|
-
|
|
2492
|
-
|
|
2573
|
+
if (options.force) {
|
|
2574
|
+
console.log('🗑️ Removing existing installation...')
|
|
2575
|
+
await commandInstaller.uninstallCommands()
|
|
2576
|
+
}
|
|
2493
2577
|
|
|
2494
|
-
|
|
2578
|
+
// Reinstall commands
|
|
2579
|
+
console.log('📦 Installing /p:* commands...')
|
|
2580
|
+
const result = await commandInstaller.updateCommands()
|
|
2495
2581
|
|
|
2496
|
-
|
|
2497
|
-
|
|
2498
|
-
|
|
2582
|
+
if (!result.success) {
|
|
2583
|
+
return {
|
|
2584
|
+
success: false,
|
|
2585
|
+
message: `❌ Setup failed: ${result.error}`,
|
|
2586
|
+
}
|
|
2587
|
+
}
|
|
2499
2588
|
|
|
2500
|
-
|
|
2501
|
-
* Format analysis with sync results
|
|
2502
|
-
*
|
|
2503
|
-
* @param {Object} summary - Analysis summary
|
|
2504
|
-
* @param {Object} syncResults - Sync results
|
|
2505
|
-
* @returns {string} Formatted report with sync info
|
|
2506
|
-
*/
|
|
2507
|
-
formatAnalysisWithSync(summary, syncResults) {
|
|
2508
|
-
return `
|
|
2509
|
-
🔍 Analysis & Sync Complete
|
|
2589
|
+
console.log(`\n✅ Installed ${result.installed.length} commands`)
|
|
2510
2590
|
|
|
2511
|
-
|
|
2512
|
-
|
|
2513
|
-
|
|
2591
|
+
if (result.errors.length > 0) {
|
|
2592
|
+
console.log(`\n⚠️ ${result.errors.length} errors:`)
|
|
2593
|
+
result.errors.forEach((e) => console.log(` - ${e.file}: ${e.error}`))
|
|
2594
|
+
}
|
|
2514
2595
|
|
|
2515
|
-
|
|
2516
|
-
${syncResults.nextMdUpdated ? `✅ Updated next.md (${syncResults.tasksMarkedComplete} tasks marked complete)` : '• next.md (no changes)'}
|
|
2517
|
-
${syncResults.shippedMdUpdated ? `✅ Updated shipped.md (${syncResults.featuresAdded} features added)` : '• shipped.md (no changes)'}
|
|
2518
|
-
✅ Created analysis/repo-summary.md
|
|
2596
|
+
console.log('\n🎉 Setup complete!')
|
|
2519
2597
|
|
|
2520
|
-
|
|
2521
|
-
|
|
2598
|
+
return {
|
|
2599
|
+
success: true,
|
|
2600
|
+
message: '',
|
|
2601
|
+
}
|
|
2522
2602
|
}
|
|
2523
2603
|
|
|
2524
2604
|
/**
|
|
2525
|
-
*
|
|
2526
|
-
* Re-analyzes the project and regenerates workflow agents in global project directory
|
|
2527
|
-
*
|
|
2528
|
-
* @param {string} [projectPath=process.cwd()] - Project directory path
|
|
2529
|
-
* @returns {Promise<Object>} Result with success status and message
|
|
2605
|
+
* Migrate all legacy projects
|
|
2530
2606
|
*/
|
|
2531
|
-
async
|
|
2532
|
-
|
|
2533
|
-
|
|
2607
|
+
async migrateAll(options = {}) {
|
|
2608
|
+
const fs = require('fs').promises
|
|
2609
|
+
const path = require('path')
|
|
2534
2610
|
|
|
2535
|
-
|
|
2536
|
-
|
|
2537
|
-
|
|
2538
|
-
|
|
2539
|
-
|
|
2540
|
-
|
|
2541
|
-
|
|
2542
|
-
|
|
2543
|
-
|
|
2544
|
-
|
|
2611
|
+
console.log('🔄 Scanning for legacy prjct projects...\n')
|
|
2612
|
+
|
|
2613
|
+
const homeDir = require('os').homedir()
|
|
2614
|
+
const globalRoot = path.join(homeDir, '.prjct-cli', 'projects')
|
|
2615
|
+
|
|
2616
|
+
// Get all project IDs
|
|
2617
|
+
let projectIds = []
|
|
2618
|
+
try {
|
|
2619
|
+
const dirs = await fs.readdir(globalRoot)
|
|
2620
|
+
projectIds = dirs.filter((d) => !d.startsWith('.'))
|
|
2621
|
+
} catch (error) {
|
|
2622
|
+
return {
|
|
2623
|
+
success: false,
|
|
2624
|
+
message: '❌ No prjct projects found',
|
|
2545
2625
|
}
|
|
2626
|
+
}
|
|
2546
2627
|
|
|
2547
|
-
|
|
2628
|
+
console.log(`📁 Found ${projectIds.length} projects in global storage\n`)
|
|
2548
2629
|
|
|
2549
|
-
|
|
2550
|
-
|
|
2630
|
+
const migrated = []
|
|
2631
|
+
const failed = []
|
|
2632
|
+
const skipped = []
|
|
2551
2633
|
|
|
2552
|
-
|
|
2553
|
-
|
|
2554
|
-
success: false,
|
|
2555
|
-
message: this.agent.formatResponse(`Sync failed: ${analysisResult.message}`, 'error'),
|
|
2556
|
-
}
|
|
2557
|
-
}
|
|
2634
|
+
for (const projectId of projectIds) {
|
|
2635
|
+
const globalProjectPath = path.join(globalRoot, projectId)
|
|
2558
2636
|
|
|
2559
|
-
|
|
2560
|
-
const
|
|
2561
|
-
|
|
2562
|
-
|
|
2637
|
+
// Read global config to get project path
|
|
2638
|
+
const globalConfig = await configManager.readGlobalConfig(projectId)
|
|
2639
|
+
if (!globalConfig || !globalConfig.projectPath) {
|
|
2640
|
+
skipped.push({ projectId, reason: 'No project path in config' })
|
|
2641
|
+
continue
|
|
2563
2642
|
}
|
|
2564
2643
|
|
|
2565
|
-
|
|
2566
|
-
console.log(` ✅ ${summary.commandsFound} commands detected`)
|
|
2567
|
-
console.log(` ✅ ${summary.featuresFound} features implemented\n`)
|
|
2644
|
+
const projectPath = globalConfig.projectPath
|
|
2568
2645
|
|
|
2569
|
-
//
|
|
2570
|
-
|
|
2571
|
-
|
|
2646
|
+
// Check if needs migration
|
|
2647
|
+
if (!(await migrator.needsMigration(projectPath))) {
|
|
2648
|
+
skipped.push({ projectId, reason: 'Already migrated' })
|
|
2649
|
+
continue
|
|
2650
|
+
}
|
|
2572
2651
|
|
|
2573
|
-
|
|
2652
|
+
console.log(`🔄 Migrating: ${projectPath}`)
|
|
2574
2653
|
|
|
2575
|
-
|
|
2576
|
-
|
|
2577
|
-
commandsDetected: summary.commandsFound,
|
|
2578
|
-
featuresDetected: summary.featuresFound,
|
|
2579
|
-
agentsGenerated: generatedAgents.length,
|
|
2580
|
-
agents: generatedAgents,
|
|
2581
|
-
})
|
|
2654
|
+
try {
|
|
2655
|
+
const result = await migrator.migrate(projectPath, options)
|
|
2582
2656
|
|
|
2583
|
-
|
|
2584
|
-
|
|
2585
|
-
|
|
2586
|
-
|
|
2657
|
+
if (result.success) {
|
|
2658
|
+
migrated.push({ projectId, path: projectPath })
|
|
2659
|
+
console.log(` ✅ ${result.message}`)
|
|
2660
|
+
} else {
|
|
2661
|
+
failed.push({ projectId, path: projectPath, error: result.message })
|
|
2662
|
+
console.log(` ❌ ${result.message}`)
|
|
2663
|
+
}
|
|
2664
|
+
} catch (error) {
|
|
2665
|
+
failed.push({ projectId, path: projectPath, error: error.message })
|
|
2666
|
+
console.log(` ❌ ${error.message}`)
|
|
2667
|
+
}
|
|
2587
2668
|
|
|
2588
|
-
|
|
2589
|
-
|
|
2669
|
+
console.log('')
|
|
2670
|
+
}
|
|
2590
2671
|
|
|
2591
|
-
|
|
2592
|
-
|
|
2593
|
-
✅ ${
|
|
2672
|
+
// Summary
|
|
2673
|
+
console.log('\n📊 Migration Summary:')
|
|
2674
|
+
console.log(` ✅ Migrated: ${migrated.length}`)
|
|
2675
|
+
console.log(` ⏭️ Skipped: ${skipped.length}`)
|
|
2676
|
+
console.log(` ❌ Failed: ${failed.length}`)
|
|
2594
2677
|
|
|
2595
|
-
|
|
2596
|
-
|
|
2597
|
-
|
|
2678
|
+
if (failed.length > 0) {
|
|
2679
|
+
console.log('\n❌ Failed migrations:')
|
|
2680
|
+
failed.forEach((f) => console.log(` - ${f.path}: ${f.error}`))
|
|
2681
|
+
}
|
|
2598
2682
|
|
|
2599
|
-
|
|
2600
|
-
|
|
2683
|
+
return {
|
|
2684
|
+
success: failed.length === 0,
|
|
2685
|
+
message: '',
|
|
2686
|
+
}
|
|
2687
|
+
}
|
|
2601
2688
|
|
|
2602
|
-
|
|
2603
|
-
|
|
2604
|
-
|
|
2605
|
-
|
|
2606
|
-
|
|
2607
|
-
} catch (error) {
|
|
2608
|
-
await this.initializeAgent()
|
|
2689
|
+
/**
|
|
2690
|
+
* Execute architect plan and generate code
|
|
2691
|
+
*/
|
|
2692
|
+
async architect(action = 'execute', projectPath = process.cwd()) {
|
|
2693
|
+
if (action !== 'execute') {
|
|
2609
2694
|
return {
|
|
2610
2695
|
success: false,
|
|
2611
|
-
message:
|
|
2696
|
+
message: '❌ Invalid action. Use: /p:architect execute',
|
|
2612
2697
|
}
|
|
2613
2698
|
}
|
|
2614
|
-
}
|
|
2615
2699
|
|
|
2616
|
-
/**
|
|
2617
|
-
* Show workflow status
|
|
2618
|
-
*
|
|
2619
|
-
* @param {string} [projectPath=process.cwd()] - Project path
|
|
2620
|
-
* @returns {Promise<Object>} Result object with success flag and message
|
|
2621
|
-
*/
|
|
2622
|
-
async workflow(projectPath = process.cwd()) {
|
|
2623
2700
|
try {
|
|
2624
|
-
await this.
|
|
2701
|
+
const initResult = await this.ensureProjectInit(projectPath)
|
|
2702
|
+
if (!initResult.success) return initResult
|
|
2625
2703
|
|
|
2626
|
-
|
|
2627
|
-
const initCheck = await this.ensureProjectInit(projectPath)
|
|
2628
|
-
if (!initCheck.success) {
|
|
2629
|
-
return initCheck
|
|
2630
|
-
}
|
|
2704
|
+
console.log('🏗️ Architect Mode - Code Generation\n')
|
|
2631
2705
|
|
|
2632
|
-
const
|
|
2633
|
-
const
|
|
2706
|
+
const globalPath = await this.getGlobalProjectPath(projectPath)
|
|
2707
|
+
const architectSession = require('./domain/architect-session')
|
|
2634
2708
|
|
|
2635
|
-
|
|
2709
|
+
// Check if there's a completed plan
|
|
2710
|
+
const planPath = path.join(globalPath, 'planning', 'architect-session.md')
|
|
2636
2711
|
|
|
2637
|
-
|
|
2712
|
+
let planContent
|
|
2713
|
+
try {
|
|
2714
|
+
planContent = await fileHelper.readFile(planPath)
|
|
2715
|
+
} catch (error) {
|
|
2638
2716
|
return {
|
|
2639
|
-
success:
|
|
2640
|
-
message:
|
|
2641
|
-
|
|
2642
|
-
'
|
|
2643
|
-
|
|
2717
|
+
success: false,
|
|
2718
|
+
message:
|
|
2719
|
+
'❌ No architect plan found.\n\n' +
|
|
2720
|
+
'Create a plan first:\n' +
|
|
2721
|
+
' 1. Run /p:init in an empty directory\n' +
|
|
2722
|
+
' 2. Answer the discovery questions\n' +
|
|
2723
|
+
' 3. Plan will be auto-generated\n' +
|
|
2724
|
+
' 4. Then run /p:architect execute',
|
|
2644
2725
|
}
|
|
2645
2726
|
}
|
|
2646
2727
|
|
|
2647
|
-
|
|
2648
|
-
|
|
2649
|
-
|
|
2650
|
-
|
|
2651
|
-
|
|
2652
|
-
message += `📋 Task: ${workflow.task}\n\n`
|
|
2653
|
-
|
|
2654
|
-
if (completedSteps.length > 0) {
|
|
2655
|
-
message += `✅ Completed:\n`
|
|
2656
|
-
completedSteps.forEach((step) => {
|
|
2657
|
-
message += ` - ${step.name}: ${step.action} (${step.agent})\n`
|
|
2658
|
-
})
|
|
2659
|
-
message += '\n'
|
|
2660
|
-
}
|
|
2661
|
-
|
|
2662
|
-
message += `🎯 Current Step:\n`
|
|
2663
|
-
message += ` - ${currentStep.name}: ${currentStep.action} (${currentStep.agent})\n`
|
|
2664
|
-
if (currentStep.required) {
|
|
2665
|
-
message += ` Required: Yes\n`
|
|
2728
|
+
if (!planContent || planContent.trim() === '') {
|
|
2729
|
+
return {
|
|
2730
|
+
success: false,
|
|
2731
|
+
message: '❌ Architect plan is empty',
|
|
2732
|
+
}
|
|
2666
2733
|
}
|
|
2667
|
-
message += '\n'
|
|
2668
2734
|
|
|
2669
|
-
|
|
2670
|
-
message += `⏳ Remaining:\n`
|
|
2671
|
-
remainingSteps.forEach((step) => {
|
|
2672
|
-
message += ` - ${step.name}: ${step.action} (${step.agent})\n`
|
|
2673
|
-
})
|
|
2674
|
-
message += '\n'
|
|
2675
|
-
}
|
|
2735
|
+
console.log('📋 Reading architect plan...\n')
|
|
2676
2736
|
|
|
2677
|
-
|
|
2737
|
+
// Extract key information from plan
|
|
2738
|
+
const ideaMatch = planContent.match(/## Project Idea\n(.+)/s)
|
|
2739
|
+
const stackMatch = planContent.match(/\*\*Stack:\*\*\n([\s\S]+?)\n\n/)
|
|
2740
|
+
const stepsMatch = planContent.match(/\*\*Implementation Steps:\*\*\n([\s\S]+?)\n\n/)
|
|
2678
2741
|
|
|
2679
|
-
|
|
2680
|
-
|
|
2681
|
-
|
|
2682
|
-
workflow,
|
|
2683
|
-
}
|
|
2684
|
-
} catch (error) {
|
|
2685
|
-
await this.initializeAgent()
|
|
2686
|
-
return {
|
|
2687
|
-
success: false,
|
|
2688
|
-
message: this.agent.formatResponse(`Workflow status failed: ${error.message}`, 'error'),
|
|
2689
|
-
}
|
|
2690
|
-
}
|
|
2691
|
-
}
|
|
2742
|
+
const idea = ideaMatch ? ideaMatch[1].split('\n')[0].trim() : 'Unknown project'
|
|
2743
|
+
const stack = stackMatch ? stackMatch[1] : 'Not specified'
|
|
2744
|
+
const steps = stepsMatch ? stepsMatch[1] : 'Not specified'
|
|
2692
2745
|
|
|
2693
|
-
|
|
2694
|
-
|
|
2695
|
-
|
|
2696
|
-
* @param {string} projectPath - Project path
|
|
2697
|
-
* @returns {Promise<boolean>} True if project has significant existing code
|
|
2698
|
-
*/
|
|
2699
|
-
async detectExistingCode(projectPath) {
|
|
2700
|
-
try {
|
|
2701
|
-
const packagePath = path.join(projectPath, 'package.json')
|
|
2702
|
-
try {
|
|
2703
|
-
const content = await fs.readFile(packagePath, 'utf-8')
|
|
2704
|
-
const pkg = JSON.parse(content)
|
|
2746
|
+
console.log(`📝 Project: ${idea}`)
|
|
2747
|
+
console.log(`\n🔧 Stack:\n${stack}`)
|
|
2748
|
+
console.log(`\n📋 Implementation Steps:\n${steps}`)
|
|
2705
2749
|
|
|
2706
|
-
|
|
2707
|
-
|
|
2708
|
-
|
|
2709
|
-
} catch {
|
|
2710
|
-
}
|
|
2750
|
+
console.log('\n' + '='.repeat(60))
|
|
2751
|
+
console.log('🤖 READY TO GENERATE CODE')
|
|
2752
|
+
console.log('='.repeat(60))
|
|
2711
2753
|
|
|
2712
|
-
|
|
2713
|
-
|
|
2714
|
-
|
|
2715
|
-
|
|
2716
|
-
|
|
2717
|
-
|
|
2718
|
-
|
|
2719
|
-
}
|
|
2754
|
+
console.log(
|
|
2755
|
+
'\nThe architect plan is ready. Claude will now:\n' +
|
|
2756
|
+
' 1. Read the architectural plan\n' +
|
|
2757
|
+
' 2. Use Context7 for official documentation\n' +
|
|
2758
|
+
' 3. Generate project structure\n' +
|
|
2759
|
+
' 4. Create starter files with boilerplate\n'
|
|
2760
|
+
)
|
|
2720
2761
|
|
|
2721
|
-
|
|
2722
|
-
|
|
2762
|
+
console.log('\n💡 This command shows the plan.')
|
|
2763
|
+
console.log(' For code generation, Claude Code will read this plan')
|
|
2764
|
+
console.log(' and generate the structure automatically.\n')
|
|
2723
2765
|
|
|
2724
|
-
|
|
2725
|
-
|
|
2726
|
-
|
|
2727
|
-
|
|
2728
|
-
}
|
|
2766
|
+
await this.logToMemory(projectPath, 'architect_executed', {
|
|
2767
|
+
timestamp: dateHelper.getTimestamp(),
|
|
2768
|
+
idea,
|
|
2769
|
+
})
|
|
2729
2770
|
|
|
2730
|
-
|
|
2731
|
-
|
|
2732
|
-
|
|
2733
|
-
|
|
2771
|
+
return {
|
|
2772
|
+
success: true,
|
|
2773
|
+
plan: planContent,
|
|
2774
|
+
idea,
|
|
2734
2775
|
}
|
|
2735
|
-
|
|
2736
|
-
return codeFileCount >= 5
|
|
2737
2776
|
} catch (error) {
|
|
2738
|
-
|
|
2777
|
+
console.error('❌ Error:', error.message)
|
|
2778
|
+
return { success: false, error: error.message }
|
|
2739
2779
|
}
|
|
2740
2780
|
}
|
|
2741
2781
|
}
|
|
2742
2782
|
|
|
2743
|
-
module.exports =
|
|
2783
|
+
module.exports = PrjctCommands
|