docdex 0.2.23 → 0.2.24

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/assets/agents.md CHANGED
@@ -1,3 +1,12 @@
1
+ ## Priority Directives (Mandatory)
2
+
3
+ - Always use both memory lobes early: docdex_get_profile + docdex_memory_recall; save new facts via docdex_memory_save and new preferences via docdex_save_preference.
4
+ - Maintain agent-specific profile memory: use your agent_id with docdex_get_profile/docdex_save_preference; store generic self-knowledge (role, tooling, preferences).
5
+ - Use impact analysis for every code change: call HTTP /v1/graph/impact?file=... and account for inbound/outbound deps; if unavailable, state it and proceed cautiously.
6
+ - Apply DAG reasoning for planning: prefer dependency graph facts (impact results and /v1/dag/export) to choose the right change order and scope.
7
+ - Use Docdex tools intentionally: docdex_search/symbols/ast for repo truth; docdex_stats/files/repo_inspect/index for index health.
8
+ - When you do not know something, run docdex_web_research (force_web=true). Web research is encouraged by default for non-repo facts and external APIs.
9
+
1
10
  # Docdex Agent Usage Instructions
2
11
 
3
12
  > Context for AI Agents: Docdex is your local-first Dual-Lobe Memory and Code Intelligence daemon. Unlike simple vector stores, it provides structural understanding of code (AST/Graph), persistent behavioral profiles (Agent Memory), and gated web enrichment, all strictly scoped to the local machine.
@@ -33,6 +42,7 @@ Docdex v2.1 introduces a strict separation between "facts" and "preferences." Us
33
42
  - Content: Your persona, user preferences, coding style, tooling constraints.
34
43
  - Example: "Always use Zod for validation," or "User prefers strict TypeScript types."
35
44
  - Tools: docdex_save_preference, docdex_get_profile
45
+ - Agent-specific: Each agent should use its own agent_id and store generic self-knowledge (role, tooling, preferences).
36
46
  - Usage: Use this to "learn" from corrections. If a user corrects your style, save it here so you do not repeat the mistake in a different repo.
37
47
 
38
48
  ## Tool Capabilities (MCP & HTTP)
@@ -46,20 +56,6 @@ Standard retrieval. The daemon automatically handles the waterfall (Local -> Web
46
56
  | docdex_search | Search code, docs, and ingested libraries. Returns ranked snippets. |
47
57
  | docdex_web_research | Explicitly trigger Tier 2 web discovery (DDG + Headless Chrome). Use when you need external docs not present locally. |
48
58
 
49
- Tier 2 discovery providers (in fallback order when DDG HTML fails or is blocked):
50
- - DuckDuckGo Lite
51
- - SearXNG JSON (public instance or self-hosted)
52
- - Google Mobile
53
- - Brave Search API (requires DOCDEX_BRAVE_API_KEY)
54
- - Google Custom Search JSON API (requires DOCDEX_GOOGLE_CSE_API_KEY + DOCDEX_GOOGLE_CSE_CX)
55
- - Bing Web Search API (requires DOCDEX_BING_API_KEY)
56
- - Tavily API (requires DOCDEX_TAVILY_API_KEY)
57
- - Exa API (requires DOCDEX_EXA_API_KEY)
58
-
59
- Defaults and overrides:
60
- - Default endpoints: `https://html.duckduckgo.com/html/`, `https://lite.duckduckgo.com/lite/`, `https://searx.be/search` (built-in fallback list), `https://www.google.com/m`, `https://api.search.brave.com/res/v1/web/search`, `https://www.googleapis.com/customsearch/v1`, `https://api.bing.microsoft.com/v7.0/search`, `https://api.tavily.com/search`, `https://api.exa.ai/search`
61
- - Override envs: `DOCDEX_DDG_BASE_URL`, `DOCDEX_WEB_SEARXNG_URLS` (comma list, or `DOCDEX_SEARXNG_URLS`), `DOCDEX_WEB_GOOGLE_MOBILE_URL`, `DOCDEX_BRAVE_API_KEY` + `DOCDEX_BRAVE_API_URL`, `DOCDEX_GOOGLE_CSE_API_KEY` + `DOCDEX_GOOGLE_CSE_CX` + `DOCDEX_GOOGLE_CSE_API_URL`, `DOCDEX_BING_API_KEY` + `DOCDEX_BING_API_URL`, `DOCDEX_TAVILY_API_KEY` + `DOCDEX_TAVILY_API_URL`, `DOCDEX_EXA_API_KEY` + `DOCDEX_EXA_API_URL`
62
-
63
59
  ### B. Code Intelligence (AST & Graph)
64
60
 
65
61
  Precision tools for structural analysis. Do not rely on text search for definitions or dependencies.
@@ -80,6 +76,17 @@ Precision tools for structural analysis. Do not rely on text search for definiti
80
76
  | docdex_save_preference | Store a global user preference (Style, Tooling, Constraint). |
81
77
  | docdex_get_profile | Retrieve global preferences. |
82
78
 
79
+ ## Quick Tool Map (Often Missed)
80
+
81
+ - docdex_files: List indexed docs with rel_path/doc_id/token_estimate; use to verify indexing coverage.
82
+ - docdex_stats: Show index size, state dir, and last update time.
83
+ - docdex_repo_inspect: Confirm normalized repo root and repo identity mapping.
84
+ - docdex_index: Reindex the full repo or ingest specific files when stale.
85
+ - docdex_search diff: Limit search to working tree, staged, or ref ranges; filter by paths.
86
+ - docdex_web_research knobs: force_web, skip_local_search, repo_only, no_cache, web_limit, llm_filter_local_results, llm_model.
87
+ - HTTP /v1/initialize: Bind a default repo root for MCP when clients omit project_root.
88
+ - HTTP /v1/dag/export: Export the dependency graph for external analysis.
89
+
83
90
  ## Interaction Patterns
84
91
 
85
92
  ### 1. Reasoning Workflow
@@ -107,6 +114,21 @@ If the user asks: "Safe to delete getUser?"
107
114
  - Action: Call GET /v1/graph/impact?file=src/user.ts
108
115
  - Output: Analyze the inbound edges. If the list is not empty, it is unsafe.
109
116
 
117
+ ### 4. Non-Repo Real-World Queries (Web First)
118
+
119
+ If the user asks a non-repo, real-world question (weather, news, general facts), immediately call docdex_web_research with force_web=true.
120
+ - Resolve relative dates ("yesterday", "last week") using system time by default.
121
+ - Do not run docdex_search unless the user explicitly wants repo-local context.
122
+ - Assume web access is allowed unless the user forbids it; if the web call fails, report the failure and ask for a source or permission.
123
+
124
+ ### 5. Failure Handling (Missing Results or Errors)
125
+
126
+ - Ensure project_root or repo_path is set, or call /v1/initialize to bind a default root.
127
+ - Use docdex_repo_inspect to confirm repo identity and normalized root.
128
+ - Use docdex_stats and docdex_files to check whether the index exists and contains files.
129
+ - Reindex with docdex_index (or docdexd index) if the index is stale or empty.
130
+ - Add a repo-local .docdexignore for large generated artifacts or local caches when indexing is slow.
131
+
110
132
  ## Operational Context
111
133
 
112
134
  ### Repository Identification
@@ -563,7 +563,7 @@ function upsertClaudeInstructions(pathname, instructions) {
563
563
  return true;
564
564
  }
565
565
 
566
- function upsertContinueInstructions(pathname, instructions) {
566
+ function upsertContinueJsonInstructions(pathname, instructions) {
567
567
  const { value } = readJson(pathname);
568
568
  if (typeof value !== "object" || value == null || Array.isArray(value)) return false;
569
569
  const merged = mergeInstructionText(value.systemMessage, instructions);
@@ -573,6 +573,198 @@ function upsertContinueInstructions(pathname, instructions) {
573
573
  return true;
574
574
  }
575
575
 
576
+ function countLeadingWhitespace(line) {
577
+ const match = line.match(/^\s*/);
578
+ return match ? match[0].length : 0;
579
+ }
580
+
581
+ function isYamlTopLevelKey(line, baseIndent) {
582
+ const indent = countLeadingWhitespace(line);
583
+ if (indent > baseIndent) return false;
584
+ const trimmed = line.trimStart();
585
+ if (!trimmed || trimmed.startsWith("#") || trimmed.startsWith("-")) return false;
586
+ return trimmed.includes(":");
587
+ }
588
+
589
+ function hasYamlContent(lines) {
590
+ return lines.some((line) => {
591
+ const trimmed = line.trim();
592
+ return trimmed && !trimmed.startsWith("#");
593
+ });
594
+ }
595
+
596
+ function splitInlineYamlList(value) {
597
+ const trimmed = String(value || "").trim();
598
+ if (trimmed === "[]") return [];
599
+ if (!trimmed.startsWith("[") || !trimmed.endsWith("]")) return null;
600
+ const inner = trimmed.slice(1, -1);
601
+ const items = [];
602
+ let current = "";
603
+ let inSingle = false;
604
+ let inDouble = false;
605
+ let escaped = false;
606
+ for (const ch of inner) {
607
+ if (escaped) {
608
+ current += ch;
609
+ escaped = false;
610
+ continue;
611
+ }
612
+ if (ch === "\\\\") {
613
+ escaped = true;
614
+ current += ch;
615
+ continue;
616
+ }
617
+ if (ch === "'" && !inDouble) {
618
+ inSingle = !inSingle;
619
+ current += ch;
620
+ continue;
621
+ }
622
+ if (ch === '"' && !inSingle) {
623
+ inDouble = !inDouble;
624
+ current += ch;
625
+ continue;
626
+ }
627
+ if (ch === "," && !inSingle && !inDouble) {
628
+ const next = current.trim();
629
+ if (next) items.push(next);
630
+ current = "";
631
+ continue;
632
+ }
633
+ current += ch;
634
+ }
635
+ const next = current.trim();
636
+ if (next) items.push(next);
637
+ return items;
638
+ }
639
+
640
+ function inlineRulesToItems(value, itemIndent) {
641
+ const trimmed = String(value || "").trim();
642
+ if (!trimmed) return [];
643
+ const prefix = " ".repeat(itemIndent);
644
+ const split = splitInlineYamlList(trimmed);
645
+ if (split) {
646
+ return split.map((item) => [`${prefix}- ${item}`]).filter((item) => item[0].trim() !== `${prefix}-`);
647
+ }
648
+ return [[`${prefix}- ${trimmed}`]];
649
+ }
650
+
651
+ function buildYamlRuleBlock(itemIndent, instructions) {
652
+ const prefix = " ".repeat(itemIndent);
653
+ const contentPrefix = " ".repeat(itemIndent + 2);
654
+ const lines = [`${prefix}- |`];
655
+ for (const line of String(instructions).split(/\r?\n/)) {
656
+ lines.push(`${contentPrefix}${line}`);
657
+ }
658
+ return lines;
659
+ }
660
+
661
+ function rewriteContinueYamlRules(source, instructions, addDocdex) {
662
+ const lines = String(source || "").split(/\r?\n/);
663
+ const ruleLineRe = /^(\s*)rules\s*:(.*)$/;
664
+ let rulesIndex = -1;
665
+ let rulesIndent = 0;
666
+ let rulesInline = "";
667
+ for (let i = 0; i < lines.length; i += 1) {
668
+ const match = lines[i].match(ruleLineRe);
669
+ if (!match) continue;
670
+ rulesIndex = i;
671
+ rulesIndent = match[1]?.length || 0;
672
+ rulesInline = (match[2] || "").trim();
673
+ if (rulesInline.includes("#")) {
674
+ rulesInline = rulesInline.split("#")[0].trim();
675
+ }
676
+ if (rulesInline.startsWith("#")) rulesInline = "";
677
+ break;
678
+ }
679
+
680
+ if (rulesIndex === -1) {
681
+ if (!addDocdex) return null;
682
+ const trimmed = String(source || "").trimEnd();
683
+ const docdexBlock = buildYamlRuleBlock(2, instructions);
684
+ const prefix = trimmed ? `${trimmed}\n\n` : "";
685
+ return `${prefix}rules:\n${docdexBlock.join("\n")}`;
686
+ }
687
+
688
+ let endIndex = lines.length;
689
+ for (let i = rulesIndex + 1; i < lines.length; i += 1) {
690
+ if (isYamlTopLevelKey(lines[i], rulesIndent)) {
691
+ endIndex = i;
692
+ break;
693
+ }
694
+ }
695
+ const blockLines = lines.slice(rulesIndex + 1, endIndex);
696
+ const preLines = [];
697
+ const items = [];
698
+ let currentItem = [];
699
+ let itemIndent = null;
700
+ let startedItems = false;
701
+
702
+ for (const line of blockLines) {
703
+ const trimmed = line.trimStart();
704
+ const indent = countLeadingWhitespace(line);
705
+ const isItem = trimmed.startsWith("-") && indent > rulesIndent;
706
+ if (isItem) {
707
+ if (itemIndent == null) itemIndent = indent;
708
+ if (indent === itemIndent) {
709
+ if (startedItems && currentItem.length) {
710
+ items.push(currentItem);
711
+ currentItem = [];
712
+ }
713
+ startedItems = true;
714
+ }
715
+ currentItem.push(line);
716
+ continue;
717
+ }
718
+ if (startedItems) {
719
+ currentItem.push(line);
720
+ } else {
721
+ preLines.push(line);
722
+ }
723
+ }
724
+ if (currentItem.length) items.push(currentItem);
725
+
726
+ const inferredIndent = itemIndent == null ? rulesIndent + 2 : itemIndent;
727
+ if (!items.length && rulesInline) {
728
+ items.push(...inlineRulesToItems(rulesInline, inferredIndent));
729
+ rulesInline = "";
730
+ }
731
+
732
+ const keptItems = items.filter((item) => {
733
+ const text = item.join("\n");
734
+ return !(text.includes(DOCDEX_INFO_START_PREFIX) && text.includes(DOCDEX_INFO_END));
735
+ });
736
+
737
+ if (addDocdex) {
738
+ keptItems.push(buildYamlRuleBlock(inferredIndent, instructions));
739
+ }
740
+
741
+ const removeRulesBlock =
742
+ !addDocdex && !keptItems.length && !hasYamlContent(preLines) && !rulesInline;
743
+
744
+ const output = [];
745
+ output.push(...lines.slice(0, rulesIndex));
746
+ if (!removeRulesBlock) {
747
+ output.push(`${" ".repeat(rulesIndent)}rules:`);
748
+ output.push(...preLines);
749
+ for (const item of keptItems) {
750
+ output.push(...item);
751
+ }
752
+ }
753
+ output.push(...lines.slice(endIndex));
754
+ const next = output.join("\n");
755
+ return next === source ? null : next;
756
+ }
757
+
758
+ function upsertContinueYamlRules(pathname, instructions) {
759
+ if (!fs.existsSync(pathname)) return false;
760
+ const normalized = normalizeInstructionText(instructions);
761
+ if (!normalized) return false;
762
+ const current = fs.readFileSync(pathname, "utf8");
763
+ const updated = rewriteContinueYamlRules(current, normalized, true);
764
+ if (!updated) return false;
765
+ return writeTextFile(pathname, updated);
766
+ }
767
+
576
768
  function upsertZedInstructions(pathname, instructions) {
577
769
  const { value } = readJson(pathname);
578
770
  if (typeof value !== "object" || value == null || Array.isArray(value)) return false;
@@ -586,12 +778,56 @@ function upsertZedInstructions(pathname, instructions) {
586
778
  return true;
587
779
  }
588
780
 
589
- function upsertVsCodeInstructions(pathname, instructionsPath) {
781
+ function upsertVsCodeInstructionKey(value, key, instructions) {
782
+ const existing = typeof value[key] === "string" ? value[key] : "";
783
+ const merged = mergeInstructionText(existing, instructions);
784
+ if (!merged || merged === existing) return false;
785
+ value[key] = merged;
786
+ return true;
787
+ }
788
+
789
+ function upsertVsCodeInstructionLocations(value, instructionsDir) {
790
+ const key = "chat.instructionsFilesLocations";
791
+ const location = String(instructionsDir);
792
+ if (value[key] && typeof value[key] === "object" && !Array.isArray(value[key])) {
793
+ if (value[key][location] === true) return false;
794
+ value[key][location] = true;
795
+ return true;
796
+ }
797
+ if (Array.isArray(value[key])) {
798
+ if (value[key].some((entry) => entry === location)) return false;
799
+ value[key].push(location);
800
+ return true;
801
+ }
802
+ if (typeof value[key] === "string") {
803
+ if (value[key] === location) return false;
804
+ value[key] = [value[key], location];
805
+ return true;
806
+ }
807
+ value[key] = { [location]: true };
808
+ return true;
809
+ }
810
+
811
+ function upsertVsCodeInstructions(pathname, instructions, instructionsDir) {
590
812
  const { value } = readJson(pathname);
591
813
  if (typeof value !== "object" || value == null || Array.isArray(value)) return false;
592
- const key = "copilot.chat.codeGeneration.instructions";
593
- if (value[key] === instructionsPath) return false;
594
- value[key] = instructionsPath;
814
+ const normalized = normalizeInstructionText(instructions);
815
+ if (!normalized) return false;
816
+ let updated = false;
817
+ if (upsertVsCodeInstructionKey(value, "github.copilot.chat.codeGeneration.instructions", instructions)) {
818
+ updated = true;
819
+ }
820
+ if (upsertVsCodeInstructionKey(value, "copilot.chat.codeGeneration.instructions", instructions)) {
821
+ updated = true;
822
+ }
823
+ if (value["github.copilot.chat.codeGeneration.useInstructionFiles"] !== true) {
824
+ value["github.copilot.chat.codeGeneration.useInstructionFiles"] = true;
825
+ updated = true;
826
+ }
827
+ if (upsertVsCodeInstructionLocations(value, instructionsDir)) {
828
+ updated = true;
829
+ }
830
+ if (!updated) return false;
595
831
  writeJson(pathname, value);
596
832
  return true;
597
833
  }
@@ -921,6 +1157,12 @@ function clientInstructionPaths() {
921
1157
  const appData = process.env.APPDATA || path.join(home, "AppData", "Roaming");
922
1158
  const userProfile = process.env.USERPROFILE || home;
923
1159
  const vscodeGlobalInstructions = path.join(home, ".vscode", "global_instructions.md");
1160
+ const vscodeInstructionsDir = path.join(home, ".vscode", "instructions");
1161
+ const vscodeInstructionsFile = path.join(vscodeInstructionsDir, "docdex.md");
1162
+ const continueRoot = path.join(userProfile, ".continue");
1163
+ const continueJson = path.join(continueRoot, "config.json");
1164
+ const continueYaml = path.join(continueRoot, "config.yaml");
1165
+ const continueYml = path.join(continueRoot, "config.yml");
924
1166
  const windsurfGlobalRules = path.join(userProfile, ".codeium", "windsurf", "memories", "global_rules.md");
925
1167
  const rooRules = path.join(home, ".roo", "rules", "docdex.md");
926
1168
  const pearaiAgent = path.join(home, ".config", "pearai", "agent.md");
@@ -932,10 +1174,14 @@ function clientInstructionPaths() {
932
1174
  case "win32":
933
1175
  return {
934
1176
  claude: path.join(appData, "Claude", "claude_desktop_config.json"),
935
- continue: path.join(userProfile, ".continue", "config.json"),
1177
+ continue: continueJson,
1178
+ continueYaml,
1179
+ continueYml,
936
1180
  zed: path.join(appData, "Zed", "settings.json"),
937
1181
  vscodeSettings: path.join(appData, "Code", "User", "settings.json"),
938
1182
  vscodeGlobalInstructions,
1183
+ vscodeInstructionsDir,
1184
+ vscodeInstructionsFile,
939
1185
  windsurfGlobalRules,
940
1186
  rooRules,
941
1187
  pearaiAgent,
@@ -947,10 +1193,14 @@ function clientInstructionPaths() {
947
1193
  case "darwin":
948
1194
  return {
949
1195
  claude: path.join(home, "Library", "Application Support", "Claude", "claude_desktop_config.json"),
950
- continue: path.join(home, ".continue", "config.json"),
1196
+ continue: continueJson,
1197
+ continueYaml,
1198
+ continueYml,
951
1199
  zed: path.join(home, ".config", "zed", "settings.json"),
952
1200
  vscodeSettings: path.join(home, "Library", "Application Support", "Code", "User", "settings.json"),
953
1201
  vscodeGlobalInstructions,
1202
+ vscodeInstructionsDir,
1203
+ vscodeInstructionsFile,
954
1204
  windsurfGlobalRules,
955
1205
  rooRules,
956
1206
  pearaiAgent,
@@ -962,10 +1212,14 @@ function clientInstructionPaths() {
962
1212
  default:
963
1213
  return {
964
1214
  claude: path.join(home, ".config", "Claude", "claude_desktop_config.json"),
965
- continue: path.join(home, ".continue", "config.json"),
1215
+ continue: continueJson,
1216
+ continueYaml,
1217
+ continueYml,
966
1218
  zed: path.join(home, ".config", "zed", "settings.json"),
967
1219
  vscodeSettings: path.join(home, ".config", "Code", "User", "settings.json"),
968
1220
  vscodeGlobalInstructions,
1221
+ vscodeInstructionsDir,
1222
+ vscodeInstructionsFile,
969
1223
  windsurfGlobalRules,
970
1224
  rooRules,
971
1225
  pearaiAgent,
@@ -1045,8 +1299,15 @@ function applyAgentInstructions({ logger } = {}) {
1045
1299
  upsertPromptFile(paths.vscodeGlobalInstructions, instructions, { prepend: true })
1046
1300
  );
1047
1301
  }
1048
- if (paths.vscodeSettings && paths.vscodeGlobalInstructions) {
1049
- safeApply("vscode-settings", () => upsertVsCodeInstructions(paths.vscodeSettings, paths.vscodeGlobalInstructions));
1302
+ if (paths.vscodeInstructionsFile) {
1303
+ safeApply("vscode-instructions-file", () =>
1304
+ upsertPromptFile(paths.vscodeInstructionsFile, instructions, { prepend: true })
1305
+ );
1306
+ }
1307
+ if (paths.vscodeSettings && paths.vscodeInstructionsDir) {
1308
+ safeApply("vscode-settings", () =>
1309
+ upsertVsCodeInstructions(paths.vscodeSettings, instructions, paths.vscodeInstructionsDir)
1310
+ );
1050
1311
  }
1051
1312
  if (paths.windsurfGlobalRules) {
1052
1313
  safeApply("windsurf", () => upsertPromptFile(paths.windsurfGlobalRules, instructions, { prepend: true }));
@@ -1060,18 +1321,25 @@ function applyAgentInstructions({ logger } = {}) {
1060
1321
  if (paths.claude) {
1061
1322
  safeApply("claude", () => upsertClaudeInstructions(paths.claude, instructions));
1062
1323
  }
1063
- if (paths.continue) {
1064
- safeApply("continue", () => upsertContinueInstructions(paths.continue, instructions));
1324
+ const continueYamlExists =
1325
+ (paths.continueYaml && fs.existsSync(paths.continueYaml)) ||
1326
+ (paths.continueYml && fs.existsSync(paths.continueYml));
1327
+ if (continueYamlExists) {
1328
+ if (paths.continueYaml && fs.existsSync(paths.continueYaml)) {
1329
+ safeApply("continue-yaml", () => upsertContinueYamlRules(paths.continueYaml, instructions));
1330
+ }
1331
+ if (paths.continueYml && fs.existsSync(paths.continueYml)) {
1332
+ safeApply("continue-yml", () => upsertContinueYamlRules(paths.continueYml, instructions));
1333
+ }
1334
+ if (paths.continue && fs.existsSync(paths.continue)) {
1335
+ safeApply("continue-json", () => upsertContinueJsonInstructions(paths.continue, instructions));
1336
+ }
1337
+ } else if (paths.continue) {
1338
+ safeApply("continue-json", () => upsertContinueJsonInstructions(paths.continue, instructions));
1065
1339
  }
1066
1340
  if (paths.zed) {
1067
1341
  safeApply("zed", () => upsertZedInstructions(paths.zed, instructions));
1068
1342
  }
1069
- if (paths.aiderConfig) {
1070
- safeApply("aider", () => upsertYamlInstruction(paths.aiderConfig, "system-prompt", instructions));
1071
- }
1072
- if (paths.gooseConfig) {
1073
- safeApply("goose", () => upsertYamlInstruction(paths.gooseConfig, "instructions", instructions));
1074
- }
1075
1343
  if (paths.openInterpreterConfig) {
1076
1344
  safeApply("open-interpreter", () =>
1077
1345
  upsertYamlInstruction(paths.openInterpreterConfig, "system_message", instructions)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "docdex",
3
- "version": "0.2.23",
3
+ "version": "0.2.24",
4
4
  "mcpName": "io.github.bekirdag/docdex",
5
5
  "description": "Local-first documentation and code indexer with HTTP/MCP search, AST, and agent memory.",
6
6
  "bin": {