claude-memory-layer 1.0.45 → 1.0.47

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/README.md CHANGED
@@ -21,6 +21,7 @@ Claude Memory Layer는 AI 에이전트의 대화와 작업 이벤트를 프로
21
21
  - **Vector Outbox V2**: SQLite source write와 vector enqueue를 같은 트랜잭션으로 묶고, versioned LanceDB upsert와 stuck-job recovery를 지원합니다.
22
22
  - **Dashboard 운영성 강화**: local-only 기본 bind, 선택적 password gate, Vector Health 카드, Perspective Memory aggregate 카드, retrieval trace/score breakdown을 제공합니다.
23
23
  - **Codex/Hermes history ingest**: 원본 세션은 read-only validate/replay로 먼저 확인하고, 명시적 import로만 프로젝트 메모리에 반영합니다.
24
+ - **Hermes/CML/Headroom 운영 모델**: Hermes built-in memory, `session_search`, CML context-pack, read-only Hermes provider, Headroom reference stance는 [`docs/HERMES_CML_HEADROOM_OPERATING_MODEL.md`](docs/HERMES_CML_HEADROOM_OPERATING_MODEL.md)에 정리되어 있습니다.
24
25
  - **npm 설치 안정화**: `@huggingface/transformers`는 optional dependency + postinstall repair로 설치하며, CUDA 11 환경에서는 CPU-only ONNX Runtime으로 자동 복구합니다.
25
26
 
26
27
  ## 빠른 시작 (신규 프로젝트 기준)
@@ -311,6 +312,7 @@ MCP client가 환경에 따라 PATH를 못 찾으면 `command -v claude-memory-l
311
312
  ### Advanced Features
312
313
 
313
314
  - **Citations System**: 검색 결과를 `[mem:abc123]` 형태로 추적하고 `source`/`mem-source-ref`로 근거 확인
315
+ - **Source Neighbor Expansion**: `mem-source-ref(includeNeighbors=true, neighborWindow=1..5)`로 MemPalace식 hit 주변 세션 이벤트를 privacy-safe preview로 함께 확인
314
316
  - **Progressive Disclosure**: index → timeline → details 순서로 필요한 만큼만 확장해 토큰 비용 절감
315
317
  - **Codex/Hermes Importers**: read-only validate/replay 후 explicit import로만 mutation 수행
316
318
  - **Perspective Query Agent**: 관점별 observation + raw memory를 읽기 전용으로 조합하고 source refs를 유지
@@ -680,7 +682,7 @@ node dist/mcp/index.js
680
682
  | Context | `mem-context-pack` | 작업 시작용 relevant memory + recent timeline + follow-up refs |
681
683
  | Context | `mem-import-latest` | 최신 Claude/Codex/Hermes 세션을 bounded import 후 context-pack freshness 확보 |
682
684
  | Context | `mem-project-timeline` | 최근 프로젝트 메모리를 session/source/count/safe-preview로 요약 |
683
- | Context | `mem-source-ref` | `mem:`/`event:` ref를 redacted preview와 safe metadata로 해석 |
685
+ | Context | `mem-source-ref` | `mem:`/`event:` ref를 redacted preview와 safe metadata로 해석; `includeNeighbors` + `neighborWindow`로 같은 세션의 전후 이벤트를 bounded/privacy-safe preview로 확장 |
684
686
  | Operations | `mem-facet-query` / `mem-facet-tag` | project-scoped facet 조회/태깅 |
685
687
  | Operations | `mem-action-list` / `mem-action-update` | 다음 작업/action 상태 조회·갱신 |
686
688
  | Operations | `mem-frontier` | blocked/next action frontier와 safe resume hints |
@@ -702,7 +704,7 @@ node dist/mcp/index.js
702
704
  ```text
703
705
  1. 새 작업 시작: mem-context-pack(projectPath, query)
704
706
  2. 최근 흐름 확인: mem-project-timeline(projectPath)
705
- 3. 근거가 더 필요할 때: mem-source-ref(projectPath, ids=["mem:abc123"])
707
+ 3. 근거가 더 필요할 때: mem-source-ref(projectPath, ids=["mem:abc123"], includeNeighbors=true, neighborWindow=1)
706
708
  ```
707
709
 
708
710
  이 workflow는 Hermes/Codex/Claude Code가 같은 project-scoped memory backend를 공유할 때 특히 유용합니다. `mem-source-ref`는 raw transcript를 그대로 덤프하지 않고 allowlisted metadata와 privacy-filtered preview만 반환합니다.
package/dist/cli/index.js CHANGED
@@ -2574,6 +2574,7 @@ var VectorOutbox = class {
2574
2574
  // src/core/retrieval-debug-lanes.ts
2575
2575
  var RETRIEVAL_DEBUG_LANE_NAMES = [
2576
2576
  "raw_event",
2577
+ "session_event",
2577
2578
  "session_summary",
2578
2579
  "graph_path",
2579
2580
  "facet_match"
@@ -9926,6 +9927,18 @@ var COMMAND_ARTIFACT_PATTERNS = [
9926
9927
  /<local-command-stdout>[\s\S]*?<\/local-command-stdout>/i,
9927
9928
  /<local-command-stderr>[\s\S]*?<\/local-command-stderr>/i
9928
9929
  ];
9930
+ var LOW_SIGNAL_CONTEXT_PATTERNS = [
9931
+ /<environment_context\b[\s\S]*<\/environment_context>/i,
9932
+ /<turn_aborted>/i,
9933
+ /^#\s*AGENTS\.md\s+instructions\b[\s\S]*<INSTRUCTIONS>/i,
9934
+ /^\s*(?:understood[,\s.]*)?(?:stopping|stopped|pausing|paused)\s+here\b[\s\S]{0,180}\blet\s+me\s+know\s+when\s+you(?:'d|\s+would)?\s+like\s+to\s+continue\b/i,
9935
+ /^\s*\[?CONTEXT\s+COMPACTION\s*[—-]\s*REFERENCE\s+ONLY\]?\b[\s\S]{0,600}\b(?:earlier\s+turns\s+were\s+compacted|handoff\s+from\s+a\s+previous\s+context\s+window|active\s+task)\b/i,
9936
+ /^\s*Summary\s+generation\s+was\s+unavailable\.\s*\d+\s+message\(s\)\s+were\s+removed\s+to\s+free\s+context\s+space\b/i,
9937
+ /^\s*---\s*END\s+OF\s+CONTEXT\s+SUMMARY\b/i,
9938
+ /^\s*\[Your\s+active\s+task\s+list\s+was\s+preserved\s+across\s+context\s+compression\]/i,
9939
+ /^➜\s+\S+\s+git:\([^)]*\)\s+/i,
9940
+ /^\$\s+\S+/i
9941
+ ];
9929
9942
  var CONTINUATION_QUERY_PATTERNS = [
9930
9943
  /^\s*(?:continue|resume|next|what(?:'s| is)? next|next\s+(?:step|task|action)|recommended\s+(?:next\s+)?(?:step|task|action)|what should (?:we|i) do next)\??\s*$/i,
9931
9944
  /^\s*(?:응\s*)?(?:이어서(?:\s*진행(?:해줘)?)?|계속(?:\s*해줘)?|다음\s*(?:단계|작업|추천\s*작업|추천|할\s*일)?(?:은|는)?(?:\s*(?:뭐야|진행(?:해줘)?))?\??|남은\s*(?:추가(?:로)?\s*)?(?:(?:할\s*만한\s*)?(?:작업|일)|할\s*일)?(?:은|는)?\s*(?:있어|있나|있나요|뭐야)\??|추천\s*작업(?:은|는)?(?:\s*뭐야)?\??|진행해줘)\s*$/i
@@ -9937,7 +9950,7 @@ var SHORT_REPAIR_FOLLOW_UP_PATTERNS = [
9937
9950
  var CURRENT_STATE_QUERY_PATTERNS = [
9938
9951
  /\bcurrent\b.*\b(?:state|status|deployment|blocker|pr|pull request)\b/i,
9939
9952
  /\b(?:still|as current|current)\b.*\b(?:unresolved|open|pending|not completed)\b/i,
9940
- /\b(?:old|obsolete|stale|resolved|already resolved)\b.*\b(?:current|still|unresolved|open|state|status)\b/i,
9953
+ /\b(?:old|obsolete|stale|resolved|already resolved)\b.*\b(?:current|still|unresolved|open|status)\b/i,
9941
9954
  /(?:현재|아직|이전|오래된|해결된).*(?:상태|미해결|열린|블로커|PR|풀리퀘스트)/i
9942
9955
  ];
9943
9956
  var STALE_CONTENT_PATTERNS = [
@@ -10083,6 +10096,21 @@ function isCommandArtifactQuery(query) {
10083
10096
  if (normalized.includes("command-name") || normalized.includes("command-message")) return true;
10084
10097
  return COMMAND_ARTIFACT_PATTERNS.some((pattern) => pattern.test(trimmed));
10085
10098
  }
10099
+ function isCommandArtifactContent(content) {
10100
+ const trimmed = content.trim();
10101
+ if (!trimmed) return false;
10102
+ const normalized = trimmed.toLowerCase();
10103
+ if (normalized.includes("local-command-stdout") || normalized.includes("local-command-stderr")) return true;
10104
+ if (normalized.includes("command-name") || normalized.includes("command-message")) return true;
10105
+ return COMMAND_ARTIFACT_PATTERNS.some((pattern) => pattern.test(trimmed));
10106
+ }
10107
+ function isLowSignalContextContent(content) {
10108
+ const trimmed = content.trim();
10109
+ if (!trimmed) return true;
10110
+ if (isCommandArtifactContent(trimmed)) return true;
10111
+ if (LOW_SIGNAL_CONTEXT_PATTERNS.some((pattern) => pattern.test(trimmed))) return true;
10112
+ return false;
10113
+ }
10086
10114
  function isGenericContinuationQuery(query) {
10087
10115
  const trimmed = query.trim();
10088
10116
  if (!trimmed) return false;
@@ -10100,6 +10128,16 @@ function isShortRepairFollowUpQuery(query) {
10100
10128
  if (tokens.length > 8) return false;
10101
10129
  return SHORT_REPAIR_FOLLOW_UP_PATTERNS.some((pattern) => pattern.test(trimmed));
10102
10130
  }
10131
+ function isLowConfidenceContextFallbackQuery(query) {
10132
+ const trimmed = query.trim();
10133
+ if (!trimmed) return false;
10134
+ if (isGenericContinuationQuery(trimmed) || isShortRepairFollowUpQuery(trimmed)) return true;
10135
+ const terms = new Set(tokenizeQualityText(trimmed));
10136
+ if ((terms.has("compacted") || terms.has("compaction")) && terms.has("handoff")) return false;
10137
+ const hasContinuationRecall = /^(?:continue|resume)\b/i.test(trimmed) && (terms.has("work") || terms.has("step") || terms.has("task") || terms.has("last") || terms.has("completed"));
10138
+ const hasValidationGateRecall = terms.has("validation") && (terms.has("gate") || terms.has("check")) && (terms.has("run") || terms.has("before") || terms.has("commit") || terms.has("committing") || terms.has("change"));
10139
+ return hasContinuationRecall || hasValidationGateRecall;
10140
+ }
10103
10141
  function isCurrentStateQuery(query) {
10104
10142
  const trimmed = query.trim();
10105
10143
  if (!trimmed) return false;
@@ -10273,6 +10311,7 @@ var Retriever = class {
10273
10311
  }
10274
10312
  async retrieve(query, options = {}) {
10275
10313
  const opts = { ...DEFAULT_OPTIONS, ...options };
10314
+ const retrievalMode = options.retrievalMode ?? ((options.strategy ?? DEFAULT_OPTIONS.strategy) === "auto" ? "session-event-hybrid" : "event");
10276
10315
  const sessionFilter = opts.scope?.sessionId ?? opts.sessionId;
10277
10316
  const fallbackTrace = [];
10278
10317
  const qualityQuery = buildRetrievalQualityQuery(query);
@@ -10303,6 +10342,7 @@ var Retriever = class {
10303
10342
  decayPolicy: opts.decayPolicy,
10304
10343
  intentRewrite: opts.intentRewrite === true,
10305
10344
  graphHop: opts.graphHop,
10345
+ retrievalMode,
10306
10346
  projectScopeMode: opts.projectScopeMode,
10307
10347
  projectHash: opts.projectHash,
10308
10348
  allowedProjectHashes: opts.allowedProjectHashes,
@@ -10321,6 +10361,7 @@ var Retriever = class {
10321
10361
  rerankWeights: opts.rerankWeights,
10322
10362
  decayPolicy: opts.decayPolicy,
10323
10363
  graphHop: opts.graphHop,
10364
+ retrievalMode,
10324
10365
  projectScopeMode: opts.projectScopeMode,
10325
10366
  projectHash: opts.projectHash,
10326
10367
  allowedProjectHashes: opts.allowedProjectHashes,
@@ -10340,6 +10381,7 @@ var Retriever = class {
10340
10381
  rerankWeights: opts.rerankWeights,
10341
10382
  decayPolicy: opts.decayPolicy,
10342
10383
  graphHop: opts.graphHop,
10384
+ retrievalMode,
10343
10385
  projectScopeMode: opts.projectScopeMode,
10344
10386
  projectHash: opts.projectHash,
10345
10387
  allowedProjectHashes: opts.allowedProjectHashes,
@@ -10360,14 +10402,37 @@ var Retriever = class {
10360
10402
  query,
10361
10403
  minScore: opts.minScore
10362
10404
  });
10405
+ const expandedSummary = retrievalMode === "session-event-hybrid" ? await this.expandSessionEventHybrid(filteredSummary, {
10406
+ query: qualityQuery,
10407
+ currentStateQuery: query,
10408
+ limit: opts.topK * 4
10409
+ }) : filteredSummary;
10410
+ const scopedExpandedSummary = retrievalMode === "session-event-hybrid" ? await this.applyScopeFilters(expandedSummary, {
10411
+ scope: opts.scope,
10412
+ projectScopeMode: opts.projectScopeMode,
10413
+ projectHash: opts.projectHash,
10414
+ allowedProjectHashes: opts.allowedProjectHashes,
10415
+ facets: opts.facets
10416
+ }) : expandedSummary;
10417
+ const finalSummary = retrievalMode === "session-event-hybrid" ? this.applyQualityFilters(scopedExpandedSummary, {
10418
+ query,
10419
+ minScore: opts.minScore
10420
+ }) : scopedExpandedSummary;
10363
10421
  current = {
10364
- results: filteredSummary,
10365
- candidateResults: filteredSummary,
10366
- matchResult: this.matcher.matchSearchResults(filteredSummary, () => 0)
10422
+ results: finalSummary,
10423
+ candidateResults: finalSummary,
10424
+ matchResult: this.matcher.matchSearchResults(finalSummary, () => 0)
10367
10425
  };
10368
10426
  fallbackTrace.push("fallback:summary");
10369
10427
  }
10370
- const memories = await this.enrichResults(current.results.slice(0, opts.topK), opts);
10428
+ const selectedResults = current.results.slice(0, opts.topK).filter((result) => {
10429
+ if (current.matchResult.confidence !== "none") return true;
10430
+ if (isLowConfidenceContextFallbackQuery(query)) {
10431
+ return (result.semanticScore ?? result.score) >= 0.5 || result.score >= 0.5;
10432
+ }
10433
+ return (result.semanticScore ?? result.score) >= 0.62 || result.score >= 0.62;
10434
+ });
10435
+ const memories = await this.enrichResults(selectedResults, opts, query);
10371
10436
  const context = this.buildContext(memories, opts.maxTokens);
10372
10437
  return {
10373
10438
  memories,
@@ -10375,7 +10440,7 @@ var Retriever = class {
10375
10440
  totalTokens: this.estimateTokens(context),
10376
10441
  context,
10377
10442
  fallbackTrace,
10378
- selectedDebug: current.results.slice(0, opts.topK).map((r) => this.debugDetailForResult(r)),
10443
+ selectedDebug: selectedResults.map((r) => this.debugDetailForResult(r)),
10379
10444
  candidateDebug: (current.candidateResults || []).slice(0, Math.max(opts.topK * 3, 20)).map((r) => this.debugDetailForResult(r)),
10380
10445
  rawQueryText: current.queryRewriteKind ? query : void 0,
10381
10446
  effectiveQueryText: current.effectiveQueryText,
@@ -10442,13 +10507,18 @@ var Retriever = class {
10442
10507
  initialResults = this.mergeResults(initialResults, rewrittenResults, input.topK * 3);
10443
10508
  }
10444
10509
  }
10445
- const expandedResults = input.graphHop?.enabled === false ? initialResults : await this.expandGraphHops(initialResults, {
10510
+ const graphExpandedResults = input.graphHop?.enabled === false ? initialResults : await this.expandGraphHops(initialResults, {
10446
10511
  query,
10447
10512
  queryGraphEnabled: this.queryGraphExpansionEnabled,
10448
10513
  maxHops: clampGraphHops(input.graphHop?.maxHops ?? 1),
10449
10514
  hopPenalty: Math.max(0, input.graphHop?.hopPenalty ?? 0.08),
10450
10515
  limit: input.topK * 4
10451
10516
  });
10517
+ const expandedResults = input.retrievalMode === "session-event-hybrid" ? await this.expandSessionEventHybrid(graphExpandedResults, {
10518
+ query: rerankQuery,
10519
+ currentStateQuery: query,
10520
+ limit: input.topK * 4
10521
+ }) : graphExpandedResults;
10452
10522
  const rerankedResults = input.rerankWithKeyword ? this.rerankByKeywordOverlap(expandedResults, rerankQuery, input.rerankWeights, input.decayPolicy) : expandedResults;
10453
10523
  const filtered = await this.applyScopeFilters(rerankedResults, {
10454
10524
  scope: input.scope,
@@ -10470,6 +10540,7 @@ var Retriever = class {
10470
10540
  if (isCurrentStateQuery(options.query)) {
10471
10541
  filtered = filtered.filter((result) => !isStaleOrSupersededContent(result.content));
10472
10542
  }
10543
+ filtered = filtered.filter((result) => !isLowSignalContextContent(result.content));
10473
10544
  filtered = filtered.filter(
10474
10545
  (result) => this.isGraphPathResult(result) || hasDiscriminativeTermOverlap(options.query, result.content)
10475
10546
  );
@@ -10495,6 +10566,47 @@ var Retriever = class {
10495
10566
  }
10496
10567
  return [...byId.values()].sort((a, b) => b.score - a.score).slice(0, limit);
10497
10568
  }
10569
+ async expandSessionEventHybrid(seeds, opts) {
10570
+ if (seeds.length === 0 || opts.limit <= seeds.length) return seeds;
10571
+ const queryTokens = this.tokenize(opts.query);
10572
+ if (queryTokens.length === 0) return seeds;
10573
+ const byId = /* @__PURE__ */ new Map();
10574
+ for (const seed of seeds) byId.set(seed.eventId, seed);
10575
+ const bestSeedBySession = /* @__PURE__ */ new Map();
10576
+ for (const seed of [...seeds].sort((a, b) => b.score - a.score || compareStable(a.eventId, b.eventId))) {
10577
+ if (!seed.sessionId || bestSeedBySession.has(seed.sessionId)) continue;
10578
+ bestSeedBySession.set(seed.sessionId, seed);
10579
+ }
10580
+ const suppressStaleState = isCurrentStateQuery(opts.currentStateQuery);
10581
+ for (const [sessionId, seed] of bestSeedBySession) {
10582
+ const sessionEvents = await this.eventStore.getSessionEvents(sessionId);
10583
+ for (const event of [...sessionEvents].sort((a, b) => a.timestamp.getTime() - b.timestamp.getTime())) {
10584
+ if (byId.has(event.id)) continue;
10585
+ if (isLowSignalContextContent(event.content)) continue;
10586
+ if (suppressStaleState && isStaleOrSupersededContent(event.content)) continue;
10587
+ const lexicalScore = this.keywordOverlap(queryTokens, this.tokenize(event.content));
10588
+ if (lexicalScore <= 0) continue;
10589
+ if (shouldApplyTechnicalGuard(opts.query) && !hasTechnicalTermOverlap(opts.query, event.content)) continue;
10590
+ const score = Math.min(0.95, Math.max(0.35, seed.score * 0.72 + lexicalScore * 0.28));
10591
+ const row = withRetrievalLane({
10592
+ id: `session-event-${seed.eventId}-${event.id}`,
10593
+ eventId: event.id,
10594
+ content: event.content,
10595
+ score,
10596
+ sessionId: event.sessionId,
10597
+ eventType: event.eventType,
10598
+ timestamp: event.timestamp.toISOString(),
10599
+ semanticScore: seed.semanticScore ?? seed.score,
10600
+ lexicalScore,
10601
+ recencyScore: seed.recencyScore
10602
+ }, { lane: "session_event", reason: `same_session:${seed.eventId}`, score });
10603
+ byId.set(row.eventId, row);
10604
+ if (byId.size >= opts.limit) break;
10605
+ }
10606
+ if (byId.size >= opts.limit) break;
10607
+ }
10608
+ return [...byId.values()].sort((a, b) => b.score - a.score || compareStable(a.eventId, b.eventId)).slice(0, opts.limit);
10609
+ }
10498
10610
  async expandGraphHops(seeds, opts) {
10499
10611
  const byId = /* @__PURE__ */ new Map();
10500
10612
  for (const s of seeds) byId.set(s.eventId, s);
@@ -10810,7 +10922,7 @@ var Retriever = class {
10810
10922
  async retrieveRecent(limit = 100) {
10811
10923
  return this.eventStore.getRecentEvents(limit);
10812
10924
  }
10813
- async enrichResults(results, options) {
10925
+ async enrichResults(results, options, query) {
10814
10926
  const memories = [];
10815
10927
  for (const result of results) {
10816
10928
  const event = await this.eventStore.getEvent(result.eventId);
@@ -10820,13 +10932,13 @@ var Retriever = class {
10820
10932
  }
10821
10933
  let sessionContext;
10822
10934
  if (options.includeSessionContext) {
10823
- sessionContext = await this.getSessionContext(event.sessionId, event.id);
10935
+ sessionContext = await this.getSessionContext(event.sessionId, event.id, query);
10824
10936
  }
10825
10937
  memories.push({ event, score: result.score, sessionContext });
10826
10938
  }
10827
10939
  return memories;
10828
10940
  }
10829
- async getSessionContext(sessionId, eventId) {
10941
+ async getSessionContext(sessionId, eventId, query) {
10830
10942
  const sessionEvents = await this.eventStore.getSessionEvents(sessionId);
10831
10943
  const eventIndex = sessionEvents.findIndex((e) => e.id === eventId);
10832
10944
  if (eventIndex === -1) return void 0;
@@ -10834,7 +10946,9 @@ var Retriever = class {
10834
10946
  const end = Math.min(sessionEvents.length, eventIndex + 2);
10835
10947
  const contextEvents = sessionEvents.slice(start, end);
10836
10948
  if (contextEvents.length <= 1) return void 0;
10837
- return contextEvents.filter((e) => e.id !== eventId).map((e) => `[${e.eventType}]: ${e.content.slice(0, 200)}...`).join("\n");
10949
+ const suppressStaleState = isCurrentStateQuery(query);
10950
+ const contextLines = contextEvents.filter((e) => e.id !== eventId).filter((e) => !isLowSignalContextContent(e.content)).filter((e) => !(suppressStaleState && isStaleOrSupersededContent(e.content))).map((e) => `[${e.eventType}]: ${e.content.slice(0, 200)}...`);
10951
+ return contextLines.length > 0 ? contextLines.join("\n") : void 0;
10838
10952
  }
10839
10953
  buildUnifiedContext(projectResult, sharedMemories) {
10840
10954
  let context = projectResult.context;
@@ -20990,7 +21104,7 @@ async function runOperationCli(action, label) {
20990
21104
  }
20991
21105
  }
20992
21106
  var program = new Command();
20993
- program.name("claude-memory-layer").description("Claude Code Memory Plugin CLI").version("1.0.45");
21107
+ program.name("claude-memory-layer").description("Claude Code Memory Plugin CLI").version("1.0.47");
20994
21108
  program.command("market-context").description("Fetch read-only DART/FRED/Finnhub context with structured MarketContextSnapshot bull/bear/risk/catalyst analysis").option("--company <name>", "Company name for DART fallback search and report subject").option("--dart-corp-code <code>", "Exact DART corp_code for issuer-specific filings").option("--symbol <ticker>", "Listed ticker for Finnhub company profile").option("--providers <list>", "Comma-separated providers: dart,fred,finnhub").option("--fred-series <list>", "Comma-separated FRED series IDs").option("--json", "Print structured JSON including analysis.marketSnapshot").option("--no-snapshot", "Disable MarketContextSnapshot and DART company snapshot analysis").action(async (options) => {
20995
21109
  try {
20996
21110
  await runMarketContextCommand(options);