prjct-cli 0.4.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 (71) hide show
  1. package/CHANGELOG.md +312 -0
  2. package/CLAUDE.md +300 -0
  3. package/LICENSE +21 -0
  4. package/README.md +424 -0
  5. package/bin/prjct +214 -0
  6. package/core/agent-detector.js +249 -0
  7. package/core/agents/claude-agent.js +250 -0
  8. package/core/agents/codex-agent.js +256 -0
  9. package/core/agents/terminal-agent.js +465 -0
  10. package/core/analyzer.js +596 -0
  11. package/core/animations-simple.js +240 -0
  12. package/core/animations.js +277 -0
  13. package/core/author-detector.js +218 -0
  14. package/core/capability-installer.js +190 -0
  15. package/core/command-installer.js +775 -0
  16. package/core/commands.js +2050 -0
  17. package/core/config-manager.js +335 -0
  18. package/core/migrator.js +784 -0
  19. package/core/path-manager.js +324 -0
  20. package/core/project-capabilities.js +144 -0
  21. package/core/session-manager.js +439 -0
  22. package/core/version.js +107 -0
  23. package/core/workflow-engine.js +213 -0
  24. package/core/workflow-prompts.js +192 -0
  25. package/core/workflow-rules.js +147 -0
  26. package/package.json +80 -0
  27. package/scripts/install.sh +433 -0
  28. package/scripts/verify-installation.sh +158 -0
  29. package/templates/agents/AGENTS.md +164 -0
  30. package/templates/commands/analyze.md +125 -0
  31. package/templates/commands/cleanup.md +102 -0
  32. package/templates/commands/context.md +105 -0
  33. package/templates/commands/design.md +113 -0
  34. package/templates/commands/done.md +44 -0
  35. package/templates/commands/fix.md +87 -0
  36. package/templates/commands/git.md +79 -0
  37. package/templates/commands/help.md +72 -0
  38. package/templates/commands/idea.md +50 -0
  39. package/templates/commands/init.md +237 -0
  40. package/templates/commands/next.md +74 -0
  41. package/templates/commands/now.md +35 -0
  42. package/templates/commands/progress.md +92 -0
  43. package/templates/commands/recap.md +86 -0
  44. package/templates/commands/roadmap.md +107 -0
  45. package/templates/commands/ship.md +41 -0
  46. package/templates/commands/stuck.md +48 -0
  47. package/templates/commands/task.md +97 -0
  48. package/templates/commands/test.md +94 -0
  49. package/templates/commands/workflow.md +224 -0
  50. package/templates/examples/natural-language-examples.md +320 -0
  51. package/templates/mcp-config.json +8 -0
  52. package/templates/workflows/analyze.md +159 -0
  53. package/templates/workflows/cleanup.md +73 -0
  54. package/templates/workflows/context.md +72 -0
  55. package/templates/workflows/design.md +88 -0
  56. package/templates/workflows/done.md +20 -0
  57. package/templates/workflows/fix.md +201 -0
  58. package/templates/workflows/git.md +192 -0
  59. package/templates/workflows/help.md +13 -0
  60. package/templates/workflows/idea.md +22 -0
  61. package/templates/workflows/init.md +80 -0
  62. package/templates/workflows/natural-language-handler.md +183 -0
  63. package/templates/workflows/next.md +44 -0
  64. package/templates/workflows/now.md +19 -0
  65. package/templates/workflows/progress.md +113 -0
  66. package/templates/workflows/recap.md +66 -0
  67. package/templates/workflows/roadmap.md +95 -0
  68. package/templates/workflows/ship.md +18 -0
  69. package/templates/workflows/stuck.md +25 -0
  70. package/templates/workflows/task.md +109 -0
  71. package/templates/workflows/test.md +243 -0
@@ -0,0 +1,256 @@
1
+ /**
2
+ * OpenAI Codex Agent Adapter
3
+ * Implements prjct commands for OpenAI Codex environment
4
+ */
5
+
6
+ const fs = require('fs').promises
7
+
8
+ class CodexAgent {
9
+ constructor() {
10
+ this.name = 'OpenAI Codex'
11
+ this.type = 'codex'
12
+ }
13
+
14
+ /**
15
+ * Format response for Codex with structured output
16
+ * Codex runs in sandboxed environments, so we use clear text formatting
17
+ */
18
+ formatResponse(message, type = 'info') {
19
+ const prefixes = {
20
+ success: '[SUCCESS]',
21
+ error: '[ERROR]',
22
+ warning: '[WARNING]',
23
+ info: '[INFO]',
24
+ celebrate: '[SHIPPED]',
25
+ ship: '[FEATURE]',
26
+ focus: '[FOCUS]',
27
+ idea: '[IDEA]',
28
+ progress: '[PROGRESS]',
29
+ task: '[TASK]',
30
+ }
31
+
32
+ const prefix = prefixes[type] || prefixes.info
33
+
34
+ return `${prefix} ${message}`
35
+ }
36
+
37
+ /**
38
+ * Read file using native fs (Codex doesn't have MCP)
39
+ */
40
+ async readFile(filePath) {
41
+ try {
42
+ return await fs.readFile(filePath, 'utf8')
43
+ } catch (error) {
44
+ throw new Error(`Failed to read file ${filePath}: ${error.message}`)
45
+ }
46
+ }
47
+
48
+ /**
49
+ * Write file using native fs
50
+ */
51
+ async writeFile(filePath, content) {
52
+ try {
53
+ await fs.writeFile(filePath, content, 'utf8')
54
+ } catch (error) {
55
+ throw new Error(`Failed to write file ${filePath}: ${error.message}`)
56
+ }
57
+ }
58
+
59
+ /**
60
+ * List directory contents
61
+ */
62
+ async listDirectory(dirPath) {
63
+ try {
64
+ return await fs.readdir(dirPath)
65
+ } catch (error) {
66
+ throw new Error(`Failed to list directory ${dirPath}: ${error.message}`)
67
+ }
68
+ }
69
+
70
+ /**
71
+ * Check if file exists
72
+ */
73
+ async fileExists(filePath) {
74
+ try {
75
+ await fs.access(filePath)
76
+ return true
77
+ } catch {
78
+ return false
79
+ }
80
+ }
81
+
82
+ /**
83
+ * Create directory
84
+ */
85
+ async createDirectory(dirPath) {
86
+ try {
87
+ await fs.mkdir(dirPath, { recursive: true })
88
+ } catch (error) {
89
+ throw new Error(`Failed to create directory ${dirPath}: ${error.message}`)
90
+ }
91
+ }
92
+
93
+ /**
94
+ * Get current timestamp in ISO format
95
+ */
96
+ getTimestamp() {
97
+ return new Date().toISOString()
98
+ }
99
+
100
+ /**
101
+ * Format task list with structured output
102
+ */
103
+ formatTaskList(tasks) {
104
+ if (!tasks || tasks.length === 0) {
105
+ return this.formatResponse('No tasks in queue', 'info')
106
+ }
107
+
108
+ let output = 'TASK QUEUE:\n'
109
+ output += '===========\n'
110
+ tasks.forEach((task, index) => {
111
+ output += ` ${index + 1}. ${task}\n`
112
+ })
113
+
114
+ return output
115
+ }
116
+
117
+ /**
118
+ * Format recap with structured output
119
+ */
120
+ formatRecap(data) {
121
+ const output = `
122
+ PROJECT RECAP
123
+ =============
124
+
125
+ Current Task: ${data.currentTask || 'None'}
126
+ Shipped Features: ${data.shippedCount} total
127
+ Queued Tasks: ${data.queuedCount}
128
+ Ideas Captured: ${data.ideasCount}
129
+
130
+ ${data.recentActivity ? 'Recent Activity:\n' + data.recentActivity : ''}
131
+
132
+ Status: ${data.shippedCount > 0 ? 'Productive' : 'Getting Started'}
133
+ `.trim()
134
+
135
+ return output
136
+ }
137
+
138
+ /**
139
+ * Format progress report
140
+ */
141
+ formatProgress(data) {
142
+ const trend =
143
+ data.velocity > data.previousVelocity
144
+ ? 'UP'
145
+ : data.velocity < data.previousVelocity
146
+ ? 'DOWN'
147
+ : 'STEADY'
148
+
149
+ const output = `
150
+ PROGRESS REPORT (${data.period.toUpperCase()})
151
+ =====================================
152
+
153
+ Features Shipped: ${data.count}
154
+ Velocity: ${data.velocity.toFixed(1)} features/day
155
+ Trend: ${trend}
156
+
157
+ ${data.recentFeatures ? 'Recent Features:\n' + data.recentFeatures : ''}
158
+
159
+ ${data.motivationalMessage}
160
+ `.trim()
161
+
162
+ return output
163
+ }
164
+
165
+ /**
166
+ * Get help content based on issue type
167
+ * Structured format for Codex readability
168
+ */
169
+ getHelpContent(issue) {
170
+ const helps = {
171
+ debugging: `
172
+ DEBUGGING STRATEGY:
173
+ ===================
174
+ 1. ISOLATE - Comment out code until error disappears
175
+ 2. LOG - Add console.log at key points
176
+ 3. SIMPLIFY - Create minimal reproduction
177
+ 4. RESEARCH - Search for exact error message
178
+ 5. BREAK - Take a walk, fresh perspective helps
179
+ `,
180
+ design: `
181
+ DESIGN APPROACH:
182
+ ================
183
+ 1. START SIMPLE - Basic version first
184
+ 2. USER FIRST - What problem does this solve?
185
+ 3. ITERATE - Ship v1, improve based on feedback
186
+ 4. PATTERNS - Look for similar solutions
187
+ 5. VALIDATE - Show mockup before building
188
+ `,
189
+ performance: `
190
+ PERFORMANCE STRATEGY:
191
+ ====================
192
+ 1. MEASURE FIRST - Profile before optimizing
193
+ 2. BIGGEST WINS - Focus on slowest parts
194
+ 3. CACHE - Store expensive computations
195
+ 4. LAZY LOAD - Defer non-critical work
196
+ 5. MONITOR - Track improvements
197
+ `,
198
+ default: `
199
+ GENERAL STRATEGY:
200
+ ================
201
+ 1. BREAK IT DOWN - Divide into smaller tasks
202
+ 2. START SMALL - Implement simplest part first
203
+ 3. TEST OFTEN - Verify each step works
204
+ 4. DOCUMENT - Write down what you learn
205
+ 5. SHIP IT - Perfect is the enemy of done
206
+ `,
207
+ }
208
+
209
+ const helpType =
210
+ Object.keys(helps).find((key) => issue.toLowerCase().includes(key)) || 'default'
211
+
212
+ return helps[helpType]
213
+ }
214
+
215
+ /**
216
+ * Suggest next action based on context
217
+ * Clear, actionable suggestions for Codex
218
+ */
219
+ suggestNextAction(context) {
220
+ const suggestions = {
221
+ taskCompleted: 'NEXT: Check task queue with /p:next',
222
+ featureShipped: 'NEXT: Set new focus with /p:now [task]',
223
+ ideaCaptured: 'NEXT: Start working with /p:now [task]',
224
+ initialized: 'NEXT: Set first task with /p:now "your first task"',
225
+ stuck: 'NEXT: Break down problem with /p:idea for each step',
226
+ }
227
+
228
+ return suggestions[context] || 'NEXT: What would you like to work on?'
229
+ }
230
+
231
+ /**
232
+ * Handle sandboxed environment limitations
233
+ */
234
+ async ensureSandboxCompatibility() {
235
+ const isSandboxed =
236
+ process.cwd().includes('/sandbox/') ||
237
+ process.cwd().includes('/tmp/') ||
238
+ process.env.CODEX_SANDBOX
239
+
240
+ if (isSandboxed) {
241
+ return {
242
+ sandboxed: true,
243
+ basePath: process.cwd(),
244
+ restrictions: ['no-network', 'limited-filesystem'],
245
+ }
246
+ }
247
+
248
+ return {
249
+ sandboxed: false,
250
+ basePath: process.cwd(),
251
+ restrictions: [],
252
+ }
253
+ }
254
+ }
255
+
256
+ module.exports = CodexAgent
@@ -0,0 +1,465 @@
1
+ /**
2
+ * Terminal Agent Adapter
3
+ * Implements prjct commands for terminal/CLI environment
4
+ */
5
+
6
+ const fs = require('fs').promises
7
+
8
+ let chalk
9
+ let animations
10
+
11
+ try {
12
+ chalk = require('chalk')
13
+
14
+ if (chalk.supportsColor && chalk.supportsColor.has16m) {
15
+ animations = require('../animations')
16
+ } else {
17
+ animations = require('../animations-simple')
18
+ }
19
+ } catch (e) {
20
+ chalk = {
21
+ green: (str) => str,
22
+ blue: (str) => str,
23
+ yellow: (str) => str,
24
+ red: (str) => str,
25
+ cyan: (str) => str,
26
+ magenta: (str) => str,
27
+ bold: (str) => str,
28
+ dim: (str) => str,
29
+ }
30
+ animations = null
31
+ }
32
+
33
+ class TerminalAgent {
34
+ constructor() {
35
+ this.name = 'Terminal/CLI'
36
+ this.type = 'terminal'
37
+ }
38
+
39
+ /**
40
+ * Format response for terminal with ANSI colors and emojis
41
+ */
42
+ formatResponse(message, type = 'info') {
43
+ if (animations) {
44
+ if (type === 'success' && message.includes('Cleanup complete')) {
45
+ const filesMatch = message.match(/Files removed: (\d+)/)
46
+ const tasksMatch = message.match(/Tasks archived: (\d+)/)
47
+ const spaceMatch = message.match(/Space freed: ([\d.]+)/)
48
+
49
+ if (filesMatch && tasksMatch && spaceMatch) {
50
+ return animations.formatCleanup(
51
+ parseInt(filesMatch[1]),
52
+ parseInt(tasksMatch[1]),
53
+ parseFloat(spaceMatch[1]),
54
+ )
55
+ }
56
+ }
57
+
58
+ switch (type) {
59
+ case 'success':
60
+ return animations.formatSuccess(message)
61
+ case 'error':
62
+ return animations.formatError(message)
63
+ case 'ship':
64
+ return animations.formatShip(message, 1)
65
+ case 'focus':
66
+ return animations.formatFocus(message, new Date().toISOString())
67
+ case 'idea':
68
+ return animations.formatIdea(message)
69
+ default: {
70
+ const emojis = {
71
+ warning: '⚠️',
72
+ info: 'ℹ️',
73
+ celebrate: '🎉',
74
+ progress: '📊',
75
+ task: '📝',
76
+ }
77
+ const emoji = emojis[type] || '💬'
78
+ return `${emoji} ${animations.colors.text(message)}`
79
+ }
80
+ }
81
+ }
82
+
83
+ const emojis = {
84
+ success: '✅',
85
+ error: '❌',
86
+ warning: '⚠️',
87
+ info: 'ℹ️',
88
+ celebrate: '🎉',
89
+ ship: '🚀',
90
+ focus: '🎯',
91
+ idea: '💡',
92
+ progress: '📊',
93
+ task: '📝',
94
+ }
95
+
96
+ const colors = {
97
+ success: chalk.green,
98
+ error: chalk.red,
99
+ warning: chalk.yellow,
100
+ info: chalk.blue,
101
+ celebrate: chalk.magenta,
102
+ ship: chalk.cyan,
103
+ focus: chalk.green,
104
+ idea: chalk.yellow,
105
+ progress: chalk.blue,
106
+ task: chalk.cyan,
107
+ }
108
+
109
+ const emoji = emojis[type] || emojis.info
110
+ const color = colors[type] || colors.info
111
+
112
+ return `${emoji} ${color(message)}`
113
+ }
114
+
115
+ /**
116
+ * Read file using native fs
117
+ */
118
+ async readFile(filePath) {
119
+ try {
120
+ return await fs.readFile(filePath, 'utf8')
121
+ } catch (error) {
122
+ throw new Error(`Failed to read file ${filePath}: ${error.message}`)
123
+ }
124
+ }
125
+
126
+ /**
127
+ * Write file using native fs
128
+ */
129
+ async writeFile(filePath, content) {
130
+ try {
131
+ await fs.writeFile(filePath, content, 'utf8')
132
+ } catch (error) {
133
+ throw new Error(`Failed to write file ${filePath}: ${error.message}`)
134
+ }
135
+ }
136
+
137
+ /**
138
+ * List directory contents
139
+ */
140
+ async listDirectory(dirPath) {
141
+ try {
142
+ return await fs.readdir(dirPath)
143
+ } catch (error) {
144
+ throw new Error(`Failed to list directory ${dirPath}: ${error.message}`)
145
+ }
146
+ }
147
+
148
+ /**
149
+ * Check if file exists
150
+ */
151
+ async fileExists(filePath) {
152
+ try {
153
+ await fs.access(filePath)
154
+ return true
155
+ } catch {
156
+ return false
157
+ }
158
+ }
159
+
160
+ /**
161
+ * Create directory
162
+ */
163
+ async createDirectory(dirPath) {
164
+ try {
165
+ await fs.mkdir(dirPath, { recursive: true })
166
+ } catch (error) {
167
+ throw new Error(`Failed to create directory ${dirPath}: ${error.message}`)
168
+ }
169
+ }
170
+
171
+ /**
172
+ * Get current timestamp in ISO format
173
+ */
174
+ getTimestamp() {
175
+ return new Date().toISOString()
176
+ }
177
+
178
+ /**
179
+ * Format task list with colors
180
+ */
181
+ formatTaskList(tasks) {
182
+ if (!tasks || tasks.length === 0) {
183
+ return this.formatResponse('No tasks in queue', 'info')
184
+ }
185
+
186
+ if (animations) {
187
+ let output = animations.colors.primary.bold('\n📋 Task Queue\n')
188
+ output += animations.colors.dim('─'.repeat(40)) + '\n'
189
+
190
+ tasks.forEach((task, index) => {
191
+ output += animations.colors.task(` ${index + 1}.`) + ` ${animations.colors.text(task)}\n`
192
+ })
193
+
194
+ return output
195
+ }
196
+
197
+ let output = chalk.bold('\n📋 Task Queue\n')
198
+ output += chalk.dim('─'.repeat(40)) + '\n'
199
+
200
+ tasks.forEach((task, index) => {
201
+ output += chalk.cyan(` ${index + 1}.`) + ` ${task}\n`
202
+ })
203
+
204
+ return output
205
+ }
206
+
207
+ /**
208
+ * Format recap with colored output
209
+ */
210
+ formatRecap(data) {
211
+ if (animations) {
212
+ return animations.formatRecap(data)
213
+ }
214
+
215
+ let output = '\n' + chalk.bold('📊 Project Recap\n')
216
+ output += chalk.dim('─'.repeat(40)) + '\n\n'
217
+
218
+ output += `${chalk.green('🎯 Current Task:')} ${data.currentTask || chalk.dim('None')}\n`
219
+ output += `${chalk.cyan('🚀 Shipped Features:')} ${chalk.bold(data.shippedCount)} total\n`
220
+ output += `${chalk.blue('📝 Queued Tasks:')} ${data.queuedCount}\n`
221
+ output += `${chalk.yellow('💡 Ideas Captured:')} ${data.ideasCount}\n`
222
+
223
+ if (data.recentActivity) {
224
+ output += '\n' + chalk.bold('Recent Activity:\n')
225
+ output += data.recentActivity
226
+ }
227
+
228
+ output += '\n' + chalk.dim('─'.repeat(40))
229
+ output += '\n' + chalk.green('💪 Keep shipping! Every feature counts!')
230
+
231
+ return output
232
+ }
233
+
234
+ /**
235
+ * Format progress report
236
+ */
237
+ formatProgress(data) {
238
+ if (animations) {
239
+ const trend =
240
+ data.velocity > data.previousVelocity
241
+ ? '📈'
242
+ : data.velocity < data.previousVelocity
243
+ ? '📉'
244
+ : '➡️'
245
+
246
+ const divider = animations.colors.primary('━'.repeat(40))
247
+
248
+ let output = '\n' + divider + '\n'
249
+ output += animations.colors.primary.bold(`📊 PROGRESS REPORT - ${data.period.toUpperCase()}\n`)
250
+ output += divider + '\n\n'
251
+
252
+ output += animations.colors.text('Features Shipped: ') + animations.colors.success.bold(data.count) + '\n'
253
+ output += animations.colors.text('Velocity: ') + animations.colors.info.bold(data.velocity.toFixed(1) + ' features/day') + ' ' + trend + '\n'
254
+
255
+ if (data.recentFeatures) {
256
+ output += '\n' + animations.colors.primary.bold('Recent Wins:\n')
257
+ output += data.recentFeatures
258
+ }
259
+
260
+ output += '\n' + divider
261
+ output += '\n' + animations.colors.celebrate(data.motivationalMessage || '🚀 Keep shipping!')
262
+
263
+ return output
264
+ }
265
+
266
+ const trend =
267
+ data.velocity > data.previousVelocity
268
+ ? '📈'
269
+ : data.velocity < data.previousVelocity
270
+ ? '📉'
271
+ : '➡️'
272
+
273
+ let output = '\n' + chalk.bold(`📊 Progress Report (${data.period})\n`)
274
+ output += chalk.dim('─'.repeat(40)) + '\n\n'
275
+
276
+ output += `${chalk.green('Features Shipped:')} ${chalk.bold(data.count)}\n`
277
+ output += `${chalk.blue('Velocity:')} ${data.velocity.toFixed(1)} features/day ${trend}\n`
278
+
279
+ if (data.recentFeatures) {
280
+ output += '\n' + chalk.bold('Recent Wins:\n')
281
+ output += data.recentFeatures
282
+ }
283
+
284
+ output += '\n' + chalk.dim('─'.repeat(40))
285
+ output += '\n' + chalk.green(data.motivationalMessage)
286
+
287
+ return output
288
+ }
289
+
290
+ /**
291
+ * Get help content based on issue type
292
+ */
293
+ getHelpContent(issue) {
294
+ if (animations) {
295
+ const c = animations.colors
296
+ const helps = {
297
+ debugging: `
298
+ ${c.error.bold('🔍 Debugging Strategy:')}
299
+ ${c.secondary('1.')} ${c.text.bold('Isolate')} - Comment out code until error disappears
300
+ ${c.secondary('2.')} ${c.text.bold('Log')} - Add console.log at key points
301
+ ${c.secondary('3.')} ${c.text.bold('Simplify')} - Create minimal reproduction
302
+ ${c.secondary('4.')} ${c.text.bold('Research')} - Search for exact error message
303
+ ${c.secondary('5.')} ${c.text.bold('Break')} - Take a walk, fresh perspective helps!
304
+ `,
305
+ design: `
306
+ ${c.primary.bold('🎨 Design Approach:')}
307
+ ${c.secondary('1.')} ${c.text.bold('Start Simple')} - Basic version first
308
+ ${c.secondary('2.')} ${c.text.bold('User First')} - What problem does this solve?
309
+ ${c.secondary('3.')} ${c.text.bold('Iterate')} - Ship v1, improve based on feedback
310
+ ${c.secondary('4.')} ${c.text.bold('Patterns')} - Look for similar solutions
311
+ ${c.secondary('5.')} ${c.text.bold('Validate')} - Show mockup before building
312
+ `,
313
+ performance: `
314
+ ${c.warning.bold('⚡ Performance Strategy:')}
315
+ ${c.secondary('1.')} ${c.text.bold('Measure First')} - Profile before optimizing
316
+ ${c.secondary('2.')} ${c.text.bold('Biggest Wins')} - Focus on slowest parts
317
+ ${c.secondary('3.')} ${c.text.bold('Cache')} - Store expensive computations
318
+ ${c.secondary('4.')} ${c.text.bold('Lazy Load')} - Defer non-critical work
319
+ ${c.secondary('5.')} ${c.text.bold('Monitor')} - Track improvements
320
+ `,
321
+ default: `
322
+ ${c.idea.bold('💡 General Strategy:')}
323
+ ${c.secondary('1.')} ${c.text.bold('Break It Down')} - Divide into smaller tasks
324
+ ${c.secondary('2.')} ${c.text.bold('Start Small')} - Implement simplest part first
325
+ ${c.secondary('3.')} ${c.text.bold('Test Often')} - Verify each step works
326
+ ${c.secondary('4.')} ${c.text.bold('Document')} - Write down what you learn
327
+ ${c.secondary('5.')} ${c.text.bold('Ship It')} - Perfect is the enemy of done
328
+ `,
329
+ }
330
+
331
+ const helpType =
332
+ Object.keys(helps).find((key) => issue.toLowerCase().includes(key)) || 'default'
333
+
334
+ return helps[helpType]
335
+ }
336
+
337
+ const helps = {
338
+ debugging: `
339
+ ${chalk.bold('🔍 Debugging Strategy:')}
340
+ ${chalk.cyan('1.')} ${chalk.bold('Isolate')} - Comment out code until error disappears
341
+ ${chalk.cyan('2.')} ${chalk.bold('Log')} - Add console.log at key points
342
+ ${chalk.cyan('3.')} ${chalk.bold('Simplify')} - Create minimal reproduction
343
+ ${chalk.cyan('4.')} ${chalk.bold('Research')} - Search for exact error message
344
+ ${chalk.cyan('5.')} ${chalk.bold('Break')} - Take a walk, fresh perspective helps!
345
+ `,
346
+ design: `
347
+ ${chalk.bold('🎨 Design Approach:')}
348
+ ${chalk.cyan('1.')} ${chalk.bold('Start Simple')} - Basic version first
349
+ ${chalk.cyan('2.')} ${chalk.bold('User First')} - What problem does this solve?
350
+ ${chalk.cyan('3.')} ${chalk.bold('Iterate')} - Ship v1, improve based on feedback
351
+ ${chalk.cyan('4.')} ${chalk.bold('Patterns')} - Look for similar solutions
352
+ ${chalk.cyan('5.')} ${chalk.bold('Validate')} - Show mockup before building
353
+ `,
354
+ performance: `
355
+ ${chalk.bold('⚡ Performance Strategy:')}
356
+ ${chalk.cyan('1.')} ${chalk.bold('Measure First')} - Profile before optimizing
357
+ ${chalk.cyan('2.')} ${chalk.bold('Biggest Wins')} - Focus on slowest parts
358
+ ${chalk.cyan('3.')} ${chalk.bold('Cache')} - Store expensive computations
359
+ ${chalk.cyan('4.')} ${chalk.bold('Lazy Load')} - Defer non-critical work
360
+ ${chalk.cyan('5.')} ${chalk.bold('Monitor')} - Track improvements
361
+ `,
362
+ default: `
363
+ ${chalk.bold('💡 General Strategy:')}
364
+ ${chalk.cyan('1.')} ${chalk.bold('Break It Down')} - Divide into smaller tasks
365
+ ${chalk.cyan('2.')} ${chalk.bold('Start Small')} - Implement simplest part first
366
+ ${chalk.cyan('3.')} ${chalk.bold('Test Often')} - Verify each step works
367
+ ${chalk.cyan('4.')} ${chalk.bold('Document')} - Write down what you learn
368
+ ${chalk.cyan('5.')} ${chalk.bold('Ship It')} - Perfect is the enemy of done
369
+ `,
370
+ }
371
+
372
+ const helpType =
373
+ Object.keys(helps).find((key) => issue.toLowerCase().includes(key)) || 'default'
374
+
375
+ return helps[helpType]
376
+ }
377
+
378
+ /**
379
+ * Suggest next action based on context
380
+ */
381
+ suggestNextAction(context) {
382
+ if (animations) {
383
+ const c = animations.colors
384
+ const suggestions = {
385
+ taskCompleted:
386
+ c.dim('→ Ready for the next challenge? Use ') +
387
+ c.success('prjct next') +
388
+ c.dim(' to see your queue!'),
389
+ featureShipped:
390
+ c.dim('→ Awesome! Take a moment to celebrate, then ') +
391
+ c.ship('prjct now') +
392
+ c.dim(' for next focus!'),
393
+ ideaCaptured:
394
+ c.dim('→ Great idea! Use ') +
395
+ c.idea('prjct now') +
396
+ c.dim(' to start working on it!'),
397
+ initialized: c.dim('→ All set! Start with ') + c.primary('prjct now "your first task"'),
398
+ stuck:
399
+ c.dim('→ You got this! Break it down with ') +
400
+ c.focus('prjct idea') +
401
+ c.dim(' for each step'),
402
+ }
403
+
404
+ return suggestions[context] || c.dim('→ What would you like to work on next?')
405
+ }
406
+
407
+ const suggestions = {
408
+ taskCompleted:
409
+ chalk.dim('→ Ready for the next challenge? Use ') +
410
+ chalk.green('prjct next') +
411
+ chalk.dim(' to see your queue!'),
412
+ featureShipped:
413
+ chalk.dim('→ Awesome! Take a moment to celebrate, then ') +
414
+ chalk.green('prjct now') +
415
+ chalk.dim(' for next focus!'),
416
+ ideaCaptured:
417
+ chalk.dim('→ Great idea! Use ') +
418
+ chalk.green('prjct now') +
419
+ chalk.dim(' to start working on it!'),
420
+ initialized: chalk.dim('→ All set! Start with ') + chalk.green('prjct now "your first task"'),
421
+ stuck:
422
+ chalk.dim('→ You got this! Break it down with ') +
423
+ chalk.green('prjct idea') +
424
+ chalk.dim(' for each step'),
425
+ }
426
+
427
+ return suggestions[context] || chalk.dim('→ What would you like to work on next?')
428
+ }
429
+
430
+ /**
431
+ * Clear terminal screen (for better UX)
432
+ */
433
+ clearScreen() {
434
+ if (process.stdout.isTTY) {
435
+ process.stdout.write('\x1Bc')
436
+ }
437
+ }
438
+
439
+ /**
440
+ * Show progress spinner for long operations
441
+ */
442
+ showSpinner(message) {
443
+ if (!process.stdout.isTTY) return
444
+
445
+ const frames = animations?.frames?.loading || ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏']
446
+ let i = 0
447
+
448
+ const formattedMessage = animations
449
+ ? animations.colors.text(message)
450
+ : message
451
+
452
+ const interval = setInterval(() => {
453
+ const frame = animations?.colors?.primary(frames[i]) || frames[i]
454
+ process.stdout.write(`\r${frame} ${formattedMessage}`)
455
+ i = (i + 1) % frames.length
456
+ }, 80)
457
+
458
+ return () => {
459
+ clearInterval(interval)
460
+ process.stdout.write('\r' + ' '.repeat(message.length + 2) + '\r')
461
+ }
462
+ }
463
+ }
464
+
465
+ module.exports = TerminalAgent