prjct-cli 1.7.2 → 1.7.4
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 +61 -0
- package/core/__tests__/agentic/cache-eviction.test.ts +294 -0
- package/core/__tests__/agentic/injection-validator.test.ts +255 -0
- package/core/agentic/context-builder.ts +31 -49
- package/core/agentic/injection-validator.ts +192 -0
- package/core/agentic/memory-system.ts +62 -0
- package/core/agentic/prompt-builder.ts +35 -30
- package/core/session/session-log-manager.ts +11 -8
- package/dist/bin/prjct.mjs +202 -76
- package/package.json +1 -1
package/dist/bin/prjct.mjs
CHANGED
|
@@ -7572,28 +7572,16 @@ var init_context_builder = __esm({
|
|
|
7572
7572
|
init_config_manager();
|
|
7573
7573
|
init_path_manager();
|
|
7574
7574
|
init_fs();
|
|
7575
|
+
init_cache();
|
|
7575
7576
|
ContextBuilder = class {
|
|
7576
7577
|
static {
|
|
7577
7578
|
__name(this, "ContextBuilder");
|
|
7578
7579
|
}
|
|
7579
7580
|
_cache;
|
|
7580
|
-
|
|
7581
|
-
_lastCacheTime;
|
|
7582
|
-
_mtimes;
|
|
7581
|
+
_currentProjectId;
|
|
7583
7582
|
constructor() {
|
|
7584
|
-
this._cache =
|
|
7585
|
-
this.
|
|
7586
|
-
this._lastCacheTime = null;
|
|
7587
|
-
this._mtimes = /* @__PURE__ */ new Map();
|
|
7588
|
-
}
|
|
7589
|
-
/**
|
|
7590
|
-
* Clear cache if stale or force clear
|
|
7591
|
-
*/
|
|
7592
|
-
_clearCacheIfStale(force = false) {
|
|
7593
|
-
if (force || !this._lastCacheTime || Date.now() - this._lastCacheTime > this._cacheTimeout) {
|
|
7594
|
-
this._cache.clear();
|
|
7595
|
-
this._lastCacheTime = Date.now();
|
|
7596
|
-
}
|
|
7583
|
+
this._cache = new TTLCache({ ttl: 5e3, maxSize: 200 });
|
|
7584
|
+
this._currentProjectId = null;
|
|
7597
7585
|
}
|
|
7598
7586
|
/**
|
|
7599
7587
|
* Build full project context for Claude
|
|
@@ -7601,6 +7589,10 @@ var init_context_builder = __esm({
|
|
|
7601
7589
|
async build(projectPath, commandParams = {}) {
|
|
7602
7590
|
const projectId = await config_manager_default.getProjectId(projectPath);
|
|
7603
7591
|
const globalPath = path_manager_default.getGlobalProjectPath(projectId);
|
|
7592
|
+
if (this._currentProjectId !== null && this._currentProjectId !== projectId) {
|
|
7593
|
+
this._cache.clear();
|
|
7594
|
+
}
|
|
7595
|
+
this._currentProjectId = projectId;
|
|
7604
7596
|
return {
|
|
7605
7597
|
// Project identification
|
|
7606
7598
|
projectId,
|
|
@@ -7636,23 +7628,20 @@ var init_context_builder = __esm({
|
|
|
7636
7628
|
* Uses Promise.all() for 40-60% faster file I/O
|
|
7637
7629
|
*/
|
|
7638
7630
|
async loadState(context2, onlyKeys = null) {
|
|
7639
|
-
this._clearCacheIfStale();
|
|
7640
7631
|
const state = {};
|
|
7641
7632
|
const entries = Object.entries(context2.paths);
|
|
7642
7633
|
const filteredEntries = onlyKeys ? entries.filter(([key]) => onlyKeys.includes(key)) : entries;
|
|
7643
7634
|
for (const [, filePath] of filteredEntries) {
|
|
7644
|
-
|
|
7635
|
+
const cachedEntry = this._cache.get(filePath);
|
|
7636
|
+
if (cachedEntry !== null) {
|
|
7645
7637
|
try {
|
|
7646
7638
|
const stat = await fs21.stat(filePath);
|
|
7647
|
-
|
|
7648
|
-
if (!cachedMtime || stat.mtimeMs > cachedMtime) {
|
|
7639
|
+
if (!cachedEntry.mtime || stat.mtimeMs > cachedEntry.mtime) {
|
|
7649
7640
|
this._cache.delete(filePath);
|
|
7650
|
-
this._mtimes.delete(filePath);
|
|
7651
7641
|
}
|
|
7652
7642
|
} catch (error) {
|
|
7653
7643
|
if (isNotFoundError(error)) {
|
|
7654
7644
|
this._cache.delete(filePath);
|
|
7655
|
-
this._mtimes.delete(filePath);
|
|
7656
7645
|
} else {
|
|
7657
7646
|
throw error;
|
|
7658
7647
|
}
|
|
@@ -7661,8 +7650,9 @@ var init_context_builder = __esm({
|
|
|
7661
7650
|
}
|
|
7662
7651
|
const uncachedEntries = [];
|
|
7663
7652
|
for (const [key, filePath] of filteredEntries) {
|
|
7664
|
-
|
|
7665
|
-
|
|
7653
|
+
const cachedEntry = this._cache.get(filePath);
|
|
7654
|
+
if (cachedEntry !== null) {
|
|
7655
|
+
state[key] = cachedEntry.content;
|
|
7666
7656
|
} else {
|
|
7667
7657
|
uncachedEntries.push([key, filePath]);
|
|
7668
7658
|
}
|
|
@@ -7685,10 +7675,7 @@ var init_context_builder = __esm({
|
|
|
7685
7675
|
const results = await Promise.all(readPromises);
|
|
7686
7676
|
for (const { key, filePath, content, mtime } of results) {
|
|
7687
7677
|
state[key] = content;
|
|
7688
|
-
this._cache.set(filePath, content);
|
|
7689
|
-
if (mtime) {
|
|
7690
|
-
this._mtimes.set(filePath, mtime);
|
|
7691
|
-
}
|
|
7678
|
+
this._cache.set(filePath, { content, mtime });
|
|
7692
7679
|
}
|
|
7693
7680
|
}
|
|
7694
7681
|
return state;
|
|
@@ -7733,12 +7720,12 @@ var init_context_builder = __esm({
|
|
|
7733
7720
|
* Utility for custom file sets
|
|
7734
7721
|
*/
|
|
7735
7722
|
async batchRead(filePaths) {
|
|
7736
|
-
this._clearCacheIfStale();
|
|
7737
7723
|
const results = /* @__PURE__ */ new Map();
|
|
7738
7724
|
const uncachedPaths = [];
|
|
7739
7725
|
for (const filePath of filePaths) {
|
|
7740
|
-
|
|
7741
|
-
|
|
7726
|
+
const cachedEntry = this._cache.get(filePath);
|
|
7727
|
+
if (cachedEntry !== null) {
|
|
7728
|
+
results.set(filePath, cachedEntry.content);
|
|
7742
7729
|
} else {
|
|
7743
7730
|
uncachedPaths.push(filePath);
|
|
7744
7731
|
}
|
|
@@ -7758,7 +7745,7 @@ var init_context_builder = __esm({
|
|
|
7758
7745
|
const readResults = await Promise.all(readPromises);
|
|
7759
7746
|
for (const { filePath, content } of readResults) {
|
|
7760
7747
|
results.set(filePath, content);
|
|
7761
|
-
this._cache.set(filePath, content);
|
|
7748
|
+
this._cache.set(filePath, { content, mtime: null });
|
|
7762
7749
|
}
|
|
7763
7750
|
}
|
|
7764
7751
|
return results;
|
|
@@ -7773,7 +7760,8 @@ var init_context_builder = __esm({
|
|
|
7773
7760
|
* Force clear entire cache
|
|
7774
7761
|
*/
|
|
7775
7762
|
clearCache() {
|
|
7776
|
-
this.
|
|
7763
|
+
this._cache.clear();
|
|
7764
|
+
this._currentProjectId = null;
|
|
7777
7765
|
}
|
|
7778
7766
|
/**
|
|
7779
7767
|
* Check file existence
|
|
@@ -7793,11 +7781,7 @@ var init_context_builder = __esm({
|
|
|
7793
7781
|
* Get cache stats (for debugging/metrics)
|
|
7794
7782
|
*/
|
|
7795
7783
|
getCacheStats() {
|
|
7796
|
-
return
|
|
7797
|
-
size: this._cache.size,
|
|
7798
|
-
lastRefresh: this._lastCacheTime,
|
|
7799
|
-
timeout: this._cacheTimeout
|
|
7800
|
-
};
|
|
7784
|
+
return this._cache.stats();
|
|
7801
7785
|
}
|
|
7802
7786
|
};
|
|
7803
7787
|
contextBuilder = new ContextBuilder();
|
|
@@ -10094,10 +10078,12 @@ var init_memory_system = __esm({
|
|
|
10094
10078
|
return getLastJsonLines(sessionPath, limit);
|
|
10095
10079
|
}
|
|
10096
10080
|
};
|
|
10097
|
-
PatternStore = class extends CachedStore {
|
|
10081
|
+
PatternStore = class _PatternStore extends CachedStore {
|
|
10098
10082
|
static {
|
|
10099
10083
|
__name(this, "PatternStore");
|
|
10100
10084
|
}
|
|
10085
|
+
static MAX_CONTEXTS = 20;
|
|
10086
|
+
static ARCHIVE_AGE_DAYS = 90;
|
|
10101
10087
|
getFilename() {
|
|
10102
10088
|
return "patterns.json";
|
|
10103
10089
|
}
|
|
@@ -10110,6 +10096,13 @@ var init_memory_system = __esm({
|
|
|
10110
10096
|
counters: {}
|
|
10111
10097
|
};
|
|
10112
10098
|
}
|
|
10099
|
+
afterLoad(patterns) {
|
|
10100
|
+
for (const decision of Object.values(patterns.decisions)) {
|
|
10101
|
+
if (decision.contexts.length > _PatternStore.MAX_CONTEXTS) {
|
|
10102
|
+
decision.contexts = decision.contexts.slice(-_PatternStore.MAX_CONTEXTS);
|
|
10103
|
+
}
|
|
10104
|
+
}
|
|
10105
|
+
}
|
|
10113
10106
|
// Convenience alias for backward compatibility
|
|
10114
10107
|
async loadPatterns(projectId) {
|
|
10115
10108
|
return this.load(projectId);
|
|
@@ -10137,6 +10130,9 @@ var init_memory_system = __esm({
|
|
|
10137
10130
|
decision.lastSeen = now;
|
|
10138
10131
|
if (context2 && !decision.contexts.includes(context2)) {
|
|
10139
10132
|
decision.contexts.push(context2);
|
|
10133
|
+
if (decision.contexts.length > _PatternStore.MAX_CONTEXTS) {
|
|
10134
|
+
decision.contexts = decision.contexts.slice(-_PatternStore.MAX_CONTEXTS);
|
|
10135
|
+
}
|
|
10140
10136
|
}
|
|
10141
10137
|
if (options.userConfirmed) {
|
|
10142
10138
|
decision.userConfirmed = true;
|
|
@@ -10246,6 +10242,39 @@ var init_memory_system = __esm({
|
|
|
10246
10242
|
preferences: Object.keys(patterns.preferences).length
|
|
10247
10243
|
};
|
|
10248
10244
|
}
|
|
10245
|
+
_getArchivePath(projectId) {
|
|
10246
|
+
const basePath = path22.join(path_manager_default.getGlobalProjectPath(projectId), "memory");
|
|
10247
|
+
return path22.join(basePath, "patterns-archive.json");
|
|
10248
|
+
}
|
|
10249
|
+
async archiveStaleDecisions(projectId) {
|
|
10250
|
+
const patterns = await this.load(projectId);
|
|
10251
|
+
const now = Date.now();
|
|
10252
|
+
const cutoff = _PatternStore.ARCHIVE_AGE_DAYS * 24 * 60 * 60 * 1e3;
|
|
10253
|
+
const staleKeys = [];
|
|
10254
|
+
for (const [key, decision] of Object.entries(patterns.decisions)) {
|
|
10255
|
+
const lastSeenMs = new Date(decision.lastSeen).getTime();
|
|
10256
|
+
if (now - lastSeenMs > cutoff) {
|
|
10257
|
+
staleKeys.push(key);
|
|
10258
|
+
}
|
|
10259
|
+
}
|
|
10260
|
+
if (staleKeys.length === 0) return 0;
|
|
10261
|
+
const archivePath = this._getArchivePath(projectId);
|
|
10262
|
+
let archive = {};
|
|
10263
|
+
try {
|
|
10264
|
+
const content = await fs24.readFile(archivePath, "utf-8");
|
|
10265
|
+
archive = JSON.parse(content);
|
|
10266
|
+
} catch (error) {
|
|
10267
|
+
if (!isNotFoundError(error)) throw error;
|
|
10268
|
+
}
|
|
10269
|
+
for (const key of staleKeys) {
|
|
10270
|
+
archive[key] = patterns.decisions[key];
|
|
10271
|
+
delete patterns.decisions[key];
|
|
10272
|
+
}
|
|
10273
|
+
await fs24.mkdir(path22.dirname(archivePath), { recursive: true });
|
|
10274
|
+
await fs24.writeFile(archivePath, JSON.stringify(archive, null, 2), "utf-8");
|
|
10275
|
+
await this.save(projectId);
|
|
10276
|
+
return staleKeys.length;
|
|
10277
|
+
}
|
|
10249
10278
|
};
|
|
10250
10279
|
SemanticMemories = class extends CachedStore {
|
|
10251
10280
|
static {
|
|
@@ -10727,6 +10756,9 @@ Context: ${context2}` : ""}`,
|
|
|
10727
10756
|
getPatternsSummary(projectId) {
|
|
10728
10757
|
return this._patternStore.getPatternsSummary(projectId);
|
|
10729
10758
|
}
|
|
10759
|
+
archiveStaleDecisions(projectId) {
|
|
10760
|
+
return this._patternStore.archiveStaleDecisions(projectId);
|
|
10761
|
+
}
|
|
10730
10762
|
// ===========================================================================
|
|
10731
10763
|
// TIER 3: History
|
|
10732
10764
|
// ===========================================================================
|
|
@@ -14219,6 +14251,97 @@ var init_outcomes2 = __esm({
|
|
|
14219
14251
|
}
|
|
14220
14252
|
});
|
|
14221
14253
|
|
|
14254
|
+
// core/agentic/injection-validator.ts
|
|
14255
|
+
function truncateToTokenBudget(text, maxTokens) {
|
|
14256
|
+
const maxChars = maxTokens * CHARS_PER_TOKEN2;
|
|
14257
|
+
if (text.length <= maxChars) return text;
|
|
14258
|
+
return `${text.substring(0, maxChars)}
|
|
14259
|
+
... (truncated to ~${maxTokens} tokens)`;
|
|
14260
|
+
}
|
|
14261
|
+
function estimateTokens(text) {
|
|
14262
|
+
return Math.ceil(text.length / CHARS_PER_TOKEN2);
|
|
14263
|
+
}
|
|
14264
|
+
function filterSkillsByDomains(skills, detectedDomains) {
|
|
14265
|
+
if (detectedDomains.length === 0 || skills.length === 0) return skills;
|
|
14266
|
+
const relevantKeywords = /* @__PURE__ */ new Set();
|
|
14267
|
+
for (const domain of detectedDomains) {
|
|
14268
|
+
const keywords = DOMAIN_KEYWORDS3[domain.toLowerCase()];
|
|
14269
|
+
if (keywords) {
|
|
14270
|
+
for (const kw of keywords) relevantKeywords.add(kw);
|
|
14271
|
+
}
|
|
14272
|
+
relevantKeywords.add(domain.toLowerCase());
|
|
14273
|
+
}
|
|
14274
|
+
return skills.filter((skill) => {
|
|
14275
|
+
const text = `${skill.name} ${skill.content}`.toLowerCase();
|
|
14276
|
+
for (const kw of relevantKeywords) {
|
|
14277
|
+
if (text.includes(kw)) return true;
|
|
14278
|
+
}
|
|
14279
|
+
return false;
|
|
14280
|
+
});
|
|
14281
|
+
}
|
|
14282
|
+
var DEFAULT_BUDGETS, CHARS_PER_TOKEN2, DOMAIN_KEYWORDS3, InjectionBudgetTracker;
|
|
14283
|
+
var init_injection_validator = __esm({
|
|
14284
|
+
"core/agentic/injection-validator.ts"() {
|
|
14285
|
+
"use strict";
|
|
14286
|
+
DEFAULT_BUDGETS = {
|
|
14287
|
+
autoContext: 500,
|
|
14288
|
+
agentContent: 400,
|
|
14289
|
+
skillContent: 500,
|
|
14290
|
+
stateData: 1e3,
|
|
14291
|
+
memories: 600,
|
|
14292
|
+
totalPrompt: 8e3
|
|
14293
|
+
};
|
|
14294
|
+
CHARS_PER_TOKEN2 = 4;
|
|
14295
|
+
__name(truncateToTokenBudget, "truncateToTokenBudget");
|
|
14296
|
+
__name(estimateTokens, "estimateTokens");
|
|
14297
|
+
DOMAIN_KEYWORDS3 = {
|
|
14298
|
+
frontend: ["react", "vue", "svelte", "css", "html", "ui", "component", "frontend", "web", "dom"],
|
|
14299
|
+
backend: ["api", "server", "backend", "endpoint", "route", "middleware", "database", "sql"],
|
|
14300
|
+
testing: ["test", "spec", "jest", "vitest", "cypress", "playwright", "coverage", "assert"],
|
|
14301
|
+
devops: ["docker", "ci", "cd", "deploy", "kubernetes", "terraform", "pipeline", "github-actions"],
|
|
14302
|
+
docs: ["documentation", "readme", "guide", "tutorial", "markdown"],
|
|
14303
|
+
design: ["design", "ux", "ui", "figma", "wireframe", "layout", "accessibility"]
|
|
14304
|
+
};
|
|
14305
|
+
__name(filterSkillsByDomains, "filterSkillsByDomains");
|
|
14306
|
+
InjectionBudgetTracker = class {
|
|
14307
|
+
static {
|
|
14308
|
+
__name(this, "InjectionBudgetTracker");
|
|
14309
|
+
}
|
|
14310
|
+
used = 0;
|
|
14311
|
+
budgets;
|
|
14312
|
+
constructor(budgets = {}) {
|
|
14313
|
+
this.budgets = { ...DEFAULT_BUDGETS, ...budgets };
|
|
14314
|
+
}
|
|
14315
|
+
/** Add content and return it (possibly truncated to fit budget) */
|
|
14316
|
+
addSection(content, sectionBudget) {
|
|
14317
|
+
const truncated = truncateToTokenBudget(content, sectionBudget);
|
|
14318
|
+
const tokens = estimateTokens(truncated);
|
|
14319
|
+
if (this.used + tokens > this.budgets.totalPrompt) {
|
|
14320
|
+
const remaining = this.budgets.totalPrompt - this.used;
|
|
14321
|
+
if (remaining <= 0) return "";
|
|
14322
|
+
const fitted = truncateToTokenBudget(truncated, remaining);
|
|
14323
|
+
this.used += estimateTokens(fitted);
|
|
14324
|
+
return fitted;
|
|
14325
|
+
}
|
|
14326
|
+
this.used += tokens;
|
|
14327
|
+
return truncated;
|
|
14328
|
+
}
|
|
14329
|
+
/** Get remaining token budget */
|
|
14330
|
+
get remaining() {
|
|
14331
|
+
return Math.max(0, this.budgets.totalPrompt - this.used);
|
|
14332
|
+
}
|
|
14333
|
+
/** Get total tokens used */
|
|
14334
|
+
get totalUsed() {
|
|
14335
|
+
return this.used;
|
|
14336
|
+
}
|
|
14337
|
+
/** Get the budgets config */
|
|
14338
|
+
get config() {
|
|
14339
|
+
return this.budgets;
|
|
14340
|
+
}
|
|
14341
|
+
};
|
|
14342
|
+
}
|
|
14343
|
+
});
|
|
14344
|
+
|
|
14222
14345
|
// core/agentic/prompt-builder.ts
|
|
14223
14346
|
import fs29 from "node:fs/promises";
|
|
14224
14347
|
import path28 from "node:path";
|
|
@@ -14231,6 +14354,7 @@ var init_prompt_builder = __esm({
|
|
|
14231
14354
|
init_fs();
|
|
14232
14355
|
init_fs_helpers();
|
|
14233
14356
|
init_version();
|
|
14357
|
+
init_injection_validator();
|
|
14234
14358
|
PromptBuilder = class {
|
|
14235
14359
|
static {
|
|
14236
14360
|
__name(this, "PromptBuilder");
|
|
@@ -14426,7 +14550,8 @@ var init_prompt_builder = __esm({
|
|
|
14426
14550
|
}
|
|
14427
14551
|
parts.push("---");
|
|
14428
14552
|
parts.push("");
|
|
14429
|
-
|
|
14553
|
+
const result = parts.join("\n");
|
|
14554
|
+
return truncateToTokenBudget(result, DEFAULT_BUDGETS.autoContext);
|
|
14430
14555
|
}
|
|
14431
14556
|
/**
|
|
14432
14557
|
* Calculate elapsed time from ISO timestamp.
|
|
@@ -14560,8 +14685,10 @@ Apply specialized expertise. Read agent file for details if needed.
|
|
|
14560
14685
|
parts.push(`Skills: ${agent2.skills.join(", ")}
|
|
14561
14686
|
`);
|
|
14562
14687
|
}
|
|
14563
|
-
const truncatedContent =
|
|
14564
|
-
|
|
14688
|
+
const truncatedContent = truncateToTokenBudget(
|
|
14689
|
+
agent2.content,
|
|
14690
|
+
DEFAULT_BUDGETS.agentContent
|
|
14691
|
+
);
|
|
14565
14692
|
parts.push(`\`\`\`markdown
|
|
14566
14693
|
${truncatedContent}
|
|
14567
14694
|
\`\`\`
|
|
@@ -14569,13 +14696,19 @@ ${truncatedContent}
|
|
|
14569
14696
|
`);
|
|
14570
14697
|
}
|
|
14571
14698
|
}
|
|
14572
|
-
|
|
14699
|
+
const relevantSkills = filterSkillsByDomains(
|
|
14700
|
+
orchestratorContext.skills,
|
|
14701
|
+
orchestratorContext.detectedDomains
|
|
14702
|
+
);
|
|
14703
|
+
if (relevantSkills.length > 0) {
|
|
14573
14704
|
parts.push("### LOADED SKILLS (From Agent Frontmatter)\n\n");
|
|
14574
|
-
for (const skill of
|
|
14705
|
+
for (const skill of relevantSkills) {
|
|
14575
14706
|
parts.push(`#### Skill: ${skill.name}
|
|
14576
14707
|
`);
|
|
14577
|
-
const truncatedContent =
|
|
14578
|
-
|
|
14708
|
+
const truncatedContent = truncateToTokenBudget(
|
|
14709
|
+
skill.content,
|
|
14710
|
+
DEFAULT_BUDGETS.skillContent
|
|
14711
|
+
);
|
|
14579
14712
|
parts.push(`\`\`\`markdown
|
|
14580
14713
|
${truncatedContent}
|
|
14581
14714
|
\`\`\`
|
|
@@ -14803,28 +14936,20 @@ Show changes, list affected files, ask for confirmation.
|
|
|
14803
14936
|
return parts.join("");
|
|
14804
14937
|
}
|
|
14805
14938
|
/**
|
|
14806
|
-
* Filter state data to include only relevant portions for the prompt
|
|
14939
|
+
* Filter state data to include only relevant portions for the prompt.
|
|
14940
|
+
* Uses InjectionBudgetTracker to enforce cumulative token limits.
|
|
14807
14941
|
*/
|
|
14808
14942
|
filterRelevantState(state) {
|
|
14809
14943
|
if (!state || Object.keys(state).length === 0) return null;
|
|
14944
|
+
const tracker = new InjectionBudgetTracker({ totalPrompt: DEFAULT_BUDGETS.stateData });
|
|
14945
|
+
const criticalFiles = ["now", "next", "context", "analysis", "codePatterns"];
|
|
14810
14946
|
const relevant = [];
|
|
14811
14947
|
for (const [key, content] of Object.entries(state)) {
|
|
14812
14948
|
if (content && content.trim()) {
|
|
14813
|
-
const
|
|
14814
|
-
|
|
14815
|
-
|
|
14816
|
-
|
|
14817
|
-
relevant.push(`### ${key}
|
|
14818
|
-
${display}`);
|
|
14819
|
-
} else if (content.length < 1e3) {
|
|
14820
|
-
relevant.push(`### ${key}
|
|
14821
|
-
${content}`);
|
|
14822
|
-
} else {
|
|
14823
|
-
relevant.push(
|
|
14824
|
-
`### ${key}
|
|
14825
|
-
${content.substring(0, 500)}... (truncated, use Read tool for full content)`
|
|
14826
|
-
);
|
|
14827
|
-
}
|
|
14949
|
+
const sectionBudget = criticalFiles.includes(key) ? 500 : 250;
|
|
14950
|
+
const section = tracker.addSection(`### ${key}
|
|
14951
|
+
${content}`, sectionBudget);
|
|
14952
|
+
if (section) relevant.push(section);
|
|
14828
14953
|
}
|
|
14829
14954
|
}
|
|
14830
14955
|
return relevant.length > 0 ? relevant.join("\n\n") : null;
|
|
@@ -14865,7 +14990,8 @@ ${content.substring(0, 500)}... (truncated, use Read tool for full content)`
|
|
|
14865
14990
|
Avoid:
|
|
14866
14991
|
${antiPatterns}`);
|
|
14867
14992
|
}
|
|
14868
|
-
const
|
|
14993
|
+
const joined = parts.join("\n");
|
|
14994
|
+
const result = truncateToTokenBudget(joined, 200);
|
|
14869
14995
|
return result || null;
|
|
14870
14996
|
}
|
|
14871
14997
|
/**
|
|
@@ -16233,13 +16359,13 @@ var init_breakdown_service = __esm({
|
|
|
16233
16359
|
});
|
|
16234
16360
|
|
|
16235
16361
|
// core/services/context-selector.ts
|
|
16236
|
-
var DEFAULT_TOKEN_BUDGET,
|
|
16362
|
+
var DEFAULT_TOKEN_BUDGET, DOMAIN_KEYWORDS4, ContextSelector, contextSelector;
|
|
16237
16363
|
var init_context_selector = __esm({
|
|
16238
16364
|
"core/services/context-selector.ts"() {
|
|
16239
16365
|
"use strict";
|
|
16240
16366
|
init_index_storage();
|
|
16241
16367
|
DEFAULT_TOKEN_BUDGET = 8e4;
|
|
16242
|
-
|
|
16368
|
+
DOMAIN_KEYWORDS4 = {
|
|
16243
16369
|
payments: [
|
|
16244
16370
|
"payment",
|
|
16245
16371
|
"pay",
|
|
@@ -16422,7 +16548,7 @@ var init_context_selector = __esm({
|
|
|
16422
16548
|
detectTaskDomains(description, projectDomains) {
|
|
16423
16549
|
const normalizedDesc = description.toLowerCase();
|
|
16424
16550
|
const detectedDomains = /* @__PURE__ */ new Set();
|
|
16425
|
-
for (const [domain, keywords] of Object.entries(
|
|
16551
|
+
for (const [domain, keywords] of Object.entries(DOMAIN_KEYWORDS4)) {
|
|
16426
16552
|
for (const keyword of keywords) {
|
|
16427
16553
|
if (normalizedDesc.includes(keyword)) {
|
|
16428
16554
|
detectedDomains.add(domain);
|
|
@@ -19043,8 +19169,8 @@ var init_analyzer2 = __esm({
|
|
|
19043
19169
|
|
|
19044
19170
|
// core/services/diff-generator.ts
|
|
19045
19171
|
import chalk11 from "chalk";
|
|
19046
|
-
function
|
|
19047
|
-
return Math.ceil(content.length /
|
|
19172
|
+
function estimateTokens2(content) {
|
|
19173
|
+
return Math.ceil(content.length / CHARS_PER_TOKEN3);
|
|
19048
19174
|
}
|
|
19049
19175
|
function parseMarkdownSections(content) {
|
|
19050
19176
|
const lines = content.split("\n");
|
|
@@ -19087,8 +19213,8 @@ function generateSyncDiff(oldContent, newContent) {
|
|
|
19087
19213
|
modified: [],
|
|
19088
19214
|
removed: [],
|
|
19089
19215
|
preserved: [],
|
|
19090
|
-
tokensBefore:
|
|
19091
|
-
tokensAfter:
|
|
19216
|
+
tokensBefore: estimateTokens2(oldContent),
|
|
19217
|
+
tokensAfter: estimateTokens2(newContent),
|
|
19092
19218
|
tokenDelta: 0
|
|
19093
19219
|
};
|
|
19094
19220
|
diff.tokenDelta = diff.tokensAfter - diff.tokensBefore;
|
|
@@ -19242,12 +19368,12 @@ function formatFullDiff(diff, options = {}) {
|
|
|
19242
19368
|
}
|
|
19243
19369
|
return lines.join("\n");
|
|
19244
19370
|
}
|
|
19245
|
-
var
|
|
19371
|
+
var CHARS_PER_TOKEN3;
|
|
19246
19372
|
var init_diff_generator = __esm({
|
|
19247
19373
|
"core/services/diff-generator.ts"() {
|
|
19248
19374
|
"use strict";
|
|
19249
|
-
|
|
19250
|
-
__name(
|
|
19375
|
+
CHARS_PER_TOKEN3 = 4;
|
|
19376
|
+
__name(estimateTokens2, "estimateTokens");
|
|
19251
19377
|
__name(parseMarkdownSections, "parseMarkdownSections");
|
|
19252
19378
|
__name(isPreservedSection, "isPreservedSection");
|
|
19253
19379
|
__name(generateSyncDiff, "generateSyncDiff");
|
|
@@ -23429,7 +23555,7 @@ You are the ${name} expert for this project. Apply best practices for the detect
|
|
|
23429
23555
|
* Token estimation: ~4 chars per token (industry standard)
|
|
23430
23556
|
*/
|
|
23431
23557
|
async recordSyncMetrics(stats, contextFiles, agents, duration) {
|
|
23432
|
-
const
|
|
23558
|
+
const CHARS_PER_TOKEN4 = 4;
|
|
23433
23559
|
let filteredChars = 0;
|
|
23434
23560
|
for (const file of contextFiles) {
|
|
23435
23561
|
try {
|
|
@@ -23452,7 +23578,7 @@ You are the ${name} expert for this project. Apply best practices for the detect
|
|
|
23452
23578
|
});
|
|
23453
23579
|
}
|
|
23454
23580
|
}
|
|
23455
|
-
const filteredSize = Math.floor(filteredChars /
|
|
23581
|
+
const filteredSize = Math.floor(filteredChars / CHARS_PER_TOKEN4);
|
|
23456
23582
|
const avgTokensPerFile = 500;
|
|
23457
23583
|
const originalSize = stats.fileCount * avgTokensPerFile;
|
|
23458
23584
|
const compressionRate = originalSize > 0 ? Math.max(0, (originalSize - filteredSize) / originalSize) : 0;
|
|
@@ -29016,7 +29142,7 @@ var require_package = __commonJS({
|
|
|
29016
29142
|
"package.json"(exports, module) {
|
|
29017
29143
|
module.exports = {
|
|
29018
29144
|
name: "prjct-cli",
|
|
29019
|
-
version: "1.7.
|
|
29145
|
+
version: "1.7.4",
|
|
29020
29146
|
description: "Context layer for AI agents. Project context for Claude Code, Gemini CLI, and more.",
|
|
29021
29147
|
main: "core/index.ts",
|
|
29022
29148
|
bin: {
|