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.
Files changed (83) hide show
  1. package/CHANGELOG.md +67 -6
  2. package/CLAUDE.md +442 -36
  3. package/README.md +47 -54
  4. package/bin/prjct +174 -240
  5. package/core/agentic/command-executor.js +113 -0
  6. package/core/agentic/context-builder.js +85 -0
  7. package/core/agentic/prompt-builder.js +86 -0
  8. package/core/agentic/template-loader.js +104 -0
  9. package/core/agentic/tool-registry.js +117 -0
  10. package/core/command-registry.js +109 -65
  11. package/core/commands.js +2213 -2173
  12. package/core/domain/agent-generator.js +118 -0
  13. package/core/domain/analyzer.js +211 -0
  14. package/core/domain/architect-session.js +300 -0
  15. package/core/{agents → infrastructure/agents}/claude-agent.js +16 -13
  16. package/core/{author-detector.js → infrastructure/author-detector.js} +3 -1
  17. package/core/{capability-installer.js → infrastructure/capability-installer.js} +3 -6
  18. package/core/{command-installer.js → infrastructure/command-installer.js} +5 -3
  19. package/core/{config-manager.js → infrastructure/config-manager.js} +4 -4
  20. package/core/{editors-config.js → infrastructure/editors-config.js} +2 -10
  21. package/core/{migrator.js → infrastructure/migrator.js} +34 -19
  22. package/core/{path-manager.js → infrastructure/path-manager.js} +20 -44
  23. package/core/{session-manager.js → infrastructure/session-manager.js} +45 -105
  24. package/core/{update-checker.js → infrastructure/update-checker.js} +67 -67
  25. package/core/{animations-simple.js → utils/animations.js} +3 -23
  26. package/core/utils/date-helper.js +238 -0
  27. package/core/utils/file-helper.js +327 -0
  28. package/core/utils/jsonl-helper.js +206 -0
  29. package/core/{project-capabilities.js → utils/project-capabilities.js} +21 -22
  30. package/core/utils/session-helper.js +277 -0
  31. package/core/{version.js → utils/version.js} +1 -1
  32. package/package.json +4 -12
  33. package/templates/agents/AGENTS.md +101 -27
  34. package/templates/analysis/analyze.md +84 -0
  35. package/templates/commands/analyze.md +9 -2
  36. package/templates/commands/bug.md +79 -0
  37. package/templates/commands/build.md +5 -2
  38. package/templates/commands/cleanup.md +5 -2
  39. package/templates/commands/design.md +5 -2
  40. package/templates/commands/done.md +4 -2
  41. package/templates/commands/feature.md +113 -0
  42. package/templates/commands/fix.md +41 -10
  43. package/templates/commands/git.md +7 -2
  44. package/templates/commands/help.md +2 -2
  45. package/templates/commands/idea.md +14 -5
  46. package/templates/commands/init.md +62 -7
  47. package/templates/commands/next.md +4 -2
  48. package/templates/commands/now.md +4 -2
  49. package/templates/commands/progress.md +27 -5
  50. package/templates/commands/recap.md +39 -10
  51. package/templates/commands/roadmap.md +19 -5
  52. package/templates/commands/ship.md +118 -16
  53. package/templates/commands/status.md +4 -2
  54. package/templates/commands/sync.md +19 -15
  55. package/templates/commands/task.md +4 -2
  56. package/templates/commands/test.md +5 -2
  57. package/templates/commands/workflow.md +4 -2
  58. package/core/agent-generator.js +0 -525
  59. package/core/analyzer.js +0 -600
  60. package/core/animations.js +0 -277
  61. package/core/ascii-graphics.js +0 -433
  62. package/core/git-integration.js +0 -401
  63. package/core/task-schema.js +0 -342
  64. package/core/workflow-engine.js +0 -213
  65. package/core/workflow-prompts.js +0 -192
  66. package/core/workflow-rules.js +0 -147
  67. package/scripts/post-install.js +0 -121
  68. package/scripts/preuninstall.js +0 -94
  69. package/scripts/verify-installation.sh +0 -158
  70. package/templates/agents/be.template.md +0 -27
  71. package/templates/agents/coordinator.template.md +0 -34
  72. package/templates/agents/data.template.md +0 -27
  73. package/templates/agents/devops.template.md +0 -27
  74. package/templates/agents/fe.template.md +0 -27
  75. package/templates/agents/mobile.template.md +0 -27
  76. package/templates/agents/qa.template.md +0 -27
  77. package/templates/agents/scribe.template.md +0 -29
  78. package/templates/agents/security.template.md +0 -27
  79. package/templates/agents/ux.template.md +0 -27
  80. package/templates/commands/context.md +0 -36
  81. package/templates/commands/stuck.md +0 -36
  82. package/templates/examples/natural-language-examples.md +0 -532
  83. /package/core/{agent-detector.js → infrastructure/agent-detector.js} +0 -0
package/core/commands.js CHANGED
@@ -1,36 +1,39 @@
1
- const fs = require('fs').promises
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
- let Agent
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
- * Main command handler for prjct CLI
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
- * Check for updates and show notification (non-blocking)
47
- * Only shows once per session
50
+ * Initialize agent (Claude Code, Desktop, or Terminal)
48
51
  */
49
- async checkAndNotifyUpdates() {
50
- if (this.updateNotificationShown) {
51
- return
52
- }
52
+ async initializeAgent() {
53
+ if (this.agent) return this.agent
53
54
 
54
- try {
55
- const notification = await this.updateChecker.getUpdateNotification()
56
- if (notification) {
57
- console.log(notification)
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
- * Generate semantic branch name from task description
67
- *
68
- * @param {string} task - Task description
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
- * Execute git command with error handling
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 execGitCommand(command, cwd = process.cwd()) {
111
- try {
112
- const { stdout, stderr } = await exec(command, { cwd })
113
- return { success: true, stdout: stdout.trim(), stderr: stderr.trim() }
114
- } catch (error) {
115
- return { success: false, error: error.message }
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
- * Check if current directory is a git repository
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 isGitRepo(projectPath = process.cwd()) {
126
- const result = await this.execGitCommand('git rev-parse --is-inside-work-tree', projectPath)
127
- return result.success && result.stdout === 'true'
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
- * Create and switch to a new git branch
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 createAndSwitchBranch(branchName, projectPath = process.cwd()) {
138
- if (!await this.isGitRepo(projectPath)) {
139
- return { success: false, message: 'Not a git repository' }
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 statusResult = await this.execGitCommand('git status --porcelain', projectPath)
143
- if (statusResult.stdout) {
144
- await this.execGitCommand('git stash push -m "Auto-stash before branch creation"', projectPath)
145
- }
104
+ const projectId = await configManager.getProjectId(projectPath)
105
+ await pathManager.ensureProjectStructure(projectId)
106
+ return pathManager.getGlobalProjectPath(projectId)
107
+ }
146
108
 
147
- const branchExists = await this.execGitCommand(`git show-ref --verify --quiet refs/heads/${branchName}`, projectPath)
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
- if (branchExists.success) {
150
- const switchResult = await this.execGitCommand(`git checkout ${branchName}`, projectPath)
151
- if (!switchResult.success) {
152
- return { success: false, message: `Failed to switch to existing branch: ${branchName}` }
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
- if (statusResult.stdout) {
164
- await this.execGitCommand('git stash pop', projectPath)
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
- * Initialize agent detection and load appropriate adapter
172
- * Also handles automatic global migration on first run
173
- *
174
- * @returns {Promise<Object>} Initialized agent instance
132
+ * /p:now - Set or show current task
133
+ * AGENTIC EXECUTION
175
134
  */
176
- async initializeAgent() {
177
- if (this.agent) return this.agent
178
-
179
- this.agentInfo = await agentDetector.detect()
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
- console.debug(`[prjct] Detected agent: ${this.agentInfo.name} (${this.agentInfo.type})`)
140
+ const context = await contextBuilder.build(projectPath, { task })
182
141
 
183
- switch (this.agentInfo.type) {
184
- case 'claude':
185
- Agent = require('./agents/claude-agent')
186
- break
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
- this.agent = new Agent()
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
- // Check for updates in background (non-blocking)
199
- this.checkAndNotifyUpdates().catch(() => {
200
- // Fail silently - don't interrupt workflow
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
- return this.agent
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
- * Check if automatic migration is needed and run it transparently
208
- * This runs only once per installation using a flag file
209
- *
210
- * @private
179
+ * /p:done - Complete current task
180
+ * AGENTIC EXECUTION
211
181
  */
212
- async checkAndRunAutoMigration() {
182
+ async done(projectPath = process.cwd()) {
213
183
  try {
214
- const flagPath = path.join(pathManager.getGlobalBasePath(), '.auto-migrated')
184
+ const initResult = await this.ensureProjectInit(projectPath)
185
+ if (!initResult.success) return initResult
215
186
 
216
- try {
217
- await fs.access(flagPath)
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
- const summary = await migrator.migrateAll({
223
- deepScan: true,
224
- removeLegacy: false,
225
- cleanupLegacy: true,
226
- dryRun: false,
227
- onProgress: null,
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
- await fs.mkdir(pathManager.getGlobalBasePath(), { recursive: true })
231
- await fs.writeFile(flagPath, JSON.stringify({
232
- migratedAt: new Date().toISOString(),
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
- * Ensure author information is loaded
244
- *
245
- * @returns {Promise<Object>} Current author information
246
- */
247
- async ensureAuthor() {
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
- * Auto-initialize project if not configured
255
- * Shows warning and runs init automatically
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
- const chalk = require('chalk')
266
- console.log(chalk.yellow('⚠️ Project not initialized'))
267
- console.log(chalk.cyan('🔧 Running prjct init...\n'))
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
- const initResult = await this.init(projectPath)
270
- if (!initResult.success) {
271
- return initResult
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
- * Get the global project path for a project
280
- * Ensures migration if needed
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 getGlobalProjectPath(projectPath) {
287
- if (await migrator.needsMigration(projectPath)) {
288
- throw new Error('Project needs migration. Run /p:migrate first.')
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
- const projectId = await configManager.getProjectId(projectPath)
242
+ const context = await contextBuilder.build(projectPath)
243
+ const nextContent = await toolRegistry.get('Read')(context.paths.next)
292
244
 
293
- await pathManager.ensureProjectStructure(projectId)
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
- return pathManager.getGlobalProjectPath(projectId)
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
- * Get file path in global structure
300
- *
301
- * @param {string} projectPath - Local project path
302
- * @param {string} layer - Layer name (core, progress, planning, etc.)
303
- * @param {string} filename - File name
304
- * @returns {Promise<string>} Full file path
305
- */
306
- async getFilePath(projectPath, layer, filename) {
307
- const projectId = await configManager.getProjectId(projectPath)
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
- * Initialize a new prjct project
272
+ * /p:init - Initialize prjct project
273
+ * AGENTIC EXECUTION
313
274
  *
314
- * @param {string} [projectPath=process.cwd()] - Project path
315
- * @returns {Promise<Object>} Result object with success flag and message
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 local config exists
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
- // Use existing ID from local config
330
- const existingConfig = await configManager.readConfig(projectPath)
331
- projectId = existingConfig.projectId
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
- // Check if global data exists
356
- const globalExists = await this.checkGlobalDataExists(globalPath)
357
-
358
- if (!globalExists) {
359
- // Create fresh global structure
360
- const files = {
361
- 'core/now.md': '# NOW\n\nNo current task. Use `/p:now` to set focus.\n',
362
- 'core/next.md': '# NEXT\n\n## Priority Queue\n\n',
363
- 'core/context.md': '# CONTEXT\n\n',
364
- 'progress/shipped.md': '# SHIPPED 🚀\n\n',
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
- // Merge local data if exists
380
- if (existingLocalData) {
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
- // Clean up local data (keep only prjct.config.json)
387
- await this.cleanupLocalData(projectPath)
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
- const projectInfo = await this.detectProjectType(projectPath)
330
+ // MODE 1: Existing project
331
+ if (hasCode || !isEmpty) {
332
+ console.log('\n📊 Existing project detected - analyzing...\n')
390
333
 
391
- let analysisMessage = ''
392
- const hasExistingCode = await this.detectExistingCode(projectPath)
334
+ // Run analysis
335
+ const analysisResult = await this.analyze({}, projectPath)
393
336
 
394
- if (hasExistingCode) {
395
- // Instead of silently analyzing, prompt the AI agent to run /p:analyze workflow
396
- analysisMessage = '\n\n📊 Existing codebase detected!\n' +
397
- `\n💡 Run ${this.agentInfo.config.commandPrefix}analyze to analyze your project and sync tasks`
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
- const displayPath = pathManager.getDisplayPath(globalPath)
401
- const fusionMessage = fusionPerformed ? '\n🔄 Merged local data with global' : ''
402
- const message =
403
- `Initializing prjct v${VERSION} for ${this.agentInfo.name}...\n` +
404
- `✅ Project ID: ${projectId}\n` +
405
- `✅ Global data: ${displayPath}\n` +
406
- `👤 Author: ${authorDetector.formatAuthor(author)}\n` +
407
- `📋 Project: ${projectInfo}` +
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
- return {
413
- success: true,
414
- message: this.agent.formatResponse(message, 'celebrate'),
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
- await this.initializeAgent()
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
- * Check if global data exists for a project
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 checkGlobalDataExists(globalPath) {
396
+ async _detectEmptyDirectory(projectPath) {
433
397
  try {
434
- const nowFile = path.join(globalPath, 'core', 'now.md')
435
- await fs.access(nowFile)
436
- return true
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 false
409
+ return true
439
410
  }
440
411
  }
441
412
 
442
413
  /**
443
- * Collect all data from local .prjct directory
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 collectLocalData(localPrjctPath) {
450
- const data = {
451
- core: {},
452
- progress: {},
453
- planning: {},
454
- memory: {},
455
- }
456
-
417
+ async _detectExistingCode(projectPath) {
457
418
  try {
458
- // Collect core files
459
- const coreFiles = ['now.md', 'next.md', 'context.md']
460
- for (const file of coreFiles) {
461
- try {
462
- const filePath = path.join(localPrjctPath, file)
463
- data.core[file] = await this.agent.readFile(filePath)
464
- } catch {
465
- // File doesn't exist locally
466
- }
467
- }
468
-
469
- // Collect progress files
470
- const progressFiles = ['shipped.md', 'metrics.md']
471
- for (const file of progressFiles) {
472
- try {
473
- const filePath = path.join(localPrjctPath, file)
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
- * Merge local project data with global data
507
- *
508
- * @param {Object} localData - Local data collected from .prjct
509
- * @param {string} globalPath - Global project path
510
- * @returns {Promise<void>}
511
- * @private
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 mergeProjectData(localData, globalPath) {
446
+ async feature(description, projectPath = process.cwd()) {
514
447
  try {
515
- // Merge core files
516
- for (const [filename, localContent] of Object.entries(localData.core)) {
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
- const merged = this.intelligentMerge(localContent, globalContent, filename)
528
- await this.agent.writeFile(globalFilePath, merged)
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
- // Merge progress files
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
- const globalFilePath = path.join(globalPath, 'progress', filename)
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
- const merged = this.intelligentMerge(localContent, globalContent, filename)
544
- await this.agent.writeFile(globalFilePath, merged)
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
- // Merge planning files
548
- for (const [filename, localContent] of Object.entries(localData.planning)) {
549
- if (!localContent) continue
468
+ // Task breakdown (Claude would generate based on feature complexity)
469
+ const tasks = this._breakdownFeatureTasks(description)
550
470
 
551
- const globalFilePath = path.join(globalPath, 'planning', filename)
552
- let globalContent = ''
553
- try {
554
- globalContent = await this.agent.readFile(globalFilePath)
555
- } catch {
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
- const merged = this.intelligentMerge(localContent, globalContent, filename)
560
- await this.agent.writeFile(globalFilePath, merged)
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
- // Merge memory (chronologically)
564
- if (localData.memory['context.jsonl']) {
565
- const globalMemoryPath = path.join(globalPath, 'memory', 'context.jsonl')
566
- let globalMemory = ''
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
- const merged = this.mergeMemory(localData.memory['context.jsonl'], globalMemory)
574
- await this.agent.writeFile(globalMemoryPath, merged)
575
- }
499
+ return { success: true, feature: description, tasks }
576
500
  } catch (error) {
577
- console.error('[prjct] Error merging data:', error.message)
501
+ console.error(' Error:', error.message)
502
+ return { success: false, error: error.message }
578
503
  }
579
504
  }
580
505
 
581
506
  /**
582
- * Intelligent merge of markdown content
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
- intelligentMerge(localContent, globalContent, filename) {
591
- // If global is empty, use local
592
- if (!globalContent || globalContent.trim() === '' || globalContent.includes('No current task')) {
593
- return localContent
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
- // If local is empty, use global
597
- if (!localContent || localContent.trim() === '') {
598
- return globalContent
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
- // For now.md, prefer local (most recent)
602
- if (filename === 'now.md') {
603
- return localContent
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
- // For shipped.md, next.md, ideas.md, roadmap.md - combine unique entries
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
- // Keep header from global
611
- const header = globalLines[0] || localLines[0]
562
+ const context = await contextBuilder.build(projectPath, { description })
612
563
 
613
- // Combine unique content (skip headers and empty lines)
614
- const allLines = [...globalLines.slice(1), ...localLines.slice(1)]
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
- return [header, '', ...uniqueLines].join('\n')
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
- * Merge memory.jsonl files chronologically
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
- mergeMemory(localMemory, globalMemory) {
629
- if (!globalMemory) return localMemory
630
- if (!localMemory) return globalMemory
631
-
632
- const localEntries = localMemory.split('\n').filter((l) => l.trim())
633
- const globalEntries = globalMemory.split('\n').filter((l) => l.trim())
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
- const allEntries = [...globalEntries, ...localEntries].map((entry) => {
636
- try {
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 unique.map((e) => JSON.stringify(e)).join('\n')
624
+ return 'medium'
658
625
  }
659
626
 
660
627
  /**
661
- * Clean up local .prjct directory, keeping only prjct.config.json
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 cleanupLocalData(projectPath) {
631
+ async ship(feature, projectPath = process.cwd()) {
668
632
  try {
669
- const localPrjctPath = path.join(projectPath, '.prjct')
670
- const files = await fs.readdir(localPrjctPath, { withFileTypes: true })
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
- const filePath = path.join(localPrjctPath, file.name)
676
- if (file.isDirectory()) {
677
- await fs.rm(filePath, { recursive: true, force: true })
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
- await fs.unlink(filePath)
644
+ feature = 'current work'
680
645
  }
681
646
  }
682
647
 
683
- console.log('🧹 Cleaned up local data - only prjct.config.json remains')
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
- * Set or view current task
691
- *
692
- * @param {string|null} [task=null] - Task description or null to view current
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
- // Auto-init if not configured
702
- const initCheck = await this.ensureProjectInit(projectPath)
703
- if (!initCheck.success) {
704
- return initCheck
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
- if (!task) {
710
- const content = await this.agent.readFile(nowFile)
711
- const lines = content.split('\n')
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
- return {
715
- success: true,
716
- message: this.agent.formatResponse(`Current focus: ${currentTask}`, 'focus'),
717
- }
718
- }
664
+ console.log('4️⃣ Bumping version...')
665
+ const newVersion = await this._bumpVersion(projectPath)
666
+ console.log(` Version: ${newVersion}`)
719
667
 
720
- const branchName = this.generateBranchName(task)
668
+ console.log('5️⃣ Updating CHANGELOG...')
669
+ await this._updateChangelog(feature, newVersion, projectPath)
670
+ console.log(' ✅ CHANGELOG updated')
721
671
 
722
- let branchMessage = ''
723
- const branchResult = await this.createAndSwitchBranch(branchName, projectPath)
724
-
725
- if (branchResult.success) {
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
- let contentWithBranch = `# NOW: ${task}\nStarted: ${this.agent.getTimestamp()}\n`
738
- if (branchResult.success) {
739
- contentWithBranch += `Branch: ${branchName}\n`
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
- await this.agent.writeFile(nowFile, contentWithBranch)
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
- const currentAuthor = await configManager.getCurrentAuthor(projectPath)
746
-
747
- const startedAt = this.agent.getTimestamp()
748
- const memoryData = {
749
- task,
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
- await configManager.updateLastSync(projectPath)
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
- await this.initializeAgent()
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
- * Mark current task as done
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 done(projectPath = process.cwd()) {
714
+ async _runLint(_projectPath) {
786
715
  try {
787
- await this.initializeAgent()
788
-
789
- // Auto-init if not configured
790
- const initCheck = await this.ensureProjectInit(projectPath)
791
- if (!initCheck.success) {
792
- return initCheck
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
- const content = await this.agent.readFile(nowFile)
799
- const lines = content.split('\n')
800
- const currentTask = lines[0].replace('# NOW: ', '')
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
- if (currentTask === '# NOW' || !currentTask) {
803
- return {
804
- success: false,
805
- message: this.agent.formatResponse('No current task to complete', 'warning'),
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
- let startedAt = null
810
- const startedLine = lines.find(line => line.startsWith('Started:'))
811
- if (startedLine) {
812
- startedAt = startedLine.replace('Started: ', '').trim()
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 currentAuthor = await configManager.getCurrentAuthor(projectPath)
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
- const completedAt = this.agent.getTimestamp()
818
- let duration = null
819
- if (startedAt) {
820
- const ms = new Date(completedAt) - new Date(startedAt)
821
- const hours = Math.floor(ms / 3600000)
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
- // Check for active workflow
827
- const workflowEngine = require('./workflow-engine')
828
- const corePath = path.dirname(nowFile)
829
- const dataPath = path.dirname(corePath)
830
- const workflow = await workflowEngine.load(dataPath)
831
-
832
- if (workflow && workflow.active) {
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
- if (!nextStep) {
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
- const projectId = await configManager.getProjectId(projectPath)
856
- await configManager.updateAuthorActivity(projectId, currentAuthor)
785
+ await toolRegistry.get('Bash')(`git commit -m "${commitMsg.replace(/"/g, '\\"')}"`)
857
786
 
858
- return {
859
- success: true,
860
- message: this.agent.formatResponse(`Workflow complete: ${currentTask}`, 'success'),
861
- }
862
- }
787
+ return { success: true, message: 'Committed' }
788
+ } catch {
789
+ return { success: false, message: 'No changes to commit' }
790
+ }
791
+ }
863
792
 
864
- // Check if next step needs prompting
865
- if (nextStep.needsPrompt) {
866
- const workflowPrompts = require('./workflow-prompts')
867
- const promptInfo = await workflowPrompts.buildPrompt(nextStep, workflow.caps, projectPath)
868
-
869
- // Save state before prompting
870
- const projectId = await configManager.getProjectId(projectPath)
871
- await configManager.updateAuthorActivity(projectId, currentAuthor)
872
-
873
- return {
874
- success: true,
875
- message: this.agent.formatResponse(`Step complete: ${completedStep}`, 'success') +
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
- // Update now.md with next step
886
- const nowMd = `# NOW: ${currentTask}
887
- Started: ${new Date().toISOString()}
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
- ## Task
890
- ${currentTask}
815
+ console.log('📋 Project Context\n')
891
816
 
892
- ## Workflow Step
893
- ${nextStep.action}
817
+ const context = await contextBuilder.build(projectPath)
894
818
 
895
- ## Agent
896
- ${nextStep.agent}
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
- ## Notes
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 this.agent.writeFile(nowFile, nowMd)
829
+ try {
830
+ const entries = await jsonlHelper.readJsonLines(memoryPath)
831
+ recentActivity = entries.slice(-10).reverse()
832
+ } catch {
833
+ recentActivity = []
834
+ }
902
835
 
903
- const projectId = await configManager.getProjectId(projectPath)
904
- await configManager.updateAuthorActivity(projectId, currentAuthor)
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
- return {
907
- success: true,
908
- message: this.agent.formatResponse(`Step done → ${nextStep.name}: ${nextStep.action} (${nextStep.agent})`, 'success'),
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
- // No workflow: normal completion
913
- await this.agent.writeFile(nowFile, '# NOW\n\nNo current task. Use `/p:now` to set focus.\n')
914
-
915
- await this.logToMemory(projectPath, 'task_completed', {
916
- task: currentTask,
917
- timestamp: completedAt,
918
- startedAt,
919
- completedAt,
920
- duration,
921
- author: currentAuthor,
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
- await this.agent.readFile(nextFile)
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
- const message = `Task complete: ${currentTask}`
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
- await this.initializeAgent()
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
- * Ship a completed feature
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 ship(feature, projectPath = process.cwd()) {
909
+ async recap(projectPath = process.cwd()) {
953
910
  try {
954
- await this.initializeAgent()
955
-
956
- // Auto-init if not configured
957
- const initCheck = await this.ensureProjectInit(projectPath)
958
- if (!initCheck.success) {
959
- return initCheck
960
- }
961
-
962
- if (!feature) {
963
- return {
964
- success: false,
965
- message: this.agent.formatResponse(
966
- `Please specify a feature name: ${this.agentInfo.config.commandPrefix}ship "feature name"`,
967
- 'warning',
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
- const config = await configManager.readConfig(projectPath)
973
-
974
- if (config && config.projectId) {
975
- const week = this.getWeekNumber(new Date())
976
- const year = new Date().getFullYear()
977
- const weekHeader = `## Week ${week}, ${year}`
978
-
979
- const entry = `${weekHeader}\n- ✅ **${feature}** _(${new Date().toLocaleString()})_\n\n`
980
-
981
- try {
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
- const recentShips = await sessionManager.getRecentLogs(config.projectId, 30, 'shipped.md')
989
- const totalShipped = recentShips.match(/✅/g)?.length || 1
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
- await this.logToMemory(projectPath, 'ship', { feature, timestamp: this.agent.getTimestamp() })
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
- const daysSinceLastShip = await this.getDaysSinceLastShip(projectPath)
994
- const velocityMsg = daysSinceLastShip > 3 ? 'Keep the momentum going!' : "You're on fire! 🔥"
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
- const message = `SHIPPED! ${feature}\nTotal shipped: ${totalShipped}\n${velocityMsg}`
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: false,
1012
- message: this.agent.formatResponse(error.message, 'error'),
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
- * Legacy ship method for non-migrated projects
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 _shipLegacy(feature, projectPath) {
1026
- const shippedFile = await this.getFilePath(projectPath, 'progress', 'shipped.md')
1027
-
1028
- let content = await this.agent.readFile(shippedFile)
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
- const week = this.getWeekNumber(new Date())
1031
- const year = new Date().getFullYear()
1032
- const weekHeader = `## Week ${week}, ${year}`
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
- if (!content.includes(weekHeader)) {
1035
- content += `\n${weekHeader}\n`
1036
- }
1035
+ console.log(`🆘 Getting help: ${issue}\n`)
1037
1036
 
1038
- const entry = `- **${feature}** _(${new Date().toLocaleString()})_\n`
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
- await this.agent.writeFile(shippedFile, content)
1039
+ // Read analysis to understand stack
1040
+ const analysisContent = await toolRegistry.get('Read')(context.paths.analysis)
1041
+ let detectedStack = 'your project'
1043
1042
 
1044
- const totalShipped = (content.match(/✅/g) || []).length
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
- await this.logToMemory(projectPath, 'ship', { feature, timestamp: this.agent.getTimestamp() })
1051
+ // Provide contextual help based on issue type
1052
+ console.log('💡 Contextual Help:\n')
1047
1053
 
1048
- const daysSinceLastShip = await this.getDaysSinceLastShip(projectPath)
1049
- const velocityMsg = daysSinceLastShip > 3 ? 'Keep the momentum going!' : "You're on fire! 🔥"
1054
+ const issueLower = issue.toLowerCase()
1050
1055
 
1051
- const message = `SHIPPED! ${feature}\nTotal shipped: ${totalShipped}\n${velocityMsg}`
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
- return {
1054
- success: true,
1055
- message:
1056
- this.agent.formatResponse(message, 'celebrate') +
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
- * Show priority queue
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 next(projectPath = process.cwd()) {
1129
+ async design(target = null, options = {}, projectPath = process.cwd()) {
1069
1130
  try {
1070
- await this.initializeAgent()
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
- // Auto-init if not configured
1073
- const initCheck = await this.ensureProjectInit(projectPath)
1074
- if (!initCheck.success) {
1075
- return initCheck
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 nextFile = await this.getFilePath(projectPath, 'core', 'next.md')
1079
- const content = await this.agent.readFile(nextFile)
1143
+ const designTarget = target || 'system'
1080
1144
 
1081
- const tasks = content
1082
- .split('\n')
1083
- .filter((line) => line.startsWith('- '))
1084
- .map((line) => line.replace('- ', ''))
1145
+ console.log(`🎨 Designing ${designType}: ${designTarget}\n`)
1085
1146
 
1086
- if (tasks.length === 0) {
1087
- return {
1088
- success: true,
1089
- message: this.agent.formatResponse(
1090
- `Queue is empty. Add tasks with ${this.agentInfo.config.commandPrefix}idea or focus on shipping!`,
1091
- 'info',
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
- * Capture a new idea
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
- // Auto-init if not configured
1121
- const initCheck = await this.ensureProjectInit(projectPath)
1122
- if (!initCheck.success) {
1123
- return initCheck
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
- if (!text) {
1127
- return {
1128
- success: false,
1129
- message: this.agent.formatResponse(
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
- const config = await configManager.readConfig(projectPath)
1137
- const globalProjectPath = pathManager.getProjectRoot(config.projectId)
1138
-
1139
- const ideasFile = await this.getFilePath(projectPath, 'planning', 'ideas.md')
1140
- const nextFile = await this.getFilePath(projectPath, 'core', 'next.md')
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, 'idea', {
1168
- text,
1169
- timestamp: this.agent.getTimestamp(),
1170
- workflow: workflowCreated ? workflowType : null,
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
- message:
1186
- this.agent.formatResponse(message, 'idea') +
1187
- '\n' +
1188
- this.agent.suggestNextAction('ideaCaptured'),
1197
+ designPath: designFilePath,
1198
+ type: designType,
1199
+ target: designTarget,
1189
1200
  }
1190
1201
  } catch (error) {
1191
- await this.initializeAgent()
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
- * Show project recap with progress overview
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
- async recap(projectPath = process.cwd()) {
1206
- try {
1207
- await this.initializeAgent()
1211
+ _generateArchitectureDesign(target, projectPath) {
1212
+ const projectName = path.basename(projectPath)
1213
+ return `# Architecture Design: ${target}
1208
1214
 
1209
- // Auto-init if not configured
1210
- const initCheck = await this.ensureProjectInit(projectPath)
1211
- if (!initCheck.success) {
1212
- return initCheck
1213
- }
1215
+ **Project**: ${projectName}
1216
+ **Created**: ${new Date().toLocaleString()}
1217
+ **Type**: System Architecture
1214
1218
 
1215
- const nowFilePath = await this.getFilePath(projectPath, 'core', 'now.md')
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
- const nowFile = await this.agent.readFile(nowFilePath)
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
- const currentTask = nowFile.split('\n')[0].replace('# NOW: ', '').replace('# NOW', 'None')
1223
+ ## Components
1224
1224
 
1225
- const queuedCount = (nextFile.match(/^- /gm) || []).length
1226
- const ideasCount = (ideasFile.match(/^- /gm) || []).length
1225
+ ### Core Components
1226
+ 1. **Component A**
1227
+ - Responsibility: [Define responsibility]
1228
+ - Dependencies: [List dependencies]
1229
+ - Interfaces: [Define interfaces]
1227
1230
 
1228
- const config = await configManager.readConfig(projectPath)
1229
- let shippedCount = 0
1230
- let recentActivity = ''
1231
+ 2. **Component B**
1232
+ - Responsibility: [Define responsibility]
1233
+ - Dependencies: [List dependencies]
1234
+ - Interfaces: [Define interfaces]
1231
1235
 
1232
- if (config && config.projectId) {
1233
- const recentShips = await this.getHistoricalData(projectPath, 'month', 'shipped.md')
1234
- shippedCount = (recentShips.match(/✅/g) || []).length
1236
+ ## Data Flow
1235
1237
 
1236
- const recentLogs = await this.getRecentLogs(projectPath, 7)
1237
- recentActivity = recentLogs
1238
- .slice(-3)
1239
- .map((entry) => {
1240
- return `• ${entry.action}: ${entry.data.task || entry.data.feature || entry.data.text || ''}`
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
- const shippedData = await this.getHistoricalData(projectPath, period, 'shipped.md')
1244
+ ## Technology Stack
1299
1245
 
1300
- const features = []
1301
- const lines = shippedData.split('\n')
1246
+ - **Frontend**: [Framework/Library]
1247
+ - **Backend**: [Framework/Runtime]
1248
+ - **Database**: [Type/System]
1249
+ - **Deployment**: [Platform/Method]
1302
1250
 
1303
- for (const line of lines) {
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
- const now = new Date()
1316
- const periodDays = period === 'day' ? 1 : period === 'week' ? 7 : period === 'month' ? 30 : 7
1317
- const cutoff = new Date(now.getTime() - periodDays * 24 * 60 * 60 * 1000)
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
- const periodFeatures = features.filter((f) => f.date >= cutoff)
1259
+ ## Implementation Plan
1320
1260
 
1321
- const timeMetrics = await this.getTimeMetrics(projectPath, period)
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
- const velocity = periodFeatures.length / periodDays
1324
- const previousVelocity = 0.3
1267
+ ## Notes
1325
1268
 
1326
- const motivationalMessage =
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
- const progressData = {
1334
- period,
1335
- count: periodFeatures.length,
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
- * Get time metrics from task completion logs
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
- async getTimeMetrics(projectPath, period) {
1367
- try {
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
- const completedTasks = logs.filter(log => log.type === 'task_completed' && log.data?.duration)
1283
+ **Created**: ${new Date().toLocaleString()}
1284
+ **Type**: API Specification
1372
1285
 
1373
- if (completedTasks.length === 0) {
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
- const parseDuration = (duration) => {
1385
- const match = duration.match(/(\d+)h (\d+)m/)
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
- const durations = completedTasks.map(t => parseDuration(t.data.duration))
1391
- const totalMinutes = durations.reduce((sum, d) => sum + d, 0)
1392
- const avgMinutes = Math.round(totalMinutes / durations.length)
1291
+ **Request**:
1292
+ \`\`\`
1293
+ GET /api/${target.toLowerCase()}?limit=10&offset=0
1294
+ \`\`\`
1393
1295
 
1394
- const sortedDurations = [...durations].sort((a, b) => b - a)
1395
- const longestMinutes = sortedDurations[0]
1396
- const shortestMinutes = sortedDurations[sortedDurations.length - 1]
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
- const formatTime = (minutes) => {
1399
- const h = Math.floor(minutes / 60)
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
- const byAuthor = {}
1405
- completedTasks.forEach(task => {
1406
- const author = task.data?.author || task.author || 'Unknown'
1407
- if (!byAuthor[author]) {
1408
- byAuthor[author] = {
1409
- tasks: 0,
1410
- totalMinutes: 0,
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
- Object.keys(byAuthor).forEach(author => {
1418
- byAuthor[author].totalTime = formatTime(byAuthor[author].totalMinutes)
1419
- byAuthor[author].avgTime = formatTime(Math.round(byAuthor[author].totalMinutes / byAuthor[author].tasks))
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
- return {
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
- * Get help when stuck on a problem
1444
- *
1445
- * @param {string} issue - Issue description
1446
- * @param {string} [projectPath=process.cwd()] - Project path
1447
- * @returns {Promise<Object>} Result object with success flag and message
1448
- */
1449
- async stuck(issue, projectPath = process.cwd()) {
1450
- try {
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
- // Auto-init if not configured
1454
- const initCheck = await this.ensureProjectInit(projectPath)
1455
- if (!initCheck.success) {
1456
- return initCheck
1457
- }
1341
+ ## Authentication
1458
1342
 
1459
- if (!issue) {
1460
- return {
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
- await this.logToMemory(projectPath, 'stuck', { issue, timestamp: this.agent.getTimestamp() })
1346
+ ## Rate Limiting
1470
1347
 
1471
- const helpContent = this.agent.getHelpContent(issue)
1348
+ - **Limit**: 100 requests/minute
1349
+ - **Headers**: \`X-RateLimit-Limit\`, \`X-RateLimit-Remaining\`
1472
1350
 
1473
- return {
1474
- success: true,
1475
- message: helpContent + '\n' + this.agent.suggestNextAction('stuck'),
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
- * Advanced cleanup with multiple cleanup types
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
- async cleanupAdvanced(_target = '.', options = {}, _projectPath = process.cwd()) {
1495
- try {
1496
- await this.initializeAgent()
1360
+ _generateComponentDesign(target) {
1361
+ return `# Component Design: ${target}
1362
+
1363
+ **Created**: ${new Date().toLocaleString()}
1364
+ **Type**: Component Specification
1497
1365
 
1498
- const type = options.type || 'all'
1499
- const mode = options.aggressive ? 'aggressive' : 'safe'
1500
- const dryRun = options.dryRun || false
1366
+ ## Overview
1501
1367
 
1502
- const results = {
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
- if (type === 'all' || type === 'code') {
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
- if (type === 'all' || type === 'imports') {
1518
- results.imports.removed = Math.floor(Math.random() * 15)
1519
- results.imports.organized = Math.floor(Math.random() * 30)
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
- if (type === 'all' || type === 'files') {
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
- if (type === 'all' || type === 'deps') {
1529
- results.deps.removed = Math.floor(Math.random() * 6)
1530
- results.deps.sizeSaved = Math.floor(Math.random() * 20)
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
- if (animations) {
1534
- const message = `
1535
- 🧹 ✨ Advanced Cleanup Complete! ✨ 🧹
1392
+ ## Component Structure
1536
1393
 
1537
- 📊 Cleanup Results:
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
- 🗑️ Dead Code Removed:
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
- 📦 Imports Optimized:
1546
- Unused imports: ${results.imports.removed} removed
1547
- • Files organized: ${results.imports.organized}
1405
+ \`\`\`tsx
1406
+ import { ${target} } from '@/components/${target}'
1548
1407
 
1549
- 📁 Files Cleaned:
1550
- Temp files: ${results.files.temp} removed
1551
- • Empty files: ${results.files.empty} removed
1552
- • Space freed: ${results.files.spaceFeed} MB
1408
+ function App() {
1409
+ return (
1410
+ <${target}
1411
+ id="example"
1412
+ onAction={(data) => console.log(data)}
1413
+ />
1414
+ )
1415
+ }
1416
+ \`\`\`
1553
1417
 
1554
- 📚 Dependencies:
1555
- • Unused packages: ${results.deps.removed} removed
1556
- • Size reduced: ${results.deps.sizeSaved} MB
1418
+ ## Dependencies
1557
1419
 
1558
- ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
1559
- Your code is clean and optimized!
1420
+ - React
1421
+ - [Other libraries]
1560
1422
 
1561
- ${dryRun ? '⚠️ DRY RUN - No changes were made' : '✅ All changes applied successfully'}
1562
- 💡 Tip: Run with --dry-run first to preview changes`
1423
+ ## Implementation Notes
1563
1424
 
1564
- return {
1565
- success: true,
1566
- message,
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
- return {
1571
- success: true,
1572
- message: this.agent.formatResponse('Advanced cleanup complete!', 'success'),
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 documents and diagrams
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
- async design(target, options = {}, projectPath = process.cwd()) {
1592
- try {
1593
- await this.initializeAgent()
1440
+ _generateDatabaseDesign(target) {
1441
+ return `# Database Design: ${target}
1594
1442
 
1595
- // Verify project is initialized
1596
- if (!await configManager.isConfigured(projectPath)) {
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
- const type = options.type || 'architecture'
1446
+ ## Schema
1607
1447
 
1608
- // Use global architecture
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
- let designContent = ''
1615
- let diagram = ''
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
- switch (type) {
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
- case 'api':
1633
- diagram = `
1634
- REST API Endpoints:
1635
- POST /api/auth/register
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
- case 'component':
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
- case 'database':
1658
- diagram = `
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
- default:
1670
- diagram = 'Custom design diagram'
1671
- }
1473
+ ## Queries
1672
1474
 
1673
- const timestamp = new Date().toISOString().split('T')[0]
1674
- const designFile = path.join(designDir, `${target.replace(/\s+/g, '-')}-${type}-${timestamp}.md`)
1475
+ ### Common Queries
1675
1476
 
1676
- designContent = `# Design: ${target}
1677
- Type: ${type}
1678
- Date: ${timestamp}
1477
+ \`\`\`sql
1478
+ -- Get active records
1479
+ SELECT * FROM ${target.toLowerCase()} WHERE status = 'active';
1679
1480
 
1680
- ## Architecture Diagram
1681
- \`\`\`
1682
- ${diagram}
1481
+ -- Get recent records
1482
+ SELECT * FROM ${target.toLowerCase()}
1483
+ ORDER BY created_at DESC
1484
+ LIMIT 10;
1683
1485
  \`\`\`
1684
1486
 
1685
- ## Technical Specifications
1686
- - Technology Stack: Modern web stack
1687
- - Design Patterns: MVC, Repository, Observer
1688
- - Key Components: Authentication, API, Database
1689
- - Data Flow: Request → Controller → Service → Database
1690
-
1691
- ## Implementation Guide
1692
- 1. Set up project structure
1693
- 2. Implement core models
1694
- 3. Build API endpoints
1695
- 4. Create UI components
1696
- 5. Add tests and documentation
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
- await this.agent.writeFile(designFile, designContent)
1505
+ /**
1506
+ * Generate flow design document
1507
+ * @private
1508
+ */
1509
+ _generateFlowDesign(target) {
1510
+ return `# Flow Design: ${target}
1700
1511
 
1701
- await this.logToMemory(projectPath, 'design', {
1702
- target,
1703
- type,
1704
- file: designFile,
1705
- })
1512
+ **Created**: ${new Date().toLocaleString()}
1513
+ **Type**: Process Flow
1706
1514
 
1707
- const message = `
1708
- 🎨 ✨ Design Complete! ✨ 🎨
1515
+ ## Flow Overview
1709
1516
 
1710
- 📐 Design: ${target}
1711
- ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
1517
+ Process flow for ${target}.
1712
1518
 
1713
- 🏗️ Architecture Overview:
1714
- ${diagram}
1519
+ ## Steps
1715
1520
 
1716
- 📋 Technical Specifications:
1717
- Technology Stack: Modern web stack
1718
- • Design Patterns: MVC, Repository
1719
- Key Components: Listed in design doc
1720
- • Data Flow: Documented
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
- 📁 Files Created:
1723
- • ${designFile}
1533
+ ## Detailed Flow
1724
1534
 
1725
- ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
1726
- Design ready for implementation!
1535
+ ### Step 1: Initial Action
1536
+ - **Input**: [What triggers this]
1537
+ - **Validation**: [What gets checked]
1538
+ - **Output**: [What proceeds]
1727
1539
 
1728
- 💡 Next: prjct now "Implement ${target}"`
1540
+ ### Step 2: Processing
1541
+ - **Actions**: [What happens]
1542
+ - **Dependencies**: [What's needed]
1543
+ - **Side Effects**: [What changes]
1729
1544
 
1730
- return {
1731
- success: true,
1732
- message,
1733
- }
1734
- } catch (error) {
1735
- await this.initializeAgent()
1736
- return {
1737
- success: false,
1738
- message: this.agent.formatResponse(error.message, 'error'),
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
- * Show project context and recent activity
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 context(projectPath = process.cwd()) {
1573
+ async cleanup(_options = {}, projectPath = process.cwd()) {
1750
1574
  try {
1751
- await this.initializeAgent()
1575
+ const initResult = await this.ensureProjectInit(projectPath)
1576
+ if (!initResult.success) return initResult
1752
1577
 
1753
- // Auto-init if not configured
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 projectInfo = await this.detectProjectType(projectPath)
1580
+ const context = await contextBuilder.build(projectPath)
1581
+ const projectId = await configManager.getProjectId(projectPath)
1760
1582
 
1761
- const nowFilePath = await this.getFilePath(projectPath, 'core', 'now.md')
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
- const config = await configManager.readConfig(projectPath)
1766
- let recentActions = []
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
- if (config && config.projectId) {
1769
- const recentLogs = await this.getRecentLogs(projectPath, 7)
1770
- recentActions = recentLogs.slice(-5).map((entry) => {
1771
- return `• ${entry.action}: ${entry.data.task || entry.data.feature || entry.data.text || ''}`
1772
- })
1773
- } else {
1774
- const memoryFile = await this.getFilePath(projectPath, 'memory', 'memory.jsonl')
1775
- try {
1776
- const memory = await this.agent.readFile(memoryFile)
1777
- const lines = memory
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
- recentActions = lines.slice(-5).map((l) => {
1782
- const entry = JSON.parse(l)
1783
- return `• ${entry.action}: ${entry.data.task || entry.data.feature || entry.data.text || ''}`
1784
- })
1785
- } catch (e) {
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
- const contextInfo =
1790
- 'Project Context\n\n' +
1791
- `Agent: ${this.agentInfo.name}\n` +
1792
- `Project: ${projectInfo}\n` +
1793
- `Current: ${currentTask}\n\n` +
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
- return {
1798
- success: true,
1799
- message: this.agent.formatResponse(contextInfo, 'info'),
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
- await this.initializeAgent()
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
- * Detect project type from package.json and files
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 detectProjectType(projectPath) {
1817
- const files = await fs.readdir(projectPath)
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
- if (files.includes('package.json')) {
1820
- try {
1821
- const pkg = JSON.parse(await fs.readFile(path.join(projectPath, 'package.json'), 'utf-8'))
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
- if (files.includes('Cargo.toml')) return 'Rust project'
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
- return 'General project'
1840
- }
1680
+ const projectId = await configManager.getProjectId(projectPath)
1681
+ const memoryPath = pathManager.getFilePath(projectId, 'memory', 'context.jsonl')
1841
1682
 
1842
- /**
1843
- * Get week number from date
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
- * Get days since last ship event
1856
- *
1857
- * @param {string} projectPath - Project path
1858
- * @returns {Promise<number>} Days since last ship or Infinity if never shipped
1859
- */
1860
- async getDaysSinceLastShip(projectPath) {
1861
- try {
1862
- await this.initializeAgent()
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
- // Use global architecture
1865
- const projectId = await configManager.getProjectId(projectPath)
1866
- const globalPath = pathManager.getGlobalProjectPath(projectId)
1867
- const memoryFile = path.join(globalPath, 'memory', 'context.jsonl')
1868
- const memory = await this.agent.readFile(memoryFile)
1869
- const lines = memory
1870
- .trim()
1871
- .split('\n')
1872
- .filter((l) => l)
1873
-
1874
- for (let i = lines.length - 1; i >= 0; i--) {
1875
- const entry = JSON.parse(lines[i])
1876
- if (entry.action === 'ship') {
1877
- const shipDate = new Date(entry.data.timestamp)
1878
- const now = new Date()
1879
- return Math.floor((now - shipDate) / 86400000)
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
- } catch (e) {
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
- * Log action to memory system
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 logToMemory(projectPath, action, data) {
1895
- await this.initializeAgent()
1896
- await this.ensureAuthor()
1778
+ async roadmap(projectPath = process.cwd()) {
1779
+ try {
1780
+ const initResult = await this.ensureProjectInit(projectPath)
1781
+ if (!initResult.success) return initResult
1897
1782
 
1898
- const config = await configManager.readConfig(projectPath)
1783
+ console.log('🗺️ Project Roadmap\n')
1899
1784
 
1900
- if (config && config.projectId) {
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
- try {
1909
- await sessionManager.writeToSession(config.projectId, entry, 'context.jsonl')
1910
- } catch (error) {
1911
- console.error('Session logging failed, falling back to legacy:', error.message)
1912
- await this._logToMemoryLegacy(projectPath, action, data)
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
- } else {
1915
- await this._logToMemoryLegacy(projectPath, action, data)
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
- * Legacy logging method (fallback)
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
- async _logToMemoryLegacy(projectPath, action, data) {
1928
- const memoryFile = await this.getFilePath(projectPath, 'memory', 'context.jsonl')
1929
- const entry = JSON.stringify({
1930
- action,
1931
- author: this.currentAuthor,
1932
- data,
1933
- timestamp: new Date().toISOString(),
1934
- }) + '\n'
1821
+ _generateRoadmapTemplate() {
1822
+ return `
1823
+ # ROADMAP
1935
1824
 
1936
- try {
1937
- const existingContent = await this.agent.readFile(memoryFile)
1938
- await this.agent.writeFile(memoryFile, existingContent + entry)
1939
- } catch (e) {
1940
- await this.agent.writeFile(memoryFile, entry)
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
- * Get historical data from sessions
1946
- * Consolidates data from multiple sessions based on time period
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 getHistoricalData(projectPath, period = 'week', filename = 'context.jsonl') {
1954
- const config = await configManager.readConfig(projectPath)
1955
-
1956
- if (!config || !config.projectId) {
1957
- return await this._getHistoricalDataLegacy(projectPath, filename)
1958
- }
1959
-
1960
- const toDate = new Date()
1961
- const fromDate = new Date()
1962
- const isMarkdown = filename.endsWith('.md')
1963
-
1964
- switch (period) {
1965
- case 'day':
1966
- case 'today':
1967
- if (isMarkdown) {
1968
- const sessionPath = await pathManager.getCurrentSessionPath(config.projectId)
1969
- const filePath = path.join(sessionPath, filename)
1970
- try {
1971
- return await fs.readFile(filePath, 'utf-8')
1972
- } catch {
1973
- return ''
1974
- }
1975
- }
1976
- return await sessionManager.readCurrentSession(config.projectId, filename)
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
- case 'week':
1979
- fromDate.setDate(fromDate.getDate() - 7)
1980
- break
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
- case 'month':
1983
- fromDate.setMonth(fromDate.getMonth() - 1)
1984
- break
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
- case 'all':
1987
- fromDate.setFullYear(fromDate.getFullYear() - 1)
1988
- break
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
- default:
1991
- fromDate.setDate(fromDate.getDate() - 7)
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
- if (isMarkdown) {
1995
- return await sessionManager.readMarkdownRange(config.projectId, fromDate, toDate, filename)
1996
- } else {
1997
- return await sessionManager.readSessionRange(config.projectId, fromDate, toDate, filename)
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
- * Get historical data from legacy single-file structure
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
- async _getHistoricalDataLegacy(projectPath, filename) {
2010
- const filePath = await this.getFilePath(projectPath, 'memory', filename)
2011
-
2012
- try {
2013
- const content = await this.agent.readFile(filePath)
2014
- const lines = content.split('\n').filter(line => line.trim())
2015
- return lines.map(line => {
2016
- try {
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
- * Get recent logs with session support
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
- async getRecentLogs(projectPath, days = 7) {
2035
- const config = await configManager.readConfig(projectPath)
1971
+ _calculateHealth(stats) {
1972
+ let score = 50 // Base score
2036
1973
 
2037
- if (config && config.projectId) {
2038
- return await sessionManager.getRecentLogs(config.projectId, days)
2039
- } else {
2040
- return await this._getHistoricalDataLegacy(projectPath, 'context.jsonl')
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
- * Cleanup old project data
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 cleanup(projectPath = process.cwd()) {
2002
+ async build(taskOrNumber, projectPath = process.cwd()) {
2051
2003
  try {
2052
- await this.initializeAgent()
2053
-
2054
- // Verify project is initialized
2055
- if (!await configManager.isConfigured(projectPath)) {
2056
- return {
2057
- success: false,
2058
- message: this.agent.formatResponse(
2059
- `Project not initialized. Run ${this.agentInfo.config.commandPrefix}init first.`,
2060
- 'warning'
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
- // Use global architecture
2066
- const projectId = await configManager.getProjectId(projectPath)
2067
- const globalPath = pathManager.getGlobalProjectPath(projectId)
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
- let totalFreed = 0
2070
- let filesRemoved = 0
2071
- let tasksArchived = 0
2047
+ console.log(`🏗️ Building: ${task}\n`)
2072
2048
 
2073
- try {
2074
- const tempDir = path.join(globalPath, 'temp')
2075
- const tempFiles = await fs.readdir(tempDir).catch(() => [])
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
- try {
2087
- const memoryFile = path.join(globalPath, 'memory', 'context.jsonl')
2088
- const content = await this.agent.readFile(memoryFile)
2089
- const lines = content.split('\n').filter(line => line.trim())
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
- if (archivedLines.length > 0) {
2111
- const archiveFile = path.join(globalPath, 'memory', `archive-${now.toISOString().split('T')[0]}.jsonl`)
2112
- await this.agent.writeFile(archiveFile, archivedLines.join('\n') + '\n')
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
- const files = await fs.readdir(globalPath)
2120
- for (const file of files) {
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
- try {
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
- await this.agent.writeFile(shippedFile, filteredLines.join('\n'))
2153
- } catch (e) {
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
- const freedMB = (totalFreed / 1024 / 1024).toFixed(2)
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
- const message = '🧹 Cleanup complete!\n' +
2159
- `• Files removed: ${filesRemoved}\n` +
2160
- `• Tasks archived: ${tasksArchived}\n` +
2161
- `• Space freed: ${freedMB} MB\n` +
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
- await this.logToMemory(projectPath, 'cleanup', {
2165
- filesRemoved,
2166
- tasksArchived,
2167
- spaceFeed: freedMB,
2168
- })
2132
+ return { level, hours, type }
2133
+ }
2169
2134
 
2170
- return {
2171
- success: true,
2172
- message: this.agent.formatResponse(message, 'success'),
2173
- }
2174
- } catch (error) {
2175
- await this.initializeAgent()
2176
- return {
2177
- success: false,
2178
- message: this.agent.formatResponse(`Cleanup failed: ${error.message}`, 'error'),
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
- * Migrate all legacy projects to new structure
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 migrateAll(options = {}) {
2173
+ async analyze(options = {}, projectPath = process.cwd()) {
2190
2174
  try {
2191
2175
  await this.initializeAgent()
2192
2176
 
2193
- const {
2194
- deepScan = false,
2195
- removeLegacy = false,
2196
- dryRun = false,
2197
- } = options
2198
-
2199
- const onProgress = (update) => {
2200
- if (update.phase === 'scanning') {
2201
- console.log(`🔍 ${update.message}`)
2202
- } else if (update.phase === 'checking' || update.phase === 'migrating') {
2203
- console.log(` ${update.message}`)
2204
- }
2205
- }
2206
-
2207
- const summary = await migrator.migrateAll({
2208
- deepScan,
2209
- removeLegacy,
2210
- dryRun,
2211
- onProgress,
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
- const report = migrator.generateMigrationSummary(summary)
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: summary.success,
2218
- message: report,
2219
- summary,
2243
+ success: true,
2244
+ summaryPath,
2245
+ data: analysisData,
2220
2246
  }
2221
2247
  } catch (error) {
2222
- await this.initializeAgent()
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
- * First-time setup - Install commands to AI editors with ASCII art welcome
2232
- *
2233
- * @returns {Promise<Object>} Result object with success flag and message
2254
+ * Generate analysis summary from collected data
2255
+ * Claude decides what's relevant - NO predetermined patterns
2256
+ * @private
2234
2257
  */
2235
- async start() {
2236
- try {
2237
- await this.initializeAgent()
2238
-
2239
- // ASCII Art
2240
- const chalk = require('chalk')
2241
- console.log('')
2242
- console.log('')
2243
- console.log(chalk.bold.cyan(' ██████╗ ██████╗ ██╗ ██████╗████████╗'))
2244
- console.log(chalk.bold.cyan(' ██╔══██╗██╔══██╗ ██║██╔════╝╚══██╔══╝'))
2245
- console.log(chalk.bold.blue(' ██████╔╝██████╔╝ ██║██║ ██║'))
2246
- console.log(chalk.bold.blue(' ██╔═══╝ ██╔══██╗██ ██║██║ ██║'))
2247
- console.log(chalk.bold.magenta(' ██║ ██║ ██║╚█████╔╝╚██████╗ ██║'))
2248
- console.log(chalk.bold.magenta(' ╚═╝ ╚═╝ ╚═╝ ╚════╝ ╚═════╝ ╚═╝'))
2249
- console.log('')
2250
- console.log(chalk.dim.white(' Turn ideas into AI-ready roadmaps'))
2251
- console.log('')
2252
- console.log(chalk.yellow(' ⚡ Ship faster with zero friction'))
2253
- console.log(chalk.green(' 📝 From idea to technical tasks in minutes'))
2254
- console.log(chalk.cyan(' 🤖 Perfect context for AI agents'))
2255
- console.log('')
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
- console.log(chalk.green('✓ Claude detected'))
2274
- console.log('')
2275
-
2276
- // Install commands
2277
- console.log(chalk.cyan('Installing /p:* commands...'))
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
- if (!installResult.success) {
2281
- return {
2282
- success: false,
2283
- message: this.agent.formatResponse(
2284
- `Installation failed: ${installResult.error || 'Unknown error'}`,
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
- // Success message
2291
- const installedCount = installResult.installed?.length || 0
2292
- const errorCount = installResult.errors?.length || 0
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
- let message = `✅ Successfully installed ${installedCount} commands to Claude\n`
2295
- message += ` Location: ${installResult.path}\n`
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
- if (errorCount > 0) {
2298
- message += `\n⚠️ ${errorCount} command(s) had issues during installation`
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
- message += '\n\n✨ prjct is ready to use!'
2302
- message += '\n\nNext steps:'
2303
- message += '\n cd your-project/'
2304
- message += '\n prjct init'
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
- return {
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
- * Setup/reconfigure commands in AI editors
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 setup(options = {}) {
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
- const { force = false } = options
2361
+ console.log('🔄 Syncing project state...\n')
2331
2362
 
2332
- // Detect Claude
2333
- const commandInstaller = require('./command-installer')
2334
- const claudeDetected = await commandInstaller.detectClaude()
2363
+ // Build context
2364
+ const context = await contextBuilder.build(projectPath)
2335
2365
 
2336
- if (!claudeDetected) {
2337
- return {
2338
- success: false,
2339
- message: this.agent.formatResponse(
2340
- 'Claude not detected. Please install Claude Code or Claude Desktop first.',
2341
- 'error'
2342
- ),
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
- // Check current installation status
2347
- const status = await commandInstaller.checkInstallation()
2370
+ if (!analysisResult.success) {
2371
+ console.error('❌ Analysis failed')
2372
+ return analysisResult
2373
+ }
2348
2374
 
2349
- if (status.installed && !force) {
2350
- const chalk = require('chalk')
2351
- let report = chalk.green('✓ Commands already installed\n')
2352
- report += chalk.dim(` Location: ${status.path}\n`)
2353
- report += chalk.dim(` Commands: ${status.commands.length}\n`)
2354
- report += '\nUse --force to reinstall'
2375
+ // Step 2: Read analysis/repo-summary.md
2376
+ const summaryContent = await toolRegistry.get('Read')(context.paths.analysis)
2355
2377
 
2356
- return {
2357
- success: true,
2358
- message: this.agent.formatResponse(report, 'info'),
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
- // Install or reinstall commands
2363
- const installResult = force
2364
- ? await commandInstaller.updateCommands()
2365
- : await commandInstaller.installCommands()
2383
+ console.log('✅ Analysis loaded\n')
2366
2384
 
2367
- if (!installResult.success) {
2368
- return {
2369
- success: false,
2370
- message: this.agent.formatResponse(
2371
- `Installation failed: ${installResult.error || 'Unknown error'}`,
2372
- 'error'
2373
- ),
2374
- }
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
- // Generate report
2378
- const installedCount = installResult.installed?.length || 0
2379
- const errorCount = installResult.errors?.length || 0
2389
+ const projectId = await configManager.getProjectId(projectPath)
2390
+ const AgentGenerator = require('./domain/agent-generator')
2391
+ const generator = new AgentGenerator(projectId)
2380
2392
 
2381
- let report = `✅ Successfully ${force ? 'reinstalled' : 'installed'} ${installedCount} commands\n`
2382
- report += ` Location: ${installResult.path}\n`
2393
+ const generatedAgents = await this._generateAgentsFromAnalysis(summaryContent, generator)
2383
2394
 
2384
- if (errorCount > 0) {
2385
- report += `\n⚠️ ${errorCount} command(s) had issues\n`
2386
- installResult.errors.forEach(err => {
2387
- report += ` - ${err.file}: ${err.error}\n`
2388
- })
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
- report += '\n📚 Context7 MCP is automatically available in Claude'
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
- message: this.agent.formatResponse(report, 'celebrate'),
2415
+ agents: generatedAgents,
2396
2416
  }
2397
2417
  } catch (error) {
2398
- await this.initializeAgent()
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
- * Analyze codebase and optionally sync with .prjct/ state
2408
- *
2409
- * @param {Object} [options={}] - Analysis options
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 analyze(options = {}, projectPath = process.cwd()) {
2414
- try {
2415
- await this.initializeAgent()
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
- const {
2418
- sync = false,
2419
- reportOnly = false,
2420
- silent = false,
2421
- } = options
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
- if (!silent) {
2424
- console.log('🔍 Analyzing codebase...')
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
- const analysis = await analyzer.analyzeProject(projectPath)
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
- const summary = {
2430
- commandsFound: analysis.commands.length,
2431
- featuresFound: analysis.features.length,
2432
- technologies: analysis.technologies.join(', '),
2433
- fileCount: analysis.structure.fileCount,
2434
- hasGit: analysis.gitHistory.hasGit,
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
- let syncResults = null
2438
- if (sync && !reportOnly) {
2439
- const globalProjectPath = await this.getGlobalProjectPath(projectPath)
2440
- syncResults = await analyzer.syncWithPrjctFiles(globalProjectPath)
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
- let message = ''
2520
+ console.log('🚀 Setting up prjct for Claude...\n')
2444
2521
 
2445
- if (silent) {
2446
- message = `Found ${summary.commandsFound} commands, ${summary.featuresFound} features`
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: true,
2457
- message: this.agent.formatResponse(message, 'info'),
2458
- analysis,
2459
- syncResults,
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
- } catch (error) {
2462
- await this.initializeAgent()
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: this.agent.formatResponse(`Analysis failed: ${error.message}`, 'error'),
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
- * Format analysis report for display
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
- formatAnalysisReport(summary, analysis) {
2478
- return `
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
- 🛠️ Implemented Commands: ${summary.commandsFound}
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
- Detected Features: ${summary.featuresFound}
2491
- ${analysis.features.slice(0, 5).map(f => ` • ${f}`).join('\n')}
2492
- ${analysis.features.length > 5 ? ` ... and ${analysis.features.length - 5} more` : ''}
2573
+ if (options.force) {
2574
+ console.log('🗑️ Removing existing installation...')
2575
+ await commandInstaller.uninstallCommands()
2576
+ }
2493
2577
 
2494
- 📝 Full report saved to: analysis/repo-summary.md
2578
+ // Reinstall commands
2579
+ console.log('📦 Installing /p:* commands...')
2580
+ const result = await commandInstaller.updateCommands()
2495
2581
 
2496
- 💡 Use /p:analyze --sync to sync with .prjct/ files
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
- 📊 Detected:
2512
- ${summary.commandsFound} implemented commands
2513
- ${summary.featuresFound} completed features
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
- 📝 Synchronized:
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
- 💡 Next: Use /p:next to see remaining tasks
2521
- `
2598
+ return {
2599
+ success: true,
2600
+ message: '',
2601
+ }
2522
2602
  }
2523
2603
 
2524
2604
  /**
2525
- * Sync project state and update AI agents
2526
- * Re-analyzes the project and regenerates workflow agents in global project directory
2527
- *
2528
- * @param {string} [projectPath=process.cwd()] - Project directory path
2529
- * @returns {Promise<Object>} Result with success status and message
2605
+ * Migrate all legacy projects
2530
2606
  */
2531
- async sync(projectPath = process.cwd()) {
2532
- try {
2533
- await this.initializeAgent()
2607
+ async migrateAll(options = {}) {
2608
+ const fs = require('fs').promises
2609
+ const path = require('path')
2534
2610
 
2535
- // Get project ID from config
2536
- const config = await configManager.readConfig(projectPath)
2537
- if (!config || !config.projectId) {
2538
- return {
2539
- success: false,
2540
- message: this.agent.formatResponse(
2541
- 'Project not initialized. Run /p:init first.',
2542
- 'error',
2543
- ),
2544
- }
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
- console.log('🔄 Syncing project state...\n')
2628
+ console.log(`📁 Found ${projectIds.length} projects in global storage\n`)
2548
2629
 
2549
- // Step 1: Re-run project analysis
2550
- const analysisResult = await this.analyze({ silent: true }, projectPath)
2630
+ const migrated = []
2631
+ const failed = []
2632
+ const skipped = []
2551
2633
 
2552
- if (!analysisResult.success) {
2553
- return {
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
- const { analysis } = analysisResult
2560
- const summary = {
2561
- commandsFound: analysis.commands.length,
2562
- featuresFound: analysis.features.length,
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
- console.log('📊 Analysis Complete')
2566
- console.log(` ✅ ${summary.commandsFound} commands detected`)
2567
- console.log(` ✅ ${summary.featuresFound} features implemented\n`)
2644
+ const projectPath = globalConfig.projectPath
2568
2645
 
2569
- // Step 2: Generate/update all agents in project directory
2570
- const AgentGenerator = agentGenerator
2571
- const generator = new AgentGenerator(config.projectId)
2646
+ // Check if needs migration
2647
+ if (!(await migrator.needsMigration(projectPath))) {
2648
+ skipped.push({ projectId, reason: 'Already migrated' })
2649
+ continue
2650
+ }
2572
2651
 
2573
- const generatedAgents = await generator.generateAll(analysis)
2652
+ console.log(`🔄 Migrating: ${projectPath}`)
2574
2653
 
2575
- // Step 3: Log sync action to memory
2576
- await this.logAction(projectPath, 'sync', {
2577
- commandsDetected: summary.commandsFound,
2578
- featuresDetected: summary.featuresFound,
2579
- agentsGenerated: generatedAgents.length,
2580
- agents: generatedAgents,
2581
- })
2654
+ try {
2655
+ const result = await migrator.migrate(projectPath, options)
2582
2656
 
2583
- const agentsPath = path.join(
2584
- pathManager.getProjectRoot(config.projectId),
2585
- 'agents',
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
- const message = `
2589
- 🔄 Sync Complete
2669
+ console.log('')
2670
+ }
2590
2671
 
2591
- 📊 Project State:
2592
- ✅ ${summary.commandsFound} commands detected
2593
- ✅ ${summary.featuresFound} features implemented
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
- 🤖 Workflow Agents:
2596
- ✨ Generated ${generatedAgents.length} agents in ${agentsPath}
2597
- ${generatedAgents.map(a => `✅ ${a.toUpperCase()}`).join('\n ')}
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
- 💡 Agents ready for workflow task assignment!
2600
- `
2683
+ return {
2684
+ success: failed.length === 0,
2685
+ message: '',
2686
+ }
2687
+ }
2601
2688
 
2602
- return {
2603
- success: true,
2604
- message: this.agent.formatResponse(message, 'success'),
2605
- generatedAgents,
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: this.agent.formatResponse(`Sync failed: ${error.message}`, 'error'),
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.initializeAgent()
2701
+ const initResult = await this.ensureProjectInit(projectPath)
2702
+ if (!initResult.success) return initResult
2625
2703
 
2626
- // Auto-init if not configured
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 config = await configManager.readConfig(projectPath)
2633
- const globalProjectPath = pathManager.getProjectRoot(config.projectId)
2706
+ const globalPath = await this.getGlobalProjectPath(projectPath)
2707
+ const architectSession = require('./domain/architect-session')
2634
2708
 
2635
- const workflow = await workflowEngine.load(globalProjectPath)
2709
+ // Check if there's a completed plan
2710
+ const planPath = path.join(globalPath, 'planning', 'architect-session.md')
2636
2711
 
2637
- if (!workflow || !workflow.active) {
2712
+ let planContent
2713
+ try {
2714
+ planContent = await fileHelper.readFile(planPath)
2715
+ } catch (error) {
2638
2716
  return {
2639
- success: true,
2640
- message: this.agent.formatResponse(
2641
- `No active workflow.\n\nStart one with ${this.agentInfo.config.commandPrefix}idea "implement [feature]"`,
2642
- 'info',
2643
- ),
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
- const currentStep = workflow.steps[workflow.current]
2648
- const completedSteps = workflow.steps.slice(0, workflow.current)
2649
- const remainingSteps = workflow.steps.slice(workflow.current + 1)
2650
-
2651
- let message = `🔄 Workflow: ${workflow.type}\n`
2652
- message += `📋 Task: ${workflow.task}\n\n`
2653
-
2654
- if (completedSteps.length > 0) {
2655
- message += `✅ Completed:\n`
2656
- completedSteps.forEach((step) => {
2657
- message += ` - ${step.name}: ${step.action} (${step.agent})\n`
2658
- })
2659
- message += '\n'
2660
- }
2661
-
2662
- message += `🎯 Current Step:\n`
2663
- message += ` - ${currentStep.name}: ${currentStep.action} (${currentStep.agent})\n`
2664
- if (currentStep.required) {
2665
- message += ` Required: Yes\n`
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
- if (remainingSteps.length > 0) {
2670
- message += `⏳ Remaining:\n`
2671
- remainingSteps.forEach((step) => {
2672
- message += ` - ${step.name}: ${step.action} (${step.agent})\n`
2673
- })
2674
- message += '\n'
2675
- }
2735
+ console.log('📋 Reading architect plan...\n')
2676
2736
 
2677
- message += `Progress: ${workflow.current + 1}/${workflow.steps.length} steps`
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
- return {
2680
- success: true,
2681
- message: this.agent.formatResponse(message, 'info'),
2682
- workflow,
2683
- }
2684
- } catch (error) {
2685
- await this.initializeAgent()
2686
- return {
2687
- success: false,
2688
- message: this.agent.formatResponse(`Workflow status failed: ${error.message}`, 'error'),
2689
- }
2690
- }
2691
- }
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
- * Detect if project has existing code (for auto-analyze during init)
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
- if (pkg.dependencies && Object.keys(pkg.dependencies).length > 0) {
2707
- return true
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
- try {
2713
- const { stdout } = await exec('git rev-list --count HEAD', { cwd: projectPath })
2714
- const commitCount = parseInt(stdout.trim())
2715
- if (commitCount > 0) {
2716
- return true
2717
- }
2718
- } catch {
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
- const entries = await fs.readdir(projectPath)
2722
- const codeExtensions = ['.js', '.ts', '.jsx', '.tsx', '.py', '.go', '.rs', '.rb', '.java']
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
- let codeFileCount = 0
2725
- for (const entry of entries) {
2726
- if (entry.startsWith('.') || entry === 'node_modules') {
2727
- continue
2728
- }
2766
+ await this.logToMemory(projectPath, 'architect_executed', {
2767
+ timestamp: dateHelper.getTimestamp(),
2768
+ idea,
2769
+ })
2729
2770
 
2730
- const ext = path.extname(entry)
2731
- if (codeExtensions.includes(ext)) {
2732
- codeFileCount++
2733
- }
2771
+ return {
2772
+ success: true,
2773
+ plan: planContent,
2774
+ idea,
2734
2775
  }
2735
-
2736
- return codeFileCount >= 5
2737
2776
  } catch (error) {
2738
- return false
2777
+ console.error('❌ Error:', error.message)
2778
+ return { success: false, error: error.message }
2739
2779
  }
2740
2780
  }
2741
2781
  }
2742
2782
 
2743
- module.exports = new PrjctCommands()
2783
+ module.exports = PrjctCommands