prjct-cli 0.10.0 ā 0.10.2
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.
- package/CHANGELOG.md +31 -0
- package/core/__tests__/agentic/memory-system.test.js +263 -0
- package/core/__tests__/agentic/plan-mode.test.js +336 -0
- package/core/agentic/chain-of-thought.js +578 -0
- package/core/agentic/command-executor.js +238 -4
- package/core/agentic/context-builder.js +208 -8
- package/core/agentic/ground-truth.js +591 -0
- package/core/agentic/loop-detector.js +406 -0
- package/core/agentic/memory-system.js +850 -0
- package/core/agentic/parallel-tools.js +366 -0
- package/core/agentic/plan-mode.js +572 -0
- package/core/agentic/prompt-builder.js +76 -1
- package/core/agentic/response-templates.js +290 -0
- package/core/agentic/semantic-compression.js +517 -0
- package/core/agentic/think-blocks.js +657 -0
- package/core/agentic/tool-registry.js +32 -0
- package/core/agentic/validation-rules.js +380 -0
- package/core/command-registry.js +48 -0
- package/core/commands.js +43 -1
- package/core/context-sync.js +183 -0
- package/package.json +7 -15
- package/templates/commands/done.md +7 -0
- package/templates/commands/feature.md +8 -0
- package/templates/commands/ship.md +8 -0
- package/templates/commands/spec.md +128 -0
- package/templates/global/CLAUDE.md +17 -0
- package/core/__tests__/agentic/agent-router.test.js +0 -398
- package/core/__tests__/agentic/command-executor.test.js +0 -223
- package/core/__tests__/agentic/context-builder.test.js +0 -160
- package/core/__tests__/agentic/context-filter.test.js +0 -494
- package/core/__tests__/agentic/prompt-builder.test.js +0 -204
- package/core/__tests__/agentic/template-loader.test.js +0 -164
- package/core/__tests__/agentic/tool-registry.test.js +0 -243
- package/core/__tests__/domain/agent-generator.test.js +0 -289
- package/core/__tests__/domain/agent-loader.test.js +0 -179
- package/core/__tests__/domain/analyzer.test.js +0 -324
- package/core/__tests__/infrastructure/author-detector.test.js +0 -103
- package/core/__tests__/infrastructure/config-manager.test.js +0 -454
- package/core/__tests__/infrastructure/path-manager.test.js +0 -412
- package/core/__tests__/setup.test.js +0 -15
- package/core/__tests__/utils/date-helper.test.js +0 -169
- package/core/__tests__/utils/file-helper.test.js +0 -258
- package/core/__tests__/utils/jsonl-helper.test.js +0 -387
|
@@ -0,0 +1,380 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Validation Rules
|
|
3
|
+
* Explicit pre-flight checks for each command
|
|
4
|
+
* Returns SPECIFIC error messages, never generic failures
|
|
5
|
+
*
|
|
6
|
+
* OPTIMIZATION (P0.2): Anti-Hallucination Pattern
|
|
7
|
+
* - Ground truth verification before actions
|
|
8
|
+
* - Specific error messages for each failure mode
|
|
9
|
+
* - Actionable suggestions in every error
|
|
10
|
+
*
|
|
11
|
+
* Source: Claude Code, Devin, Augment Code patterns
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
const contextBuilder = require('./context-builder')
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Validation result structure
|
|
18
|
+
* @typedef {Object} ValidationResult
|
|
19
|
+
* @property {boolean} valid - Whether validation passed
|
|
20
|
+
* @property {string|null} error - Specific error message if invalid
|
|
21
|
+
* @property {string|null} suggestion - Actionable next step
|
|
22
|
+
* @property {Object} state - Pre-loaded state for command execution
|
|
23
|
+
*/
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Command-specific validation rules
|
|
27
|
+
* Each rule returns { valid, error, suggestion, state }
|
|
28
|
+
*/
|
|
29
|
+
const validationRules = {
|
|
30
|
+
/**
|
|
31
|
+
* /p:done - Complete current task
|
|
32
|
+
*/
|
|
33
|
+
async done(context) {
|
|
34
|
+
const state = await contextBuilder.loadStateForCommand(context, 'done')
|
|
35
|
+
|
|
36
|
+
// Check 1: now.md exists and has content
|
|
37
|
+
if (!state.now || state.now.trim() === '') {
|
|
38
|
+
return {
|
|
39
|
+
valid: false,
|
|
40
|
+
error: 'No active task to complete',
|
|
41
|
+
suggestion: 'Start a task first with /p:now "task description"',
|
|
42
|
+
state
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// Check 2: Task is not a placeholder/comment
|
|
47
|
+
const content = state.now.trim()
|
|
48
|
+
if (content.startsWith('#') && content.split('\n').length === 1) {
|
|
49
|
+
return {
|
|
50
|
+
valid: false,
|
|
51
|
+
error: 'now.md contains only a header, no task description',
|
|
52
|
+
suggestion: 'Add task details or start fresh with /p:now "task"',
|
|
53
|
+
state
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// Check 3: Task is not blocked
|
|
58
|
+
if (content.toLowerCase().includes('blocked') ||
|
|
59
|
+
content.toLowerCase().includes('waiting for')) {
|
|
60
|
+
return {
|
|
61
|
+
valid: false,
|
|
62
|
+
error: 'Task appears to be blocked',
|
|
63
|
+
suggestion: 'Resolve the blocker first or use /p:pause to save progress',
|
|
64
|
+
state
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
return { valid: true, error: null, suggestion: null, state }
|
|
69
|
+
},
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* /p:ship - Ship a feature
|
|
73
|
+
*/
|
|
74
|
+
async ship(context) {
|
|
75
|
+
const state = await contextBuilder.loadStateForCommand(context, 'ship')
|
|
76
|
+
|
|
77
|
+
// Check 1: Has something to ship (now.md or recent shipped items)
|
|
78
|
+
const hasCurrentTask = state.now && state.now.trim() !== ''
|
|
79
|
+
const hasRecentShips = state.shipped && state.shipped.trim() !== ''
|
|
80
|
+
|
|
81
|
+
if (!hasCurrentTask && !hasRecentShips) {
|
|
82
|
+
return {
|
|
83
|
+
valid: false,
|
|
84
|
+
error: 'Nothing to ship yet',
|
|
85
|
+
suggestion: 'Build something first with /p:now "feature name"',
|
|
86
|
+
state
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// Check 2: Feature name provided (from params)
|
|
91
|
+
if (!context.params.feature && !context.params.description) {
|
|
92
|
+
// Try to extract from now.md
|
|
93
|
+
if (hasCurrentTask) {
|
|
94
|
+
// Auto-extract feature name - this is OK
|
|
95
|
+
return { valid: true, error: null, suggestion: null, state }
|
|
96
|
+
}
|
|
97
|
+
return {
|
|
98
|
+
valid: false,
|
|
99
|
+
error: 'No feature name specified',
|
|
100
|
+
suggestion: 'Specify what to ship: /p:ship "feature name"',
|
|
101
|
+
state
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
return { valid: true, error: null, suggestion: null, state }
|
|
106
|
+
},
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* /p:now - Set or show current task
|
|
110
|
+
*/
|
|
111
|
+
async now(context) {
|
|
112
|
+
const state = await contextBuilder.loadStateForCommand(context, 'now')
|
|
113
|
+
|
|
114
|
+
// If no task param, this is a "show" request - always valid
|
|
115
|
+
if (!context.params.task && !context.params.description) {
|
|
116
|
+
return { valid: true, error: null, suggestion: null, state }
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// Check: If setting new task, warn if one exists
|
|
120
|
+
if (state.now && state.now.trim() !== '') {
|
|
121
|
+
return {
|
|
122
|
+
valid: true, // Still valid, but with warning
|
|
123
|
+
error: null,
|
|
124
|
+
suggestion: `Note: Replacing current task. Use /p:done first to track completion.`,
|
|
125
|
+
state
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
return { valid: true, error: null, suggestion: null, state }
|
|
130
|
+
},
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* /p:next - Show priority queue
|
|
134
|
+
*/
|
|
135
|
+
async next(context) {
|
|
136
|
+
const state = await contextBuilder.loadStateForCommand(context, 'next')
|
|
137
|
+
|
|
138
|
+
if (!state.next || state.next.trim() === '' ||
|
|
139
|
+
!state.next.includes('- [')) {
|
|
140
|
+
return {
|
|
141
|
+
valid: true, // Valid but empty
|
|
142
|
+
error: null,
|
|
143
|
+
suggestion: 'Queue is empty. Add tasks with /p:feature or /p:idea',
|
|
144
|
+
state
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
return { valid: true, error: null, suggestion: null, state }
|
|
149
|
+
},
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* /p:idea - Capture an idea
|
|
153
|
+
*/
|
|
154
|
+
async idea(context) {
|
|
155
|
+
const state = await contextBuilder.loadStateForCommand(context, 'idea')
|
|
156
|
+
|
|
157
|
+
// Check: Idea text provided
|
|
158
|
+
if (!context.params.text && !context.params.description) {
|
|
159
|
+
return {
|
|
160
|
+
valid: false,
|
|
161
|
+
error: 'No idea text provided',
|
|
162
|
+
suggestion: 'Provide your idea: /p:idea "your idea here"',
|
|
163
|
+
state
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
return { valid: true, error: null, suggestion: null, state }
|
|
168
|
+
},
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* /p:feature - Add a new feature
|
|
172
|
+
*/
|
|
173
|
+
async feature(context) {
|
|
174
|
+
const state = await contextBuilder.loadStateForCommand(context, 'feature')
|
|
175
|
+
|
|
176
|
+
// If no description, show interactive template - valid
|
|
177
|
+
if (!context.params.description && !context.params.feature) {
|
|
178
|
+
return { valid: true, error: null, suggestion: null, state }
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
return { valid: true, error: null, suggestion: null, state }
|
|
182
|
+
},
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* /p:pause - Pause current task
|
|
186
|
+
*/
|
|
187
|
+
async pause(context) {
|
|
188
|
+
const state = await contextBuilder.loadStateForCommand(context, 'now')
|
|
189
|
+
|
|
190
|
+
if (!state.now || state.now.trim() === '') {
|
|
191
|
+
return {
|
|
192
|
+
valid: false,
|
|
193
|
+
error: 'No active task to pause',
|
|
194
|
+
suggestion: 'Start a task first with /p:now "task"',
|
|
195
|
+
state
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
return { valid: true, error: null, suggestion: null, state }
|
|
200
|
+
},
|
|
201
|
+
|
|
202
|
+
/**
|
|
203
|
+
* /p:resume - Resume paused task
|
|
204
|
+
*/
|
|
205
|
+
async resume(context) {
|
|
206
|
+
const state = await contextBuilder.loadState(context, ['now'])
|
|
207
|
+
|
|
208
|
+
// Check if there's a paused state to resume
|
|
209
|
+
// This would need to check a paused.md or similar
|
|
210
|
+
return { valid: true, error: null, suggestion: null, state }
|
|
211
|
+
},
|
|
212
|
+
|
|
213
|
+
/**
|
|
214
|
+
* /p:recap - Show project overview
|
|
215
|
+
*/
|
|
216
|
+
async recap(context) {
|
|
217
|
+
const state = await contextBuilder.loadStateForCommand(context, 'recap')
|
|
218
|
+
return { valid: true, error: null, suggestion: null, state }
|
|
219
|
+
},
|
|
220
|
+
|
|
221
|
+
/**
|
|
222
|
+
* /p:progress - Show progress metrics
|
|
223
|
+
*/
|
|
224
|
+
async progress(context) {
|
|
225
|
+
const state = await contextBuilder.loadStateForCommand(context, 'progress')
|
|
226
|
+
return { valid: true, error: null, suggestion: null, state }
|
|
227
|
+
},
|
|
228
|
+
|
|
229
|
+
/**
|
|
230
|
+
* /p:analyze - Analyze repository
|
|
231
|
+
*/
|
|
232
|
+
async analyze(context) {
|
|
233
|
+
const state = await contextBuilder.loadStateForCommand(context, 'analyze')
|
|
234
|
+
return { valid: true, error: null, suggestion: null, state }
|
|
235
|
+
},
|
|
236
|
+
|
|
237
|
+
/**
|
|
238
|
+
* /p:sync - Sync project state
|
|
239
|
+
*/
|
|
240
|
+
async sync(context) {
|
|
241
|
+
const state = await contextBuilder.loadStateForCommand(context, 'sync')
|
|
242
|
+
return { valid: true, error: null, suggestion: null, state }
|
|
243
|
+
},
|
|
244
|
+
|
|
245
|
+
/**
|
|
246
|
+
* /p:bug - Report a bug
|
|
247
|
+
*/
|
|
248
|
+
async bug(context) {
|
|
249
|
+
const state = await contextBuilder.loadState(context, ['next'])
|
|
250
|
+
|
|
251
|
+
if (!context.params.description && !context.params.bug) {
|
|
252
|
+
return {
|
|
253
|
+
valid: false,
|
|
254
|
+
error: 'No bug description provided',
|
|
255
|
+
suggestion: 'Describe the bug: /p:bug "description of the issue"',
|
|
256
|
+
state
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
return { valid: true, error: null, suggestion: null, state }
|
|
261
|
+
},
|
|
262
|
+
|
|
263
|
+
/**
|
|
264
|
+
* /p:help - Show help
|
|
265
|
+
*/
|
|
266
|
+
async help(context) {
|
|
267
|
+
const state = await contextBuilder.loadStateForCommand(context, 'now')
|
|
268
|
+
return { valid: true, error: null, suggestion: null, state }
|
|
269
|
+
},
|
|
270
|
+
|
|
271
|
+
/**
|
|
272
|
+
* /p:ask - Intent translator
|
|
273
|
+
*/
|
|
274
|
+
async ask(context) {
|
|
275
|
+
if (!context.params.query && !context.params.question) {
|
|
276
|
+
return {
|
|
277
|
+
valid: false,
|
|
278
|
+
error: 'No question provided',
|
|
279
|
+
suggestion: 'Ask what you want to do: /p:ask "how do I..."',
|
|
280
|
+
state: {}
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
return { valid: true, error: null, suggestion: null, state: {} }
|
|
285
|
+
},
|
|
286
|
+
|
|
287
|
+
/**
|
|
288
|
+
* /p:suggest - Smart suggestions
|
|
289
|
+
*/
|
|
290
|
+
async suggest(context) {
|
|
291
|
+
const state = await contextBuilder.loadState(context, ['now', 'next', 'shipped', 'metrics'])
|
|
292
|
+
return { valid: true, error: null, suggestion: null, state }
|
|
293
|
+
},
|
|
294
|
+
|
|
295
|
+
/**
|
|
296
|
+
* /p:spec - Spec-driven development
|
|
297
|
+
*/
|
|
298
|
+
async spec(context) {
|
|
299
|
+
const state = await contextBuilder.loadState(context, ['roadmap', 'next'])
|
|
300
|
+
|
|
301
|
+
// If no feature name, this is a "show template" request - always valid
|
|
302
|
+
if (!context.params.feature && !context.params.name && !context.params.description) {
|
|
303
|
+
return {
|
|
304
|
+
valid: true,
|
|
305
|
+
error: null,
|
|
306
|
+
suggestion: 'Provide a feature name to create a spec',
|
|
307
|
+
state
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
// Check queue capacity for new tasks
|
|
312
|
+
const queueContent = state.next || ''
|
|
313
|
+
const taskCount = (queueContent.match(/- \[[ x]\]/g) || []).length
|
|
314
|
+
if (taskCount >= 95) {
|
|
315
|
+
return {
|
|
316
|
+
valid: false,
|
|
317
|
+
error: 'Queue almost full (95+ tasks)',
|
|
318
|
+
suggestion: 'Complete some tasks before creating a new spec. Use /p:done',
|
|
319
|
+
state
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
return { valid: true, error: null, suggestion: null, state }
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
/**
|
|
328
|
+
* Validate command before execution
|
|
329
|
+
*
|
|
330
|
+
* @param {string} commandName - Command to validate
|
|
331
|
+
* @param {Object} context - Built context from contextBuilder
|
|
332
|
+
* @returns {Promise<ValidationResult>}
|
|
333
|
+
*/
|
|
334
|
+
async function validate(commandName, context) {
|
|
335
|
+
const validator = validationRules[commandName]
|
|
336
|
+
|
|
337
|
+
if (!validator) {
|
|
338
|
+
// No specific validation - default to valid
|
|
339
|
+
return {
|
|
340
|
+
valid: true,
|
|
341
|
+
error: null,
|
|
342
|
+
suggestion: null,
|
|
343
|
+
state: await contextBuilder.loadState(context)
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
try {
|
|
348
|
+
return await validator(context)
|
|
349
|
+
} catch (error) {
|
|
350
|
+
return {
|
|
351
|
+
valid: false,
|
|
352
|
+
error: `Validation error: ${error.message}`,
|
|
353
|
+
suggestion: 'Check file permissions and project configuration',
|
|
354
|
+
state: {}
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
/**
|
|
360
|
+
* Format validation error for display
|
|
361
|
+
* Minimal, actionable output
|
|
362
|
+
*
|
|
363
|
+
* @param {ValidationResult} result - Validation result
|
|
364
|
+
* @returns {string} Formatted error message
|
|
365
|
+
*/
|
|
366
|
+
function formatError(result) {
|
|
367
|
+
if (result.valid) return null
|
|
368
|
+
|
|
369
|
+
let output = `ā ${result.error}`
|
|
370
|
+
if (result.suggestion) {
|
|
371
|
+
output += `\nā ${result.suggestion}`
|
|
372
|
+
}
|
|
373
|
+
return output
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
module.exports = {
|
|
377
|
+
validate,
|
|
378
|
+
formatError,
|
|
379
|
+
validationRules
|
|
380
|
+
}
|
package/core/command-registry.js
CHANGED
|
@@ -88,6 +88,31 @@ const COMMANDS = [
|
|
|
88
88
|
],
|
|
89
89
|
},
|
|
90
90
|
|
|
91
|
+
// 3.5. Spec-Driven Development (P1.2)
|
|
92
|
+
{
|
|
93
|
+
name: 'spec',
|
|
94
|
+
category: 'core',
|
|
95
|
+
description: 'Create detailed specifications for complex features',
|
|
96
|
+
usage: {
|
|
97
|
+
claude: '/p:spec "Dark Mode"',
|
|
98
|
+
terminal: 'prjct spec "Dark Mode"',
|
|
99
|
+
},
|
|
100
|
+
params: '[feature]',
|
|
101
|
+
implemented: true,
|
|
102
|
+
hasTemplate: true,
|
|
103
|
+
icon: 'FileText',
|
|
104
|
+
requiresInit: true,
|
|
105
|
+
blockingRules: null,
|
|
106
|
+
features: [
|
|
107
|
+
'Requirements documentation',
|
|
108
|
+
'Design decisions tracking',
|
|
109
|
+
'Tasks broken into 20-30min chunks',
|
|
110
|
+
'User approval workflow',
|
|
111
|
+
'Auto-add tasks to queue on approve',
|
|
112
|
+
'Integrates with /p:feature',
|
|
113
|
+
],
|
|
114
|
+
},
|
|
115
|
+
|
|
91
116
|
// 4. Work - Unified task management (replaces now + build)
|
|
92
117
|
{
|
|
93
118
|
name: 'work',
|
|
@@ -346,6 +371,29 @@ const COMMANDS = [
|
|
|
346
371
|
blockingRules: null,
|
|
347
372
|
isOptional: true,
|
|
348
373
|
},
|
|
374
|
+
{
|
|
375
|
+
name: 'spec',
|
|
376
|
+
category: 'optional',
|
|
377
|
+
description: 'Spec-driven development for complex features',
|
|
378
|
+
usage: {
|
|
379
|
+
claude: '/p:spec "Dark Mode"',
|
|
380
|
+
terminal: 'prjct spec "Dark Mode"',
|
|
381
|
+
},
|
|
382
|
+
params: '[feature_name]',
|
|
383
|
+
implemented: true,
|
|
384
|
+
hasTemplate: true,
|
|
385
|
+
icon: 'FileText',
|
|
386
|
+
requiresInit: true,
|
|
387
|
+
blockingRules: null,
|
|
388
|
+
isOptional: true,
|
|
389
|
+
features: [
|
|
390
|
+
'Clear requirements before coding',
|
|
391
|
+
'Design decisions documented',
|
|
392
|
+
'Tasks broken into 20-30 min chunks',
|
|
393
|
+
'User approval before starting',
|
|
394
|
+
'Auto-adds tasks to queue on approval',
|
|
395
|
+
],
|
|
396
|
+
},
|
|
349
397
|
{
|
|
350
398
|
name: 'cleanup',
|
|
351
399
|
category: 'optional',
|
package/core/commands.js
CHANGED
|
@@ -21,6 +21,7 @@ const path = require('path')
|
|
|
21
21
|
const commandExecutor = require('./agentic/command-executor')
|
|
22
22
|
const contextBuilder = require('./agentic/context-builder')
|
|
23
23
|
const toolRegistry = require('./agentic/tool-registry')
|
|
24
|
+
const memorySystem = require('./agentic/memory-system')
|
|
24
25
|
const pathManager = require('./infrastructure/path-manager')
|
|
25
26
|
const configManager = require('./infrastructure/config-manager')
|
|
26
27
|
const authorDetector = require('./infrastructure/author-detector')
|
|
@@ -306,6 +307,7 @@ class PrjctCommands {
|
|
|
306
307
|
const globalPath = pathManager.getGlobalProjectPath(projectId)
|
|
307
308
|
|
|
308
309
|
// Create base files
|
|
310
|
+
// P1.1: Added patterns.json for Layered Memory System
|
|
309
311
|
const baseFiles = {
|
|
310
312
|
'core/now.md': '# NOW\n\nNo current task. Use `/p:now` to set focus.\n',
|
|
311
313
|
'core/next.md': '# NEXT\n\n## Priority Queue\n\n',
|
|
@@ -314,7 +316,15 @@ class PrjctCommands {
|
|
|
314
316
|
'progress/metrics.md': '# METRICS\n\n',
|
|
315
317
|
'planning/ideas.md': '# IDEAS š”\n\n## Brain Dump\n\n',
|
|
316
318
|
'planning/roadmap.md': '# ROADMAP\n\n',
|
|
319
|
+
'planning/specs/.gitkeep': '# Specs directory - created by /p:spec\n',
|
|
317
320
|
'memory/context.jsonl': '',
|
|
321
|
+
'memory/patterns.json': JSON.stringify({
|
|
322
|
+
version: 1,
|
|
323
|
+
decisions: {},
|
|
324
|
+
preferences: {},
|
|
325
|
+
workflows: {},
|
|
326
|
+
counters: {}
|
|
327
|
+
}, null, 2),
|
|
318
328
|
}
|
|
319
329
|
|
|
320
330
|
for (const [filePath, content] of Object.entries(baseFiles)) {
|
|
@@ -693,6 +703,27 @@ class PrjctCommands {
|
|
|
693
703
|
timestamp: dateHelper.getTimestamp(),
|
|
694
704
|
})
|
|
695
705
|
|
|
706
|
+
// P1.1: Learn patterns from this ship
|
|
707
|
+
const config = await configManager.getConfig(projectPath)
|
|
708
|
+
const projectId = config.projectId
|
|
709
|
+
|
|
710
|
+
// Record shipping workflow patterns
|
|
711
|
+
await memorySystem.learnDecision(projectId, 'commit_footer', 'prjct', 'ship')
|
|
712
|
+
|
|
713
|
+
// Track if tests were run (for quick_ship pattern learning)
|
|
714
|
+
if (testResult.success) {
|
|
715
|
+
await memorySystem.recordDecision(projectId, 'test_before_ship', 'true', 'ship')
|
|
716
|
+
}
|
|
717
|
+
|
|
718
|
+
// Record workflow if it's a quick ship (small changes)
|
|
719
|
+
const isQuickShip = !lintResult.success || !testResult.success
|
|
720
|
+
if (isQuickShip) {
|
|
721
|
+
await memorySystem.recordWorkflow(projectId, 'quick_ship', {
|
|
722
|
+
description: 'Ship without full checks',
|
|
723
|
+
feature_type: feature.toLowerCase().includes('doc') ? 'docs' : 'other'
|
|
724
|
+
})
|
|
725
|
+
}
|
|
726
|
+
|
|
696
727
|
console.log('\nš Feature shipped successfully!\n')
|
|
697
728
|
console.log('š” Recommendation: Compact conversation now')
|
|
698
729
|
console.log(' (Keeps context clean for next feature)\n')
|
|
@@ -2316,8 +2347,14 @@ Agent: ${agent}
|
|
|
2316
2347
|
gitCommits: analysisData.gitStats.totalCommits,
|
|
2317
2348
|
})
|
|
2318
2349
|
|
|
2350
|
+
// Generate dynamic context for Claude
|
|
2351
|
+
const contextSync = require('./context-sync')
|
|
2352
|
+
const projectId = await configManager.getProjectId(projectPath)
|
|
2353
|
+
await contextSync.generateLocalContext(projectPath, projectId)
|
|
2354
|
+
|
|
2319
2355
|
console.log('ā
Analysis complete!\n')
|
|
2320
|
-
console.log('š Full report: analysis/repo-summary.md
|
|
2356
|
+
console.log('š Full report: analysis/repo-summary.md')
|
|
2357
|
+
console.log('š Context: ~/.prjct-cli/projects/' + projectId + '/CLAUDE.md\n')
|
|
2321
2358
|
console.log('Next steps:')
|
|
2322
2359
|
console.log('⢠/p:sync ā Generate agents based on stack')
|
|
2323
2360
|
console.log('⢠/p:feature ā Add a new feature')
|
|
@@ -2482,11 +2519,16 @@ Agent: ${agent}
|
|
|
2482
2519
|
count: generatedAgents.length,
|
|
2483
2520
|
})
|
|
2484
2521
|
|
|
2522
|
+
// Generate dynamic context for Claude
|
|
2523
|
+
const contextSync = require('./context-sync')
|
|
2524
|
+
await contextSync.generateLocalContext(projectPath, projectId)
|
|
2525
|
+
|
|
2485
2526
|
console.log('\nā
Sync complete!\n')
|
|
2486
2527
|
console.log(`š¤ Agents Generated: ${generatedAgents.length}`)
|
|
2487
2528
|
generatedAgents.forEach((agent) => {
|
|
2488
2529
|
console.log(` ⢠${agent}`)
|
|
2489
2530
|
})
|
|
2531
|
+
console.log('š Context: ~/.prjct-cli/projects/' + projectId + '/CLAUDE.md')
|
|
2490
2532
|
console.log('\nš Based on: analysis/repo-summary.md')
|
|
2491
2533
|
console.log('š” See templates/agents/AGENTS.md for reference\n')
|
|
2492
2534
|
console.log('Next steps:')
|