opencode-codebase-index 0.4.0 → 0.5.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/README.md +55 -3
- package/dist/cli.cjs +4862 -0
- package/dist/cli.cjs.map +1 -0
- package/dist/cli.js +4867 -0
- package/dist/cli.js.map +1 -0
- package/dist/index.cjs +697 -355
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +699 -356
- package/dist/index.js.map +1 -1
- package/native/codebase-index-native.darwin-arm64.node +0 -0
- package/native/codebase-index-native.darwin-x64.node +0 -0
- package/native/codebase-index-native.linux-arm64-gnu.node +0 -0
- package/native/codebase-index-native.linux-x64-gnu.node +0 -0
- package/native/codebase-index-native.win32-x64-msvc.node +0 -0
- package/package.json +23 -4
package/dist/index.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
// opencode-codebase-index - Semantic codebase search for OpenCode
|
|
2
|
+
import { createRequire } from 'module'; const require = createRequire(import.meta.url);
|
|
2
3
|
var __create = Object.create;
|
|
3
4
|
var __defProp = Object.defineProperty;
|
|
4
5
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
@@ -657,7 +658,7 @@ import * as path8 from "path";
|
|
|
657
658
|
import * as os3 from "os";
|
|
658
659
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
659
660
|
|
|
660
|
-
// src/config/
|
|
661
|
+
// src/config/constants.ts
|
|
661
662
|
var DEFAULT_INCLUDE = [
|
|
662
663
|
"**/*.{ts,tsx,js,jsx,mjs,cjs}",
|
|
663
664
|
"**/*.{py,pyi}",
|
|
@@ -685,6 +686,80 @@ var DEFAULT_EXCLUDE = [
|
|
|
685
686
|
"**/.nuxt/**",
|
|
686
687
|
"**/.opencode/**"
|
|
687
688
|
];
|
|
689
|
+
var EMBEDDING_MODELS = {
|
|
690
|
+
"google": {
|
|
691
|
+
// `text-embedding-004` is DEPRECATED - https://ai.google.dev/gemini-api/docs/deprecations
|
|
692
|
+
"text-embedding-005": {
|
|
693
|
+
provider: "google",
|
|
694
|
+
model: "text-embedding-005",
|
|
695
|
+
dimensions: 768,
|
|
696
|
+
maxTokens: 2048,
|
|
697
|
+
costPer1MTokens: 0.025,
|
|
698
|
+
taskAble: false
|
|
699
|
+
// Note: on reality, this model allows for task-specific embeddings. See: https://docs.cloud.google.com/vertex-ai/generative-ai/docs/embeddings/task-types
|
|
700
|
+
},
|
|
701
|
+
"gemini-embedding-001": {
|
|
702
|
+
provider: "google",
|
|
703
|
+
model: "gemini-embedding-001",
|
|
704
|
+
// Native output is 3072D, but we use Matryoshka truncation via outputDimensionality
|
|
705
|
+
// to reduce to 1536D for better storage/search efficiency with minimal quality loss.
|
|
706
|
+
// Google recommends 768, 1536, or 3072. See: https://ai.google.dev/gemini-api/docs/embeddings
|
|
707
|
+
dimensions: 1536,
|
|
708
|
+
maxTokens: 2048,
|
|
709
|
+
costPer1MTokens: 0.15,
|
|
710
|
+
taskAble: true
|
|
711
|
+
}
|
|
712
|
+
},
|
|
713
|
+
"openai": {
|
|
714
|
+
"text-embedding-3-small": {
|
|
715
|
+
provider: "openai",
|
|
716
|
+
model: "text-embedding-3-small",
|
|
717
|
+
dimensions: 1536,
|
|
718
|
+
maxTokens: 8191,
|
|
719
|
+
costPer1MTokens: 0.02
|
|
720
|
+
},
|
|
721
|
+
"text-embedding-3-large": {
|
|
722
|
+
provider: "openai",
|
|
723
|
+
model: "text-embedding-3-large",
|
|
724
|
+
dimensions: 3072,
|
|
725
|
+
maxTokens: 8191,
|
|
726
|
+
costPer1MTokens: 0.13
|
|
727
|
+
}
|
|
728
|
+
},
|
|
729
|
+
"ollama": {
|
|
730
|
+
"nomic-embed-text": {
|
|
731
|
+
provider: "ollama",
|
|
732
|
+
model: "nomic-embed-text",
|
|
733
|
+
dimensions: 768,
|
|
734
|
+
maxTokens: 8192,
|
|
735
|
+
costPer1MTokens: 0
|
|
736
|
+
},
|
|
737
|
+
"mxbai-embed-large": {
|
|
738
|
+
provider: "ollama",
|
|
739
|
+
model: "mxbai-embed-large",
|
|
740
|
+
dimensions: 1024,
|
|
741
|
+
maxTokens: 512,
|
|
742
|
+
costPer1MTokens: 0
|
|
743
|
+
}
|
|
744
|
+
},
|
|
745
|
+
"github-copilot": {
|
|
746
|
+
"text-embedding-3-small": {
|
|
747
|
+
provider: "github-copilot",
|
|
748
|
+
model: "text-embedding-3-small",
|
|
749
|
+
dimensions: 1536,
|
|
750
|
+
maxTokens: 8191,
|
|
751
|
+
costPer1MTokens: 0
|
|
752
|
+
}
|
|
753
|
+
}
|
|
754
|
+
};
|
|
755
|
+
var DEFAULT_PROVIDER_MODELS = {
|
|
756
|
+
"github-copilot": "text-embedding-3-small",
|
|
757
|
+
"openai": "text-embedding-3-small",
|
|
758
|
+
"google": "text-embedding-005",
|
|
759
|
+
"ollama": "nomic-embed-text"
|
|
760
|
+
};
|
|
761
|
+
|
|
762
|
+
// src/config/schema.ts
|
|
688
763
|
function getDefaultIndexingConfig() {
|
|
689
764
|
return {
|
|
690
765
|
autoIndex: false,
|
|
@@ -696,7 +771,8 @@ function getDefaultIndexingConfig() {
|
|
|
696
771
|
retryDelayMs: 1e3,
|
|
697
772
|
autoGc: true,
|
|
698
773
|
gcIntervalDays: 7,
|
|
699
|
-
gcOrphanThreshold: 100
|
|
774
|
+
gcOrphanThreshold: 100,
|
|
775
|
+
requireProjectMarker: true
|
|
700
776
|
};
|
|
701
777
|
}
|
|
702
778
|
function getDefaultSearchConfig() {
|
|
@@ -720,11 +796,13 @@ function getDefaultDebugConfig() {
|
|
|
720
796
|
metrics: true
|
|
721
797
|
};
|
|
722
798
|
}
|
|
723
|
-
var VALID_PROVIDERS = ["auto", "github-copilot", "openai", "google", "ollama"];
|
|
724
799
|
var VALID_SCOPES = ["project", "global"];
|
|
725
800
|
var VALID_LOG_LEVELS = ["error", "warn", "info", "debug"];
|
|
726
801
|
function isValidProvider(value) {
|
|
727
|
-
return typeof value === "string" &&
|
|
802
|
+
return typeof value === "string" && Object.keys(EMBEDDING_MODELS).includes(value);
|
|
803
|
+
}
|
|
804
|
+
function isValidModel(value, provider) {
|
|
805
|
+
return typeof value === "string" && Object.keys(EMBEDDING_MODELS[provider]).includes(value);
|
|
728
806
|
}
|
|
729
807
|
function isValidScope(value) {
|
|
730
808
|
return typeof value === "string" && VALID_SCOPES.includes(value);
|
|
@@ -751,7 +829,8 @@ function parseConfig(raw) {
|
|
|
751
829
|
retryDelayMs: typeof rawIndexing.retryDelayMs === "number" ? rawIndexing.retryDelayMs : defaultIndexing.retryDelayMs,
|
|
752
830
|
autoGc: typeof rawIndexing.autoGc === "boolean" ? rawIndexing.autoGc : defaultIndexing.autoGc,
|
|
753
831
|
gcIntervalDays: typeof rawIndexing.gcIntervalDays === "number" ? Math.max(1, rawIndexing.gcIntervalDays) : defaultIndexing.gcIntervalDays,
|
|
754
|
-
gcOrphanThreshold: typeof rawIndexing.gcOrphanThreshold === "number" ? Math.max(0, rawIndexing.gcOrphanThreshold) : defaultIndexing.gcOrphanThreshold
|
|
832
|
+
gcOrphanThreshold: typeof rawIndexing.gcOrphanThreshold === "number" ? Math.max(0, rawIndexing.gcOrphanThreshold) : defaultIndexing.gcOrphanThreshold,
|
|
833
|
+
requireProjectMarker: typeof rawIndexing.requireProjectMarker === "boolean" ? rawIndexing.requireProjectMarker : defaultIndexing.requireProjectMarker
|
|
755
834
|
};
|
|
756
835
|
const rawSearch = input.search && typeof input.search === "object" ? input.search : {};
|
|
757
836
|
const search = {
|
|
@@ -772,9 +851,19 @@ function parseConfig(raw) {
|
|
|
772
851
|
logBranch: typeof rawDebug.logBranch === "boolean" ? rawDebug.logBranch : defaultDebug.logBranch,
|
|
773
852
|
metrics: typeof rawDebug.metrics === "boolean" ? rawDebug.metrics : defaultDebug.metrics
|
|
774
853
|
};
|
|
854
|
+
let embeddingProvider;
|
|
855
|
+
let embeddingModel = void 0;
|
|
856
|
+
if (isValidProvider(input.embeddingProvider)) {
|
|
857
|
+
embeddingProvider = input.embeddingProvider;
|
|
858
|
+
if (input.embeddingModel) {
|
|
859
|
+
embeddingModel = isValidModel(input.embeddingModel, embeddingProvider) ? input.embeddingModel : DEFAULT_PROVIDER_MODELS[embeddingProvider];
|
|
860
|
+
}
|
|
861
|
+
} else {
|
|
862
|
+
embeddingProvider = "auto";
|
|
863
|
+
}
|
|
775
864
|
return {
|
|
776
|
-
embeddingProvider
|
|
777
|
-
embeddingModel
|
|
865
|
+
embeddingProvider,
|
|
866
|
+
embeddingModel,
|
|
778
867
|
scope: isValidScope(input.scope) ? input.scope : "project",
|
|
779
868
|
include: isStringArray(input.include) ? input.include : DEFAULT_INCLUDE,
|
|
780
869
|
exclude: isStringArray(input.exclude) ? input.exclude : DEFAULT_EXCLUDE,
|
|
@@ -783,67 +872,15 @@ function parseConfig(raw) {
|
|
|
783
872
|
debug
|
|
784
873
|
};
|
|
785
874
|
}
|
|
786
|
-
var EMBEDDING_MODELS = {
|
|
787
|
-
"github-copilot/text-embedding-3-small": {
|
|
788
|
-
provider: "github-copilot",
|
|
789
|
-
model: "text-embedding-3-small",
|
|
790
|
-
dimensions: 1536,
|
|
791
|
-
maxTokens: 8191,
|
|
792
|
-
costPer1MTokens: 0
|
|
793
|
-
},
|
|
794
|
-
"openai/text-embedding-3-small": {
|
|
795
|
-
provider: "openai",
|
|
796
|
-
model: "text-embedding-3-small",
|
|
797
|
-
dimensions: 1536,
|
|
798
|
-
maxTokens: 8191,
|
|
799
|
-
costPer1MTokens: 0.02
|
|
800
|
-
},
|
|
801
|
-
"openai/text-embedding-3-large": {
|
|
802
|
-
provider: "openai",
|
|
803
|
-
model: "text-embedding-3-large",
|
|
804
|
-
dimensions: 3072,
|
|
805
|
-
maxTokens: 8191,
|
|
806
|
-
costPer1MTokens: 0.13
|
|
807
|
-
},
|
|
808
|
-
"google/text-embedding-004": {
|
|
809
|
-
provider: "google",
|
|
810
|
-
model: "text-embedding-004",
|
|
811
|
-
dimensions: 768,
|
|
812
|
-
maxTokens: 2048,
|
|
813
|
-
costPer1MTokens: 0
|
|
814
|
-
},
|
|
815
|
-
"ollama/nomic-embed-text": {
|
|
816
|
-
provider: "ollama",
|
|
817
|
-
model: "nomic-embed-text",
|
|
818
|
-
dimensions: 768,
|
|
819
|
-
maxTokens: 8192,
|
|
820
|
-
costPer1MTokens: 0
|
|
821
|
-
},
|
|
822
|
-
"ollama/mxbai-embed-large": {
|
|
823
|
-
provider: "ollama",
|
|
824
|
-
model: "mxbai-embed-large",
|
|
825
|
-
dimensions: 1024,
|
|
826
|
-
maxTokens: 512,
|
|
827
|
-
costPer1MTokens: 0
|
|
828
|
-
}
|
|
829
|
-
};
|
|
830
875
|
function getDefaultModelForProvider(provider) {
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
case "openai":
|
|
835
|
-
return EMBEDDING_MODELS["openai/text-embedding-3-small"];
|
|
836
|
-
case "google":
|
|
837
|
-
return EMBEDDING_MODELS["google/text-embedding-004"];
|
|
838
|
-
case "ollama":
|
|
839
|
-
return EMBEDDING_MODELS["ollama/nomic-embed-text"];
|
|
840
|
-
default:
|
|
841
|
-
return EMBEDDING_MODELS["github-copilot/text-embedding-3-small"];
|
|
842
|
-
}
|
|
876
|
+
const models = EMBEDDING_MODELS[provider];
|
|
877
|
+
const providerDefault = DEFAULT_PROVIDER_MODELS[provider];
|
|
878
|
+
return models[providerDefault];
|
|
843
879
|
}
|
|
880
|
+
var availableProviders = Object.keys(EMBEDDING_MODELS);
|
|
844
881
|
|
|
845
882
|
// src/indexer/index.ts
|
|
846
|
-
import { existsSync as existsSync4, readFileSync as readFileSync4, writeFileSync, promises as fsPromises2 } from "fs";
|
|
883
|
+
import { existsSync as existsSync4, readFileSync as readFileSync4, writeFileSync, renameSync, unlinkSync, promises as fsPromises2 } from "fs";
|
|
847
884
|
import * as path5 from "path";
|
|
848
885
|
import { performance as performance2 } from "perf_hooks";
|
|
849
886
|
|
|
@@ -869,7 +906,7 @@ function pTimeout(promise, options) {
|
|
|
869
906
|
} = options;
|
|
870
907
|
let timer;
|
|
871
908
|
let abortHandler;
|
|
872
|
-
const wrappedPromise = new Promise((
|
|
909
|
+
const wrappedPromise = new Promise((resolve5, reject) => {
|
|
873
910
|
if (typeof milliseconds !== "number" || Math.sign(milliseconds) !== 1) {
|
|
874
911
|
throw new TypeError(`Expected \`milliseconds\` to be a positive number, got \`${milliseconds}\``);
|
|
875
912
|
}
|
|
@@ -883,7 +920,7 @@ function pTimeout(promise, options) {
|
|
|
883
920
|
};
|
|
884
921
|
signal.addEventListener("abort", abortHandler, { once: true });
|
|
885
922
|
}
|
|
886
|
-
promise.then(
|
|
923
|
+
promise.then(resolve5, reject);
|
|
887
924
|
if (milliseconds === Number.POSITIVE_INFINITY) {
|
|
888
925
|
return;
|
|
889
926
|
}
|
|
@@ -891,7 +928,7 @@ function pTimeout(promise, options) {
|
|
|
891
928
|
timer = customTimers.setTimeout.call(void 0, () => {
|
|
892
929
|
if (fallback) {
|
|
893
930
|
try {
|
|
894
|
-
|
|
931
|
+
resolve5(fallback());
|
|
895
932
|
} catch (error) {
|
|
896
933
|
reject(error);
|
|
897
934
|
}
|
|
@@ -901,7 +938,7 @@ function pTimeout(promise, options) {
|
|
|
901
938
|
promise.cancel();
|
|
902
939
|
}
|
|
903
940
|
if (message === false) {
|
|
904
|
-
|
|
941
|
+
resolve5();
|
|
905
942
|
} else if (message instanceof Error) {
|
|
906
943
|
reject(message);
|
|
907
944
|
} else {
|
|
@@ -1291,7 +1328,7 @@ var PQueue = class extends import_index.default {
|
|
|
1291
1328
|
// Assign unique ID if not provided
|
|
1292
1329
|
id: options.id ?? (this.#idAssigner++).toString()
|
|
1293
1330
|
};
|
|
1294
|
-
return new Promise((
|
|
1331
|
+
return new Promise((resolve5, reject) => {
|
|
1295
1332
|
const taskSymbol = /* @__PURE__ */ Symbol(`task-${options.id}`);
|
|
1296
1333
|
this.#queue.enqueue(async () => {
|
|
1297
1334
|
this.#pending++;
|
|
@@ -1329,7 +1366,7 @@ var PQueue = class extends import_index.default {
|
|
|
1329
1366
|
})]);
|
|
1330
1367
|
}
|
|
1331
1368
|
const result = await operation;
|
|
1332
|
-
|
|
1369
|
+
resolve5(result);
|
|
1333
1370
|
this.emit("completed", result);
|
|
1334
1371
|
} catch (error) {
|
|
1335
1372
|
reject(error);
|
|
@@ -1486,13 +1523,13 @@ var PQueue = class extends import_index.default {
|
|
|
1486
1523
|
});
|
|
1487
1524
|
}
|
|
1488
1525
|
async #onEvent(event, filter) {
|
|
1489
|
-
return new Promise((
|
|
1526
|
+
return new Promise((resolve5) => {
|
|
1490
1527
|
const listener = () => {
|
|
1491
1528
|
if (filter && !filter()) {
|
|
1492
1529
|
return;
|
|
1493
1530
|
}
|
|
1494
1531
|
this.off(event, listener);
|
|
1495
|
-
|
|
1532
|
+
resolve5();
|
|
1496
1533
|
};
|
|
1497
1534
|
this.on(event, listener);
|
|
1498
1535
|
});
|
|
@@ -1777,7 +1814,7 @@ async function onAttemptFailure({ error, attemptNumber, retriesConsumed, startTi
|
|
|
1777
1814
|
const finalDelay = Math.min(delayTime, remainingTime);
|
|
1778
1815
|
options.signal?.throwIfAborted();
|
|
1779
1816
|
if (finalDelay > 0) {
|
|
1780
|
-
await new Promise((
|
|
1817
|
+
await new Promise((resolve5, reject) => {
|
|
1781
1818
|
const onAbort = () => {
|
|
1782
1819
|
clearTimeout(timeoutToken);
|
|
1783
1820
|
options.signal?.removeEventListener("abort", onAbort);
|
|
@@ -1785,7 +1822,7 @@ async function onAttemptFailure({ error, attemptNumber, retriesConsumed, startTi
|
|
|
1785
1822
|
};
|
|
1786
1823
|
const timeoutToken = setTimeout(() => {
|
|
1787
1824
|
options.signal?.removeEventListener("abort", onAbort);
|
|
1788
|
-
|
|
1825
|
+
resolve5();
|
|
1789
1826
|
}, finalDelay);
|
|
1790
1827
|
if (options.unref) {
|
|
1791
1828
|
timeoutToken.unref?.();
|
|
@@ -1849,12 +1886,6 @@ async function pRetry(input, options = {}) {
|
|
|
1849
1886
|
import { existsSync, readFileSync } from "fs";
|
|
1850
1887
|
import * as path from "path";
|
|
1851
1888
|
import * as os from "os";
|
|
1852
|
-
var EMBEDDING_CAPABLE_PROVIDERS = [
|
|
1853
|
-
"github-copilot",
|
|
1854
|
-
"openai",
|
|
1855
|
-
"google",
|
|
1856
|
-
"ollama"
|
|
1857
|
-
];
|
|
1858
1889
|
function getOpenCodeAuthPath() {
|
|
1859
1890
|
return path.join(os.homedir(), ".local", "share", "opencode", "auth.json");
|
|
1860
1891
|
}
|
|
@@ -1868,21 +1899,34 @@ function loadOpenCodeAuth() {
|
|
|
1868
1899
|
}
|
|
1869
1900
|
return {};
|
|
1870
1901
|
}
|
|
1871
|
-
async function detectEmbeddingProvider(preferredProvider) {
|
|
1872
|
-
|
|
1873
|
-
|
|
1874
|
-
if (
|
|
1902
|
+
async function detectEmbeddingProvider(preferredProvider, model) {
|
|
1903
|
+
const credentials = await getProviderCredentials(preferredProvider);
|
|
1904
|
+
if (credentials) {
|
|
1905
|
+
if (!model) {
|
|
1875
1906
|
return {
|
|
1876
1907
|
provider: preferredProvider,
|
|
1877
1908
|
credentials,
|
|
1878
1909
|
modelInfo: getDefaultModelForProvider(preferredProvider)
|
|
1879
1910
|
};
|
|
1880
1911
|
}
|
|
1881
|
-
|
|
1882
|
-
|
|
1883
|
-
|
|
1912
|
+
if (!isValidModel(model, preferredProvider)) {
|
|
1913
|
+
throw new Error(
|
|
1914
|
+
`Model '${model}' is not supported by provider '${preferredProvider}'`
|
|
1915
|
+
);
|
|
1916
|
+
}
|
|
1917
|
+
const providerModels = EMBEDDING_MODELS[preferredProvider];
|
|
1918
|
+
return {
|
|
1919
|
+
provider: preferredProvider,
|
|
1920
|
+
credentials,
|
|
1921
|
+
modelInfo: providerModels[model]
|
|
1922
|
+
};
|
|
1884
1923
|
}
|
|
1885
|
-
|
|
1924
|
+
throw new Error(
|
|
1925
|
+
`Preferred provider '${preferredProvider}' is not configured or authenticated`
|
|
1926
|
+
);
|
|
1927
|
+
}
|
|
1928
|
+
async function tryDetectProvider() {
|
|
1929
|
+
for (const provider of availableProviders) {
|
|
1886
1930
|
const credentials = await getProviderCredentials(provider);
|
|
1887
1931
|
if (credentials) {
|
|
1888
1932
|
return {
|
|
@@ -1893,7 +1937,7 @@ async function detectEmbeddingProvider(preferredProvider) {
|
|
|
1893
1937
|
}
|
|
1894
1938
|
}
|
|
1895
1939
|
throw new Error(
|
|
1896
|
-
`No embedding-capable provider found. Please authenticate with OpenCode using one of: ${
|
|
1940
|
+
`No embedding-capable provider found. Please authenticate with OpenCode using one of: ${availableProviders.join(", ")}.`
|
|
1897
1941
|
);
|
|
1898
1942
|
}
|
|
1899
1943
|
async function getProviderCredentials(provider) {
|
|
@@ -1991,18 +2035,20 @@ function getProviderDisplayName(provider) {
|
|
|
1991
2035
|
}
|
|
1992
2036
|
|
|
1993
2037
|
// src/embeddings/provider.ts
|
|
1994
|
-
function createEmbeddingProvider(
|
|
1995
|
-
switch (
|
|
2038
|
+
function createEmbeddingProvider(configuredProviderInfo) {
|
|
2039
|
+
switch (configuredProviderInfo.provider) {
|
|
1996
2040
|
case "github-copilot":
|
|
1997
|
-
return new GitHubCopilotEmbeddingProvider(credentials, modelInfo);
|
|
2041
|
+
return new GitHubCopilotEmbeddingProvider(configuredProviderInfo.credentials, configuredProviderInfo.modelInfo);
|
|
1998
2042
|
case "openai":
|
|
1999
|
-
return new OpenAIEmbeddingProvider(credentials, modelInfo);
|
|
2043
|
+
return new OpenAIEmbeddingProvider(configuredProviderInfo.credentials, configuredProviderInfo.modelInfo);
|
|
2000
2044
|
case "google":
|
|
2001
|
-
return new GoogleEmbeddingProvider(credentials, modelInfo);
|
|
2045
|
+
return new GoogleEmbeddingProvider(configuredProviderInfo.credentials, configuredProviderInfo.modelInfo);
|
|
2002
2046
|
case "ollama":
|
|
2003
|
-
return new OllamaEmbeddingProvider(credentials, modelInfo);
|
|
2004
|
-
default:
|
|
2005
|
-
|
|
2047
|
+
return new OllamaEmbeddingProvider(configuredProviderInfo.credentials, configuredProviderInfo.modelInfo);
|
|
2048
|
+
default: {
|
|
2049
|
+
const _exhaustive = configuredProviderInfo;
|
|
2050
|
+
throw new Error(`Unsupported embedding provider: ${_exhaustive.provider}`);
|
|
2051
|
+
}
|
|
2006
2052
|
}
|
|
2007
2053
|
}
|
|
2008
2054
|
var GitHubCopilotEmbeddingProvider = class {
|
|
@@ -2016,8 +2062,15 @@ var GitHubCopilotEmbeddingProvider = class {
|
|
|
2016
2062
|
}
|
|
2017
2063
|
return this.credentials.refreshToken;
|
|
2018
2064
|
}
|
|
2019
|
-
async
|
|
2020
|
-
const result = await this.embedBatch([
|
|
2065
|
+
async embedQuery(query) {
|
|
2066
|
+
const result = await this.embedBatch([query]);
|
|
2067
|
+
return {
|
|
2068
|
+
embedding: result.embeddings[0],
|
|
2069
|
+
tokensUsed: result.totalTokensUsed
|
|
2070
|
+
};
|
|
2071
|
+
}
|
|
2072
|
+
async embedDocument(document) {
|
|
2073
|
+
const result = await this.embedBatch([document]);
|
|
2021
2074
|
return {
|
|
2022
2075
|
embedding: result.embeddings[0],
|
|
2023
2076
|
tokensUsed: result.totalTokensUsed
|
|
@@ -2057,8 +2110,15 @@ var OpenAIEmbeddingProvider = class {
|
|
|
2057
2110
|
this.credentials = credentials;
|
|
2058
2111
|
this.modelInfo = modelInfo;
|
|
2059
2112
|
}
|
|
2060
|
-
async
|
|
2061
|
-
const result = await this.embedBatch([
|
|
2113
|
+
async embedQuery(query) {
|
|
2114
|
+
const result = await this.embedBatch([query]);
|
|
2115
|
+
return {
|
|
2116
|
+
embedding: result.embeddings[0],
|
|
2117
|
+
tokensUsed: result.totalTokensUsed
|
|
2118
|
+
};
|
|
2119
|
+
}
|
|
2120
|
+
async embedDocument(document) {
|
|
2121
|
+
const result = await this.embedBatch([document]);
|
|
2062
2122
|
return {
|
|
2063
2123
|
embedding: result.embeddings[0],
|
|
2064
2124
|
tokensUsed: result.totalTokensUsed
|
|
@@ -2090,33 +2150,61 @@ var OpenAIEmbeddingProvider = class {
|
|
|
2090
2150
|
return this.modelInfo;
|
|
2091
2151
|
}
|
|
2092
2152
|
};
|
|
2093
|
-
var GoogleEmbeddingProvider = class {
|
|
2153
|
+
var GoogleEmbeddingProvider = class _GoogleEmbeddingProvider {
|
|
2094
2154
|
constructor(credentials, modelInfo) {
|
|
2095
2155
|
this.credentials = credentials;
|
|
2096
2156
|
this.modelInfo = modelInfo;
|
|
2097
2157
|
}
|
|
2098
|
-
|
|
2099
|
-
|
|
2158
|
+
static BATCH_SIZE = 20;
|
|
2159
|
+
async embedQuery(query) {
|
|
2160
|
+
const taskType = this.modelInfo.taskAble ? "CODE_RETRIEVAL_QUERY" : void 0;
|
|
2161
|
+
const result = await this.embedWithTaskType([query], taskType);
|
|
2162
|
+
return {
|
|
2163
|
+
embedding: result.embeddings[0],
|
|
2164
|
+
tokensUsed: result.totalTokensUsed
|
|
2165
|
+
};
|
|
2166
|
+
}
|
|
2167
|
+
async embedDocument(document) {
|
|
2168
|
+
const taskType = this.modelInfo.taskAble ? "RETRIEVAL_DOCUMENT" : void 0;
|
|
2169
|
+
const result = await this.embedWithTaskType([document], taskType);
|
|
2100
2170
|
return {
|
|
2101
2171
|
embedding: result.embeddings[0],
|
|
2102
2172
|
tokensUsed: result.totalTokensUsed
|
|
2103
2173
|
};
|
|
2104
2174
|
}
|
|
2105
2175
|
async embedBatch(texts) {
|
|
2106
|
-
const
|
|
2107
|
-
|
|
2176
|
+
const taskType = this.modelInfo.taskAble ? "RETRIEVAL_DOCUMENT" : void 0;
|
|
2177
|
+
return this.embedWithTaskType(texts, taskType);
|
|
2178
|
+
}
|
|
2179
|
+
/**
|
|
2180
|
+
* Embeds texts using the Google embedContent API.
|
|
2181
|
+
* Sends multiple texts as parts in batched requests (up to BATCH_SIZE per call).
|
|
2182
|
+
* When taskType is provided (gemini-embedding-001), includes it in the request
|
|
2183
|
+
* for task-specific embedding optimization.
|
|
2184
|
+
*/
|
|
2185
|
+
async embedWithTaskType(texts, taskType) {
|
|
2186
|
+
const batches = [];
|
|
2187
|
+
for (let i = 0; i < texts.length; i += _GoogleEmbeddingProvider.BATCH_SIZE) {
|
|
2188
|
+
batches.push(texts.slice(i, i + _GoogleEmbeddingProvider.BATCH_SIZE));
|
|
2189
|
+
}
|
|
2190
|
+
const batchResults = await Promise.all(
|
|
2191
|
+
batches.map(async (batch) => {
|
|
2192
|
+
const requests = batch.map((text) => ({
|
|
2193
|
+
model: `models/${this.modelInfo.model}`,
|
|
2194
|
+
content: {
|
|
2195
|
+
parts: [{ text }]
|
|
2196
|
+
},
|
|
2197
|
+
taskType,
|
|
2198
|
+
outputDimensionality: this.modelInfo.dimensions
|
|
2199
|
+
}));
|
|
2108
2200
|
const response = await fetch(
|
|
2109
|
-
`${this.credentials.baseUrl}/models/${this.modelInfo.model}:
|
|
2201
|
+
`${this.credentials.baseUrl}/models/${this.modelInfo.model}:batchEmbedContents?key=${this.credentials.apiKey}`,
|
|
2110
2202
|
{
|
|
2111
2203
|
method: "POST",
|
|
2112
2204
|
headers: {
|
|
2113
2205
|
"Content-Type": "application/json"
|
|
2114
2206
|
},
|
|
2115
|
-
body: JSON.stringify({
|
|
2116
|
-
content: {
|
|
2117
|
-
parts: [{ text }]
|
|
2118
|
-
}
|
|
2119
|
-
})
|
|
2207
|
+
body: JSON.stringify({ requests })
|
|
2120
2208
|
}
|
|
2121
2209
|
);
|
|
2122
2210
|
if (!response.ok) {
|
|
@@ -2125,14 +2213,14 @@ var GoogleEmbeddingProvider = class {
|
|
|
2125
2213
|
}
|
|
2126
2214
|
const data = await response.json();
|
|
2127
2215
|
return {
|
|
2128
|
-
|
|
2129
|
-
tokensUsed: Math.ceil(text.length / 4)
|
|
2216
|
+
embeddings: data.embeddings.map((e) => e.values),
|
|
2217
|
+
tokensUsed: batch.reduce((sum, text) => sum + Math.ceil(text.length / 4), 0)
|
|
2130
2218
|
};
|
|
2131
2219
|
})
|
|
2132
2220
|
);
|
|
2133
2221
|
return {
|
|
2134
|
-
embeddings:
|
|
2135
|
-
totalTokensUsed:
|
|
2222
|
+
embeddings: batchResults.flatMap((r) => r.embeddings),
|
|
2223
|
+
totalTokensUsed: batchResults.reduce((sum, r) => sum + r.tokensUsed, 0)
|
|
2136
2224
|
};
|
|
2137
2225
|
}
|
|
2138
2226
|
getModelInfo() {
|
|
@@ -2144,29 +2232,44 @@ var OllamaEmbeddingProvider = class {
|
|
|
2144
2232
|
this.credentials = credentials;
|
|
2145
2233
|
this.modelInfo = modelInfo;
|
|
2146
2234
|
}
|
|
2147
|
-
async
|
|
2148
|
-
const
|
|
2149
|
-
method: "POST",
|
|
2150
|
-
headers: {
|
|
2151
|
-
"Content-Type": "application/json"
|
|
2152
|
-
},
|
|
2153
|
-
body: JSON.stringify({
|
|
2154
|
-
model: this.modelInfo.model,
|
|
2155
|
-
prompt: text
|
|
2156
|
-
})
|
|
2157
|
-
});
|
|
2158
|
-
if (!response.ok) {
|
|
2159
|
-
const error = await response.text();
|
|
2160
|
-
throw new Error(`Ollama embedding API error: ${response.status} - ${error}`);
|
|
2161
|
-
}
|
|
2162
|
-
const data = await response.json();
|
|
2235
|
+
async embedQuery(query) {
|
|
2236
|
+
const result = await this.embedBatch([query]);
|
|
2163
2237
|
return {
|
|
2164
|
-
embedding:
|
|
2165
|
-
tokensUsed:
|
|
2238
|
+
embedding: result.embeddings[0],
|
|
2239
|
+
tokensUsed: result.totalTokensUsed
|
|
2240
|
+
};
|
|
2241
|
+
}
|
|
2242
|
+
async embedDocument(document) {
|
|
2243
|
+
const result = await this.embedBatch([document]);
|
|
2244
|
+
return {
|
|
2245
|
+
embedding: result.embeddings[0],
|
|
2246
|
+
tokensUsed: result.totalTokensUsed
|
|
2166
2247
|
};
|
|
2167
2248
|
}
|
|
2168
2249
|
async embedBatch(texts) {
|
|
2169
|
-
const results = await Promise.all(
|
|
2250
|
+
const results = await Promise.all(
|
|
2251
|
+
texts.map(async (text) => {
|
|
2252
|
+
const response = await fetch(`${this.credentials.baseUrl}/api/embeddings`, {
|
|
2253
|
+
method: "POST",
|
|
2254
|
+
headers: {
|
|
2255
|
+
"Content-Type": "application/json"
|
|
2256
|
+
},
|
|
2257
|
+
body: JSON.stringify({
|
|
2258
|
+
model: this.modelInfo.model,
|
|
2259
|
+
prompt: text
|
|
2260
|
+
})
|
|
2261
|
+
});
|
|
2262
|
+
if (!response.ok) {
|
|
2263
|
+
const error = await response.text();
|
|
2264
|
+
throw new Error(`Ollama embedding API error: ${response.status} - ${error}`);
|
|
2265
|
+
}
|
|
2266
|
+
const data = await response.json();
|
|
2267
|
+
return {
|
|
2268
|
+
embedding: data.embedding,
|
|
2269
|
+
tokensUsed: Math.ceil(text.length / 4)
|
|
2270
|
+
};
|
|
2271
|
+
})
|
|
2272
|
+
);
|
|
2170
2273
|
return {
|
|
2171
2274
|
embeddings: results.map((r) => r.embedding),
|
|
2172
2275
|
totalTokensUsed: results.reduce((sum, r) => sum + r.tokensUsed, 0)
|
|
@@ -2181,6 +2284,30 @@ var OllamaEmbeddingProvider = class {
|
|
|
2181
2284
|
var import_ignore = __toESM(require_ignore(), 1);
|
|
2182
2285
|
import { existsSync as existsSync2, readFileSync as readFileSync2, promises as fsPromises } from "fs";
|
|
2183
2286
|
import * as path2 from "path";
|
|
2287
|
+
var PROJECT_MARKERS = [
|
|
2288
|
+
".git",
|
|
2289
|
+
"package.json",
|
|
2290
|
+
"Cargo.toml",
|
|
2291
|
+
"go.mod",
|
|
2292
|
+
"pyproject.toml",
|
|
2293
|
+
"setup.py",
|
|
2294
|
+
"requirements.txt",
|
|
2295
|
+
"Gemfile",
|
|
2296
|
+
"composer.json",
|
|
2297
|
+
"pom.xml",
|
|
2298
|
+
"build.gradle",
|
|
2299
|
+
"CMakeLists.txt",
|
|
2300
|
+
"Makefile",
|
|
2301
|
+
".opencode"
|
|
2302
|
+
];
|
|
2303
|
+
function hasProjectMarker(projectRoot) {
|
|
2304
|
+
for (const marker of PROJECT_MARKERS) {
|
|
2305
|
+
if (existsSync2(path2.join(projectRoot, marker))) {
|
|
2306
|
+
return true;
|
|
2307
|
+
}
|
|
2308
|
+
}
|
|
2309
|
+
return false;
|
|
2310
|
+
}
|
|
2184
2311
|
function createIgnoreFilter(projectRoot) {
|
|
2185
2312
|
const ig = (0, import_ignore.default)();
|
|
2186
2313
|
const defaultIgnores = [
|
|
@@ -3086,11 +3213,40 @@ var Database = class {
|
|
|
3086
3213
|
import { existsSync as existsSync3, readFileSync as readFileSync3, readdirSync, statSync } from "fs";
|
|
3087
3214
|
import * as path4 from "path";
|
|
3088
3215
|
import { execSync } from "child_process";
|
|
3216
|
+
function resolveGitDir(repoRoot) {
|
|
3217
|
+
const gitPath = path4.join(repoRoot, ".git");
|
|
3218
|
+
if (!existsSync3(gitPath)) {
|
|
3219
|
+
return null;
|
|
3220
|
+
}
|
|
3221
|
+
try {
|
|
3222
|
+
const stat4 = statSync(gitPath);
|
|
3223
|
+
if (stat4.isDirectory()) {
|
|
3224
|
+
return gitPath;
|
|
3225
|
+
}
|
|
3226
|
+
if (stat4.isFile()) {
|
|
3227
|
+
const content = readFileSync3(gitPath, "utf-8").trim();
|
|
3228
|
+
const match = content.match(/^gitdir:\s*(.+)$/);
|
|
3229
|
+
if (match) {
|
|
3230
|
+
const gitdir = match[1];
|
|
3231
|
+
const resolvedPath = path4.isAbsolute(gitdir) ? gitdir : path4.resolve(repoRoot, gitdir);
|
|
3232
|
+
if (existsSync3(resolvedPath)) {
|
|
3233
|
+
return resolvedPath;
|
|
3234
|
+
}
|
|
3235
|
+
}
|
|
3236
|
+
}
|
|
3237
|
+
} catch {
|
|
3238
|
+
}
|
|
3239
|
+
return null;
|
|
3240
|
+
}
|
|
3089
3241
|
function isGitRepo(dir) {
|
|
3090
|
-
return
|
|
3242
|
+
return resolveGitDir(dir) !== null;
|
|
3091
3243
|
}
|
|
3092
3244
|
function getCurrentBranch(repoRoot) {
|
|
3093
|
-
const
|
|
3245
|
+
const gitDir = resolveGitDir(repoRoot);
|
|
3246
|
+
if (!gitDir) {
|
|
3247
|
+
return null;
|
|
3248
|
+
}
|
|
3249
|
+
const headPath = path4.join(gitDir, "HEAD");
|
|
3094
3250
|
if (!existsSync3(headPath)) {
|
|
3095
3251
|
return null;
|
|
3096
3252
|
}
|
|
@@ -3109,20 +3265,23 @@ function getCurrentBranch(repoRoot) {
|
|
|
3109
3265
|
}
|
|
3110
3266
|
}
|
|
3111
3267
|
function getBaseBranch(repoRoot) {
|
|
3268
|
+
const gitDir = resolveGitDir(repoRoot);
|
|
3112
3269
|
const candidates = ["main", "master", "develop", "trunk"];
|
|
3113
|
-
|
|
3114
|
-
|
|
3115
|
-
|
|
3116
|
-
|
|
3117
|
-
|
|
3118
|
-
|
|
3119
|
-
|
|
3120
|
-
|
|
3121
|
-
|
|
3122
|
-
|
|
3123
|
-
|
|
3270
|
+
if (gitDir) {
|
|
3271
|
+
for (const candidate of candidates) {
|
|
3272
|
+
const refPath = path4.join(gitDir, "refs", "heads", candidate);
|
|
3273
|
+
if (existsSync3(refPath)) {
|
|
3274
|
+
return candidate;
|
|
3275
|
+
}
|
|
3276
|
+
const packedRefsPath = path4.join(gitDir, "packed-refs");
|
|
3277
|
+
if (existsSync3(packedRefsPath)) {
|
|
3278
|
+
try {
|
|
3279
|
+
const content = readFileSync3(packedRefsPath, "utf-8");
|
|
3280
|
+
if (content.includes(`refs/heads/${candidate}`)) {
|
|
3281
|
+
return candidate;
|
|
3282
|
+
}
|
|
3283
|
+
} catch {
|
|
3124
3284
|
}
|
|
3125
|
-
} catch {
|
|
3126
3285
|
}
|
|
3127
3286
|
}
|
|
3128
3287
|
}
|
|
@@ -3147,6 +3306,10 @@ function getBranchOrDefault(repoRoot) {
|
|
|
3147
3306
|
return getCurrentBranch(repoRoot) ?? "default";
|
|
3148
3307
|
}
|
|
3149
3308
|
function getHeadPath(repoRoot) {
|
|
3309
|
+
const gitDir = resolveGitDir(repoRoot);
|
|
3310
|
+
if (gitDir) {
|
|
3311
|
+
return path4.join(gitDir, "HEAD");
|
|
3312
|
+
}
|
|
3150
3313
|
return path4.join(repoRoot, ".git", "HEAD");
|
|
3151
3314
|
}
|
|
3152
3315
|
|
|
@@ -3174,6 +3337,7 @@ function isRateLimitError(error) {
|
|
|
3174
3337
|
const message = getErrorMessage(error);
|
|
3175
3338
|
return message.includes("429") || message.toLowerCase().includes("rate limit") || message.toLowerCase().includes("too many requests");
|
|
3176
3339
|
}
|
|
3340
|
+
var INDEX_METADATA_VERSION = "1";
|
|
3177
3341
|
var Indexer = class {
|
|
3178
3342
|
config;
|
|
3179
3343
|
projectRoot;
|
|
@@ -3182,7 +3346,7 @@ var Indexer = class {
|
|
|
3182
3346
|
invertedIndex = null;
|
|
3183
3347
|
database = null;
|
|
3184
3348
|
provider = null;
|
|
3185
|
-
|
|
3349
|
+
configuredProviderInfo = null;
|
|
3186
3350
|
fileHashCache = /* @__PURE__ */ new Map();
|
|
3187
3351
|
fileHashCachePath = "";
|
|
3188
3352
|
failedBatchesPath = "";
|
|
@@ -3193,12 +3357,15 @@ var Indexer = class {
|
|
|
3193
3357
|
maxQueryCacheSize = 100;
|
|
3194
3358
|
queryCacheTtlMs = 5 * 60 * 1e3;
|
|
3195
3359
|
querySimilarityThreshold = 0.85;
|
|
3360
|
+
indexCompatibility = null;
|
|
3361
|
+
indexingLockPath = "";
|
|
3196
3362
|
constructor(projectRoot, config) {
|
|
3197
3363
|
this.projectRoot = projectRoot;
|
|
3198
3364
|
this.config = config;
|
|
3199
3365
|
this.indexPath = this.getIndexPath();
|
|
3200
3366
|
this.fileHashCachePath = path5.join(this.indexPath, "file-hashes.json");
|
|
3201
3367
|
this.failedBatchesPath = path5.join(this.indexPath, "failed-batches.json");
|
|
3368
|
+
this.indexingLockPath = path5.join(this.indexPath, "indexing.lock");
|
|
3202
3369
|
this.logger = initializeLogger(config.debug);
|
|
3203
3370
|
}
|
|
3204
3371
|
getIndexPath() {
|
|
@@ -3224,7 +3391,36 @@ var Indexer = class {
|
|
|
3224
3391
|
for (const [k, v] of this.fileHashCache) {
|
|
3225
3392
|
obj[k] = v;
|
|
3226
3393
|
}
|
|
3227
|
-
|
|
3394
|
+
this.atomicWriteSync(this.fileHashCachePath, JSON.stringify(obj));
|
|
3395
|
+
}
|
|
3396
|
+
atomicWriteSync(targetPath, data) {
|
|
3397
|
+
const tempPath = `${targetPath}.tmp`;
|
|
3398
|
+
writeFileSync(tempPath, data);
|
|
3399
|
+
renameSync(tempPath, targetPath);
|
|
3400
|
+
}
|
|
3401
|
+
checkForInterruptedIndexing() {
|
|
3402
|
+
return existsSync4(this.indexingLockPath);
|
|
3403
|
+
}
|
|
3404
|
+
acquireIndexingLock() {
|
|
3405
|
+
const lockData = {
|
|
3406
|
+
startedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
3407
|
+
pid: process.pid
|
|
3408
|
+
};
|
|
3409
|
+
writeFileSync(this.indexingLockPath, JSON.stringify(lockData));
|
|
3410
|
+
}
|
|
3411
|
+
releaseIndexingLock() {
|
|
3412
|
+
if (existsSync4(this.indexingLockPath)) {
|
|
3413
|
+
unlinkSync(this.indexingLockPath);
|
|
3414
|
+
}
|
|
3415
|
+
}
|
|
3416
|
+
async recoverFromInterruptedIndexing() {
|
|
3417
|
+
this.logger.warn("Detected interrupted indexing session, recovering...");
|
|
3418
|
+
if (existsSync4(this.fileHashCachePath)) {
|
|
3419
|
+
unlinkSync(this.fileHashCachePath);
|
|
3420
|
+
}
|
|
3421
|
+
await this.healthCheck();
|
|
3422
|
+
this.releaseIndexingLock();
|
|
3423
|
+
this.logger.info("Recovery complete, next index will re-process all files");
|
|
3228
3424
|
}
|
|
3229
3425
|
loadFailedBatches() {
|
|
3230
3426
|
try {
|
|
@@ -3272,23 +3468,27 @@ var Indexer = class {
|
|
|
3272
3468
|
}
|
|
3273
3469
|
}
|
|
3274
3470
|
async initialize() {
|
|
3275
|
-
|
|
3276
|
-
|
|
3471
|
+
if (this.config.embeddingProvider === "auto") {
|
|
3472
|
+
this.configuredProviderInfo = await tryDetectProvider();
|
|
3473
|
+
} else {
|
|
3474
|
+
this.configuredProviderInfo = await detectEmbeddingProvider(this.config.embeddingProvider, this.config.embeddingModel);
|
|
3475
|
+
}
|
|
3476
|
+
if (!this.configuredProviderInfo) {
|
|
3277
3477
|
throw new Error(
|
|
3278
3478
|
"No embedding provider available. Configure GitHub, OpenAI, Google, or Ollama."
|
|
3279
3479
|
);
|
|
3280
3480
|
}
|
|
3281
3481
|
this.logger.info("Initializing indexer", {
|
|
3282
|
-
provider: this.
|
|
3283
|
-
model: this.
|
|
3482
|
+
provider: this.configuredProviderInfo.provider,
|
|
3483
|
+
model: this.configuredProviderInfo.modelInfo.model,
|
|
3284
3484
|
scope: this.config.scope
|
|
3285
3485
|
});
|
|
3286
|
-
this.provider = createEmbeddingProvider(
|
|
3287
|
-
this.detectedProvider.credentials,
|
|
3288
|
-
this.detectedProvider.modelInfo
|
|
3289
|
-
);
|
|
3486
|
+
this.provider = createEmbeddingProvider(this.configuredProviderInfo);
|
|
3290
3487
|
await fsPromises2.mkdir(this.indexPath, { recursive: true });
|
|
3291
|
-
|
|
3488
|
+
if (this.checkForInterruptedIndexing()) {
|
|
3489
|
+
await this.recoverFromInterruptedIndexing();
|
|
3490
|
+
}
|
|
3491
|
+
const dimensions = this.configuredProviderInfo.modelInfo.dimensions;
|
|
3292
3492
|
const storePath = path5.join(this.indexPath, "vectors");
|
|
3293
3493
|
this.store = new VectorStore(storePath, dimensions);
|
|
3294
3494
|
const indexFilePath = path5.join(this.indexPath, "vectors.usearch");
|
|
@@ -3311,6 +3511,14 @@ var Indexer = class {
|
|
|
3311
3511
|
if (dbIsNew && this.store.count() > 0) {
|
|
3312
3512
|
this.migrateFromLegacyIndex();
|
|
3313
3513
|
}
|
|
3514
|
+
this.indexCompatibility = this.validateIndexCompatibility(this.configuredProviderInfo);
|
|
3515
|
+
if (!this.indexCompatibility.compatible) {
|
|
3516
|
+
this.logger.warn("Index compatibility issue detected", {
|
|
3517
|
+
reason: this.indexCompatibility.reason,
|
|
3518
|
+
storedMetadata: this.indexCompatibility.storedMetadata,
|
|
3519
|
+
configuredProviderInfo: this.configuredProviderInfo
|
|
3520
|
+
});
|
|
3521
|
+
}
|
|
3314
3522
|
if (isGitRepo(this.projectRoot)) {
|
|
3315
3523
|
this.currentBranch = getBranchOrDefault(this.projectRoot);
|
|
3316
3524
|
this.baseBranch = getBaseBranch(this.projectRoot);
|
|
@@ -3381,30 +3589,106 @@ var Indexer = class {
|
|
|
3381
3589
|
}
|
|
3382
3590
|
this.database.addChunksToBranchBatch(this.currentBranch || "default", chunkIds);
|
|
3383
3591
|
}
|
|
3592
|
+
loadIndexMetadata() {
|
|
3593
|
+
if (!this.database) return null;
|
|
3594
|
+
const version = this.database.getMetadata("index.version");
|
|
3595
|
+
if (!version) return null;
|
|
3596
|
+
return {
|
|
3597
|
+
indexVersion: version,
|
|
3598
|
+
embeddingProvider: this.database.getMetadata("index.embeddingProvider") ?? "",
|
|
3599
|
+
embeddingModel: this.database.getMetadata("index.embeddingModel") ?? "",
|
|
3600
|
+
embeddingDimensions: parseInt(this.database.getMetadata("index.embeddingDimensions") ?? "0", 10),
|
|
3601
|
+
createdAt: this.database.getMetadata("index.createdAt") ?? "",
|
|
3602
|
+
updatedAt: this.database.getMetadata("index.updatedAt") ?? ""
|
|
3603
|
+
};
|
|
3604
|
+
}
|
|
3605
|
+
saveIndexMetadata(provider) {
|
|
3606
|
+
if (!this.database) return;
|
|
3607
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
3608
|
+
const existingCreatedAt = this.database.getMetadata("index.createdAt");
|
|
3609
|
+
this.database.setMetadata("index.version", INDEX_METADATA_VERSION);
|
|
3610
|
+
this.database.setMetadata("index.embeddingProvider", provider.provider);
|
|
3611
|
+
this.database.setMetadata("index.embeddingModel", provider.modelInfo.model);
|
|
3612
|
+
this.database.setMetadata("index.embeddingDimensions", provider.modelInfo.dimensions.toString());
|
|
3613
|
+
this.database.setMetadata("index.updatedAt", now);
|
|
3614
|
+
if (!existingCreatedAt) {
|
|
3615
|
+
this.database.setMetadata("index.createdAt", now);
|
|
3616
|
+
}
|
|
3617
|
+
}
|
|
3618
|
+
validateIndexCompatibility(provider) {
|
|
3619
|
+
const storedMetadata = this.loadIndexMetadata();
|
|
3620
|
+
if (!storedMetadata) {
|
|
3621
|
+
return { compatible: true };
|
|
3622
|
+
}
|
|
3623
|
+
const currentProvider = provider.provider;
|
|
3624
|
+
const currentModel = provider.modelInfo.model;
|
|
3625
|
+
const currentDimensions = provider.modelInfo.dimensions;
|
|
3626
|
+
if (storedMetadata.embeddingDimensions !== currentDimensions) {
|
|
3627
|
+
return {
|
|
3628
|
+
compatible: false,
|
|
3629
|
+
code: "DIMENSION_MISMATCH" /* DIMENSION_MISMATCH */,
|
|
3630
|
+
reason: `Dimension mismatch: index has ${storedMetadata.embeddingDimensions}D vectors (${storedMetadata.embeddingProvider}/${storedMetadata.embeddingModel}), but current provider uses ${currentDimensions}D (${currentProvider}/${currentModel}). Run index_codebase with force=true to rebuild.`,
|
|
3631
|
+
storedMetadata
|
|
3632
|
+
};
|
|
3633
|
+
}
|
|
3634
|
+
if (storedMetadata.embeddingModel !== currentModel) {
|
|
3635
|
+
return {
|
|
3636
|
+
compatible: false,
|
|
3637
|
+
code: "MODEL_MISMATCH" /* MODEL_MISMATCH */,
|
|
3638
|
+
reason: `Model mismatch: index was built with "${storedMetadata.embeddingModel}", but current model is "${currentModel}". Embeddings are incompatible. Run index_codebase with force=true to rebuild.`,
|
|
3639
|
+
storedMetadata
|
|
3640
|
+
};
|
|
3641
|
+
}
|
|
3642
|
+
if (storedMetadata.embeddingProvider !== currentProvider) {
|
|
3643
|
+
this.logger.warn("Provider changed", {
|
|
3644
|
+
storedProvider: storedMetadata.embeddingProvider,
|
|
3645
|
+
currentProvider
|
|
3646
|
+
});
|
|
3647
|
+
}
|
|
3648
|
+
return {
|
|
3649
|
+
compatible: true,
|
|
3650
|
+
storedMetadata
|
|
3651
|
+
};
|
|
3652
|
+
}
|
|
3653
|
+
checkCompatibility() {
|
|
3654
|
+
if (!this.indexCompatibility) {
|
|
3655
|
+
if (!this.configuredProviderInfo) {
|
|
3656
|
+
throw new Error("No embedding provider info, you must initialize the indexer first.");
|
|
3657
|
+
}
|
|
3658
|
+
this.indexCompatibility = this.validateIndexCompatibility(this.configuredProviderInfo);
|
|
3659
|
+
}
|
|
3660
|
+
return this.indexCompatibility;
|
|
3661
|
+
}
|
|
3384
3662
|
async ensureInitialized() {
|
|
3385
|
-
if (!this.store || !this.provider || !this.invertedIndex || !this.
|
|
3663
|
+
if (!this.store || !this.provider || !this.invertedIndex || !this.configuredProviderInfo || !this.database) {
|
|
3386
3664
|
await this.initialize();
|
|
3387
3665
|
}
|
|
3388
3666
|
return {
|
|
3389
3667
|
store: this.store,
|
|
3390
3668
|
provider: this.provider,
|
|
3391
3669
|
invertedIndex: this.invertedIndex,
|
|
3392
|
-
|
|
3670
|
+
configuredProviderInfo: this.configuredProviderInfo,
|
|
3393
3671
|
database: this.database
|
|
3394
3672
|
};
|
|
3395
3673
|
}
|
|
3396
3674
|
async estimateCost() {
|
|
3397
|
-
const {
|
|
3675
|
+
const { configuredProviderInfo } = await this.ensureInitialized();
|
|
3398
3676
|
const { files } = await collectFiles(
|
|
3399
3677
|
this.projectRoot,
|
|
3400
3678
|
this.config.include,
|
|
3401
3679
|
this.config.exclude,
|
|
3402
3680
|
this.config.indexing.maxFileSize
|
|
3403
3681
|
);
|
|
3404
|
-
return createCostEstimate(files,
|
|
3682
|
+
return createCostEstimate(files, configuredProviderInfo);
|
|
3405
3683
|
}
|
|
3406
3684
|
async index(onProgress) {
|
|
3407
|
-
const { store, provider, invertedIndex, database,
|
|
3685
|
+
const { store, provider, invertedIndex, database, configuredProviderInfo } = await this.ensureInitialized();
|
|
3686
|
+
if (!this.indexCompatibility?.compatible) {
|
|
3687
|
+
throw new Error(
|
|
3688
|
+
`${this.indexCompatibility?.reason} Run index_codebase with force=true to rebuild the index.`
|
|
3689
|
+
);
|
|
3690
|
+
}
|
|
3691
|
+
this.acquireIndexingLock();
|
|
3408
3692
|
this.logger.recordIndexingStart();
|
|
3409
3693
|
this.logger.info("Starting indexing", { projectRoot: this.projectRoot });
|
|
3410
3694
|
const startTime = Date.now();
|
|
@@ -3511,7 +3795,7 @@ var Indexer = class {
|
|
|
3511
3795
|
const id = generateChunkId(parsed.path, chunk);
|
|
3512
3796
|
const contentHash = generateChunkHash(chunk);
|
|
3513
3797
|
currentChunkIds.add(id);
|
|
3514
|
-
|
|
3798
|
+
chunkDataBatch.push({
|
|
3515
3799
|
chunkId: id,
|
|
3516
3800
|
contentHash,
|
|
3517
3801
|
filePath: parsed.path,
|
|
@@ -3520,8 +3804,7 @@ var Indexer = class {
|
|
|
3520
3804
|
nodeType: chunk.chunkType,
|
|
3521
3805
|
name: chunk.name,
|
|
3522
3806
|
language: chunk.language
|
|
3523
|
-
};
|
|
3524
|
-
chunkDataBatch.push(chunkData);
|
|
3807
|
+
});
|
|
3525
3808
|
if (existingChunks.get(id) === contentHash) {
|
|
3526
3809
|
fileChunkCount++;
|
|
3527
3810
|
continue;
|
|
@@ -3574,6 +3857,7 @@ var Indexer = class {
|
|
|
3574
3857
|
chunksProcessed: 0,
|
|
3575
3858
|
totalChunks: 0
|
|
3576
3859
|
});
|
|
3860
|
+
this.releaseIndexingLock();
|
|
3577
3861
|
return stats;
|
|
3578
3862
|
}
|
|
3579
3863
|
if (pendingChunks.length === 0) {
|
|
@@ -3591,6 +3875,7 @@ var Indexer = class {
|
|
|
3591
3875
|
chunksProcessed: 0,
|
|
3592
3876
|
totalChunks: 0
|
|
3593
3877
|
});
|
|
3878
|
+
this.releaseIndexingLock();
|
|
3594
3879
|
return stats;
|
|
3595
3880
|
}
|
|
3596
3881
|
onProgress?.({
|
|
@@ -3619,7 +3904,7 @@ var Indexer = class {
|
|
|
3619
3904
|
stats.indexedChunks++;
|
|
3620
3905
|
}
|
|
3621
3906
|
}
|
|
3622
|
-
const providerRateLimits = this.getProviderRateLimits(
|
|
3907
|
+
const providerRateLimits = this.getProviderRateLimits(configuredProviderInfo.provider);
|
|
3623
3908
|
const queue = new PQueue({
|
|
3624
3909
|
concurrency: providerRateLimits.concurrency,
|
|
3625
3910
|
interval: providerRateLimits.intervalMs,
|
|
@@ -3630,7 +3915,7 @@ var Indexer = class {
|
|
|
3630
3915
|
for (const batch of dynamicBatches) {
|
|
3631
3916
|
queue.add(async () => {
|
|
3632
3917
|
if (rateLimitBackoffMs > 0) {
|
|
3633
|
-
await new Promise((
|
|
3918
|
+
await new Promise((resolve5) => setTimeout(resolve5, rateLimitBackoffMs));
|
|
3634
3919
|
}
|
|
3635
3920
|
try {
|
|
3636
3921
|
const result = await pRetry(
|
|
@@ -3674,7 +3959,7 @@ var Indexer = class {
|
|
|
3674
3959
|
contentHash: chunk.contentHash,
|
|
3675
3960
|
embedding: float32ArrayToBuffer(result.embeddings[i]),
|
|
3676
3961
|
chunkText: chunk.text,
|
|
3677
|
-
model:
|
|
3962
|
+
model: configuredProviderInfo.modelInfo.model
|
|
3678
3963
|
}));
|
|
3679
3964
|
database.upsertEmbeddingsBatch(embeddingBatchItems);
|
|
3680
3965
|
for (const chunk of batch) {
|
|
@@ -3725,6 +4010,8 @@ var Indexer = class {
|
|
|
3725
4010
|
await this.maybeRunOrphanGc();
|
|
3726
4011
|
}
|
|
3727
4012
|
stats.durationMs = Date.now() - startTime;
|
|
4013
|
+
this.saveIndexMetadata(configuredProviderInfo);
|
|
4014
|
+
this.indexCompatibility = { compatible: true };
|
|
3728
4015
|
this.logger.recordIndexingEnd();
|
|
3729
4016
|
this.logger.info("Indexing complete", {
|
|
3730
4017
|
files: stats.totalFiles,
|
|
@@ -3745,6 +4032,7 @@ var Indexer = class {
|
|
|
3745
4032
|
chunksProcessed: stats.indexedChunks,
|
|
3746
4033
|
totalChunks: pendingChunks.length
|
|
3747
4034
|
});
|
|
4035
|
+
this.releaseIndexingLock();
|
|
3748
4036
|
return stats;
|
|
3749
4037
|
}
|
|
3750
4038
|
async getQueryEmbedding(query, provider) {
|
|
@@ -3767,7 +4055,7 @@ var Indexer = class {
|
|
|
3767
4055
|
}
|
|
3768
4056
|
this.logger.cache("debug", "Query embedding cache miss", { query: query.slice(0, 50) });
|
|
3769
4057
|
this.logger.recordQueryCacheMiss();
|
|
3770
|
-
const { embedding, tokensUsed } = await provider.
|
|
4058
|
+
const { embedding, tokensUsed } = await provider.embedQuery(query);
|
|
3771
4059
|
this.logger.recordEmbeddingApiCall(tokensUsed);
|
|
3772
4060
|
if (this.queryEmbeddingCache.size >= this.maxQueryCacheSize) {
|
|
3773
4061
|
const oldestKey = this.queryEmbeddingCache.keys().next().value;
|
|
@@ -3810,8 +4098,14 @@ var Indexer = class {
|
|
|
3810
4098
|
return intersection / union;
|
|
3811
4099
|
}
|
|
3812
4100
|
async search(query, limit, options) {
|
|
3813
|
-
const searchStartTime = performance2.now();
|
|
3814
4101
|
const { store, provider, database } = await this.ensureInitialized();
|
|
4102
|
+
const compatibility = this.checkCompatibility();
|
|
4103
|
+
if (!compatibility.compatible) {
|
|
4104
|
+
throw new Error(
|
|
4105
|
+
`${compatibility.reason ?? "Index is incompatible with current embedding provider."} A possible solution is to run index_codebase with force=true to rebuild the index.`
|
|
4106
|
+
);
|
|
4107
|
+
}
|
|
4108
|
+
const searchStartTime = performance2.now();
|
|
3815
4109
|
if (store.count() === 0) {
|
|
3816
4110
|
this.logger.search("debug", "Search on empty index", { query });
|
|
3817
4111
|
return [];
|
|
@@ -3953,15 +4247,16 @@ var Indexer = class {
|
|
|
3953
4247
|
return results.slice(0, limit);
|
|
3954
4248
|
}
|
|
3955
4249
|
async getStatus() {
|
|
3956
|
-
const { store,
|
|
4250
|
+
const { store, configuredProviderInfo } = await this.ensureInitialized();
|
|
3957
4251
|
return {
|
|
3958
4252
|
indexed: store.count() > 0,
|
|
3959
4253
|
vectorCount: store.count(),
|
|
3960
|
-
provider:
|
|
3961
|
-
model:
|
|
4254
|
+
provider: configuredProviderInfo.provider,
|
|
4255
|
+
model: configuredProviderInfo.modelInfo.model,
|
|
3962
4256
|
indexPath: this.indexPath,
|
|
3963
4257
|
currentBranch: this.currentBranch,
|
|
3964
|
-
baseBranch: this.baseBranch
|
|
4258
|
+
baseBranch: this.baseBranch,
|
|
4259
|
+
compatibility: this.indexCompatibility
|
|
3965
4260
|
};
|
|
3966
4261
|
}
|
|
3967
4262
|
async clearIndex() {
|
|
@@ -3973,6 +4268,13 @@ var Indexer = class {
|
|
|
3973
4268
|
this.fileHashCache.clear();
|
|
3974
4269
|
this.saveFileHashCache();
|
|
3975
4270
|
database.clearBranch(this.currentBranch);
|
|
4271
|
+
database.deleteMetadata("index.version");
|
|
4272
|
+
database.deleteMetadata("index.embeddingProvider");
|
|
4273
|
+
database.deleteMetadata("index.embeddingModel");
|
|
4274
|
+
database.deleteMetadata("index.embeddingDimensions");
|
|
4275
|
+
database.deleteMetadata("index.createdAt");
|
|
4276
|
+
database.deleteMetadata("index.updatedAt");
|
|
4277
|
+
this.indexCompatibility = this.validateIndexCompatibility(this.configuredProviderInfo);
|
|
3976
4278
|
}
|
|
3977
4279
|
async healthCheck() {
|
|
3978
4280
|
const { store, invertedIndex, database } = await this.ensureInitialized();
|
|
@@ -4086,26 +4388,31 @@ var Indexer = class {
|
|
|
4086
4388
|
getLogger() {
|
|
4087
4389
|
return this.logger;
|
|
4088
4390
|
}
|
|
4089
|
-
async findSimilar(code, limit, options) {
|
|
4090
|
-
const searchStartTime = performance2.now();
|
|
4391
|
+
async findSimilar(code, limit = this.config.search.maxResults, options) {
|
|
4091
4392
|
const { store, provider, database } = await this.ensureInitialized();
|
|
4393
|
+
const compatibility = this.checkCompatibility();
|
|
4394
|
+
if (!compatibility.compatible) {
|
|
4395
|
+
throw new Error(
|
|
4396
|
+
`${compatibility.reason ?? "Index is incompatible with current embedding provider."} Run index_codebase with force=true to rebuild the index.`
|
|
4397
|
+
);
|
|
4398
|
+
}
|
|
4399
|
+
const searchStartTime = performance2.now();
|
|
4092
4400
|
if (store.count() === 0) {
|
|
4093
4401
|
this.logger.search("debug", "Find similar on empty index");
|
|
4094
4402
|
return [];
|
|
4095
4403
|
}
|
|
4096
|
-
const maxResults = limit ?? this.config.search.maxResults;
|
|
4097
4404
|
const filterByBranch = options?.filterByBranch ?? true;
|
|
4098
4405
|
this.logger.search("debug", "Starting find similar", {
|
|
4099
4406
|
codeLength: code.length,
|
|
4100
|
-
|
|
4407
|
+
limit,
|
|
4101
4408
|
filterByBranch
|
|
4102
4409
|
});
|
|
4103
4410
|
const embeddingStartTime = performance2.now();
|
|
4104
|
-
const { embedding, tokensUsed } = await provider.
|
|
4411
|
+
const { embedding, tokensUsed } = await provider.embedDocument(code);
|
|
4105
4412
|
const embeddingMs = performance2.now() - embeddingStartTime;
|
|
4106
4413
|
this.logger.recordEmbeddingApiCall(tokensUsed);
|
|
4107
4414
|
const vectorStartTime = performance2.now();
|
|
4108
|
-
const semanticResults = store.search(embedding,
|
|
4415
|
+
const semanticResults = store.search(embedding, limit * 2);
|
|
4109
4416
|
const vectorMs = performance2.now() - vectorStartTime;
|
|
4110
4417
|
let branchChunkIds = null;
|
|
4111
4418
|
if (filterByBranch && this.currentBranch !== "default") {
|
|
@@ -4129,7 +4436,7 @@ var Indexer = class {
|
|
|
4129
4436
|
if (r.metadata.chunkType !== options.chunkType) return false;
|
|
4130
4437
|
}
|
|
4131
4438
|
return true;
|
|
4132
|
-
}).slice(0,
|
|
4439
|
+
}).slice(0, limit);
|
|
4133
4440
|
const totalSearchMs = performance2.now() - searchStartTime;
|
|
4134
4441
|
this.logger.recordSearch(totalSearchMs, {
|
|
4135
4442
|
embeddingMs,
|
|
@@ -5044,7 +5351,7 @@ var NodeFsHandler = class {
|
|
|
5044
5351
|
this._addToNodeFs(path9, initialAdd, wh, depth + 1);
|
|
5045
5352
|
}
|
|
5046
5353
|
}).on(EV.ERROR, this._boundHandleError);
|
|
5047
|
-
return new Promise((
|
|
5354
|
+
return new Promise((resolve5, reject) => {
|
|
5048
5355
|
if (!stream)
|
|
5049
5356
|
return reject();
|
|
5050
5357
|
stream.once(STR_END, () => {
|
|
@@ -5053,7 +5360,7 @@ var NodeFsHandler = class {
|
|
|
5053
5360
|
return;
|
|
5054
5361
|
}
|
|
5055
5362
|
const wasThrottled = throttler ? throttler.clear() : false;
|
|
5056
|
-
|
|
5363
|
+
resolve5(void 0);
|
|
5057
5364
|
previous.getChildren().filter((item) => {
|
|
5058
5365
|
return item !== directory && !current.has(item);
|
|
5059
5366
|
}).forEach((item) => {
|
|
@@ -6100,6 +6407,175 @@ function createWatcherWithIndexer(indexer, projectRoot, config) {
|
|
|
6100
6407
|
|
|
6101
6408
|
// src/tools/index.ts
|
|
6102
6409
|
import { tool } from "@opencode-ai/plugin";
|
|
6410
|
+
|
|
6411
|
+
// src/tools/utils.ts
|
|
6412
|
+
var MAX_CONTENT_LINES = 30;
|
|
6413
|
+
function truncateContent(content) {
|
|
6414
|
+
const lines = content.split("\n");
|
|
6415
|
+
if (lines.length <= MAX_CONTENT_LINES) return content;
|
|
6416
|
+
return lines.slice(0, MAX_CONTENT_LINES).join("\n") + `
|
|
6417
|
+
// ... (${lines.length - MAX_CONTENT_LINES} more lines)`;
|
|
6418
|
+
}
|
|
6419
|
+
function formatIndexStats(stats, verbose = false) {
|
|
6420
|
+
const lines = [];
|
|
6421
|
+
if (stats.indexedChunks === 0 && stats.removedChunks === 0) {
|
|
6422
|
+
lines.push(`Indexed. ${stats.totalFiles} files processed, ${stats.existingChunks} code chunks already up to date.`);
|
|
6423
|
+
} else if (stats.indexedChunks === 0) {
|
|
6424
|
+
lines.push(`Indexed. ${stats.totalFiles} files, removed ${stats.removedChunks} stale chunks, ${stats.existingChunks} chunks remain.`);
|
|
6425
|
+
} else {
|
|
6426
|
+
let main = `Indexed. ${stats.totalFiles} files processed, ${stats.indexedChunks} new chunks embedded.`;
|
|
6427
|
+
if (stats.existingChunks > 0) {
|
|
6428
|
+
main += ` ${stats.existingChunks} unchanged chunks skipped.`;
|
|
6429
|
+
}
|
|
6430
|
+
lines.push(main);
|
|
6431
|
+
if (stats.removedChunks > 0) {
|
|
6432
|
+
lines.push(`Removed ${stats.removedChunks} stale chunks.`);
|
|
6433
|
+
}
|
|
6434
|
+
if (stats.failedChunks > 0) {
|
|
6435
|
+
lines.push(`Failed: ${stats.failedChunks} chunks.`);
|
|
6436
|
+
}
|
|
6437
|
+
lines.push(`Tokens: ${stats.tokensUsed.toLocaleString()}, Duration: ${(stats.durationMs / 1e3).toFixed(1)}s`);
|
|
6438
|
+
}
|
|
6439
|
+
if (verbose) {
|
|
6440
|
+
if (stats.skippedFiles.length > 0) {
|
|
6441
|
+
const tooLarge = stats.skippedFiles.filter((f) => f.reason === "too_large");
|
|
6442
|
+
const excluded = stats.skippedFiles.filter((f) => f.reason === "excluded");
|
|
6443
|
+
const gitignored = stats.skippedFiles.filter((f) => f.reason === "gitignore");
|
|
6444
|
+
lines.push("");
|
|
6445
|
+
lines.push(`Skipped files: ${stats.skippedFiles.length}`);
|
|
6446
|
+
if (tooLarge.length > 0) {
|
|
6447
|
+
lines.push(` Too large (${tooLarge.length}): ${tooLarge.slice(0, 5).map((f) => f.path).join(", ")}${tooLarge.length > 5 ? "..." : ""}`);
|
|
6448
|
+
}
|
|
6449
|
+
if (excluded.length > 0) {
|
|
6450
|
+
lines.push(` Excluded (${excluded.length}): ${excluded.slice(0, 5).map((f) => f.path).join(", ")}${excluded.length > 5 ? "..." : ""}`);
|
|
6451
|
+
}
|
|
6452
|
+
if (gitignored.length > 0) {
|
|
6453
|
+
lines.push(` Gitignored (${gitignored.length}): ${gitignored.slice(0, 5).map((f) => f.path).join(", ")}${gitignored.length > 5 ? "..." : ""}`);
|
|
6454
|
+
}
|
|
6455
|
+
}
|
|
6456
|
+
if (stats.parseFailures.length > 0) {
|
|
6457
|
+
lines.push("");
|
|
6458
|
+
lines.push(`Files with no extractable chunks (${stats.parseFailures.length}): ${stats.parseFailures.slice(0, 10).join(", ")}${stats.parseFailures.length > 10 ? "..." : ""}`);
|
|
6459
|
+
}
|
|
6460
|
+
}
|
|
6461
|
+
return lines.join("\n");
|
|
6462
|
+
}
|
|
6463
|
+
function formatStatus(status) {
|
|
6464
|
+
if (!status.indexed) {
|
|
6465
|
+
return "Codebase is not indexed. Run index_codebase to create an index.";
|
|
6466
|
+
}
|
|
6467
|
+
const lines = [
|
|
6468
|
+
`Index status:`,
|
|
6469
|
+
` Indexed chunks: ${status.vectorCount.toLocaleString()}`,
|
|
6470
|
+
` Provider: ${status.provider}`,
|
|
6471
|
+
` Model: ${status.model}`,
|
|
6472
|
+
` Location: ${status.indexPath}`
|
|
6473
|
+
];
|
|
6474
|
+
if (status.currentBranch !== "default") {
|
|
6475
|
+
lines.push(` Current branch: ${status.currentBranch}`);
|
|
6476
|
+
lines.push(` Base branch: ${status.baseBranch}`);
|
|
6477
|
+
}
|
|
6478
|
+
if (status.compatibility && !status.compatibility.compatible) {
|
|
6479
|
+
lines.push("");
|
|
6480
|
+
lines.push(`COMPATIBILITY WARNING: ${status.compatibility.reason}`);
|
|
6481
|
+
if (status.compatibility.storedMetadata) {
|
|
6482
|
+
const stored = status.compatibility.storedMetadata;
|
|
6483
|
+
lines.push(` Index was built with: ${stored.embeddingProvider}/${stored.embeddingModel} (${stored.embeddingDimensions}D)`);
|
|
6484
|
+
lines.push(` Current config: ${status.provider}/${status.model}`);
|
|
6485
|
+
}
|
|
6486
|
+
} else if (!status.compatibility) {
|
|
6487
|
+
lines.push(` Compatibility: No compatibility information found. Maybe the index is not initialized yet, try running index_codebase.`);
|
|
6488
|
+
} else {
|
|
6489
|
+
lines.push(` Compatibility: Index is compatible with the current provider and model.`);
|
|
6490
|
+
}
|
|
6491
|
+
return lines.join("\n");
|
|
6492
|
+
}
|
|
6493
|
+
function formatProgressTitle(progress) {
|
|
6494
|
+
switch (progress.phase) {
|
|
6495
|
+
case "scanning":
|
|
6496
|
+
return "Scanning files...";
|
|
6497
|
+
case "parsing":
|
|
6498
|
+
return `Parsing: ${progress.filesProcessed}/${progress.totalFiles} files`;
|
|
6499
|
+
case "embedding":
|
|
6500
|
+
return `Embedding: ${progress.chunksProcessed}/${progress.totalChunks} chunks`;
|
|
6501
|
+
case "storing":
|
|
6502
|
+
return "Storing index...";
|
|
6503
|
+
case "complete":
|
|
6504
|
+
return "Indexing complete";
|
|
6505
|
+
default:
|
|
6506
|
+
return "Indexing...";
|
|
6507
|
+
}
|
|
6508
|
+
}
|
|
6509
|
+
function calculatePercentage(progress) {
|
|
6510
|
+
if (progress.phase === "scanning") return 0;
|
|
6511
|
+
if (progress.phase === "complete") return 100;
|
|
6512
|
+
if (progress.phase === "parsing") {
|
|
6513
|
+
if (progress.totalFiles === 0) return 5;
|
|
6514
|
+
return Math.round(5 + progress.filesProcessed / progress.totalFiles * 15);
|
|
6515
|
+
}
|
|
6516
|
+
if (progress.phase === "embedding") {
|
|
6517
|
+
if (progress.totalChunks === 0) return 20;
|
|
6518
|
+
return Math.round(20 + progress.chunksProcessed / progress.totalChunks * 70);
|
|
6519
|
+
}
|
|
6520
|
+
if (progress.phase === "storing") return 95;
|
|
6521
|
+
return 0;
|
|
6522
|
+
}
|
|
6523
|
+
function formatCodebasePeek(results, query) {
|
|
6524
|
+
if (results.length === 0) {
|
|
6525
|
+
return "No matching code found. Try a different query or run index_codebase first.";
|
|
6526
|
+
}
|
|
6527
|
+
const formatted = results.map((r, idx) => {
|
|
6528
|
+
const location = `${r.filePath}:${r.startLine}-${r.endLine}`;
|
|
6529
|
+
const name = r.name ? `"${r.name}"` : "(anonymous)";
|
|
6530
|
+
return `[${idx + 1}] ${r.chunkType} ${name} at ${location} (score: ${r.score.toFixed(2)})`;
|
|
6531
|
+
});
|
|
6532
|
+
return `Found ${results.length} locations for "${query}":
|
|
6533
|
+
|
|
6534
|
+
${formatted.join("\n")}
|
|
6535
|
+
|
|
6536
|
+
Use Read tool to examine specific files.`;
|
|
6537
|
+
}
|
|
6538
|
+
function formatHealthCheck(result) {
|
|
6539
|
+
if (result.removed === 0 && result.gcOrphanEmbeddings === 0 && result.gcOrphanChunks === 0) {
|
|
6540
|
+
return "Index is healthy. No stale entries found.";
|
|
6541
|
+
}
|
|
6542
|
+
const lines = [`Health check complete:`];
|
|
6543
|
+
if (result.removed > 0) {
|
|
6544
|
+
lines.push(` Removed stale entries: ${result.removed}`);
|
|
6545
|
+
}
|
|
6546
|
+
if (result.gcOrphanEmbeddings > 0) {
|
|
6547
|
+
lines.push(` Garbage collected orphan embeddings: ${result.gcOrphanEmbeddings}`);
|
|
6548
|
+
}
|
|
6549
|
+
if (result.gcOrphanChunks > 0) {
|
|
6550
|
+
lines.push(` Garbage collected orphan chunks: ${result.gcOrphanChunks}`);
|
|
6551
|
+
}
|
|
6552
|
+
if (result.filePaths.length > 0) {
|
|
6553
|
+
lines.push(` Cleaned paths: ${result.filePaths.join(", ")}`);
|
|
6554
|
+
}
|
|
6555
|
+
return lines.join("\n");
|
|
6556
|
+
}
|
|
6557
|
+
function formatLogs(logs) {
|
|
6558
|
+
if (logs.length === 0) {
|
|
6559
|
+
return "No logs recorded yet. Logs are captured during indexing and search operations.";
|
|
6560
|
+
}
|
|
6561
|
+
return logs.map((l) => {
|
|
6562
|
+
const dataStr = l.data ? ` ${JSON.stringify(l.data)}` : "";
|
|
6563
|
+
return `[${l.timestamp}] [${l.level.toUpperCase()}] [${l.category}] ${l.message}${dataStr}`;
|
|
6564
|
+
}).join("\n");
|
|
6565
|
+
}
|
|
6566
|
+
function formatSearchResults(results, scoreFormat = "similarity") {
|
|
6567
|
+
const formatted = results.map((r, idx) => {
|
|
6568
|
+
const header = r.name ? `[${idx + 1}] ${r.chunkType} "${r.name}" in ${r.filePath}:${r.startLine}-${r.endLine}` : `[${idx + 1}] ${r.chunkType} in ${r.filePath}:${r.startLine}-${r.endLine}`;
|
|
6569
|
+
const scoreLabel = scoreFormat === "similarity" ? `(similarity: ${(r.score * 100).toFixed(1)}%)` : `(score: ${r.score.toFixed(2)})`;
|
|
6570
|
+
return `${header} ${scoreLabel}
|
|
6571
|
+
\`\`\`
|
|
6572
|
+
${truncateContent(r.content)}
|
|
6573
|
+
\`\`\``;
|
|
6574
|
+
});
|
|
6575
|
+
return formatted.join("\n\n");
|
|
6576
|
+
}
|
|
6577
|
+
|
|
6578
|
+
// src/tools/index.ts
|
|
6103
6579
|
var z = tool.schema;
|
|
6104
6580
|
var sharedIndexer = null;
|
|
6105
6581
|
function initializeTools(projectRoot, config) {
|
|
@@ -6111,39 +6587,6 @@ function getIndexer() {
|
|
|
6111
6587
|
}
|
|
6112
6588
|
return sharedIndexer;
|
|
6113
6589
|
}
|
|
6114
|
-
var codebase_search = tool({
|
|
6115
|
-
description: "Search codebase by MEANING, not keywords. Returns full code content. Use when you need to see actual implementation. For just finding WHERE code is (saves ~90% tokens), use codebase_peek instead. For known identifiers like 'validateToken', use grep - it's faster.",
|
|
6116
|
-
args: {
|
|
6117
|
-
query: z.string().describe("Natural language description of what code you're looking for. Describe behavior, not syntax."),
|
|
6118
|
-
limit: z.number().optional().default(10).describe("Maximum number of results to return"),
|
|
6119
|
-
fileType: z.string().optional().describe("Filter by file extension (e.g., 'ts', 'py', 'rs')"),
|
|
6120
|
-
directory: z.string().optional().describe("Filter by directory path (e.g., 'src/utils', 'lib')"),
|
|
6121
|
-
chunkType: z.enum(["function", "class", "method", "interface", "type", "enum", "struct", "impl", "trait", "module", "other"]).optional().describe("Filter by code chunk type"),
|
|
6122
|
-
contextLines: z.number().optional().describe("Number of extra lines to include before/after each match (default: 0)")
|
|
6123
|
-
},
|
|
6124
|
-
async execute(args) {
|
|
6125
|
-
const indexer = getIndexer();
|
|
6126
|
-
const results = await indexer.search(args.query, args.limit ?? 10, {
|
|
6127
|
-
fileType: args.fileType,
|
|
6128
|
-
directory: args.directory,
|
|
6129
|
-
chunkType: args.chunkType,
|
|
6130
|
-
contextLines: args.contextLines
|
|
6131
|
-
});
|
|
6132
|
-
if (results.length === 0) {
|
|
6133
|
-
return "No matching code found. Try a different query or run index_codebase first.";
|
|
6134
|
-
}
|
|
6135
|
-
const formatted = results.map((r, idx) => {
|
|
6136
|
-
const header = r.name ? `[${idx + 1}] ${r.chunkType} "${r.name}" in ${r.filePath}:${r.startLine}-${r.endLine}` : `[${idx + 1}] ${r.chunkType} in ${r.filePath}:${r.startLine}-${r.endLine}`;
|
|
6137
|
-
return `${header} (score: ${r.score.toFixed(2)})
|
|
6138
|
-
\`\`\`
|
|
6139
|
-
${r.content}
|
|
6140
|
-
\`\`\``;
|
|
6141
|
-
});
|
|
6142
|
-
return `Found ${results.length} results for "${args.query}":
|
|
6143
|
-
|
|
6144
|
-
${formatted.join("\n\n")}`;
|
|
6145
|
-
}
|
|
6146
|
-
});
|
|
6147
6590
|
var codebase_peek = tool({
|
|
6148
6591
|
description: "Quick lookup of code locations by meaning. Returns only metadata (file, line, name, type) WITHOUT code content. Use this first to find WHERE code is, then use Read tool to examine specific files. Saves tokens by not returning full code blocks. Best for: discovery, navigation, finding multiple related locations.",
|
|
6149
6592
|
args: {
|
|
@@ -6161,19 +6604,7 @@ var codebase_peek = tool({
|
|
|
6161
6604
|
chunkType: args.chunkType,
|
|
6162
6605
|
metadataOnly: true
|
|
6163
6606
|
});
|
|
6164
|
-
|
|
6165
|
-
return "No matching code found. Try a different query or run index_codebase first.";
|
|
6166
|
-
}
|
|
6167
|
-
const formatted = results.map((r, idx) => {
|
|
6168
|
-
const location = `${r.filePath}:${r.startLine}-${r.endLine}`;
|
|
6169
|
-
const name = r.name ? `"${r.name}"` : "(anonymous)";
|
|
6170
|
-
return `[${idx + 1}] ${r.chunkType} ${name} at ${location} (score: ${r.score.toFixed(2)})`;
|
|
6171
|
-
});
|
|
6172
|
-
return `Found ${results.length} locations for "${args.query}":
|
|
6173
|
-
|
|
6174
|
-
${formatted.join("\n")}
|
|
6175
|
-
|
|
6176
|
-
Use Read tool to examine specific files.`;
|
|
6607
|
+
return formatCodebasePeek(results, args.query);
|
|
6177
6608
|
}
|
|
6178
6609
|
});
|
|
6179
6610
|
var index_codebase = tool({
|
|
@@ -6223,23 +6654,7 @@ var index_health_check = tool({
|
|
|
6223
6654
|
async execute() {
|
|
6224
6655
|
const indexer = getIndexer();
|
|
6225
6656
|
const result = await indexer.healthCheck();
|
|
6226
|
-
|
|
6227
|
-
return "Index is healthy. No stale entries found.";
|
|
6228
|
-
}
|
|
6229
|
-
const lines = [`Health check complete:`];
|
|
6230
|
-
if (result.removed > 0) {
|
|
6231
|
-
lines.push(` Removed stale entries: ${result.removed}`);
|
|
6232
|
-
}
|
|
6233
|
-
if (result.gcOrphanEmbeddings > 0) {
|
|
6234
|
-
lines.push(` Garbage collected orphan embeddings: ${result.gcOrphanEmbeddings}`);
|
|
6235
|
-
}
|
|
6236
|
-
if (result.gcOrphanChunks > 0) {
|
|
6237
|
-
lines.push(` Garbage collected orphan chunks: ${result.gcOrphanChunks}`);
|
|
6238
|
-
}
|
|
6239
|
-
if (result.filePaths.length > 0) {
|
|
6240
|
-
lines.push(` Cleaned paths: ${result.filePaths.join(", ")}`);
|
|
6241
|
-
}
|
|
6242
|
-
return lines.join("\n");
|
|
6657
|
+
return formatHealthCheck(result);
|
|
6243
6658
|
}
|
|
6244
6659
|
});
|
|
6245
6660
|
var index_metrics = tool({
|
|
@@ -6278,13 +6693,7 @@ var index_logs = tool({
|
|
|
6278
6693
|
} else {
|
|
6279
6694
|
logs = logger.getLogs(args.limit);
|
|
6280
6695
|
}
|
|
6281
|
-
|
|
6282
|
-
return "No logs recorded yet. Logs are captured during indexing and search operations.";
|
|
6283
|
-
}
|
|
6284
|
-
return logs.map((l) => {
|
|
6285
|
-
const dataStr = l.data ? ` ${JSON.stringify(l.data)}` : "";
|
|
6286
|
-
return `[${l.timestamp}] [${l.level.toUpperCase()}] [${l.category}] ${l.message}${dataStr}`;
|
|
6287
|
-
}).join("\n");
|
|
6696
|
+
return formatLogs(logs);
|
|
6288
6697
|
}
|
|
6289
6698
|
});
|
|
6290
6699
|
var find_similar = tool({
|
|
@@ -6299,7 +6708,7 @@ var find_similar = tool({
|
|
|
6299
6708
|
},
|
|
6300
6709
|
async execute(args) {
|
|
6301
6710
|
const indexer = getIndexer();
|
|
6302
|
-
const results = await indexer.findSimilar(args.code, args.limit
|
|
6711
|
+
const results = await indexer.findSimilar(args.code, args.limit, {
|
|
6303
6712
|
fileType: args.fileType,
|
|
6304
6713
|
directory: args.directory,
|
|
6305
6714
|
chunkType: args.chunkType,
|
|
@@ -6308,109 +6717,37 @@ var find_similar = tool({
|
|
|
6308
6717
|
if (results.length === 0) {
|
|
6309
6718
|
return "No similar code found. Try a different snippet or run index_codebase first.";
|
|
6310
6719
|
}
|
|
6311
|
-
const formatted = results.map((r, idx) => {
|
|
6312
|
-
const header = r.name ? `[${idx + 1}] ${r.chunkType} "${r.name}" in ${r.filePath}:${r.startLine}-${r.endLine}` : `[${idx + 1}] ${r.chunkType} in ${r.filePath}:${r.startLine}-${r.endLine}`;
|
|
6313
|
-
return `${header} (similarity: ${(r.score * 100).toFixed(1)}%)
|
|
6314
|
-
\`\`\`
|
|
6315
|
-
${r.content}
|
|
6316
|
-
\`\`\``;
|
|
6317
|
-
});
|
|
6318
6720
|
return `Found ${results.length} similar code blocks:
|
|
6319
6721
|
|
|
6320
|
-
${
|
|
6722
|
+
${formatSearchResults(results)}`;
|
|
6321
6723
|
}
|
|
6322
6724
|
});
|
|
6323
|
-
|
|
6324
|
-
|
|
6325
|
-
|
|
6326
|
-
|
|
6327
|
-
|
|
6328
|
-
|
|
6329
|
-
|
|
6330
|
-
|
|
6331
|
-
|
|
6332
|
-
|
|
6333
|
-
|
|
6334
|
-
|
|
6335
|
-
|
|
6336
|
-
|
|
6337
|
-
|
|
6338
|
-
|
|
6339
|
-
|
|
6340
|
-
}
|
|
6341
|
-
|
|
6342
|
-
|
|
6343
|
-
if (verbose) {
|
|
6344
|
-
if (stats.skippedFiles.length > 0) {
|
|
6345
|
-
const tooLarge = stats.skippedFiles.filter((f) => f.reason === "too_large");
|
|
6346
|
-
const excluded = stats.skippedFiles.filter((f) => f.reason === "excluded");
|
|
6347
|
-
const gitignored = stats.skippedFiles.filter((f) => f.reason === "gitignore");
|
|
6348
|
-
lines.push("");
|
|
6349
|
-
lines.push(`Skipped files: ${stats.skippedFiles.length}`);
|
|
6350
|
-
if (tooLarge.length > 0) {
|
|
6351
|
-
lines.push(` Too large (${tooLarge.length}): ${tooLarge.slice(0, 5).map((f) => f.path).join(", ")}${tooLarge.length > 5 ? "..." : ""}`);
|
|
6352
|
-
}
|
|
6353
|
-
if (excluded.length > 0) {
|
|
6354
|
-
lines.push(` Excluded (${excluded.length}): ${excluded.slice(0, 5).map((f) => f.path).join(", ")}${excluded.length > 5 ? "..." : ""}`);
|
|
6355
|
-
}
|
|
6356
|
-
if (gitignored.length > 0) {
|
|
6357
|
-
lines.push(` Gitignored (${gitignored.length}): ${gitignored.slice(0, 5).map((f) => f.path).join(", ")}${gitignored.length > 5 ? "..." : ""}`);
|
|
6358
|
-
}
|
|
6359
|
-
}
|
|
6360
|
-
if (stats.parseFailures.length > 0) {
|
|
6361
|
-
lines.push("");
|
|
6362
|
-
lines.push(`Files with no extractable chunks (${stats.parseFailures.length}): ${stats.parseFailures.slice(0, 10).join(", ")}${stats.parseFailures.length > 10 ? "..." : ""}`);
|
|
6725
|
+
var codebase_search = tool({
|
|
6726
|
+
description: "Search codebase by MEANING, not keywords. Returns full code content. Use when you need to see actual implementation. For just finding WHERE code is (saves ~90% tokens), use codebase_peek instead. For known identifiers like 'validateToken', use grep - it's faster.",
|
|
6727
|
+
args: {
|
|
6728
|
+
query: z.string().describe("Natural language description of what code you're looking for. Describe behavior, not syntax."),
|
|
6729
|
+
limit: z.number().optional().default(5).describe("Maximum number of results to return"),
|
|
6730
|
+
fileType: z.string().optional().describe("Filter by file extension (e.g., 'ts', 'py', 'rs')"),
|
|
6731
|
+
directory: z.string().optional().describe("Filter by directory path (e.g., 'src/utils', 'lib')"),
|
|
6732
|
+
chunkType: z.enum(["function", "class", "method", "interface", "type", "enum", "struct", "impl", "trait", "module", "other"]).optional().describe("Filter by code chunk type"),
|
|
6733
|
+
contextLines: z.number().optional().describe("Number of extra lines to include before/after each match (default: 0)")
|
|
6734
|
+
},
|
|
6735
|
+
async execute(args) {
|
|
6736
|
+
const indexer = getIndexer();
|
|
6737
|
+
const results = await indexer.search(args.query, args.limit ?? 5, {
|
|
6738
|
+
fileType: args.fileType,
|
|
6739
|
+
directory: args.directory,
|
|
6740
|
+
chunkType: args.chunkType,
|
|
6741
|
+
contextLines: args.contextLines
|
|
6742
|
+
});
|
|
6743
|
+
if (results.length === 0) {
|
|
6744
|
+
return "No matching code found. Try a different query or run index_codebase first.";
|
|
6363
6745
|
}
|
|
6746
|
+
return `Found ${results.length} results for "${args.query}":
|
|
6747
|
+
|
|
6748
|
+
${formatSearchResults(results, "score")}`;
|
|
6364
6749
|
}
|
|
6365
|
-
|
|
6366
|
-
}
|
|
6367
|
-
function formatStatus(status) {
|
|
6368
|
-
if (!status.indexed) {
|
|
6369
|
-
return "Codebase is not indexed. Run index_codebase to create an index.";
|
|
6370
|
-
}
|
|
6371
|
-
const lines = [
|
|
6372
|
-
`Index status:`,
|
|
6373
|
-
` Indexed chunks: ${status.vectorCount.toLocaleString()}`,
|
|
6374
|
-
` Provider: ${status.provider}`,
|
|
6375
|
-
` Model: ${status.model}`,
|
|
6376
|
-
` Location: ${status.indexPath}`
|
|
6377
|
-
];
|
|
6378
|
-
if (status.currentBranch !== "default") {
|
|
6379
|
-
lines.push(` Current branch: ${status.currentBranch}`);
|
|
6380
|
-
lines.push(` Base branch: ${status.baseBranch}`);
|
|
6381
|
-
}
|
|
6382
|
-
return lines.join("\n");
|
|
6383
|
-
}
|
|
6384
|
-
function formatProgressTitle(progress) {
|
|
6385
|
-
switch (progress.phase) {
|
|
6386
|
-
case "scanning":
|
|
6387
|
-
return "Scanning files...";
|
|
6388
|
-
case "parsing":
|
|
6389
|
-
return `Parsing: ${progress.filesProcessed}/${progress.totalFiles} files`;
|
|
6390
|
-
case "embedding":
|
|
6391
|
-
return `Embedding: ${progress.chunksProcessed}/${progress.totalChunks} chunks`;
|
|
6392
|
-
case "storing":
|
|
6393
|
-
return "Storing index...";
|
|
6394
|
-
case "complete":
|
|
6395
|
-
return "Indexing complete";
|
|
6396
|
-
default:
|
|
6397
|
-
return "Indexing...";
|
|
6398
|
-
}
|
|
6399
|
-
}
|
|
6400
|
-
function calculatePercentage(progress) {
|
|
6401
|
-
if (progress.phase === "scanning") return 0;
|
|
6402
|
-
if (progress.phase === "complete") return 100;
|
|
6403
|
-
if (progress.phase === "parsing") {
|
|
6404
|
-
if (progress.totalFiles === 0) return 5;
|
|
6405
|
-
return Math.round(5 + progress.filesProcessed / progress.totalFiles * 15);
|
|
6406
|
-
}
|
|
6407
|
-
if (progress.phase === "embedding") {
|
|
6408
|
-
if (progress.totalChunks === 0) return 20;
|
|
6409
|
-
return Math.round(20 + progress.chunksProcessed / progress.totalChunks * 70);
|
|
6410
|
-
}
|
|
6411
|
-
if (progress.phase === "storing") return 95;
|
|
6412
|
-
return 0;
|
|
6413
|
-
}
|
|
6750
|
+
});
|
|
6414
6751
|
|
|
6415
6752
|
// src/commands/loader.ts
|
|
6416
6753
|
import { existsSync as existsSync5, readdirSync as readdirSync2, readFileSync as readFileSync5 } from "fs";
|
|
@@ -6489,14 +6826,20 @@ var plugin = async ({ directory }) => {
|
|
|
6489
6826
|
const config = parseConfig(rawConfig);
|
|
6490
6827
|
initializeTools(projectRoot, config);
|
|
6491
6828
|
const indexer = new Indexer(projectRoot, config);
|
|
6492
|
-
|
|
6829
|
+
const isValidProject = !config.indexing.requireProjectMarker || hasProjectMarker(projectRoot);
|
|
6830
|
+
if (!isValidProject) {
|
|
6831
|
+
console.warn(
|
|
6832
|
+
`[codebase-index] Skipping file watching and auto-indexing: no project marker found in "${projectRoot}". Set "indexing.requireProjectMarker": false in config to override.`
|
|
6833
|
+
);
|
|
6834
|
+
}
|
|
6835
|
+
if (config.indexing.autoIndex && isValidProject) {
|
|
6493
6836
|
indexer.initialize().then(() => {
|
|
6494
6837
|
indexer.index().catch(() => {
|
|
6495
6838
|
});
|
|
6496
6839
|
}).catch(() => {
|
|
6497
6840
|
});
|
|
6498
6841
|
}
|
|
6499
|
-
if (config.indexing.watchFiles) {
|
|
6842
|
+
if (config.indexing.watchFiles && isValidProject) {
|
|
6500
6843
|
createWatcherWithIndexer(indexer, projectRoot, config);
|
|
6501
6844
|
}
|
|
6502
6845
|
return {
|