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.
Files changed (143) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/adopt-cli.mjs +125 -51
  4. package/bash-utils.mjs +105 -29
  5. package/claudemd.mjs +63 -19
  6. package/cli/activity.mjs +36 -18
  7. package/cli/common.mjs +101 -18
  8. package/cli/doctor.mjs +29 -9
  9. package/cli.mjs +69 -10
  10. package/deep-search.mjs +63 -20
  11. package/format-utils.mjs +15 -7
  12. package/haiku-client.mjs +189 -111
  13. package/hash-utils.mjs +15 -6
  14. package/hook-context.mjs +135 -46
  15. package/hook-episode.mjs +76 -23
  16. package/hook-handoff.mjs +216 -84
  17. package/hook-llm.mjs +476 -216
  18. package/hook-memory.mjs +106 -43
  19. package/hook-optimize.mjs +272 -109
  20. package/hook-precompact.mjs +1 -1
  21. package/hook-semaphore.mjs +46 -12
  22. package/hook-shared.mjs +101 -131
  23. package/hook-update.mjs +286 -88
  24. package/hook.mjs +1207 -617
  25. package/install-metadata.mjs +246 -125
  26. package/install.mjs +1327 -917
  27. package/lib/activity.mjs +52 -29
  28. package/lib/atomic-write.mjs +23 -4
  29. package/lib/binding-probe.mjs +40 -38
  30. package/lib/browse-core.mjs +22 -8
  31. package/lib/citation-tracker.mjs +189 -79
  32. package/lib/cite-back-hint.mjs +37 -19
  33. package/lib/cite-recall-path.mjs +3 -1
  34. package/lib/cli-flags.mjs +4 -2
  35. package/lib/cli-project.mjs +8 -5
  36. package/lib/compress-core.mjs +34 -9
  37. package/lib/cooldown-path.mjs +3 -1
  38. package/lib/db-backup.mjs +33 -11
  39. package/lib/deferred-work.mjs +73 -32
  40. package/lib/delete-core.mjs +25 -9
  41. package/lib/doctor-benchmark.mjs +5 -1
  42. package/lib/doctor-drift.mjs +25 -8
  43. package/lib/edge-attribution.mjs +23 -9
  44. package/lib/env-number.mjs +7 -8
  45. package/lib/err-sampler.mjs +22 -11
  46. package/lib/error-recall-core.mjs +15 -17
  47. package/lib/events-injection.mjs +25 -8
  48. package/lib/export-columns.mjs +49 -10
  49. package/lib/fast-summary.mjs +22 -9
  50. package/lib/file-edge-match.mjs +4 -2
  51. package/lib/file-intel.mjs +38 -13
  52. package/lib/frontmatter.mjs +22 -5
  53. package/lib/get-core.mjs +78 -11
  54. package/lib/handoff-constants.mjs +18 -0
  55. package/lib/hook-stdin.mjs +10 -3
  56. package/lib/hook-stdout.mjs +21 -7
  57. package/lib/hook-telemetry.mjs +44 -17
  58. package/lib/id-routing.mjs +11 -6
  59. package/lib/import-jsonl.mjs +92 -41
  60. package/lib/inject-search-core.mjs +5 -2
  61. package/lib/injected-ids.mjs +19 -12
  62. package/lib/install-shape.mjs +47 -23
  63. package/lib/keyctx-marker.mjs +3 -1
  64. package/lib/lesson-bridge.mjs +8 -5
  65. package/lib/llm-call.mjs +76 -0
  66. package/lib/llm-provider-probe.mjs +24 -12
  67. package/lib/low-signal-patterns.mjs +25 -22
  68. package/lib/maintain-core.mjs +221 -92
  69. package/lib/mem-override.mjs +4 -2
  70. package/lib/metrics.mjs +30 -25
  71. package/lib/native-binding-hint.mjs +29 -8
  72. package/lib/observation-write.mjs +74 -19
  73. package/lib/patha-exclude-meter.mjs +10 -6
  74. package/lib/persist-reminder.mjs +22 -10
  75. package/lib/plan-reader.mjs +2 -2
  76. package/lib/private-strip.mjs +3 -3
  77. package/lib/proc-lock.mjs +15 -3
  78. package/lib/proxy-fetch.mjs +49 -13
  79. package/lib/quiet-scope.mjs +42 -0
  80. package/lib/recall-core.mjs +19 -7
  81. package/lib/recent-core.mjs +18 -5
  82. package/lib/registry-core.mjs +12 -6
  83. package/lib/release-digest.mjs +4 -1
  84. package/lib/relevance-floor.mjs +5 -2
  85. package/lib/reread-guard.mjs +16 -8
  86. package/lib/resolve-data-dir.mjs +1 -1
  87. package/lib/rrf.mjs +4 -1
  88. package/lib/save-enrich.mjs +36 -18
  89. package/lib/save-observation.mjs +78 -35
  90. package/lib/scrub-record.mjs +20 -6
  91. package/lib/search-core.mjs +456 -123
  92. package/lib/shard-gc.mjs +50 -0
  93. package/lib/startup-dashboard.mjs +20 -9
  94. package/lib/stats-core.mjs +101 -35
  95. package/lib/stats-quality.mjs +56 -20
  96. package/lib/summary-extractor.mjs +2 -2
  97. package/lib/task-imperative.mjs +12 -4
  98. package/lib/timeline-core.mjs +88 -28
  99. package/lib/tmp-fixture-sweep.mjs +32 -9
  100. package/lib/transcript-scan.mjs +20 -4
  101. package/lib/upgrade-banner.mjs +11 -7
  102. package/mem-cli.mjs +1314 -506
  103. package/memdir.mjs +53 -21
  104. package/nlp.mjs +68 -38
  105. package/npm-shrinkwrap.json +2 -2
  106. package/package.json +9 -2
  107. package/plugin-cache-guard.mjs +31 -15
  108. package/project-utils.mjs +44 -20
  109. package/registry-enricher.mjs +26 -6
  110. package/registry-github.mjs +4 -2
  111. package/registry-importer.mjs +151 -40
  112. package/registry-recommend.mjs +189 -96
  113. package/registry-retriever.mjs +229 -70
  114. package/registry-scanner.mjs +11 -6
  115. package/registry.mjs +163 -66
  116. package/rerank.mjs +18 -4
  117. package/resource-discovery.mjs +14 -4
  118. package/schema.mjs +258 -99
  119. package/scoring-sql.mjs +21 -10
  120. package/scripts/binding-probe-cli.mjs +17 -12
  121. package/scripts/hook-launcher.mjs +148 -31
  122. package/scripts/launch-preflight.mjs +5 -7
  123. package/scripts/launch.mjs +21 -9
  124. package/scripts/post-tool-recall.js +21 -5
  125. package/scripts/pre-agent-inject.js +51 -13
  126. package/scripts/pre-skill-bridge.js +22 -7
  127. package/scripts/pre-tool-recall.js +157 -77
  128. package/scripts/prompt-search-utils.mjs +70 -25
  129. package/scripts/setup.sh +16 -1
  130. package/scripts/user-prompt-search.js +238 -97
  131. package/search-engine.mjs +329 -96
  132. package/search-scoring.mjs +75 -33
  133. package/secret-scrub.mjs +48 -12
  134. package/server/fts-check.mjs +12 -9
  135. package/server.mjs +674 -243
  136. package/skip-tools.mjs +19 -8
  137. package/source-files.mjs +54 -21
  138. package/stop-words.mjs +152 -19
  139. package/synonyms.mjs +579 -164
  140. package/tfidf.mjs +130 -40
  141. package/tier.mjs +1 -1
  142. package/tool-schemas.mjs +381 -131
  143. 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 { DECAY_HALF_LIFE_BY_TYPE, DEFAULT_DECAY_HALF_LIFE_MS, OBS_BM25, SESS_BM25, EVT_BM25, TYPE_DECAY_CASE, TYPE_QUALITY_CASE, OBS_FTS_COLUMNS, notLowSignalTitleClause, noisePenaltyClause } from './scoring-sql.mjs';
16
- export { cjkBigrams, extractCjkSynonymTokens, extractCjkKeywords, extractCjkLikePatterns, SYNONYM_MAP, expandToken, sanitizeFtsQuery, relaxFtsQueryToOr, FTS_STOP_WORDS, CJK_COMPOUNDS } from './nlp.mjs';
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 { truncate, typeIcon, fmtDate, fmtTime, isoWeekKey, formatErrorRecallHints, neutralizeContextDelimiters } from './format-utils.mjs';
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 { detectBashSignificance, extractErrorKeywords, planErrorRecall, extractFilePaths, stripTestSuffix } from './bash-utils.mjs';
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 ((c >= 0x4e00 && c <= 0x9fff) || (c >= 0x3400 && c <= 0x4dbf) ||
79
- (c >= 0x3000 && c <= 0x303f) || (c >= 0xff00 && c <= 0xffef) ||
80
- (c >= 0xac00 && c <= 0xd7af)) {
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 : (typeof val === 'string' ? Number(val) : NaN);
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)) { importance = 3; break; }
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))) { importance = 3; break; }
155
- if (isEdit && files.some(f => /migration|schema\.|prisma|alembic/i.test(f))) { importance = 3; break; }
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 (files.some(f => /\.config\.|tsconfig|Dockerfile|docker-compose|package\.json|\.yml$|\.yaml$/i.test(basename(f))) && importance < 2) importance = 2;
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 = opts?.isError ?? (/\berror\b|\bfail(ed|ure)?\b|\bexception\b|\bpanic\b/i.test(resp) && resp.length > 30);
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': case 'Agent':
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 { /* sampler dynamic-import fault must not propagate */ }
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)) { start = brackAt; open = '['; close = ']'; }
322
- else { start = braceAt; open = '{'; close = '}'; }
323
- let depth = 0, inStr = false, esc = false;
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 { return JSON.parse(text); } catch {}
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) try { return JSON.parse(fenced[1]); } catch {}
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) try { return JSON.parse(balanced); } catch {}
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 { return JSON.parse(text.slice(firstBrace, lastBrace + 1)); } catch {}
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', 'and', 'for', 'that', 'this', 'with', 'from', 'are', 'was', 'were',
374
- 'been', 'have', 'has', 'had', 'does', 'did', 'will', 'would', 'should', 'could',
375
- 'can', 'may', 'must', 'not', 'but', 'its', 'all', 'any', 'each', 'some',
376
- 'into', 'over', 'after', 'before', 'between', 'about', 'also', 'just', 'then',
377
- 'than', 'when', 'where', 'how', 'what', 'which', 'who', 'why', 'here', 'there',
378
- 'more', 'very', 'only', 'still', 'now', 'new', 'old', 'get', 'got', 'set',
379
- 'true', 'false', 'null', 'undefined', 'function', 'return', 'const', 'let', 'var',
380
- 'import', 'export', 'default', 'class', 'async', 'await', 'try', 'catch',
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 && (now - _branchCacheTime) < BRANCH_CACHE_TTL) return _cachedBranch;
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', timeout: 2000, stdio: ['pipe', 'pipe', 'pipe'],
633
+ encoding: 'utf8',
634
+ timeout: 2000,
635
+ stdio: ['pipe', 'pipe', 'pipe'],
488
636
  }).trim();
489
- _cachedBranch = (result && result !== 'HEAD') ? result : null;
637
+ _cachedBranch = result && result !== 'HEAD' ? result : null;
490
638
  } catch {
491
639
  _cachedBranch = null;
492
640
  }