prjct-cli 0.10.0 → 0.10.3

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 (43) hide show
  1. package/CHANGELOG.md +41 -0
  2. package/core/__tests__/agentic/memory-system.test.js +263 -0
  3. package/core/__tests__/agentic/plan-mode.test.js +336 -0
  4. package/core/agentic/chain-of-thought.js +578 -0
  5. package/core/agentic/command-executor.js +238 -4
  6. package/core/agentic/context-builder.js +208 -8
  7. package/core/agentic/ground-truth.js +591 -0
  8. package/core/agentic/loop-detector.js +406 -0
  9. package/core/agentic/memory-system.js +850 -0
  10. package/core/agentic/parallel-tools.js +366 -0
  11. package/core/agentic/plan-mode.js +572 -0
  12. package/core/agentic/prompt-builder.js +76 -1
  13. package/core/agentic/response-templates.js +290 -0
  14. package/core/agentic/semantic-compression.js +517 -0
  15. package/core/agentic/think-blocks.js +657 -0
  16. package/core/agentic/tool-registry.js +32 -0
  17. package/core/agentic/validation-rules.js +380 -0
  18. package/core/command-registry.js +48 -0
  19. package/core/commands.js +65 -1
  20. package/core/context-sync.js +183 -0
  21. package/package.json +7 -15
  22. package/templates/commands/done.md +7 -0
  23. package/templates/commands/feature.md +8 -0
  24. package/templates/commands/ship.md +8 -0
  25. package/templates/commands/spec.md +128 -0
  26. package/templates/global/CLAUDE.md +17 -0
  27. package/core/__tests__/agentic/agent-router.test.js +0 -398
  28. package/core/__tests__/agentic/command-executor.test.js +0 -223
  29. package/core/__tests__/agentic/context-builder.test.js +0 -160
  30. package/core/__tests__/agentic/context-filter.test.js +0 -494
  31. package/core/__tests__/agentic/prompt-builder.test.js +0 -204
  32. package/core/__tests__/agentic/template-loader.test.js +0 -164
  33. package/core/__tests__/agentic/tool-registry.test.js +0 -243
  34. package/core/__tests__/domain/agent-generator.test.js +0 -289
  35. package/core/__tests__/domain/agent-loader.test.js +0 -179
  36. package/core/__tests__/domain/analyzer.test.js +0 -324
  37. package/core/__tests__/infrastructure/author-detector.test.js +0 -103
  38. package/core/__tests__/infrastructure/config-manager.test.js +0 -454
  39. package/core/__tests__/infrastructure/path-manager.test.js +0 -412
  40. package/core/__tests__/setup.test.js +0 -15
  41. package/core/__tests__/utils/date-helper.test.js +0 -169
  42. package/core/__tests__/utils/file-helper.test.js +0 -258
  43. package/core/__tests__/utils/jsonl-helper.test.js +0 -387
@@ -0,0 +1,290 @@
1
+ /**
2
+ * Response Templates
3
+ * Minimal output templates for all commands
4
+ * Rule: < 4 lines, always actionable
5
+ *
6
+ * OPTIMIZATION (P0.3): Minimal Output
7
+ * - Concise responses (< 4 lines)
8
+ * - Always suggest next action
9
+ * - Use symbols for status, not words
10
+ *
11
+ * Source: Claude Code, Kiro patterns
12
+ */
13
+
14
+ /**
15
+ * Format duration from milliseconds or ISO strings
16
+ * @param {number|string|Date} start - Start time
17
+ * @param {number|string|Date} end - End time (defaults to now)
18
+ * @returns {string} Human-readable duration
19
+ */
20
+ function formatDuration(start, end = new Date()) {
21
+ const startMs = new Date(start).getTime()
22
+ const endMs = new Date(end).getTime()
23
+ const diffMs = endMs - startMs
24
+
25
+ const hours = Math.floor(diffMs / (1000 * 60 * 60))
26
+ const minutes = Math.floor((diffMs % (1000 * 60 * 60)) / (1000 * 60))
27
+
28
+ if (hours > 0) {
29
+ return `${hours}h ${minutes}m`
30
+ }
31
+ return `${minutes}m`
32
+ }
33
+
34
+ /**
35
+ * Truncate text to max length with ellipsis
36
+ * @param {string} text - Text to truncate
37
+ * @param {number} maxLength - Maximum length
38
+ * @returns {string}
39
+ */
40
+ function truncate(text, maxLength = 40) {
41
+ if (!text) return ''
42
+ if (text.length <= maxLength) return text
43
+ return text.substring(0, maxLength - 3) + '...'
44
+ }
45
+
46
+ /**
47
+ * Response templates for each command
48
+ * Each template is a function that returns minimal formatted output
49
+ */
50
+ const templates = {
51
+ /**
52
+ * /p:done - Task completed
53
+ */
54
+ done: ({ task, duration, nextTask }) => {
55
+ let output = `✓ '${truncate(task)}' (${duration})`
56
+ if (nextTask) {
57
+ output += `\n→ Next: '${truncate(nextTask)}'`
58
+ }
59
+ output += '\n\n/p:ship to release'
60
+ return output
61
+ },
62
+
63
+ /**
64
+ * /p:now - Current task set/shown
65
+ */
66
+ now: ({ task, started, isNew }) => {
67
+ if (!task) {
68
+ return `No active task\n→ /p:now "task" to start`
69
+ }
70
+ if (isNew) {
71
+ return `🎯 Started: '${truncate(task)}'\n→ /p:done when complete`
72
+ }
73
+ return `🎯 Working on: '${truncate(task)}'\n⏱️ ${started || 'just now'}\n→ /p:done when complete`
74
+ },
75
+
76
+ /**
77
+ * /p:next - Priority queue
78
+ */
79
+ next: ({ tasks, total }) => {
80
+ if (!tasks || tasks.length === 0) {
81
+ return `Queue empty\n→ /p:feature or /p:idea to add`
82
+ }
83
+ const top3 = tasks.slice(0, 3).map((t, i) =>
84
+ `${i + 1}. ${truncate(t.name, 35)}`
85
+ ).join('\n')
86
+ const more = total > 3 ? `\n+${total - 3} more` : ''
87
+ return `${top3}${more}\n\n/p:now 1 to start`
88
+ },
89
+
90
+ /**
91
+ * /p:ship - Feature shipped
92
+ */
93
+ ship: ({ feature, agent, duration, version }) => {
94
+ let output = `🚀 Shipped: '${truncate(feature)}'`
95
+ if (agent) output += ` (${agent})`
96
+ if (duration) output += ` | ${duration}`
97
+ if (version) output += ` | v${version}`
98
+ output += '\n→ /compact recommended'
99
+ return output
100
+ },
101
+
102
+ /**
103
+ * /p:idea - Idea captured
104
+ */
105
+ idea: ({ idea, addedToQueue }) => {
106
+ let output = `💡 Captured: '${truncate(idea)}'`
107
+ if (addedToQueue) {
108
+ output += '\n→ Added to queue'
109
+ }
110
+ output += '\n\n/p:next to see queue'
111
+ return output
112
+ },
113
+
114
+ /**
115
+ * /p:feature - Feature added
116
+ */
117
+ feature: ({ feature, tasks, impact, effort }) => {
118
+ let output = `📋 Feature: '${truncate(feature)}'`
119
+ if (tasks) output += ` (${tasks} tasks)`
120
+ if (impact) output += `\nImpact: ${impact}`
121
+ if (effort) output += ` | Effort: ${effort}`
122
+ output += '\n\n/p:now to start'
123
+ return output
124
+ },
125
+
126
+ /**
127
+ * /p:bug - Bug reported
128
+ */
129
+ bug: ({ description, priority, addedAt }) => {
130
+ const priorityIcon = {
131
+ 'critical': '🔴',
132
+ 'high': '🟠',
133
+ 'medium': '🟡',
134
+ 'low': '🟢'
135
+ }[priority] || '🟡'
136
+
137
+ let output = `${priorityIcon} Bug: '${truncate(description)}'\nPriority: ${priority}`
138
+ if (addedAt) {
139
+ output += ` | Added: ${addedAt}`
140
+ }
141
+ output += '\n\n/p:now to fix'
142
+ return output
143
+ },
144
+
145
+ /**
146
+ * /p:pause - Task paused
147
+ */
148
+ pause: ({ task, duration }) => {
149
+ return `⏸️ Paused: '${truncate(task)}' (${duration})\n→ /p:resume to continue`
150
+ },
151
+
152
+ /**
153
+ * /p:resume - Task resumed
154
+ */
155
+ resume: ({ task, pausedFor }) => {
156
+ return `▶️ Resumed: '${truncate(task)}'\nPaused for: ${pausedFor}\n→ /p:done when complete`
157
+ },
158
+
159
+ /**
160
+ * /p:recap - Project overview
161
+ */
162
+ recap: ({ shipped, inProgress, queued, momentum }) => {
163
+ const momentumIcon = {
164
+ 'high': '🔥',
165
+ 'medium': '✨',
166
+ 'low': '💤'
167
+ }[momentum] || '✨'
168
+
169
+ return `${momentumIcon} ${shipped} shipped | ${inProgress ? '1 active' : '0 active'} | ${queued} queued`
170
+ },
171
+
172
+ /**
173
+ * /p:progress - Progress metrics
174
+ */
175
+ progress: ({ period, shipped, velocity, trend }) => {
176
+ const trendIcon = trend > 0 ? '↑' : trend < 0 ? '↓' : '→'
177
+ return `📊 ${period}: ${shipped} shipped\nVelocity: ${velocity}/week ${trendIcon}`
178
+ },
179
+
180
+ /**
181
+ * /p:analyze - Analysis complete
182
+ */
183
+ analyze: ({ stack, files, agents }) => {
184
+ return `🔍 Analyzed: ${stack}\n${files} files | ${agents} agents generated\n\n/p:sync to update`
185
+ },
186
+
187
+ /**
188
+ * /p:sync - Sync complete
189
+ */
190
+ sync: ({ updated, agents }) => {
191
+ return `🔄 Synced: ${updated} files updated\n${agents} agents refreshed`
192
+ },
193
+
194
+ /**
195
+ * /p:help - Help shown
196
+ */
197
+ help: ({ context, suggestions }) => {
198
+ const sugs = suggestions.slice(0, 3).map(s => `• ${s}`).join('\n')
199
+ return `📚 ${context}\n\n${sugs}`
200
+ },
201
+
202
+ /**
203
+ * /p:suggest - Suggestions
204
+ */
205
+ suggest: ({ urgency, suggestion, command }) => {
206
+ const urgencyIcon = {
207
+ 'high': '🔥',
208
+ 'medium': '💡',
209
+ 'low': '✨'
210
+ }[urgency] || '💡'
211
+
212
+ return `${urgencyIcon} ${suggestion}\n→ ${command}`
213
+ },
214
+
215
+ /**
216
+ * /p:spec - Spec created/updated
217
+ */
218
+ spec: ({ name, status, tasks, requirements, isNew }) => {
219
+ let output = isNew
220
+ ? `📋 Created spec: '${truncate(name)}'`
221
+ : `📋 Updated spec: '${truncate(name)}'`
222
+
223
+ if (requirements) output += `\n${requirements} requirements`
224
+ if (tasks) output += ` | ${tasks} tasks`
225
+ if (status) output += ` | Status: ${status}`
226
+
227
+ output += '\n\n→ Review and approve to start'
228
+ return output
229
+ },
230
+
231
+ /**
232
+ * Generic success response
233
+ */
234
+ success: ({ message, nextAction }) => {
235
+ let output = `✓ ${message}`
236
+ if (nextAction) {
237
+ output += `\n→ ${nextAction}`
238
+ }
239
+ return output
240
+ },
241
+
242
+ /**
243
+ * Generic error response
244
+ */
245
+ error: ({ error, suggestion }) => {
246
+ let output = `❌ ${error}`
247
+ if (suggestion) {
248
+ output += `\n→ ${suggestion}`
249
+ }
250
+ return output
251
+ }
252
+ }
253
+
254
+ /**
255
+ * Format a response using the appropriate template
256
+ *
257
+ * @param {string} commandName - Command name
258
+ * @param {Object} data - Data for the template
259
+ * @returns {string} Formatted response
260
+ */
261
+ function format(commandName, data) {
262
+ const template = templates[commandName]
263
+ if (!template) {
264
+ // Fallback to generic success/error
265
+ if (data.error) {
266
+ return templates.error(data)
267
+ }
268
+ return templates.success(data)
269
+ }
270
+
271
+ return template(data)
272
+ }
273
+
274
+ /**
275
+ * Check if response exceeds recommended length
276
+ * @param {string} response - Response text
277
+ * @returns {boolean} True if too long
278
+ */
279
+ function isTooLong(response) {
280
+ const lines = response.split('\n').filter(l => l.trim())
281
+ return lines.length > 4
282
+ }
283
+
284
+ module.exports = {
285
+ format,
286
+ templates,
287
+ formatDuration,
288
+ truncate,
289
+ isTooLong
290
+ }