context-mode 1.0.111 → 1.0.112
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/marketplace.json +2 -2
- package/.claude-plugin/plugin.json +1 -1
- package/.openclaw-plugin/index.ts +3 -2
- package/.openclaw-plugin/openclaw.plugin.json +1 -1
- package/.openclaw-plugin/package.json +1 -1
- package/README.md +152 -34
- package/bin/statusline.mjs +144 -127
- package/build/adapters/base.d.ts +8 -5
- package/build/adapters/base.js +8 -18
- package/build/adapters/claude-code/index.d.ts +24 -3
- package/build/adapters/claude-code/index.js +44 -11
- package/build/adapters/codex/hooks.d.ts +10 -5
- package/build/adapters/codex/hooks.js +10 -5
- package/build/adapters/codex/index.d.ts +17 -5
- package/build/adapters/codex/index.js +337 -37
- package/build/adapters/codex/paths.d.ts +1 -0
- package/build/adapters/codex/paths.js +12 -0
- package/build/adapters/cursor/index.d.ts +6 -0
- package/build/adapters/cursor/index.js +83 -2
- package/build/adapters/detect.d.ts +1 -1
- package/build/adapters/detect.js +29 -6
- package/build/adapters/omp/index.d.ts +65 -0
- package/build/adapters/omp/index.js +182 -0
- package/build/adapters/omp/plugin.d.ts +75 -0
- package/build/adapters/omp/plugin.js +220 -0
- package/build/adapters/openclaw/mcp-tools.d.ts +54 -0
- package/build/adapters/openclaw/mcp-tools.js +198 -0
- package/build/adapters/openclaw/plugin.d.ts +130 -0
- package/build/adapters/openclaw/plugin.js +629 -0
- package/build/adapters/openclaw/workspace-router.d.ts +29 -0
- package/build/adapters/openclaw/workspace-router.js +64 -0
- package/build/adapters/opencode/plugin.d.ts +145 -0
- package/build/adapters/opencode/plugin.js +457 -0
- package/build/adapters/pi/extension.d.ts +26 -0
- package/build/adapters/pi/extension.js +552 -0
- package/build/adapters/pi/index.d.ts +57 -0
- package/build/adapters/pi/index.js +173 -0
- package/build/adapters/pi/mcp-bridge.d.ts +113 -0
- package/build/adapters/pi/mcp-bridge.js +251 -0
- package/build/adapters/types.d.ts +11 -6
- package/build/cli.js +186 -170
- package/build/db-base.d.ts +15 -2
- package/build/db-base.js +50 -5
- package/build/executor.d.ts +2 -0
- package/build/executor.js +15 -2
- package/build/runPool.d.ts +36 -0
- package/build/runPool.js +51 -0
- package/build/runtime.js +64 -5
- package/build/search/auto-memory.js +6 -4
- package/build/security.js +30 -10
- package/build/server.d.ts +23 -1
- package/build/server.js +652 -174
- package/build/session/analytics.d.ts +404 -1
- package/build/session/analytics.js +1347 -42
- package/build/session/db.d.ts +114 -5
- package/build/session/db.js +275 -27
- package/build/session/event-emit.d.ts +48 -0
- package/build/session/event-emit.js +101 -0
- package/build/session/extract.d.ts +1 -0
- package/build/session/extract.js +79 -12
- package/build/session/purge.d.ts +111 -0
- package/build/session/purge.js +138 -0
- package/build/store.d.ts +7 -0
- package/build/store.js +69 -6
- package/build/util/claude-config.d.ts +26 -0
- package/build/util/claude-config.js +91 -0
- package/build/util/hook-config.d.ts +4 -0
- package/build/util/hook-config.js +39 -0
- package/cli.bundle.mjs +411 -208
- package/configs/antigravity/GEMINI.md +0 -3
- package/configs/claude-code/CLAUDE.md +1 -4
- package/configs/codex/AGENTS.md +1 -4
- package/configs/codex/config.toml +3 -0
- package/configs/codex/hooks.json +8 -0
- package/configs/cursor/context-mode.mdc +0 -3
- package/configs/gemini-cli/GEMINI.md +0 -3
- package/configs/jetbrains-copilot/copilot-instructions.md +0 -3
- package/configs/kilo/AGENTS.md +0 -3
- package/configs/kiro/KIRO.md +0 -3
- package/configs/omp/SYSTEM.md +85 -0
- package/configs/omp/mcp.json +7 -0
- package/configs/openclaw/AGENTS.md +0 -3
- package/configs/opencode/AGENTS.md +0 -3
- package/configs/pi/AGENTS.md +0 -3
- package/configs/qwen-code/QWEN.md +1 -4
- package/configs/vscode-copilot/copilot-instructions.md +0 -3
- package/configs/zed/AGENTS.md +0 -3
- package/hooks/codex/posttooluse.mjs +9 -2
- package/hooks/codex/precompact.mjs +69 -0
- package/hooks/codex/sessionstart.mjs +13 -9
- package/hooks/codex/stop.mjs +1 -2
- package/hooks/codex/userpromptsubmit.mjs +1 -2
- package/hooks/core/routing.mjs +237 -18
- package/hooks/cursor/afteragentresponse.mjs +1 -1
- package/hooks/cursor/hooks.json +31 -0
- package/hooks/cursor/posttooluse.mjs +1 -1
- package/hooks/cursor/sessionstart.mjs +5 -5
- package/hooks/cursor/stop.mjs +1 -1
- package/hooks/ensure-deps.mjs +12 -13
- package/hooks/gemini-cli/aftertool.mjs +1 -1
- package/hooks/gemini-cli/beforeagent.mjs +1 -1
- package/hooks/gemini-cli/precompress.mjs +3 -2
- package/hooks/gemini-cli/sessionstart.mjs +9 -9
- package/hooks/jetbrains-copilot/posttooluse.mjs +1 -1
- package/hooks/jetbrains-copilot/precompact.mjs +3 -2
- package/hooks/jetbrains-copilot/sessionstart.mjs +9 -9
- package/hooks/kiro/agentspawn.mjs +5 -5
- package/hooks/kiro/posttooluse.mjs +2 -2
- package/hooks/kiro/userpromptsubmit.mjs +1 -1
- package/hooks/posttooluse.mjs +45 -0
- package/hooks/precompact.mjs +17 -0
- package/hooks/pretooluse.mjs +23 -0
- package/hooks/routing-block.mjs +0 -12
- package/hooks/run-hook.mjs +16 -3
- package/hooks/session-db.bundle.mjs +27 -18
- package/hooks/session-extract.bundle.mjs +2 -2
- package/hooks/session-helpers.mjs +101 -64
- package/hooks/sessionstart.mjs +51 -2
- package/hooks/vscode-copilot/posttooluse.mjs +1 -1
- package/hooks/vscode-copilot/precompact.mjs +3 -2
- package/hooks/vscode-copilot/sessionstart.mjs +9 -9
- package/openclaw.plugin.json +1 -1
- package/package.json +14 -8
- package/server.bundle.mjs +349 -147
- package/skills/UPSTREAM-CREDITS.md +0 -51
- package/skills/context-mode-ops/SKILL.md +0 -299
- package/skills/context-mode-ops/agent-teams.md +0 -198
- package/skills/context-mode-ops/communication.md +0 -224
- package/skills/context-mode-ops/marketing.md +0 -124
- package/skills/context-mode-ops/release.md +0 -214
- package/skills/context-mode-ops/review-pr.md +0 -269
- package/skills/context-mode-ops/tdd.md +0 -329
- package/skills/context-mode-ops/triage-issue.md +0 -266
- package/skills/context-mode-ops/validation.md +0 -307
- package/skills/diagnose/SKILL.md +0 -122
- package/skills/diagnose/scripts/hitl-loop.template.sh +0 -41
- package/skills/grill-me/SKILL.md +0 -15
- package/skills/grill-with-docs/ADR-FORMAT.md +0 -47
- package/skills/grill-with-docs/CONTEXT-FORMAT.md +0 -77
- package/skills/grill-with-docs/SKILL.md +0 -93
- package/skills/improve-codebase-architecture/DEEPENING.md +0 -37
- package/skills/improve-codebase-architecture/INTERFACE-DESIGN.md +0 -44
- package/skills/improve-codebase-architecture/LANGUAGE.md +0 -53
- package/skills/improve-codebase-architecture/SKILL.md +0 -76
- package/skills/tdd/SKILL.md +0 -114
- package/skills/tdd/deep-modules.md +0 -33
- package/skills/tdd/interface-design.md +0 -31
- package/skills/tdd/mocking.md +0 -59
- package/skills/tdd/refactoring.md +0 -10
- package/skills/tdd/tests.md +0 -61
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* event-emit — Phase 5+7 of D2 PRD (stats-event-driven-architecture)
|
|
3
|
+
*
|
|
4
|
+
* Server-side helpers that record sandbox / index / cache work into
|
|
5
|
+
* `session_events` with the new `bytes_avoided` / `bytes_returned`
|
|
6
|
+
* columns so the renderer can compute the real $ saved instead of the
|
|
7
|
+
* conservative `events × 256` token estimate.
|
|
8
|
+
*
|
|
9
|
+
* Design notes
|
|
10
|
+
* ────────────
|
|
11
|
+
* - Uses the public `SessionDB.insertEvent(... , bytes)` API the schema
|
|
12
|
+
* engineer extended in this branch — same dedup + FIFO eviction +
|
|
13
|
+
* transaction wrapping you'd get from any other event source.
|
|
14
|
+
* - Best-effort error swallowing matches `persistToolCallCounter` in
|
|
15
|
+
* `persist-tool-calls.ts`. A stats-side failure must NEVER break the
|
|
16
|
+
* parent MCP tool call.
|
|
17
|
+
* - Resolves the latest `session_id` from `session_meta` so the wiring
|
|
18
|
+
* in `server.ts` is `setImmediate(() => emit*({...}))` — no need to
|
|
19
|
+
* plumb session ids through every handler.
|
|
20
|
+
*/
|
|
21
|
+
import { existsSync } from "node:fs";
|
|
22
|
+
import { SessionDB } from "./db.js";
|
|
23
|
+
/**
|
|
24
|
+
* Open the SessionDB at `dbPath`, find the latest session_id, and run
|
|
25
|
+
* `fn` with both. Wraps everything in try/catch so callers stay
|
|
26
|
+
* fire-and-forget.
|
|
27
|
+
*/
|
|
28
|
+
function withLatestSession(dbPath, fn) {
|
|
29
|
+
try {
|
|
30
|
+
if (!existsSync(dbPath))
|
|
31
|
+
return;
|
|
32
|
+
const sdb = new SessionDB({ dbPath });
|
|
33
|
+
try {
|
|
34
|
+
const sid = sdb.getLatestSessionId();
|
|
35
|
+
if (!sid)
|
|
36
|
+
return;
|
|
37
|
+
fn(sdb, sid);
|
|
38
|
+
}
|
|
39
|
+
finally {
|
|
40
|
+
try {
|
|
41
|
+
sdb.close();
|
|
42
|
+
}
|
|
43
|
+
catch { /* ignore */ }
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
catch {
|
|
47
|
+
// Best-effort: never break the parent MCP tool call.
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Record a `ctx_execute` / `ctx_execute_file` / `ctx_batch_execute` run.
|
|
52
|
+
* `bytesReturned` is the size of the stdout text the user actually saw —
|
|
53
|
+
* the rest of the sandbox output stayed out of context.
|
|
54
|
+
*/
|
|
55
|
+
export function emitSandboxExecuteEvent(opts) {
|
|
56
|
+
withLatestSession(opts.sessionDbPath, (sdb, sid) => {
|
|
57
|
+
sdb.insertEvent(sid, {
|
|
58
|
+
type: "sandbox-execute",
|
|
59
|
+
category: "sandbox",
|
|
60
|
+
priority: 1,
|
|
61
|
+
data: opts.toolName,
|
|
62
|
+
project_dir: "",
|
|
63
|
+
attribution_source: "server",
|
|
64
|
+
attribution_confidence: 1,
|
|
65
|
+
}, "ctx-server", undefined, { bytesReturned: opts.bytesReturned });
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Record a `ctx_index` / `trackIndexed` write — content kept out of
|
|
70
|
+
* context by being chunked into FTS5 instead of returned inline.
|
|
71
|
+
*/
|
|
72
|
+
export function emitIndexWriteEvent(opts) {
|
|
73
|
+
withLatestSession(opts.sessionDbPath, (sdb, sid) => {
|
|
74
|
+
sdb.insertEvent(sid, {
|
|
75
|
+
type: "index-write",
|
|
76
|
+
category: "sandbox",
|
|
77
|
+
priority: 1,
|
|
78
|
+
data: opts.source,
|
|
79
|
+
project_dir: "",
|
|
80
|
+
attribution_source: "server",
|
|
81
|
+
attribution_confidence: 1,
|
|
82
|
+
}, "ctx-server", undefined, { bytesAvoided: opts.bytesAvoided });
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Record a `ctx_fetch_and_index` TTL cache hit — bytes the user would
|
|
87
|
+
* have spent re-fetching the same URL within the 24h cache window.
|
|
88
|
+
*/
|
|
89
|
+
export function emitCacheHitEvent(opts) {
|
|
90
|
+
withLatestSession(opts.sessionDbPath, (sdb, sid) => {
|
|
91
|
+
sdb.insertEvent(sid, {
|
|
92
|
+
type: "cache-hit",
|
|
93
|
+
category: "cache",
|
|
94
|
+
priority: 1,
|
|
95
|
+
data: opts.source,
|
|
96
|
+
project_dir: "",
|
|
97
|
+
attribution_source: "server",
|
|
98
|
+
attribution_confidence: 1,
|
|
99
|
+
}, "ctx-server", undefined, { bytesAvoided: opts.bytesAvoided });
|
|
100
|
+
});
|
|
101
|
+
}
|
package/build/session/extract.js
CHANGED
|
@@ -17,6 +17,48 @@ function safeStringAny(value) {
|
|
|
17
17
|
return "";
|
|
18
18
|
return typeof value === "string" ? value : JSON.stringify(value);
|
|
19
19
|
}
|
|
20
|
+
function isToolError(input) {
|
|
21
|
+
const response = String(input.tool_response ?? "");
|
|
22
|
+
const isErrorFlag = input.tool_output?.isError === true || input.tool_output?.is_error === true;
|
|
23
|
+
const isBashError = input.tool_name === "Bash" &&
|
|
24
|
+
/exit code [1-9]|error:|Error:|FAIL|failed/i.test(response);
|
|
25
|
+
return isBashError || isErrorFlag;
|
|
26
|
+
}
|
|
27
|
+
function extractApplyPatchTargets(command) {
|
|
28
|
+
if (!command)
|
|
29
|
+
return [];
|
|
30
|
+
const targets = [];
|
|
31
|
+
for (const line of command.split(/\r?\n/)) {
|
|
32
|
+
if (line.startsWith("*** Add File: ")) {
|
|
33
|
+
targets.push({ path: line.slice(14).trim(), type: "file_write" });
|
|
34
|
+
continue;
|
|
35
|
+
}
|
|
36
|
+
if (line.startsWith("*** Update File: ")) {
|
|
37
|
+
targets.push({ path: line.slice(17).trim(), type: "file_edit" });
|
|
38
|
+
continue;
|
|
39
|
+
}
|
|
40
|
+
if (line.startsWith("*** Delete File: ")) {
|
|
41
|
+
targets.push({ path: line.slice(17).trim(), type: "file_edit" });
|
|
42
|
+
continue;
|
|
43
|
+
}
|
|
44
|
+
if (line.startsWith("*** Move to: ")) {
|
|
45
|
+
targets.push({ path: line.slice(13).trim(), type: "file_edit" });
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
const seen = new Set();
|
|
49
|
+
return targets.filter((target) => {
|
|
50
|
+
if (!target.path)
|
|
51
|
+
return false;
|
|
52
|
+
const key = `${target.type}:${target.path}`;
|
|
53
|
+
if (seen.has(key))
|
|
54
|
+
return false;
|
|
55
|
+
seen.add(key);
|
|
56
|
+
return true;
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
function isPlanFilePath(filePath) {
|
|
60
|
+
return /(?:^|[/\\])\.claude[/\\]plans[/\\]/.test(filePath);
|
|
61
|
+
}
|
|
20
62
|
// ── Category extractors ────────────────────────────────────────────────────
|
|
21
63
|
/**
|
|
22
64
|
* Category 1 & 2: rule + file
|
|
@@ -105,6 +147,20 @@ function extractFileAndRule(input) {
|
|
|
105
147
|
});
|
|
106
148
|
return events;
|
|
107
149
|
}
|
|
150
|
+
if (tool_name === "apply_patch") {
|
|
151
|
+
if (isToolError(input))
|
|
152
|
+
return [];
|
|
153
|
+
const patchTargets = extractApplyPatchTargets(String(tool_input["command"] ?? tool_input["patch"] ?? ""));
|
|
154
|
+
for (const target of patchTargets) {
|
|
155
|
+
events.push({
|
|
156
|
+
type: target.type,
|
|
157
|
+
category: "file",
|
|
158
|
+
data: safeString(target.path),
|
|
159
|
+
priority: 1,
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
return events;
|
|
163
|
+
}
|
|
108
164
|
// Glob — file pattern exploration
|
|
109
165
|
if (tool_name === "Glob") {
|
|
110
166
|
const pattern = String(tool_input["pattern"] ?? "");
|
|
@@ -156,12 +212,9 @@ function extractCwd(input) {
|
|
|
156
212
|
* isError flag in tool_output.
|
|
157
213
|
*/
|
|
158
214
|
function extractError(input) {
|
|
159
|
-
const {
|
|
215
|
+
const { tool_response } = input;
|
|
160
216
|
const response = String(tool_response ?? "");
|
|
161
|
-
|
|
162
|
-
const isBashError = tool_name === "Bash" &&
|
|
163
|
-
/exit code [1-9]|error:|Error:|FAIL|failed/i.test(response);
|
|
164
|
-
if (!isBashError && !isErrorFlag)
|
|
217
|
+
if (!isToolError(input))
|
|
165
218
|
return [];
|
|
166
219
|
return [{
|
|
167
220
|
type: "error_tool",
|
|
@@ -287,7 +340,7 @@ function extractPlan(input) {
|
|
|
287
340
|
// Detect plan file writes (Write/Edit to ~/.claude/plans/)
|
|
288
341
|
if (input.tool_name === "Write" || input.tool_name === "Edit") {
|
|
289
342
|
const filePath = String(input.tool_input["file_path"] ?? "");
|
|
290
|
-
if (
|
|
343
|
+
if (isPlanFilePath(filePath)) {
|
|
291
344
|
return [{
|
|
292
345
|
type: "plan_file_write",
|
|
293
346
|
category: "plan",
|
|
@@ -296,6 +349,19 @@ function extractPlan(input) {
|
|
|
296
349
|
}];
|
|
297
350
|
}
|
|
298
351
|
}
|
|
352
|
+
if (input.tool_name === "apply_patch") {
|
|
353
|
+
if (isToolError(input))
|
|
354
|
+
return [];
|
|
355
|
+
const patchTargets = extractApplyPatchTargets(String(input.tool_input["command"] ?? input.tool_input["patch"] ?? ""));
|
|
356
|
+
return patchTargets
|
|
357
|
+
.filter((target) => isPlanFilePath(target.path))
|
|
358
|
+
.map((target) => ({
|
|
359
|
+
type: "plan_file_write",
|
|
360
|
+
category: "plan",
|
|
361
|
+
data: safeString(`plan file: ${target.path.split(/[/\\]/).pop() ?? target.path}`),
|
|
362
|
+
priority: 2,
|
|
363
|
+
}));
|
|
364
|
+
}
|
|
299
365
|
return [];
|
|
300
366
|
}
|
|
301
367
|
/**
|
|
@@ -766,13 +832,10 @@ function extractData(message) {
|
|
|
766
832
|
*/
|
|
767
833
|
let lastError = null;
|
|
768
834
|
function extractErrorResolution(input) {
|
|
769
|
-
const { tool_name, tool_response
|
|
835
|
+
const { tool_name, tool_response } = input;
|
|
770
836
|
const response = String(tool_response ?? "");
|
|
771
|
-
const isErrorFlag = tool_output?.isError === true;
|
|
772
|
-
const isBashError = tool_name === "Bash" &&
|
|
773
|
-
/exit code [1-9]|error:|Error:|FAIL|failed/i.test(response);
|
|
774
837
|
// If this call is an error, store it and return
|
|
775
|
-
if (
|
|
838
|
+
if (isToolError(input)) {
|
|
776
839
|
lastError = { tool: tool_name, error: response.slice(0, 200), callsSince: 0 };
|
|
777
840
|
return [];
|
|
778
841
|
}
|
|
@@ -786,9 +849,13 @@ function extractErrorResolution(input) {
|
|
|
786
849
|
lastError = null;
|
|
787
850
|
return [];
|
|
788
851
|
}
|
|
852
|
+
const callSucceeded = !isToolError(input);
|
|
853
|
+
if (!callSucceeded)
|
|
854
|
+
return [];
|
|
789
855
|
// Check if this is a resolution: same tool, or Edit/Write after a Read error
|
|
790
856
|
const sameTool = tool_name === lastError.tool;
|
|
791
|
-
const editAfterReadError = lastError.tool === "Read"
|
|
857
|
+
const editAfterReadError = lastError.tool === "Read"
|
|
858
|
+
&& (tool_name === "Edit" || tool_name === "Write" || tool_name === "apply_patch");
|
|
792
859
|
if (sameTool || editAfterReadError) {
|
|
793
860
|
const event = {
|
|
794
861
|
type: "error_resolved",
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* purgeSession — deep module that wipes ALL session-related on-disk artifacts
|
|
3
|
+
* for a single project directory.
|
|
4
|
+
*
|
|
5
|
+
* Why a deep module instead of an inline handler:
|
|
6
|
+
* - The previous inline ctx_purge handler was 100+ lines split across three
|
|
7
|
+
* try/catch blocks. Only ONE of those blocks knew about the case-fold
|
|
8
|
+
* migration's dual-hash legacy filenames, so a partial upgrade could
|
|
9
|
+
* leak orphaned events.md / .cleanup files on macOS / Windows.
|
|
10
|
+
* - Centralizing the logic here means: one canonical sidecar list, one
|
|
11
|
+
* uniform dual-hash sweep, one place to add new file kinds.
|
|
12
|
+
*
|
|
13
|
+
* Worktree separation guarantee (carried over from the case-fold migration):
|
|
14
|
+
* Every path this module touches is derived deterministically from the input
|
|
15
|
+
* `projectDir`. There is NO `readdirSync` + glob-filter loop. Different
|
|
16
|
+
* worktrees → different physical paths → different canonical hashes →
|
|
17
|
+
* different file names → cannot collapse worktrees on disk.
|
|
18
|
+
*
|
|
19
|
+
* SQLite sidecar handling:
|
|
20
|
+
* Each `.db` file may be accompanied by `-wal` (write-ahead log) and `-shm`
|
|
21
|
+
* (shared memory index) sidecars. We unlink the triple unconditionally —
|
|
22
|
+
* missing sidecars are not an error. This matches the canonical SQLite
|
|
23
|
+
* sidecar naming used elsewhere (see refs/platforms/zed/crates/sqlez:
|
|
24
|
+
* `[main, "{main}-wal", "{main}-shm"]`).
|
|
25
|
+
*
|
|
26
|
+
* Cross-platform notes:
|
|
27
|
+
* - All paths are joined via `node:path.join` so Windows backslash
|
|
28
|
+
* separators and POSIX forward slashes both work.
|
|
29
|
+
* - On macOS / Windows (case-insensitive FS) we sweep BOTH the canonical
|
|
30
|
+
* (lowercased) and legacy (raw-cased) project-dir hash variants for the
|
|
31
|
+
* session-related kinds. On Linux the two hashes coincide, so the dual
|
|
32
|
+
* sweep collapses into a single unique-path pass.
|
|
33
|
+
*/
|
|
34
|
+
export interface PurgeOpts {
|
|
35
|
+
/**
|
|
36
|
+
* Absolute path to the project root. Drives every other path the module
|
|
37
|
+
* touches via the project-dir hash. MUST be the same string the rest of
|
|
38
|
+
* the system uses (e.g. `getProjectDir()`); otherwise the wrong DB is
|
|
39
|
+
* targeted. Worktree separation is preserved — only files matching
|
|
40
|
+
* THIS projectDir's hash are unlinked.
|
|
41
|
+
*/
|
|
42
|
+
projectDir: string;
|
|
43
|
+
/**
|
|
44
|
+
* Adapter-specific session directory (e.g. `~/.claude/context-mode/sessions`).
|
|
45
|
+
* Holds: `<hash><suffix>.db`, `<hash><suffix>-events.md`,
|
|
46
|
+
* `<hash><suffix>.cleanup`.
|
|
47
|
+
*/
|
|
48
|
+
sessionsDir: string;
|
|
49
|
+
/**
|
|
50
|
+
* Absolute path to the per-project FTS5 knowledge-base DB
|
|
51
|
+
* (e.g. `~/.claude/context-mode/content/<hash>.db`). When omitted no
|
|
52
|
+
* FTS5 wipe runs. Caller is responsible for closing any open handle
|
|
53
|
+
* BEFORE invoking purgeSession (Windows file locks).
|
|
54
|
+
*
|
|
55
|
+
* Use `contentDir` instead for new code — it dual-sweeps the canonical
|
|
56
|
+
* AND legacy raw-casing variants, mirroring the session events pattern.
|
|
57
|
+
* `storePath` remains for callers that have already pre-resolved a single
|
|
58
|
+
* absolute path and only want to wipe that exact file.
|
|
59
|
+
*/
|
|
60
|
+
storePath?: string;
|
|
61
|
+
/**
|
|
62
|
+
* Per-platform FTS5 content directory (e.g.
|
|
63
|
+
* `~/.claude/context-mode/content`). When provided, purgeSession sweeps
|
|
64
|
+
* BOTH the canonical and legacy raw-casing hash variants of the FTS5
|
|
65
|
+
* store inside this directory plus their `-wal` / `-shm` sidecars. This
|
|
66
|
+
* is the recommended input — covers a partial upgrade where the user
|
|
67
|
+
* had been writing to a legacy raw-casing FTS5 file before the case-fold
|
|
68
|
+
* migration landed.
|
|
69
|
+
*
|
|
70
|
+
* Mutually-additive with `storePath`: if both are passed, both are swept
|
|
71
|
+
* (de-duped on path). Closing FTS5 handles before invoking is still the
|
|
72
|
+
* caller's responsibility.
|
|
73
|
+
*/
|
|
74
|
+
contentDir?: string;
|
|
75
|
+
/**
|
|
76
|
+
* Legacy shared content directory at `~/.context-mode/content`. When
|
|
77
|
+
* omitted, the legacy content sweep is skipped.
|
|
78
|
+
*/
|
|
79
|
+
legacyContentDir?: string;
|
|
80
|
+
/**
|
|
81
|
+
* Hash used to locate the legacy shared content DB. Required when
|
|
82
|
+
* `legacyContentDir` is provided. Computed by the caller because the
|
|
83
|
+
* legacy code-path uses a different hash function than the canonical
|
|
84
|
+
* session DB hash.
|
|
85
|
+
*/
|
|
86
|
+
contentHash?: string;
|
|
87
|
+
}
|
|
88
|
+
export interface PurgeResult {
|
|
89
|
+
/**
|
|
90
|
+
* Human-readable labels rendered to the user by the ctx_purge handler.
|
|
91
|
+
* MUST stay backward-compatible with the existing UI strings:
|
|
92
|
+
* "knowledge base (FTS5)", "session events DB", "session events markdown".
|
|
93
|
+
* Each label appears at most once, and only when at least one matching
|
|
94
|
+
* file was actually unlinked.
|
|
95
|
+
*/
|
|
96
|
+
deleted: string[];
|
|
97
|
+
/**
|
|
98
|
+
* Every full path that was successfully `unlink`ed. Surfaced for tests
|
|
99
|
+
* and for diagnostic logging — NEVER shown to end users (the labels
|
|
100
|
+
* above carry the human story).
|
|
101
|
+
*/
|
|
102
|
+
wipedPaths: string[];
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Wipe every session-related on-disk artifact for `projectDir`.
|
|
106
|
+
*
|
|
107
|
+
* This function never throws on missing files (a fresh install is a no-op).
|
|
108
|
+
* It throws only when given an invalid argument (e.g. `legacyContentDir`
|
|
109
|
+
* without `contentHash`), which is a programmer bug not a runtime concern.
|
|
110
|
+
*/
|
|
111
|
+
export declare function purgeSession(opts: PurgeOpts): PurgeResult;
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* purgeSession — deep module that wipes ALL session-related on-disk artifacts
|
|
3
|
+
* for a single project directory.
|
|
4
|
+
*
|
|
5
|
+
* Why a deep module instead of an inline handler:
|
|
6
|
+
* - The previous inline ctx_purge handler was 100+ lines split across three
|
|
7
|
+
* try/catch blocks. Only ONE of those blocks knew about the case-fold
|
|
8
|
+
* migration's dual-hash legacy filenames, so a partial upgrade could
|
|
9
|
+
* leak orphaned events.md / .cleanup files on macOS / Windows.
|
|
10
|
+
* - Centralizing the logic here means: one canonical sidecar list, one
|
|
11
|
+
* uniform dual-hash sweep, one place to add new file kinds.
|
|
12
|
+
*
|
|
13
|
+
* Worktree separation guarantee (carried over from the case-fold migration):
|
|
14
|
+
* Every path this module touches is derived deterministically from the input
|
|
15
|
+
* `projectDir`. There is NO `readdirSync` + glob-filter loop. Different
|
|
16
|
+
* worktrees → different physical paths → different canonical hashes →
|
|
17
|
+
* different file names → cannot collapse worktrees on disk.
|
|
18
|
+
*
|
|
19
|
+
* SQLite sidecar handling:
|
|
20
|
+
* Each `.db` file may be accompanied by `-wal` (write-ahead log) and `-shm`
|
|
21
|
+
* (shared memory index) sidecars. We unlink the triple unconditionally —
|
|
22
|
+
* missing sidecars are not an error. This matches the canonical SQLite
|
|
23
|
+
* sidecar naming used elsewhere (see refs/platforms/zed/crates/sqlez:
|
|
24
|
+
* `[main, "{main}-wal", "{main}-shm"]`).
|
|
25
|
+
*
|
|
26
|
+
* Cross-platform notes:
|
|
27
|
+
* - All paths are joined via `node:path.join` so Windows backslash
|
|
28
|
+
* separators and POSIX forward slashes both work.
|
|
29
|
+
* - On macOS / Windows (case-insensitive FS) we sweep BOTH the canonical
|
|
30
|
+
* (lowercased) and legacy (raw-cased) project-dir hash variants for the
|
|
31
|
+
* session-related kinds. On Linux the two hashes coincide, so the dual
|
|
32
|
+
* sweep collapses into a single unique-path pass.
|
|
33
|
+
*/
|
|
34
|
+
import { unlinkSync } from "node:fs";
|
|
35
|
+
import { join } from "node:path";
|
|
36
|
+
import { getWorktreeSuffix, hashProjectDirCanonical, hashProjectDirLegacy, } from "./db.js";
|
|
37
|
+
/** Canonical SQLite sidecar suffixes. The empty string is the main DB. */
|
|
38
|
+
const SQLITE_SIDECARS = ["", "-wal", "-shm"];
|
|
39
|
+
/** Try to unlink one path; report success without throwing on ENOENT etc. */
|
|
40
|
+
function tryUnlink(p, wipedPaths) {
|
|
41
|
+
try {
|
|
42
|
+
unlinkSync(p);
|
|
43
|
+
wipedPaths.push(p);
|
|
44
|
+
return true;
|
|
45
|
+
}
|
|
46
|
+
catch {
|
|
47
|
+
return false;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Unlink a SQLite db at `path` plus its `-wal` / `-shm` sidecars.
|
|
52
|
+
* Returns true when the MAIN db file (not a sidecar) was removed.
|
|
53
|
+
*/
|
|
54
|
+
function tryUnlinkSqliteTriple(path, wipedPaths) {
|
|
55
|
+
let mainRemoved = false;
|
|
56
|
+
for (const suffix of SQLITE_SIDECARS) {
|
|
57
|
+
const removed = tryUnlink(`${path}${suffix}`, wipedPaths);
|
|
58
|
+
if (removed && suffix === "")
|
|
59
|
+
mainRemoved = true;
|
|
60
|
+
}
|
|
61
|
+
return mainRemoved;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Wipe every session-related on-disk artifact for `projectDir`.
|
|
65
|
+
*
|
|
66
|
+
* This function never throws on missing files (a fresh install is a no-op).
|
|
67
|
+
* It throws only when given an invalid argument (e.g. `legacyContentDir`
|
|
68
|
+
* without `contentHash`), which is a programmer bug not a runtime concern.
|
|
69
|
+
*/
|
|
70
|
+
export function purgeSession(opts) {
|
|
71
|
+
const { projectDir, sessionsDir, storePath, contentDir, legacyContentDir, contentHash } = opts;
|
|
72
|
+
const deleted = [];
|
|
73
|
+
const wipedPaths = [];
|
|
74
|
+
// ── 1. Knowledge base FTS5 store (per-platform). ──────────────────────
|
|
75
|
+
// Two input modes:
|
|
76
|
+
// - `storePath`: single absolute path; pre-resolved by caller. Wipes
|
|
77
|
+
// exactly that file plus -wal / -shm sidecars. Back-compat path.
|
|
78
|
+
// - `contentDir`: directory; purgeSession derives BOTH canonical and
|
|
79
|
+
// legacy raw-casing variants of the FTS5 store filename (matches
|
|
80
|
+
// the case-fold migration pattern from `resolveContentStorePath`)
|
|
81
|
+
// and sweeps each with sidecars. Recommended for new callers.
|
|
82
|
+
// Both inputs may be supplied; paths are de-duped via the unlink-or-fail
|
|
83
|
+
// semantics of `tryUnlinkSqliteTriple`. The "knowledge base (FTS5)"
|
|
84
|
+
// label appears at most once.
|
|
85
|
+
let storeFound = false;
|
|
86
|
+
if (storePath && tryUnlinkSqliteTriple(storePath, wipedPaths))
|
|
87
|
+
storeFound = true;
|
|
88
|
+
if (contentDir) {
|
|
89
|
+
const canonicalHash = hashProjectDirCanonical(projectDir);
|
|
90
|
+
const legacyHash = hashProjectDirLegacy(projectDir);
|
|
91
|
+
const storeHashes = canonicalHash === legacyHash
|
|
92
|
+
? [canonicalHash]
|
|
93
|
+
: [canonicalHash, legacyHash];
|
|
94
|
+
for (const h of storeHashes) {
|
|
95
|
+
const path = join(contentDir, `${h}.db`);
|
|
96
|
+
if (tryUnlinkSqliteTriple(path, wipedPaths))
|
|
97
|
+
storeFound = true;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
if (storeFound)
|
|
101
|
+
deleted.push("knowledge base (FTS5)");
|
|
102
|
+
// ── 2. Legacy shared content DB at ~/.context-mode/content/<hash>.db.
|
|
103
|
+
// Same reasoning as (1) — single hash, legacy code-path only.
|
|
104
|
+
if (legacyContentDir) {
|
|
105
|
+
if (!contentHash) {
|
|
106
|
+
throw new TypeError("purgeSession: contentHash is required when legacyContentDir is provided");
|
|
107
|
+
}
|
|
108
|
+
const legacyPath = join(legacyContentDir, `${contentHash}.db`);
|
|
109
|
+
tryUnlinkSqliteTriple(legacyPath, wipedPaths);
|
|
110
|
+
// No user-facing label — this is a silent legacy cleanup.
|
|
111
|
+
}
|
|
112
|
+
// ── 3. Session-events kinds at BOTH canonical AND legacy hashes. ─────
|
|
113
|
+
// This is the bug fix: the prior handler only dual-hashed the .db file
|
|
114
|
+
// (after migration commit a32cc29). events.md and .cleanup were left
|
|
115
|
+
// single-hash, so a casing-drift project on macOS/Windows could leak
|
|
116
|
+
// orphan files past a purge. We now sweep all three uniformly.
|
|
117
|
+
const worktreeSuffix = getWorktreeSuffix(projectDir);
|
|
118
|
+
const canonicalHash = hashProjectDirCanonical(projectDir);
|
|
119
|
+
const legacyHash = hashProjectDirLegacy(projectDir);
|
|
120
|
+
const hashes = canonicalHash === legacyHash
|
|
121
|
+
? [canonicalHash]
|
|
122
|
+
: [canonicalHash, legacyHash];
|
|
123
|
+
let sessDbFound = false;
|
|
124
|
+
let eventsFound = false;
|
|
125
|
+
for (const h of hashes) {
|
|
126
|
+
const base = join(sessionsDir, `${h}${worktreeSuffix}`);
|
|
127
|
+
if (tryUnlinkSqliteTriple(`${base}.db`, wipedPaths))
|
|
128
|
+
sessDbFound = true;
|
|
129
|
+
if (tryUnlink(`${base}-events.md`, wipedPaths))
|
|
130
|
+
eventsFound = true;
|
|
131
|
+
tryUnlink(`${base}.cleanup`, wipedPaths); // no user-facing label
|
|
132
|
+
}
|
|
133
|
+
if (sessDbFound)
|
|
134
|
+
deleted.push("session events DB");
|
|
135
|
+
if (eventsFound)
|
|
136
|
+
deleted.push("session events markdown");
|
|
137
|
+
return { deleted, wipedPaths };
|
|
138
|
+
}
|
package/build/store.d.ts
CHANGED
|
@@ -30,6 +30,13 @@ export declare class ContentStore {
|
|
|
30
30
|
constructor(dbPath?: string);
|
|
31
31
|
/** Delete this session's DB files. Call on process exit. */
|
|
32
32
|
cleanup(): void;
|
|
33
|
+
/**
|
|
34
|
+
* Register a deny-policy checker. When set, #refreshStaleSources
|
|
35
|
+
* calls it before re-reading any file_path during auto-refresh.
|
|
36
|
+
* Returning `true` causes the source to be skipped (kept in cache,
|
|
37
|
+
* not re-indexed). server.ts wires this to the Read deny patterns.
|
|
38
|
+
*/
|
|
39
|
+
setDenyChecker(fn: ((filePath: string) => boolean) | undefined): void;
|
|
33
40
|
index(options: {
|
|
34
41
|
content?: string;
|
|
35
42
|
path?: string;
|
package/build/store.js
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
*/
|
|
10
10
|
var _a;
|
|
11
11
|
import { loadDatabase, applyWALPragmas, closeDB, cleanOrphanedWALFiles, withRetry, deleteDBFiles, isSQLiteCorruptionError } from "./db-base.js";
|
|
12
|
-
import { readFileSync, readdirSync, unlinkSync, existsSync, statSync } from "node:fs";
|
|
12
|
+
import { readFileSync, readdirSync, unlinkSync, existsSync, statSync, openSync, fstatSync, closeSync } from "node:fs";
|
|
13
13
|
import { createHash } from "node:crypto";
|
|
14
14
|
import { tmpdir } from "node:os";
|
|
15
15
|
import { join } from "node:path";
|
|
@@ -290,6 +290,13 @@ function findMinSpan(positionLists) {
|
|
|
290
290
|
export class ContentStore {
|
|
291
291
|
#db;
|
|
292
292
|
#dbPath;
|
|
293
|
+
// Optional deny-policy callback. When set (by server.ts at startup),
|
|
294
|
+
// #refreshStaleSources consults it before re-reading file_path during
|
|
295
|
+
// auto-refresh. This catches policy edits between initial indexing and
|
|
296
|
+
// a later search: a file that was allowed at index time may have been
|
|
297
|
+
// added to the Read deny list afterwards. Without this hook, refresh
|
|
298
|
+
// would re-read and re-expose the file. See #442 round-3.
|
|
299
|
+
#denyChecker;
|
|
293
300
|
// ── Cached Prepared Statements ──
|
|
294
301
|
// Prepared once at construction, reused on every call to avoid
|
|
295
302
|
// re-compiling SQL on each invocation.
|
|
@@ -695,6 +702,16 @@ export class ContentStore {
|
|
|
695
702
|
this.#stmtCleanupChunksTrigram = this.#db.prepare("DELETE FROM chunks_trigram WHERE source_id IN (SELECT id FROM sources WHERE datetime(indexed_at) < datetime('now', '-' || ? || ' days'))");
|
|
696
703
|
this.#stmtCleanupSources = this.#db.prepare("DELETE FROM sources WHERE datetime(indexed_at) < datetime('now', '-' || ? || ' days')");
|
|
697
704
|
}
|
|
705
|
+
// ── Deny Policy Hook ──
|
|
706
|
+
/**
|
|
707
|
+
* Register a deny-policy checker. When set, #refreshStaleSources
|
|
708
|
+
* calls it before re-reading any file_path during auto-refresh.
|
|
709
|
+
* Returning `true` causes the source to be skipped (kept in cache,
|
|
710
|
+
* not re-indexed). server.ts wires this to the Read deny patterns.
|
|
711
|
+
*/
|
|
712
|
+
setDenyChecker(fn) {
|
|
713
|
+
this.#denyChecker = fn;
|
|
714
|
+
}
|
|
698
715
|
// ── Index ──
|
|
699
716
|
index(options) {
|
|
700
717
|
const { content, path, source } = options;
|
|
@@ -707,7 +724,31 @@ export class ContentStore {
|
|
|
707
724
|
if (!hasContent && !path) {
|
|
708
725
|
throw new Error("Either content or path must be provided");
|
|
709
726
|
}
|
|
710
|
-
|
|
727
|
+
// Read file via fd to close the TOCTOU window between the security
|
|
728
|
+
// gate (security.ts evaluateFilePath calls realpathSync) and the read
|
|
729
|
+
// here. Lexical re-read by path string allowed an attacker to swap a
|
|
730
|
+
// symlink to a denied target (e.g. ~/.ssh/id_rsa) AFTER gate passed.
|
|
731
|
+
// openSync + fstat + readFileSync(fd) binds the read to the inode
|
|
732
|
+
// captured at gate-time. fstat also rejects non-regular files
|
|
733
|
+
// (directories, character devices) which would otherwise read as ""
|
|
734
|
+
// or throw inconsistently. See #442 round-3.
|
|
735
|
+
let text;
|
|
736
|
+
if (hasContent) {
|
|
737
|
+
text = content;
|
|
738
|
+
}
|
|
739
|
+
else {
|
|
740
|
+
const fd = openSync(path, "r");
|
|
741
|
+
try {
|
|
742
|
+
const st = fstatSync(fd);
|
|
743
|
+
if (!st.isFile()) {
|
|
744
|
+
throw new Error(`refusing to index ${path}: not a regular file`);
|
|
745
|
+
}
|
|
746
|
+
text = readFileSync(fd, "utf-8");
|
|
747
|
+
}
|
|
748
|
+
finally {
|
|
749
|
+
closeSync(fd);
|
|
750
|
+
}
|
|
751
|
+
}
|
|
711
752
|
const label = source ?? path ?? "untitled";
|
|
712
753
|
const chunks = this.#chunkMarkdown(text);
|
|
713
754
|
// Stale detection: store file_path + SHA-256 for file-backed sources
|
|
@@ -1028,17 +1069,39 @@ export class ContentStore {
|
|
|
1028
1069
|
try {
|
|
1029
1070
|
if (!existsSync(src.file_path))
|
|
1030
1071
|
continue; // file deleted — keep cached results
|
|
1072
|
+
// Re-check deny policy before re-reading. The Read deny list may
|
|
1073
|
+
// have been edited after this source was originally indexed; a
|
|
1074
|
+
// file that was allowed then may now be denied. Without this
|
|
1075
|
+
// gate, refresh would happily re-read and re-expose it. #442 r3.
|
|
1076
|
+
if (this.#denyChecker && this.#denyChecker(src.file_path))
|
|
1077
|
+
continue;
|
|
1031
1078
|
const mtime = statSync(src.file_path).mtime;
|
|
1032
1079
|
const indexedAt = new Date(src.indexed_at + "Z");
|
|
1033
1080
|
if (mtime <= indexedAt)
|
|
1034
1081
|
continue; // file unchanged — fast path
|
|
1035
|
-
// mtime advanced —
|
|
1036
|
-
|
|
1082
|
+
// mtime advanced — fd-bound read for hash + indexing in one go.
|
|
1083
|
+
// Open once, fstat, read from fd. Closes the swap-mid-flight
|
|
1084
|
+
// window between hash read and re-index. #442 round-3.
|
|
1085
|
+
const fd = openSync(src.file_path, "r");
|
|
1086
|
+
let newContent;
|
|
1087
|
+
try {
|
|
1088
|
+
const st = fstatSync(fd);
|
|
1089
|
+
if (!st.isFile())
|
|
1090
|
+
continue; // skip non-regular targets
|
|
1091
|
+
newContent = readFileSync(fd, "utf-8");
|
|
1092
|
+
}
|
|
1093
|
+
finally {
|
|
1094
|
+
closeSync(fd);
|
|
1095
|
+
}
|
|
1037
1096
|
const newHash = createHash("sha256").update(newContent).digest("hex");
|
|
1038
1097
|
if (newHash === src.content_hash)
|
|
1039
1098
|
continue; // content identical — skip
|
|
1040
|
-
// File genuinely changed — re-index
|
|
1041
|
-
|
|
1099
|
+
// File genuinely changed — re-index using already-read content
|
|
1100
|
+
// (avoids a second open/read race) but preserve file_path/hash
|
|
1101
|
+
// by going through index() which stores them. Since we pass
|
|
1102
|
+
// content, index() does NOT re-read; the bytes hashed above
|
|
1103
|
+
// are exactly the bytes indexed.
|
|
1104
|
+
this.index({ content: newContent, path: src.file_path, source: src.label });
|
|
1042
1105
|
this.lastRefreshCount++;
|
|
1043
1106
|
}
|
|
1044
1107
|
catch {
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
export declare function resolveClaudeConfigDir(env?: NodeJS.ProcessEnv): string;
|
|
2
|
+
/** Resolve the global settings.json path, honoring CLAUDE_CONFIG_DIR. */
|
|
3
|
+
export declare function resolveClaudeGlobalSettingsPath(env?: NodeJS.ProcessEnv): string;
|
|
4
|
+
/**
|
|
5
|
+
* Issue #451 round-3: cross-adapter deny-policy parity.
|
|
6
|
+
*
|
|
7
|
+
* `resolveClaudeGlobalSettingsPath` hardcodes the `.claude` segment, so
|
|
8
|
+
* non-Claude adapters (cursor, codex, qwen-code, gemini-cli, jetbrains-copilot,
|
|
9
|
+
* vscode-copilot, etc.) never had their global settings consulted by the
|
|
10
|
+
* security policy reader. This helper returns the union of:
|
|
11
|
+
*
|
|
12
|
+
* 1. The currently-detected adapter's home-rooted settings.json (when the
|
|
13
|
+
* adapter is non-claude — claude is already covered by entry 2).
|
|
14
|
+
* 2. The claude global settings.json (always — defense in depth).
|
|
15
|
+
*
|
|
16
|
+
* Lazy import of `./adapters/detect.js` keeps this file free of any direct
|
|
17
|
+
* adapter dependency: the detect module itself only `import type`s adapter
|
|
18
|
+
* types at the top level (concrete adapters are loaded dynamically inside
|
|
19
|
+
* `getAdapter()`), so a static import is safe — but we use `createRequire`
|
|
20
|
+
* to make the dependency direction crystal clear and to avoid surprising
|
|
21
|
+
* future maintainers who add eager adapter imports to detect.ts.
|
|
22
|
+
*
|
|
23
|
+
* The returned array is deduplicated and order-stable: adapter-specific path
|
|
24
|
+
* first (most specific), claude global second (fallback).
|
|
25
|
+
*/
|
|
26
|
+
export declare function resolveAdapterGlobalSettingsPaths(env?: NodeJS.ProcessEnv): string[];
|