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