prjct-cli 0.52.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 +26 -0
- package/core/agentic/prompt-builder.ts +87 -13
- package/dist/bin/prjct.mjs +63 -13
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,31 @@
|
|
|
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
|
+
|
|
3
29
|
## [0.52.0] - 2026-01-30
|
|
4
30
|
|
|
5
31
|
### Features
|
|
@@ -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/dist/bin/prjct.mjs
CHANGED
|
@@ -12332,11 +12332,51 @@ var init_prompt_builder = __esm({
|
|
|
12332
12332
|
__name(this, "PromptBuilder");
|
|
12333
12333
|
}
|
|
12334
12334
|
_checklistsCache = null;
|
|
12335
|
+
_checklistsCacheTime = 0;
|
|
12335
12336
|
_checklistRoutingCache = null;
|
|
12337
|
+
_checklistRoutingCacheTime = 0;
|
|
12336
12338
|
_currentContext = null;
|
|
12337
12339
|
_stateCache = /* @__PURE__ */ new Map();
|
|
12338
12340
|
_stateCacheTTL = 5e3;
|
|
12339
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
|
+
}
|
|
12340
12380
|
/**
|
|
12341
12381
|
* Reset context (for testing)
|
|
12342
12382
|
*/
|
|
@@ -12351,9 +12391,14 @@ var init_prompt_builder = __esm({
|
|
|
12351
12391
|
}
|
|
12352
12392
|
/**
|
|
12353
12393
|
* Load quality checklists from templates/checklists/
|
|
12394
|
+
* Uses lazy loading with TTL cache.
|
|
12395
|
+
* @see PRJ-76
|
|
12354
12396
|
*/
|
|
12355
12397
|
loadChecklists() {
|
|
12356
|
-
|
|
12398
|
+
const now = Date.now();
|
|
12399
|
+
if (this._checklistsCache && now - this._checklistsCacheTime < this.TEMPLATE_CACHE_TTL_MS) {
|
|
12400
|
+
return this._checklistsCache;
|
|
12401
|
+
}
|
|
12357
12402
|
const checklistsDir = path25.join(__dirname, "..", "..", "templates", "checklists");
|
|
12358
12403
|
const checklists = {};
|
|
12359
12404
|
try {
|
|
@@ -12361,8 +12406,11 @@ var init_prompt_builder = __esm({
|
|
|
12361
12406
|
const files = fs25.readdirSync(checklistsDir).filter((f) => f.endsWith(".md"));
|
|
12362
12407
|
for (const file of files) {
|
|
12363
12408
|
const name = file.replace(".md", "");
|
|
12364
|
-
const
|
|
12365
|
-
|
|
12409
|
+
const templatePath = path25.join(checklistsDir, file);
|
|
12410
|
+
const content = this.getTemplate(templatePath);
|
|
12411
|
+
if (content) {
|
|
12412
|
+
checklists[name] = content;
|
|
12413
|
+
}
|
|
12366
12414
|
}
|
|
12367
12415
|
}
|
|
12368
12416
|
} catch (error) {
|
|
@@ -12371,6 +12419,7 @@ var init_prompt_builder = __esm({
|
|
|
12371
12419
|
}
|
|
12372
12420
|
}
|
|
12373
12421
|
this._checklistsCache = checklists;
|
|
12422
|
+
this._checklistsCacheTime = now;
|
|
12374
12423
|
return checklists;
|
|
12375
12424
|
}
|
|
12376
12425
|
/**
|
|
@@ -12468,9 +12517,14 @@ var init_prompt_builder = __esm({
|
|
|
12468
12517
|
}
|
|
12469
12518
|
/**
|
|
12470
12519
|
* Load checklist routing template for Claude to decide which checklists apply
|
|
12520
|
+
* Uses lazy loading with TTL cache.
|
|
12521
|
+
* @see PRJ-76
|
|
12471
12522
|
*/
|
|
12472
12523
|
loadChecklistRouting() {
|
|
12473
|
-
|
|
12524
|
+
const now = Date.now();
|
|
12525
|
+
if (this._checklistRoutingCache && now - this._checklistRoutingCacheTime < this.TEMPLATE_CACHE_TTL_MS) {
|
|
12526
|
+
return this._checklistRoutingCache;
|
|
12527
|
+
}
|
|
12474
12528
|
const routingPath = path25.join(
|
|
12475
12529
|
__dirname,
|
|
12476
12530
|
"..",
|
|
@@ -12479,14 +12533,10 @@ var init_prompt_builder = __esm({
|
|
|
12479
12533
|
"agentic",
|
|
12480
12534
|
"checklist-routing.md"
|
|
12481
12535
|
);
|
|
12482
|
-
|
|
12483
|
-
|
|
12484
|
-
|
|
12485
|
-
|
|
12486
|
-
} catch (error) {
|
|
12487
|
-
if (!isNotFoundError(error)) {
|
|
12488
|
-
console.error(`Checklist routing warning: ${error.message}`);
|
|
12489
|
-
}
|
|
12536
|
+
const content = this.getTemplate(routingPath);
|
|
12537
|
+
if (content) {
|
|
12538
|
+
this._checklistRoutingCache = content;
|
|
12539
|
+
this._checklistRoutingCacheTime = now;
|
|
12490
12540
|
}
|
|
12491
12541
|
return this._checklistRoutingCache || null;
|
|
12492
12542
|
}
|
|
@@ -24664,7 +24714,7 @@ var require_package = __commonJS({
|
|
|
24664
24714
|
"package.json"(exports, module) {
|
|
24665
24715
|
module.exports = {
|
|
24666
24716
|
name: "prjct-cli",
|
|
24667
|
-
version: "0.
|
|
24717
|
+
version: "0.53.0",
|
|
24668
24718
|
description: "Context layer for AI agents. Project context for Claude Code, Gemini CLI, and more.",
|
|
24669
24719
|
main: "core/index.ts",
|
|
24670
24720
|
bin: {
|