claude-mem-lite 3.96.0 → 3.97.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/.claude-plugin/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +1 -1
- package/adopt-cli.mjs +125 -51
- package/bash-utils.mjs +105 -29
- package/claudemd.mjs +63 -19
- package/cli/activity.mjs +36 -18
- package/cli/common.mjs +101 -18
- package/cli/doctor.mjs +29 -9
- package/cli.mjs +69 -10
- package/deep-search.mjs +63 -20
- package/format-utils.mjs +15 -7
- package/haiku-client.mjs +189 -111
- package/hash-utils.mjs +15 -6
- package/hook-context.mjs +135 -46
- package/hook-episode.mjs +76 -23
- package/hook-handoff.mjs +216 -84
- package/hook-llm.mjs +476 -216
- package/hook-memory.mjs +106 -43
- package/hook-optimize.mjs +272 -109
- package/hook-precompact.mjs +1 -1
- package/hook-semaphore.mjs +46 -12
- package/hook-shared.mjs +101 -131
- package/hook-update.mjs +286 -88
- package/hook.mjs +1207 -617
- package/install-metadata.mjs +246 -125
- package/install.mjs +1327 -917
- package/lib/activity.mjs +52 -29
- package/lib/atomic-write.mjs +23 -4
- package/lib/binding-probe.mjs +40 -38
- package/lib/browse-core.mjs +22 -8
- package/lib/citation-tracker.mjs +189 -79
- package/lib/cite-back-hint.mjs +37 -19
- package/lib/cite-recall-path.mjs +3 -1
- package/lib/cli-flags.mjs +4 -2
- package/lib/cli-project.mjs +8 -5
- package/lib/compress-core.mjs +34 -9
- package/lib/cooldown-path.mjs +3 -1
- package/lib/db-backup.mjs +33 -11
- package/lib/deferred-work.mjs +73 -32
- package/lib/delete-core.mjs +25 -9
- package/lib/doctor-benchmark.mjs +5 -1
- package/lib/doctor-drift.mjs +25 -8
- package/lib/edge-attribution.mjs +23 -9
- package/lib/env-number.mjs +7 -8
- package/lib/err-sampler.mjs +22 -11
- package/lib/error-recall-core.mjs +15 -17
- package/lib/events-injection.mjs +25 -8
- package/lib/export-columns.mjs +49 -10
- package/lib/fast-summary.mjs +22 -9
- package/lib/file-edge-match.mjs +4 -2
- package/lib/file-intel.mjs +38 -13
- package/lib/frontmatter.mjs +22 -5
- package/lib/get-core.mjs +78 -11
- package/lib/handoff-constants.mjs +18 -0
- package/lib/hook-stdin.mjs +10 -3
- package/lib/hook-stdout.mjs +21 -7
- package/lib/hook-telemetry.mjs +44 -17
- package/lib/id-routing.mjs +11 -6
- package/lib/import-jsonl.mjs +92 -41
- package/lib/inject-search-core.mjs +5 -2
- package/lib/injected-ids.mjs +19 -12
- package/lib/install-shape.mjs +47 -23
- package/lib/keyctx-marker.mjs +3 -1
- package/lib/lesson-bridge.mjs +8 -5
- package/lib/llm-call.mjs +76 -0
- package/lib/llm-provider-probe.mjs +24 -12
- package/lib/low-signal-patterns.mjs +25 -22
- package/lib/maintain-core.mjs +221 -92
- package/lib/mem-override.mjs +4 -2
- package/lib/metrics.mjs +30 -25
- package/lib/native-binding-hint.mjs +29 -8
- package/lib/observation-write.mjs +74 -19
- package/lib/patha-exclude-meter.mjs +10 -6
- package/lib/persist-reminder.mjs +22 -10
- package/lib/plan-reader.mjs +2 -2
- package/lib/private-strip.mjs +3 -3
- package/lib/proc-lock.mjs +15 -3
- package/lib/proxy-fetch.mjs +49 -13
- package/lib/quiet-scope.mjs +42 -0
- package/lib/recall-core.mjs +19 -7
- package/lib/recent-core.mjs +18 -5
- package/lib/registry-core.mjs +12 -6
- package/lib/release-digest.mjs +4 -1
- package/lib/relevance-floor.mjs +5 -2
- package/lib/reread-guard.mjs +16 -8
- package/lib/resolve-data-dir.mjs +1 -1
- package/lib/rrf.mjs +4 -1
- package/lib/save-enrich.mjs +36 -18
- package/lib/save-observation.mjs +78 -35
- package/lib/scrub-record.mjs +20 -6
- package/lib/search-core.mjs +456 -123
- package/lib/shard-gc.mjs +50 -0
- package/lib/startup-dashboard.mjs +20 -9
- package/lib/stats-core.mjs +101 -35
- package/lib/stats-quality.mjs +56 -20
- package/lib/summary-extractor.mjs +2 -2
- package/lib/task-imperative.mjs +12 -4
- package/lib/timeline-core.mjs +88 -28
- package/lib/tmp-fixture-sweep.mjs +32 -9
- package/lib/transcript-scan.mjs +20 -4
- package/lib/upgrade-banner.mjs +11 -7
- package/mem-cli.mjs +1314 -506
- package/memdir.mjs +53 -21
- package/nlp.mjs +68 -38
- package/npm-shrinkwrap.json +2 -2
- package/package.json +9 -2
- package/plugin-cache-guard.mjs +31 -15
- package/project-utils.mjs +44 -20
- package/registry-enricher.mjs +26 -6
- package/registry-github.mjs +4 -2
- package/registry-importer.mjs +151 -40
- package/registry-recommend.mjs +189 -96
- package/registry-retriever.mjs +229 -70
- package/registry-scanner.mjs +11 -6
- package/registry.mjs +163 -66
- package/rerank.mjs +18 -4
- package/resource-discovery.mjs +14 -4
- package/schema.mjs +258 -99
- package/scoring-sql.mjs +21 -10
- package/scripts/binding-probe-cli.mjs +17 -12
- package/scripts/hook-launcher.mjs +148 -31
- package/scripts/launch-preflight.mjs +5 -7
- package/scripts/launch.mjs +21 -9
- package/scripts/post-tool-recall.js +21 -5
- package/scripts/pre-agent-inject.js +51 -13
- package/scripts/pre-skill-bridge.js +22 -7
- package/scripts/pre-tool-recall.js +157 -77
- package/scripts/prompt-search-utils.mjs +70 -25
- package/scripts/setup.sh +16 -1
- package/scripts/user-prompt-search.js +238 -97
- package/search-engine.mjs +329 -96
- package/search-scoring.mjs +75 -33
- package/secret-scrub.mjs +48 -12
- package/server/fts-check.mjs +12 -9
- package/server.mjs +674 -243
- package/skip-tools.mjs +19 -8
- package/source-files.mjs +54 -21
- package/stop-words.mjs +152 -19
- package/synonyms.mjs +579 -164
- package/tfidf.mjs +130 -40
- package/tier.mjs +1 -1
- package/tool-schemas.mjs +381 -131
- package/utils.mjs +184 -36
package/utils.mjs
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
// claude-mem-lite shared utilities
|
|
2
2
|
// Used by server.mjs, hook.mjs, and tests
|
|
3
3
|
|
|
4
|
-
|
|
5
4
|
import { basename, dirname, resolve, sep } from 'path';
|
|
6
5
|
import { execSync } from 'child_process';
|
|
7
6
|
import { buildLowSignalRegex } from './lib/low-signal-patterns.mjs';
|
|
@@ -12,14 +11,50 @@ import { scrubSecrets as _scrubSecrets } from './secret-scrub.mjs';
|
|
|
12
11
|
// ─── Re-exports from extracted modules ──────────────────────────────────────
|
|
13
12
|
// Backward compatibility: all consumers import from utils.mjs
|
|
14
13
|
|
|
15
|
-
export {
|
|
16
|
-
|
|
14
|
+
export {
|
|
15
|
+
DECAY_HALF_LIFE_BY_TYPE,
|
|
16
|
+
DEFAULT_DECAY_HALF_LIFE_MS,
|
|
17
|
+
OBS_BM25,
|
|
18
|
+
SESS_BM25,
|
|
19
|
+
EVT_BM25,
|
|
20
|
+
TYPE_DECAY_CASE,
|
|
21
|
+
TYPE_QUALITY_CASE,
|
|
22
|
+
OBS_FTS_COLUMNS,
|
|
23
|
+
notLowSignalTitleClause,
|
|
24
|
+
noisePenaltyClause,
|
|
25
|
+
} from './scoring-sql.mjs';
|
|
26
|
+
export {
|
|
27
|
+
cjkBigrams,
|
|
28
|
+
extractCjkSynonymTokens,
|
|
29
|
+
extractCjkKeywords,
|
|
30
|
+
extractCjkLikePatterns,
|
|
31
|
+
SYNONYM_MAP,
|
|
32
|
+
expandToken,
|
|
33
|
+
sanitizeFtsQuery,
|
|
34
|
+
relaxFtsQueryToOr,
|
|
35
|
+
FTS_STOP_WORDS,
|
|
36
|
+
CJK_COMPOUNDS,
|
|
37
|
+
} from './nlp.mjs';
|
|
17
38
|
export { inferProject, resolveProject, _resetProjectCache } from './project-utils.mjs';
|
|
18
39
|
export { scrubSecrets, SECRET_PATTERNS } from './secret-scrub.mjs';
|
|
19
40
|
export { stripPrivate } from './lib/private-strip.mjs';
|
|
20
|
-
export {
|
|
41
|
+
export {
|
|
42
|
+
truncate,
|
|
43
|
+
typeIcon,
|
|
44
|
+
fmtDate,
|
|
45
|
+
fmtTime,
|
|
46
|
+
isoWeekKey,
|
|
47
|
+
formatErrorRecallHints,
|
|
48
|
+
neutralizeContextDelimiters,
|
|
49
|
+
} from './format-utils.mjs';
|
|
21
50
|
export { computeMinHash, estimateJaccardFromMinHash, jaccardSimilarity } from './hash-utils.mjs';
|
|
22
|
-
export {
|
|
51
|
+
export {
|
|
52
|
+
detectBashSignificance,
|
|
53
|
+
extractErrorKeywords,
|
|
54
|
+
planErrorRecall,
|
|
55
|
+
extractFilePaths,
|
|
56
|
+
stripTestSuffix,
|
|
57
|
+
} from './bash-utils.mjs';
|
|
23
58
|
|
|
24
59
|
// Internal imports for functions that remain in this module
|
|
25
60
|
import { truncate } from './format-utils.mjs';
|
|
@@ -75,9 +110,13 @@ export function estimateTokens(text) {
|
|
|
75
110
|
let cjkCount = 0;
|
|
76
111
|
for (let i = 0; i < s.length; i++) {
|
|
77
112
|
const c = s.charCodeAt(i);
|
|
78
|
-
if (
|
|
79
|
-
|
|
80
|
-
|
|
113
|
+
if (
|
|
114
|
+
(c >= 0x4e00 && c <= 0x9fff) ||
|
|
115
|
+
(c >= 0x3400 && c <= 0x4dbf) ||
|
|
116
|
+
(c >= 0x3000 && c <= 0x303f) ||
|
|
117
|
+
(c >= 0xff00 && c <= 0xffef) ||
|
|
118
|
+
(c >= 0xac00 && c <= 0xd7af)
|
|
119
|
+
) {
|
|
81
120
|
cjkCount++;
|
|
82
121
|
}
|
|
83
122
|
}
|
|
@@ -95,7 +134,7 @@ export function estimateTokens(text) {
|
|
|
95
134
|
export function clampImportance(val) {
|
|
96
135
|
// Coerce numeric strings: an LLM emitting "importance":"2" (quoted) would otherwise
|
|
97
136
|
// collapse to 1, silently dropping its signal. Non-numeric strings → NaN → 1.
|
|
98
|
-
const n = typeof val === 'number' ? val :
|
|
137
|
+
const n = typeof val === 'number' ? val : typeof val === 'string' ? Number(val) : NaN;
|
|
99
138
|
if (!Number.isFinite(n)) return 1;
|
|
100
139
|
return Math.max(1, Math.min(3, Math.round(n)));
|
|
101
140
|
}
|
|
@@ -145,18 +184,33 @@ export function computeRuleImportance(episode) {
|
|
|
145
184
|
if (lastWasError && EDIT_TOOLS.has(entry.tool)) hasErrorThenEdit = true;
|
|
146
185
|
lastWasError = entry.isError || sig?.isError;
|
|
147
186
|
|
|
148
|
-
if (sig?.isError && (sig?.isTest || sig?.isBuild)) {
|
|
187
|
+
if (sig?.isError && (sig?.isTest || sig?.isBuild)) {
|
|
188
|
+
importance = 3;
|
|
189
|
+
break;
|
|
190
|
+
}
|
|
149
191
|
// Sensitive-file → critical only when the file was EDITED, not merely read or
|
|
150
192
|
// referenced in a bash command (finding #7): reading auth.js / .env / schema.mjs
|
|
151
193
|
// incidentally during an unrelated task must not promote the whole memory to
|
|
152
194
|
// imp=3 and outrank genuine memories in top-K injection.
|
|
153
195
|
const isEdit = EDIT_TOOLS.has(entry.tool);
|
|
154
|
-
if (isEdit && files.some(f => /\.(env|pem|key)$|\/auth\.|\/credential|\/password/i.test(f))) {
|
|
155
|
-
|
|
196
|
+
if (isEdit && files.some((f) => /\.(env|pem|key)$|\/auth\.|\/credential|\/password/i.test(f))) {
|
|
197
|
+
importance = 3;
|
|
198
|
+
break;
|
|
199
|
+
}
|
|
200
|
+
if (isEdit && files.some((f) => /migration|schema\.|prisma|alembic/i.test(f))) {
|
|
201
|
+
importance = 3;
|
|
202
|
+
break;
|
|
203
|
+
}
|
|
156
204
|
if (sig?.isError && importance < 2) importance = 2;
|
|
157
205
|
if (sig?.isGit && importance < 2) importance = 2;
|
|
158
206
|
if (sig?.isDeploy && importance < 2) importance = 2;
|
|
159
|
-
if (
|
|
207
|
+
if (
|
|
208
|
+
files.some((f) =>
|
|
209
|
+
/\.config\.|tsconfig|Dockerfile|docker-compose|package\.json|\.yml$|\.yaml$/i.test(basename(f)),
|
|
210
|
+
) &&
|
|
211
|
+
importance < 2
|
|
212
|
+
)
|
|
213
|
+
importance = 2;
|
|
160
214
|
}
|
|
161
215
|
|
|
162
216
|
// Debug cycle: error followed by edit = active debugging
|
|
@@ -232,7 +286,9 @@ export function makeEntryDesc(toolName, input, resp, opts) {
|
|
|
232
286
|
const cmd = scrubTruncate(input.command || '', 50);
|
|
233
287
|
// Use caller-provided bashSig.isError (word-boundary aware) when available;
|
|
234
288
|
// fall back to inline regex only for standalone callers (tests, etc.)
|
|
235
|
-
const isErr =
|
|
289
|
+
const isErr =
|
|
290
|
+
opts?.isError ??
|
|
291
|
+
(/\berror\b|\bfail(ed|ure)?\b|\bexception\b|\bpanic\b/i.test(resp) && resp.length > 30);
|
|
236
292
|
const snippet = scrubTruncate(resp, 60);
|
|
237
293
|
return isErr ? `${cmd} → ERROR: ${snippet}` : `${cmd} → ${snippet}`;
|
|
238
294
|
}
|
|
@@ -240,7 +296,8 @@ export function makeEntryDesc(toolName, input, resp, opts) {
|
|
|
240
296
|
return `Search "${scrubTruncate(input.pattern || '', 20)}" → ${scrubTruncate(resp, 60)}`;
|
|
241
297
|
case 'LSP':
|
|
242
298
|
return `${input.operation || ''} ${basename(input.filePath || '')}`;
|
|
243
|
-
case 'Task':
|
|
299
|
+
case 'Task':
|
|
300
|
+
case 'Agent':
|
|
244
301
|
return scrubTruncate(input.description || '', 60);
|
|
245
302
|
case 'WebSearch':
|
|
246
303
|
return `Web: ${scrubTruncate(input.query || '', 50)}`;
|
|
@@ -297,7 +354,9 @@ export function debugCatch(e, context) {
|
|
|
297
354
|
try {
|
|
298
355
|
const { maybeSampleError } = await import('./lib/err-sampler.mjs');
|
|
299
356
|
maybeSampleError(e, context, resolveDataDir(process.env.CLAUDE_MEM_DIR));
|
|
300
|
-
} catch {
|
|
357
|
+
} catch {
|
|
358
|
+
/* sampler dynamic-import fault must not propagate */
|
|
359
|
+
}
|
|
301
360
|
})();
|
|
302
361
|
}
|
|
303
362
|
}
|
|
@@ -318,9 +377,18 @@ function firstBalancedJsonObject(text) {
|
|
|
318
377
|
const brackAt = text.indexOf('[');
|
|
319
378
|
let start, open, close;
|
|
320
379
|
if (braceAt === -1 && brackAt === -1) return null;
|
|
321
|
-
if (brackAt !== -1 && (braceAt === -1 || brackAt < braceAt)) {
|
|
322
|
-
|
|
323
|
-
|
|
380
|
+
if (brackAt !== -1 && (braceAt === -1 || brackAt < braceAt)) {
|
|
381
|
+
start = brackAt;
|
|
382
|
+
open = '[';
|
|
383
|
+
close = ']';
|
|
384
|
+
} else {
|
|
385
|
+
start = braceAt;
|
|
386
|
+
open = '{';
|
|
387
|
+
close = '}';
|
|
388
|
+
}
|
|
389
|
+
let depth = 0,
|
|
390
|
+
inStr = false,
|
|
391
|
+
esc = false;
|
|
324
392
|
for (let i = start; i < text.length; i++) {
|
|
325
393
|
const c = text[i];
|
|
326
394
|
if (inStr) {
|
|
@@ -342,16 +410,24 @@ function firstBalancedJsonObject(text) {
|
|
|
342
410
|
*/
|
|
343
411
|
export function parseJsonFromLLM(text) {
|
|
344
412
|
if (!text) return null;
|
|
345
|
-
try {
|
|
413
|
+
try {
|
|
414
|
+
return JSON.parse(text);
|
|
415
|
+
} catch {}
|
|
346
416
|
// No `\s*` around the lazy capture: `\s*([\s\S]*?)\s*` catastrophically backtracks
|
|
347
417
|
// (O(n²)) on a fence + long whitespace + no closing fence — a ~5KB partial buffer hung
|
|
348
418
|
// the CLI-timeout salvage path for 10+s (round-5 review). `([\s\S]*?)```` is a single
|
|
349
419
|
// O(n) lazy scan; JSON.parse tolerates the surrounding whitespace the trim used to strip.
|
|
350
420
|
const fenced = text.match(/```(?:json)?([\s\S]*?)```/);
|
|
351
|
-
if (fenced)
|
|
421
|
+
if (fenced)
|
|
422
|
+
try {
|
|
423
|
+
return JSON.parse(fenced[1]);
|
|
424
|
+
} catch {}
|
|
352
425
|
// First balanced object — survives unfenced output wrapped in brace-containing prose.
|
|
353
426
|
const balanced = firstBalancedJsonObject(text);
|
|
354
|
-
if (balanced)
|
|
427
|
+
if (balanced)
|
|
428
|
+
try {
|
|
429
|
+
return JSON.parse(balanced);
|
|
430
|
+
} catch {}
|
|
355
431
|
// Last-resort span from the first `{` to the last `}` — handles a payload that isn't the
|
|
356
432
|
// FIRST balanced object. Resolved via index scan (O(n)) rather than the greedy
|
|
357
433
|
// /\{[\s\S]*\}/, which backtracks O(n²) across k unclosed opening braces (a synthetic
|
|
@@ -361,7 +437,9 @@ export function parseJsonFromLLM(text) {
|
|
|
361
437
|
const firstBrace = text.indexOf('{');
|
|
362
438
|
const lastBrace = text.lastIndexOf('}');
|
|
363
439
|
if (firstBrace !== -1 && lastBrace > firstBrace) {
|
|
364
|
-
try {
|
|
440
|
+
try {
|
|
441
|
+
return JSON.parse(text.slice(firstBrace, lastBrace + 1));
|
|
442
|
+
} catch {}
|
|
365
443
|
}
|
|
366
444
|
return null;
|
|
367
445
|
}
|
|
@@ -370,14 +448,82 @@ export function parseJsonFromLLM(text) {
|
|
|
370
448
|
|
|
371
449
|
/** Stop words for handoff keyword extraction (broader than ERROR_STOP_WORDS). */
|
|
372
450
|
export const HANDOFF_STOP_WORDS = new Set([
|
|
373
|
-
'the',
|
|
374
|
-
'
|
|
375
|
-
'
|
|
376
|
-
'
|
|
377
|
-
'
|
|
378
|
-
'
|
|
379
|
-
'
|
|
380
|
-
'
|
|
451
|
+
'the',
|
|
452
|
+
'and',
|
|
453
|
+
'for',
|
|
454
|
+
'that',
|
|
455
|
+
'this',
|
|
456
|
+
'with',
|
|
457
|
+
'from',
|
|
458
|
+
'are',
|
|
459
|
+
'was',
|
|
460
|
+
'were',
|
|
461
|
+
'been',
|
|
462
|
+
'have',
|
|
463
|
+
'has',
|
|
464
|
+
'had',
|
|
465
|
+
'does',
|
|
466
|
+
'did',
|
|
467
|
+
'will',
|
|
468
|
+
'would',
|
|
469
|
+
'should',
|
|
470
|
+
'could',
|
|
471
|
+
'can',
|
|
472
|
+
'may',
|
|
473
|
+
'must',
|
|
474
|
+
'not',
|
|
475
|
+
'but',
|
|
476
|
+
'its',
|
|
477
|
+
'all',
|
|
478
|
+
'any',
|
|
479
|
+
'each',
|
|
480
|
+
'some',
|
|
481
|
+
'into',
|
|
482
|
+
'over',
|
|
483
|
+
'after',
|
|
484
|
+
'before',
|
|
485
|
+
'between',
|
|
486
|
+
'about',
|
|
487
|
+
'also',
|
|
488
|
+
'just',
|
|
489
|
+
'then',
|
|
490
|
+
'than',
|
|
491
|
+
'when',
|
|
492
|
+
'where',
|
|
493
|
+
'how',
|
|
494
|
+
'what',
|
|
495
|
+
'which',
|
|
496
|
+
'who',
|
|
497
|
+
'why',
|
|
498
|
+
'here',
|
|
499
|
+
'there',
|
|
500
|
+
'more',
|
|
501
|
+
'very',
|
|
502
|
+
'only',
|
|
503
|
+
'still',
|
|
504
|
+
'now',
|
|
505
|
+
'new',
|
|
506
|
+
'old',
|
|
507
|
+
'get',
|
|
508
|
+
'got',
|
|
509
|
+
'set',
|
|
510
|
+
'true',
|
|
511
|
+
'false',
|
|
512
|
+
'null',
|
|
513
|
+
'undefined',
|
|
514
|
+
'function',
|
|
515
|
+
'return',
|
|
516
|
+
'const',
|
|
517
|
+
'let',
|
|
518
|
+
'var',
|
|
519
|
+
'import',
|
|
520
|
+
'export',
|
|
521
|
+
'default',
|
|
522
|
+
'class',
|
|
523
|
+
'async',
|
|
524
|
+
'await',
|
|
525
|
+
'try',
|
|
526
|
+
'catch',
|
|
381
527
|
]);
|
|
382
528
|
|
|
383
529
|
/**
|
|
@@ -390,8 +536,8 @@ export function tokenizeHandoff(text) {
|
|
|
390
536
|
if (!text) return [];
|
|
391
537
|
return text
|
|
392
538
|
.split(/[\s,;:.()[\]{}'"`<>→|/\\#@!?=+*&^%$~]+/)
|
|
393
|
-
.map(w => w.toLowerCase().replace(/^[.-]+|[.-]+$/g, ''))
|
|
394
|
-
.filter(w => w.length >= 3);
|
|
539
|
+
.map((w) => w.toLowerCase().replace(/^[.-]+|[.-]+$/g, ''))
|
|
540
|
+
.filter((w) => w.length >= 3);
|
|
395
541
|
}
|
|
396
542
|
|
|
397
543
|
/**
|
|
@@ -481,12 +627,14 @@ let _branchCacheTime = 0;
|
|
|
481
627
|
const BRANCH_CACHE_TTL = 60000; // 60s TTL for long-running MCP server process
|
|
482
628
|
export function getCurrentBranch() {
|
|
483
629
|
const now = Date.now();
|
|
484
|
-
if (_cachedBranch !== undefined &&
|
|
630
|
+
if (_cachedBranch !== undefined && now - _branchCacheTime < BRANCH_CACHE_TTL) return _cachedBranch;
|
|
485
631
|
try {
|
|
486
632
|
const result = execSync('git rev-parse --abbrev-ref HEAD', {
|
|
487
|
-
encoding: 'utf8',
|
|
633
|
+
encoding: 'utf8',
|
|
634
|
+
timeout: 2000,
|
|
635
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
488
636
|
}).trim();
|
|
489
|
-
_cachedBranch =
|
|
637
|
+
_cachedBranch = result && result !== 'HEAD' ? result : null;
|
|
490
638
|
} catch {
|
|
491
639
|
_cachedBranch = null;
|
|
492
640
|
}
|