opencode-codebase-index 0.24.0 → 0.25.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/.claude-plugin/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +1 -1
- package/.codex-plugin/plugin.json +1 -1
- package/README.md +10 -0
- package/dist/cbi.cjs +14792 -0
- package/dist/cbi.cjs.map +1 -0
- package/dist/cbi.js +14786 -0
- package/dist/cbi.js.map +1 -0
- package/dist/cli.cjs +101 -3
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +101 -3
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +97 -3
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +97 -3
- package/dist/index.js.map +1 -1
- package/dist/pi-extension.cjs +93 -1
- package/dist/pi-extension.cjs.map +1 -1
- package/dist/pi-extension.js +93 -1
- package/dist/pi-extension.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.win32-x64-msvc.node +0 -0
- package/package.json +3 -2
package/dist/cli.js
CHANGED
|
@@ -3596,6 +3596,26 @@ function formatCostEstimate(estimate) {
|
|
|
3596
3596
|
\u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518
|
|
3597
3597
|
`;
|
|
3598
3598
|
}
|
|
3599
|
+
function formatDryRunEstimate(estimate) {
|
|
3600
|
+
return `Dry run: parsed the file set to measure the embedding workload. No embedding requests were made and the index was not changed.
|
|
3601
|
+
|
|
3602
|
+
Files to embed: ${estimate.filesCount.toLocaleString()}
|
|
3603
|
+
Chunks to embed: ${estimate.chunksCount.toLocaleString()}
|
|
3604
|
+
Tokens to embed: ${estimate.tokensToEmbed.toLocaleString()}
|
|
3605
|
+
|
|
3606
|
+
The "Tokens to embed" value uses the local estimateTokens(text) = ceil(len/4). It
|
|
3607
|
+
matches the live "Tokens used" counter only for providers that report usage on the
|
|
3608
|
+
same basis (ollama); for providers that report a server tokenizer count (OpenAI,
|
|
3609
|
+
Gemini, custom) it is only an estimate.
|
|
3610
|
+
|
|
3611
|
+
For a matching provider and a project-scoped force index, the force pass clears its
|
|
3612
|
+
own cached embeddings, so the live counter climbs to this number. A force index on a
|
|
3613
|
+
shared global index can reuse cached embeddings from other projects, and an
|
|
3614
|
+
incremental index counts cached chunks that are not re-embedded; in both cases this
|
|
3615
|
+
number is an upper bound on the live counter, so a progress percent against this
|
|
3616
|
+
total tops out below 100%.
|
|
3617
|
+
`;
|
|
3618
|
+
}
|
|
3599
3619
|
function formatBytes(bytes) {
|
|
3600
3620
|
if (bytes === 0) return "0 B";
|
|
3601
3621
|
const k = 1024;
|
|
@@ -6820,6 +6840,9 @@ var CALL_GRAPH_SYMBOL_CHUNK_TYPES = /* @__PURE__ */ new Set([
|
|
|
6820
6840
|
"enum_declaration",
|
|
6821
6841
|
"function_definition",
|
|
6822
6842
|
"class_definition",
|
|
6843
|
+
// Ruby module/class symbols that are declaration-bearing and navigable.
|
|
6844
|
+
"class",
|
|
6845
|
+
"module",
|
|
6823
6846
|
"class_specifier",
|
|
6824
6847
|
"struct_specifier",
|
|
6825
6848
|
"namespace_definition",
|
|
@@ -10866,6 +10889,70 @@ var Indexer = class _Indexer {
|
|
|
10866
10889
|
);
|
|
10867
10890
|
return createCostEstimate(files, configuredProviderInfo);
|
|
10868
10891
|
}
|
|
10892
|
+
// Dry-run counterpart to index()/forceIndex(): parse the real file set and sum
|
|
10893
|
+
// estimateTokens over the embedding text of every indexable chunk, without
|
|
10894
|
+
// calling the embedding provider or writing to the index. Read-only and
|
|
10895
|
+
// lock-free (mirrors estimateCost). The token sum is the exact value "Tokens
|
|
10896
|
+
// used" climbs to for a force index (cache bypassed); for an incremental it is
|
|
10897
|
+
// an upper bound because cached chunks are counted here but not re-embedded.
|
|
10898
|
+
// Used by index_codebase(dryRun:true) to give a stable, monotonic progress
|
|
10899
|
+
// denominator that matches the live "Tokens used" basis.
|
|
10900
|
+
async dryRunCost() {
|
|
10901
|
+
const { configuredProviderInfo } = await this.ensureInitialized();
|
|
10902
|
+
const maxChunkTokens = getSafeEmbeddingChunkTokenLimit(configuredProviderInfo);
|
|
10903
|
+
const includePatterns = [...this.config.include, ...this.config.additionalInclude];
|
|
10904
|
+
const { files } = await collectFiles(
|
|
10905
|
+
this.materializedProjectRoot,
|
|
10906
|
+
includePatterns,
|
|
10907
|
+
this.config.exclude,
|
|
10908
|
+
this.config.indexing.maxFileSize,
|
|
10909
|
+
this.getMaterializedKnowledgeBases(),
|
|
10910
|
+
{ maxDepth: this.config.indexing.maxDepth, maxFilesPerDirectory: this.config.indexing.maxFilesPerDirectory }
|
|
10911
|
+
);
|
|
10912
|
+
let filesCount = 0;
|
|
10913
|
+
let chunksCount = 0;
|
|
10914
|
+
let tokensToEmbed = 0;
|
|
10915
|
+
for (const batch of iterateOrderedFileBatches(files, (f) => f.size, this.fileBatchLimits)) {
|
|
10916
|
+
const loadedFiles = await Promise.all(batch.map(async (f) => {
|
|
10917
|
+
try {
|
|
10918
|
+
return {
|
|
10919
|
+
path: this.toStoredFilePath(f.path),
|
|
10920
|
+
content: await fsPromises3.readFile(f.path, "utf-8")
|
|
10921
|
+
};
|
|
10922
|
+
} catch {
|
|
10923
|
+
return null;
|
|
10924
|
+
}
|
|
10925
|
+
}));
|
|
10926
|
+
const readable = loadedFiles.filter(
|
|
10927
|
+
(f) => f !== null
|
|
10928
|
+
);
|
|
10929
|
+
filesCount += readable.length;
|
|
10930
|
+
const contentByPath = new Map(readable.map((f) => [f.path, f.content]));
|
|
10931
|
+
const parsedFiles = parseFiles(readable, this.config.indexing.linesPerChunk);
|
|
10932
|
+
for (const parsed of parsedFiles) {
|
|
10933
|
+
let chunksToProcess = parsed.chunks;
|
|
10934
|
+
if (this.config.indexing.fallbackToTextOnMaxChunks && chunksToProcess.length > this.config.indexing.maxChunksPerFile) {
|
|
10935
|
+
const content = contentByPath.get(parsed.path);
|
|
10936
|
+
if (content !== void 0) {
|
|
10937
|
+
chunksToProcess = parseFileAsText(parsed.path, content, this.config.indexing.linesPerChunk);
|
|
10938
|
+
}
|
|
10939
|
+
}
|
|
10940
|
+
chunksToProcess = selectIndexableChunks(
|
|
10941
|
+
chunksToProcess,
|
|
10942
|
+
this.config.indexing.maxChunksPerFile,
|
|
10943
|
+
this.config.indexing.semanticOnly
|
|
10944
|
+
);
|
|
10945
|
+
for (const chunk of chunksToProcess) {
|
|
10946
|
+
const texts = createEmbeddingTexts(chunk, parsed.path, maxChunkTokens);
|
|
10947
|
+
chunksCount += 1;
|
|
10948
|
+
for (const text of texts) {
|
|
10949
|
+
tokensToEmbed += estimateTokens2(text);
|
|
10950
|
+
}
|
|
10951
|
+
}
|
|
10952
|
+
}
|
|
10953
|
+
}
|
|
10954
|
+
return { filesCount, chunksCount, tokensToEmbed };
|
|
10955
|
+
}
|
|
10869
10956
|
async index(onProgress) {
|
|
10870
10957
|
return this.withIndexMutationLease("index", async (recoveredOwners) => {
|
|
10871
10958
|
return this.indexUnlocked(onProgress, recoveredOwners);
|
|
@@ -15369,6 +15456,9 @@ async function runIndexCodebase(projectRoot, host, args, onProgress) {
|
|
|
15369
15456
|
if (args.estimateOnly) {
|
|
15370
15457
|
return { kind: "estimate", estimate: await indexer.estimateCost() };
|
|
15371
15458
|
}
|
|
15459
|
+
if (args.dryRun) {
|
|
15460
|
+
return { kind: "dryrun", dryrun: await indexer.dryRunCost() };
|
|
15461
|
+
}
|
|
15372
15462
|
const coordinated = runCoordinatedIndex(root, host, args.force ?? false, (progress) => {
|
|
15373
15463
|
if (onProgress) {
|
|
15374
15464
|
void onProgress(formatProgressTitle(progress), {
|
|
@@ -18295,6 +18385,7 @@ async function executeCodebaseEditContext(projectRoot, host, args) {
|
|
|
18295
18385
|
async function executeIndexCodebase(projectRoot, host, args, onProgress) {
|
|
18296
18386
|
const result = await runIndexCodebase(projectRoot, host, args, onProgress);
|
|
18297
18387
|
if (result.kind === "estimate") return { text: formatCostEstimate(result.estimate) };
|
|
18388
|
+
if (result.kind === "dryrun") return { text: formatDryRunEstimate(result.dryrun) };
|
|
18298
18389
|
if (result.kind === "busy") return { text: result.text, isError: true };
|
|
18299
18390
|
if (result.kind === "message") return { text: result.text };
|
|
18300
18391
|
return { text: formatIndexStats(result.stats, args.verbose ?? false) };
|
|
@@ -18622,6 +18713,7 @@ ${formatCodebasePeek(results)}`;
|
|
|
18622
18713
|
{
|
|
18623
18714
|
force: allowNullAsUndefined(z2.boolean().optional().default(false)).describe("Force reindex even if already indexed"),
|
|
18624
18715
|
estimateOnly: allowNullAsUndefined(z2.boolean().optional().default(false)).describe("Only show cost estimate without indexing"),
|
|
18716
|
+
dryRun: allowNullAsUndefined(z2.boolean().optional().default(false)).describe("Parse the file set and report the exact embedding token total without indexing. Read-only; the index is not changed. The total is the value 'Tokens used' climbs to for a force index (and an upper bound for an incremental)."),
|
|
18625
18717
|
verbose: allowNullAsUndefined(z2.boolean().optional().default(false)).describe("Show detailed info about skipped files and parsing failures")
|
|
18626
18718
|
},
|
|
18627
18719
|
async (args) => {
|
|
@@ -18836,7 +18928,7 @@ ${formatSearchResults(results)}` }] };
|
|
|
18836
18928
|
// src/adapters/mcp/server.ts
|
|
18837
18929
|
function getServerInstructions(host) {
|
|
18838
18930
|
const hostText = `host ${host}`;
|
|
18839
|
-
return `This MCP server is the preferred codebase-understanding path for ${hostText}. Start a repository task with index_status when index readiness or freshness is unknown. Use codebase_context as the preferred first entry point because it returns a token-budgeted location pack and routes to definitions or call-graph helpers when symbol intent is present. Keep the default tokenBudget for normal discovery, then use implementation_lookup, codebase_search, or a targeted file read only for selected locations that need source content. Use codebase_peek for direct conceptual location lookup. For exact identifiers or exhaustive matches, use grep. After identifying symbols, use call_graph or call_graph_path to trace dependencies. If the index is unavailable, run index_codebase, then retry the retrieval tool.`;
|
|
18931
|
+
return `This MCP server is the preferred codebase-understanding path for ${hostText}. Start a repository task with index_status when index readiness or freshness is unknown. Use codebase_context as the preferred first entry point because it returns a token-budgeted location pack and routes to definitions or call-graph helpers when symbol intent is present. For code changes with a known or suspected symbol target, optionally call codebase_edit_context as a compact pre-edit step for bounded source plus direct callers and callees before broad file reads. Keep the default tokenBudget for normal discovery, then use implementation_lookup, codebase_search, or a targeted file read only for selected locations that need source content. Use codebase_peek for direct conceptual location lookup. For exact identifiers or exhaustive matches, use grep. After identifying symbols, use call_graph or call_graph_path to trace dependencies. If the index is unavailable, run index_codebase, then retry the retrieval tool.`;
|
|
18840
18932
|
}
|
|
18841
18933
|
function createMcpServer(projectRoot, config, host) {
|
|
18842
18934
|
const server = new McpServer({
|
|
@@ -22254,6 +22346,7 @@ function parseIndexArgs(argv, cwd) {
|
|
|
22254
22346
|
let config;
|
|
22255
22347
|
let force = false;
|
|
22256
22348
|
let estimateOnly = false;
|
|
22349
|
+
let dryRun = false;
|
|
22257
22350
|
let verbose = false;
|
|
22258
22351
|
for (let i = 0; i < argv.length; i += 1) {
|
|
22259
22352
|
const arg = argv[i];
|
|
@@ -22291,13 +22384,16 @@ function parseIndexArgs(argv, cwd) {
|
|
|
22291
22384
|
host = parseHostMode(value);
|
|
22292
22385
|
continue;
|
|
22293
22386
|
}
|
|
22294
|
-
if (arg === "--force" || arg === "--estimate-only" || arg === "--verbose") {
|
|
22387
|
+
if (arg === "--force" || arg === "--estimate-only" || arg === "--dry-run" || arg === "--verbose") {
|
|
22295
22388
|
if (arg === "--force") {
|
|
22296
22389
|
force = true;
|
|
22297
22390
|
}
|
|
22298
22391
|
if (arg === "--estimate-only") {
|
|
22299
22392
|
estimateOnly = true;
|
|
22300
22393
|
}
|
|
22394
|
+
if (arg === "--dry-run") {
|
|
22395
|
+
dryRun = true;
|
|
22396
|
+
}
|
|
22301
22397
|
if (arg === "--verbose") {
|
|
22302
22398
|
verbose = true;
|
|
22303
22399
|
}
|
|
@@ -22308,7 +22404,7 @@ function parseIndexArgs(argv, cwd) {
|
|
|
22308
22404
|
}
|
|
22309
22405
|
throw new Error(`Unknown index option: ${arg}`);
|
|
22310
22406
|
}
|
|
22311
|
-
return { project, host, config, force, estimateOnly, verbose };
|
|
22407
|
+
return { project, host, config, force, estimateOnly, dryRun, verbose };
|
|
22312
22408
|
}
|
|
22313
22409
|
function loadCliRawConfig(args) {
|
|
22314
22410
|
return args.config ? loadConfigFile(args.config) : loadMergedConfig(args.project, args.host);
|
|
@@ -22325,6 +22421,7 @@ Options:
|
|
|
22325
22421
|
--config <path> Explicit JSON config path
|
|
22326
22422
|
--force Rebuild index even if already up to date
|
|
22327
22423
|
--estimate-only Estimate indexing cost only
|
|
22424
|
+
--dry-run Parse only; report the exact embedding token total without indexing
|
|
22328
22425
|
--verbose Include detailed final index statistics
|
|
22329
22426
|
--help Show this message
|
|
22330
22427
|
|
|
@@ -22520,6 +22617,7 @@ async function handleIndexCommand(argv, cwd, deps = {}) {
|
|
|
22520
22617
|
const indexArgs = {
|
|
22521
22618
|
force: parsedArgs.force,
|
|
22522
22619
|
estimateOnly: parsedArgs.estimateOnly,
|
|
22620
|
+
dryRun: parsedArgs.dryRun,
|
|
22523
22621
|
verbose: parsedArgs.verbose
|
|
22524
22622
|
};
|
|
22525
22623
|
const result = await runIndex(parsedArgs.project, parsedArgs.host, indexArgs, (title, metadata) => {
|