@zokizuan/satori-mcp 4.11.8 → 4.11.13
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 +6 -6
- package/dist/config.d.ts +1 -0
- package/dist/config.js +1 -1
- package/dist/core/backend-diagnostics.js +5 -2
- package/dist/core/handlers.d.ts +13 -1
- package/dist/core/handlers.js +96 -10
- package/dist/core/manage-indexing-handlers.d.ts +8 -4
- package/dist/core/manage-indexing-handlers.js +117 -13
- package/dist/core/manage-maintenance-handlers.d.ts +2 -0
- package/dist/core/manage-maintenance-handlers.js +5 -1
- package/dist/core/manage-types.d.ts +2 -2
- package/dist/core/search-frontdoor.d.ts +16 -1
- package/dist/core/search-frontdoor.js +25 -12
- package/dist/core/snapshot.d.ts +3 -2
- package/dist/core/snapshot.js +23 -2
- package/dist/core/sync.d.ts +1 -0
- package/dist/core/sync.js +7 -2
- package/dist/core/tool-response-builders.d.ts +8 -1
- package/dist/core/tool-response-builders.js +5 -1
- package/dist/core/vector-backend-maintenance.js +1 -2
- package/dist/server/start-server.js +4 -1
- package/dist/tools/manage_index.js +6 -3
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -7,8 +7,8 @@ Read-only MCP server for Satori. It gives coding agents six deterministic tools
|
|
|
7
7
|
Use the CLI installer for normal setup:
|
|
8
8
|
|
|
9
9
|
```bash
|
|
10
|
-
npx -y @zokizuan/satori-cli@
|
|
11
|
-
npx -y @zokizuan/satori-cli@
|
|
10
|
+
npx -y @zokizuan/satori-cli@latest install --client all
|
|
11
|
+
npx -y @zokizuan/satori-cli@latest doctor
|
|
12
12
|
```
|
|
13
13
|
|
|
14
14
|
The CLI installer supports `codex`, `claude`, `opencode`, and `all`. It creates the runtime cache, writes the stable launcher, and writes client config for you. Avoid using `npx` as the resident MCP server command; first-run package resolution can exceed normal MCP startup timeouts.
|
|
@@ -16,7 +16,7 @@ The CLI installer supports `codex`, `claude`, `opencode`, and `all`. It creates
|
|
|
16
16
|
Use `--profile default|minimal|all-text` to write repo-local `satori.toml` during install:
|
|
17
17
|
|
|
18
18
|
```bash
|
|
19
|
-
npx -y @zokizuan/satori-cli@
|
|
19
|
+
npx -y @zokizuan/satori-cli@latest install --client all --profile minimal
|
|
20
20
|
```
|
|
21
21
|
|
|
22
22
|
Profiles control indexing breadth, not search scope. `default` is safe-broad, `minimal` indexes source plus docs/text, and `all-text` indexes additional UTF-8 text files under the size limit. `search_codebase` still defaults to `scope=runtime`.
|
|
@@ -45,7 +45,7 @@ For Codex, add `--install-guidance-hook` only when you want an installer-managed
|
|
|
45
45
|
Advanced direct execution is available through the package bin:
|
|
46
46
|
|
|
47
47
|
```bash
|
|
48
|
-
npx -y @zokizuan/satori-mcp@4.11.
|
|
48
|
+
npx -y @zokizuan/satori-mcp@4.11.13 --help
|
|
49
49
|
```
|
|
50
50
|
|
|
51
51
|
Use direct package execution for inspection, smoke tests, or unsupported harnesses. For supported clients, prefer `satori-cli install` so startup does not depend on package-manager resolution.
|
|
@@ -131,11 +131,11 @@ The full generated tool reference below is kept in the npm README for MCP client
|
|
|
131
131
|
|
|
132
132
|
### `manage_index`
|
|
133
133
|
|
|
134
|
-
Manage index lifecycle operations (create/reindex/sync/status/clear) for a codebase path. Ignore-rule edits in repo-root .satoriignore/.gitignore reconcile automatically in the normal sync path. Use action="sync" for immediate convergence and action="reindex" for full rebuild recovery (preflight may block unnecessary ignore-only reindex churn unless allowUnnecessaryReindex=true). create/reindex return the kickoff response immediately and do not poll to terminal state; use action="status" to observe progress.
|
|
134
|
+
Manage index lifecycle operations (create/reindex/sync/status/clear/repair) for a codebase path. repair rebuilds local readiness only when existing vector payload and trusted runtime fingerprint proof match; otherwise it refuses and asks for create/reindex. Ignore-rule edits in repo-root .satoriignore/.gitignore reconcile automatically in the normal sync path. Use action="sync" for immediate convergence and action="reindex" for full rebuild recovery (preflight may block unnecessary ignore-only reindex churn unless allowUnnecessaryReindex=true). create/reindex return the kickoff response immediately and do not poll to terminal state; use action="status" to observe progress.
|
|
135
135
|
|
|
136
136
|
| Parameter | Type | Required | Default | Description |
|
|
137
137
|
|---|---|---|---|---|
|
|
138
|
-
| `action` | enum("create", "reindex", "sync", "status", "clear") | yes | | Required operation to run. |
|
|
138
|
+
| `action` | enum("create", "reindex", "sync", "status", "clear", "repair") | yes | | Required operation to run. |
|
|
139
139
|
| `path` | string | yes | | ABSOLUTE path to the target codebase. |
|
|
140
140
|
| `force` | boolean | no | | Only for action='create'. Force rebuild from scratch. |
|
|
141
141
|
| `allowUnnecessaryReindex` | boolean | no | | Only for action='reindex'. Override preflight block when reindex is detected as unnecessary ignore-only churn. |
|
package/dist/config.d.ts
CHANGED
|
@@ -54,6 +54,7 @@ export interface CodebaseSnapshotV1 {
|
|
|
54
54
|
}
|
|
55
55
|
interface CodebaseInfoBase {
|
|
56
56
|
lastUpdated: string;
|
|
57
|
+
collectionName?: string;
|
|
57
58
|
indexFingerprint?: IndexFingerprint;
|
|
58
59
|
fingerprintSource?: FingerprintSource;
|
|
59
60
|
reindexReason?: 'legacy_unverified_fingerprint' | 'fingerprint_mismatch' | 'missing_fingerprint' | 'navigation_recovery_failed';
|
package/dist/config.js
CHANGED
|
@@ -209,7 +209,7 @@ Environment Variables:
|
|
|
209
209
|
|
|
210
210
|
Examples:
|
|
211
211
|
# Install resident MCP config without package-manager startup on every client launch
|
|
212
|
-
npx -y @zokizuan/satori-cli@
|
|
212
|
+
npx -y @zokizuan/satori-cli@latest install --client all
|
|
213
213
|
|
|
214
214
|
# Start MCP server with OpenAI and explicit Milvus address
|
|
215
215
|
OPENAI_API_KEY=sk-xxx MILVUS_ADDRESS=localhost:19530 satori
|
|
@@ -36,7 +36,7 @@ function createVectorBackendDiagnostic(code) {
|
|
|
36
36
|
case "ZILLIZ_CLUSTER_STOPPED":
|
|
37
37
|
return {
|
|
38
38
|
code,
|
|
39
|
-
message: "
|
|
39
|
+
message: "Zilliz Cloud cluster is stopped. Resume it in Zilliz Cloud, then retry the tool call.",
|
|
40
40
|
hints: {
|
|
41
41
|
backend: {
|
|
42
42
|
code,
|
|
@@ -101,13 +101,16 @@ function createVectorBackendDiagnostic(code) {
|
|
|
101
101
|
case "VECTOR_BACKEND_CONNECTION_CLOSED":
|
|
102
102
|
return {
|
|
103
103
|
code,
|
|
104
|
-
message: "Vector backend connection closed before the tool call completed.",
|
|
104
|
+
message: "Vector backend connection closed before the tool call completed. Check backend health, provider environment, network connectivity, or an MCP process restart in the previous log lines.",
|
|
105
105
|
hints: {
|
|
106
106
|
backend: {
|
|
107
107
|
code,
|
|
108
108
|
provider: "unknown",
|
|
109
109
|
retryable: true,
|
|
110
110
|
nextSteps: [
|
|
111
|
+
"If the previous response mentions ZILLIZ_CLUSTER_STOPPED, resume the Zilliz cluster at https://cloud.zilliz.com.",
|
|
112
|
+
"If the previous response mentions MISSING_PROVIDER_CONFIG, set the missing env values in the MCP client environment and restart the client.",
|
|
113
|
+
"For non-Zilliz Milvus-compatible backends, confirm the service is running and reachable at MILVUS_ADDRESS.",
|
|
111
114
|
"Retry the MCP tool call after confirming the backend is healthy.",
|
|
112
115
|
"Restart the MCP client/session if the MCP process exited after the transport failure.",
|
|
113
116
|
],
|
package/dist/core/handlers.d.ts
CHANGED
|
@@ -58,6 +58,8 @@ export declare class ToolHandlers {
|
|
|
58
58
|
private buildReindexInstruction;
|
|
59
59
|
private buildReindexHint;
|
|
60
60
|
private buildCreateHint;
|
|
61
|
+
private buildSyncHint;
|
|
62
|
+
private buildRepairHint;
|
|
61
63
|
private buildManageIndexRecommendedAction;
|
|
62
64
|
private buildStatusHint;
|
|
63
65
|
private buildManageRequiresReindexHints;
|
|
@@ -83,9 +85,9 @@ export declare class ToolHandlers {
|
|
|
83
85
|
private getContextActiveIgnorePatterns;
|
|
84
86
|
private getContextIndexedExtensions;
|
|
85
87
|
private getContextTrackedRelativePaths;
|
|
86
|
-
private writeIndexCompletionMarker;
|
|
87
88
|
private clearIndexCompletionMarker;
|
|
88
89
|
private pruneIndexedCollectionFamily;
|
|
90
|
+
private pruneUnprovenStagedCollectionFamily;
|
|
89
91
|
private markCodebaseCleared;
|
|
90
92
|
private stringifyToolJson;
|
|
91
93
|
private buildRuntimeOwnerConflictResponseIfBlocked;
|
|
@@ -96,11 +98,14 @@ export declare class ToolHandlers {
|
|
|
96
98
|
private buildManageActionBlockedMessage;
|
|
97
99
|
private getManageRetryAfterMs;
|
|
98
100
|
private buildStaleLocalHint;
|
|
101
|
+
private getSnapshotCollectionName;
|
|
102
|
+
private canSyncStaleLocal;
|
|
99
103
|
private buildStaleLocalMessage;
|
|
100
104
|
private withProofDebugHint;
|
|
101
105
|
private validateCompletionProof;
|
|
102
106
|
private extractIndexedRecoveryFromCompletionProof;
|
|
103
107
|
private recoverIndexedSnapshotFromCompletionProof;
|
|
108
|
+
private getActiveIndexedCollectionNameForSnapshotRecovery;
|
|
104
109
|
private refreshSnapshotStateFromDisk;
|
|
105
110
|
private isPathWithinCodebase;
|
|
106
111
|
private getTrackedRootEntryForPath;
|
|
@@ -181,6 +186,13 @@ export declare class ToolHandlers {
|
|
|
181
186
|
}>;
|
|
182
187
|
isError?: boolean;
|
|
183
188
|
}>;
|
|
189
|
+
handleRepairIndex(args: ToolArgs): Promise<{
|
|
190
|
+
content: Array<{
|
|
191
|
+
type: "text";
|
|
192
|
+
text: string;
|
|
193
|
+
}>;
|
|
194
|
+
isError?: boolean;
|
|
195
|
+
}>;
|
|
184
196
|
handleSearchCode(args: ToolArgs): Promise<{
|
|
185
197
|
content: {
|
|
186
198
|
type: string;
|
package/dist/core/handlers.js
CHANGED
|
@@ -151,6 +151,7 @@ export class ToolHandlers {
|
|
|
151
151
|
buildManageIndexRecommendedAction: this.buildManageIndexRecommendedAction.bind(this),
|
|
152
152
|
buildCreateHint: this.buildCreateHint.bind(this),
|
|
153
153
|
buildReindexHint: this.buildReindexHint.bind(this),
|
|
154
|
+
buildRepairHint: this.buildRepairHint.bind(this),
|
|
154
155
|
buildStatusHint: this.buildStatusHint.bind(this),
|
|
155
156
|
buildStaleLocalHint: this.buildStaleLocalHint.bind(this),
|
|
156
157
|
buildStaleLocalMessage: this.buildStaleLocalMessage.bind(this),
|
|
@@ -267,8 +268,10 @@ export class ToolHandlers {
|
|
|
267
268
|
buildStaleLocalMessage: this.buildStaleLocalMessage.bind(this),
|
|
268
269
|
enforceFingerprintGate: this.enforceFingerprintGate.bind(this),
|
|
269
270
|
buildReindexHint: this.buildReindexHint.bind(this),
|
|
271
|
+
buildSyncHint: this.buildSyncHint.bind(this),
|
|
270
272
|
touchWatchedCodebase: this.touchWatchedCodebase.bind(this),
|
|
271
273
|
manageVectorBackendResponse: this.toolResponseBuilders.manageVectorBackendResponse.bind(this.toolResponseBuilders),
|
|
274
|
+
canSyncStaleLocal: this.canSyncStaleLocal.bind(this),
|
|
272
275
|
};
|
|
273
276
|
this.manageMaintenanceHandlers = new ManageMaintenanceHandlers(manageMaintenanceHandlersHost);
|
|
274
277
|
const getManageIndexingContext = () => this.context;
|
|
@@ -299,6 +302,7 @@ export class ToolHandlers {
|
|
|
299
302
|
getSnapshotCodebaseInfo: this.getSnapshotCodebaseInfo.bind(this),
|
|
300
303
|
getSnapshotIndexedCodebases: this.getSnapshotIndexedCodebases.bind(this),
|
|
301
304
|
buildManageActionBlockedMessage: this.buildManageActionBlockedMessage.bind(this),
|
|
305
|
+
buildCreateHint: this.buildCreateHint.bind(this),
|
|
302
306
|
buildStatusHint: this.buildStatusHint.bind(this),
|
|
303
307
|
getManageRetryAfterMs: this.getManageRetryAfterMs.bind(this),
|
|
304
308
|
buildIndexingMetadata: this.buildIndexingMetadata.bind(this),
|
|
@@ -319,8 +323,8 @@ export class ToolHandlers {
|
|
|
319
323
|
getContextActiveIgnorePatterns: this.getContextActiveIgnorePatterns.bind(this),
|
|
320
324
|
getContextIndexedExtensions: this.getContextIndexedExtensions.bind(this),
|
|
321
325
|
canonicalizeCodebasePath: this.canonicalizeCodebasePath.bind(this),
|
|
322
|
-
writeIndexCompletionMarker: this.writeIndexCompletionMarker.bind(this),
|
|
323
326
|
pruneIndexedCollectionFamily: this.pruneIndexedCollectionFamily.bind(this),
|
|
327
|
+
pruneUnprovenStagedCollectionFamily: this.pruneUnprovenStagedCollectionFamily.bind(this),
|
|
324
328
|
getContextTrackedRelativePaths: this.getContextTrackedRelativePaths.bind(this),
|
|
325
329
|
setIndexingStats: this.setIndexingStats.bind(this),
|
|
326
330
|
rebuildCallGraphForIndex: this.rebuildCallGraphForIndex.bind(this),
|
|
@@ -396,6 +400,24 @@ export class ToolHandlers {
|
|
|
396
400
|
}
|
|
397
401
|
};
|
|
398
402
|
}
|
|
403
|
+
buildSyncHint(codebasePath) {
|
|
404
|
+
return {
|
|
405
|
+
tool: "manage_index",
|
|
406
|
+
args: {
|
|
407
|
+
action: "sync",
|
|
408
|
+
path: codebasePath
|
|
409
|
+
}
|
|
410
|
+
};
|
|
411
|
+
}
|
|
412
|
+
buildRepairHint(codebasePath) {
|
|
413
|
+
return {
|
|
414
|
+
tool: "manage_index",
|
|
415
|
+
args: {
|
|
416
|
+
action: "repair",
|
|
417
|
+
path: codebasePath
|
|
418
|
+
}
|
|
419
|
+
};
|
|
420
|
+
}
|
|
399
421
|
buildManageIndexRecommendedAction(action, codebasePath, reason) {
|
|
400
422
|
return {
|
|
401
423
|
tool: "manage_index",
|
|
@@ -567,9 +589,6 @@ export class ToolHandlers {
|
|
|
567
589
|
const paths = this.contextLifecycle().getTrackedRelativePaths?.(codebasePath);
|
|
568
590
|
return Array.isArray(paths) ? paths.filter((entry) => typeof entry === 'string') : [];
|
|
569
591
|
}
|
|
570
|
-
async writeIndexCompletionMarker(codebasePath, marker) {
|
|
571
|
-
await this.contextLifecycle().writeIndexCompletionMarker?.(codebasePath, marker);
|
|
572
|
-
}
|
|
573
592
|
async clearIndexCompletionMarker(codebasePath) {
|
|
574
593
|
await this.contextLifecycle().clearIndexCompletionMarker?.(codebasePath);
|
|
575
594
|
}
|
|
@@ -577,6 +596,10 @@ export class ToolHandlers {
|
|
|
577
596
|
const dropped = await this.contextLifecycle().pruneIndexedCollectionFamily?.(codebasePath, keepCollectionName);
|
|
578
597
|
return Array.isArray(dropped) ? dropped.filter((entry) => typeof entry === 'string') : [];
|
|
579
598
|
}
|
|
599
|
+
async pruneUnprovenStagedCollectionFamily(codebasePath) {
|
|
600
|
+
const dropped = await this.contextLifecycle().pruneUnprovenStagedCollectionFamily?.(codebasePath);
|
|
601
|
+
return Array.isArray(dropped) ? dropped.filter((entry) => typeof entry === 'string') : [];
|
|
602
|
+
}
|
|
580
603
|
markCodebaseCleared(codebasePath, collectionName) {
|
|
581
604
|
this.snapshotCapabilities().markCodebaseCleared?.(codebasePath, collectionName);
|
|
582
605
|
}
|
|
@@ -654,7 +677,8 @@ export class ToolHandlers {
|
|
|
654
677
|
}
|
|
655
678
|
const decision = decideInterruptedIndexingRecovery(marker, this.runtimeFingerprint);
|
|
656
679
|
if (decision.action === "promote_indexed") {
|
|
657
|
-
this.
|
|
680
|
+
const collectionName = await this.getActiveIndexedCollectionNameForSnapshotRecovery(codebasePath);
|
|
681
|
+
this.snapshotManager.setCodebaseIndexed(codebasePath, decision.stats, decision.indexFingerprint, "verified", collectionName);
|
|
658
682
|
this.saveSnapshotIfSupported();
|
|
659
683
|
const recoveryMode = decision.reason === "valid_marker_runtime_mismatch"
|
|
660
684
|
? " using completion marker proof from a different runtime fingerprint"
|
|
@@ -691,11 +715,48 @@ export class ToolHandlers {
|
|
|
691
715
|
return DEFAULT_MANAGE_RETRY_AFTER_MS;
|
|
692
716
|
}
|
|
693
717
|
buildStaleLocalHint(codebasePath, reason) {
|
|
718
|
+
const preferSync = this.canSyncStaleLocal(codebasePath, reason);
|
|
719
|
+
const preferRepair = !preferSync && reason === "missing_marker_doc";
|
|
694
720
|
return {
|
|
695
721
|
completionProof: reason,
|
|
696
|
-
recommendedAction:
|
|
722
|
+
recommendedAction: preferSync
|
|
723
|
+
? this.buildSyncHint(codebasePath)
|
|
724
|
+
: preferRepair
|
|
725
|
+
? this.buildRepairHint(codebasePath)
|
|
726
|
+
: this.buildCreateHint(codebasePath),
|
|
727
|
+
...(preferSync ? { sync: this.buildSyncHint(codebasePath) } : {}),
|
|
728
|
+
...(preferRepair ? { create: this.buildCreateHint(codebasePath) } : {})
|
|
697
729
|
};
|
|
698
730
|
}
|
|
731
|
+
getSnapshotCollectionName(codebasePath) {
|
|
732
|
+
const fromSnapshot = this.snapshotCapabilities().getCodebaseCollectionName?.(codebasePath);
|
|
733
|
+
if (typeof fromSnapshot === 'string' && fromSnapshot.trim().length > 0) {
|
|
734
|
+
return fromSnapshot.trim();
|
|
735
|
+
}
|
|
736
|
+
const fromInfo = this.getSnapshotCodebaseInfo(codebasePath)?.collectionName;
|
|
737
|
+
return typeof fromInfo === 'string' && fromInfo.trim().length > 0
|
|
738
|
+
? fromInfo.trim()
|
|
739
|
+
: undefined;
|
|
740
|
+
}
|
|
741
|
+
canSyncStaleLocal(codebasePath, reason) {
|
|
742
|
+
if (reason !== "missing_marker_doc") {
|
|
743
|
+
return false;
|
|
744
|
+
}
|
|
745
|
+
const info = this.getSnapshotCodebaseInfo(codebasePath);
|
|
746
|
+
if (!info || (info.status !== 'indexed' && info.status !== 'sync_completed')) {
|
|
747
|
+
return false;
|
|
748
|
+
}
|
|
749
|
+
if (info.fingerprintSource !== 'verified' || !info.indexFingerprint) {
|
|
750
|
+
return false;
|
|
751
|
+
}
|
|
752
|
+
if (!this.getSnapshotCollectionName(codebasePath)) {
|
|
753
|
+
return false;
|
|
754
|
+
}
|
|
755
|
+
if (!this.fingerprintsEqual(info.indexFingerprint, this.runtimeFingerprint)) {
|
|
756
|
+
return false;
|
|
757
|
+
}
|
|
758
|
+
return true;
|
|
759
|
+
}
|
|
699
760
|
buildStaleLocalMessage(codebasePath, requestedPath, reason) {
|
|
700
761
|
const requestedPathDetail = requestedPath !== codebasePath
|
|
701
762
|
? ` Requested path: '${requestedPath}'.`
|
|
@@ -771,15 +832,26 @@ export class ToolHandlers {
|
|
|
771
832
|
},
|
|
772
833
|
};
|
|
773
834
|
}
|
|
774
|
-
recoverIndexedSnapshotFromCompletionProof(codebasePath, completionProof) {
|
|
835
|
+
async recoverIndexedSnapshotFromCompletionProof(codebasePath, completionProof) {
|
|
775
836
|
const recovered = this.extractIndexedRecoveryFromCompletionProof(completionProof);
|
|
776
837
|
if (!recovered) {
|
|
777
838
|
return false;
|
|
778
839
|
}
|
|
779
|
-
this.
|
|
840
|
+
const collectionName = await this.getActiveIndexedCollectionNameForSnapshotRecovery(codebasePath);
|
|
841
|
+
this.snapshotManager.setCodebaseIndexed(codebasePath, recovered.stats, recovered.indexFingerprint, 'verified', collectionName);
|
|
780
842
|
this.saveSnapshotIfSupported();
|
|
781
843
|
return true;
|
|
782
844
|
}
|
|
845
|
+
async getActiveIndexedCollectionNameForSnapshotRecovery(codebasePath) {
|
|
846
|
+
const context = this.context;
|
|
847
|
+
if (typeof context.getActiveIndexedCollectionName !== 'function') {
|
|
848
|
+
return undefined;
|
|
849
|
+
}
|
|
850
|
+
const collectionName = await context.getActiveIndexedCollectionName(codebasePath);
|
|
851
|
+
return typeof collectionName === 'string' && collectionName.trim().length > 0
|
|
852
|
+
? collectionName.trim()
|
|
853
|
+
: undefined;
|
|
854
|
+
}
|
|
783
855
|
refreshSnapshotStateFromDisk() {
|
|
784
856
|
const snapshotManager = this.snapshotManager;
|
|
785
857
|
if (typeof snapshotManager.refreshFromDiskIfChanged !== 'function') {
|
|
@@ -818,7 +890,11 @@ export class ToolHandlers {
|
|
|
818
890
|
}
|
|
819
891
|
let collectionName;
|
|
820
892
|
try {
|
|
821
|
-
|
|
893
|
+
const snapshotCollectionName = this.getSnapshotCollectionName(codebasePath);
|
|
894
|
+
if (snapshotCollectionName) {
|
|
895
|
+
collectionName = snapshotCollectionName;
|
|
896
|
+
}
|
|
897
|
+
else if (typeof context.getActiveIndexedCollectionName === 'function') {
|
|
822
898
|
const activeCollectionName = await context.getActiveIndexedCollectionName(codebasePath);
|
|
823
899
|
if (typeof activeCollectionName === 'string' && activeCollectionName.trim().length > 0) {
|
|
824
900
|
collectionName = activeCollectionName;
|
|
@@ -1179,6 +1255,7 @@ export class ToolHandlers {
|
|
|
1179
1255
|
}
|
|
1180
1256
|
buildRequiresReindexFileOutlinePayload(codebasePath, input, detail, reason = 'requires_reindex') {
|
|
1181
1257
|
const detailLine = detail ? `${detail}\n\n` : '';
|
|
1258
|
+
const preferRepair = reason === 'missing_symbol_registry' || reason === 'missing_relationship_sidecar';
|
|
1182
1259
|
return {
|
|
1183
1260
|
status: 'requires_reindex',
|
|
1184
1261
|
reason,
|
|
@@ -1186,8 +1263,11 @@ export class ToolHandlers {
|
|
|
1186
1263
|
file: input.file,
|
|
1187
1264
|
outline: null,
|
|
1188
1265
|
hasMore: false,
|
|
1189
|
-
message:
|
|
1266
|
+
message: preferRepair
|
|
1267
|
+
? `${detailLine}Relationship-backed navigation sidecars are missing. Please run manage_index with {"action":"repair","path":"${codebasePath}"}.`
|
|
1268
|
+
: `${detailLine}Relationship-backed navigation sidecars are missing or incompatible. Please run manage_index with {"action":"reindex","path":"${codebasePath}"}.`,
|
|
1190
1269
|
hints: {
|
|
1270
|
+
...(preferRepair ? { repair: this.buildRepairHint(codebasePath) } : {}),
|
|
1191
1271
|
reindex: this.buildReindexHint(codebasePath)
|
|
1192
1272
|
}
|
|
1193
1273
|
};
|
|
@@ -1304,6 +1384,9 @@ export class ToolHandlers {
|
|
|
1304
1384
|
async handleReindexCodebase(args) {
|
|
1305
1385
|
return this.manageIndexingHandlers.handleReindexCodebase(args);
|
|
1306
1386
|
}
|
|
1387
|
+
async handleRepairIndex(args) {
|
|
1388
|
+
return this.manageIndexingHandlers.handleRepairIndex(args);
|
|
1389
|
+
}
|
|
1307
1390
|
async handleSearchCode(args) {
|
|
1308
1391
|
const scope = (typeof args.scope === 'string' ? args.scope : 'runtime');
|
|
1309
1392
|
const resultMode = (typeof args.resultMode === 'string' ? args.resultMode : 'grouped');
|
|
@@ -1382,8 +1465,11 @@ export class ToolHandlers {
|
|
|
1382
1465
|
buildFreshnessBlockedSearchPayload: (codebasePath, freshnessDecision, searchContext) => this.buildFreshnessBlockedSearchPayload(codebasePath, freshnessDecision, searchContext),
|
|
1383
1466
|
buildManageIndexRecommendedAction: (action, codebasePath, rationale) => this.buildManageIndexRecommendedAction(action, codebasePath, rationale),
|
|
1384
1467
|
buildCreateHint: (codebasePath) => this.buildCreateHint(codebasePath),
|
|
1468
|
+
buildSyncHint: (codebasePath) => this.buildSyncHint(codebasePath),
|
|
1469
|
+
buildRepairHint: (codebasePath) => this.buildRepairHint(codebasePath),
|
|
1385
1470
|
buildStaleLocalHint: (codebasePath, reason) => this.buildStaleLocalHint(codebasePath, reason),
|
|
1386
1471
|
buildStaleLocalMessage: (codebasePath, requestedPath, reason) => this.buildStaleLocalMessage(codebasePath, requestedPath, reason),
|
|
1472
|
+
canSyncStaleLocal: (codebasePath, reason) => this.canSyncStaleLocal(codebasePath, reason),
|
|
1387
1473
|
withProofDebugHint: (payload, proofDebugHint) => this.withProofDebugHint(payload, proofDebugHint),
|
|
1388
1474
|
isPartialIndexNavigationUnavailable: (info) => this.isPartialIndexNavigationUnavailable(info),
|
|
1389
1475
|
partialIndexWarnings: [
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Context
|
|
1
|
+
import { Context } from "@zokizuan/satori-core";
|
|
2
2
|
import type { SnapshotManager } from "./snapshot.js";
|
|
3
3
|
import type { SyncManager } from "./sync.js";
|
|
4
4
|
import { ManageIndexAction } from "./manage-types.js";
|
|
@@ -44,14 +44,15 @@ type ManageIndexingHandlersHost = {
|
|
|
44
44
|
getSnapshotIndexingCodebases(): string[];
|
|
45
45
|
getSnapshotCodebaseInfo(codebasePath: string): Record<string, unknown> | undefined;
|
|
46
46
|
getSnapshotIndexedCodebases(): string[];
|
|
47
|
-
buildManageActionBlockedMessage(codebasePath: string, action: "create" | "reindex"): string;
|
|
47
|
+
buildManageActionBlockedMessage(codebasePath: string, action: "create" | "reindex" | "repair"): string;
|
|
48
|
+
buildCreateHint(codebasePath: string): Record<string, unknown>;
|
|
48
49
|
buildStatusHint(codebasePath: string): Record<string, unknown>;
|
|
49
50
|
getManageRetryAfterMs(): number;
|
|
50
51
|
buildIndexingMetadata(codebasePath: string): Record<string, unknown> | undefined;
|
|
51
52
|
buildReindexInstruction(codebasePath: string, detail?: string): string;
|
|
52
53
|
buildManageRequiresReindexHints(codebasePath: string): Record<string, unknown>;
|
|
53
54
|
validateCompletionProof(codebasePath: string): Promise<CompletionProofValidationResult>;
|
|
54
|
-
recoverIndexedSnapshotFromCompletionProof(codebasePath: string, proof: CompletionProofValidationResult): boolean
|
|
55
|
+
recoverIndexedSnapshotFromCompletionProof(codebasePath: string, proof: CompletionProofValidationResult): Promise<boolean>;
|
|
55
56
|
isZillizBackend(): boolean;
|
|
56
57
|
resolveCollectionName(codebasePath: string): string;
|
|
57
58
|
dropZillizCollectionForCreate(collectionName: string): Promise<{
|
|
@@ -67,8 +68,8 @@ type ManageIndexingHandlersHost = {
|
|
|
67
68
|
getContextActiveIgnorePatterns(codebasePath: string): string[];
|
|
68
69
|
getContextIndexedExtensions(codebasePath: string): string[];
|
|
69
70
|
canonicalizeCodebasePath(codebasePath: string): string;
|
|
70
|
-
writeIndexCompletionMarker(codebasePath: string, marker: IndexCompletionMarkerDocument): Promise<void>;
|
|
71
71
|
pruneIndexedCollectionFamily(codebasePath: string, keepCollectionName: string): Promise<string[]>;
|
|
72
|
+
pruneUnprovenStagedCollectionFamily(codebasePath: string): Promise<string[]>;
|
|
72
73
|
getContextTrackedRelativePaths(codebasePath: string): string[];
|
|
73
74
|
setIndexingStats(stats: {
|
|
74
75
|
indexedFiles: number;
|
|
@@ -82,8 +83,11 @@ type ManageIndexingHandlersHost = {
|
|
|
82
83
|
export declare class ManageIndexingHandlers {
|
|
83
84
|
private readonly host;
|
|
84
85
|
constructor(host: ManageIndexingHandlersHost);
|
|
86
|
+
private isStagedCollectionName;
|
|
87
|
+
private cleanupFailedStagedCollection;
|
|
85
88
|
handleIndexCodebase(args: IndexCodebaseArgs): Promise<ToolTextResponse>;
|
|
86
89
|
handleReindexCodebase(args: ReindexCodebaseArgs): Promise<ToolTextResponse>;
|
|
90
|
+
handleRepairIndex(args: Record<string, unknown>): Promise<ToolTextResponse>;
|
|
87
91
|
private startBackgroundIndexing;
|
|
88
92
|
}
|
|
89
93
|
export {};
|
|
@@ -1,8 +1,24 @@
|
|
|
1
1
|
import * as fs from "fs";
|
|
2
2
|
import * as crypto from "node:crypto";
|
|
3
|
-
import { COLLECTION_LIMIT_MESSAGE, RemoteCollectionDeletePendingError, } from "@zokizuan/satori-core";
|
|
3
|
+
import { COLLECTION_LIMIT_MESSAGE, deleteCollectionWithVerification, RemoteCollectionDeletePendingError, } from "@zokizuan/satori-core";
|
|
4
4
|
import { classifyVectorBackendError, } from "./backend-diagnostics.js";
|
|
5
5
|
import { ensureAbsolutePath, trackCodebasePath } from "../utils.js";
|
|
6
|
+
function isMatchingVerifiedFingerprint(info, runtimeFingerprint) {
|
|
7
|
+
if (info?.fingerprintSource !== "verified") {
|
|
8
|
+
return undefined;
|
|
9
|
+
}
|
|
10
|
+
const fingerprint = info.indexFingerprint;
|
|
11
|
+
if (!fingerprint || typeof fingerprint !== "object") {
|
|
12
|
+
return undefined;
|
|
13
|
+
}
|
|
14
|
+
const record = fingerprint;
|
|
15
|
+
const matches = record.embeddingProvider === runtimeFingerprint.embeddingProvider
|
|
16
|
+
&& record.embeddingModel === runtimeFingerprint.embeddingModel
|
|
17
|
+
&& Number(record.embeddingDimension) === Number(runtimeFingerprint.embeddingDimension)
|
|
18
|
+
&& record.vectorStoreProvider === runtimeFingerprint.vectorStoreProvider
|
|
19
|
+
&& record.schemaVersion === runtimeFingerprint.schemaVersion;
|
|
20
|
+
return matches ? fingerprint : undefined;
|
|
21
|
+
}
|
|
6
22
|
const COLLECTION_LIMIT_PATTERNS = [
|
|
7
23
|
/exceeded the limit number of collections/i,
|
|
8
24
|
/collection limit/i,
|
|
@@ -96,6 +112,21 @@ export class ManageIndexingHandlers {
|
|
|
96
112
|
constructor(host) {
|
|
97
113
|
this.host = host;
|
|
98
114
|
}
|
|
115
|
+
isStagedCollectionName(collectionName) {
|
|
116
|
+
return typeof collectionName === "string" && collectionName.includes("__gen_");
|
|
117
|
+
}
|
|
118
|
+
async cleanupFailedStagedCollection(codebasePath, collectionName) {
|
|
119
|
+
if (!this.isStagedCollectionName(collectionName)) {
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
try {
|
|
123
|
+
await deleteCollectionWithVerification(this.host.context.getVectorStore(), collectionName);
|
|
124
|
+
console.log(`[BACKGROUND-INDEX] Cleaned failed staged collection '${collectionName}' for '${codebasePath}'.`);
|
|
125
|
+
}
|
|
126
|
+
catch (cleanupError) {
|
|
127
|
+
console.warn(`[BACKGROUND-INDEX] Failed to clean staged collection '${collectionName}' after indexing failure for '${codebasePath}': ${formatUnknownError(cleanupError)}`);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
99
130
|
async handleIndexCodebase(args) {
|
|
100
131
|
const { path: codebasePath, force, customExtensions, ignorePatterns, zillizDropCollection } = args;
|
|
101
132
|
const forceReindex = force || false;
|
|
@@ -149,7 +180,7 @@ export class ManageIndexingHandlers {
|
|
|
149
180
|
const isIndexedInSnapshot = this.host.getSnapshotIndexedCodebases().includes(absolutePath);
|
|
150
181
|
if (!forceReindex && !isIndexedInSnapshot) {
|
|
151
182
|
const proof = await this.host.validateCompletionProof(absolutePath);
|
|
152
|
-
if (this.host.recoverIndexedSnapshotFromCompletionProof(absolutePath, proof)) {
|
|
183
|
+
if (await this.host.recoverIndexedSnapshotFromCompletionProof(absolutePath, proof)) {
|
|
153
184
|
if (proof.outcome === "fingerprint_mismatch") {
|
|
154
185
|
return this.host.manageResponse(manageAction, absolutePath, "requires_reindex", this.host.buildReindexInstruction(absolutePath, "Recovered local readiness from completion marker proof, but the current runtime fingerprint does not match the existing index."), {
|
|
155
186
|
...preflightOptions,
|
|
@@ -200,6 +231,10 @@ export class ManageIndexingHandlers {
|
|
|
200
231
|
}
|
|
201
232
|
const stagedCollectionName = this.host.resolveStagedCollectionName(absolutePath, `run_${crypto.randomUUID()}`);
|
|
202
233
|
try {
|
|
234
|
+
const prunedStagedCollections = await this.host.pruneUnprovenStagedCollectionFamily(absolutePath);
|
|
235
|
+
if (prunedStagedCollections.length > 0) {
|
|
236
|
+
console.log(`[INDEX-VALIDATION] 🧹 Removed ${prunedStagedCollections.length} unproven staged collection(s): ${prunedStagedCollections.join(", ")}`);
|
|
237
|
+
}
|
|
203
238
|
console.log("[INDEX-VALIDATION] 🔍 Validating collection creation capability");
|
|
204
239
|
const canCreateCollection = await this.host.context.getVectorStore().checkCollectionLimit();
|
|
205
240
|
if (!canCreateCollection) {
|
|
@@ -332,12 +367,89 @@ export class ManageIndexingHandlers {
|
|
|
332
367
|
__reindexPreflight: forwardedPreflight,
|
|
333
368
|
});
|
|
334
369
|
}
|
|
370
|
+
async handleRepairIndex(args) {
|
|
371
|
+
const codebasePath = args.path;
|
|
372
|
+
if (typeof codebasePath !== "string" || codebasePath.trim().length === 0) {
|
|
373
|
+
return this.host.manageResponse("repair", "", "error", "Error: Path is required.");
|
|
374
|
+
}
|
|
375
|
+
let absolutePath = codebasePath;
|
|
376
|
+
try {
|
|
377
|
+
absolutePath = ensureAbsolutePath(codebasePath);
|
|
378
|
+
if (!fs.existsSync(absolutePath)) {
|
|
379
|
+
return this.host.manageResponse("repair", absolutePath, "error", `Error: Path '${absolutePath}' does not exist. Original input: '${codebasePath}'`);
|
|
380
|
+
}
|
|
381
|
+
const stat = fs.statSync(absolutePath);
|
|
382
|
+
if (!stat.isDirectory()) {
|
|
383
|
+
return this.host.manageResponse("repair", absolutePath, "error", `Error: Path '${absolutePath}' is not a directory`);
|
|
384
|
+
}
|
|
385
|
+
const runtimeOwnerConflict = await this.host.buildRuntimeOwnerConflictResponseIfBlocked("repair", absolutePath);
|
|
386
|
+
if (runtimeOwnerConflict) {
|
|
387
|
+
return runtimeOwnerConflict;
|
|
388
|
+
}
|
|
389
|
+
if (this.host.getSnapshotIndexingCodebases().includes(absolutePath)) {
|
|
390
|
+
return this.host.manageResponse("repair", absolutePath, "not_ready", this.host.buildManageActionBlockedMessage(absolutePath, "repair"), {
|
|
391
|
+
reason: "indexing",
|
|
392
|
+
hints: {
|
|
393
|
+
status: this.host.buildStatusHint(absolutePath),
|
|
394
|
+
retryAfterMs: this.host.getManageRetryAfterMs(),
|
|
395
|
+
indexing: this.host.buildIndexingMetadata(absolutePath),
|
|
396
|
+
},
|
|
397
|
+
});
|
|
398
|
+
}
|
|
399
|
+
const trustedFingerprint = isMatchingVerifiedFingerprint(this.host.getSnapshotCodebaseInfo(absolutePath), this.host.runtimeFingerprint);
|
|
400
|
+
const result = await this.host.context.repairIndex(absolutePath, { trustedFingerprint });
|
|
401
|
+
if (result.status === "ok") {
|
|
402
|
+
await this.host.rebuildCallGraphForIndex(absolutePath);
|
|
403
|
+
await this.host.touchWatchedCodebase(absolutePath);
|
|
404
|
+
const stats = {
|
|
405
|
+
indexedFiles: result.indexedFiles || 0,
|
|
406
|
+
totalChunks: result.totalChunks || 0,
|
|
407
|
+
status: "completed"
|
|
408
|
+
};
|
|
409
|
+
this.host.snapshotManager.setCodebaseIndexed(absolutePath, stats, this.host.runtimeFingerprint, "verified", result.collectionName);
|
|
410
|
+
const trackedRelativePaths = result.trackedRelativePaths;
|
|
411
|
+
this.host.snapshotManager.setCodebaseIndexManifest(absolutePath, Array.isArray(trackedRelativePaths)
|
|
412
|
+
? trackedRelativePaths.filter((entry) => typeof entry === "string")
|
|
413
|
+
: this.host.getContextTrackedRelativePaths(absolutePath));
|
|
414
|
+
this.host.setIndexingStats(stats);
|
|
415
|
+
this.host.saveSnapshotIfSupported();
|
|
416
|
+
const warningsArray = Array.isArray(result.warnings) ? result.warnings : [];
|
|
417
|
+
return this.host.manageResponse("repair", absolutePath, "ok", result.message, {
|
|
418
|
+
warnings: warningsArray,
|
|
419
|
+
});
|
|
420
|
+
}
|
|
421
|
+
else if (result.status === "requires_reindex") {
|
|
422
|
+
return this.host.manageResponse("repair", absolutePath, "requires_reindex", result.message, {
|
|
423
|
+
reason: "requires_reindex",
|
|
424
|
+
hints: this.host.buildManageRequiresReindexHints(absolutePath),
|
|
425
|
+
});
|
|
426
|
+
}
|
|
427
|
+
else {
|
|
428
|
+
return this.host.manageResponse("repair", absolutePath, "blocked", result.message, {
|
|
429
|
+
reason: result.reason || "needs_create",
|
|
430
|
+
hints: {
|
|
431
|
+
create: this.host.buildCreateHint(absolutePath),
|
|
432
|
+
missingCount: result.missingCount,
|
|
433
|
+
}
|
|
434
|
+
});
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
catch (error) {
|
|
438
|
+
console.error("Error in handleRepairIndex:", error);
|
|
439
|
+
const vectorBackendDiagnostic = classifyVectorBackendError(error);
|
|
440
|
+
if (vectorBackendDiagnostic) {
|
|
441
|
+
return this.host.manageVectorBackendResponse("repair", absolutePath, vectorBackendDiagnostic);
|
|
442
|
+
}
|
|
443
|
+
return this.host.manageResponse("repair", absolutePath, "error", `Error performing repair: ${formatUnknownError(error)}`);
|
|
444
|
+
}
|
|
445
|
+
}
|
|
335
446
|
async startBackgroundIndexing(codebasePath, forceReindex, writeCollectionName) {
|
|
336
447
|
const absolutePath = codebasePath;
|
|
337
448
|
let lastSaveTime = 0;
|
|
449
|
+
let targetCollectionName;
|
|
338
450
|
try {
|
|
339
451
|
console.log(`[BACKGROUND-INDEX] Starting background indexing for: ${absolutePath}`);
|
|
340
|
-
|
|
452
|
+
targetCollectionName = typeof writeCollectionName === "string" && writeCollectionName.trim().length > 0
|
|
341
453
|
? writeCollectionName
|
|
342
454
|
: this.host.resolveCollectionName(absolutePath);
|
|
343
455
|
this.host.setWriteCollectionOverride(absolutePath, targetCollectionName);
|
|
@@ -370,15 +482,6 @@ export class ManageIndexingHandlers {
|
|
|
370
482
|
console.log(`[BACKGROUND-INDEX] Progress: ${progress.phase} - ${progress.percentage}% (${progress.current}/${progress.total})`);
|
|
371
483
|
});
|
|
372
484
|
console.log(`[BACKGROUND-INDEX] ✅ Indexing completed successfully! Files: ${stats.indexedFiles}, Chunks: ${stats.totalChunks}`);
|
|
373
|
-
await this.host.writeIndexCompletionMarker(absolutePath, {
|
|
374
|
-
kind: "satori_index_completion_v1",
|
|
375
|
-
codebasePath: this.host.canonicalizeCodebasePath(absolutePath),
|
|
376
|
-
fingerprint: this.host.runtimeFingerprint,
|
|
377
|
-
indexedFiles: stats.indexedFiles,
|
|
378
|
-
totalChunks: stats.totalChunks,
|
|
379
|
-
completedAt: new Date().toISOString(),
|
|
380
|
-
runId: `run_${crypto.randomUUID()}`,
|
|
381
|
-
});
|
|
382
485
|
try {
|
|
383
486
|
const droppedCollections = await this.host.pruneIndexedCollectionFamily(absolutePath, targetCollectionName);
|
|
384
487
|
if (droppedCollections.length > 0) {
|
|
@@ -388,7 +491,7 @@ export class ManageIndexingHandlers {
|
|
|
388
491
|
catch (pruneError) {
|
|
389
492
|
console.warn(`[BACKGROUND-INDEX] Failed to retire superseded generations for '${absolutePath}': ${formatUnknownError(pruneError)}`);
|
|
390
493
|
}
|
|
391
|
-
this.host.snapshotManager.setCodebaseIndexed(absolutePath, stats, this.host.runtimeFingerprint, "verified");
|
|
494
|
+
this.host.snapshotManager.setCodebaseIndexed(absolutePath, stats, this.host.runtimeFingerprint, "verified", targetCollectionName);
|
|
392
495
|
this.host.snapshotManager.setCodebaseIndexManifest(absolutePath, this.host.getContextTrackedRelativePaths(absolutePath));
|
|
393
496
|
this.host.setIndexingStats({ indexedFiles: stats.indexedFiles, totalChunks: stats.totalChunks });
|
|
394
497
|
await this.host.syncManager.recordCurrentIgnoreControlSignature(absolutePath);
|
|
@@ -413,6 +516,7 @@ export class ManageIndexingHandlers {
|
|
|
413
516
|
catch (clearError) {
|
|
414
517
|
console.warn(`[BACKGROUND-INDEX] Failed to clear completion marker after indexing error for '${absolutePath}': ${formatUnknownError(clearError)}`);
|
|
415
518
|
}
|
|
519
|
+
await this.cleanupFailedStagedCollection(absolutePath, targetCollectionName);
|
|
416
520
|
this.host.snapshotManager.setCodebaseIndexFailed(absolutePath, errorMessage, this.host.getSnapshotIndexingProgress(absolutePath));
|
|
417
521
|
this.host.saveSnapshotIfSupported();
|
|
418
522
|
console.error(`[BACKGROUND-INDEX] Indexing failed for ${absolutePath}: ${errorMessage}`);
|
|
@@ -40,8 +40,10 @@ type ManageMaintenanceHandlersHost = {
|
|
|
40
40
|
buildReindexInstruction(codebasePath: string, detail?: string): string;
|
|
41
41
|
buildCompatibilityStatusLines(codebasePath: string): string;
|
|
42
42
|
buildManageRequiresReindexHints(codebasePath: string): Record<string, unknown>;
|
|
43
|
+
buildSyncHint(codebasePath: string): Record<string, unknown>;
|
|
43
44
|
buildStaleLocalHint(codebasePath: string, reason: string): Record<string, unknown>;
|
|
44
45
|
buildStaleLocalMessage(codebasePath: string, requestedPath: string, reason: string): string;
|
|
46
|
+
canSyncStaleLocal(codebasePath: string, reason: string): boolean;
|
|
45
47
|
enforceFingerprintGate(codebasePath: string): {
|
|
46
48
|
blockedResponse?: ToolTextResponse;
|
|
47
49
|
message?: string;
|
|
@@ -208,11 +208,15 @@ export class ManageMaintenanceHandlers {
|
|
|
208
208
|
envelopePath = trackedRootState.codebasePath;
|
|
209
209
|
envelopeStatus = "not_indexed";
|
|
210
210
|
envelopeReason = "not_indexed";
|
|
211
|
+
const syncable = this.host.canSyncStaleLocal(trackedRootState.codebasePath, trackedRootState.reason);
|
|
211
212
|
envelopeHints = {
|
|
213
|
+
...(syncable ? { sync: this.host.buildSyncHint(trackedRootState.codebasePath) } : {}),
|
|
212
214
|
create: this.host.buildCreateHint(trackedRootState.codebasePath),
|
|
213
215
|
staleLocal: this.host.buildStaleLocalHint(trackedRootState.codebasePath, trackedRootState.reason),
|
|
214
216
|
};
|
|
215
|
-
|
|
217
|
+
const nextAction = syncable ? "sync" : trackedRootState.reason === "missing_marker_doc" ? "repair" : "create";
|
|
218
|
+
const nextVerb = syncable ? "sync it" : nextAction === "repair" ? "repair it" : "create it";
|
|
219
|
+
statusMessage = `❌ ${this.host.buildStaleLocalMessage(trackedRootState.codebasePath, absolutePath, trackedRootState.reason)} Run manage_index with {"action":"${nextAction}","path":"${trackedRootState.codebasePath}"} to ${nextVerb}.`;
|
|
216
220
|
}
|
|
217
221
|
else if (trackedRootState.state === "missing_collection") {
|
|
218
222
|
envelopePath = trackedRootState.codebasePath;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { WarningCode } from "./warnings.js";
|
|
2
|
-
export type ManageIndexAction = "create" | "reindex" | "sync" | "status" | "clear";
|
|
2
|
+
export type ManageIndexAction = "create" | "reindex" | "sync" | "status" | "clear" | "repair";
|
|
3
3
|
export type ManageIndexStatus = "ok" | "not_ready" | "not_indexed" | "requires_reindex" | "blocked" | "error";
|
|
4
|
-
export type ManageIndexReason = "indexing" | "not_indexed" | "requires_reindex" | "unnecessary_reindex_ignore_only" | "preflight_unknown" | "backend_timeout" | "remote_delete_pending" | "missing_provider_config" | "vector_backend_unavailable" | "runtime_owner_conflict";
|
|
4
|
+
export type ManageIndexReason = "indexing" | "not_indexed" | "requires_reindex" | "unnecessary_reindex_ignore_only" | "preflight_unknown" | "backend_timeout" | "remote_delete_pending" | "missing_provider_config" | "vector_backend_unavailable" | "runtime_owner_conflict" | "needs_create";
|
|
5
5
|
export type VectorBackendResponseCode = "ZILLIZ_CLUSTER_STOPPED" | "VECTOR_BACKEND_AUTH_FAILED" | "VECTOR_BACKEND_UNREACHABLE" | "VECTOR_BACKEND_TIMEOUT" | "VECTOR_BACKEND_CONNECTION_CLOSED";
|
|
6
6
|
export type ManageReindexPreflightOutcome = "reindex_required" | "reindex_unnecessary_ignore_only" | "unknown" | "probe_failed";
|
|
7
7
|
export interface ManageIndexToolHint {
|
|
@@ -31,7 +31,7 @@ export type SearchFrontDoorHost = {
|
|
|
31
31
|
buildRequiresReindexPayload: (codebasePath: string, detail: string | undefined, searchContext: SearchFrontDoorSearchContext) => SearchResponseEnvelope;
|
|
32
32
|
buildNotReadySearchPayload: (codebasePath: string, searchContext: SearchFrontDoorSearchContext) => SearchResponseEnvelope;
|
|
33
33
|
buildFreshnessBlockedSearchPayload: (codebasePath: string, freshnessDecision: FreshnessDecision, searchContext: SearchFrontDoorSearchContext) => SearchResponseEnvelope | null;
|
|
34
|
-
buildManageIndexRecommendedAction: (action: "create" | "reindex" | "sync" | "status", codebasePath: string, rationale: string) => SearchRecommendedNextAction;
|
|
34
|
+
buildManageIndexRecommendedAction: (action: "create" | "reindex" | "sync" | "status" | "repair", codebasePath: string, rationale: string) => SearchRecommendedNextAction;
|
|
35
35
|
buildCreateHint: (codebasePath: string) => {
|
|
36
36
|
tool: string;
|
|
37
37
|
args: {
|
|
@@ -39,8 +39,23 @@ export type SearchFrontDoorHost = {
|
|
|
39
39
|
path: string;
|
|
40
40
|
};
|
|
41
41
|
};
|
|
42
|
+
buildSyncHint: (codebasePath: string) => {
|
|
43
|
+
tool: string;
|
|
44
|
+
args: {
|
|
45
|
+
action: string;
|
|
46
|
+
path: string;
|
|
47
|
+
};
|
|
48
|
+
};
|
|
49
|
+
buildRepairHint: (codebasePath: string) => {
|
|
50
|
+
tool: string;
|
|
51
|
+
args: {
|
|
52
|
+
action: string;
|
|
53
|
+
path: string;
|
|
54
|
+
};
|
|
55
|
+
};
|
|
42
56
|
buildStaleLocalHint: (codebasePath: string, reason: CompletionProofReason) => Record<string, unknown>;
|
|
43
57
|
buildStaleLocalMessage: (codebasePath: string, requestedPath: string, reason: CompletionProofReason) => string;
|
|
58
|
+
canSyncStaleLocal: (codebasePath: string, reason: CompletionProofReason) => boolean;
|
|
44
59
|
withProofDebugHint: <T extends object>(payload: T, proofDebugHint?: CompletionProbeDebugHint) => T;
|
|
45
60
|
isPartialIndexNavigationUnavailable: (info: unknown) => boolean;
|
|
46
61
|
partialIndexWarnings: readonly string[];
|
|
@@ -48,6 +48,9 @@ function buildBlockedReadinessPayload(state, searchContext, host) {
|
|
|
48
48
|
};
|
|
49
49
|
}
|
|
50
50
|
if (state.state === "stale_local") {
|
|
51
|
+
const preferSync = host.canSyncStaleLocal(state.codebasePath, state.reason);
|
|
52
|
+
const preferRepair = !preferSync && state.reason === "missing_marker_doc";
|
|
53
|
+
const action = preferSync ? "sync" : preferRepair ? "repair" : "create";
|
|
51
54
|
return {
|
|
52
55
|
status: "not_indexed",
|
|
53
56
|
reason: "not_indexed",
|
|
@@ -59,8 +62,14 @@ function buildBlockedReadinessPayload(state, searchContext, host) {
|
|
|
59
62
|
resultMode: searchContext.resultMode,
|
|
60
63
|
freshnessDecision: null,
|
|
61
64
|
message: host.buildStaleLocalMessage(state.codebasePath, searchContext.path, state.reason),
|
|
62
|
-
recommendedNextAction: host.buildManageIndexRecommendedAction(
|
|
65
|
+
recommendedNextAction: host.buildManageIndexRecommendedAction(action, state.codebasePath, preferSync
|
|
66
|
+
? "Run incremental sync; the local snapshot proves the committed collection and can restore the missing completion marker."
|
|
67
|
+
: preferRepair
|
|
68
|
+
? "Repair local readiness because completion marker proof is missing and no syncable snapshot collection is available."
|
|
69
|
+
: "Create a fresh index because local readiness metadata is stale."),
|
|
63
70
|
hints: {
|
|
71
|
+
...(preferSync ? { sync: host.buildSyncHint(state.codebasePath) } : {}),
|
|
72
|
+
...(preferRepair ? { repair: host.buildRepairHint(state.codebasePath) } : {}),
|
|
64
73
|
create: host.buildCreateHint(state.codebasePath),
|
|
65
74
|
staleLocal: host.buildStaleLocalHint(state.codebasePath, state.reason),
|
|
66
75
|
},
|
|
@@ -89,20 +98,24 @@ export async function runSearchFrontDoor(input, host) {
|
|
|
89
98
|
}
|
|
90
99
|
trackCodebasePath(absolutePath);
|
|
91
100
|
const trackedRootState = await host.prepareInitialTrackedRootRead(absolutePath);
|
|
92
|
-
const
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
101
|
+
const canSyncInitialStaleLocal = trackedRootState.state === "stale_local"
|
|
102
|
+
&& host.canSyncStaleLocal(trackedRootState.codebasePath, trackedRootState.reason);
|
|
103
|
+
if (!canSyncInitialStaleLocal) {
|
|
104
|
+
const blockedReadinessPayload = buildBlockedReadinessPayload(trackedRootState, searchContext, host);
|
|
105
|
+
if (blockedReadinessPayload) {
|
|
106
|
+
return {
|
|
107
|
+
kind: "blocked",
|
|
108
|
+
payload: blockedReadinessPayload,
|
|
109
|
+
};
|
|
110
|
+
}
|
|
98
111
|
}
|
|
99
|
-
if (trackedRootState.state !== "ready") {
|
|
112
|
+
if (trackedRootState.state !== "ready" && !canSyncInitialStaleLocal) {
|
|
100
113
|
throw new Error(`Unexpected non-ready tracked root state after readiness gating: ${trackedRootState.state}`);
|
|
101
114
|
}
|
|
102
|
-
let searchableRoot = trackedRootState.root;
|
|
103
|
-
let effectiveRoot = searchableRoot
|
|
104
|
-
let proofDebugHint = trackedRootState.proofDebugHint;
|
|
105
|
-
let partialIndexSearchWarnings = buildPartialIndexWarnings(host, searchableRoot);
|
|
115
|
+
let searchableRoot = trackedRootState.state === "ready" ? trackedRootState.root : null;
|
|
116
|
+
let effectiveRoot = searchableRoot?.path || (trackedRootState.state === "stale_local" ? trackedRootState.codebasePath : absolutePath);
|
|
117
|
+
let proofDebugHint = trackedRootState.state === "ready" ? trackedRootState.proofDebugHint : undefined;
|
|
118
|
+
let partialIndexSearchWarnings = searchableRoot ? buildPartialIndexWarnings(host, searchableRoot) : [];
|
|
106
119
|
const freshnessDecision = await host.ensureSearchFreshness(effectiveRoot);
|
|
107
120
|
host.noteFreshnessMode(freshnessDecision.mode);
|
|
108
121
|
const freshnessBlockedPayload = host.buildFreshnessBlockedSearchPayload(effectiveRoot, freshnessDecision, searchContext);
|
package/dist/core/snapshot.d.ts
CHANGED
|
@@ -83,6 +83,7 @@ export declare class SnapshotManager {
|
|
|
83
83
|
getIndexingProgress(codebasePath: string): number | undefined;
|
|
84
84
|
getCodebaseStatus(codebasePath: string): CodebaseInfo['status'] | 'not_found';
|
|
85
85
|
getCodebaseInfo(codebasePath: string): CodebaseInfo | undefined;
|
|
86
|
+
getCodebaseCollectionName(codebasePath: string): string | undefined;
|
|
86
87
|
getAllCodebases(): Array<{
|
|
87
88
|
path: string;
|
|
88
89
|
info: CodebaseInfo;
|
|
@@ -97,13 +98,13 @@ export declare class SnapshotManager {
|
|
|
97
98
|
indexedFiles: number;
|
|
98
99
|
totalChunks: number;
|
|
99
100
|
status: 'completed' | 'limit_reached';
|
|
100
|
-
}, indexFingerprint?: IndexFingerprint, fingerprintSource?: FingerprintSource): void;
|
|
101
|
+
}, indexFingerprint?: IndexFingerprint, fingerprintSource?: FingerprintSource, collectionName?: string): void;
|
|
101
102
|
setCodebaseIndexFailed(codebasePath: string, errorMessage: string, lastAttemptedPercentage?: number): void;
|
|
102
103
|
setCodebaseSyncCompleted(codebasePath: string, stats: {
|
|
103
104
|
added: number;
|
|
104
105
|
removed: number;
|
|
105
106
|
modified: number;
|
|
106
|
-
}, indexFingerprint?: IndexFingerprint, fingerprintSource?: FingerprintSource): void;
|
|
107
|
+
}, indexFingerprint?: IndexFingerprint, fingerprintSource?: FingerprintSource, collectionName?: string): void;
|
|
107
108
|
setCodebaseRequiresReindex(codebasePath: string, reason: AccessGateReason, message?: string): void;
|
|
108
109
|
private recoverCodebaseFromResolvedFingerprintMismatch;
|
|
109
110
|
setCodebaseCallGraphSidecar(codebasePath: string, sidecar: CallGraphSidecarInfo): void;
|
package/dist/core/snapshot.js
CHANGED
|
@@ -384,6 +384,9 @@ export class SnapshotManager {
|
|
|
384
384
|
if (rawInfo.indexFingerprint !== undefined && !this.isValidIndexFingerprint(rawInfo.indexFingerprint)) {
|
|
385
385
|
return false;
|
|
386
386
|
}
|
|
387
|
+
if (rawInfo.collectionName !== undefined && typeof rawInfo.collectionName !== "string") {
|
|
388
|
+
return false;
|
|
389
|
+
}
|
|
387
390
|
if (rawInfo.fingerprintSource !== undefined && rawInfo.fingerprintSource !== "verified" && rawInfo.fingerprintSource !== "assumed_v2") {
|
|
388
391
|
return false;
|
|
389
392
|
}
|
|
@@ -802,6 +805,12 @@ export class SnapshotManager {
|
|
|
802
805
|
getCodebaseInfo(codebasePath) {
|
|
803
806
|
return this.codebaseInfoMap.get(codebasePath);
|
|
804
807
|
}
|
|
808
|
+
getCodebaseCollectionName(codebasePath) {
|
|
809
|
+
const collectionName = this.codebaseInfoMap.get(codebasePath)?.collectionName;
|
|
810
|
+
return typeof collectionName === "string" && collectionName.trim().length > 0
|
|
811
|
+
? collectionName.trim()
|
|
812
|
+
: undefined;
|
|
813
|
+
}
|
|
805
814
|
getAllCodebases() {
|
|
806
815
|
return Array.from(this.codebaseInfoMap.entries()).map(([p, info]) => ({ path: p, info }));
|
|
807
816
|
}
|
|
@@ -849,6 +858,7 @@ export class SnapshotManager {
|
|
|
849
858
|
indexFingerprint: existing?.indexFingerprint,
|
|
850
859
|
fingerprintSource: existing?.fingerprintSource,
|
|
851
860
|
reindexReason: existing?.reindexReason,
|
|
861
|
+
collectionName: existing?.collectionName,
|
|
852
862
|
callGraphSidecar: existing?.callGraphSidecar,
|
|
853
863
|
indexManifest: existing?.indexManifest,
|
|
854
864
|
ignoreRulesVersion: existing?.ignoreRulesVersion,
|
|
@@ -858,15 +868,19 @@ export class SnapshotManager {
|
|
|
858
868
|
this.markDirty();
|
|
859
869
|
this.refreshDerivedState();
|
|
860
870
|
}
|
|
861
|
-
setCodebaseIndexed(codebasePath, stats, indexFingerprint, fingerprintSource = 'verified') {
|
|
871
|
+
setCodebaseIndexed(codebasePath, stats, indexFingerprint, fingerprintSource = 'verified', collectionName) {
|
|
862
872
|
this.markCodebasePresent(codebasePath);
|
|
863
873
|
const existing = this.codebaseInfoMap.get(codebasePath);
|
|
874
|
+
const resolvedCollectionName = typeof collectionName === "string" && collectionName.trim().length > 0
|
|
875
|
+
? collectionName.trim()
|
|
876
|
+
: existing?.collectionName;
|
|
864
877
|
const info = {
|
|
865
878
|
status: 'indexed',
|
|
866
879
|
indexedFiles: stats.indexedFiles,
|
|
867
880
|
totalChunks: stats.totalChunks,
|
|
868
881
|
indexStatus: stats.status,
|
|
869
882
|
lastUpdated: new Date().toISOString(),
|
|
883
|
+
collectionName: resolvedCollectionName,
|
|
870
884
|
indexFingerprint: indexFingerprint || this.runtimeFingerprint,
|
|
871
885
|
fingerprintSource,
|
|
872
886
|
callGraphSidecar: existing?.callGraphSidecar,
|
|
@@ -886,6 +900,7 @@ export class SnapshotManager {
|
|
|
886
900
|
errorMessage,
|
|
887
901
|
lastAttemptedPercentage,
|
|
888
902
|
lastUpdated: new Date().toISOString(),
|
|
903
|
+
collectionName: existing?.collectionName,
|
|
889
904
|
indexFingerprint: existing?.indexFingerprint,
|
|
890
905
|
fingerprintSource: existing?.fingerprintSource,
|
|
891
906
|
callGraphSidecar: existing?.callGraphSidecar,
|
|
@@ -897,10 +912,13 @@ export class SnapshotManager {
|
|
|
897
912
|
this.markDirty();
|
|
898
913
|
this.refreshDerivedState();
|
|
899
914
|
}
|
|
900
|
-
setCodebaseSyncCompleted(codebasePath, stats, indexFingerprint, fingerprintSource = 'verified') {
|
|
915
|
+
setCodebaseSyncCompleted(codebasePath, stats, indexFingerprint, fingerprintSource = 'verified', collectionName) {
|
|
901
916
|
this.markCodebasePresent(codebasePath);
|
|
902
917
|
const totalChanges = stats.added + stats.removed + stats.modified;
|
|
903
918
|
const existing = this.codebaseInfoMap.get(codebasePath);
|
|
919
|
+
const resolvedCollectionName = typeof collectionName === "string" && collectionName.trim().length > 0
|
|
920
|
+
? collectionName.trim()
|
|
921
|
+
: existing?.collectionName;
|
|
904
922
|
const info = {
|
|
905
923
|
status: 'sync_completed',
|
|
906
924
|
added: stats.added,
|
|
@@ -908,6 +926,7 @@ export class SnapshotManager {
|
|
|
908
926
|
modified: stats.modified,
|
|
909
927
|
totalChanges,
|
|
910
928
|
lastUpdated: new Date().toISOString(),
|
|
929
|
+
collectionName: resolvedCollectionName,
|
|
911
930
|
indexFingerprint: indexFingerprint || existing?.indexFingerprint || this.runtimeFingerprint,
|
|
912
931
|
fingerprintSource,
|
|
913
932
|
callGraphSidecar: existing?.callGraphSidecar,
|
|
@@ -927,6 +946,7 @@ export class SnapshotManager {
|
|
|
927
946
|
message: message || 'Index is incompatible with the current runtime fingerprint and must be rebuilt.',
|
|
928
947
|
reindexReason: reason,
|
|
929
948
|
lastUpdated: new Date().toISOString(),
|
|
949
|
+
collectionName: existing?.collectionName,
|
|
930
950
|
indexFingerprint: existing?.indexFingerprint,
|
|
931
951
|
fingerprintSource: existing?.fingerprintSource,
|
|
932
952
|
callGraphSidecar: existing?.callGraphSidecar,
|
|
@@ -946,6 +966,7 @@ export class SnapshotManager {
|
|
|
946
966
|
modified: 0,
|
|
947
967
|
totalChanges: 0,
|
|
948
968
|
lastUpdated: new Date().toISOString(),
|
|
969
|
+
collectionName: info.collectionName,
|
|
949
970
|
indexFingerprint: info.indexFingerprint,
|
|
950
971
|
fingerprintSource: info.fingerprintSource,
|
|
951
972
|
callGraphSidecar: info.callGraphSidecar,
|
package/dist/core/sync.d.ts
CHANGED
package/dist/core/sync.js
CHANGED
|
@@ -295,7 +295,12 @@ export class SyncManager {
|
|
|
295
295
|
}
|
|
296
296
|
try {
|
|
297
297
|
// Incremental sync
|
|
298
|
-
const
|
|
298
|
+
const collectionName = this.snapshotManager.getCodebaseCollectionName?.(codebasePath);
|
|
299
|
+
const syncOptions = {
|
|
300
|
+
...(collectionName ? { targetCollectionName: collectionName } : {}),
|
|
301
|
+
maintainCompletionMarker: true,
|
|
302
|
+
};
|
|
303
|
+
const stats = await this.context.reindexByChange(codebasePath, undefined, syncOptions);
|
|
299
304
|
if (typeof this.context.getTrackedRelativePaths === 'function') {
|
|
300
305
|
const trackedPaths = this.context.getTrackedRelativePaths(codebasePath);
|
|
301
306
|
if (typeof this.snapshotManager.setCodebaseIndexManifest === 'function') {
|
|
@@ -310,7 +315,7 @@ export class SyncManager {
|
|
|
310
315
|
return { mode: 'skipped_requires_reindex', stats };
|
|
311
316
|
}
|
|
312
317
|
// Persist Snapshot
|
|
313
|
-
this.snapshotManager.setCodebaseSyncCompleted(codebasePath, stats);
|
|
318
|
+
this.snapshotManager.setCodebaseSyncCompleted(codebasePath, stats, undefined, 'verified', stats.collectionName || collectionName);
|
|
314
319
|
this.snapshotManager.saveCodebaseSnapshot();
|
|
315
320
|
if (this.onSyncCompleted) {
|
|
316
321
|
await this.onSyncCompleted(codebasePath, {
|
|
@@ -23,7 +23,7 @@ type CallGraphContext = {
|
|
|
23
23
|
limit: number;
|
|
24
24
|
};
|
|
25
25
|
export type ToolResponseBuildersHost = {
|
|
26
|
-
buildManageIndexRecommendedAction(action: "create" | "reindex" | "sync" | "status", codebasePath: string, rationale: string): SearchRecommendedNextAction;
|
|
26
|
+
buildManageIndexRecommendedAction(action: "create" | "reindex" | "sync" | "status" | "repair", codebasePath: string, rationale: string): SearchRecommendedNextAction;
|
|
27
27
|
buildCreateHint(codebasePath: string): {
|
|
28
28
|
tool: string;
|
|
29
29
|
args: {
|
|
@@ -38,6 +38,13 @@ export type ToolResponseBuildersHost = {
|
|
|
38
38
|
path: string;
|
|
39
39
|
};
|
|
40
40
|
};
|
|
41
|
+
buildRepairHint(codebasePath: string): {
|
|
42
|
+
tool: string;
|
|
43
|
+
args: {
|
|
44
|
+
action: string;
|
|
45
|
+
path: string;
|
|
46
|
+
};
|
|
47
|
+
};
|
|
41
48
|
buildStatusHint(codebasePath: string): {
|
|
42
49
|
tool: string;
|
|
43
50
|
args: {
|
|
@@ -110,6 +110,7 @@ export class ToolResponseBuilders {
|
|
|
110
110
|
}
|
|
111
111
|
buildRequiresReindexCallGraphPayload(codebasePath, detail, context, reason = "requires_reindex") {
|
|
112
112
|
const detailLine = detail ? `${detail}\n\n` : "";
|
|
113
|
+
const preferRepair = reason === "missing_symbol_registry" || reason === "missing_relationship_sidecar";
|
|
113
114
|
return {
|
|
114
115
|
status: "requires_reindex",
|
|
115
116
|
supported: false,
|
|
@@ -126,8 +127,11 @@ export class ToolResponseBuilders {
|
|
|
126
127
|
freshnessDecision: {
|
|
127
128
|
mode: "skipped_requires_reindex",
|
|
128
129
|
},
|
|
129
|
-
message:
|
|
130
|
+
message: preferRepair
|
|
131
|
+
? `${detailLine}The index at '${codebasePath}' is missing navigation sidecars. Please run manage_index with {"action":"repair","path":"${codebasePath}"}.`
|
|
132
|
+
: `${detailLine}The index at '${codebasePath}' is incompatible with the current runtime and must be rebuilt. Please run manage_index with {"action":"reindex","path":"${codebasePath}"}.`,
|
|
130
133
|
hints: {
|
|
134
|
+
...(preferRepair ? { repair: this.host.buildRepairHint(codebasePath) } : {}),
|
|
131
135
|
reindex: this.host.buildReindexHint(codebasePath),
|
|
132
136
|
},
|
|
133
137
|
compatibility: this.host.buildCompatibilityDiagnostics(codebasePath),
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { COLLECTION_LIMIT_MESSAGE, deleteCollectionWithVerification, } from "@zokizuan/satori-core";
|
|
2
2
|
const SATORI_COLLECTION_PREFIXES = ["code_chunks_", "hybrid_code_chunks_"];
|
|
3
|
-
const ZILLIZ_FREE_TIER_COLLECTION_LIMIT = 5;
|
|
4
3
|
const MIN_RELIABLE_COLLECTION_CREATED_AT_MS = Date.UTC(2000, 0, 1);
|
|
5
4
|
function isRecord(value) {
|
|
6
5
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
@@ -185,7 +184,7 @@ export class VectorBackendMaintenance {
|
|
|
185
184
|
: "No Satori-managed collections were discovered.";
|
|
186
185
|
return `${COLLECTION_LIMIT_MESSAGE}
|
|
187
186
|
|
|
188
|
-
Reason:
|
|
187
|
+
Reason: The connected Zilliz cluster has no remaining collection slots.
|
|
189
188
|
Target codebase: '${targetCodebasePath}'
|
|
190
189
|
Target collection: '${targetCollectionName}'
|
|
191
190
|
|
|
@@ -153,7 +153,10 @@ class ContextMcpServer {
|
|
|
153
153
|
: null;
|
|
154
154
|
const decision = decideInterruptedIndexingRecovery(marker, this.runtimeFingerprint);
|
|
155
155
|
if (decision.action === "promote_indexed") {
|
|
156
|
-
toolContext.
|
|
156
|
+
const collectionName = typeof toolContext.context.getActiveIndexedCollectionName === "function"
|
|
157
|
+
? await toolContext.context.getActiveIndexedCollectionName(codebasePath) ?? undefined
|
|
158
|
+
: undefined;
|
|
159
|
+
toolContext.snapshotManager.setCodebaseIndexed(codebasePath, decision.stats, decision.indexFingerprint, "verified", collectionName);
|
|
157
160
|
promotedCount++;
|
|
158
161
|
const recoveryMode = decision.reason === "valid_marker_runtime_mismatch"
|
|
159
162
|
? "indexed (fingerprint recovered from marker; current runtime differs)"
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
import { formatZodError } from "./types.js";
|
|
3
3
|
import { classifyVectorBackendError, formatManageProviderConfigError, formatManageVectorBackendError, isMissingProviderConfigIssue } from "./setup-errors.js";
|
|
4
|
-
const actionEnum = z.enum(["create", "reindex", "sync", "status", "clear"]);
|
|
4
|
+
const actionEnum = z.enum(["create", "reindex", "sync", "status", "clear", "repair"]);
|
|
5
5
|
const manageIndexInputSchema = z.object({
|
|
6
6
|
action: actionEnum.describe("Required operation to run."),
|
|
7
7
|
path: z.string().min(1).describe("ABSOLUTE path to the target codebase."),
|
|
@@ -13,7 +13,7 @@ const manageIndexInputSchema = z.object({
|
|
|
13
13
|
});
|
|
14
14
|
export const manageIndexTool = {
|
|
15
15
|
name: "manage_index",
|
|
16
|
-
description: () => "Manage index lifecycle operations (create/reindex/sync/status/clear) for a codebase path. Ignore-rule edits in repo-root .satoriignore/.gitignore reconcile automatically in the normal sync path. Use action=\"sync\" for immediate convergence and action=\"reindex\" for full rebuild recovery (preflight may block unnecessary ignore-only reindex churn unless allowUnnecessaryReindex=true). create/reindex return the kickoff response immediately and do not poll to terminal state; use action=\"status\" to observe progress.",
|
|
16
|
+
description: () => "Manage index lifecycle operations (create/reindex/sync/status/clear/repair) for a codebase path. repair rebuilds local readiness only when existing vector payload and trusted runtime fingerprint proof match; otherwise it refuses and asks for create/reindex. Ignore-rule edits in repo-root .satoriignore/.gitignore reconcile automatically in the normal sync path. Use action=\"sync\" for immediate convergence and action=\"reindex\" for full rebuild recovery (preflight may block unnecessary ignore-only reindex churn unless allowUnnecessaryReindex=true). create/reindex return the kickoff response immediately and do not poll to terminal state; use action=\"status\" to observe progress.",
|
|
17
17
|
inputSchemaZod: () => manageIndexInputSchema,
|
|
18
18
|
execute: async (args, ctx) => {
|
|
19
19
|
const parsed = manageIndexInputSchema.safeParse(args || {});
|
|
@@ -29,7 +29,7 @@ export const manageIndexTool = {
|
|
|
29
29
|
const input = parsed.data;
|
|
30
30
|
const providerOperation = input.action === "clear" || input.action === "status"
|
|
31
31
|
? "vector_only"
|
|
32
|
-
: (input.action === "create" || input.action === "reindex" || input.action === "sync")
|
|
32
|
+
: (input.action === "create" || input.action === "reindex" || input.action === "sync" || input.action === "repair")
|
|
33
33
|
? "embedding_vector"
|
|
34
34
|
: null;
|
|
35
35
|
let executionContext;
|
|
@@ -76,6 +76,9 @@ export const manageIndexTool = {
|
|
|
76
76
|
case 'clear':
|
|
77
77
|
response = await executionContext.toolHandlers.handleClearIndex(input);
|
|
78
78
|
break;
|
|
79
|
+
case 'repair':
|
|
80
|
+
response = await executionContext.toolHandlers.handleRepairIndex(input);
|
|
81
|
+
break;
|
|
79
82
|
default:
|
|
80
83
|
return {
|
|
81
84
|
content: [{
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zokizuan/satori-mcp",
|
|
3
|
-
"version": "4.11.
|
|
3
|
+
"version": "4.11.13",
|
|
4
4
|
"description": "Repo-aware MCP tools for AI coding agents: intent search, symbol navigation, exact reads, related paths, and freshness warnings.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
"jsonc-parser": "^3.3.1",
|
|
16
16
|
"zod": "^3.25.55",
|
|
17
17
|
"zod-to-json-schema": "^3.25.1",
|
|
18
|
-
"@zokizuan/satori-core": "1.6.
|
|
18
|
+
"@zokizuan/satori-core": "1.6.9"
|
|
19
19
|
},
|
|
20
20
|
"devDependencies": {
|
|
21
21
|
"@types/node": "^20.0.0",
|