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.cjs
CHANGED
|
@@ -662,7 +662,7 @@ var path8 = __toESM(require("path"), 1);
|
|
|
662
662
|
var os3 = __toESM(require("os"), 1);
|
|
663
663
|
var import_url2 = require("url");
|
|
664
664
|
|
|
665
|
-
// src/config/
|
|
665
|
+
// src/config/constants.ts
|
|
666
666
|
var DEFAULT_INCLUDE = [
|
|
667
667
|
"**/*.{ts,tsx,js,jsx,mjs,cjs}",
|
|
668
668
|
"**/*.{py,pyi}",
|
|
@@ -690,6 +690,80 @@ var DEFAULT_EXCLUDE = [
|
|
|
690
690
|
"**/.nuxt/**",
|
|
691
691
|
"**/.opencode/**"
|
|
692
692
|
];
|
|
693
|
+
var EMBEDDING_MODELS = {
|
|
694
|
+
"google": {
|
|
695
|
+
// `text-embedding-004` is DEPRECATED - https://ai.google.dev/gemini-api/docs/deprecations
|
|
696
|
+
"text-embedding-005": {
|
|
697
|
+
provider: "google",
|
|
698
|
+
model: "text-embedding-005",
|
|
699
|
+
dimensions: 768,
|
|
700
|
+
maxTokens: 2048,
|
|
701
|
+
costPer1MTokens: 0.025,
|
|
702
|
+
taskAble: false
|
|
703
|
+
// Note: on reality, this model allows for task-specific embeddings. See: https://docs.cloud.google.com/vertex-ai/generative-ai/docs/embeddings/task-types
|
|
704
|
+
},
|
|
705
|
+
"gemini-embedding-001": {
|
|
706
|
+
provider: "google",
|
|
707
|
+
model: "gemini-embedding-001",
|
|
708
|
+
// Native output is 3072D, but we use Matryoshka truncation via outputDimensionality
|
|
709
|
+
// to reduce to 1536D for better storage/search efficiency with minimal quality loss.
|
|
710
|
+
// Google recommends 768, 1536, or 3072. See: https://ai.google.dev/gemini-api/docs/embeddings
|
|
711
|
+
dimensions: 1536,
|
|
712
|
+
maxTokens: 2048,
|
|
713
|
+
costPer1MTokens: 0.15,
|
|
714
|
+
taskAble: true
|
|
715
|
+
}
|
|
716
|
+
},
|
|
717
|
+
"openai": {
|
|
718
|
+
"text-embedding-3-small": {
|
|
719
|
+
provider: "openai",
|
|
720
|
+
model: "text-embedding-3-small",
|
|
721
|
+
dimensions: 1536,
|
|
722
|
+
maxTokens: 8191,
|
|
723
|
+
costPer1MTokens: 0.02
|
|
724
|
+
},
|
|
725
|
+
"text-embedding-3-large": {
|
|
726
|
+
provider: "openai",
|
|
727
|
+
model: "text-embedding-3-large",
|
|
728
|
+
dimensions: 3072,
|
|
729
|
+
maxTokens: 8191,
|
|
730
|
+
costPer1MTokens: 0.13
|
|
731
|
+
}
|
|
732
|
+
},
|
|
733
|
+
"ollama": {
|
|
734
|
+
"nomic-embed-text": {
|
|
735
|
+
provider: "ollama",
|
|
736
|
+
model: "nomic-embed-text",
|
|
737
|
+
dimensions: 768,
|
|
738
|
+
maxTokens: 8192,
|
|
739
|
+
costPer1MTokens: 0
|
|
740
|
+
},
|
|
741
|
+
"mxbai-embed-large": {
|
|
742
|
+
provider: "ollama",
|
|
743
|
+
model: "mxbai-embed-large",
|
|
744
|
+
dimensions: 1024,
|
|
745
|
+
maxTokens: 512,
|
|
746
|
+
costPer1MTokens: 0
|
|
747
|
+
}
|
|
748
|
+
},
|
|
749
|
+
"github-copilot": {
|
|
750
|
+
"text-embedding-3-small": {
|
|
751
|
+
provider: "github-copilot",
|
|
752
|
+
model: "text-embedding-3-small",
|
|
753
|
+
dimensions: 1536,
|
|
754
|
+
maxTokens: 8191,
|
|
755
|
+
costPer1MTokens: 0
|
|
756
|
+
}
|
|
757
|
+
}
|
|
758
|
+
};
|
|
759
|
+
var DEFAULT_PROVIDER_MODELS = {
|
|
760
|
+
"github-copilot": "text-embedding-3-small",
|
|
761
|
+
"openai": "text-embedding-3-small",
|
|
762
|
+
"google": "text-embedding-005",
|
|
763
|
+
"ollama": "nomic-embed-text"
|
|
764
|
+
};
|
|
765
|
+
|
|
766
|
+
// src/config/schema.ts
|
|
693
767
|
function getDefaultIndexingConfig() {
|
|
694
768
|
return {
|
|
695
769
|
autoIndex: false,
|
|
@@ -701,7 +775,8 @@ function getDefaultIndexingConfig() {
|
|
|
701
775
|
retryDelayMs: 1e3,
|
|
702
776
|
autoGc: true,
|
|
703
777
|
gcIntervalDays: 7,
|
|
704
|
-
gcOrphanThreshold: 100
|
|
778
|
+
gcOrphanThreshold: 100,
|
|
779
|
+
requireProjectMarker: true
|
|
705
780
|
};
|
|
706
781
|
}
|
|
707
782
|
function getDefaultSearchConfig() {
|
|
@@ -725,11 +800,13 @@ function getDefaultDebugConfig() {
|
|
|
725
800
|
metrics: true
|
|
726
801
|
};
|
|
727
802
|
}
|
|
728
|
-
var VALID_PROVIDERS = ["auto", "github-copilot", "openai", "google", "ollama"];
|
|
729
803
|
var VALID_SCOPES = ["project", "global"];
|
|
730
804
|
var VALID_LOG_LEVELS = ["error", "warn", "info", "debug"];
|
|
731
805
|
function isValidProvider(value) {
|
|
732
|
-
return typeof value === "string" &&
|
|
806
|
+
return typeof value === "string" && Object.keys(EMBEDDING_MODELS).includes(value);
|
|
807
|
+
}
|
|
808
|
+
function isValidModel(value, provider) {
|
|
809
|
+
return typeof value === "string" && Object.keys(EMBEDDING_MODELS[provider]).includes(value);
|
|
733
810
|
}
|
|
734
811
|
function isValidScope(value) {
|
|
735
812
|
return typeof value === "string" && VALID_SCOPES.includes(value);
|
|
@@ -756,7 +833,8 @@ function parseConfig(raw) {
|
|
|
756
833
|
retryDelayMs: typeof rawIndexing.retryDelayMs === "number" ? rawIndexing.retryDelayMs : defaultIndexing.retryDelayMs,
|
|
757
834
|
autoGc: typeof rawIndexing.autoGc === "boolean" ? rawIndexing.autoGc : defaultIndexing.autoGc,
|
|
758
835
|
gcIntervalDays: typeof rawIndexing.gcIntervalDays === "number" ? Math.max(1, rawIndexing.gcIntervalDays) : defaultIndexing.gcIntervalDays,
|
|
759
|
-
gcOrphanThreshold: typeof rawIndexing.gcOrphanThreshold === "number" ? Math.max(0, rawIndexing.gcOrphanThreshold) : defaultIndexing.gcOrphanThreshold
|
|
836
|
+
gcOrphanThreshold: typeof rawIndexing.gcOrphanThreshold === "number" ? Math.max(0, rawIndexing.gcOrphanThreshold) : defaultIndexing.gcOrphanThreshold,
|
|
837
|
+
requireProjectMarker: typeof rawIndexing.requireProjectMarker === "boolean" ? rawIndexing.requireProjectMarker : defaultIndexing.requireProjectMarker
|
|
760
838
|
};
|
|
761
839
|
const rawSearch = input.search && typeof input.search === "object" ? input.search : {};
|
|
762
840
|
const search = {
|
|
@@ -777,9 +855,19 @@ function parseConfig(raw) {
|
|
|
777
855
|
logBranch: typeof rawDebug.logBranch === "boolean" ? rawDebug.logBranch : defaultDebug.logBranch,
|
|
778
856
|
metrics: typeof rawDebug.metrics === "boolean" ? rawDebug.metrics : defaultDebug.metrics
|
|
779
857
|
};
|
|
858
|
+
let embeddingProvider;
|
|
859
|
+
let embeddingModel = void 0;
|
|
860
|
+
if (isValidProvider(input.embeddingProvider)) {
|
|
861
|
+
embeddingProvider = input.embeddingProvider;
|
|
862
|
+
if (input.embeddingModel) {
|
|
863
|
+
embeddingModel = isValidModel(input.embeddingModel, embeddingProvider) ? input.embeddingModel : DEFAULT_PROVIDER_MODELS[embeddingProvider];
|
|
864
|
+
}
|
|
865
|
+
} else {
|
|
866
|
+
embeddingProvider = "auto";
|
|
867
|
+
}
|
|
780
868
|
return {
|
|
781
|
-
embeddingProvider
|
|
782
|
-
embeddingModel
|
|
869
|
+
embeddingProvider,
|
|
870
|
+
embeddingModel,
|
|
783
871
|
scope: isValidScope(input.scope) ? input.scope : "project",
|
|
784
872
|
include: isStringArray(input.include) ? input.include : DEFAULT_INCLUDE,
|
|
785
873
|
exclude: isStringArray(input.exclude) ? input.exclude : DEFAULT_EXCLUDE,
|
|
@@ -788,64 +876,12 @@ function parseConfig(raw) {
|
|
|
788
876
|
debug
|
|
789
877
|
};
|
|
790
878
|
}
|
|
791
|
-
var EMBEDDING_MODELS = {
|
|
792
|
-
"github-copilot/text-embedding-3-small": {
|
|
793
|
-
provider: "github-copilot",
|
|
794
|
-
model: "text-embedding-3-small",
|
|
795
|
-
dimensions: 1536,
|
|
796
|
-
maxTokens: 8191,
|
|
797
|
-
costPer1MTokens: 0
|
|
798
|
-
},
|
|
799
|
-
"openai/text-embedding-3-small": {
|
|
800
|
-
provider: "openai",
|
|
801
|
-
model: "text-embedding-3-small",
|
|
802
|
-
dimensions: 1536,
|
|
803
|
-
maxTokens: 8191,
|
|
804
|
-
costPer1MTokens: 0.02
|
|
805
|
-
},
|
|
806
|
-
"openai/text-embedding-3-large": {
|
|
807
|
-
provider: "openai",
|
|
808
|
-
model: "text-embedding-3-large",
|
|
809
|
-
dimensions: 3072,
|
|
810
|
-
maxTokens: 8191,
|
|
811
|
-
costPer1MTokens: 0.13
|
|
812
|
-
},
|
|
813
|
-
"google/text-embedding-004": {
|
|
814
|
-
provider: "google",
|
|
815
|
-
model: "text-embedding-004",
|
|
816
|
-
dimensions: 768,
|
|
817
|
-
maxTokens: 2048,
|
|
818
|
-
costPer1MTokens: 0
|
|
819
|
-
},
|
|
820
|
-
"ollama/nomic-embed-text": {
|
|
821
|
-
provider: "ollama",
|
|
822
|
-
model: "nomic-embed-text",
|
|
823
|
-
dimensions: 768,
|
|
824
|
-
maxTokens: 8192,
|
|
825
|
-
costPer1MTokens: 0
|
|
826
|
-
},
|
|
827
|
-
"ollama/mxbai-embed-large": {
|
|
828
|
-
provider: "ollama",
|
|
829
|
-
model: "mxbai-embed-large",
|
|
830
|
-
dimensions: 1024,
|
|
831
|
-
maxTokens: 512,
|
|
832
|
-
costPer1MTokens: 0
|
|
833
|
-
}
|
|
834
|
-
};
|
|
835
879
|
function getDefaultModelForProvider(provider) {
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
case "openai":
|
|
840
|
-
return EMBEDDING_MODELS["openai/text-embedding-3-small"];
|
|
841
|
-
case "google":
|
|
842
|
-
return EMBEDDING_MODELS["google/text-embedding-004"];
|
|
843
|
-
case "ollama":
|
|
844
|
-
return EMBEDDING_MODELS["ollama/nomic-embed-text"];
|
|
845
|
-
default:
|
|
846
|
-
return EMBEDDING_MODELS["github-copilot/text-embedding-3-small"];
|
|
847
|
-
}
|
|
880
|
+
const models = EMBEDDING_MODELS[provider];
|
|
881
|
+
const providerDefault = DEFAULT_PROVIDER_MODELS[provider];
|
|
882
|
+
return models[providerDefault];
|
|
848
883
|
}
|
|
884
|
+
var availableProviders = Object.keys(EMBEDDING_MODELS);
|
|
849
885
|
|
|
850
886
|
// src/indexer/index.ts
|
|
851
887
|
var import_fs4 = require("fs");
|
|
@@ -874,7 +910,7 @@ function pTimeout(promise, options) {
|
|
|
874
910
|
} = options;
|
|
875
911
|
let timer;
|
|
876
912
|
let abortHandler;
|
|
877
|
-
const wrappedPromise = new Promise((
|
|
913
|
+
const wrappedPromise = new Promise((resolve5, reject) => {
|
|
878
914
|
if (typeof milliseconds !== "number" || Math.sign(milliseconds) !== 1) {
|
|
879
915
|
throw new TypeError(`Expected \`milliseconds\` to be a positive number, got \`${milliseconds}\``);
|
|
880
916
|
}
|
|
@@ -888,7 +924,7 @@ function pTimeout(promise, options) {
|
|
|
888
924
|
};
|
|
889
925
|
signal.addEventListener("abort", abortHandler, { once: true });
|
|
890
926
|
}
|
|
891
|
-
promise.then(
|
|
927
|
+
promise.then(resolve5, reject);
|
|
892
928
|
if (milliseconds === Number.POSITIVE_INFINITY) {
|
|
893
929
|
return;
|
|
894
930
|
}
|
|
@@ -896,7 +932,7 @@ function pTimeout(promise, options) {
|
|
|
896
932
|
timer = customTimers.setTimeout.call(void 0, () => {
|
|
897
933
|
if (fallback) {
|
|
898
934
|
try {
|
|
899
|
-
|
|
935
|
+
resolve5(fallback());
|
|
900
936
|
} catch (error) {
|
|
901
937
|
reject(error);
|
|
902
938
|
}
|
|
@@ -906,7 +942,7 @@ function pTimeout(promise, options) {
|
|
|
906
942
|
promise.cancel();
|
|
907
943
|
}
|
|
908
944
|
if (message === false) {
|
|
909
|
-
|
|
945
|
+
resolve5();
|
|
910
946
|
} else if (message instanceof Error) {
|
|
911
947
|
reject(message);
|
|
912
948
|
} else {
|
|
@@ -1296,7 +1332,7 @@ var PQueue = class extends import_index.default {
|
|
|
1296
1332
|
// Assign unique ID if not provided
|
|
1297
1333
|
id: options.id ?? (this.#idAssigner++).toString()
|
|
1298
1334
|
};
|
|
1299
|
-
return new Promise((
|
|
1335
|
+
return new Promise((resolve5, reject) => {
|
|
1300
1336
|
const taskSymbol = /* @__PURE__ */ Symbol(`task-${options.id}`);
|
|
1301
1337
|
this.#queue.enqueue(async () => {
|
|
1302
1338
|
this.#pending++;
|
|
@@ -1334,7 +1370,7 @@ var PQueue = class extends import_index.default {
|
|
|
1334
1370
|
})]);
|
|
1335
1371
|
}
|
|
1336
1372
|
const result = await operation;
|
|
1337
|
-
|
|
1373
|
+
resolve5(result);
|
|
1338
1374
|
this.emit("completed", result);
|
|
1339
1375
|
} catch (error) {
|
|
1340
1376
|
reject(error);
|
|
@@ -1491,13 +1527,13 @@ var PQueue = class extends import_index.default {
|
|
|
1491
1527
|
});
|
|
1492
1528
|
}
|
|
1493
1529
|
async #onEvent(event, filter) {
|
|
1494
|
-
return new Promise((
|
|
1530
|
+
return new Promise((resolve5) => {
|
|
1495
1531
|
const listener = () => {
|
|
1496
1532
|
if (filter && !filter()) {
|
|
1497
1533
|
return;
|
|
1498
1534
|
}
|
|
1499
1535
|
this.off(event, listener);
|
|
1500
|
-
|
|
1536
|
+
resolve5();
|
|
1501
1537
|
};
|
|
1502
1538
|
this.on(event, listener);
|
|
1503
1539
|
});
|
|
@@ -1782,7 +1818,7 @@ async function onAttemptFailure({ error, attemptNumber, retriesConsumed, startTi
|
|
|
1782
1818
|
const finalDelay = Math.min(delayTime, remainingTime);
|
|
1783
1819
|
options.signal?.throwIfAborted();
|
|
1784
1820
|
if (finalDelay > 0) {
|
|
1785
|
-
await new Promise((
|
|
1821
|
+
await new Promise((resolve5, reject) => {
|
|
1786
1822
|
const onAbort = () => {
|
|
1787
1823
|
clearTimeout(timeoutToken);
|
|
1788
1824
|
options.signal?.removeEventListener("abort", onAbort);
|
|
@@ -1790,7 +1826,7 @@ async function onAttemptFailure({ error, attemptNumber, retriesConsumed, startTi
|
|
|
1790
1826
|
};
|
|
1791
1827
|
const timeoutToken = setTimeout(() => {
|
|
1792
1828
|
options.signal?.removeEventListener("abort", onAbort);
|
|
1793
|
-
|
|
1829
|
+
resolve5();
|
|
1794
1830
|
}, finalDelay);
|
|
1795
1831
|
if (options.unref) {
|
|
1796
1832
|
timeoutToken.unref?.();
|
|
@@ -1854,12 +1890,6 @@ async function pRetry(input, options = {}) {
|
|
|
1854
1890
|
var import_fs = require("fs");
|
|
1855
1891
|
var path = __toESM(require("path"), 1);
|
|
1856
1892
|
var os = __toESM(require("os"), 1);
|
|
1857
|
-
var EMBEDDING_CAPABLE_PROVIDERS = [
|
|
1858
|
-
"github-copilot",
|
|
1859
|
-
"openai",
|
|
1860
|
-
"google",
|
|
1861
|
-
"ollama"
|
|
1862
|
-
];
|
|
1863
1893
|
function getOpenCodeAuthPath() {
|
|
1864
1894
|
return path.join(os.homedir(), ".local", "share", "opencode", "auth.json");
|
|
1865
1895
|
}
|
|
@@ -1873,21 +1903,34 @@ function loadOpenCodeAuth() {
|
|
|
1873
1903
|
}
|
|
1874
1904
|
return {};
|
|
1875
1905
|
}
|
|
1876
|
-
async function detectEmbeddingProvider(preferredProvider) {
|
|
1877
|
-
|
|
1878
|
-
|
|
1879
|
-
if (
|
|
1906
|
+
async function detectEmbeddingProvider(preferredProvider, model) {
|
|
1907
|
+
const credentials = await getProviderCredentials(preferredProvider);
|
|
1908
|
+
if (credentials) {
|
|
1909
|
+
if (!model) {
|
|
1880
1910
|
return {
|
|
1881
1911
|
provider: preferredProvider,
|
|
1882
1912
|
credentials,
|
|
1883
1913
|
modelInfo: getDefaultModelForProvider(preferredProvider)
|
|
1884
1914
|
};
|
|
1885
1915
|
}
|
|
1886
|
-
|
|
1887
|
-
|
|
1888
|
-
|
|
1916
|
+
if (!isValidModel(model, preferredProvider)) {
|
|
1917
|
+
throw new Error(
|
|
1918
|
+
`Model '${model}' is not supported by provider '${preferredProvider}'`
|
|
1919
|
+
);
|
|
1920
|
+
}
|
|
1921
|
+
const providerModels = EMBEDDING_MODELS[preferredProvider];
|
|
1922
|
+
return {
|
|
1923
|
+
provider: preferredProvider,
|
|
1924
|
+
credentials,
|
|
1925
|
+
modelInfo: providerModels[model]
|
|
1926
|
+
};
|
|
1889
1927
|
}
|
|
1890
|
-
|
|
1928
|
+
throw new Error(
|
|
1929
|
+
`Preferred provider '${preferredProvider}' is not configured or authenticated`
|
|
1930
|
+
);
|
|
1931
|
+
}
|
|
1932
|
+
async function tryDetectProvider() {
|
|
1933
|
+
for (const provider of availableProviders) {
|
|
1891
1934
|
const credentials = await getProviderCredentials(provider);
|
|
1892
1935
|
if (credentials) {
|
|
1893
1936
|
return {
|
|
@@ -1898,7 +1941,7 @@ async function detectEmbeddingProvider(preferredProvider) {
|
|
|
1898
1941
|
}
|
|
1899
1942
|
}
|
|
1900
1943
|
throw new Error(
|
|
1901
|
-
`No embedding-capable provider found. Please authenticate with OpenCode using one of: ${
|
|
1944
|
+
`No embedding-capable provider found. Please authenticate with OpenCode using one of: ${availableProviders.join(", ")}.`
|
|
1902
1945
|
);
|
|
1903
1946
|
}
|
|
1904
1947
|
async function getProviderCredentials(provider) {
|
|
@@ -1996,18 +2039,20 @@ function getProviderDisplayName(provider) {
|
|
|
1996
2039
|
}
|
|
1997
2040
|
|
|
1998
2041
|
// src/embeddings/provider.ts
|
|
1999
|
-
function createEmbeddingProvider(
|
|
2000
|
-
switch (
|
|
2042
|
+
function createEmbeddingProvider(configuredProviderInfo) {
|
|
2043
|
+
switch (configuredProviderInfo.provider) {
|
|
2001
2044
|
case "github-copilot":
|
|
2002
|
-
return new GitHubCopilotEmbeddingProvider(credentials, modelInfo);
|
|
2045
|
+
return new GitHubCopilotEmbeddingProvider(configuredProviderInfo.credentials, configuredProviderInfo.modelInfo);
|
|
2003
2046
|
case "openai":
|
|
2004
|
-
return new OpenAIEmbeddingProvider(credentials, modelInfo);
|
|
2047
|
+
return new OpenAIEmbeddingProvider(configuredProviderInfo.credentials, configuredProviderInfo.modelInfo);
|
|
2005
2048
|
case "google":
|
|
2006
|
-
return new GoogleEmbeddingProvider(credentials, modelInfo);
|
|
2049
|
+
return new GoogleEmbeddingProvider(configuredProviderInfo.credentials, configuredProviderInfo.modelInfo);
|
|
2007
2050
|
case "ollama":
|
|
2008
|
-
return new OllamaEmbeddingProvider(credentials, modelInfo);
|
|
2009
|
-
default:
|
|
2010
|
-
|
|
2051
|
+
return new OllamaEmbeddingProvider(configuredProviderInfo.credentials, configuredProviderInfo.modelInfo);
|
|
2052
|
+
default: {
|
|
2053
|
+
const _exhaustive = configuredProviderInfo;
|
|
2054
|
+
throw new Error(`Unsupported embedding provider: ${_exhaustive.provider}`);
|
|
2055
|
+
}
|
|
2011
2056
|
}
|
|
2012
2057
|
}
|
|
2013
2058
|
var GitHubCopilotEmbeddingProvider = class {
|
|
@@ -2021,8 +2066,15 @@ var GitHubCopilotEmbeddingProvider = class {
|
|
|
2021
2066
|
}
|
|
2022
2067
|
return this.credentials.refreshToken;
|
|
2023
2068
|
}
|
|
2024
|
-
async
|
|
2025
|
-
const result = await this.embedBatch([
|
|
2069
|
+
async embedQuery(query) {
|
|
2070
|
+
const result = await this.embedBatch([query]);
|
|
2071
|
+
return {
|
|
2072
|
+
embedding: result.embeddings[0],
|
|
2073
|
+
tokensUsed: result.totalTokensUsed
|
|
2074
|
+
};
|
|
2075
|
+
}
|
|
2076
|
+
async embedDocument(document) {
|
|
2077
|
+
const result = await this.embedBatch([document]);
|
|
2026
2078
|
return {
|
|
2027
2079
|
embedding: result.embeddings[0],
|
|
2028
2080
|
tokensUsed: result.totalTokensUsed
|
|
@@ -2062,8 +2114,15 @@ var OpenAIEmbeddingProvider = class {
|
|
|
2062
2114
|
this.credentials = credentials;
|
|
2063
2115
|
this.modelInfo = modelInfo;
|
|
2064
2116
|
}
|
|
2065
|
-
async
|
|
2066
|
-
const result = await this.embedBatch([
|
|
2117
|
+
async embedQuery(query) {
|
|
2118
|
+
const result = await this.embedBatch([query]);
|
|
2119
|
+
return {
|
|
2120
|
+
embedding: result.embeddings[0],
|
|
2121
|
+
tokensUsed: result.totalTokensUsed
|
|
2122
|
+
};
|
|
2123
|
+
}
|
|
2124
|
+
async embedDocument(document) {
|
|
2125
|
+
const result = await this.embedBatch([document]);
|
|
2067
2126
|
return {
|
|
2068
2127
|
embedding: result.embeddings[0],
|
|
2069
2128
|
tokensUsed: result.totalTokensUsed
|
|
@@ -2095,33 +2154,61 @@ var OpenAIEmbeddingProvider = class {
|
|
|
2095
2154
|
return this.modelInfo;
|
|
2096
2155
|
}
|
|
2097
2156
|
};
|
|
2098
|
-
var GoogleEmbeddingProvider = class {
|
|
2157
|
+
var GoogleEmbeddingProvider = class _GoogleEmbeddingProvider {
|
|
2099
2158
|
constructor(credentials, modelInfo) {
|
|
2100
2159
|
this.credentials = credentials;
|
|
2101
2160
|
this.modelInfo = modelInfo;
|
|
2102
2161
|
}
|
|
2103
|
-
|
|
2104
|
-
|
|
2162
|
+
static BATCH_SIZE = 20;
|
|
2163
|
+
async embedQuery(query) {
|
|
2164
|
+
const taskType = this.modelInfo.taskAble ? "CODE_RETRIEVAL_QUERY" : void 0;
|
|
2165
|
+
const result = await this.embedWithTaskType([query], taskType);
|
|
2166
|
+
return {
|
|
2167
|
+
embedding: result.embeddings[0],
|
|
2168
|
+
tokensUsed: result.totalTokensUsed
|
|
2169
|
+
};
|
|
2170
|
+
}
|
|
2171
|
+
async embedDocument(document) {
|
|
2172
|
+
const taskType = this.modelInfo.taskAble ? "RETRIEVAL_DOCUMENT" : void 0;
|
|
2173
|
+
const result = await this.embedWithTaskType([document], taskType);
|
|
2105
2174
|
return {
|
|
2106
2175
|
embedding: result.embeddings[0],
|
|
2107
2176
|
tokensUsed: result.totalTokensUsed
|
|
2108
2177
|
};
|
|
2109
2178
|
}
|
|
2110
2179
|
async embedBatch(texts) {
|
|
2111
|
-
const
|
|
2112
|
-
|
|
2180
|
+
const taskType = this.modelInfo.taskAble ? "RETRIEVAL_DOCUMENT" : void 0;
|
|
2181
|
+
return this.embedWithTaskType(texts, taskType);
|
|
2182
|
+
}
|
|
2183
|
+
/**
|
|
2184
|
+
* Embeds texts using the Google embedContent API.
|
|
2185
|
+
* Sends multiple texts as parts in batched requests (up to BATCH_SIZE per call).
|
|
2186
|
+
* When taskType is provided (gemini-embedding-001), includes it in the request
|
|
2187
|
+
* for task-specific embedding optimization.
|
|
2188
|
+
*/
|
|
2189
|
+
async embedWithTaskType(texts, taskType) {
|
|
2190
|
+
const batches = [];
|
|
2191
|
+
for (let i = 0; i < texts.length; i += _GoogleEmbeddingProvider.BATCH_SIZE) {
|
|
2192
|
+
batches.push(texts.slice(i, i + _GoogleEmbeddingProvider.BATCH_SIZE));
|
|
2193
|
+
}
|
|
2194
|
+
const batchResults = await Promise.all(
|
|
2195
|
+
batches.map(async (batch) => {
|
|
2196
|
+
const requests = batch.map((text) => ({
|
|
2197
|
+
model: `models/${this.modelInfo.model}`,
|
|
2198
|
+
content: {
|
|
2199
|
+
parts: [{ text }]
|
|
2200
|
+
},
|
|
2201
|
+
taskType,
|
|
2202
|
+
outputDimensionality: this.modelInfo.dimensions
|
|
2203
|
+
}));
|
|
2113
2204
|
const response = await fetch(
|
|
2114
|
-
`${this.credentials.baseUrl}/models/${this.modelInfo.model}:
|
|
2205
|
+
`${this.credentials.baseUrl}/models/${this.modelInfo.model}:batchEmbedContents?key=${this.credentials.apiKey}`,
|
|
2115
2206
|
{
|
|
2116
2207
|
method: "POST",
|
|
2117
2208
|
headers: {
|
|
2118
2209
|
"Content-Type": "application/json"
|
|
2119
2210
|
},
|
|
2120
|
-
body: JSON.stringify({
|
|
2121
|
-
content: {
|
|
2122
|
-
parts: [{ text }]
|
|
2123
|
-
}
|
|
2124
|
-
})
|
|
2211
|
+
body: JSON.stringify({ requests })
|
|
2125
2212
|
}
|
|
2126
2213
|
);
|
|
2127
2214
|
if (!response.ok) {
|
|
@@ -2130,14 +2217,14 @@ var GoogleEmbeddingProvider = class {
|
|
|
2130
2217
|
}
|
|
2131
2218
|
const data = await response.json();
|
|
2132
2219
|
return {
|
|
2133
|
-
|
|
2134
|
-
tokensUsed: Math.ceil(text.length / 4)
|
|
2220
|
+
embeddings: data.embeddings.map((e) => e.values),
|
|
2221
|
+
tokensUsed: batch.reduce((sum, text) => sum + Math.ceil(text.length / 4), 0)
|
|
2135
2222
|
};
|
|
2136
2223
|
})
|
|
2137
2224
|
);
|
|
2138
2225
|
return {
|
|
2139
|
-
embeddings:
|
|
2140
|
-
totalTokensUsed:
|
|
2226
|
+
embeddings: batchResults.flatMap((r) => r.embeddings),
|
|
2227
|
+
totalTokensUsed: batchResults.reduce((sum, r) => sum + r.tokensUsed, 0)
|
|
2141
2228
|
};
|
|
2142
2229
|
}
|
|
2143
2230
|
getModelInfo() {
|
|
@@ -2149,29 +2236,44 @@ var OllamaEmbeddingProvider = class {
|
|
|
2149
2236
|
this.credentials = credentials;
|
|
2150
2237
|
this.modelInfo = modelInfo;
|
|
2151
2238
|
}
|
|
2152
|
-
async
|
|
2153
|
-
const
|
|
2154
|
-
method: "POST",
|
|
2155
|
-
headers: {
|
|
2156
|
-
"Content-Type": "application/json"
|
|
2157
|
-
},
|
|
2158
|
-
body: JSON.stringify({
|
|
2159
|
-
model: this.modelInfo.model,
|
|
2160
|
-
prompt: text
|
|
2161
|
-
})
|
|
2162
|
-
});
|
|
2163
|
-
if (!response.ok) {
|
|
2164
|
-
const error = await response.text();
|
|
2165
|
-
throw new Error(`Ollama embedding API error: ${response.status} - ${error}`);
|
|
2166
|
-
}
|
|
2167
|
-
const data = await response.json();
|
|
2239
|
+
async embedQuery(query) {
|
|
2240
|
+
const result = await this.embedBatch([query]);
|
|
2168
2241
|
return {
|
|
2169
|
-
embedding:
|
|
2170
|
-
tokensUsed:
|
|
2242
|
+
embedding: result.embeddings[0],
|
|
2243
|
+
tokensUsed: result.totalTokensUsed
|
|
2244
|
+
};
|
|
2245
|
+
}
|
|
2246
|
+
async embedDocument(document) {
|
|
2247
|
+
const result = await this.embedBatch([document]);
|
|
2248
|
+
return {
|
|
2249
|
+
embedding: result.embeddings[0],
|
|
2250
|
+
tokensUsed: result.totalTokensUsed
|
|
2171
2251
|
};
|
|
2172
2252
|
}
|
|
2173
2253
|
async embedBatch(texts) {
|
|
2174
|
-
const results = await Promise.all(
|
|
2254
|
+
const results = await Promise.all(
|
|
2255
|
+
texts.map(async (text) => {
|
|
2256
|
+
const response = await fetch(`${this.credentials.baseUrl}/api/embeddings`, {
|
|
2257
|
+
method: "POST",
|
|
2258
|
+
headers: {
|
|
2259
|
+
"Content-Type": "application/json"
|
|
2260
|
+
},
|
|
2261
|
+
body: JSON.stringify({
|
|
2262
|
+
model: this.modelInfo.model,
|
|
2263
|
+
prompt: text
|
|
2264
|
+
})
|
|
2265
|
+
});
|
|
2266
|
+
if (!response.ok) {
|
|
2267
|
+
const error = await response.text();
|
|
2268
|
+
throw new Error(`Ollama embedding API error: ${response.status} - ${error}`);
|
|
2269
|
+
}
|
|
2270
|
+
const data = await response.json();
|
|
2271
|
+
return {
|
|
2272
|
+
embedding: data.embedding,
|
|
2273
|
+
tokensUsed: Math.ceil(text.length / 4)
|
|
2274
|
+
};
|
|
2275
|
+
})
|
|
2276
|
+
);
|
|
2175
2277
|
return {
|
|
2176
2278
|
embeddings: results.map((r) => r.embedding),
|
|
2177
2279
|
totalTokensUsed: results.reduce((sum, r) => sum + r.tokensUsed, 0)
|
|
@@ -2186,6 +2288,30 @@ var OllamaEmbeddingProvider = class {
|
|
|
2186
2288
|
var import_ignore = __toESM(require_ignore(), 1);
|
|
2187
2289
|
var import_fs2 = require("fs");
|
|
2188
2290
|
var path2 = __toESM(require("path"), 1);
|
|
2291
|
+
var PROJECT_MARKERS = [
|
|
2292
|
+
".git",
|
|
2293
|
+
"package.json",
|
|
2294
|
+
"Cargo.toml",
|
|
2295
|
+
"go.mod",
|
|
2296
|
+
"pyproject.toml",
|
|
2297
|
+
"setup.py",
|
|
2298
|
+
"requirements.txt",
|
|
2299
|
+
"Gemfile",
|
|
2300
|
+
"composer.json",
|
|
2301
|
+
"pom.xml",
|
|
2302
|
+
"build.gradle",
|
|
2303
|
+
"CMakeLists.txt",
|
|
2304
|
+
"Makefile",
|
|
2305
|
+
".opencode"
|
|
2306
|
+
];
|
|
2307
|
+
function hasProjectMarker(projectRoot) {
|
|
2308
|
+
for (const marker of PROJECT_MARKERS) {
|
|
2309
|
+
if ((0, import_fs2.existsSync)(path2.join(projectRoot, marker))) {
|
|
2310
|
+
return true;
|
|
2311
|
+
}
|
|
2312
|
+
}
|
|
2313
|
+
return false;
|
|
2314
|
+
}
|
|
2189
2315
|
function createIgnoreFilter(projectRoot) {
|
|
2190
2316
|
const ig = (0, import_ignore.default)();
|
|
2191
2317
|
const defaultIgnores = [
|
|
@@ -3092,11 +3218,40 @@ var Database = class {
|
|
|
3092
3218
|
var import_fs3 = require("fs");
|
|
3093
3219
|
var path4 = __toESM(require("path"), 1);
|
|
3094
3220
|
var import_child_process = require("child_process");
|
|
3221
|
+
function resolveGitDir(repoRoot) {
|
|
3222
|
+
const gitPath = path4.join(repoRoot, ".git");
|
|
3223
|
+
if (!(0, import_fs3.existsSync)(gitPath)) {
|
|
3224
|
+
return null;
|
|
3225
|
+
}
|
|
3226
|
+
try {
|
|
3227
|
+
const stat4 = (0, import_fs3.statSync)(gitPath);
|
|
3228
|
+
if (stat4.isDirectory()) {
|
|
3229
|
+
return gitPath;
|
|
3230
|
+
}
|
|
3231
|
+
if (stat4.isFile()) {
|
|
3232
|
+
const content = (0, import_fs3.readFileSync)(gitPath, "utf-8").trim();
|
|
3233
|
+
const match = content.match(/^gitdir:\s*(.+)$/);
|
|
3234
|
+
if (match) {
|
|
3235
|
+
const gitdir = match[1];
|
|
3236
|
+
const resolvedPath = path4.isAbsolute(gitdir) ? gitdir : path4.resolve(repoRoot, gitdir);
|
|
3237
|
+
if ((0, import_fs3.existsSync)(resolvedPath)) {
|
|
3238
|
+
return resolvedPath;
|
|
3239
|
+
}
|
|
3240
|
+
}
|
|
3241
|
+
}
|
|
3242
|
+
} catch {
|
|
3243
|
+
}
|
|
3244
|
+
return null;
|
|
3245
|
+
}
|
|
3095
3246
|
function isGitRepo(dir) {
|
|
3096
|
-
return (
|
|
3247
|
+
return resolveGitDir(dir) !== null;
|
|
3097
3248
|
}
|
|
3098
3249
|
function getCurrentBranch(repoRoot) {
|
|
3099
|
-
const
|
|
3250
|
+
const gitDir = resolveGitDir(repoRoot);
|
|
3251
|
+
if (!gitDir) {
|
|
3252
|
+
return null;
|
|
3253
|
+
}
|
|
3254
|
+
const headPath = path4.join(gitDir, "HEAD");
|
|
3100
3255
|
if (!(0, import_fs3.existsSync)(headPath)) {
|
|
3101
3256
|
return null;
|
|
3102
3257
|
}
|
|
@@ -3115,20 +3270,23 @@ function getCurrentBranch(repoRoot) {
|
|
|
3115
3270
|
}
|
|
3116
3271
|
}
|
|
3117
3272
|
function getBaseBranch(repoRoot) {
|
|
3273
|
+
const gitDir = resolveGitDir(repoRoot);
|
|
3118
3274
|
const candidates = ["main", "master", "develop", "trunk"];
|
|
3119
|
-
|
|
3120
|
-
|
|
3121
|
-
|
|
3122
|
-
|
|
3123
|
-
|
|
3124
|
-
|
|
3125
|
-
|
|
3126
|
-
|
|
3127
|
-
|
|
3128
|
-
|
|
3129
|
-
|
|
3275
|
+
if (gitDir) {
|
|
3276
|
+
for (const candidate of candidates) {
|
|
3277
|
+
const refPath = path4.join(gitDir, "refs", "heads", candidate);
|
|
3278
|
+
if ((0, import_fs3.existsSync)(refPath)) {
|
|
3279
|
+
return candidate;
|
|
3280
|
+
}
|
|
3281
|
+
const packedRefsPath = path4.join(gitDir, "packed-refs");
|
|
3282
|
+
if ((0, import_fs3.existsSync)(packedRefsPath)) {
|
|
3283
|
+
try {
|
|
3284
|
+
const content = (0, import_fs3.readFileSync)(packedRefsPath, "utf-8");
|
|
3285
|
+
if (content.includes(`refs/heads/${candidate}`)) {
|
|
3286
|
+
return candidate;
|
|
3287
|
+
}
|
|
3288
|
+
} catch {
|
|
3130
3289
|
}
|
|
3131
|
-
} catch {
|
|
3132
3290
|
}
|
|
3133
3291
|
}
|
|
3134
3292
|
}
|
|
@@ -3153,6 +3311,10 @@ function getBranchOrDefault(repoRoot) {
|
|
|
3153
3311
|
return getCurrentBranch(repoRoot) ?? "default";
|
|
3154
3312
|
}
|
|
3155
3313
|
function getHeadPath(repoRoot) {
|
|
3314
|
+
const gitDir = resolveGitDir(repoRoot);
|
|
3315
|
+
if (gitDir) {
|
|
3316
|
+
return path4.join(gitDir, "HEAD");
|
|
3317
|
+
}
|
|
3156
3318
|
return path4.join(repoRoot, ".git", "HEAD");
|
|
3157
3319
|
}
|
|
3158
3320
|
|
|
@@ -3180,6 +3342,7 @@ function isRateLimitError(error) {
|
|
|
3180
3342
|
const message = getErrorMessage(error);
|
|
3181
3343
|
return message.includes("429") || message.toLowerCase().includes("rate limit") || message.toLowerCase().includes("too many requests");
|
|
3182
3344
|
}
|
|
3345
|
+
var INDEX_METADATA_VERSION = "1";
|
|
3183
3346
|
var Indexer = class {
|
|
3184
3347
|
config;
|
|
3185
3348
|
projectRoot;
|
|
@@ -3188,7 +3351,7 @@ var Indexer = class {
|
|
|
3188
3351
|
invertedIndex = null;
|
|
3189
3352
|
database = null;
|
|
3190
3353
|
provider = null;
|
|
3191
|
-
|
|
3354
|
+
configuredProviderInfo = null;
|
|
3192
3355
|
fileHashCache = /* @__PURE__ */ new Map();
|
|
3193
3356
|
fileHashCachePath = "";
|
|
3194
3357
|
failedBatchesPath = "";
|
|
@@ -3199,12 +3362,15 @@ var Indexer = class {
|
|
|
3199
3362
|
maxQueryCacheSize = 100;
|
|
3200
3363
|
queryCacheTtlMs = 5 * 60 * 1e3;
|
|
3201
3364
|
querySimilarityThreshold = 0.85;
|
|
3365
|
+
indexCompatibility = null;
|
|
3366
|
+
indexingLockPath = "";
|
|
3202
3367
|
constructor(projectRoot, config) {
|
|
3203
3368
|
this.projectRoot = projectRoot;
|
|
3204
3369
|
this.config = config;
|
|
3205
3370
|
this.indexPath = this.getIndexPath();
|
|
3206
3371
|
this.fileHashCachePath = path5.join(this.indexPath, "file-hashes.json");
|
|
3207
3372
|
this.failedBatchesPath = path5.join(this.indexPath, "failed-batches.json");
|
|
3373
|
+
this.indexingLockPath = path5.join(this.indexPath, "indexing.lock");
|
|
3208
3374
|
this.logger = initializeLogger(config.debug);
|
|
3209
3375
|
}
|
|
3210
3376
|
getIndexPath() {
|
|
@@ -3230,7 +3396,36 @@ var Indexer = class {
|
|
|
3230
3396
|
for (const [k, v] of this.fileHashCache) {
|
|
3231
3397
|
obj[k] = v;
|
|
3232
3398
|
}
|
|
3233
|
-
|
|
3399
|
+
this.atomicWriteSync(this.fileHashCachePath, JSON.stringify(obj));
|
|
3400
|
+
}
|
|
3401
|
+
atomicWriteSync(targetPath, data) {
|
|
3402
|
+
const tempPath = `${targetPath}.tmp`;
|
|
3403
|
+
(0, import_fs4.writeFileSync)(tempPath, data);
|
|
3404
|
+
(0, import_fs4.renameSync)(tempPath, targetPath);
|
|
3405
|
+
}
|
|
3406
|
+
checkForInterruptedIndexing() {
|
|
3407
|
+
return (0, import_fs4.existsSync)(this.indexingLockPath);
|
|
3408
|
+
}
|
|
3409
|
+
acquireIndexingLock() {
|
|
3410
|
+
const lockData = {
|
|
3411
|
+
startedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
3412
|
+
pid: process.pid
|
|
3413
|
+
};
|
|
3414
|
+
(0, import_fs4.writeFileSync)(this.indexingLockPath, JSON.stringify(lockData));
|
|
3415
|
+
}
|
|
3416
|
+
releaseIndexingLock() {
|
|
3417
|
+
if ((0, import_fs4.existsSync)(this.indexingLockPath)) {
|
|
3418
|
+
(0, import_fs4.unlinkSync)(this.indexingLockPath);
|
|
3419
|
+
}
|
|
3420
|
+
}
|
|
3421
|
+
async recoverFromInterruptedIndexing() {
|
|
3422
|
+
this.logger.warn("Detected interrupted indexing session, recovering...");
|
|
3423
|
+
if ((0, import_fs4.existsSync)(this.fileHashCachePath)) {
|
|
3424
|
+
(0, import_fs4.unlinkSync)(this.fileHashCachePath);
|
|
3425
|
+
}
|
|
3426
|
+
await this.healthCheck();
|
|
3427
|
+
this.releaseIndexingLock();
|
|
3428
|
+
this.logger.info("Recovery complete, next index will re-process all files");
|
|
3234
3429
|
}
|
|
3235
3430
|
loadFailedBatches() {
|
|
3236
3431
|
try {
|
|
@@ -3278,23 +3473,27 @@ var Indexer = class {
|
|
|
3278
3473
|
}
|
|
3279
3474
|
}
|
|
3280
3475
|
async initialize() {
|
|
3281
|
-
|
|
3282
|
-
|
|
3476
|
+
if (this.config.embeddingProvider === "auto") {
|
|
3477
|
+
this.configuredProviderInfo = await tryDetectProvider();
|
|
3478
|
+
} else {
|
|
3479
|
+
this.configuredProviderInfo = await detectEmbeddingProvider(this.config.embeddingProvider, this.config.embeddingModel);
|
|
3480
|
+
}
|
|
3481
|
+
if (!this.configuredProviderInfo) {
|
|
3283
3482
|
throw new Error(
|
|
3284
3483
|
"No embedding provider available. Configure GitHub, OpenAI, Google, or Ollama."
|
|
3285
3484
|
);
|
|
3286
3485
|
}
|
|
3287
3486
|
this.logger.info("Initializing indexer", {
|
|
3288
|
-
provider: this.
|
|
3289
|
-
model: this.
|
|
3487
|
+
provider: this.configuredProviderInfo.provider,
|
|
3488
|
+
model: this.configuredProviderInfo.modelInfo.model,
|
|
3290
3489
|
scope: this.config.scope
|
|
3291
3490
|
});
|
|
3292
|
-
this.provider = createEmbeddingProvider(
|
|
3293
|
-
this.detectedProvider.credentials,
|
|
3294
|
-
this.detectedProvider.modelInfo
|
|
3295
|
-
);
|
|
3491
|
+
this.provider = createEmbeddingProvider(this.configuredProviderInfo);
|
|
3296
3492
|
await import_fs4.promises.mkdir(this.indexPath, { recursive: true });
|
|
3297
|
-
|
|
3493
|
+
if (this.checkForInterruptedIndexing()) {
|
|
3494
|
+
await this.recoverFromInterruptedIndexing();
|
|
3495
|
+
}
|
|
3496
|
+
const dimensions = this.configuredProviderInfo.modelInfo.dimensions;
|
|
3298
3497
|
const storePath = path5.join(this.indexPath, "vectors");
|
|
3299
3498
|
this.store = new VectorStore(storePath, dimensions);
|
|
3300
3499
|
const indexFilePath = path5.join(this.indexPath, "vectors.usearch");
|
|
@@ -3317,6 +3516,14 @@ var Indexer = class {
|
|
|
3317
3516
|
if (dbIsNew && this.store.count() > 0) {
|
|
3318
3517
|
this.migrateFromLegacyIndex();
|
|
3319
3518
|
}
|
|
3519
|
+
this.indexCompatibility = this.validateIndexCompatibility(this.configuredProviderInfo);
|
|
3520
|
+
if (!this.indexCompatibility.compatible) {
|
|
3521
|
+
this.logger.warn("Index compatibility issue detected", {
|
|
3522
|
+
reason: this.indexCompatibility.reason,
|
|
3523
|
+
storedMetadata: this.indexCompatibility.storedMetadata,
|
|
3524
|
+
configuredProviderInfo: this.configuredProviderInfo
|
|
3525
|
+
});
|
|
3526
|
+
}
|
|
3320
3527
|
if (isGitRepo(this.projectRoot)) {
|
|
3321
3528
|
this.currentBranch = getBranchOrDefault(this.projectRoot);
|
|
3322
3529
|
this.baseBranch = getBaseBranch(this.projectRoot);
|
|
@@ -3387,30 +3594,106 @@ var Indexer = class {
|
|
|
3387
3594
|
}
|
|
3388
3595
|
this.database.addChunksToBranchBatch(this.currentBranch || "default", chunkIds);
|
|
3389
3596
|
}
|
|
3597
|
+
loadIndexMetadata() {
|
|
3598
|
+
if (!this.database) return null;
|
|
3599
|
+
const version = this.database.getMetadata("index.version");
|
|
3600
|
+
if (!version) return null;
|
|
3601
|
+
return {
|
|
3602
|
+
indexVersion: version,
|
|
3603
|
+
embeddingProvider: this.database.getMetadata("index.embeddingProvider") ?? "",
|
|
3604
|
+
embeddingModel: this.database.getMetadata("index.embeddingModel") ?? "",
|
|
3605
|
+
embeddingDimensions: parseInt(this.database.getMetadata("index.embeddingDimensions") ?? "0", 10),
|
|
3606
|
+
createdAt: this.database.getMetadata("index.createdAt") ?? "",
|
|
3607
|
+
updatedAt: this.database.getMetadata("index.updatedAt") ?? ""
|
|
3608
|
+
};
|
|
3609
|
+
}
|
|
3610
|
+
saveIndexMetadata(provider) {
|
|
3611
|
+
if (!this.database) return;
|
|
3612
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
3613
|
+
const existingCreatedAt = this.database.getMetadata("index.createdAt");
|
|
3614
|
+
this.database.setMetadata("index.version", INDEX_METADATA_VERSION);
|
|
3615
|
+
this.database.setMetadata("index.embeddingProvider", provider.provider);
|
|
3616
|
+
this.database.setMetadata("index.embeddingModel", provider.modelInfo.model);
|
|
3617
|
+
this.database.setMetadata("index.embeddingDimensions", provider.modelInfo.dimensions.toString());
|
|
3618
|
+
this.database.setMetadata("index.updatedAt", now);
|
|
3619
|
+
if (!existingCreatedAt) {
|
|
3620
|
+
this.database.setMetadata("index.createdAt", now);
|
|
3621
|
+
}
|
|
3622
|
+
}
|
|
3623
|
+
validateIndexCompatibility(provider) {
|
|
3624
|
+
const storedMetadata = this.loadIndexMetadata();
|
|
3625
|
+
if (!storedMetadata) {
|
|
3626
|
+
return { compatible: true };
|
|
3627
|
+
}
|
|
3628
|
+
const currentProvider = provider.provider;
|
|
3629
|
+
const currentModel = provider.modelInfo.model;
|
|
3630
|
+
const currentDimensions = provider.modelInfo.dimensions;
|
|
3631
|
+
if (storedMetadata.embeddingDimensions !== currentDimensions) {
|
|
3632
|
+
return {
|
|
3633
|
+
compatible: false,
|
|
3634
|
+
code: "DIMENSION_MISMATCH" /* DIMENSION_MISMATCH */,
|
|
3635
|
+
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.`,
|
|
3636
|
+
storedMetadata
|
|
3637
|
+
};
|
|
3638
|
+
}
|
|
3639
|
+
if (storedMetadata.embeddingModel !== currentModel) {
|
|
3640
|
+
return {
|
|
3641
|
+
compatible: false,
|
|
3642
|
+
code: "MODEL_MISMATCH" /* MODEL_MISMATCH */,
|
|
3643
|
+
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.`,
|
|
3644
|
+
storedMetadata
|
|
3645
|
+
};
|
|
3646
|
+
}
|
|
3647
|
+
if (storedMetadata.embeddingProvider !== currentProvider) {
|
|
3648
|
+
this.logger.warn("Provider changed", {
|
|
3649
|
+
storedProvider: storedMetadata.embeddingProvider,
|
|
3650
|
+
currentProvider
|
|
3651
|
+
});
|
|
3652
|
+
}
|
|
3653
|
+
return {
|
|
3654
|
+
compatible: true,
|
|
3655
|
+
storedMetadata
|
|
3656
|
+
};
|
|
3657
|
+
}
|
|
3658
|
+
checkCompatibility() {
|
|
3659
|
+
if (!this.indexCompatibility) {
|
|
3660
|
+
if (!this.configuredProviderInfo) {
|
|
3661
|
+
throw new Error("No embedding provider info, you must initialize the indexer first.");
|
|
3662
|
+
}
|
|
3663
|
+
this.indexCompatibility = this.validateIndexCompatibility(this.configuredProviderInfo);
|
|
3664
|
+
}
|
|
3665
|
+
return this.indexCompatibility;
|
|
3666
|
+
}
|
|
3390
3667
|
async ensureInitialized() {
|
|
3391
|
-
if (!this.store || !this.provider || !this.invertedIndex || !this.
|
|
3668
|
+
if (!this.store || !this.provider || !this.invertedIndex || !this.configuredProviderInfo || !this.database) {
|
|
3392
3669
|
await this.initialize();
|
|
3393
3670
|
}
|
|
3394
3671
|
return {
|
|
3395
3672
|
store: this.store,
|
|
3396
3673
|
provider: this.provider,
|
|
3397
3674
|
invertedIndex: this.invertedIndex,
|
|
3398
|
-
|
|
3675
|
+
configuredProviderInfo: this.configuredProviderInfo,
|
|
3399
3676
|
database: this.database
|
|
3400
3677
|
};
|
|
3401
3678
|
}
|
|
3402
3679
|
async estimateCost() {
|
|
3403
|
-
const {
|
|
3680
|
+
const { configuredProviderInfo } = await this.ensureInitialized();
|
|
3404
3681
|
const { files } = await collectFiles(
|
|
3405
3682
|
this.projectRoot,
|
|
3406
3683
|
this.config.include,
|
|
3407
3684
|
this.config.exclude,
|
|
3408
3685
|
this.config.indexing.maxFileSize
|
|
3409
3686
|
);
|
|
3410
|
-
return createCostEstimate(files,
|
|
3687
|
+
return createCostEstimate(files, configuredProviderInfo);
|
|
3411
3688
|
}
|
|
3412
3689
|
async index(onProgress) {
|
|
3413
|
-
const { store, provider, invertedIndex, database,
|
|
3690
|
+
const { store, provider, invertedIndex, database, configuredProviderInfo } = await this.ensureInitialized();
|
|
3691
|
+
if (!this.indexCompatibility?.compatible) {
|
|
3692
|
+
throw new Error(
|
|
3693
|
+
`${this.indexCompatibility?.reason} Run index_codebase with force=true to rebuild the index.`
|
|
3694
|
+
);
|
|
3695
|
+
}
|
|
3696
|
+
this.acquireIndexingLock();
|
|
3414
3697
|
this.logger.recordIndexingStart();
|
|
3415
3698
|
this.logger.info("Starting indexing", { projectRoot: this.projectRoot });
|
|
3416
3699
|
const startTime = Date.now();
|
|
@@ -3517,7 +3800,7 @@ var Indexer = class {
|
|
|
3517
3800
|
const id = generateChunkId(parsed.path, chunk);
|
|
3518
3801
|
const contentHash = generateChunkHash(chunk);
|
|
3519
3802
|
currentChunkIds.add(id);
|
|
3520
|
-
|
|
3803
|
+
chunkDataBatch.push({
|
|
3521
3804
|
chunkId: id,
|
|
3522
3805
|
contentHash,
|
|
3523
3806
|
filePath: parsed.path,
|
|
@@ -3526,8 +3809,7 @@ var Indexer = class {
|
|
|
3526
3809
|
nodeType: chunk.chunkType,
|
|
3527
3810
|
name: chunk.name,
|
|
3528
3811
|
language: chunk.language
|
|
3529
|
-
};
|
|
3530
|
-
chunkDataBatch.push(chunkData);
|
|
3812
|
+
});
|
|
3531
3813
|
if (existingChunks.get(id) === contentHash) {
|
|
3532
3814
|
fileChunkCount++;
|
|
3533
3815
|
continue;
|
|
@@ -3580,6 +3862,7 @@ var Indexer = class {
|
|
|
3580
3862
|
chunksProcessed: 0,
|
|
3581
3863
|
totalChunks: 0
|
|
3582
3864
|
});
|
|
3865
|
+
this.releaseIndexingLock();
|
|
3583
3866
|
return stats;
|
|
3584
3867
|
}
|
|
3585
3868
|
if (pendingChunks.length === 0) {
|
|
@@ -3597,6 +3880,7 @@ var Indexer = class {
|
|
|
3597
3880
|
chunksProcessed: 0,
|
|
3598
3881
|
totalChunks: 0
|
|
3599
3882
|
});
|
|
3883
|
+
this.releaseIndexingLock();
|
|
3600
3884
|
return stats;
|
|
3601
3885
|
}
|
|
3602
3886
|
onProgress?.({
|
|
@@ -3625,7 +3909,7 @@ var Indexer = class {
|
|
|
3625
3909
|
stats.indexedChunks++;
|
|
3626
3910
|
}
|
|
3627
3911
|
}
|
|
3628
|
-
const providerRateLimits = this.getProviderRateLimits(
|
|
3912
|
+
const providerRateLimits = this.getProviderRateLimits(configuredProviderInfo.provider);
|
|
3629
3913
|
const queue = new PQueue({
|
|
3630
3914
|
concurrency: providerRateLimits.concurrency,
|
|
3631
3915
|
interval: providerRateLimits.intervalMs,
|
|
@@ -3636,7 +3920,7 @@ var Indexer = class {
|
|
|
3636
3920
|
for (const batch of dynamicBatches) {
|
|
3637
3921
|
queue.add(async () => {
|
|
3638
3922
|
if (rateLimitBackoffMs > 0) {
|
|
3639
|
-
await new Promise((
|
|
3923
|
+
await new Promise((resolve5) => setTimeout(resolve5, rateLimitBackoffMs));
|
|
3640
3924
|
}
|
|
3641
3925
|
try {
|
|
3642
3926
|
const result = await pRetry(
|
|
@@ -3680,7 +3964,7 @@ var Indexer = class {
|
|
|
3680
3964
|
contentHash: chunk.contentHash,
|
|
3681
3965
|
embedding: float32ArrayToBuffer(result.embeddings[i]),
|
|
3682
3966
|
chunkText: chunk.text,
|
|
3683
|
-
model:
|
|
3967
|
+
model: configuredProviderInfo.modelInfo.model
|
|
3684
3968
|
}));
|
|
3685
3969
|
database.upsertEmbeddingsBatch(embeddingBatchItems);
|
|
3686
3970
|
for (const chunk of batch) {
|
|
@@ -3731,6 +4015,8 @@ var Indexer = class {
|
|
|
3731
4015
|
await this.maybeRunOrphanGc();
|
|
3732
4016
|
}
|
|
3733
4017
|
stats.durationMs = Date.now() - startTime;
|
|
4018
|
+
this.saveIndexMetadata(configuredProviderInfo);
|
|
4019
|
+
this.indexCompatibility = { compatible: true };
|
|
3734
4020
|
this.logger.recordIndexingEnd();
|
|
3735
4021
|
this.logger.info("Indexing complete", {
|
|
3736
4022
|
files: stats.totalFiles,
|
|
@@ -3751,6 +4037,7 @@ var Indexer = class {
|
|
|
3751
4037
|
chunksProcessed: stats.indexedChunks,
|
|
3752
4038
|
totalChunks: pendingChunks.length
|
|
3753
4039
|
});
|
|
4040
|
+
this.releaseIndexingLock();
|
|
3754
4041
|
return stats;
|
|
3755
4042
|
}
|
|
3756
4043
|
async getQueryEmbedding(query, provider) {
|
|
@@ -3773,7 +4060,7 @@ var Indexer = class {
|
|
|
3773
4060
|
}
|
|
3774
4061
|
this.logger.cache("debug", "Query embedding cache miss", { query: query.slice(0, 50) });
|
|
3775
4062
|
this.logger.recordQueryCacheMiss();
|
|
3776
|
-
const { embedding, tokensUsed } = await provider.
|
|
4063
|
+
const { embedding, tokensUsed } = await provider.embedQuery(query);
|
|
3777
4064
|
this.logger.recordEmbeddingApiCall(tokensUsed);
|
|
3778
4065
|
if (this.queryEmbeddingCache.size >= this.maxQueryCacheSize) {
|
|
3779
4066
|
const oldestKey = this.queryEmbeddingCache.keys().next().value;
|
|
@@ -3816,8 +4103,14 @@ var Indexer = class {
|
|
|
3816
4103
|
return intersection / union;
|
|
3817
4104
|
}
|
|
3818
4105
|
async search(query, limit, options) {
|
|
3819
|
-
const searchStartTime = import_perf_hooks.performance.now();
|
|
3820
4106
|
const { store, provider, database } = await this.ensureInitialized();
|
|
4107
|
+
const compatibility = this.checkCompatibility();
|
|
4108
|
+
if (!compatibility.compatible) {
|
|
4109
|
+
throw new Error(
|
|
4110
|
+
`${compatibility.reason ?? "Index is incompatible with current embedding provider."} A possible solution is to run index_codebase with force=true to rebuild the index.`
|
|
4111
|
+
);
|
|
4112
|
+
}
|
|
4113
|
+
const searchStartTime = import_perf_hooks.performance.now();
|
|
3821
4114
|
if (store.count() === 0) {
|
|
3822
4115
|
this.logger.search("debug", "Search on empty index", { query });
|
|
3823
4116
|
return [];
|
|
@@ -3959,15 +4252,16 @@ var Indexer = class {
|
|
|
3959
4252
|
return results.slice(0, limit);
|
|
3960
4253
|
}
|
|
3961
4254
|
async getStatus() {
|
|
3962
|
-
const { store,
|
|
4255
|
+
const { store, configuredProviderInfo } = await this.ensureInitialized();
|
|
3963
4256
|
return {
|
|
3964
4257
|
indexed: store.count() > 0,
|
|
3965
4258
|
vectorCount: store.count(),
|
|
3966
|
-
provider:
|
|
3967
|
-
model:
|
|
4259
|
+
provider: configuredProviderInfo.provider,
|
|
4260
|
+
model: configuredProviderInfo.modelInfo.model,
|
|
3968
4261
|
indexPath: this.indexPath,
|
|
3969
4262
|
currentBranch: this.currentBranch,
|
|
3970
|
-
baseBranch: this.baseBranch
|
|
4263
|
+
baseBranch: this.baseBranch,
|
|
4264
|
+
compatibility: this.indexCompatibility
|
|
3971
4265
|
};
|
|
3972
4266
|
}
|
|
3973
4267
|
async clearIndex() {
|
|
@@ -3979,6 +4273,13 @@ var Indexer = class {
|
|
|
3979
4273
|
this.fileHashCache.clear();
|
|
3980
4274
|
this.saveFileHashCache();
|
|
3981
4275
|
database.clearBranch(this.currentBranch);
|
|
4276
|
+
database.deleteMetadata("index.version");
|
|
4277
|
+
database.deleteMetadata("index.embeddingProvider");
|
|
4278
|
+
database.deleteMetadata("index.embeddingModel");
|
|
4279
|
+
database.deleteMetadata("index.embeddingDimensions");
|
|
4280
|
+
database.deleteMetadata("index.createdAt");
|
|
4281
|
+
database.deleteMetadata("index.updatedAt");
|
|
4282
|
+
this.indexCompatibility = this.validateIndexCompatibility(this.configuredProviderInfo);
|
|
3982
4283
|
}
|
|
3983
4284
|
async healthCheck() {
|
|
3984
4285
|
const { store, invertedIndex, database } = await this.ensureInitialized();
|
|
@@ -4092,26 +4393,31 @@ var Indexer = class {
|
|
|
4092
4393
|
getLogger() {
|
|
4093
4394
|
return this.logger;
|
|
4094
4395
|
}
|
|
4095
|
-
async findSimilar(code, limit, options) {
|
|
4096
|
-
const searchStartTime = import_perf_hooks.performance.now();
|
|
4396
|
+
async findSimilar(code, limit = this.config.search.maxResults, options) {
|
|
4097
4397
|
const { store, provider, database } = await this.ensureInitialized();
|
|
4398
|
+
const compatibility = this.checkCompatibility();
|
|
4399
|
+
if (!compatibility.compatible) {
|
|
4400
|
+
throw new Error(
|
|
4401
|
+
`${compatibility.reason ?? "Index is incompatible with current embedding provider."} Run index_codebase with force=true to rebuild the index.`
|
|
4402
|
+
);
|
|
4403
|
+
}
|
|
4404
|
+
const searchStartTime = import_perf_hooks.performance.now();
|
|
4098
4405
|
if (store.count() === 0) {
|
|
4099
4406
|
this.logger.search("debug", "Find similar on empty index");
|
|
4100
4407
|
return [];
|
|
4101
4408
|
}
|
|
4102
|
-
const maxResults = limit ?? this.config.search.maxResults;
|
|
4103
4409
|
const filterByBranch = options?.filterByBranch ?? true;
|
|
4104
4410
|
this.logger.search("debug", "Starting find similar", {
|
|
4105
4411
|
codeLength: code.length,
|
|
4106
|
-
|
|
4412
|
+
limit,
|
|
4107
4413
|
filterByBranch
|
|
4108
4414
|
});
|
|
4109
4415
|
const embeddingStartTime = import_perf_hooks.performance.now();
|
|
4110
|
-
const { embedding, tokensUsed } = await provider.
|
|
4416
|
+
const { embedding, tokensUsed } = await provider.embedDocument(code);
|
|
4111
4417
|
const embeddingMs = import_perf_hooks.performance.now() - embeddingStartTime;
|
|
4112
4418
|
this.logger.recordEmbeddingApiCall(tokensUsed);
|
|
4113
4419
|
const vectorStartTime = import_perf_hooks.performance.now();
|
|
4114
|
-
const semanticResults = store.search(embedding,
|
|
4420
|
+
const semanticResults = store.search(embedding, limit * 2);
|
|
4115
4421
|
const vectorMs = import_perf_hooks.performance.now() - vectorStartTime;
|
|
4116
4422
|
let branchChunkIds = null;
|
|
4117
4423
|
if (filterByBranch && this.currentBranch !== "default") {
|
|
@@ -4135,7 +4441,7 @@ var Indexer = class {
|
|
|
4135
4441
|
if (r.metadata.chunkType !== options.chunkType) return false;
|
|
4136
4442
|
}
|
|
4137
4443
|
return true;
|
|
4138
|
-
}).slice(0,
|
|
4444
|
+
}).slice(0, limit);
|
|
4139
4445
|
const totalSearchMs = import_perf_hooks.performance.now() - searchStartTime;
|
|
4140
4446
|
this.logger.recordSearch(totalSearchMs, {
|
|
4141
4447
|
embeddingMs,
|
|
@@ -5050,7 +5356,7 @@ var NodeFsHandler = class {
|
|
|
5050
5356
|
this._addToNodeFs(path9, initialAdd, wh, depth + 1);
|
|
5051
5357
|
}
|
|
5052
5358
|
}).on(EV.ERROR, this._boundHandleError);
|
|
5053
|
-
return new Promise((
|
|
5359
|
+
return new Promise((resolve5, reject) => {
|
|
5054
5360
|
if (!stream)
|
|
5055
5361
|
return reject();
|
|
5056
5362
|
stream.once(STR_END, () => {
|
|
@@ -5059,7 +5365,7 @@ var NodeFsHandler = class {
|
|
|
5059
5365
|
return;
|
|
5060
5366
|
}
|
|
5061
5367
|
const wasThrottled = throttler ? throttler.clear() : false;
|
|
5062
|
-
|
|
5368
|
+
resolve5(void 0);
|
|
5063
5369
|
previous.getChildren().filter((item) => {
|
|
5064
5370
|
return item !== directory && !current.has(item);
|
|
5065
5371
|
}).forEach((item) => {
|
|
@@ -6106,6 +6412,175 @@ function createWatcherWithIndexer(indexer, projectRoot, config) {
|
|
|
6106
6412
|
|
|
6107
6413
|
// src/tools/index.ts
|
|
6108
6414
|
var import_plugin = require("@opencode-ai/plugin");
|
|
6415
|
+
|
|
6416
|
+
// src/tools/utils.ts
|
|
6417
|
+
var MAX_CONTENT_LINES = 30;
|
|
6418
|
+
function truncateContent(content) {
|
|
6419
|
+
const lines = content.split("\n");
|
|
6420
|
+
if (lines.length <= MAX_CONTENT_LINES) return content;
|
|
6421
|
+
return lines.slice(0, MAX_CONTENT_LINES).join("\n") + `
|
|
6422
|
+
// ... (${lines.length - MAX_CONTENT_LINES} more lines)`;
|
|
6423
|
+
}
|
|
6424
|
+
function formatIndexStats(stats, verbose = false) {
|
|
6425
|
+
const lines = [];
|
|
6426
|
+
if (stats.indexedChunks === 0 && stats.removedChunks === 0) {
|
|
6427
|
+
lines.push(`Indexed. ${stats.totalFiles} files processed, ${stats.existingChunks} code chunks already up to date.`);
|
|
6428
|
+
} else if (stats.indexedChunks === 0) {
|
|
6429
|
+
lines.push(`Indexed. ${stats.totalFiles} files, removed ${stats.removedChunks} stale chunks, ${stats.existingChunks} chunks remain.`);
|
|
6430
|
+
} else {
|
|
6431
|
+
let main = `Indexed. ${stats.totalFiles} files processed, ${stats.indexedChunks} new chunks embedded.`;
|
|
6432
|
+
if (stats.existingChunks > 0) {
|
|
6433
|
+
main += ` ${stats.existingChunks} unchanged chunks skipped.`;
|
|
6434
|
+
}
|
|
6435
|
+
lines.push(main);
|
|
6436
|
+
if (stats.removedChunks > 0) {
|
|
6437
|
+
lines.push(`Removed ${stats.removedChunks} stale chunks.`);
|
|
6438
|
+
}
|
|
6439
|
+
if (stats.failedChunks > 0) {
|
|
6440
|
+
lines.push(`Failed: ${stats.failedChunks} chunks.`);
|
|
6441
|
+
}
|
|
6442
|
+
lines.push(`Tokens: ${stats.tokensUsed.toLocaleString()}, Duration: ${(stats.durationMs / 1e3).toFixed(1)}s`);
|
|
6443
|
+
}
|
|
6444
|
+
if (verbose) {
|
|
6445
|
+
if (stats.skippedFiles.length > 0) {
|
|
6446
|
+
const tooLarge = stats.skippedFiles.filter((f) => f.reason === "too_large");
|
|
6447
|
+
const excluded = stats.skippedFiles.filter((f) => f.reason === "excluded");
|
|
6448
|
+
const gitignored = stats.skippedFiles.filter((f) => f.reason === "gitignore");
|
|
6449
|
+
lines.push("");
|
|
6450
|
+
lines.push(`Skipped files: ${stats.skippedFiles.length}`);
|
|
6451
|
+
if (tooLarge.length > 0) {
|
|
6452
|
+
lines.push(` Too large (${tooLarge.length}): ${tooLarge.slice(0, 5).map((f) => f.path).join(", ")}${tooLarge.length > 5 ? "..." : ""}`);
|
|
6453
|
+
}
|
|
6454
|
+
if (excluded.length > 0) {
|
|
6455
|
+
lines.push(` Excluded (${excluded.length}): ${excluded.slice(0, 5).map((f) => f.path).join(", ")}${excluded.length > 5 ? "..." : ""}`);
|
|
6456
|
+
}
|
|
6457
|
+
if (gitignored.length > 0) {
|
|
6458
|
+
lines.push(` Gitignored (${gitignored.length}): ${gitignored.slice(0, 5).map((f) => f.path).join(", ")}${gitignored.length > 5 ? "..." : ""}`);
|
|
6459
|
+
}
|
|
6460
|
+
}
|
|
6461
|
+
if (stats.parseFailures.length > 0) {
|
|
6462
|
+
lines.push("");
|
|
6463
|
+
lines.push(`Files with no extractable chunks (${stats.parseFailures.length}): ${stats.parseFailures.slice(0, 10).join(", ")}${stats.parseFailures.length > 10 ? "..." : ""}`);
|
|
6464
|
+
}
|
|
6465
|
+
}
|
|
6466
|
+
return lines.join("\n");
|
|
6467
|
+
}
|
|
6468
|
+
function formatStatus(status) {
|
|
6469
|
+
if (!status.indexed) {
|
|
6470
|
+
return "Codebase is not indexed. Run index_codebase to create an index.";
|
|
6471
|
+
}
|
|
6472
|
+
const lines = [
|
|
6473
|
+
`Index status:`,
|
|
6474
|
+
` Indexed chunks: ${status.vectorCount.toLocaleString()}`,
|
|
6475
|
+
` Provider: ${status.provider}`,
|
|
6476
|
+
` Model: ${status.model}`,
|
|
6477
|
+
` Location: ${status.indexPath}`
|
|
6478
|
+
];
|
|
6479
|
+
if (status.currentBranch !== "default") {
|
|
6480
|
+
lines.push(` Current branch: ${status.currentBranch}`);
|
|
6481
|
+
lines.push(` Base branch: ${status.baseBranch}`);
|
|
6482
|
+
}
|
|
6483
|
+
if (status.compatibility && !status.compatibility.compatible) {
|
|
6484
|
+
lines.push("");
|
|
6485
|
+
lines.push(`COMPATIBILITY WARNING: ${status.compatibility.reason}`);
|
|
6486
|
+
if (status.compatibility.storedMetadata) {
|
|
6487
|
+
const stored = status.compatibility.storedMetadata;
|
|
6488
|
+
lines.push(` Index was built with: ${stored.embeddingProvider}/${stored.embeddingModel} (${stored.embeddingDimensions}D)`);
|
|
6489
|
+
lines.push(` Current config: ${status.provider}/${status.model}`);
|
|
6490
|
+
}
|
|
6491
|
+
} else if (!status.compatibility) {
|
|
6492
|
+
lines.push(` Compatibility: No compatibility information found. Maybe the index is not initialized yet, try running index_codebase.`);
|
|
6493
|
+
} else {
|
|
6494
|
+
lines.push(` Compatibility: Index is compatible with the current provider and model.`);
|
|
6495
|
+
}
|
|
6496
|
+
return lines.join("\n");
|
|
6497
|
+
}
|
|
6498
|
+
function formatProgressTitle(progress) {
|
|
6499
|
+
switch (progress.phase) {
|
|
6500
|
+
case "scanning":
|
|
6501
|
+
return "Scanning files...";
|
|
6502
|
+
case "parsing":
|
|
6503
|
+
return `Parsing: ${progress.filesProcessed}/${progress.totalFiles} files`;
|
|
6504
|
+
case "embedding":
|
|
6505
|
+
return `Embedding: ${progress.chunksProcessed}/${progress.totalChunks} chunks`;
|
|
6506
|
+
case "storing":
|
|
6507
|
+
return "Storing index...";
|
|
6508
|
+
case "complete":
|
|
6509
|
+
return "Indexing complete";
|
|
6510
|
+
default:
|
|
6511
|
+
return "Indexing...";
|
|
6512
|
+
}
|
|
6513
|
+
}
|
|
6514
|
+
function calculatePercentage(progress) {
|
|
6515
|
+
if (progress.phase === "scanning") return 0;
|
|
6516
|
+
if (progress.phase === "complete") return 100;
|
|
6517
|
+
if (progress.phase === "parsing") {
|
|
6518
|
+
if (progress.totalFiles === 0) return 5;
|
|
6519
|
+
return Math.round(5 + progress.filesProcessed / progress.totalFiles * 15);
|
|
6520
|
+
}
|
|
6521
|
+
if (progress.phase === "embedding") {
|
|
6522
|
+
if (progress.totalChunks === 0) return 20;
|
|
6523
|
+
return Math.round(20 + progress.chunksProcessed / progress.totalChunks * 70);
|
|
6524
|
+
}
|
|
6525
|
+
if (progress.phase === "storing") return 95;
|
|
6526
|
+
return 0;
|
|
6527
|
+
}
|
|
6528
|
+
function formatCodebasePeek(results, query) {
|
|
6529
|
+
if (results.length === 0) {
|
|
6530
|
+
return "No matching code found. Try a different query or run index_codebase first.";
|
|
6531
|
+
}
|
|
6532
|
+
const formatted = results.map((r, idx) => {
|
|
6533
|
+
const location = `${r.filePath}:${r.startLine}-${r.endLine}`;
|
|
6534
|
+
const name = r.name ? `"${r.name}"` : "(anonymous)";
|
|
6535
|
+
return `[${idx + 1}] ${r.chunkType} ${name} at ${location} (score: ${r.score.toFixed(2)})`;
|
|
6536
|
+
});
|
|
6537
|
+
return `Found ${results.length} locations for "${query}":
|
|
6538
|
+
|
|
6539
|
+
${formatted.join("\n")}
|
|
6540
|
+
|
|
6541
|
+
Use Read tool to examine specific files.`;
|
|
6542
|
+
}
|
|
6543
|
+
function formatHealthCheck(result) {
|
|
6544
|
+
if (result.removed === 0 && result.gcOrphanEmbeddings === 0 && result.gcOrphanChunks === 0) {
|
|
6545
|
+
return "Index is healthy. No stale entries found.";
|
|
6546
|
+
}
|
|
6547
|
+
const lines = [`Health check complete:`];
|
|
6548
|
+
if (result.removed > 0) {
|
|
6549
|
+
lines.push(` Removed stale entries: ${result.removed}`);
|
|
6550
|
+
}
|
|
6551
|
+
if (result.gcOrphanEmbeddings > 0) {
|
|
6552
|
+
lines.push(` Garbage collected orphan embeddings: ${result.gcOrphanEmbeddings}`);
|
|
6553
|
+
}
|
|
6554
|
+
if (result.gcOrphanChunks > 0) {
|
|
6555
|
+
lines.push(` Garbage collected orphan chunks: ${result.gcOrphanChunks}`);
|
|
6556
|
+
}
|
|
6557
|
+
if (result.filePaths.length > 0) {
|
|
6558
|
+
lines.push(` Cleaned paths: ${result.filePaths.join(", ")}`);
|
|
6559
|
+
}
|
|
6560
|
+
return lines.join("\n");
|
|
6561
|
+
}
|
|
6562
|
+
function formatLogs(logs) {
|
|
6563
|
+
if (logs.length === 0) {
|
|
6564
|
+
return "No logs recorded yet. Logs are captured during indexing and search operations.";
|
|
6565
|
+
}
|
|
6566
|
+
return logs.map((l) => {
|
|
6567
|
+
const dataStr = l.data ? ` ${JSON.stringify(l.data)}` : "";
|
|
6568
|
+
return `[${l.timestamp}] [${l.level.toUpperCase()}] [${l.category}] ${l.message}${dataStr}`;
|
|
6569
|
+
}).join("\n");
|
|
6570
|
+
}
|
|
6571
|
+
function formatSearchResults(results, scoreFormat = "similarity") {
|
|
6572
|
+
const formatted = results.map((r, idx) => {
|
|
6573
|
+
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}`;
|
|
6574
|
+
const scoreLabel = scoreFormat === "similarity" ? `(similarity: ${(r.score * 100).toFixed(1)}%)` : `(score: ${r.score.toFixed(2)})`;
|
|
6575
|
+
return `${header} ${scoreLabel}
|
|
6576
|
+
\`\`\`
|
|
6577
|
+
${truncateContent(r.content)}
|
|
6578
|
+
\`\`\``;
|
|
6579
|
+
});
|
|
6580
|
+
return formatted.join("\n\n");
|
|
6581
|
+
}
|
|
6582
|
+
|
|
6583
|
+
// src/tools/index.ts
|
|
6109
6584
|
var z = import_plugin.tool.schema;
|
|
6110
6585
|
var sharedIndexer = null;
|
|
6111
6586
|
function initializeTools(projectRoot, config) {
|
|
@@ -6117,39 +6592,6 @@ function getIndexer() {
|
|
|
6117
6592
|
}
|
|
6118
6593
|
return sharedIndexer;
|
|
6119
6594
|
}
|
|
6120
|
-
var codebase_search = (0, import_plugin.tool)({
|
|
6121
|
-
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.",
|
|
6122
|
-
args: {
|
|
6123
|
-
query: z.string().describe("Natural language description of what code you're looking for. Describe behavior, not syntax."),
|
|
6124
|
-
limit: z.number().optional().default(10).describe("Maximum number of results to return"),
|
|
6125
|
-
fileType: z.string().optional().describe("Filter by file extension (e.g., 'ts', 'py', 'rs')"),
|
|
6126
|
-
directory: z.string().optional().describe("Filter by directory path (e.g., 'src/utils', 'lib')"),
|
|
6127
|
-
chunkType: z.enum(["function", "class", "method", "interface", "type", "enum", "struct", "impl", "trait", "module", "other"]).optional().describe("Filter by code chunk type"),
|
|
6128
|
-
contextLines: z.number().optional().describe("Number of extra lines to include before/after each match (default: 0)")
|
|
6129
|
-
},
|
|
6130
|
-
async execute(args) {
|
|
6131
|
-
const indexer = getIndexer();
|
|
6132
|
-
const results = await indexer.search(args.query, args.limit ?? 10, {
|
|
6133
|
-
fileType: args.fileType,
|
|
6134
|
-
directory: args.directory,
|
|
6135
|
-
chunkType: args.chunkType,
|
|
6136
|
-
contextLines: args.contextLines
|
|
6137
|
-
});
|
|
6138
|
-
if (results.length === 0) {
|
|
6139
|
-
return "No matching code found. Try a different query or run index_codebase first.";
|
|
6140
|
-
}
|
|
6141
|
-
const formatted = results.map((r, idx) => {
|
|
6142
|
-
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}`;
|
|
6143
|
-
return `${header} (score: ${r.score.toFixed(2)})
|
|
6144
|
-
\`\`\`
|
|
6145
|
-
${r.content}
|
|
6146
|
-
\`\`\``;
|
|
6147
|
-
});
|
|
6148
|
-
return `Found ${results.length} results for "${args.query}":
|
|
6149
|
-
|
|
6150
|
-
${formatted.join("\n\n")}`;
|
|
6151
|
-
}
|
|
6152
|
-
});
|
|
6153
6595
|
var codebase_peek = (0, import_plugin.tool)({
|
|
6154
6596
|
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.",
|
|
6155
6597
|
args: {
|
|
@@ -6167,19 +6609,7 @@ var codebase_peek = (0, import_plugin.tool)({
|
|
|
6167
6609
|
chunkType: args.chunkType,
|
|
6168
6610
|
metadataOnly: true
|
|
6169
6611
|
});
|
|
6170
|
-
|
|
6171
|
-
return "No matching code found. Try a different query or run index_codebase first.";
|
|
6172
|
-
}
|
|
6173
|
-
const formatted = results.map((r, idx) => {
|
|
6174
|
-
const location = `${r.filePath}:${r.startLine}-${r.endLine}`;
|
|
6175
|
-
const name = r.name ? `"${r.name}"` : "(anonymous)";
|
|
6176
|
-
return `[${idx + 1}] ${r.chunkType} ${name} at ${location} (score: ${r.score.toFixed(2)})`;
|
|
6177
|
-
});
|
|
6178
|
-
return `Found ${results.length} locations for "${args.query}":
|
|
6179
|
-
|
|
6180
|
-
${formatted.join("\n")}
|
|
6181
|
-
|
|
6182
|
-
Use Read tool to examine specific files.`;
|
|
6612
|
+
return formatCodebasePeek(results, args.query);
|
|
6183
6613
|
}
|
|
6184
6614
|
});
|
|
6185
6615
|
var index_codebase = (0, import_plugin.tool)({
|
|
@@ -6229,23 +6659,7 @@ var index_health_check = (0, import_plugin.tool)({
|
|
|
6229
6659
|
async execute() {
|
|
6230
6660
|
const indexer = getIndexer();
|
|
6231
6661
|
const result = await indexer.healthCheck();
|
|
6232
|
-
|
|
6233
|
-
return "Index is healthy. No stale entries found.";
|
|
6234
|
-
}
|
|
6235
|
-
const lines = [`Health check complete:`];
|
|
6236
|
-
if (result.removed > 0) {
|
|
6237
|
-
lines.push(` Removed stale entries: ${result.removed}`);
|
|
6238
|
-
}
|
|
6239
|
-
if (result.gcOrphanEmbeddings > 0) {
|
|
6240
|
-
lines.push(` Garbage collected orphan embeddings: ${result.gcOrphanEmbeddings}`);
|
|
6241
|
-
}
|
|
6242
|
-
if (result.gcOrphanChunks > 0) {
|
|
6243
|
-
lines.push(` Garbage collected orphan chunks: ${result.gcOrphanChunks}`);
|
|
6244
|
-
}
|
|
6245
|
-
if (result.filePaths.length > 0) {
|
|
6246
|
-
lines.push(` Cleaned paths: ${result.filePaths.join(", ")}`);
|
|
6247
|
-
}
|
|
6248
|
-
return lines.join("\n");
|
|
6662
|
+
return formatHealthCheck(result);
|
|
6249
6663
|
}
|
|
6250
6664
|
});
|
|
6251
6665
|
var index_metrics = (0, import_plugin.tool)({
|
|
@@ -6284,13 +6698,7 @@ var index_logs = (0, import_plugin.tool)({
|
|
|
6284
6698
|
} else {
|
|
6285
6699
|
logs = logger.getLogs(args.limit);
|
|
6286
6700
|
}
|
|
6287
|
-
|
|
6288
|
-
return "No logs recorded yet. Logs are captured during indexing and search operations.";
|
|
6289
|
-
}
|
|
6290
|
-
return logs.map((l) => {
|
|
6291
|
-
const dataStr = l.data ? ` ${JSON.stringify(l.data)}` : "";
|
|
6292
|
-
return `[${l.timestamp}] [${l.level.toUpperCase()}] [${l.category}] ${l.message}${dataStr}`;
|
|
6293
|
-
}).join("\n");
|
|
6701
|
+
return formatLogs(logs);
|
|
6294
6702
|
}
|
|
6295
6703
|
});
|
|
6296
6704
|
var find_similar = (0, import_plugin.tool)({
|
|
@@ -6305,7 +6713,7 @@ var find_similar = (0, import_plugin.tool)({
|
|
|
6305
6713
|
},
|
|
6306
6714
|
async execute(args) {
|
|
6307
6715
|
const indexer = getIndexer();
|
|
6308
|
-
const results = await indexer.findSimilar(args.code, args.limit
|
|
6716
|
+
const results = await indexer.findSimilar(args.code, args.limit, {
|
|
6309
6717
|
fileType: args.fileType,
|
|
6310
6718
|
directory: args.directory,
|
|
6311
6719
|
chunkType: args.chunkType,
|
|
@@ -6314,109 +6722,37 @@ var find_similar = (0, import_plugin.tool)({
|
|
|
6314
6722
|
if (results.length === 0) {
|
|
6315
6723
|
return "No similar code found. Try a different snippet or run index_codebase first.";
|
|
6316
6724
|
}
|
|
6317
|
-
const formatted = results.map((r, idx) => {
|
|
6318
|
-
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}`;
|
|
6319
|
-
return `${header} (similarity: ${(r.score * 100).toFixed(1)}%)
|
|
6320
|
-
\`\`\`
|
|
6321
|
-
${r.content}
|
|
6322
|
-
\`\`\``;
|
|
6323
|
-
});
|
|
6324
6725
|
return `Found ${results.length} similar code blocks:
|
|
6325
6726
|
|
|
6326
|
-
${
|
|
6727
|
+
${formatSearchResults(results)}`;
|
|
6327
6728
|
}
|
|
6328
6729
|
});
|
|
6329
|
-
|
|
6330
|
-
|
|
6331
|
-
|
|
6332
|
-
|
|
6333
|
-
|
|
6334
|
-
|
|
6335
|
-
|
|
6336
|
-
|
|
6337
|
-
|
|
6338
|
-
|
|
6339
|
-
|
|
6340
|
-
|
|
6341
|
-
|
|
6342
|
-
|
|
6343
|
-
|
|
6344
|
-
|
|
6345
|
-
|
|
6346
|
-
}
|
|
6347
|
-
|
|
6348
|
-
|
|
6349
|
-
if (verbose) {
|
|
6350
|
-
if (stats.skippedFiles.length > 0) {
|
|
6351
|
-
const tooLarge = stats.skippedFiles.filter((f) => f.reason === "too_large");
|
|
6352
|
-
const excluded = stats.skippedFiles.filter((f) => f.reason === "excluded");
|
|
6353
|
-
const gitignored = stats.skippedFiles.filter((f) => f.reason === "gitignore");
|
|
6354
|
-
lines.push("");
|
|
6355
|
-
lines.push(`Skipped files: ${stats.skippedFiles.length}`);
|
|
6356
|
-
if (tooLarge.length > 0) {
|
|
6357
|
-
lines.push(` Too large (${tooLarge.length}): ${tooLarge.slice(0, 5).map((f) => f.path).join(", ")}${tooLarge.length > 5 ? "..." : ""}`);
|
|
6358
|
-
}
|
|
6359
|
-
if (excluded.length > 0) {
|
|
6360
|
-
lines.push(` Excluded (${excluded.length}): ${excluded.slice(0, 5).map((f) => f.path).join(", ")}${excluded.length > 5 ? "..." : ""}`);
|
|
6361
|
-
}
|
|
6362
|
-
if (gitignored.length > 0) {
|
|
6363
|
-
lines.push(` Gitignored (${gitignored.length}): ${gitignored.slice(0, 5).map((f) => f.path).join(", ")}${gitignored.length > 5 ? "..." : ""}`);
|
|
6364
|
-
}
|
|
6365
|
-
}
|
|
6366
|
-
if (stats.parseFailures.length > 0) {
|
|
6367
|
-
lines.push("");
|
|
6368
|
-
lines.push(`Files with no extractable chunks (${stats.parseFailures.length}): ${stats.parseFailures.slice(0, 10).join(", ")}${stats.parseFailures.length > 10 ? "..." : ""}`);
|
|
6730
|
+
var codebase_search = (0, import_plugin.tool)({
|
|
6731
|
+
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.",
|
|
6732
|
+
args: {
|
|
6733
|
+
query: z.string().describe("Natural language description of what code you're looking for. Describe behavior, not syntax."),
|
|
6734
|
+
limit: z.number().optional().default(5).describe("Maximum number of results to return"),
|
|
6735
|
+
fileType: z.string().optional().describe("Filter by file extension (e.g., 'ts', 'py', 'rs')"),
|
|
6736
|
+
directory: z.string().optional().describe("Filter by directory path (e.g., 'src/utils', 'lib')"),
|
|
6737
|
+
chunkType: z.enum(["function", "class", "method", "interface", "type", "enum", "struct", "impl", "trait", "module", "other"]).optional().describe("Filter by code chunk type"),
|
|
6738
|
+
contextLines: z.number().optional().describe("Number of extra lines to include before/after each match (default: 0)")
|
|
6739
|
+
},
|
|
6740
|
+
async execute(args) {
|
|
6741
|
+
const indexer = getIndexer();
|
|
6742
|
+
const results = await indexer.search(args.query, args.limit ?? 5, {
|
|
6743
|
+
fileType: args.fileType,
|
|
6744
|
+
directory: args.directory,
|
|
6745
|
+
chunkType: args.chunkType,
|
|
6746
|
+
contextLines: args.contextLines
|
|
6747
|
+
});
|
|
6748
|
+
if (results.length === 0) {
|
|
6749
|
+
return "No matching code found. Try a different query or run index_codebase first.";
|
|
6369
6750
|
}
|
|
6751
|
+
return `Found ${results.length} results for "${args.query}":
|
|
6752
|
+
|
|
6753
|
+
${formatSearchResults(results, "score")}`;
|
|
6370
6754
|
}
|
|
6371
|
-
|
|
6372
|
-
}
|
|
6373
|
-
function formatStatus(status) {
|
|
6374
|
-
if (!status.indexed) {
|
|
6375
|
-
return "Codebase is not indexed. Run index_codebase to create an index.";
|
|
6376
|
-
}
|
|
6377
|
-
const lines = [
|
|
6378
|
-
`Index status:`,
|
|
6379
|
-
` Indexed chunks: ${status.vectorCount.toLocaleString()}`,
|
|
6380
|
-
` Provider: ${status.provider}`,
|
|
6381
|
-
` Model: ${status.model}`,
|
|
6382
|
-
` Location: ${status.indexPath}`
|
|
6383
|
-
];
|
|
6384
|
-
if (status.currentBranch !== "default") {
|
|
6385
|
-
lines.push(` Current branch: ${status.currentBranch}`);
|
|
6386
|
-
lines.push(` Base branch: ${status.baseBranch}`);
|
|
6387
|
-
}
|
|
6388
|
-
return lines.join("\n");
|
|
6389
|
-
}
|
|
6390
|
-
function formatProgressTitle(progress) {
|
|
6391
|
-
switch (progress.phase) {
|
|
6392
|
-
case "scanning":
|
|
6393
|
-
return "Scanning files...";
|
|
6394
|
-
case "parsing":
|
|
6395
|
-
return `Parsing: ${progress.filesProcessed}/${progress.totalFiles} files`;
|
|
6396
|
-
case "embedding":
|
|
6397
|
-
return `Embedding: ${progress.chunksProcessed}/${progress.totalChunks} chunks`;
|
|
6398
|
-
case "storing":
|
|
6399
|
-
return "Storing index...";
|
|
6400
|
-
case "complete":
|
|
6401
|
-
return "Indexing complete";
|
|
6402
|
-
default:
|
|
6403
|
-
return "Indexing...";
|
|
6404
|
-
}
|
|
6405
|
-
}
|
|
6406
|
-
function calculatePercentage(progress) {
|
|
6407
|
-
if (progress.phase === "scanning") return 0;
|
|
6408
|
-
if (progress.phase === "complete") return 100;
|
|
6409
|
-
if (progress.phase === "parsing") {
|
|
6410
|
-
if (progress.totalFiles === 0) return 5;
|
|
6411
|
-
return Math.round(5 + progress.filesProcessed / progress.totalFiles * 15);
|
|
6412
|
-
}
|
|
6413
|
-
if (progress.phase === "embedding") {
|
|
6414
|
-
if (progress.totalChunks === 0) return 20;
|
|
6415
|
-
return Math.round(20 + progress.chunksProcessed / progress.totalChunks * 70);
|
|
6416
|
-
}
|
|
6417
|
-
if (progress.phase === "storing") return 95;
|
|
6418
|
-
return 0;
|
|
6419
|
-
}
|
|
6755
|
+
});
|
|
6420
6756
|
|
|
6421
6757
|
// src/commands/loader.ts
|
|
6422
6758
|
var import_fs5 = require("fs");
|
|
@@ -6496,14 +6832,20 @@ var plugin = async ({ directory }) => {
|
|
|
6496
6832
|
const config = parseConfig(rawConfig);
|
|
6497
6833
|
initializeTools(projectRoot, config);
|
|
6498
6834
|
const indexer = new Indexer(projectRoot, config);
|
|
6499
|
-
|
|
6835
|
+
const isValidProject = !config.indexing.requireProjectMarker || hasProjectMarker(projectRoot);
|
|
6836
|
+
if (!isValidProject) {
|
|
6837
|
+
console.warn(
|
|
6838
|
+
`[codebase-index] Skipping file watching and auto-indexing: no project marker found in "${projectRoot}". Set "indexing.requireProjectMarker": false in config to override.`
|
|
6839
|
+
);
|
|
6840
|
+
}
|
|
6841
|
+
if (config.indexing.autoIndex && isValidProject) {
|
|
6500
6842
|
indexer.initialize().then(() => {
|
|
6501
6843
|
indexer.index().catch(() => {
|
|
6502
6844
|
});
|
|
6503
6845
|
}).catch(() => {
|
|
6504
6846
|
});
|
|
6505
6847
|
}
|
|
6506
|
-
if (config.indexing.watchFiles) {
|
|
6848
|
+
if (config.indexing.watchFiles && isValidProject) {
|
|
6507
6849
|
createWatcherWithIndexer(indexer, projectRoot, config);
|
|
6508
6850
|
}
|
|
6509
6851
|
return {
|