prjct-cli 0.51.0 → 0.53.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.
- package/CHANGELOG.md +45 -0
- package/core/agentic/memory-system.ts +96 -18
- package/core/agentic/prompt-builder.ts +87 -13
- package/core/types/memory.ts +41 -2
- package/dist/bin/prjct.mjs +138 -28
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,50 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.53.0] - 2026-01-30
|
|
4
|
+
|
|
5
|
+
### Features
|
|
6
|
+
|
|
7
|
+
- Lazy template loading with TTL cache - PRJ-76 (#79)
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
## [0.53.0] - 2026-01-30
|
|
11
|
+
|
|
12
|
+
### Features
|
|
13
|
+
|
|
14
|
+
- Lazy template loading with TTL cache - PRJ-76
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
## [0.53.0] - 2026-01-30
|
|
18
|
+
|
|
19
|
+
### Added
|
|
20
|
+
|
|
21
|
+
- **Lazy template loading with TTL cache** (PRJ-76)
|
|
22
|
+
- Templates now loaded on-demand with 60-second TTL cache
|
|
23
|
+
- Added `getTemplate()` method with per-file caching
|
|
24
|
+
- `loadChecklists()` and `loadChecklistRouting()` now use TTL cache
|
|
25
|
+
- Added `clearTemplateCache()` method for testing/forced refresh
|
|
26
|
+
- Reduces disk I/O for frequently accessed templates
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
## [0.52.0] - 2026-01-30
|
|
30
|
+
|
|
31
|
+
### Features
|
|
32
|
+
|
|
33
|
+
- Add confidence scores to all stored preferences - PRJ-104 (#78)
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
## [0.52.0] - 2026-01-30
|
|
37
|
+
|
|
38
|
+
### Added
|
|
39
|
+
|
|
40
|
+
- **Confidence scores for stored preferences** (PRJ-104)
|
|
41
|
+
- All preferences, decisions, and workflows now track confidence level
|
|
42
|
+
- Confidence: `low` (1-2 obs), `medium` (3-5 obs), `high` (6+ or confirmed)
|
|
43
|
+
- Added `confirmPreference()`, `confirmDecision()`, `confirmWorkflow()` methods
|
|
44
|
+
- User confirmation immediately sets confidence to `high`
|
|
45
|
+
- Added `calculateConfidence()` utility function
|
|
46
|
+
|
|
47
|
+
|
|
3
48
|
## [0.51.0] - 2026-01-30
|
|
4
49
|
|
|
5
50
|
### Features
|
|
@@ -22,6 +22,7 @@ import { appendJsonLine, getLastJsonLines } from '../utils/jsonl-helper'
|
|
|
22
22
|
|
|
23
23
|
// Re-export types from canonical location
|
|
24
24
|
export type {
|
|
25
|
+
ConfidenceLevel,
|
|
25
26
|
Decision,
|
|
26
27
|
HistoryEntry,
|
|
27
28
|
HistoryEventType,
|
|
@@ -35,7 +36,7 @@ export type {
|
|
|
35
36
|
Workflow,
|
|
36
37
|
} from '../types/memory'
|
|
37
38
|
|
|
38
|
-
export { MEMORY_TAGS } from '../types/memory'
|
|
39
|
+
export { calculateConfidence, MEMORY_TAGS } from '../types/memory'
|
|
39
40
|
|
|
40
41
|
import type {
|
|
41
42
|
HistoryEntry,
|
|
@@ -49,7 +50,7 @@ import type {
|
|
|
49
50
|
Workflow,
|
|
50
51
|
} from '../types/memory'
|
|
51
52
|
|
|
52
|
-
import { MEMORY_TAGS } from '../types/memory'
|
|
53
|
+
import { calculateConfidence, MEMORY_TAGS } from '../types/memory'
|
|
53
54
|
|
|
54
55
|
// =============================================================================
|
|
55
56
|
// Base Store
|
|
@@ -306,7 +307,8 @@ export class PatternStore extends CachedStore<Patterns> {
|
|
|
306
307
|
projectId: string,
|
|
307
308
|
key: string,
|
|
308
309
|
value: string,
|
|
309
|
-
context: string = ''
|
|
310
|
+
context: string = '',
|
|
311
|
+
options: { userConfirmed?: boolean } = {}
|
|
310
312
|
): Promise<void> {
|
|
311
313
|
const patterns = await this.load(projectId)
|
|
312
314
|
const now = getTimestamp()
|
|
@@ -317,11 +319,14 @@ export class PatternStore extends CachedStore<Patterns> {
|
|
|
317
319
|
count: 1,
|
|
318
320
|
firstSeen: now,
|
|
319
321
|
lastSeen: now,
|
|
320
|
-
confidence: 'low',
|
|
322
|
+
confidence: options.userConfirmed ? 'high' : 'low',
|
|
321
323
|
contexts: [context].filter(Boolean),
|
|
322
|
-
|
|
324
|
+
userConfirmed: options.userConfirmed || false,
|
|
325
|
+
} as Patterns['decisions'][string]
|
|
323
326
|
} else {
|
|
324
|
-
const decision = patterns.decisions[key]
|
|
327
|
+
const decision = patterns.decisions[key] as Patterns['decisions'][string] & {
|
|
328
|
+
userConfirmed?: boolean
|
|
329
|
+
}
|
|
325
330
|
|
|
326
331
|
if (decision.value === value) {
|
|
327
332
|
decision.count++
|
|
@@ -329,23 +334,36 @@ export class PatternStore extends CachedStore<Patterns> {
|
|
|
329
334
|
if (context && !decision.contexts.includes(context)) {
|
|
330
335
|
decision.contexts.push(context)
|
|
331
336
|
}
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
decision.confidence = 'high'
|
|
335
|
-
} else if (decision.count >= 3) {
|
|
336
|
-
decision.confidence = 'medium'
|
|
337
|
+
if (options.userConfirmed) {
|
|
338
|
+
decision.userConfirmed = true
|
|
337
339
|
}
|
|
340
|
+
decision.confidence = calculateConfidence(decision.count, decision.userConfirmed)
|
|
338
341
|
} else {
|
|
339
342
|
decision.value = value
|
|
340
343
|
decision.count = 1
|
|
341
344
|
decision.lastSeen = now
|
|
342
|
-
decision.
|
|
345
|
+
decision.userConfirmed = options.userConfirmed || false
|
|
346
|
+
decision.confidence = options.userConfirmed ? 'high' : 'low'
|
|
343
347
|
}
|
|
344
348
|
}
|
|
345
349
|
|
|
346
350
|
await this.save(projectId)
|
|
347
351
|
}
|
|
348
352
|
|
|
353
|
+
async confirmDecision(projectId: string, key: string): Promise<boolean> {
|
|
354
|
+
const patterns = await this.load(projectId)
|
|
355
|
+
const decision = patterns.decisions[key] as
|
|
356
|
+
| (Patterns['decisions'][string] & { userConfirmed?: boolean })
|
|
357
|
+
| undefined
|
|
358
|
+
if (!decision) return false
|
|
359
|
+
|
|
360
|
+
decision.userConfirmed = true
|
|
361
|
+
decision.confidence = 'high'
|
|
362
|
+
decision.lastSeen = getTimestamp()
|
|
363
|
+
await this.save(projectId)
|
|
364
|
+
return true
|
|
365
|
+
}
|
|
366
|
+
|
|
349
367
|
async getDecision(
|
|
350
368
|
projectId: string,
|
|
351
369
|
key: string
|
|
@@ -378,15 +396,31 @@ export class PatternStore extends CachedStore<Patterns> {
|
|
|
378
396
|
count: 1,
|
|
379
397
|
firstSeen: now,
|
|
380
398
|
lastSeen: now,
|
|
399
|
+
confidence: 'low',
|
|
400
|
+
userConfirmed: false,
|
|
381
401
|
}
|
|
382
402
|
} else {
|
|
383
|
-
patterns.workflows[workflowName]
|
|
384
|
-
|
|
403
|
+
const workflow = patterns.workflows[workflowName]
|
|
404
|
+
workflow.count++
|
|
405
|
+
workflow.lastSeen = now
|
|
406
|
+
workflow.confidence = calculateConfidence(workflow.count, workflow.userConfirmed)
|
|
385
407
|
}
|
|
386
408
|
|
|
387
409
|
await this.save(projectId)
|
|
388
410
|
}
|
|
389
411
|
|
|
412
|
+
async confirmWorkflow(projectId: string, workflowName: string): Promise<boolean> {
|
|
413
|
+
const patterns = await this.load(projectId)
|
|
414
|
+
const workflow = patterns.workflows[workflowName]
|
|
415
|
+
if (!workflow) return false
|
|
416
|
+
|
|
417
|
+
workflow.userConfirmed = true
|
|
418
|
+
workflow.confidence = 'high'
|
|
419
|
+
workflow.lastSeen = getTimestamp()
|
|
420
|
+
await this.save(projectId)
|
|
421
|
+
return true
|
|
422
|
+
}
|
|
423
|
+
|
|
390
424
|
async getWorkflow(projectId: string, workflowName: string): Promise<Workflow | null> {
|
|
391
425
|
const patterns = await this.load(projectId)
|
|
392
426
|
const workflow = patterns.workflows[workflowName]
|
|
@@ -395,12 +429,39 @@ export class PatternStore extends CachedStore<Patterns> {
|
|
|
395
429
|
return workflow
|
|
396
430
|
}
|
|
397
431
|
|
|
398
|
-
async setPreference(
|
|
432
|
+
async setPreference(
|
|
433
|
+
projectId: string,
|
|
434
|
+
key: string,
|
|
435
|
+
value: Preference['value'],
|
|
436
|
+
options: { userConfirmed?: boolean } = {}
|
|
437
|
+
): Promise<void> {
|
|
399
438
|
const patterns = await this.load(projectId)
|
|
400
|
-
patterns.preferences[key]
|
|
439
|
+
const existing = patterns.preferences[key]
|
|
440
|
+
const observationCount = existing ? existing.observationCount + 1 : 1
|
|
441
|
+
const userConfirmed = options.userConfirmed || existing?.userConfirmed || false
|
|
442
|
+
|
|
443
|
+
patterns.preferences[key] = {
|
|
444
|
+
value,
|
|
445
|
+
updatedAt: getTimestamp(),
|
|
446
|
+
confidence: calculateConfidence(observationCount, userConfirmed),
|
|
447
|
+
observationCount,
|
|
448
|
+
userConfirmed,
|
|
449
|
+
}
|
|
401
450
|
await this.save(projectId)
|
|
402
451
|
}
|
|
403
452
|
|
|
453
|
+
async confirmPreference(projectId: string, key: string): Promise<boolean> {
|
|
454
|
+
const patterns = await this.load(projectId)
|
|
455
|
+
const pref = patterns.preferences[key]
|
|
456
|
+
if (!pref) return false
|
|
457
|
+
|
|
458
|
+
pref.userConfirmed = true
|
|
459
|
+
pref.confidence = 'high'
|
|
460
|
+
pref.updatedAt = getTimestamp()
|
|
461
|
+
await this.save(projectId)
|
|
462
|
+
return true
|
|
463
|
+
}
|
|
464
|
+
|
|
404
465
|
async getPreference(
|
|
405
466
|
projectId: string,
|
|
406
467
|
key: string,
|
|
@@ -857,14 +918,31 @@ export class MemorySystem {
|
|
|
857
918
|
return this._patternStore.getWorkflow(projectId, workflowName)
|
|
858
919
|
}
|
|
859
920
|
|
|
860
|
-
setPreference(
|
|
861
|
-
|
|
921
|
+
setPreference(
|
|
922
|
+
projectId: string,
|
|
923
|
+
key: string,
|
|
924
|
+
value: Preference['value'],
|
|
925
|
+
options?: { userConfirmed?: boolean }
|
|
926
|
+
): Promise<void> {
|
|
927
|
+
return this._patternStore.setPreference(projectId, key, value, options)
|
|
862
928
|
}
|
|
863
929
|
|
|
864
930
|
getPreference(projectId: string, key: string, defaultValue?: unknown): Promise<unknown> {
|
|
865
931
|
return this._patternStore.getPreference(projectId, key, defaultValue)
|
|
866
932
|
}
|
|
867
933
|
|
|
934
|
+
confirmPreference(projectId: string, key: string): Promise<boolean> {
|
|
935
|
+
return this._patternStore.confirmPreference(projectId, key)
|
|
936
|
+
}
|
|
937
|
+
|
|
938
|
+
confirmDecision(projectId: string, key: string): Promise<boolean> {
|
|
939
|
+
return this._patternStore.confirmDecision(projectId, key)
|
|
940
|
+
}
|
|
941
|
+
|
|
942
|
+
confirmWorkflow(projectId: string, workflowName: string): Promise<boolean> {
|
|
943
|
+
return this._patternStore.confirmWorkflow(projectId, workflowName)
|
|
944
|
+
}
|
|
945
|
+
|
|
868
946
|
getPatternsSummary(projectId: string) {
|
|
869
947
|
return this._patternStore.getPatternsSummary(projectId)
|
|
870
948
|
}
|
|
@@ -43,17 +43,73 @@ type Agent = PromptAgent
|
|
|
43
43
|
type Context = PromptContext
|
|
44
44
|
type State = PromptState
|
|
45
45
|
|
|
46
|
+
/**
|
|
47
|
+
* Cached template entry with TTL support
|
|
48
|
+
* @see PRJ-76
|
|
49
|
+
*/
|
|
50
|
+
interface CachedTemplate {
|
|
51
|
+
content: string
|
|
52
|
+
loadedAt: number
|
|
53
|
+
}
|
|
54
|
+
|
|
46
55
|
/**
|
|
47
56
|
* Builds prompts for Claude using templates, context, and learned patterns.
|
|
48
57
|
* Supports plan mode, think blocks, and quality checklists.
|
|
49
58
|
* Auto-injects unified state and performance insights.
|
|
59
|
+
*
|
|
60
|
+
* Uses lazy loading for templates with 60s TTL cache.
|
|
61
|
+
* @see PRJ-76
|
|
50
62
|
*/
|
|
51
63
|
class PromptBuilder {
|
|
52
64
|
private _checklistsCache: Record<string, string> | null = null
|
|
65
|
+
private _checklistsCacheTime: number = 0
|
|
53
66
|
private _checklistRoutingCache: string | null = null
|
|
67
|
+
private _checklistRoutingCacheTime: number = 0
|
|
54
68
|
private _currentContext: Context | null = null
|
|
55
69
|
private _stateCache: Map<string, { state: ProjectState; timestamp: number }> = new Map()
|
|
56
70
|
private _stateCacheTTL = 5000 // 5 seconds
|
|
71
|
+
private _templateCache: Map<string, CachedTemplate> = new Map()
|
|
72
|
+
private readonly TEMPLATE_CACHE_TTL_MS = 60_000 // 60 seconds
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Get a template with TTL caching.
|
|
76
|
+
* Returns cached content if within TTL, otherwise loads from disk.
|
|
77
|
+
* @see PRJ-76
|
|
78
|
+
*/
|
|
79
|
+
getTemplate(templatePath: string): string | null {
|
|
80
|
+
const cached = this._templateCache.get(templatePath)
|
|
81
|
+
const now = Date.now()
|
|
82
|
+
|
|
83
|
+
if (cached && now - cached.loadedAt < this.TEMPLATE_CACHE_TTL_MS) {
|
|
84
|
+
return cached.content
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
try {
|
|
88
|
+
if (fs.existsSync(templatePath)) {
|
|
89
|
+
const content = fs.readFileSync(templatePath, 'utf-8')
|
|
90
|
+
this._templateCache.set(templatePath, { content, loadedAt: now })
|
|
91
|
+
return content
|
|
92
|
+
}
|
|
93
|
+
} catch (error) {
|
|
94
|
+
if (!isNotFoundError(error)) {
|
|
95
|
+
console.error(`Template loading warning: ${(error as Error).message}`)
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
return null
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Clear the template cache (for testing or forced refresh)
|
|
104
|
+
* @see PRJ-76
|
|
105
|
+
*/
|
|
106
|
+
clearTemplateCache(): void {
|
|
107
|
+
this._templateCache.clear()
|
|
108
|
+
this._checklistsCache = null
|
|
109
|
+
this._checklistsCacheTime = 0
|
|
110
|
+
this._checklistRoutingCache = null
|
|
111
|
+
this._checklistRoutingCacheTime = 0
|
|
112
|
+
}
|
|
57
113
|
|
|
58
114
|
/**
|
|
59
115
|
* Reset context (for testing)
|
|
@@ -71,9 +127,16 @@ class PromptBuilder {
|
|
|
71
127
|
|
|
72
128
|
/**
|
|
73
129
|
* Load quality checklists from templates/checklists/
|
|
130
|
+
* Uses lazy loading with TTL cache.
|
|
131
|
+
* @see PRJ-76
|
|
74
132
|
*/
|
|
75
133
|
loadChecklists(): Record<string, string> {
|
|
76
|
-
|
|
134
|
+
const now = Date.now()
|
|
135
|
+
|
|
136
|
+
// Check if cache is still valid
|
|
137
|
+
if (this._checklistsCache && now - this._checklistsCacheTime < this.TEMPLATE_CACHE_TTL_MS) {
|
|
138
|
+
return this._checklistsCache
|
|
139
|
+
}
|
|
77
140
|
|
|
78
141
|
const checklistsDir = path.join(__dirname, '..', '..', 'templates', 'checklists')
|
|
79
142
|
const checklists: Record<string, string> = {}
|
|
@@ -83,8 +146,12 @@ class PromptBuilder {
|
|
|
83
146
|
const files = fs.readdirSync(checklistsDir).filter((f) => f.endsWith('.md'))
|
|
84
147
|
for (const file of files) {
|
|
85
148
|
const name = file.replace('.md', '')
|
|
86
|
-
const
|
|
87
|
-
|
|
149
|
+
const templatePath = path.join(checklistsDir, file)
|
|
150
|
+
// Use getTemplate for individual files to leverage per-file caching
|
|
151
|
+
const content = this.getTemplate(templatePath)
|
|
152
|
+
if (content) {
|
|
153
|
+
checklists[name] = content
|
|
154
|
+
}
|
|
88
155
|
}
|
|
89
156
|
}
|
|
90
157
|
} catch (error) {
|
|
@@ -95,6 +162,7 @@ class PromptBuilder {
|
|
|
95
162
|
}
|
|
96
163
|
|
|
97
164
|
this._checklistsCache = checklists
|
|
165
|
+
this._checklistsCacheTime = now
|
|
98
166
|
return checklists
|
|
99
167
|
}
|
|
100
168
|
|
|
@@ -215,9 +283,19 @@ class PromptBuilder {
|
|
|
215
283
|
|
|
216
284
|
/**
|
|
217
285
|
* Load checklist routing template for Claude to decide which checklists apply
|
|
286
|
+
* Uses lazy loading with TTL cache.
|
|
287
|
+
* @see PRJ-76
|
|
218
288
|
*/
|
|
219
289
|
loadChecklistRouting(): string | null {
|
|
220
|
-
|
|
290
|
+
const now = Date.now()
|
|
291
|
+
|
|
292
|
+
// Check if cache is still valid
|
|
293
|
+
if (
|
|
294
|
+
this._checklistRoutingCache &&
|
|
295
|
+
now - this._checklistRoutingCacheTime < this.TEMPLATE_CACHE_TTL_MS
|
|
296
|
+
) {
|
|
297
|
+
return this._checklistRoutingCache
|
|
298
|
+
}
|
|
221
299
|
|
|
222
300
|
const routingPath = path.join(
|
|
223
301
|
__dirname,
|
|
@@ -228,15 +306,11 @@ class PromptBuilder {
|
|
|
228
306
|
'checklist-routing.md'
|
|
229
307
|
)
|
|
230
308
|
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
// Silent fail - checklist routing is optional
|
|
237
|
-
if (!isNotFoundError(error)) {
|
|
238
|
-
console.error(`Checklist routing warning: ${(error as Error).message}`)
|
|
239
|
-
}
|
|
309
|
+
// Use getTemplate for consistent caching behavior
|
|
310
|
+
const content = this.getTemplate(routingPath)
|
|
311
|
+
if (content) {
|
|
312
|
+
this._checklistRoutingCache = content
|
|
313
|
+
this._checklistRoutingCacheTime = now
|
|
240
314
|
}
|
|
241
315
|
|
|
242
316
|
return this._checklistRoutingCache || null
|
package/core/types/memory.ts
CHANGED
|
@@ -43,6 +43,10 @@ export interface Memory {
|
|
|
43
43
|
userTriggered: boolean
|
|
44
44
|
createdAt: string
|
|
45
45
|
updatedAt: string
|
|
46
|
+
/** Confidence level for this memory (optional for backward compatibility) */
|
|
47
|
+
confidence?: ConfidenceLevel
|
|
48
|
+
/** Number of times this memory was reinforced */
|
|
49
|
+
observationCount?: number
|
|
46
50
|
}
|
|
47
51
|
|
|
48
52
|
/**
|
|
@@ -123,8 +127,10 @@ export interface Decision {
|
|
|
123
127
|
count: number
|
|
124
128
|
firstSeen: string
|
|
125
129
|
lastSeen: string
|
|
126
|
-
confidence:
|
|
130
|
+
confidence: ConfidenceLevel
|
|
127
131
|
contexts: string[]
|
|
132
|
+
/** Whether user explicitly confirmed this decision */
|
|
133
|
+
userConfirmed?: boolean
|
|
128
134
|
}
|
|
129
135
|
|
|
130
136
|
/**
|
|
@@ -141,14 +147,47 @@ export interface Workflow {
|
|
|
141
147
|
successRate?: number
|
|
142
148
|
/** Steps in the workflow */
|
|
143
149
|
steps?: string[]
|
|
150
|
+
/** Confidence level based on execution count */
|
|
151
|
+
confidence?: ConfidenceLevel
|
|
152
|
+
/** Whether user explicitly confirmed this workflow */
|
|
153
|
+
userConfirmed?: boolean
|
|
144
154
|
}
|
|
145
155
|
|
|
146
156
|
/**
|
|
147
|
-
*
|
|
157
|
+
* Confidence level for stored preferences and decisions.
|
|
158
|
+
* @see PRJ-104
|
|
159
|
+
*/
|
|
160
|
+
export type ConfidenceLevel = 'low' | 'medium' | 'high'
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Calculate confidence level from observation count.
|
|
164
|
+
* - low: 1-2 observations
|
|
165
|
+
* - medium: 3-5 observations
|
|
166
|
+
* - high: 6+ observations or explicit user confirmation
|
|
167
|
+
*/
|
|
168
|
+
export function calculateConfidence(
|
|
169
|
+
count: number,
|
|
170
|
+
userConfirmed: boolean = false
|
|
171
|
+
): ConfidenceLevel {
|
|
172
|
+
if (userConfirmed) return 'high'
|
|
173
|
+
if (count >= 6) return 'high'
|
|
174
|
+
if (count >= 3) return 'medium'
|
|
175
|
+
return 'low'
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* A user preference value with confidence scoring.
|
|
180
|
+
* @see PRJ-104
|
|
148
181
|
*/
|
|
149
182
|
export interface Preference {
|
|
150
183
|
value: string | number | boolean
|
|
151
184
|
updatedAt: string
|
|
185
|
+
/** Confidence level based on observations */
|
|
186
|
+
confidence: ConfidenceLevel
|
|
187
|
+
/** Number of times this preference was observed */
|
|
188
|
+
observationCount: number
|
|
189
|
+
/** Whether user explicitly confirmed this preference */
|
|
190
|
+
userConfirmed: boolean
|
|
152
191
|
}
|
|
153
192
|
|
|
154
193
|
/**
|
package/dist/bin/prjct.mjs
CHANGED
|
@@ -8568,6 +8568,12 @@ var init_jsonl_helper = __esm({
|
|
|
8568
8568
|
});
|
|
8569
8569
|
|
|
8570
8570
|
// core/types/memory.ts
|
|
8571
|
+
function calculateConfidence(count, userConfirmed = false) {
|
|
8572
|
+
if (userConfirmed) return "high";
|
|
8573
|
+
if (count >= 6) return "high";
|
|
8574
|
+
if (count >= 3) return "medium";
|
|
8575
|
+
return "low";
|
|
8576
|
+
}
|
|
8571
8577
|
var MEMORY_TAGS;
|
|
8572
8578
|
var init_memory = __esm({
|
|
8573
8579
|
"core/types/memory.ts"() {
|
|
@@ -8591,6 +8597,7 @@ var init_memory = __esm({
|
|
|
8591
8597
|
CONFIRMATION_LEVEL: "confirmation_level",
|
|
8592
8598
|
AGENT_PREFERENCE: "agent_preference"
|
|
8593
8599
|
};
|
|
8600
|
+
__name(calculateConfidence, "calculateConfidence");
|
|
8594
8601
|
}
|
|
8595
8602
|
});
|
|
8596
8603
|
|
|
@@ -8782,7 +8789,7 @@ var init_memory_system = __esm({
|
|
|
8782
8789
|
async savePatterns(projectId) {
|
|
8783
8790
|
return this.save(projectId);
|
|
8784
8791
|
}
|
|
8785
|
-
async recordDecision(projectId, key, value, context2 = "") {
|
|
8792
|
+
async recordDecision(projectId, key, value, context2 = "", options = {}) {
|
|
8786
8793
|
const patterns = await this.load(projectId);
|
|
8787
8794
|
const now = getTimestamp();
|
|
8788
8795
|
if (!patterns.decisions[key]) {
|
|
@@ -8791,8 +8798,9 @@ var init_memory_system = __esm({
|
|
|
8791
8798
|
count: 1,
|
|
8792
8799
|
firstSeen: now,
|
|
8793
8800
|
lastSeen: now,
|
|
8794
|
-
confidence: "low",
|
|
8795
|
-
contexts: [context2].filter(Boolean)
|
|
8801
|
+
confidence: options.userConfirmed ? "high" : "low",
|
|
8802
|
+
contexts: [context2].filter(Boolean),
|
|
8803
|
+
userConfirmed: options.userConfirmed || false
|
|
8796
8804
|
};
|
|
8797
8805
|
} else {
|
|
8798
8806
|
const decision = patterns.decisions[key];
|
|
@@ -8802,20 +8810,30 @@ var init_memory_system = __esm({
|
|
|
8802
8810
|
if (context2 && !decision.contexts.includes(context2)) {
|
|
8803
8811
|
decision.contexts.push(context2);
|
|
8804
8812
|
}
|
|
8805
|
-
if (
|
|
8806
|
-
decision.
|
|
8807
|
-
} else if (decision.count >= 3) {
|
|
8808
|
-
decision.confidence = "medium";
|
|
8813
|
+
if (options.userConfirmed) {
|
|
8814
|
+
decision.userConfirmed = true;
|
|
8809
8815
|
}
|
|
8816
|
+
decision.confidence = calculateConfidence(decision.count, decision.userConfirmed);
|
|
8810
8817
|
} else {
|
|
8811
8818
|
decision.value = value;
|
|
8812
8819
|
decision.count = 1;
|
|
8813
8820
|
decision.lastSeen = now;
|
|
8814
|
-
decision.
|
|
8821
|
+
decision.userConfirmed = options.userConfirmed || false;
|
|
8822
|
+
decision.confidence = options.userConfirmed ? "high" : "low";
|
|
8815
8823
|
}
|
|
8816
8824
|
}
|
|
8817
8825
|
await this.save(projectId);
|
|
8818
8826
|
}
|
|
8827
|
+
async confirmDecision(projectId, key) {
|
|
8828
|
+
const patterns = await this.load(projectId);
|
|
8829
|
+
const decision = patterns.decisions[key];
|
|
8830
|
+
if (!decision) return false;
|
|
8831
|
+
decision.userConfirmed = true;
|
|
8832
|
+
decision.confidence = "high";
|
|
8833
|
+
decision.lastSeen = getTimestamp();
|
|
8834
|
+
await this.save(projectId);
|
|
8835
|
+
return true;
|
|
8836
|
+
}
|
|
8819
8837
|
async getDecision(projectId, key) {
|
|
8820
8838
|
const patterns = await this.load(projectId);
|
|
8821
8839
|
const decision = patterns.decisions[key];
|
|
@@ -8835,24 +8853,57 @@ var init_memory_system = __esm({
|
|
|
8835
8853
|
...pattern,
|
|
8836
8854
|
count: 1,
|
|
8837
8855
|
firstSeen: now,
|
|
8838
|
-
lastSeen: now
|
|
8856
|
+
lastSeen: now,
|
|
8857
|
+
confidence: "low",
|
|
8858
|
+
userConfirmed: false
|
|
8839
8859
|
};
|
|
8840
8860
|
} else {
|
|
8841
|
-
patterns.workflows[workflowName]
|
|
8842
|
-
|
|
8861
|
+
const workflow2 = patterns.workflows[workflowName];
|
|
8862
|
+
workflow2.count++;
|
|
8863
|
+
workflow2.lastSeen = now;
|
|
8864
|
+
workflow2.confidence = calculateConfidence(workflow2.count, workflow2.userConfirmed);
|
|
8843
8865
|
}
|
|
8844
8866
|
await this.save(projectId);
|
|
8845
8867
|
}
|
|
8868
|
+
async confirmWorkflow(projectId, workflowName) {
|
|
8869
|
+
const patterns = await this.load(projectId);
|
|
8870
|
+
const workflow2 = patterns.workflows[workflowName];
|
|
8871
|
+
if (!workflow2) return false;
|
|
8872
|
+
workflow2.userConfirmed = true;
|
|
8873
|
+
workflow2.confidence = "high";
|
|
8874
|
+
workflow2.lastSeen = getTimestamp();
|
|
8875
|
+
await this.save(projectId);
|
|
8876
|
+
return true;
|
|
8877
|
+
}
|
|
8846
8878
|
async getWorkflow(projectId, workflowName) {
|
|
8847
8879
|
const patterns = await this.load(projectId);
|
|
8848
8880
|
const workflow2 = patterns.workflows[workflowName];
|
|
8849
8881
|
if (!workflow2 || workflow2.count < 3) return null;
|
|
8850
8882
|
return workflow2;
|
|
8851
8883
|
}
|
|
8852
|
-
async setPreference(projectId, key, value) {
|
|
8884
|
+
async setPreference(projectId, key, value, options = {}) {
|
|
8885
|
+
const patterns = await this.load(projectId);
|
|
8886
|
+
const existing = patterns.preferences[key];
|
|
8887
|
+
const observationCount = existing ? existing.observationCount + 1 : 1;
|
|
8888
|
+
const userConfirmed = options.userConfirmed || existing?.userConfirmed || false;
|
|
8889
|
+
patterns.preferences[key] = {
|
|
8890
|
+
value,
|
|
8891
|
+
updatedAt: getTimestamp(),
|
|
8892
|
+
confidence: calculateConfidence(observationCount, userConfirmed),
|
|
8893
|
+
observationCount,
|
|
8894
|
+
userConfirmed
|
|
8895
|
+
};
|
|
8896
|
+
await this.save(projectId);
|
|
8897
|
+
}
|
|
8898
|
+
async confirmPreference(projectId, key) {
|
|
8853
8899
|
const patterns = await this.load(projectId);
|
|
8854
|
-
patterns.preferences[key]
|
|
8900
|
+
const pref = patterns.preferences[key];
|
|
8901
|
+
if (!pref) return false;
|
|
8902
|
+
pref.userConfirmed = true;
|
|
8903
|
+
pref.confidence = "high";
|
|
8904
|
+
pref.updatedAt = getTimestamp();
|
|
8855
8905
|
await this.save(projectId);
|
|
8906
|
+
return true;
|
|
8856
8907
|
}
|
|
8857
8908
|
async getPreference(projectId, key, defaultValue = null) {
|
|
8858
8909
|
const patterns = await this.load(projectId);
|
|
@@ -9169,12 +9220,21 @@ Context: ${context2}` : ""}`,
|
|
|
9169
9220
|
getWorkflow(projectId, workflowName) {
|
|
9170
9221
|
return this._patternStore.getWorkflow(projectId, workflowName);
|
|
9171
9222
|
}
|
|
9172
|
-
setPreference(projectId, key, value) {
|
|
9173
|
-
return this._patternStore.setPreference(projectId, key, value);
|
|
9223
|
+
setPreference(projectId, key, value, options) {
|
|
9224
|
+
return this._patternStore.setPreference(projectId, key, value, options);
|
|
9174
9225
|
}
|
|
9175
9226
|
getPreference(projectId, key, defaultValue) {
|
|
9176
9227
|
return this._patternStore.getPreference(projectId, key, defaultValue);
|
|
9177
9228
|
}
|
|
9229
|
+
confirmPreference(projectId, key) {
|
|
9230
|
+
return this._patternStore.confirmPreference(projectId, key);
|
|
9231
|
+
}
|
|
9232
|
+
confirmDecision(projectId, key) {
|
|
9233
|
+
return this._patternStore.confirmDecision(projectId, key);
|
|
9234
|
+
}
|
|
9235
|
+
confirmWorkflow(projectId, workflowName) {
|
|
9236
|
+
return this._patternStore.confirmWorkflow(projectId, workflowName);
|
|
9237
|
+
}
|
|
9178
9238
|
getPatternsSummary(projectId) {
|
|
9179
9239
|
return this._patternStore.getPatternsSummary(projectId);
|
|
9180
9240
|
}
|
|
@@ -12272,11 +12332,51 @@ var init_prompt_builder = __esm({
|
|
|
12272
12332
|
__name(this, "PromptBuilder");
|
|
12273
12333
|
}
|
|
12274
12334
|
_checklistsCache = null;
|
|
12335
|
+
_checklistsCacheTime = 0;
|
|
12275
12336
|
_checklistRoutingCache = null;
|
|
12337
|
+
_checklistRoutingCacheTime = 0;
|
|
12276
12338
|
_currentContext = null;
|
|
12277
12339
|
_stateCache = /* @__PURE__ */ new Map();
|
|
12278
12340
|
_stateCacheTTL = 5e3;
|
|
12279
12341
|
// 5 seconds
|
|
12342
|
+
_templateCache = /* @__PURE__ */ new Map();
|
|
12343
|
+
TEMPLATE_CACHE_TTL_MS = 6e4;
|
|
12344
|
+
// 60 seconds
|
|
12345
|
+
/**
|
|
12346
|
+
* Get a template with TTL caching.
|
|
12347
|
+
* Returns cached content if within TTL, otherwise loads from disk.
|
|
12348
|
+
* @see PRJ-76
|
|
12349
|
+
*/
|
|
12350
|
+
getTemplate(templatePath) {
|
|
12351
|
+
const cached = this._templateCache.get(templatePath);
|
|
12352
|
+
const now = Date.now();
|
|
12353
|
+
if (cached && now - cached.loadedAt < this.TEMPLATE_CACHE_TTL_MS) {
|
|
12354
|
+
return cached.content;
|
|
12355
|
+
}
|
|
12356
|
+
try {
|
|
12357
|
+
if (fs25.existsSync(templatePath)) {
|
|
12358
|
+
const content = fs25.readFileSync(templatePath, "utf-8");
|
|
12359
|
+
this._templateCache.set(templatePath, { content, loadedAt: now });
|
|
12360
|
+
return content;
|
|
12361
|
+
}
|
|
12362
|
+
} catch (error) {
|
|
12363
|
+
if (!isNotFoundError(error)) {
|
|
12364
|
+
console.error(`Template loading warning: ${error.message}`);
|
|
12365
|
+
}
|
|
12366
|
+
}
|
|
12367
|
+
return null;
|
|
12368
|
+
}
|
|
12369
|
+
/**
|
|
12370
|
+
* Clear the template cache (for testing or forced refresh)
|
|
12371
|
+
* @see PRJ-76
|
|
12372
|
+
*/
|
|
12373
|
+
clearTemplateCache() {
|
|
12374
|
+
this._templateCache.clear();
|
|
12375
|
+
this._checklistsCache = null;
|
|
12376
|
+
this._checklistsCacheTime = 0;
|
|
12377
|
+
this._checklistRoutingCache = null;
|
|
12378
|
+
this._checklistRoutingCacheTime = 0;
|
|
12379
|
+
}
|
|
12280
12380
|
/**
|
|
12281
12381
|
* Reset context (for testing)
|
|
12282
12382
|
*/
|
|
@@ -12291,9 +12391,14 @@ var init_prompt_builder = __esm({
|
|
|
12291
12391
|
}
|
|
12292
12392
|
/**
|
|
12293
12393
|
* Load quality checklists from templates/checklists/
|
|
12394
|
+
* Uses lazy loading with TTL cache.
|
|
12395
|
+
* @see PRJ-76
|
|
12294
12396
|
*/
|
|
12295
12397
|
loadChecklists() {
|
|
12296
|
-
|
|
12398
|
+
const now = Date.now();
|
|
12399
|
+
if (this._checklistsCache && now - this._checklistsCacheTime < this.TEMPLATE_CACHE_TTL_MS) {
|
|
12400
|
+
return this._checklistsCache;
|
|
12401
|
+
}
|
|
12297
12402
|
const checklistsDir = path25.join(__dirname, "..", "..", "templates", "checklists");
|
|
12298
12403
|
const checklists = {};
|
|
12299
12404
|
try {
|
|
@@ -12301,8 +12406,11 @@ var init_prompt_builder = __esm({
|
|
|
12301
12406
|
const files = fs25.readdirSync(checklistsDir).filter((f) => f.endsWith(".md"));
|
|
12302
12407
|
for (const file of files) {
|
|
12303
12408
|
const name = file.replace(".md", "");
|
|
12304
|
-
const
|
|
12305
|
-
|
|
12409
|
+
const templatePath = path25.join(checklistsDir, file);
|
|
12410
|
+
const content = this.getTemplate(templatePath);
|
|
12411
|
+
if (content) {
|
|
12412
|
+
checklists[name] = content;
|
|
12413
|
+
}
|
|
12306
12414
|
}
|
|
12307
12415
|
}
|
|
12308
12416
|
} catch (error) {
|
|
@@ -12311,6 +12419,7 @@ var init_prompt_builder = __esm({
|
|
|
12311
12419
|
}
|
|
12312
12420
|
}
|
|
12313
12421
|
this._checklistsCache = checklists;
|
|
12422
|
+
this._checklistsCacheTime = now;
|
|
12314
12423
|
return checklists;
|
|
12315
12424
|
}
|
|
12316
12425
|
/**
|
|
@@ -12408,9 +12517,14 @@ var init_prompt_builder = __esm({
|
|
|
12408
12517
|
}
|
|
12409
12518
|
/**
|
|
12410
12519
|
* Load checklist routing template for Claude to decide which checklists apply
|
|
12520
|
+
* Uses lazy loading with TTL cache.
|
|
12521
|
+
* @see PRJ-76
|
|
12411
12522
|
*/
|
|
12412
12523
|
loadChecklistRouting() {
|
|
12413
|
-
|
|
12524
|
+
const now = Date.now();
|
|
12525
|
+
if (this._checklistRoutingCache && now - this._checklistRoutingCacheTime < this.TEMPLATE_CACHE_TTL_MS) {
|
|
12526
|
+
return this._checklistRoutingCache;
|
|
12527
|
+
}
|
|
12414
12528
|
const routingPath = path25.join(
|
|
12415
12529
|
__dirname,
|
|
12416
12530
|
"..",
|
|
@@ -12419,14 +12533,10 @@ var init_prompt_builder = __esm({
|
|
|
12419
12533
|
"agentic",
|
|
12420
12534
|
"checklist-routing.md"
|
|
12421
12535
|
);
|
|
12422
|
-
|
|
12423
|
-
|
|
12424
|
-
|
|
12425
|
-
|
|
12426
|
-
} catch (error) {
|
|
12427
|
-
if (!isNotFoundError(error)) {
|
|
12428
|
-
console.error(`Checklist routing warning: ${error.message}`);
|
|
12429
|
-
}
|
|
12536
|
+
const content = this.getTemplate(routingPath);
|
|
12537
|
+
if (content) {
|
|
12538
|
+
this._checklistRoutingCache = content;
|
|
12539
|
+
this._checklistRoutingCacheTime = now;
|
|
12430
12540
|
}
|
|
12431
12541
|
return this._checklistRoutingCache || null;
|
|
12432
12542
|
}
|
|
@@ -24604,7 +24714,7 @@ var require_package = __commonJS({
|
|
|
24604
24714
|
"package.json"(exports, module) {
|
|
24605
24715
|
module.exports = {
|
|
24606
24716
|
name: "prjct-cli",
|
|
24607
|
-
version: "0.
|
|
24717
|
+
version: "0.53.0",
|
|
24608
24718
|
description: "Context layer for AI agents. Project context for Claude Code, Gemini CLI, and more.",
|
|
24609
24719
|
main: "core/index.ts",
|
|
24610
24720
|
bin: {
|