@monoes/monomindcli 1.10.9 → 1.10.10

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.
@@ -45,23 +45,33 @@ function getMonographSuggestions(taskText, limit) {
45
45
  if (!db) return [];
46
46
  try {
47
47
  var words = String(taskText).toLowerCase().match(/[a-z][a-z0-9_-]{3,}/g) || [];
48
- var stop = { "this":1,"that":1,"with":1,"from":1,"have":1,"into":1,"their":1,"what":1,"when":1,"where":1,"which":1,"should":1,"would":1,"could":1,"make":1,"just":1,"also":1,"them":1,"they":1,"will":1,"been":1,"were":1,"because":1,"about":1,"does":1,"work":1 };
48
+ var stop = { "this":1,"that":1,"with":1,"from":1,"have":1,"into":1,"their":1,"what":1,"when":1,"where":1,"which":1,"should":1,"would":1,"could":1,"make":1,"just":1,"also":1,"them":1,"they":1,"will":1,"been":1,"were":1,"because":1,"about":1,"does":1,"work":1,"else":1,"more":1,"some":1,"like":1,"need":1,"want":1,"used":1,"using":1,"please":1,"thanks":1,"good":1,"great":1,"nice":1,"thing":1,"things":1,"better":1,"again":1,"first":1,"then":1,"only":1,"even":1 };
49
49
  var uniq = {};
50
50
  for (var i = 0; i < words.length; i++) if (!stop[words[i]]) uniq[words[i]] = 1;
51
51
  var keys = Object.keys(uniq).slice(0, 8);
52
+ // Smart filter: free-form prompts need ≥2 content words to avoid noise.
53
+ // Single-word inputs are allowed only when they look like a symbol/search
54
+ // (entire string is ≤30 chars and contains letters+separators only).
55
+ var isSymbolLookup = taskText.length <= 30 && /^[a-zA-Z0-9_\-./:]+$/.test(taskText.trim());
52
56
  if (keys.length === 0) return [];
57
+ if (keys.length < 2 && !isSymbolLookup) return [];
53
58
 
54
59
  var ftsQuery = keys.map(function(k){ return '"' + k.replace(/"/g, "") + '"'; }).join(" OR ");
55
60
  var lim = Math.max(1, limit || 5);
56
61
  var rows = [];
57
62
  try {
63
+ // BM25 ranks better than degree for keyword relevance; tie-break by deg.
64
+ // File/Function/Class outrank Section so navigable nodes win.
58
65
  rows = db.prepare(
59
66
  "SELECT n.id, n.name, n.label, n.file_path AS file, " +
60
- "(SELECT COUNT(*) FROM edges WHERE source_id=n.id OR target_id=n.id) AS deg " +
67
+ "bm25(nodes_fts) AS bm25_score, " +
68
+ "(SELECT COUNT(*) FROM edges WHERE source_id=n.id OR target_id=n.id) AS deg, " +
69
+ "CASE n.label WHEN 'File' THEN 3 WHEN 'Function' THEN 3 WHEN 'Class' THEN 3 " +
70
+ " WHEN 'Method' THEN 2 WHEN 'Interface' THEN 2 ELSE 1 END AS label_rank " +
61
71
  "FROM nodes_fts f JOIN nodes n ON f.rowid = n.rowid " +
62
72
  "WHERE nodes_fts MATCH ? AND n.file_path IS NOT NULL AND n.file_path != '' " +
63
73
  "AND n.label NOT IN ('Concept') " +
64
- "ORDER BY deg DESC LIMIT ?"
74
+ "ORDER BY label_rank DESC, bm25_score ASC, deg DESC LIMIT ?"
65
75
  ).all(ftsQuery, lim);
66
76
  } catch (e) {
67
77
  var likeFrag = keys.map(function(){ return "lower(n.name) LIKE ?"; }).join(" OR ");
@@ -108,6 +118,17 @@ function getMonographNeighbors(filePath) {
108
118
  finally { try { db.close(); } catch (_) {} }
109
119
  }
110
120
 
121
+ // Rough per-event token + USD cost estimates. Tuned to Sonnet input pricing
122
+ // ($3/M tokens) — adjust if needed. Used by the statusline to surface savings.
123
+ var _TOKEN_PER_EVENT = {
124
+ monograph_call: 300, // typical monograph_query result size
125
+ grep_call: 2000, // typical Grep tool output across many files
126
+ glob_call: 800,
127
+ bash_grep_call: 2000,
128
+ bash_find_call: 800,
129
+ };
130
+ var _DOLLAR_PER_1M_TOKENS = 3.0;
131
+
111
132
  function _recordGraphTelemetry(event) {
112
133
  try {
113
134
  var metricsDir = path.join(CWD, ".monomind", "metrics");
@@ -117,11 +138,65 @@ function _recordGraphTelemetry(event) {
117
138
  try { d = JSON.parse(fs.readFileSync(f, "utf-8")); } catch (e) {}
118
139
  if (typeof d !== "object" || d === null) d = {};
119
140
  d[event] = (d[event] || 0) + 1;
141
+
142
+ // Token-saved estimator: each monograph_call avoids a grep equivalent
143
+ // (~2000 tokens) at a cost of ~300 tokens — net ~1700 saved.
144
+ if (event === 'monograph_call') {
145
+ var saved = (_TOKEN_PER_EVENT.grep_call - _TOKEN_PER_EVENT.monograph_call);
146
+ d.tokens_saved = (d.tokens_saved || 0) + saved;
147
+ d.dollars_saved = ((d.tokens_saved / 1000000) * _DOLLAR_PER_1M_TOKENS);
148
+ }
149
+ // Each unprompted grep/bash_grep "wastes" the same amount vs the graph alternative.
150
+ if (event === 'grep_call' || event === 'bash_grep_call') {
151
+ var wasted = (_TOKEN_PER_EVENT.grep_call - _TOKEN_PER_EVENT.monograph_call);
152
+ d.tokens_wasted = (d.tokens_wasted || 0) + wasted;
153
+ }
154
+
120
155
  d.lastUpdated = Date.now();
121
156
  fs.writeFileSync(f, JSON.stringify(d));
122
157
  } catch (e) { /* non-fatal */ }
123
158
  }
124
159
 
160
+ // Auto-rebuild the monograph after N writes — graph staleness during heavy
161
+ // editing is the main reason suggestions go cold. Triggered by post-edit.
162
+ function _maybeRebuildMonograph() {
163
+ try {
164
+ var metricsDir = path.join(CWD, ".monomind", "metrics");
165
+ fs.mkdirSync(metricsDir, { recursive: true });
166
+ var f = path.join(metricsDir, "graph-rebuild.json");
167
+ var d = {};
168
+ try { d = JSON.parse(fs.readFileSync(f, "utf-8")); } catch (_) {}
169
+ if (typeof d !== "object" || d === null) d = {};
170
+ d.writesSinceRebuild = (d.writesSinceRebuild || 0) + 1;
171
+ d.lastWriteAt = Date.now();
172
+ var THRESHOLD = 20;
173
+ var MIN_INTERVAL_MS = 5 * 60 * 1000; // never more often than every 5 min
174
+ var dueByCount = d.writesSinceRebuild >= THRESHOLD;
175
+ var dueByTime = !d.lastRebuildAt || (Date.now() - d.lastRebuildAt) > MIN_INTERVAL_MS;
176
+ if (dueByCount && dueByTime) {
177
+ // Reset counter immediately so concurrent post-edits don't all fire.
178
+ d.writesSinceRebuild = 0;
179
+ d.lastRebuildAt = Date.now();
180
+ fs.writeFileSync(f, JSON.stringify(d));
181
+ // Fire-and-forget background freshen script (same one used by SessionStart).
182
+ try {
183
+ var freshenScript = path.join(CWD, '.claude', 'helpers', 'graphify-freshen.cjs');
184
+ if (fs.existsSync(freshenScript)) {
185
+ var spawn = require('child_process').spawn;
186
+ var child = spawn(process.execPath, [freshenScript], {
187
+ detached: true,
188
+ stdio: 'ignore',
189
+ cwd: CWD,
190
+ });
191
+ child.unref();
192
+ }
193
+ } catch (_) {}
194
+ } else {
195
+ fs.writeFileSync(f, JSON.stringify(d));
196
+ }
197
+ } catch (e) { /* non-fatal */ }
198
+ }
199
+
125
200
  function safeRequire(modulePath) {
126
201
  try {
127
202
  if (fs.existsSync(modulePath)) {
@@ -888,7 +963,8 @@ const handlers = {
888
963
 
889
964
  'pre-bash': () => {
890
965
  var _rawCmd = hookInput.command || prompt;
891
- var cmd = (typeof _rawCmd === 'string' ? _rawCmd : String(_rawCmd || '')).toLowerCase();
966
+ var rawCmdStr = (typeof _rawCmd === 'string' ? _rawCmd : String(_rawCmd || ''));
967
+ var cmd = rawCmdStr.toLowerCase();
892
968
  var dangerous = ['rm -rf /', 'rm -rf/', 'format c:', 'del /s /q c:\\', ':(){:|:&};:'];
893
969
  for (var i = 0; i < dangerous.length; i++) {
894
970
  if (cmd.includes(dangerous[i])) {
@@ -896,6 +972,36 @@ const handlers = {
896
972
  process.exit(1);
897
973
  }
898
974
  }
975
+
976
+ // Intercept Bash-side grep/rg/find/ag — close the loophole where LLM
977
+ // bypasses the Grep tool by shelling out. Same monograph hint shape as pre-search.
978
+ try {
979
+ // Match: grep <flags> <pattern>, rg <flags> <pattern>, ag <pattern>, find . -name <pattern>
980
+ var grepMatch = rawCmdStr.match(/\b(?:grep|rg|ag)\b(?:\s+-[a-zA-Z]+)*\s+(?:"([^"]+)"|'([^']+)'|([^\s|;&<>]+))/);
981
+ var findMatch = rawCmdStr.match(/\bfind\b.*?-name\s+(?:"([^"]+)"|'([^']+)'|([^\s|;&<>]+))/);
982
+ var pattern = '';
983
+ if (grepMatch) {
984
+ pattern = grepMatch[1] || grepMatch[2] || grepMatch[3] || '';
985
+ _recordGraphTelemetry('bash_grep_call');
986
+ } else if (findMatch) {
987
+ pattern = findMatch[1] || findMatch[2] || findMatch[3] || '';
988
+ _recordGraphTelemetry('bash_find_call');
989
+ }
990
+ if (pattern && pattern.length >= 3) {
991
+ var clean = pattern.replace(/[\\^$.*+?()\[\]{}|]/g, ' ').trim();
992
+ if (clean.length >= 3) {
993
+ var hits = getMonographSuggestions(clean, 5);
994
+ if (hits.length > 0) {
995
+ console.log('[MONOGRAPH_HIT] Graph has ' + hits.length + ' file(s) for "' + clean.slice(0, 40) + '" — consider monograph_query instead of shell grep:');
996
+ for (var j = 0; j < hits.length; j++) {
997
+ var h = hits[j];
998
+ console.log(' · ' + h.name + ' [' + h.label + '] — ' + (h.file || ''));
999
+ }
1000
+ }
1001
+ }
1002
+ }
1003
+ } catch (e) { /* non-fatal */ }
1004
+
899
1005
  console.log('[OK] Command validated');
900
1006
  },
901
1007
 
@@ -956,6 +1062,8 @@ const handlers = {
956
1062
  intelligence.recordEdit(file);
957
1063
  } catch (e) { /* non-fatal */ }
958
1064
  }
1065
+ // Increment write counter and rebuild monograph when threshold hit.
1066
+ _maybeRebuildMonograph();
959
1067
  // ── Security-Sensitive File Auto-Alert ────────────────────────────────
960
1068
  // When editing auth, security, crypto, or env-related files, flag it
961
1069
  try {
@@ -1886,6 +1994,47 @@ const handlers = {
1886
1994
  'utf-8'
1887
1995
  );
1888
1996
  } catch (e) { /* non-fatal — never block a subagent from starting */ }
1997
+
1998
+ // Subagent context inheritance — inject graph god nodes + parent's last
1999
+ // pre-resolved suggestions so the spawned agent inherits spatial map
2000
+ // instead of starting blind.
2001
+ try {
2002
+ var subDb = _openMonographDb();
2003
+ if (subDb) {
2004
+ try {
2005
+ var godRows = subDb.prepare(
2006
+ "SELECT n.name, n.label, n.file_path AS file, " +
2007
+ "(SELECT COUNT(*) FROM edges WHERE source_id=n.id OR target_id=n.id) AS deg " +
2008
+ "FROM nodes n " +
2009
+ "WHERE n.label NOT IN ('Concept') AND n.file_path IS NOT NULL AND n.file_path != '' " +
2010
+ "ORDER BY deg DESC LIMIT 5"
2011
+ ).all();
2012
+ if (godRows.length > 0) {
2013
+ console.log('[MONOGRAPH_SUBAGENT_CTX] Graph map inherited from parent:');
2014
+ for (var gi = 0; gi < godRows.length; gi++) {
2015
+ var gr = godRows[gi];
2016
+ console.log(' · ' + gr.name + ' [' + gr.label + '] — ' + (gr.file || '') + ' (deg ' + gr.deg + ')');
2017
+ }
2018
+ // Also forward parent's last routing suggestion text if any
2019
+ try {
2020
+ var subAgentDesc = hookInput.description || hookInput.prompt_description || '';
2021
+ if (subAgentDesc && subAgentDesc.length > 8) {
2022
+ var subHints = getMonographSuggestions(subAgentDesc, 3);
2023
+ if (subHints.length > 0) {
2024
+ console.log(' Top files for this subagent task:');
2025
+ for (var si2 = 0; si2 < subHints.length; si2++) {
2026
+ var sh = subHints[si2];
2027
+ console.log(' · ' + sh.name + ' [' + sh.label + '] — ' + (sh.file || ''));
2028
+ }
2029
+ }
2030
+ }
2031
+ } catch (_) {}
2032
+ console.log(' Use mcp__monomind__monograph_suggest / monograph_query in this subagent before grepping.');
2033
+ }
2034
+ } finally { try { subDb.close(); } catch (_) {} }
2035
+ }
2036
+ } catch (e) { /* non-fatal */ }
2037
+
1889
2038
  console.log('[OK] Agent registered');
1890
2039
  },
1891
2040
 
@@ -696,20 +696,22 @@ function getGraphifyStats() {
696
696
  return { nodes: 0, edges: 0, exists: false };
697
697
  }
698
698
 
699
- // Graph usage telemetry — ratio of monograph_* tool calls vs Grep/Glob
699
+ // Graph usage telemetry — ratio of monograph_* vs Grep/Glob, plus $ saved
700
700
  function getGraphUsage() {
701
701
  const usagePath = path.join(CWD, '.monomind', 'metrics', 'graph-usage.json');
702
702
  try {
703
703
  if (!fs.existsSync(usagePath)) return null;
704
704
  const d = JSON.parse(fs.readFileSync(usagePath, 'utf-8'));
705
705
  const monograph = d.monograph_call || 0;
706
- const search = (d.grep_call || 0) + (d.glob_call || 0);
706
+ const search = (d.grep_call || 0) + (d.glob_call || 0)
707
+ + (d.bash_grep_call || 0) + (d.bash_find_call || 0);
707
708
  const total = monograph + search;
708
709
  if (total === 0) return null;
709
710
  return {
710
711
  monograph,
711
712
  search,
712
713
  pct: Math.round((monograph / total) * 100),
714
+ dollarsSaved: d.dollars_saved || 0,
713
715
  };
714
716
  } catch { return null; }
715
717
  }
@@ -1257,12 +1259,15 @@ function generateDashboard() {
1257
1259
  loopStr = `${x.slate}🔄 no active loops${x.reset}`;
1258
1260
  }
1259
1261
 
1260
- // Graph usage ratio — show only when there's data
1262
+ // Graph usage ratio + $ saved — show only when there's data
1261
1263
  const usage = getGraphUsage();
1262
1264
  let usageStr = '';
1263
1265
  if (usage) {
1264
1266
  const col = usage.pct >= 40 ? x.green : usage.pct >= 15 ? x.gold : x.coral;
1265
- usageStr = ` ${DIV} ${col}📊 graph ${usage.pct}%${x.reset}${x.slate} · grep ${100 - usage.pct}%${x.reset}`;
1267
+ const saved = usage.dollarsSaved > 0
1268
+ ? ` ${x.green}💰 +$${usage.dollarsSaved.toFixed(2)}${x.reset}`
1269
+ : '';
1270
+ usageStr = ` ${DIV} ${col}📊 graph ${usage.pct}%${x.reset}${x.slate} · grep ${100 - usage.pct}%${x.reset}${saved}`;
1266
1271
  }
1267
1272
 
1268
1273
  lines.push(`${x.purple}🤖 AGENT${x.reset} ${agentStr} ${DIV} ${loopStr}${usageStr}`);
@@ -1 +1 @@
1
- {"version":3,"file":"statusline-generator.d.ts","sourceRoot":"","sources":["../../../src/init/statusline-generator.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAE9C;;;;;;;;;GASG;AACH,wBAAgB,wBAAwB,CAAC,OAAO,EAAE,WAAW,GAAG,MAAM,CA0oCrE;AAED,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,WAAW,GAAG,MAAM,CA8BnE"}
1
+ {"version":3,"file":"statusline-generator.d.ts","sourceRoot":"","sources":["../../../src/init/statusline-generator.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAE9C;;;;;;;;;GASG;AACH,wBAAgB,wBAAwB,CAAC,OAAO,EAAE,WAAW,GAAG,MAAM,CA8oCrE;AAED,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,WAAW,GAAG,MAAM,CA8BnE"}
@@ -828,17 +828,18 @@ function getTriggerStats() {
828
828
  } catch { return { triggers: 0, agents: 0 }; }
829
829
  }
830
830
 
831
- // Graph usage telemetry — ratio of monograph_* tool calls vs Grep/Glob
831
+ // Graph usage telemetry — ratio of monograph_* vs Grep/Glob/Bash-grep, + $ saved
832
832
  function getGraphUsage() {
833
833
  const usagePath = path.join(CWD, '.monomind', 'metrics', 'graph-usage.json');
834
834
  try {
835
835
  if (!fs.existsSync(usagePath)) return null;
836
836
  const d = JSON.parse(fs.readFileSync(usagePath, 'utf-8'));
837
837
  const monograph = d.monograph_call || 0;
838
- const search = (d.grep_call || 0) + (d.glob_call || 0);
838
+ const search = (d.grep_call || 0) + (d.glob_call || 0)
839
+ + (d.bash_grep_call || 0) + (d.bash_find_call || 0);
839
840
  const total = monograph + search;
840
841
  if (total === 0) return null;
841
- return { monograph: monograph, search: search, pct: Math.round((monograph / total) * 100) };
842
+ return { monograph: monograph, search: search, pct: Math.round((monograph / total) * 100), dollarsSaved: d.dollars_saved || 0 };
842
843
  } catch { return null; }
843
844
  }
844
845
 
@@ -1084,12 +1085,15 @@ function generateDashboard() {
1084
1085
  loopStr = \`\${x.slate}🔄 no active loops\${x.reset}\`;
1085
1086
  }
1086
1087
 
1087
- // Graph usage ratio — show only when there's data
1088
+ // Graph usage ratio + $ saved — show only when there's data
1088
1089
  const usage = getGraphUsage();
1089
1090
  let usageStr = '';
1090
1091
  if (usage) {
1091
1092
  const col = usage.pct >= 40 ? x.green : usage.pct >= 15 ? x.gold : x.coral;
1092
- usageStr = \` \${DIV} \${col}📊 graph \${usage.pct}%\${x.reset}\${x.slate} · grep \${100 - usage.pct}%\${x.reset}\`;
1093
+ const saved = usage.dollarsSaved > 0
1094
+ ? \` \${x.green}💰 +$\${usage.dollarsSaved.toFixed(2)}\${x.reset}\`
1095
+ : '';
1096
+ usageStr = \` \${DIV} \${col}📊 graph \${usage.pct}%\${x.reset}\${x.slate} · grep \${100 - usage.pct}%\${x.reset}\${saved}\`;
1093
1097
  }
1094
1098
 
1095
1099
  lines.push(\`\${x.purple}🤖 AGENT\${x.reset} \${agentStr} \${DIV} \${loopStr}\${usageStr}\`);
@@ -1 +1 @@
1
- {"version":3,"file":"statusline-generator.js","sourceRoot":"","sources":["../../../src/init/statusline-generator.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAIH;;;;;;;;;GASG;AACH,MAAM,UAAU,wBAAwB,CAAC,OAAoB;IAC3D,MAAM,SAAS,GAAG,OAAO,CAAC,OAAO,CAAC,SAAS,CAAC;IAC5C,OAAO;;;;;;;;;;;;;;;;;;;;;;;eAuBM,SAAS;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAgnCvB,CAAC;AACF,CAAC;AAED,MAAM,UAAU,sBAAsB,CAAC,OAAoB;IACzD,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,OAAO,EAAE,CAAC;QAChC,OAAO,sCAAsC,CAAC;IAChD,CAAC;IAED,OAAO;;;;;;;;;;;;;;;;;;;;;;;;CAwBR,CAAC;AACF,CAAC"}
1
+ {"version":3,"file":"statusline-generator.js","sourceRoot":"","sources":["../../../src/init/statusline-generator.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAIH;;;;;;;;;GASG;AACH,MAAM,UAAU,wBAAwB,CAAC,OAAoB;IAC3D,MAAM,SAAS,GAAG,OAAO,CAAC,OAAO,CAAC,SAAS,CAAC;IAC5C,OAAO;;;;;;;;;;;;;;;;;;;;;;;eAuBM,SAAS;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAonCvB,CAAC;AACF,CAAC;AAED,MAAM,UAAU,sBAAsB,CAAC,OAAoB;IACzD,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,OAAO,EAAE,CAAC;QAChC,OAAO,sCAAsC,CAAC;IAChD,CAAC;IAED,OAAO;;;;;;;;;;;;;;;;;;;;;;;;CAwBR,CAAC;AACF,CAAC"}