@mxalbert/context-mode 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/marketplace.json +33 -0
- package/.claude-plugin/plugin.json +31 -0
- package/.codex-plugin/hooks.json +65 -0
- package/.codex-plugin/mcp.json +13 -0
- package/.codex-plugin/plugin.json +31 -0
- package/.openclaw-plugin/index.ts +12 -0
- package/.openclaw-plugin/openclaw.plugin.json +38 -0
- package/.openclaw-plugin/package.json +28 -0
- package/LICENSE +94 -0
- package/README.md +1619 -0
- package/bin/statusline.mjs +378 -0
- package/build/adapters/antigravity/index.d.ts +51 -0
- package/build/adapters/antigravity/index.js +169 -0
- package/build/adapters/antigravity-cli/index.d.ts +51 -0
- package/build/adapters/antigravity-cli/index.js +342 -0
- package/build/adapters/base.d.ts +92 -0
- package/build/adapters/base.js +135 -0
- package/build/adapters/claude-code/config.d.ts +8 -0
- package/build/adapters/claude-code/config.js +8 -0
- package/build/adapters/claude-code/hooks.d.ts +106 -0
- package/build/adapters/claude-code/hooks.js +183 -0
- package/build/adapters/claude-code/index.d.ts +80 -0
- package/build/adapters/claude-code/index.js +569 -0
- package/build/adapters/claude-code-base.d.ts +49 -0
- package/build/adapters/claude-code-base.js +113 -0
- package/build/adapters/client-map.d.ts +10 -0
- package/build/adapters/client-map.js +42 -0
- package/build/adapters/codex/config.d.ts +8 -0
- package/build/adapters/codex/config.js +8 -0
- package/build/adapters/codex/hooks.d.ts +55 -0
- package/build/adapters/codex/hooks.js +64 -0
- package/build/adapters/codex/index.d.ts +87 -0
- package/build/adapters/codex/index.js +995 -0
- package/build/adapters/codex/paths.d.ts +1 -0
- package/build/adapters/codex/paths.js +12 -0
- package/build/adapters/codex/usage.d.ts +107 -0
- package/build/adapters/codex/usage.js +227 -0
- package/build/adapters/copilot-base.d.ts +94 -0
- package/build/adapters/copilot-base.js +297 -0
- package/build/adapters/copilot-cli/hooks.d.ts +33 -0
- package/build/adapters/copilot-cli/hooks.js +64 -0
- package/build/adapters/copilot-cli/index.d.ts +48 -0
- package/build/adapters/copilot-cli/index.js +341 -0
- package/build/adapters/cursor/config.d.ts +4 -0
- package/build/adapters/cursor/config.js +4 -0
- package/build/adapters/cursor/hooks.d.ts +55 -0
- package/build/adapters/cursor/hooks.js +82 -0
- package/build/adapters/cursor/index.d.ts +64 -0
- package/build/adapters/cursor/index.js +517 -0
- package/build/adapters/detect.d.ts +136 -0
- package/build/adapters/detect.js +623 -0
- package/build/adapters/gemini-cli/config.d.ts +8 -0
- package/build/adapters/gemini-cli/config.js +8 -0
- package/build/adapters/gemini-cli/hooks.d.ts +83 -0
- package/build/adapters/gemini-cli/hooks.js +115 -0
- package/build/adapters/gemini-cli/index.d.ts +80 -0
- package/build/adapters/gemini-cli/index.js +483 -0
- package/build/adapters/jetbrains-copilot/config.d.ts +8 -0
- package/build/adapters/jetbrains-copilot/config.js +8 -0
- package/build/adapters/jetbrains-copilot/hooks.d.ts +59 -0
- package/build/adapters/jetbrains-copilot/hooks.js +87 -0
- package/build/adapters/jetbrains-copilot/index.d.ts +31 -0
- package/build/adapters/jetbrains-copilot/index.js +131 -0
- package/build/adapters/kimi/config.d.ts +8 -0
- package/build/adapters/kimi/config.js +8 -0
- package/build/adapters/kimi/hooks.d.ts +28 -0
- package/build/adapters/kimi/hooks.js +34 -0
- package/build/adapters/kimi/index.d.ts +66 -0
- package/build/adapters/kimi/index.js +537 -0
- package/build/adapters/kimi/paths.d.ts +21 -0
- package/build/adapters/kimi/paths.js +52 -0
- package/build/adapters/kimi/usage.d.ts +82 -0
- package/build/adapters/kimi/usage.js +217 -0
- package/build/adapters/kiro/hooks.d.ts +61 -0
- package/build/adapters/kiro/hooks.js +94 -0
- package/build/adapters/kiro/index.d.ts +61 -0
- package/build/adapters/kiro/index.js +301 -0
- package/build/adapters/omp/index.d.ts +65 -0
- package/build/adapters/omp/index.js +182 -0
- package/build/adapters/omp/plugin.d.ts +81 -0
- package/build/adapters/omp/plugin.js +331 -0
- package/build/adapters/omp/usage.d.ts +49 -0
- package/build/adapters/omp/usage.js +110 -0
- package/build/adapters/openclaw/config.d.ts +8 -0
- package/build/adapters/openclaw/config.js +8 -0
- package/build/adapters/openclaw/hooks.d.ts +50 -0
- package/build/adapters/openclaw/hooks.js +61 -0
- package/build/adapters/openclaw/index.d.ts +74 -0
- package/build/adapters/openclaw/index.js +439 -0
- package/build/adapters/openclaw/mcp-tools.d.ts +54 -0
- package/build/adapters/openclaw/mcp-tools.js +207 -0
- package/build/adapters/openclaw/plugin.d.ts +141 -0
- package/build/adapters/openclaw/plugin.js +818 -0
- package/build/adapters/openclaw/session-db.d.ts +55 -0
- package/build/adapters/openclaw/session-db.js +88 -0
- package/build/adapters/openclaw/usage.d.ts +34 -0
- package/build/adapters/openclaw/usage.js +52 -0
- package/build/adapters/openclaw/workspace-router.d.ts +29 -0
- package/build/adapters/openclaw/workspace-router.js +64 -0
- package/build/adapters/opencode/config.d.ts +8 -0
- package/build/adapters/opencode/config.js +8 -0
- package/build/adapters/opencode/hooks.d.ts +38 -0
- package/build/adapters/opencode/hooks.js +50 -0
- package/build/adapters/opencode/index.d.ts +144 -0
- package/build/adapters/opencode/index.js +638 -0
- package/build/adapters/opencode/plugin.d.ts +249 -0
- package/build/adapters/opencode/plugin.js +1536 -0
- package/build/adapters/opencode/v2.d.ts +117 -0
- package/build/adapters/opencode/v2.js +224 -0
- package/build/adapters/opencode/zod3tov4.d.ts +1 -0
- package/build/adapters/opencode/zod3tov4.js +132 -0
- package/build/adapters/pi/extension.d.ts +81 -0
- package/build/adapters/pi/extension.js +861 -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 +268 -0
- package/build/adapters/pi/mcp-bridge.js +894 -0
- package/build/adapters/qwen-code/hooks.d.ts +26 -0
- package/build/adapters/qwen-code/hooks.js +29 -0
- package/build/adapters/qwen-code/index.d.ts +40 -0
- package/build/adapters/qwen-code/index.js +361 -0
- package/build/adapters/qwen-code/usage.d.ts +90 -0
- package/build/adapters/qwen-code/usage.js +222 -0
- package/build/adapters/types.d.ts +360 -0
- package/build/adapters/types.js +118 -0
- package/build/adapters/vscode-copilot/config.d.ts +8 -0
- package/build/adapters/vscode-copilot/config.js +8 -0
- package/build/adapters/vscode-copilot/hooks.d.ts +71 -0
- package/build/adapters/vscode-copilot/hooks.js +98 -0
- package/build/adapters/vscode-copilot/index.d.ts +32 -0
- package/build/adapters/vscode-copilot/index.js +222 -0
- package/build/adapters/zed/index.d.ts +40 -0
- package/build/adapters/zed/index.js +163 -0
- package/build/cli.d.ts +34 -0
- package/build/cli.js +1828 -0
- package/build/db-base.d.ts +250 -0
- package/build/db-base.js +756 -0
- package/build/executor.d.ts +72 -0
- package/build/executor.js +678 -0
- package/build/exit-classify.d.ts +19 -0
- package/build/exit-classify.js +12 -0
- package/build/fetch/blocks.d.ts +145 -0
- package/build/fetch/blocks.js +244 -0
- package/build/fetch/extract.d.ts +68 -0
- package/build/fetch/extract.js +123 -0
- package/build/fetch/page-store.d.ts +66 -0
- package/build/fetch/page-store.js +174 -0
- package/build/fetch-cache.d.ts +13 -0
- package/build/fetch-cache.js +15 -0
- package/build/lifecycle.d.ts +124 -0
- package/build/lifecycle.js +278 -0
- package/build/package-identity.d.ts +26 -0
- package/build/package-identity.js +28 -0
- package/build/runPool.d.ts +36 -0
- package/build/runPool.js +51 -0
- package/build/runtime.d.ts +71 -0
- package/build/runtime.js +659 -0
- package/build/search/auto-memory.d.ts +47 -0
- package/build/search/auto-memory.js +170 -0
- package/build/search/ctx-search-schema.d.ts +90 -0
- package/build/search/ctx-search-schema.js +135 -0
- package/build/search/flood-guard.d.ts +57 -0
- package/build/search/flood-guard.js +80 -0
- package/build/search/unified.d.ts +56 -0
- package/build/search/unified.js +104 -0
- package/build/security.d.ts +206 -0
- package/build/security.js +780 -0
- package/build/server.d.ts +240 -0
- package/build/server.js +5034 -0
- package/build/session/analytics.d.ts +773 -0
- package/build/session/analytics.js +2420 -0
- package/build/session/db.d.ts +468 -0
- package/build/session/db.js +1330 -0
- package/build/session/error-classifier.d.ts +87 -0
- package/build/session/error-classifier.js +303 -0
- package/build/session/event-emit.d.ts +48 -0
- package/build/session/event-emit.js +101 -0
- package/build/session/extract.d.ts +333 -0
- package/build/session/extract.js +2688 -0
- package/build/session/model-prices.json +429 -0
- package/build/session/persist-tool-calls.d.ts +54 -0
- package/build/session/persist-tool-calls.js +105 -0
- package/build/session/pricing.d.ts +64 -0
- package/build/session/pricing.js +151 -0
- package/build/session/project-attribution.d.ts +73 -0
- package/build/session/project-attribution.js +245 -0
- package/build/session/purge.d.ts +138 -0
- package/build/session/purge.js +240 -0
- package/build/session/retrieval-marker.d.ts +39 -0
- package/build/session/retrieval-marker.js +65 -0
- package/build/session/snapshot.d.ts +46 -0
- package/build/session/snapshot.js +532 -0
- package/build/store-directory.d.ts +56 -0
- package/build/store-directory.js +254 -0
- package/build/store.d.ts +147 -0
- package/build/store.js +1714 -0
- package/build/truncate.d.ts +57 -0
- package/build/truncate.js +142 -0
- package/build/types.d.ts +117 -0
- package/build/types.js +20 -0
- package/build/util/claude-config.d.ts +32 -0
- package/build/util/claude-config.js +84 -0
- package/build/util/hook-config.d.ts +27 -0
- package/build/util/hook-config.js +76 -0
- package/build/util/jsonc.d.ts +14 -0
- package/build/util/jsonc.js +104 -0
- package/build/util/plugin-cache-integrity.d.ts +51 -0
- package/build/util/plugin-cache-integrity.js +146 -0
- package/build/util/project-dir.d.ts +149 -0
- package/build/util/project-dir.js +340 -0
- package/build/util/sibling-mcp.d.ts +79 -0
- package/build/util/sibling-mcp.js +181 -0
- package/cli.bundle.mjs +1495 -0
- package/configs/antigravity/GEMINI.md +68 -0
- package/configs/antigravity/mcp_config.json +7 -0
- package/configs/antigravity-cli/hooks/hooks.json +37 -0
- package/configs/antigravity-cli/hooks.json +37 -0
- package/configs/antigravity-cli/mcp_config.json +10 -0
- package/configs/antigravity-cli/plugin.json +14 -0
- package/configs/antigravity-cli/rules/context-mode.md +77 -0
- package/configs/antigravity-cli/skills/context-mode/SKILL.md +77 -0
- package/configs/claude-code/CLAUDE.md +91 -0
- package/configs/codex/AGENTS.md +99 -0
- package/configs/codex/config.toml +8 -0
- package/configs/codex/hooks.json +47 -0
- package/configs/copilot-cli/.github/plugin/plugin.json +23 -0
- package/configs/copilot-cli/.mcp.json +12 -0
- package/configs/copilot-cli/README.md +47 -0
- package/configs/copilot-cli/hooks.json +41 -0
- package/configs/copilot-cli/skills/context-mode/SKILL.md +38 -0
- package/configs/cursor/context-mode.mdc +76 -0
- package/configs/cursor/hooks.json +21 -0
- package/configs/cursor/mcp.json +7 -0
- package/configs/gemini-cli/GEMINI.md +88 -0
- package/configs/gemini-cli/mcp.json +7 -0
- package/configs/gemini-cli/settings.json +60 -0
- package/configs/jetbrains-copilot/copilot-instructions.md +80 -0
- package/configs/jetbrains-copilot/hooks.json +16 -0
- package/configs/jetbrains-copilot/mcp.json +7 -0
- package/configs/kilo/AGENTS.md +84 -0
- package/configs/kilo/kilo.json +6 -0
- package/configs/kimi/hooks.json +54 -0
- package/configs/kiro/KIRO.md +84 -0
- package/configs/kiro/agent.json +18 -0
- package/configs/kiro/mcp.json +7 -0
- package/configs/omp/SYSTEM.md +85 -0
- package/configs/omp/mcp.json +7 -0
- package/configs/openclaw/AGENTS.md +87 -0
- package/configs/openclaw/openclaw.json +13 -0
- package/configs/opencode/AGENTS.md +84 -0
- package/configs/opencode/opencode.json +6 -0
- package/configs/pi/AGENTS.md +3 -0
- package/configs/qwen-code/QWEN.md +91 -0
- package/configs/vscode-copilot/copilot-instructions.md +80 -0
- package/configs/vscode-copilot/hooks.json +16 -0
- package/configs/vscode-copilot/mcp.json +7 -0
- package/configs/zed/AGENTS.md +68 -0
- package/hooks/antigravity-cli/payload.mjs +98 -0
- package/hooks/antigravity-cli/posttooluse.mjs +138 -0
- package/hooks/antigravity-cli/pretooluse.mjs +78 -0
- package/hooks/antigravity-cli/stop.mjs +58 -0
- package/hooks/auto-injection.mjs +102 -0
- package/hooks/cache-heal-utils.mjs +379 -0
- package/hooks/codex/platform.mjs +1 -0
- package/hooks/codex/posttooluse.mjs +66 -0
- package/hooks/codex/precompact.mjs +70 -0
- package/hooks/codex/pretooluse.mjs +42 -0
- package/hooks/codex/sessionstart.mjs +121 -0
- package/hooks/codex/stop.mjs +138 -0
- package/hooks/codex/userpromptsubmit.mjs +75 -0
- package/hooks/copilot-cli/posttooluse.mjs +79 -0
- package/hooks/copilot-cli/precompact.mjs +66 -0
- package/hooks/copilot-cli/pretooluse.mjs +41 -0
- package/hooks/copilot-cli/sessionstart.mjs +121 -0
- package/hooks/copilot-cli/stop.mjs +59 -0
- package/hooks/copilot-cli/userpromptsubmit.mjs +77 -0
- package/hooks/core/codex-caps.mjs +112 -0
- package/hooks/core/formatters.mjs +356 -0
- package/hooks/core/mcp-ready.mjs +109 -0
- package/hooks/core/platform-detect.mjs +49 -0
- package/hooks/core/routing.mjs +1050 -0
- package/hooks/core/stdin.mjs +90 -0
- package/hooks/core/tool-naming.mjs +57 -0
- package/hooks/cursor/afteragentresponse.mjs +74 -0
- package/hooks/cursor/hooks.json +31 -0
- package/hooks/cursor/posttooluse.mjs +72 -0
- package/hooks/cursor/pretooluse.mjs +26 -0
- package/hooks/cursor/sessionstart.mjs +97 -0
- package/hooks/cursor/stop.mjs +48 -0
- package/hooks/ensure-deps.mjs +254 -0
- package/hooks/formatters/claude-code.mjs +113 -0
- package/hooks/formatters/cursor.mjs +37 -0
- package/hooks/formatters/gemini-cli.mjs +55 -0
- package/hooks/formatters/vscode-copilot.mjs +55 -0
- package/hooks/gemini-cli/aftermodel.mjs +70 -0
- package/hooks/gemini-cli/aftertool.mjs +61 -0
- package/hooks/gemini-cli/beforeagent.mjs +99 -0
- package/hooks/gemini-cli/beforetool.mjs +27 -0
- package/hooks/gemini-cli/precompress.mjs +55 -0
- package/hooks/gemini-cli/sessionstart.mjs +137 -0
- package/hooks/heal-partial-install.mjs +712 -0
- package/hooks/hooks.json +143 -0
- package/hooks/jetbrains-copilot/posttooluse.mjs +61 -0
- package/hooks/jetbrains-copilot/precompact.mjs +55 -0
- package/hooks/jetbrains-copilot/pretooluse.mjs +27 -0
- package/hooks/jetbrains-copilot/sessionstart.mjs +124 -0
- package/hooks/kimi/platform.mjs +1 -0
- package/hooks/kimi/posttooluse.mjs +72 -0
- package/hooks/kimi/precompact.mjs +80 -0
- package/hooks/kimi/pretooluse.mjs +42 -0
- package/hooks/kimi/sessionend.mjs +61 -0
- package/hooks/kimi/sessionstart.mjs +113 -0
- package/hooks/kimi/stop.mjs +132 -0
- package/hooks/kimi/userpromptsubmit.mjs +90 -0
- package/hooks/kiro/agentspawn.mjs +97 -0
- package/hooks/kiro/posttooluse.mjs +51 -0
- package/hooks/kiro/pretooluse.mjs +64 -0
- package/hooks/kiro/userpromptsubmit.mjs +88 -0
- package/hooks/normalize-hooks.mjs +323 -0
- package/hooks/platform-bridge.mjs +340 -0
- package/hooks/posttooluse.mjs +225 -0
- package/hooks/precompact.mjs +100 -0
- package/hooks/pretooluse.mjs +227 -0
- package/hooks/qwen-code/platform.mjs +1 -0
- package/hooks/qwen-code/stop.mjs +168 -0
- package/hooks/routing-block.mjs +105 -0
- package/hooks/run-hook.mjs +95 -0
- package/hooks/security.bundle.mjs +2 -0
- package/hooks/session-attribution.bundle.mjs +1 -0
- package/hooks/session-db.bundle.mjs +145 -0
- package/hooks/session-directive.mjs +520 -0
- package/hooks/session-extract.bundle.mjs +3 -0
- package/hooks/session-helpers.mjs +428 -0
- package/hooks/session-loaders.mjs +448 -0
- package/hooks/session-snapshot.bundle.mjs +32 -0
- package/hooks/sessionstart.mjs +464 -0
- package/hooks/stop.mjs +82 -0
- package/hooks/suppress-stderr.mjs +25 -0
- package/hooks/userpromptsubmit.mjs +108 -0
- package/hooks/vscode-copilot/posttooluse.mjs +61 -0
- package/hooks/vscode-copilot/precompact.mjs +55 -0
- package/hooks/vscode-copilot/pretooluse.mjs +27 -0
- package/hooks/vscode-copilot/sessionstart.mjs +129 -0
- package/openclaw.plugin.json +38 -0
- package/package.json +132 -0
- package/scripts/heal-better-sqlite3.mjs +430 -0
- package/scripts/heal-installed-plugins.mjs +635 -0
- package/scripts/plugin-cache-integrity.mjs +248 -0
- package/scripts/postinstall.mjs +397 -0
- package/server.bundle.mjs +1421 -0
- package/server.js +5 -0
- package/skills/.ignore +7 -0
- package/skills/context-mode/SKILL.md +300 -0
- package/skills/context-mode/references/anti-patterns.md +283 -0
- package/skills/context-mode/references/patterns-javascript.md +298 -0
- package/skills/context-mode/references/patterns-python.md +304 -0
- package/skills/context-mode/references/patterns-shell.md +277 -0
- package/skills/ctx-doctor/SKILL.md +22 -0
- package/skills/ctx-index/SKILL.md +46 -0
- package/skills/ctx-insight/SKILL.md +24 -0
- package/skills/ctx-purge/SKILL.md +49 -0
- package/skills/ctx-search/SKILL.md +35 -0
- package/skills/ctx-stats/SKILL.md +26 -0
- package/skills/ctx-upgrade/SKILL.md +31 -0
- package/start.mjs +616 -0
|
@@ -0,0 +1,240 @@
|
|
|
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 { existsSync, unlinkSync } from "node:fs";
|
|
35
|
+
import { join } from "node:path";
|
|
36
|
+
import { loadDatabase } from "../db-base.js";
|
|
37
|
+
import { getWorktreeSuffix, hashProjectDirCanonical, hashProjectDirLegacy, SessionDB, } from "./db.js";
|
|
38
|
+
/** Canonical SQLite sidecar suffixes. The empty string is the main DB. */
|
|
39
|
+
const SQLITE_SIDECARS = ["", "-wal", "-shm"];
|
|
40
|
+
/** Try to unlink one path; report success without throwing on ENOENT etc. */
|
|
41
|
+
function tryUnlink(p, wipedPaths) {
|
|
42
|
+
try {
|
|
43
|
+
unlinkSync(p);
|
|
44
|
+
wipedPaths.push(p);
|
|
45
|
+
return true;
|
|
46
|
+
}
|
|
47
|
+
catch {
|
|
48
|
+
return false;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Unlink a SQLite db at `path` plus its `-wal` / `-shm` sidecars.
|
|
53
|
+
* Returns true when the MAIN db file (not a sidecar) was removed.
|
|
54
|
+
*/
|
|
55
|
+
function tryUnlinkSqliteTriple(path, wipedPaths) {
|
|
56
|
+
let mainRemoved = false;
|
|
57
|
+
for (const suffix of SQLITE_SIDECARS) {
|
|
58
|
+
const removed = tryUnlink(`${path}${suffix}`, wipedPaths);
|
|
59
|
+
if (removed && suffix === "")
|
|
60
|
+
mainRemoved = true;
|
|
61
|
+
}
|
|
62
|
+
return mainRemoved;
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Wipe every session-related on-disk artifact for `projectDir`.
|
|
66
|
+
*
|
|
67
|
+
* This function never throws on missing files (a fresh install is a no-op).
|
|
68
|
+
* It throws only when given an invalid argument (e.g. `legacyContentDir`
|
|
69
|
+
* without `contentHash`), which is a programmer bug not a runtime concern.
|
|
70
|
+
*/
|
|
71
|
+
export function purgeSession(opts) {
|
|
72
|
+
const { projectDir, sessionsDir, storePath, contentDir, legacyContentDir, contentHash, sessionId, scope } = opts;
|
|
73
|
+
const deleted = [];
|
|
74
|
+
const wipedPaths = [];
|
|
75
|
+
// Issue #520 — scope discipline.
|
|
76
|
+
// Resolve effective scope: explicit `scope` wins; otherwise infer
|
|
77
|
+
// "session" iff sessionId is given, else "project".
|
|
78
|
+
const effectiveScope = scope ?? (sessionId ? "session" : "project");
|
|
79
|
+
if (effectiveScope === "session" && !sessionId) {
|
|
80
|
+
throw new TypeError("purgeSession: scope:'session' requires sessionId. " +
|
|
81
|
+
"Pass scope:'project' for the legacy whole-project wipe.");
|
|
82
|
+
}
|
|
83
|
+
// ── Session-scoped path (issue #520). ─────────────────────────────────
|
|
84
|
+
// Wipe ONLY this sessionId's rows from the project's SessionDB. The DB
|
|
85
|
+
// file itself, the events.md sidecar, the FTS5 store, and the stats
|
|
86
|
+
// file are all left intact — those are project-scoped concerns. The
|
|
87
|
+
// label "session rows for <id>" appears once when at least one row was
|
|
88
|
+
// removed, mirroring the project-scoped UI contract.
|
|
89
|
+
if (effectiveScope === "session" && sessionId) {
|
|
90
|
+
const worktreeSuffix = getWorktreeSuffix(projectDir);
|
|
91
|
+
const canonicalHash = hashProjectDirCanonical(projectDir);
|
|
92
|
+
const legacyHash = hashProjectDirLegacy(projectDir);
|
|
93
|
+
const hashes = canonicalHash === legacyHash
|
|
94
|
+
? [canonicalHash]
|
|
95
|
+
: [canonicalHash, legacyHash];
|
|
96
|
+
let rowsRemoved = false;
|
|
97
|
+
for (const h of hashes) {
|
|
98
|
+
const dbPath = join(sessionsDir, `${h}${worktreeSuffix}.db`);
|
|
99
|
+
if (!existsSync(dbPath))
|
|
100
|
+
continue;
|
|
101
|
+
let db = null;
|
|
102
|
+
try {
|
|
103
|
+
db = new SessionDB({ dbPath });
|
|
104
|
+
const before = db.getEvents(sessionId).length;
|
|
105
|
+
db.deleteSession(sessionId);
|
|
106
|
+
if (before > 0)
|
|
107
|
+
rowsRemoved = true;
|
|
108
|
+
}
|
|
109
|
+
catch {
|
|
110
|
+
// Best-effort — corrupt DB is logged elsewhere; do not block purge.
|
|
111
|
+
}
|
|
112
|
+
finally {
|
|
113
|
+
// close() releases the handle WITHOUT deleting the file —
|
|
114
|
+
// this is what makes the scoped wipe non-destructive at the
|
|
115
|
+
// file-system level. Using cleanup() here would erase the
|
|
116
|
+
// entire DB (main + WAL + SHM), defeating per-session scope.
|
|
117
|
+
try {
|
|
118
|
+
db?.close();
|
|
119
|
+
}
|
|
120
|
+
catch { /* best effort */ }
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
if (rowsRemoved)
|
|
124
|
+
deleted.push(`session rows for ${sessionId}`);
|
|
125
|
+
// Per-session FTS5 chunk wipe. The chunks table has a `session_id
|
|
126
|
+
// UNINDEXED` column (src/store.ts schema). The public index() path
|
|
127
|
+
// currently inserts NULL — but a future per-session-tagged path
|
|
128
|
+
// (e.g. tool-call indexing keyed to a session) will populate it,
|
|
129
|
+
// and the SQL contract here keeps that future correct from day one.
|
|
130
|
+
// Today this is a safe no-op against existing data.
|
|
131
|
+
//
|
|
132
|
+
// Caller is responsible for closing any persistent ContentStore
|
|
133
|
+
// handle BEFORE invoking purgeSession (Windows file lock). The
|
|
134
|
+
// ctx_purge handler does this via _store?.cleanup() before delegating.
|
|
135
|
+
const ftsTargets = [];
|
|
136
|
+
if (storePath && existsSync(storePath))
|
|
137
|
+
ftsTargets.push(storePath);
|
|
138
|
+
if (contentDir) {
|
|
139
|
+
const canonicalH = hashProjectDirCanonical(projectDir);
|
|
140
|
+
const legacyH = hashProjectDirLegacy(projectDir);
|
|
141
|
+
const hh = canonicalH === legacyH ? [canonicalH] : [canonicalH, legacyH];
|
|
142
|
+
for (const h of hh) {
|
|
143
|
+
const p = join(contentDir, `${h}.db`);
|
|
144
|
+
if (existsSync(p) && !ftsTargets.includes(p))
|
|
145
|
+
ftsTargets.push(p);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
let chunksRemoved = false;
|
|
149
|
+
for (const path of ftsTargets) {
|
|
150
|
+
try {
|
|
151
|
+
const Database = loadDatabase();
|
|
152
|
+
const fts = new Database(path, { timeout: 30000 });
|
|
153
|
+
try {
|
|
154
|
+
const before = fts.prepare("SELECT COUNT(*) AS c FROM chunks WHERE session_id = ?").get(sessionId).c;
|
|
155
|
+
fts.prepare("DELETE FROM chunks WHERE session_id = ?").run(sessionId);
|
|
156
|
+
fts.prepare("DELETE FROM chunks_trigram WHERE session_id = ?").run(sessionId);
|
|
157
|
+
if (before > 0)
|
|
158
|
+
chunksRemoved = true;
|
|
159
|
+
}
|
|
160
|
+
finally {
|
|
161
|
+
try {
|
|
162
|
+
fts.close();
|
|
163
|
+
}
|
|
164
|
+
catch { /* best effort */ }
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
catch {
|
|
168
|
+
// Best-effort — schema mismatch / corrupt DB / missing FTS5 must not
|
|
169
|
+
// block the per-session SessionDB wipe that already succeeded.
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
if (chunksRemoved)
|
|
173
|
+
deleted.push(`FTS5 chunks for ${sessionId}`);
|
|
174
|
+
return { deleted, wipedPaths };
|
|
175
|
+
}
|
|
176
|
+
// ── 1. Knowledge base FTS5 store (per-platform). ──────────────────────
|
|
177
|
+
// Two input modes:
|
|
178
|
+
// - `storePath`: single absolute path; pre-resolved by caller. Wipes
|
|
179
|
+
// exactly that file plus -wal / -shm sidecars. Back-compat path.
|
|
180
|
+
// - `contentDir`: directory; purgeSession derives BOTH canonical and
|
|
181
|
+
// legacy raw-casing variants of the FTS5 store filename (matches
|
|
182
|
+
// the case-fold migration pattern from `resolveContentStorePath`)
|
|
183
|
+
// and sweeps each with sidecars. Recommended for new callers.
|
|
184
|
+
// Both inputs may be supplied; paths are de-duped via the unlink-or-fail
|
|
185
|
+
// semantics of `tryUnlinkSqliteTriple`. The "knowledge base (FTS5)"
|
|
186
|
+
// label appears at most once.
|
|
187
|
+
let storeFound = false;
|
|
188
|
+
if (storePath && tryUnlinkSqliteTriple(storePath, wipedPaths))
|
|
189
|
+
storeFound = true;
|
|
190
|
+
if (contentDir) {
|
|
191
|
+
const canonicalHash = hashProjectDirCanonical(projectDir);
|
|
192
|
+
const legacyHash = hashProjectDirLegacy(projectDir);
|
|
193
|
+
const storeHashes = canonicalHash === legacyHash
|
|
194
|
+
? [canonicalHash]
|
|
195
|
+
: [canonicalHash, legacyHash];
|
|
196
|
+
for (const h of storeHashes) {
|
|
197
|
+
const path = join(contentDir, `${h}.db`);
|
|
198
|
+
if (tryUnlinkSqliteTriple(path, wipedPaths))
|
|
199
|
+
storeFound = true;
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
if (storeFound)
|
|
203
|
+
deleted.push("knowledge base (FTS5)");
|
|
204
|
+
// ── 2. Legacy shared content DB at ~/.context-mode/content/<hash>.db.
|
|
205
|
+
// Same reasoning as (1) — single hash, legacy code-path only.
|
|
206
|
+
if (legacyContentDir) {
|
|
207
|
+
if (!contentHash) {
|
|
208
|
+
throw new TypeError("purgeSession: contentHash is required when legacyContentDir is provided");
|
|
209
|
+
}
|
|
210
|
+
const legacyPath = join(legacyContentDir, `${contentHash}.db`);
|
|
211
|
+
tryUnlinkSqliteTriple(legacyPath, wipedPaths);
|
|
212
|
+
// No user-facing label — this is a silent legacy cleanup.
|
|
213
|
+
}
|
|
214
|
+
// ── 3. Session-events kinds at BOTH canonical AND legacy hashes. ─────
|
|
215
|
+
// This is the bug fix: the prior handler only dual-hashed the .db file
|
|
216
|
+
// (after migration commit a32cc29). events.md and .cleanup were left
|
|
217
|
+
// single-hash, so a casing-drift project on macOS/Windows could leak
|
|
218
|
+
// orphan files past a purge. We now sweep all three uniformly.
|
|
219
|
+
const worktreeSuffix = getWorktreeSuffix(projectDir);
|
|
220
|
+
const canonicalHash = hashProjectDirCanonical(projectDir);
|
|
221
|
+
const legacyHash = hashProjectDirLegacy(projectDir);
|
|
222
|
+
const hashes = canonicalHash === legacyHash
|
|
223
|
+
? [canonicalHash]
|
|
224
|
+
: [canonicalHash, legacyHash];
|
|
225
|
+
let sessDbFound = false;
|
|
226
|
+
let eventsFound = false;
|
|
227
|
+
for (const h of hashes) {
|
|
228
|
+
const base = join(sessionsDir, `${h}${worktreeSuffix}`);
|
|
229
|
+
if (tryUnlinkSqliteTriple(`${base}.db`, wipedPaths))
|
|
230
|
+
sessDbFound = true;
|
|
231
|
+
if (tryUnlink(`${base}-events.md`, wipedPaths))
|
|
232
|
+
eventsFound = true;
|
|
233
|
+
tryUnlink(`${base}.cleanup`, wipedPaths); // no user-facing label
|
|
234
|
+
}
|
|
235
|
+
if (sessDbFound)
|
|
236
|
+
deleted.push("session events DB");
|
|
237
|
+
if (eventsFound)
|
|
238
|
+
deleted.push("session events markdown");
|
|
239
|
+
return { deleted, wipedPaths };
|
|
240
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Server→hook bridge for the retrieval ("With context-mode") byte count.
|
|
3
|
+
*
|
|
4
|
+
* WHY THIS EXISTS — context-mode's OWN MCP retrieval tools (ctx_search /
|
|
5
|
+
* ctx_fetch_and_index) never fire a PostToolUse hook for the plugin's own
|
|
6
|
+
* server, so the hook-side `extractMcpToolCall` path can never observe them
|
|
7
|
+
* (verified empirically: 0 `mcp_tool_call` events locally, bytes_retrieved
|
|
8
|
+
* 0/124454 in production D1). The MCP server, however, measures each
|
|
9
|
+
* retrieval response's byte length directly.
|
|
10
|
+
*
|
|
11
|
+
* The server appends that count to a tmp marker keyed by the session DB
|
|
12
|
+
* *basename* — the one identifier the server process and the hook process
|
|
13
|
+
* both resolve reliably (CLAUDE_SESSION_ID is not guaranteed in the server
|
|
14
|
+
* env; the per-project session DB path is). The next PostToolUse fire — which
|
|
15
|
+
* DOES run for ordinary tools (Bash/Read/Edit) — consumes the marker and
|
|
16
|
+
* emits a forwardable event carrying `bytes_retrieved`. Mirrors the existing
|
|
17
|
+
* redirect / latency / rejected marker handshake in posttooluse.mjs.
|
|
18
|
+
*/
|
|
19
|
+
/**
|
|
20
|
+
* Tmp marker path for a session DB. Keyed by basename so the server (which
|
|
21
|
+
* holds the DB path via getSessionDbPath) and the hook (getSessionDBPath)
|
|
22
|
+
* derive the SAME file. Session DB filenames embed the worktree hash
|
|
23
|
+
* (`<hash>__<suffix>.db`), so basename collisions across projects are
|
|
24
|
+
* negligible.
|
|
25
|
+
*/
|
|
26
|
+
export declare function retrievalMarkerPath(sessionDbPath: string, tmpDir?: string): string;
|
|
27
|
+
/**
|
|
28
|
+
* Record one retrieval's response byte count. Positive-only (a 0-byte or
|
|
29
|
+
* failed retrieval is not a context cost). Append-only so several retrievals
|
|
30
|
+
* between two hook fires accumulate. Best-effort — never throws into the
|
|
31
|
+
* MCP response path.
|
|
32
|
+
*/
|
|
33
|
+
export declare function appendRetrievalBytes(sessionDbPath: string, bytes: number, tmpDir?: string): void;
|
|
34
|
+
/**
|
|
35
|
+
* Sum every recorded retrieval and delete the marker (consume-once) so the
|
|
36
|
+
* next PostToolUse fire cannot re-forward the same bytes. Returns 0 when no
|
|
37
|
+
* marker exists (phantom-event guard).
|
|
38
|
+
*/
|
|
39
|
+
export declare function consumeRetrievalBytes(sessionDbPath: string, tmpDir?: string): number;
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Server→hook bridge for the retrieval ("With context-mode") byte count.
|
|
3
|
+
*
|
|
4
|
+
* WHY THIS EXISTS — context-mode's OWN MCP retrieval tools (ctx_search /
|
|
5
|
+
* ctx_fetch_and_index) never fire a PostToolUse hook for the plugin's own
|
|
6
|
+
* server, so the hook-side `extractMcpToolCall` path can never observe them
|
|
7
|
+
* (verified empirically: 0 `mcp_tool_call` events locally, bytes_retrieved
|
|
8
|
+
* 0/124454 in production D1). The MCP server, however, measures each
|
|
9
|
+
* retrieval response's byte length directly.
|
|
10
|
+
*
|
|
11
|
+
* The server appends that count to a tmp marker keyed by the session DB
|
|
12
|
+
* *basename* — the one identifier the server process and the hook process
|
|
13
|
+
* both resolve reliably (CLAUDE_SESSION_ID is not guaranteed in the server
|
|
14
|
+
* env; the per-project session DB path is). The next PostToolUse fire — which
|
|
15
|
+
* DOES run for ordinary tools (Bash/Read/Edit) — consumes the marker and
|
|
16
|
+
* emits a forwardable event carrying `bytes_retrieved`. Mirrors the existing
|
|
17
|
+
* redirect / latency / rejected marker handshake in posttooluse.mjs.
|
|
18
|
+
*/
|
|
19
|
+
import { appendFileSync, readFileSync, rmSync } from "node:fs";
|
|
20
|
+
import { tmpdir } from "node:os";
|
|
21
|
+
import { basename, join } from "node:path";
|
|
22
|
+
/**
|
|
23
|
+
* Tmp marker path for a session DB. Keyed by basename so the server (which
|
|
24
|
+
* holds the DB path via getSessionDbPath) and the hook (getSessionDBPath)
|
|
25
|
+
* derive the SAME file. Session DB filenames embed the worktree hash
|
|
26
|
+
* (`<hash>__<suffix>.db`), so basename collisions across projects are
|
|
27
|
+
* negligible.
|
|
28
|
+
*/
|
|
29
|
+
export function retrievalMarkerPath(sessionDbPath, tmpDir = tmpdir()) {
|
|
30
|
+
return join(tmpDir, `context-mode-retrieval-${basename(sessionDbPath)}.txt`);
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Record one retrieval's response byte count. Positive-only (a 0-byte or
|
|
34
|
+
* failed retrieval is not a context cost). Append-only so several retrievals
|
|
35
|
+
* between two hook fires accumulate. Best-effort — never throws into the
|
|
36
|
+
* MCP response path.
|
|
37
|
+
*/
|
|
38
|
+
export function appendRetrievalBytes(sessionDbPath, bytes, tmpDir) {
|
|
39
|
+
if (!Number.isFinite(bytes) || bytes <= 0)
|
|
40
|
+
return;
|
|
41
|
+
try {
|
|
42
|
+
appendFileSync(retrievalMarkerPath(sessionDbPath, tmpDir), `${Math.floor(bytes)}\n`);
|
|
43
|
+
}
|
|
44
|
+
catch { /* best-effort — never block the MCP response */ }
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Sum every recorded retrieval and delete the marker (consume-once) so the
|
|
48
|
+
* next PostToolUse fire cannot re-forward the same bytes. Returns 0 when no
|
|
49
|
+
* marker exists (phantom-event guard).
|
|
50
|
+
*/
|
|
51
|
+
export function consumeRetrievalBytes(sessionDbPath, tmpDir) {
|
|
52
|
+
const path = retrievalMarkerPath(sessionDbPath, tmpDir);
|
|
53
|
+
let total = 0;
|
|
54
|
+
try {
|
|
55
|
+
const raw = readFileSync(path, "utf8");
|
|
56
|
+
for (const line of raw.split("\n")) {
|
|
57
|
+
const n = Number.parseInt(line, 10);
|
|
58
|
+
if (Number.isFinite(n) && n > 0)
|
|
59
|
+
total += n;
|
|
60
|
+
}
|
|
61
|
+
rmSync(path, { force: true });
|
|
62
|
+
}
|
|
63
|
+
catch { /* no marker — phantom-event guard */ }
|
|
64
|
+
return total;
|
|
65
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Snapshot builder — converts stored SessionEvents into a reference-based
|
|
3
|
+
* XML resume snapshot.
|
|
4
|
+
*
|
|
5
|
+
* Pure functions only. No database access, no file system, no side effects.
|
|
6
|
+
*
|
|
7
|
+
* The output XML is injected into the LLM's context after a compact event to
|
|
8
|
+
* restore session awareness. Instead of truncated inline data, each section
|
|
9
|
+
* contains a natural summary plus a runnable search tool call that retrieves
|
|
10
|
+
* full details from the indexed knowledge base on demand.
|
|
11
|
+
*
|
|
12
|
+
* Zero truncation. Zero information loss. Full data lives in SessionDB;
|
|
13
|
+
* the snapshot is a table of contents.
|
|
14
|
+
*/
|
|
15
|
+
/** Stored event as read from SessionDB. */
|
|
16
|
+
export interface StoredEvent {
|
|
17
|
+
type: string;
|
|
18
|
+
category: string;
|
|
19
|
+
data: string;
|
|
20
|
+
priority: number;
|
|
21
|
+
created_at?: string;
|
|
22
|
+
}
|
|
23
|
+
export interface BuildSnapshotOpts {
|
|
24
|
+
maxBytes?: number;
|
|
25
|
+
compactCount?: number;
|
|
26
|
+
searchTool?: string;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Render <task_state> from task events.
|
|
30
|
+
* Reconstructs the full task list from create/update events,
|
|
31
|
+
* filters out completed tasks, and renders only pending/in-progress work.
|
|
32
|
+
*
|
|
33
|
+
* TaskCreate events have `{ subject }`, TaskUpdate events have `{ taskId, status }`.
|
|
34
|
+
* Match by chronological order: creates[0] -> lowest taskId from updates.
|
|
35
|
+
*/
|
|
36
|
+
export declare function renderTaskState(taskEvents: StoredEvent[]): string;
|
|
37
|
+
/**
|
|
38
|
+
* Build a reference-based resume snapshot XML string from stored session events.
|
|
39
|
+
*
|
|
40
|
+
* Algorithm:
|
|
41
|
+
* 1. Group events by category
|
|
42
|
+
* 2. For each non-empty category, build a summary section with a runnable
|
|
43
|
+
* search tool call containing exact queries for full details
|
|
44
|
+
* 3. Assemble ALL non-empty sections — no priority dropping, no byte budget
|
|
45
|
+
*/
|
|
46
|
+
export declare function buildResumeSnapshot(events: StoredEvent[], opts?: BuildSnapshotOpts): string;
|