context-mode 1.0.125 → 1.0.126

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.
Files changed (38) hide show
  1. package/.claude-plugin/marketplace.json +2 -2
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/.openclaw-plugin/openclaw.plugin.json +1 -1
  4. package/.openclaw-plugin/package.json +1 -1
  5. package/build/adapters/claude-code/hooks.d.ts +10 -4
  6. package/build/adapters/claude-code/hooks.js +22 -12
  7. package/build/adapters/claude-code/index.d.ts +24 -1
  8. package/build/adapters/claude-code/index.js +67 -11
  9. package/build/adapters/types.d.ts +57 -0
  10. package/build/adapters/types.js +29 -0
  11. package/build/cli.js +38 -13
  12. package/build/util/hook-config.d.ts +24 -1
  13. package/build/util/hook-config.js +39 -2
  14. package/build/util/plugin-cache-integrity.d.ts +37 -0
  15. package/build/util/plugin-cache-integrity.js +105 -0
  16. package/cli.bundle.mjs +122 -122
  17. package/openclaw.plugin.json +1 -1
  18. package/package.json +2 -1
  19. package/scripts/plugin-cache-integrity.mjs +168 -0
  20. package/server.bundle.mjs +88 -88
  21. package/start.mjs +37 -0
  22. package/skills/UPSTREAM-CREDITS.md +0 -51
  23. package/skills/diagnose/SKILL.md +0 -122
  24. package/skills/diagnose/scripts/hitl-loop.template.sh +0 -41
  25. package/skills/grill-me/SKILL.md +0 -15
  26. package/skills/grill-with-docs/ADR-FORMAT.md +0 -47
  27. package/skills/grill-with-docs/CONTEXT-FORMAT.md +0 -77
  28. package/skills/grill-with-docs/SKILL.md +0 -93
  29. package/skills/improve-codebase-architecture/DEEPENING.md +0 -37
  30. package/skills/improve-codebase-architecture/INTERFACE-DESIGN.md +0 -44
  31. package/skills/improve-codebase-architecture/LANGUAGE.md +0 -53
  32. package/skills/improve-codebase-architecture/SKILL.md +0 -76
  33. package/skills/tdd/SKILL.md +0 -114
  34. package/skills/tdd/deep-modules.md +0 -33
  35. package/skills/tdd/interface-design.md +0 -31
  36. package/skills/tdd/mocking.md +0 -59
  37. package/skills/tdd/refactoring.md +0 -10
  38. package/skills/tdd/tests.md +0 -61
@@ -3,7 +3,7 @@
3
3
  "name": "Context Mode",
4
4
  "kind": "tool",
5
5
  "description": "OpenClaw plugin that saves 98% of your context window. Sandboxed code execution in 11 languages, FTS5 knowledge base with BM25 ranking, and intent-driven search.",
6
- "version": "1.0.125",
6
+ "version": "1.0.126",
7
7
  "sandbox": {
8
8
  "mode": "permissive",
9
9
  "filesystem_access": "full",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "context-mode",
3
- "version": "1.0.125",
3
+ "version": "1.0.126",
4
4
  "type": "module",
5
5
  "description": "MCP plugin that saves 98% of your context window. Works with Claude Code, Gemini CLI, VS Code Copilot, OpenCode, and Codex CLI. Sandboxed code execution, FTS5 knowledge base, and intent-driven search.",
6
6
  "author": "Mert Koseoğlu",
@@ -79,6 +79,7 @@
79
79
  "scripts/postinstall.mjs",
80
80
  "scripts/heal-better-sqlite3.mjs",
81
81
  "scripts/heal-installed-plugins.mjs",
82
+ "scripts/plugin-cache-integrity.mjs",
82
83
  "README.md",
83
84
  "LICENSE"
84
85
  ],
@@ -0,0 +1,168 @@
1
+ /**
2
+ * Plugin cache integrity check (Algo-D4 + Algo-D5).
3
+ *
4
+ * Algorithmic defense against #550: a partial install (interrupted npm
5
+ * install, broken marketplace pull, half-finished /ctx-upgrade) leaves
6
+ * start.mjs spawnable but a critical sibling (server.bundle.mjs,
7
+ * cli.bundle.mjs, hooks/<event>.mjs, …) missing. The MCP child then
8
+ * dies silently downstream and the user sees an opaque "MCP server
9
+ * failed to start" with no actionable signal.
10
+ *
11
+ * The expected sibling tree is DERIVED from `package.json files[]` —
12
+ * the npm publish source of truth. Adding a new entry there auto-
13
+ * extends the integrity check; no parallel hardcoded list to maintain
14
+ * (the trap that bites every project that hand-rolls "list of files
15
+ * that must exist at runtime").
16
+ *
17
+ * Two consumers:
18
+ * 1. start.mjs at boot — calls assertPluginCacheIntegrity, on !ok
19
+ * writes a structured CONTEXT_MODE_PARTIAL_INSTALL stderr block
20
+ * and exits 2. Fail-fast — the alternative is a downstream stack
21
+ * trace from `import("./server.bundle.mjs")` that hides the
22
+ * actual root cause.
23
+ * 2. src/cli.ts ctx doctor (Algo-D5) — same helper, same answer,
24
+ * surfaced as a HealthCheck so users get the diagnostic without
25
+ * restarting the MCP server.
26
+ *
27
+ * Pure JS, Node.js built-ins only. Ships in package.json files[] so
28
+ * users running off the npm tarball get the same code path the
29
+ * developer ran during `pretest`.
30
+ */
31
+ import { existsSync, readFileSync, readdirSync, statSync } from "node:fs";
32
+ import { join, relative } from "node:path";
33
+
34
+ /**
35
+ * Walk a directory recursively, returning a flat list of relative file
36
+ * paths (using `/` as separator inside the returned strings). Skips
37
+ * unreadable entries silently — the integrity check operates on what
38
+ * IS readable; missing entries are reported by the caller.
39
+ */
40
+ function listFilesRecursive(absDir, baseAbs) {
41
+ const out = [];
42
+ let entries;
43
+ try {
44
+ entries = readdirSync(absDir);
45
+ } catch {
46
+ return out; // unreadable — caller will report the parent as missing
47
+ }
48
+ for (const name of entries) {
49
+ const full = join(absDir, name);
50
+ let st;
51
+ try {
52
+ st = statSync(full);
53
+ } catch {
54
+ continue;
55
+ }
56
+ if (st.isDirectory()) {
57
+ out.push(...listFilesRecursive(full, baseAbs));
58
+ } else {
59
+ out.push(relative(baseAbs, full));
60
+ }
61
+ }
62
+ return out;
63
+ }
64
+
65
+ /**
66
+ * Compute the expected sibling tree for a given pluginRoot, derived
67
+ * from the supplied `package.json files[]` array.
68
+ *
69
+ * Algorithm:
70
+ * - Each entry in files[] is resolved against pluginRoot.
71
+ * - If it points to a directory → list every file inside recursively.
72
+ * - If it points to a file → kept as-is.
73
+ * - Entries that don't exist at probe-time are EXCLUDED from the
74
+ * manifest (they show up as `missing` in the assert step instead).
75
+ * This avoids the trap of "manifest contains paths that have never
76
+ * existed" — the manifest is a snapshot of WHAT IS, not WHAT WAS
77
+ * PUBLISHED.
78
+ *
79
+ * Returns relative paths (relative to pluginRoot). Used by both
80
+ * assertPluginCacheIntegrity and the doctor surface.
81
+ */
82
+ export function derivePluginManifest({ pkg, pluginRoot }) {
83
+ if (!pkg || !Array.isArray(pkg.files)) return [];
84
+ const manifest = new Set();
85
+ for (const entry of pkg.files) {
86
+ if (typeof entry !== "string" || !entry) continue;
87
+ const absEntry = join(pluginRoot, entry);
88
+ if (!existsSync(absEntry)) continue;
89
+ let st;
90
+ try {
91
+ st = statSync(absEntry);
92
+ } catch {
93
+ continue;
94
+ }
95
+ if (st.isDirectory()) {
96
+ for (const f of listFilesRecursive(absEntry, pluginRoot)) manifest.add(f);
97
+ } else {
98
+ manifest.add(entry);
99
+ }
100
+ }
101
+ return [...manifest];
102
+ }
103
+
104
+ /**
105
+ * REQUIRED_RUNTIME_SIBLINGS — the minimum set of files start.mjs must
106
+ * find at boot. These are the files start.mjs actively `import()`s or
107
+ * needs to re-symlink against. The check is intentionally narrower
108
+ * than the full manifest:
109
+ *
110
+ * - server.bundle.mjs / cli.bundle.mjs are produced by `npm run
111
+ * bundle`. Without server.bundle.mjs the server can't start;
112
+ * without cli.bundle.mjs `context-mode doctor` can't run.
113
+ * - hooks/{5 hook scripts}.mjs are spawned per Claude Code event.
114
+ * Missing any one produces a silent hook failure.
115
+ *
116
+ * Other files in package.json files[] (insight/, configs/, README, …)
117
+ * are not boot-critical, so missing them is a "warn"-class issue
118
+ * surfaced only via the doctor — never enough to fail-fast at boot.
119
+ */
120
+ const REQUIRED_RUNTIME_SIBLINGS = Object.freeze([
121
+ "server.bundle.mjs",
122
+ "cli.bundle.mjs",
123
+ join("hooks", "pretooluse.mjs"),
124
+ join("hooks", "posttooluse.mjs"),
125
+ join("hooks", "precompact.mjs"),
126
+ join("hooks", "sessionstart.mjs"),
127
+ join("hooks", "userpromptsubmit.mjs"),
128
+ ]);
129
+
130
+ /**
131
+ * Verify boot-critical siblings exist at pluginRoot.
132
+ *
133
+ * Returns `{ ok, missing }`. Pure — does NOT touch process.exit or
134
+ * stderr. The caller (start.mjs at boot, src/cli.ts at doctor) decides
135
+ * the failure surface (fail-fast exit 2 vs. doctor diagnostic).
136
+ *
137
+ * Uses package.json (read from pluginRoot) only as a source-of-truth
138
+ * cross-check; the actual REQUIRED list is hardcoded above to keep the
139
+ * runtime contract independent of package.json being readable. If
140
+ * package.json IS readable AND files[] omits something we require, the
141
+ * check fails — that's the "drift between contract and tarball" trap.
142
+ */
143
+ export function assertPluginCacheIntegrity({ pluginRoot }) {
144
+ const missing = [];
145
+ for (const rel of REQUIRED_RUNTIME_SIBLINGS) {
146
+ const abs = join(pluginRoot, rel);
147
+ if (!existsSync(abs)) missing.push(abs);
148
+ }
149
+ return { ok: missing.length === 0, missing };
150
+ }
151
+
152
+ /**
153
+ * Format the structured stderr block start.mjs emits when integrity
154
+ * fails. Marker line `CONTEXT_MODE_PARTIAL_INSTALL` lets external
155
+ * monitoring grep for the exact failure mode without parsing free-form
156
+ * text. Keep the format stable across versions.
157
+ */
158
+ export function formatPartialInstallReport({ pluginRoot, missing }) {
159
+ const lines = [
160
+ "CONTEXT_MODE_PARTIAL_INSTALL",
161
+ ` pluginRoot: ${pluginRoot}`,
162
+ " missing:",
163
+ ...missing.map((m) => ` - ${m}`),
164
+ " fix: rm -rf the install dir and re-pull (marketplace) or run `npm install -g context-mode` again.",
165
+ "",
166
+ ];
167
+ return lines.join("\n");
168
+ }