@zokizuan/satori-mcp 4.11.7 โ 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/cli/install.js +1 -0
- 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 +45 -239
- package/dist/core/handlers.js +894 -7710
- package/dist/core/manage-indexing-handlers.d.ts +94 -0
- package/dist/core/manage-indexing-handlers.js +529 -0
- package/dist/core/manage-maintenance-handlers.d.ts +63 -0
- package/dist/core/manage-maintenance-handlers.js +471 -0
- package/dist/core/manage-types.d.ts +2 -2
- package/dist/core/navigation-handlers.d.ts +138 -0
- package/dist/core/navigation-handlers.js +675 -0
- package/dist/core/python-call-fallback.d.ts +42 -0
- package/dist/core/python-call-fallback.js +383 -0
- package/dist/core/registry-file-outline.d.ts +26 -0
- package/dist/core/registry-file-outline.js +162 -0
- package/dist/core/relationship-backed-call-graph.d.ts +56 -0
- package/dist/core/relationship-backed-call-graph.js +322 -0
- package/dist/core/search-debug-helpers.d.ts +29 -0
- package/dist/core/search-debug-helpers.js +150 -0
- package/dist/core/search-exact-fast-path.d.ts +100 -0
- package/dist/core/search-exact-fast-path.js +163 -0
- package/dist/core/search-exact-registry-hit.d.ts +53 -0
- package/dist/core/search-exact-registry-hit.js +98 -0
- package/dist/core/search-execution.d.ts +144 -0
- package/dist/core/search-execution.js +404 -0
- package/dist/core/search-frontdoor.d.ts +65 -0
- package/dist/core/search-frontdoor.js +153 -0
- package/dist/core/search-group-ordering.d.ts +6 -0
- package/dist/core/search-group-ordering.js +96 -0
- package/dist/core/search-group-results.d.ts +141 -0
- package/dist/core/search-group-results.js +383 -0
- package/dist/core/search-grouping.d.ts +18 -0
- package/dist/core/search-grouping.js +69 -0
- package/dist/core/search-lexical-scoring.d.ts +39 -0
- package/dist/core/search-lexical-scoring.js +241 -0
- package/dist/core/search-navigation.d.ts +44 -0
- package/dist/core/search-navigation.js +196 -0
- package/dist/core/search-owner-resolution.d.ts +27 -0
- package/dist/core/search-owner-resolution.js +252 -0
- package/dist/core/search-query-planning.d.ts +13 -0
- package/dist/core/search-query-planning.js +320 -0
- package/dist/core/search-query-support.d.ts +123 -0
- package/dist/core/search-query-support.js +883 -0
- package/dist/core/search-ranking-policy.d.ts +64 -0
- package/dist/core/search-ranking-policy.js +342 -0
- package/dist/core/search-response-envelopes.d.ts +29 -0
- package/dist/core/search-response-envelopes.js +65 -0
- package/dist/core/search-response-helpers.d.ts +39 -0
- package/dist/core/search-response-helpers.js +446 -0
- package/dist/core/search-result-finalization.d.ts +96 -0
- package/dist/core/search-result-finalization.js +207 -0
- package/dist/core/search-types.d.ts +1 -1
- package/dist/core/snapshot.d.ts +11 -2
- package/dist/core/snapshot.js +71 -12
- package/dist/core/sync.d.ts +1 -0
- package/dist/core/sync.js +13 -4
- package/dist/core/tool-response-builders.d.ts +128 -0
- package/dist/core/tool-response-builders.js +399 -0
- package/dist/core/tracked-root-readiness.d.ts +132 -0
- package/dist/core/tracked-root-readiness.js +278 -0
- package/dist/core/vector-backend-maintenance.d.ts +37 -0
- package/dist/core/vector-backend-maintenance.js +246 -0
- package/dist/core/working-tree-state.d.ts +43 -0
- package/dist/core/working-tree-state.js +136 -0
- package/dist/server/bootstrap-stdio.js +1 -1
- package/dist/server/start-server.js +4 -1
- package/dist/tools/list_codebases.js +29 -1
- package/dist/tools/manage_index.js +6 -3
- package/dist/tools/read_file.js +38 -8
- package/package.json +4 -4
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { type Context } from "@zokizuan/satori-core";
|
|
2
|
+
import type { SnapshotCorruptionWarning, SnapshotManager } from "./snapshot.js";
|
|
3
|
+
import type { SyncManager } from "./sync.js";
|
|
4
|
+
import type { TrackedRootReadiness } from "./tracked-root-readiness.js";
|
|
5
|
+
import { type VectorBackendDiagnostic } from "./backend-diagnostics.js";
|
|
6
|
+
type ToolArgs = Record<string, unknown>;
|
|
7
|
+
type ToolTextResponse = {
|
|
8
|
+
content: Array<{
|
|
9
|
+
type: "text";
|
|
10
|
+
text: string;
|
|
11
|
+
}>;
|
|
12
|
+
isError?: boolean;
|
|
13
|
+
};
|
|
14
|
+
type ManageIndexStatus = "ok" | "error" | "not_ready" | "not_indexed" | "blocked" | "requires_reindex";
|
|
15
|
+
type ManageMaintenanceHandlersHost = {
|
|
16
|
+
context: Pick<Context, "clearIndex">;
|
|
17
|
+
snapshotManager: Pick<SnapshotManager, "removeCodebaseCompletely">;
|
|
18
|
+
syncManager: Pick<SyncManager, "ensureFreshness">;
|
|
19
|
+
trackedRootReadiness: Pick<TrackedRootReadiness, "prepareTrackedRootForRead" | "buildMissingLocalCollectionMessage">;
|
|
20
|
+
getSnapshotAllCodebases(): string[];
|
|
21
|
+
getSnapshotIndexedCodebases(): string[];
|
|
22
|
+
getSnapshotIndexingCodebases(): string[];
|
|
23
|
+
getSnapshotCodebaseStatus(codebasePath: string): string;
|
|
24
|
+
getSnapshotCodebaseInfo(codebasePath: string): Record<string, unknown> | undefined;
|
|
25
|
+
getSnapshotCorruptionWarning(): SnapshotCorruptionWarning | undefined;
|
|
26
|
+
buildRuntimeOwnerConflictResponseIfBlocked(action: "clear" | "sync", codebasePath: string): Promise<ToolTextResponse | null>;
|
|
27
|
+
recoverStaleIndexingStateIfNeeded(codebasePath: string): Promise<void>;
|
|
28
|
+
manageResponse(action: string, path: string, status: ManageIndexStatus | string, message: string, options?: Record<string, unknown>): ToolTextResponse;
|
|
29
|
+
buildCreateHint(codebasePath: string): Record<string, unknown>;
|
|
30
|
+
buildManageActionBlockedMessage(codebasePath: string, action: "clear" | "sync"): string;
|
|
31
|
+
buildStatusHint(codebasePath: string): Record<string, unknown>;
|
|
32
|
+
getManageRetryAfterMs(): number;
|
|
33
|
+
buildIndexingMetadata(codebasePath: string): Record<string, unknown> | undefined;
|
|
34
|
+
markCodebaseCleared(codebasePath: string, collectionName: string): void;
|
|
35
|
+
resolveCollectionName(codebasePath: string): string;
|
|
36
|
+
clearIndexingStats(): void;
|
|
37
|
+
saveSnapshotIfSupported(): void;
|
|
38
|
+
unwatchCodebase(codebasePath: string): Promise<void>;
|
|
39
|
+
refreshSnapshotStateFromDisk(): void;
|
|
40
|
+
buildReindexInstruction(codebasePath: string, detail?: string): string;
|
|
41
|
+
buildCompatibilityStatusLines(codebasePath: string): string;
|
|
42
|
+
buildManageRequiresReindexHints(codebasePath: string): Record<string, unknown>;
|
|
43
|
+
buildSyncHint(codebasePath: string): Record<string, unknown>;
|
|
44
|
+
buildStaleLocalHint(codebasePath: string, reason: string): Record<string, unknown>;
|
|
45
|
+
buildStaleLocalMessage(codebasePath: string, requestedPath: string, reason: string): string;
|
|
46
|
+
canSyncStaleLocal(codebasePath: string, reason: string): boolean;
|
|
47
|
+
enforceFingerprintGate(codebasePath: string): {
|
|
48
|
+
blockedResponse?: ToolTextResponse;
|
|
49
|
+
message?: string;
|
|
50
|
+
};
|
|
51
|
+
buildReindexHint(codebasePath: string): Record<string, unknown>;
|
|
52
|
+
touchWatchedCodebase(codebasePath: string): Promise<void>;
|
|
53
|
+
manageVectorBackendResponse(action: string, path: string, diagnostic: VectorBackendDiagnostic, humanText?: string): ToolTextResponse;
|
|
54
|
+
};
|
|
55
|
+
export declare class ManageMaintenanceHandlers {
|
|
56
|
+
private readonly host;
|
|
57
|
+
constructor(host: ManageMaintenanceHandlersHost);
|
|
58
|
+
handleClearIndex(args: ToolArgs): Promise<ToolTextResponse>;
|
|
59
|
+
handleGetIndexingStatus(args: ToolArgs): Promise<ToolTextResponse>;
|
|
60
|
+
handleSyncCodebase(args: ToolArgs): Promise<ToolTextResponse>;
|
|
61
|
+
}
|
|
62
|
+
export {};
|
|
63
|
+
//# sourceMappingURL=manage-maintenance-handlers.d.ts.map
|
|
@@ -0,0 +1,471 @@
|
|
|
1
|
+
import * as fs from "fs";
|
|
2
|
+
import { COLLECTION_LIMIT_MESSAGE, RemoteCollectionDeletePendingError, } from "@zokizuan/satori-core";
|
|
3
|
+
import { WARNING_CODES } from "./warnings.js";
|
|
4
|
+
import { classifyVectorBackendError, } from "./backend-diagnostics.js";
|
|
5
|
+
import { ensureAbsolutePath } from "../utils.js";
|
|
6
|
+
function collectErrorFragments(value, output, visited, depth = 0) {
|
|
7
|
+
if (value === null || value === undefined || depth > 4 || output.length >= 8) {
|
|
8
|
+
return;
|
|
9
|
+
}
|
|
10
|
+
if (typeof value === "string") {
|
|
11
|
+
const trimmed = value.trim();
|
|
12
|
+
if (trimmed.length > 0) {
|
|
13
|
+
output.push(trimmed);
|
|
14
|
+
}
|
|
15
|
+
return;
|
|
16
|
+
}
|
|
17
|
+
if (typeof value === "number" || typeof value === "boolean") {
|
|
18
|
+
output.push(String(value));
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
if (value instanceof Error) {
|
|
22
|
+
collectErrorFragments(value.message, output, visited, depth + 1);
|
|
23
|
+
collectErrorFragments(value.cause, output, visited, depth + 1);
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
if (typeof value !== "object") {
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
if (visited.has(value)) {
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
visited.add(value);
|
|
33
|
+
if (Array.isArray(value)) {
|
|
34
|
+
for (const item of value) {
|
|
35
|
+
collectErrorFragments(item, output, visited, depth + 1);
|
|
36
|
+
if (output.length >= 8) {
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
const record = value;
|
|
43
|
+
for (const key of ["message", "reason", "detail", "details", "error", "msg", "code", "error_code"]) {
|
|
44
|
+
if (key in record) {
|
|
45
|
+
collectErrorFragments(record[key], output, visited, depth + 1);
|
|
46
|
+
if (output.length >= 8) {
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
for (const nestedValue of Object.values(record)) {
|
|
52
|
+
collectErrorFragments(nestedValue, output, visited, depth + 1);
|
|
53
|
+
if (output.length >= 8) {
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
function formatUnknownError(error) {
|
|
59
|
+
if (error === COLLECTION_LIMIT_MESSAGE) {
|
|
60
|
+
return COLLECTION_LIMIT_MESSAGE;
|
|
61
|
+
}
|
|
62
|
+
const fragments = [];
|
|
63
|
+
collectErrorFragments(error, fragments, new Set());
|
|
64
|
+
const deduped = Array.from(new Set(fragments.map((fragment) => fragment.trim()).filter(Boolean)));
|
|
65
|
+
if (deduped.length > 0) {
|
|
66
|
+
return deduped.slice(0, 3).join(" | ");
|
|
67
|
+
}
|
|
68
|
+
try {
|
|
69
|
+
return JSON.stringify(error);
|
|
70
|
+
}
|
|
71
|
+
catch {
|
|
72
|
+
return String(error);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
export class ManageMaintenanceHandlers {
|
|
76
|
+
constructor(host) {
|
|
77
|
+
this.host = host;
|
|
78
|
+
}
|
|
79
|
+
async handleClearIndex(args) {
|
|
80
|
+
const codebasePath = typeof args.path === "string" ? args.path : "";
|
|
81
|
+
const requestedPath = ensureAbsolutePath(codebasePath);
|
|
82
|
+
if (this.host.getSnapshotAllCodebases().length === 0) {
|
|
83
|
+
return this.host.manageResponse("clear", requestedPath, "not_indexed", "No codebases are currently tracked.", { reason: "not_indexed" });
|
|
84
|
+
}
|
|
85
|
+
try {
|
|
86
|
+
const absolutePath = requestedPath;
|
|
87
|
+
const pathExists = fs.existsSync(absolutePath);
|
|
88
|
+
if (pathExists) {
|
|
89
|
+
const stat = fs.statSync(absolutePath);
|
|
90
|
+
if (!stat.isDirectory()) {
|
|
91
|
+
return this.host.manageResponse("clear", absolutePath, "error", `Error: Path '${absolutePath}' is not a directory`);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
const runtimeOwnerConflict = await this.host.buildRuntimeOwnerConflictResponseIfBlocked("clear", absolutePath);
|
|
95
|
+
if (runtimeOwnerConflict) {
|
|
96
|
+
return runtimeOwnerConflict;
|
|
97
|
+
}
|
|
98
|
+
if (pathExists) {
|
|
99
|
+
await this.host.recoverStaleIndexingStateIfNeeded(absolutePath);
|
|
100
|
+
}
|
|
101
|
+
const isIndexed = this.host.getSnapshotIndexedCodebases().includes(absolutePath);
|
|
102
|
+
const isIndexing = this.host.getSnapshotIndexingCodebases().includes(absolutePath);
|
|
103
|
+
const status = this.host.getSnapshotCodebaseStatus(absolutePath);
|
|
104
|
+
const isRequiresReindex = status === "requires_reindex";
|
|
105
|
+
if (!isIndexed && !isIndexing && !isRequiresReindex) {
|
|
106
|
+
if (!pathExists) {
|
|
107
|
+
return this.host.manageResponse("clear", absolutePath, "error", `Error: Path '${absolutePath}' does not exist. Original input: '${codebasePath}'`);
|
|
108
|
+
}
|
|
109
|
+
return this.host.manageResponse("clear", absolutePath, "not_indexed", `Error: Codebase '${absolutePath}' is not indexed or being indexed.`, {
|
|
110
|
+
reason: "not_indexed",
|
|
111
|
+
hints: {
|
|
112
|
+
create: this.host.buildCreateHint(absolutePath),
|
|
113
|
+
},
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
if (isIndexing) {
|
|
117
|
+
return this.host.manageResponse("clear", absolutePath, "not_ready", this.host.buildManageActionBlockedMessage(absolutePath, "clear"), {
|
|
118
|
+
reason: "indexing",
|
|
119
|
+
hints: {
|
|
120
|
+
status: this.host.buildStatusHint(absolutePath),
|
|
121
|
+
retryAfterMs: this.host.getManageRetryAfterMs(),
|
|
122
|
+
indexing: this.host.buildIndexingMetadata(absolutePath),
|
|
123
|
+
},
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
console.log(`[CLEAR] Clearing codebase: ${absolutePath}`);
|
|
127
|
+
try {
|
|
128
|
+
await this.host.context.clearIndex(absolutePath);
|
|
129
|
+
console.log(`[CLEAR] Successfully cleared index for: ${absolutePath}`);
|
|
130
|
+
}
|
|
131
|
+
catch (error) {
|
|
132
|
+
if (error instanceof RemoteCollectionDeletePendingError) {
|
|
133
|
+
const errorMsg = `Remote deletion is still pending for ${absolutePath}. Local index state was not changed. Details: ${formatUnknownError(error)}`;
|
|
134
|
+
console.error(`[CLEAR] ${errorMsg}`);
|
|
135
|
+
return this.host.manageResponse("clear", absolutePath, "error", errorMsg, {
|
|
136
|
+
reason: "remote_delete_pending",
|
|
137
|
+
hints: {
|
|
138
|
+
retry: this.host.buildStatusHint(absolutePath),
|
|
139
|
+
clear: { tool: "manage_index", args: { action: "clear", path: absolutePath } },
|
|
140
|
+
},
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
const errorMsg = `Failed to clear ${absolutePath}: ${formatUnknownError(error)}`;
|
|
144
|
+
console.error(`[CLEAR] ${errorMsg}`);
|
|
145
|
+
return this.host.manageResponse("clear", absolutePath, "error", errorMsg);
|
|
146
|
+
}
|
|
147
|
+
this.host.snapshotManager.removeCodebaseCompletely(absolutePath);
|
|
148
|
+
this.host.markCodebaseCleared(absolutePath, this.host.resolveCollectionName(absolutePath));
|
|
149
|
+
this.host.clearIndexingStats();
|
|
150
|
+
this.host.saveSnapshotIfSupported();
|
|
151
|
+
await this.host.unwatchCodebase(absolutePath);
|
|
152
|
+
let resultText = `Successfully cleared codebase '${absolutePath}'`;
|
|
153
|
+
const remainingIndexed = this.host.getSnapshotIndexedCodebases().length;
|
|
154
|
+
const remainingIndexing = this.host.getSnapshotIndexingCodebases().length;
|
|
155
|
+
if (remainingIndexed > 0 || remainingIndexing > 0) {
|
|
156
|
+
resultText += `\n${remainingIndexed} other indexed codebase(s) and ${remainingIndexing} indexing codebase(s) remain`;
|
|
157
|
+
}
|
|
158
|
+
return this.host.manageResponse("clear", absolutePath, "ok", resultText);
|
|
159
|
+
}
|
|
160
|
+
catch (error) {
|
|
161
|
+
const errorMessage = typeof error === "string" ? error : (error instanceof Error ? error.message : String(error));
|
|
162
|
+
if (errorMessage === COLLECTION_LIMIT_MESSAGE || errorMessage.includes(COLLECTION_LIMIT_MESSAGE)) {
|
|
163
|
+
return this.host.manageResponse("clear", requestedPath, "error", COLLECTION_LIMIT_MESSAGE);
|
|
164
|
+
}
|
|
165
|
+
return this.host.manageResponse("clear", requestedPath, "error", `Error clearing index: ${errorMessage}`);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
async handleGetIndexingStatus(args) {
|
|
169
|
+
const codebasePath = typeof args.path === "string" ? args.path : "";
|
|
170
|
+
const requestedPath = ensureAbsolutePath(codebasePath);
|
|
171
|
+
try {
|
|
172
|
+
const absolutePath = requestedPath;
|
|
173
|
+
if (!fs.existsSync(absolutePath)) {
|
|
174
|
+
return this.host.manageResponse("status", absolutePath, "error", `Error: Path '${absolutePath}' does not exist. Original input: '${codebasePath}'`);
|
|
175
|
+
}
|
|
176
|
+
const stat = fs.statSync(absolutePath);
|
|
177
|
+
if (!stat.isDirectory()) {
|
|
178
|
+
return this.host.manageResponse("status", absolutePath, "error", `Error: Path '${absolutePath}' is not a directory`);
|
|
179
|
+
}
|
|
180
|
+
this.host.refreshSnapshotStateFromDisk();
|
|
181
|
+
const snapshotCorruptionWarning = this.host.getSnapshotCorruptionWarning();
|
|
182
|
+
await this.host.recoverStaleIndexingStateIfNeeded(absolutePath);
|
|
183
|
+
const trackedRootState = await this.host.trackedRootReadiness.prepareTrackedRootForRead(absolutePath);
|
|
184
|
+
if (trackedRootState.state === "requires_reindex") {
|
|
185
|
+
const statusMessage = this.host.buildReindexInstruction(trackedRootState.codebasePath, trackedRootState.message);
|
|
186
|
+
const compatibilityStatus = this.host.buildCompatibilityStatusLines(trackedRootState.codebasePath);
|
|
187
|
+
const pathInfo = codebasePath !== trackedRootState.codebasePath
|
|
188
|
+
? `\nNote: Input path '${codebasePath}' was resolved to absolute path '${trackedRootState.codebasePath}'`
|
|
189
|
+
: "";
|
|
190
|
+
return this.host.manageResponse("status", trackedRootState.codebasePath, "requires_reindex", statusMessage + compatibilityStatus + pathInfo, {
|
|
191
|
+
reason: "requires_reindex",
|
|
192
|
+
hints: this.host.buildManageRequiresReindexHints(trackedRootState.codebasePath),
|
|
193
|
+
});
|
|
194
|
+
}
|
|
195
|
+
let statusMessage = "";
|
|
196
|
+
let envelopePath = absolutePath;
|
|
197
|
+
let envelopeStatus = "ok";
|
|
198
|
+
let envelopeReason;
|
|
199
|
+
let envelopeHints;
|
|
200
|
+
let proofDebugHint;
|
|
201
|
+
if (trackedRootState.state === "not_indexed") {
|
|
202
|
+
envelopeStatus = "not_indexed";
|
|
203
|
+
envelopeReason = "not_indexed";
|
|
204
|
+
envelopeHints = { create: this.host.buildCreateHint(absolutePath) };
|
|
205
|
+
statusMessage = `โ Codebase '${absolutePath}' is not indexed. Call manage_index with {"action":"create","path":"${absolutePath}"} to index it first.`;
|
|
206
|
+
}
|
|
207
|
+
else if (trackedRootState.state === "stale_local") {
|
|
208
|
+
envelopePath = trackedRootState.codebasePath;
|
|
209
|
+
envelopeStatus = "not_indexed";
|
|
210
|
+
envelopeReason = "not_indexed";
|
|
211
|
+
const syncable = this.host.canSyncStaleLocal(trackedRootState.codebasePath, trackedRootState.reason);
|
|
212
|
+
envelopeHints = {
|
|
213
|
+
...(syncable ? { sync: this.host.buildSyncHint(trackedRootState.codebasePath) } : {}),
|
|
214
|
+
create: this.host.buildCreateHint(trackedRootState.codebasePath),
|
|
215
|
+
staleLocal: this.host.buildStaleLocalHint(trackedRootState.codebasePath, trackedRootState.reason),
|
|
216
|
+
};
|
|
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}.`;
|
|
220
|
+
}
|
|
221
|
+
else if (trackedRootState.state === "missing_collection") {
|
|
222
|
+
envelopePath = trackedRootState.codebasePath;
|
|
223
|
+
envelopeStatus = "not_indexed";
|
|
224
|
+
envelopeReason = "not_indexed";
|
|
225
|
+
envelopeHints = { create: this.host.buildCreateHint(trackedRootState.codebasePath) };
|
|
226
|
+
statusMessage = `โ ${this.host.trackedRootReadiness.buildMissingLocalCollectionMessage(trackedRootState.codebasePath, absolutePath, trackedRootState.collectionName)}`;
|
|
227
|
+
proofDebugHint = trackedRootState.proofDebugHint;
|
|
228
|
+
}
|
|
229
|
+
else if (trackedRootState.state === "indexing") {
|
|
230
|
+
envelopePath = trackedRootState.codebasePath;
|
|
231
|
+
envelopeStatus = "not_ready";
|
|
232
|
+
envelopeReason = "indexing";
|
|
233
|
+
envelopeHints = {
|
|
234
|
+
status: this.host.buildStatusHint(trackedRootState.codebasePath),
|
|
235
|
+
retryAfterMs: this.host.getManageRetryAfterMs(),
|
|
236
|
+
indexing: this.host.buildIndexingMetadata(trackedRootState.codebasePath),
|
|
237
|
+
};
|
|
238
|
+
const info = this.host.getSnapshotCodebaseInfo(trackedRootState.codebasePath);
|
|
239
|
+
if (info?.status === "indexing") {
|
|
240
|
+
const progressPercentage = typeof info.indexingPercentage === "number" && Number.isFinite(info.indexingPercentage)
|
|
241
|
+
? info.indexingPercentage
|
|
242
|
+
: 0;
|
|
243
|
+
statusMessage = `๐ Codebase '${trackedRootState.codebasePath}' is currently being indexed. Progress: ${progressPercentage.toFixed(1)}%`;
|
|
244
|
+
if (progressPercentage < 10) {
|
|
245
|
+
statusMessage += " (Preparing and scanning files...)";
|
|
246
|
+
}
|
|
247
|
+
else if (progressPercentage < 100) {
|
|
248
|
+
statusMessage += " (Processing files and generating embeddings...)";
|
|
249
|
+
}
|
|
250
|
+
if (typeof info.lastUpdated === "string") {
|
|
251
|
+
statusMessage += `\n๐ Last updated: ${new Date(info.lastUpdated).toLocaleString()}`;
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
else {
|
|
255
|
+
statusMessage = `๐ Codebase '${trackedRootState.codebasePath}' is currently being indexed.`;
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
else if (trackedRootState.state === "index_failed") {
|
|
259
|
+
envelopePath = trackedRootState.codebasePath;
|
|
260
|
+
envelopeStatus = "error";
|
|
261
|
+
const failedInfo = trackedRootState.info;
|
|
262
|
+
if (typeof failedInfo.errorMessage === "string") {
|
|
263
|
+
statusMessage = `โ Codebase '${trackedRootState.codebasePath}' indexing failed.`;
|
|
264
|
+
statusMessage += `\n๐จ Error: ${failedInfo.errorMessage}`;
|
|
265
|
+
if (typeof failedInfo.lastAttemptedPercentage === "number" && Number.isFinite(failedInfo.lastAttemptedPercentage)) {
|
|
266
|
+
statusMessage += `\n๐ Failed at: ${failedInfo.lastAttemptedPercentage.toFixed(1)}% progress`;
|
|
267
|
+
}
|
|
268
|
+
if (typeof failedInfo.lastUpdated === "string") {
|
|
269
|
+
statusMessage += `\n๐ Failed at: ${new Date(failedInfo.lastUpdated).toLocaleString()}`;
|
|
270
|
+
}
|
|
271
|
+
statusMessage += `\n๐ก Retry with manage_index action='create'.`;
|
|
272
|
+
}
|
|
273
|
+
else {
|
|
274
|
+
statusMessage = `โ Codebase '${trackedRootState.codebasePath}' indexing failed. You can retry indexing.`;
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
else {
|
|
278
|
+
envelopePath = trackedRootState.root.path;
|
|
279
|
+
proofDebugHint = trackedRootState.proofDebugHint;
|
|
280
|
+
const status = this.host.getSnapshotCodebaseStatus(trackedRootState.root.path);
|
|
281
|
+
const info = trackedRootState.root.info || this.host.getSnapshotCodebaseInfo(trackedRootState.root.path);
|
|
282
|
+
switch (status) {
|
|
283
|
+
case "indexed":
|
|
284
|
+
if (info?.status === "indexed") {
|
|
285
|
+
statusMessage = `โ
Codebase '${trackedRootState.root.path}' is fully indexed and ready for search.`;
|
|
286
|
+
statusMessage += `\n๐ Statistics: ${info.indexedFiles} files, ${info.totalChunks} chunks`;
|
|
287
|
+
statusMessage += `\n๐
Status: ${info.indexStatus}`;
|
|
288
|
+
if (typeof info.lastUpdated === "string") {
|
|
289
|
+
statusMessage += `\n๐ Last updated: ${new Date(info.lastUpdated).toLocaleString()}`;
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
else {
|
|
293
|
+
statusMessage = `โ
Codebase '${trackedRootState.root.path}' is fully indexed and ready for search.`;
|
|
294
|
+
}
|
|
295
|
+
break;
|
|
296
|
+
case "sync_completed":
|
|
297
|
+
if (info?.status === "sync_completed") {
|
|
298
|
+
statusMessage = `๐ Codebase '${trackedRootState.root.path}' sync completed.`;
|
|
299
|
+
statusMessage += `\n๐ Changes: +${info.added} added, -${info.removed} removed, ~${info.modified} modified`;
|
|
300
|
+
if (typeof info.lastUpdated === "string") {
|
|
301
|
+
statusMessage += `\n๐ Last synced: ${new Date(info.lastUpdated).toLocaleString()}`;
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
else {
|
|
305
|
+
statusMessage = `๐ Codebase '${trackedRootState.root.path}' sync completed.`;
|
|
306
|
+
}
|
|
307
|
+
break;
|
|
308
|
+
case "not_found":
|
|
309
|
+
default:
|
|
310
|
+
envelopeStatus = "not_indexed";
|
|
311
|
+
envelopeReason = "not_indexed";
|
|
312
|
+
envelopeHints = { create: this.host.buildCreateHint(trackedRootState.root.path) };
|
|
313
|
+
statusMessage = `โ Codebase '${trackedRootState.root.path}' is not indexed. Call manage_index with {"action":"create","path":"${trackedRootState.root.path}"} to index it first.`;
|
|
314
|
+
break;
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
const warnings = [];
|
|
318
|
+
if (proofDebugHint) {
|
|
319
|
+
statusMessage += `\nโ ๏ธ Completion proof check is temporarily unavailable (probe_failed); keeping local status.`;
|
|
320
|
+
warnings.push(WARNING_CODES.IGNORE_POLICY_PROBE_FAILED);
|
|
321
|
+
}
|
|
322
|
+
const pathInfo = codebasePath !== envelopePath
|
|
323
|
+
? `\nNote: Input path '${codebasePath}' was resolved to absolute path '${envelopePath}'`
|
|
324
|
+
: "";
|
|
325
|
+
const compatibilityStatus = this.host.buildCompatibilityStatusLines(envelopePath);
|
|
326
|
+
const snapshotWarningText = snapshotCorruptionWarning
|
|
327
|
+
? `\nWARNING: Snapshot state was recovered after a corrupt snapshot was quarantined. Tracked codebases may be incomplete.`
|
|
328
|
+
+ `\nSnapshot path: ${snapshotCorruptionWarning.snapshotPath}`
|
|
329
|
+
+ (typeof snapshotCorruptionWarning.quarantinedPath === "string" ? `\nQuarantined snapshot: ${snapshotCorruptionWarning.quarantinedPath}` : "")
|
|
330
|
+
+ `\nReason: ${snapshotCorruptionWarning.message}`
|
|
331
|
+
: "";
|
|
332
|
+
if (snapshotCorruptionWarning) {
|
|
333
|
+
envelopeHints = {
|
|
334
|
+
...(envelopeHints || {}),
|
|
335
|
+
snapshotCorruption: snapshotCorruptionWarning,
|
|
336
|
+
};
|
|
337
|
+
}
|
|
338
|
+
return this.host.manageResponse("status", envelopePath, envelopeStatus, statusMessage + compatibilityStatus + pathInfo + snapshotWarningText, {
|
|
339
|
+
reason: envelopeReason,
|
|
340
|
+
hints: envelopeHints,
|
|
341
|
+
warnings,
|
|
342
|
+
});
|
|
343
|
+
}
|
|
344
|
+
catch (error) {
|
|
345
|
+
return this.host.manageResponse("status", requestedPath, "error", `Error getting indexing status: ${formatUnknownError(error)}`);
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
async handleSyncCodebase(args) {
|
|
349
|
+
const codebasePath = typeof args.path === "string" ? args.path : "";
|
|
350
|
+
const requestedPath = ensureAbsolutePath(codebasePath);
|
|
351
|
+
try {
|
|
352
|
+
const absolutePath = requestedPath;
|
|
353
|
+
if (!fs.existsSync(absolutePath)) {
|
|
354
|
+
return this.host.manageResponse("sync", absolutePath, "error", `Error: Path '${absolutePath}' does not exist. Original input: '${codebasePath}'`);
|
|
355
|
+
}
|
|
356
|
+
const stat = fs.statSync(absolutePath);
|
|
357
|
+
if (!stat.isDirectory()) {
|
|
358
|
+
return this.host.manageResponse("sync", absolutePath, "error", `Error: Path '${absolutePath}' is not a directory`);
|
|
359
|
+
}
|
|
360
|
+
const runtimeOwnerConflict = await this.host.buildRuntimeOwnerConflictResponseIfBlocked("sync", absolutePath);
|
|
361
|
+
if (runtimeOwnerConflict) {
|
|
362
|
+
return runtimeOwnerConflict;
|
|
363
|
+
}
|
|
364
|
+
await this.host.recoverStaleIndexingStateIfNeeded(absolutePath);
|
|
365
|
+
const syncGate = this.host.enforceFingerprintGate(absolutePath);
|
|
366
|
+
if (syncGate.blockedResponse) {
|
|
367
|
+
return this.host.manageResponse("sync", absolutePath, "requires_reindex", this.host.buildReindexInstruction(absolutePath, syncGate.message), {
|
|
368
|
+
reason: "requires_reindex",
|
|
369
|
+
hints: {
|
|
370
|
+
reindex: this.host.buildReindexHint(absolutePath),
|
|
371
|
+
status: this.host.buildStatusHint(absolutePath),
|
|
372
|
+
},
|
|
373
|
+
});
|
|
374
|
+
}
|
|
375
|
+
if (this.host.getSnapshotIndexingCodebases().includes(absolutePath)) {
|
|
376
|
+
return this.host.manageResponse("sync", absolutePath, "not_ready", this.host.buildManageActionBlockedMessage(absolutePath, "sync"), {
|
|
377
|
+
reason: "indexing",
|
|
378
|
+
hints: {
|
|
379
|
+
status: this.host.buildStatusHint(absolutePath),
|
|
380
|
+
retryAfterMs: this.host.getManageRetryAfterMs(),
|
|
381
|
+
indexing: this.host.buildIndexingMetadata(absolutePath),
|
|
382
|
+
},
|
|
383
|
+
});
|
|
384
|
+
}
|
|
385
|
+
const isIndexed = this.host.getSnapshotIndexedCodebases().includes(absolutePath);
|
|
386
|
+
if (!isIndexed) {
|
|
387
|
+
return this.host.manageResponse("sync", absolutePath, "not_indexed", `Error: Codebase '${absolutePath}' is not indexed. Call manage_index with {"action":"create","path":"${absolutePath}"} first.`, {
|
|
388
|
+
reason: "not_indexed",
|
|
389
|
+
hints: {
|
|
390
|
+
create: this.host.buildCreateHint(absolutePath),
|
|
391
|
+
},
|
|
392
|
+
});
|
|
393
|
+
}
|
|
394
|
+
console.log(`[SYNC] Manually triggering incremental sync for: ${absolutePath}`);
|
|
395
|
+
const decision = await this.host.syncManager.ensureFreshness(absolutePath, 0);
|
|
396
|
+
if (decision.mode === "ignore_reload_failed") {
|
|
397
|
+
const fallbackLine = decision.fallbackSyncExecuted
|
|
398
|
+
? "\nFallback incremental sync was executed, but ignore-rule reconciliation did not complete deterministically."
|
|
399
|
+
: "";
|
|
400
|
+
return this.host.manageResponse("sync", absolutePath, "error", `Error syncing codebase: ignore-rule reconciliation failed (${decision.errorMessage || "unknown_ignore_reload_error"}).${fallbackLine}`);
|
|
401
|
+
}
|
|
402
|
+
if (decision.mode === "skipped_indexing") {
|
|
403
|
+
return this.host.manageResponse("sync", absolutePath, "not_ready", this.host.buildManageActionBlockedMessage(absolutePath, "sync"), {
|
|
404
|
+
reason: "indexing",
|
|
405
|
+
hints: {
|
|
406
|
+
status: this.host.buildStatusHint(absolutePath),
|
|
407
|
+
retryAfterMs: this.host.getManageRetryAfterMs(),
|
|
408
|
+
indexing: this.host.buildIndexingMetadata(absolutePath),
|
|
409
|
+
},
|
|
410
|
+
});
|
|
411
|
+
}
|
|
412
|
+
if (decision.mode === "skipped_requires_reindex") {
|
|
413
|
+
return this.host.manageResponse("sync", absolutePath, "requires_reindex", this.host.buildReindexInstruction(absolutePath, "Sync blocked because this codebase requires reindex."), {
|
|
414
|
+
reason: "requires_reindex",
|
|
415
|
+
hints: {
|
|
416
|
+
reindex: this.host.buildReindexHint(absolutePath),
|
|
417
|
+
status: this.host.buildStatusHint(absolutePath),
|
|
418
|
+
},
|
|
419
|
+
});
|
|
420
|
+
}
|
|
421
|
+
if (decision.mode === "skipped_missing_path") {
|
|
422
|
+
return this.host.manageResponse("sync", absolutePath, "error", `Error: Codebase path '${absolutePath}' no longer exists.`);
|
|
423
|
+
}
|
|
424
|
+
const added = decision.stats?.added ?? 0;
|
|
425
|
+
const removed = decision.stats?.removed ?? 0;
|
|
426
|
+
const modified = decision.stats?.modified ?? 0;
|
|
427
|
+
const ignoredDeletes = decision.deletedFiles ?? 0;
|
|
428
|
+
const totalChanges = added + removed + modified;
|
|
429
|
+
if (decision.mode === "coalesced") {
|
|
430
|
+
if (typeof decision.errorMessage === "string" && decision.errorMessage.trim().length > 0) {
|
|
431
|
+
const fallbackLine = decision.fallbackSyncExecuted
|
|
432
|
+
? "\nFallback incremental sync was executed, but ignore-rule reconciliation did not complete deterministically."
|
|
433
|
+
: "";
|
|
434
|
+
return this.host.manageResponse("sync", absolutePath, "error", `Error syncing codebase: coalesced in-flight reconcile failed (${decision.errorMessage}).${fallbackLine}`);
|
|
435
|
+
}
|
|
436
|
+
await this.host.touchWatchedCodebase(absolutePath);
|
|
437
|
+
return this.host.manageResponse("sync", absolutePath, "ok", `๐ Sync request coalesced for '${absolutePath}'. Reused in-flight sync result.`);
|
|
438
|
+
}
|
|
439
|
+
if (decision.mode === "reconciled_ignore_change") {
|
|
440
|
+
if (totalChanges === 0 && ignoredDeletes === 0) {
|
|
441
|
+
await this.host.touchWatchedCodebase(absolutePath);
|
|
442
|
+
return this.host.manageResponse("sync", absolutePath, "ok", `โ
Ignore-rule reconciliation completed for '${absolutePath}'. No additional index changes were required.`);
|
|
443
|
+
}
|
|
444
|
+
const resultMessage = `๐ Incremental sync + ignore-rule reconciliation completed for '${absolutePath}'.\n\n` +
|
|
445
|
+
`๐ Sync changes:\n+ ${added} file(s) added\n- ${removed} file(s) removed\n~ ${modified} file(s) modified\n` +
|
|
446
|
+
`๐งน Ignored paths removed from index: ${ignoredDeletes}\n` +
|
|
447
|
+
`\nTotal changes: ${totalChanges + ignoredDeletes}`;
|
|
448
|
+
console.log(`[SYNC] โ
Sync+ignore reconcile completed: +${added}, -${removed}, ~${modified}, ignoredDeleted=${ignoredDeletes}`);
|
|
449
|
+
await this.host.touchWatchedCodebase(absolutePath);
|
|
450
|
+
return this.host.manageResponse("sync", absolutePath, "ok", resultMessage);
|
|
451
|
+
}
|
|
452
|
+
if (totalChanges === 0) {
|
|
453
|
+
await this.host.touchWatchedCodebase(absolutePath);
|
|
454
|
+
return this.host.manageResponse("sync", absolutePath, "ok", `โ
No changes detected for codebase '${absolutePath}'. Index is up to date.`);
|
|
455
|
+
}
|
|
456
|
+
const resultMessage = `๐ Incremental sync completed for '${absolutePath}'.\n\n๐ Changes:\n+ ${added} file(s) added\n- ${removed} file(s) removed\n~ ${modified} file(s) modified\n\nTotal changes: ${totalChanges}`;
|
|
457
|
+
console.log(`[SYNC] โ
Sync completed: +${added}, -${removed}, ~${modified}`);
|
|
458
|
+
await this.host.touchWatchedCodebase(absolutePath);
|
|
459
|
+
return this.host.manageResponse("sync", absolutePath, "ok", resultMessage);
|
|
460
|
+
}
|
|
461
|
+
catch (error) {
|
|
462
|
+
console.error("[SYNC] Error during sync:", error);
|
|
463
|
+
const vectorBackendDiagnostic = classifyVectorBackendError(error);
|
|
464
|
+
if (vectorBackendDiagnostic) {
|
|
465
|
+
return this.host.manageVectorBackendResponse("sync", requestedPath, vectorBackendDiagnostic);
|
|
466
|
+
}
|
|
467
|
+
return this.host.manageResponse("sync", requestedPath, "error", `Error syncing codebase: ${formatUnknownError(error)}`);
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
//# sourceMappingURL=manage-maintenance-handlers.js.map
|
|
@@ -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 {
|