prjct-cli 0.5.1 → 0.7.0

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