clawvault 3.5.0 → 3.5.2

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 (45) hide show
  1. package/README.md +33 -25
  2. package/bin/command-registration.test.js +179 -0
  3. package/bin/command-runtime.test.js +154 -0
  4. package/bin/help-contract.test.js +55 -0
  5. package/bin/register-config-route-commands.test.js +121 -0
  6. package/bin/register-core-commands.test.js +80 -0
  7. package/bin/register-kanban-commands.test.js +83 -0
  8. package/bin/register-project-commands.test.js +206 -0
  9. package/bin/register-query-commands.test.js +80 -0
  10. package/bin/register-resilience-commands.test.js +81 -0
  11. package/bin/register-task-commands.test.js +69 -0
  12. package/bin/register-template-commands.test.js +87 -0
  13. package/bin/test-helpers/cli-command-fixtures.js +120 -0
  14. package/dashboard/lib/graph-diff.test.js +75 -0
  15. package/dashboard/lib/vault-parser.test.js +254 -0
  16. package/dist/{chunk-DCF4KMFD.js → chunk-DPK6P6BO.js} +3 -3
  17. package/dist/{chunk-BLQXXX7Q.js → chunk-FNFP7N6A.js} +2 -2
  18. package/dist/{chunk-JI7VUQV7.js → chunk-IFUHSHN3.js} +146 -118
  19. package/dist/{chunk-QFWERBDP.js → chunk-J6DW6HBX.js} +1 -1
  20. package/dist/{chunk-VXAGOLDP.js → chunk-LCBHM3D6.js} +1 -1
  21. package/dist/{chunk-HGDDW24U.js → chunk-NTQD55S3.js} +3 -3
  22. package/dist/{chunk-QUFQBAHP.js → chunk-P35SHNAU.js} +93 -147
  23. package/dist/cli/index.js +8 -8
  24. package/dist/commands/compat.js +1 -1
  25. package/dist/commands/inject.js +2 -2
  26. package/dist/commands/maintain.js +2 -2
  27. package/dist/commands/observe.js +4 -4
  28. package/dist/commands/rebuild.js +3 -3
  29. package/dist/commands/replay.js +4 -4
  30. package/dist/commands/sleep.js +5 -5
  31. package/dist/commands/status.js +3 -3
  32. package/dist/index.d.ts +2 -2
  33. package/dist/index.js +17 -17
  34. package/dist/{openclaw-plugin--gqA2BZw.d.ts → openclaw-plugin-9M9qCZgl.d.ts} +2 -2
  35. package/dist/openclaw-plugin.d.ts +1 -1
  36. package/dist/openclaw-plugin.js +6 -1
  37. package/package.json +4 -26
  38. package/CHANGELOG.md +0 -543
  39. package/SKILL.md +0 -369
  40. package/docs/clawhub-security-release-playbook.md +0 -75
  41. package/docs/getting-started/installation.md +0 -99
  42. package/docs/openclaw-plugin-usage.md +0 -152
  43. package/dist/{chunk-7SWP5FKU.js → chunk-FSYISBTU.js} +4 -4
  44. package/dist/{chunk-D5U3Q4N5.js → chunk-IOKLQR4W.js} +4 -4
  45. package/dist/{chunk-OFOCU2V4.js → chunk-QL34TMGN.js} +3 -3
@@ -4,18 +4,8 @@ import * as path from "path";
4
4
  import matter from "gray-matter";
5
5
  import { spawnSync } from "child_process";
6
6
  import { fileURLToPath } from "url";
7
- var REQUIRED_OPENCLAW_PLUGIN_PATH = "./openclaw.plugin.json";
8
- var REQUIRED_OPENCLAW_EXTENSION = "./dist/openclaw-plugin.js";
9
- var LEGACY_HOOK_DIR = "hooks/clawvault";
10
- var FORBIDDEN_PACKAGE_FILE_ENTRIES = [
11
- "hooks",
12
- "src",
13
- "tests",
14
- "testdata",
15
- "benchmarks",
16
- "eval",
17
- "autoresearch"
18
- ];
7
+ var REQUIRED_HOOK_EVENTS = ["gateway:startup", "command:new", "session:start"];
8
+ var REQUIRED_HOOK_BIN = "clawvault";
19
9
  function readOptionalFile(filePath) {
20
10
  try {
21
11
  if (!fs.existsSync(filePath)) return null;
@@ -44,17 +34,6 @@ function resolveProjectFile(relativePath, baseDir) {
44
34
  }
45
35
  return path.resolve(findPackageRoot(), relativePath);
46
36
  }
47
- function loadPackageJson(options) {
48
- const packageRaw = readOptionalFile(resolveProjectFile("package.json", options.baseDir));
49
- if (!packageRaw) {
50
- return { parsed: null, error: "package.json not found" };
51
- }
52
- try {
53
- return { parsed: JSON.parse(packageRaw) };
54
- } catch (err) {
55
- return { parsed: null, error: err?.message || "Unable to parse package.json" };
56
- }
57
- }
58
37
  function checkOpenClawCli() {
59
38
  const result = spawnSync("openclaw", ["--version"], { stdio: "ignore" });
60
39
  if (result.error) {
@@ -62,7 +41,7 @@ function checkOpenClawCli() {
62
41
  label: "openclaw CLI available",
63
42
  status: "warn",
64
43
  detail: "openclaw binary not found",
65
- hint: "Install OpenClaw CLI to enable runtime validation."
44
+ hint: "Install OpenClaw CLI to enable hook runtime validation."
66
45
  };
67
46
  }
68
47
  if (typeof result.status === "number" && result.status !== 0) {
@@ -83,69 +62,166 @@ function checkOpenClawCli() {
83
62
  }
84
63
  return { label: "openclaw CLI available", status: "ok" };
85
64
  }
86
- function checkPackagePluginRegistration(options) {
87
- const packageJson = loadPackageJson(options);
88
- if (!packageJson.parsed) {
65
+ function checkPackageHookRegistration(options) {
66
+ const packageRaw = readOptionalFile(resolveProjectFile("package.json", options.baseDir));
67
+ if (!packageRaw) {
89
68
  return {
90
- label: "package plugin registration",
69
+ label: "package hook registration",
91
70
  status: "error",
92
- detail: packageJson.error
71
+ detail: "package.json not found"
93
72
  };
94
73
  }
95
- const openclaw = packageJson.parsed.openclaw;
96
- const pluginPath = typeof openclaw?.plugin === "string" ? openclaw.plugin : "";
97
- if (pluginPath === REQUIRED_OPENCLAW_PLUGIN_PATH) {
74
+ try {
75
+ const parsed = JSON.parse(packageRaw);
76
+ const registeredHooks = parsed.openclaw?.hooks ?? [];
77
+ if (registeredHooks.includes("./hooks/clawvault")) {
78
+ return {
79
+ label: "package hook registration",
80
+ status: "ok",
81
+ detail: "./hooks/clawvault"
82
+ };
83
+ }
98
84
  return {
99
- label: "package plugin registration",
100
- status: "ok",
101
- detail: pluginPath
85
+ label: "package hook registration",
86
+ status: "error",
87
+ detail: "Missing ./hooks/clawvault in package openclaw.hooks"
88
+ };
89
+ } catch (err) {
90
+ return {
91
+ label: "package hook registration",
92
+ status: "error",
93
+ detail: err?.message || "Unable to parse package.json"
102
94
  };
103
95
  }
104
- return {
105
- label: "package plugin registration",
106
- status: "error",
107
- detail: `Missing openclaw.plugin=${REQUIRED_OPENCLAW_PLUGIN_PATH}`,
108
- hint: `Set package.json openclaw.plugin to "${REQUIRED_OPENCLAW_PLUGIN_PATH}".`
109
- };
110
96
  }
111
- function checkPackageExtensionRegistration(options) {
112
- const packageJson = loadPackageJson(options);
113
- if (!packageJson.parsed) {
97
+ function checkHookManifest(options) {
98
+ const hookRaw = readOptionalFile(resolveProjectFile("hooks/clawvault/HOOK.md", options.baseDir));
99
+ if (!hookRaw) {
100
+ return {
101
+ label: "hook manifest",
102
+ status: "error",
103
+ detail: "HOOK.md not found"
104
+ };
105
+ }
106
+ try {
107
+ const parsed = matter(hookRaw);
108
+ const openclaw = parsed.data?.metadata?.openclaw;
109
+ const events = Array.isArray(openclaw?.events) ? openclaw?.events ?? [] : [];
110
+ const missingEvents = REQUIRED_HOOK_EVENTS.filter((event) => !events.includes(event));
111
+ if (missingEvents.length === 0) {
112
+ return {
113
+ label: "hook manifest events",
114
+ status: "ok",
115
+ detail: events.join(", ")
116
+ };
117
+ }
118
+ return {
119
+ label: "hook manifest events",
120
+ status: "error",
121
+ detail: `Missing events: ${missingEvents.join(", ")}`
122
+ };
123
+ } catch (err) {
114
124
  return {
115
- label: "package extension registration",
125
+ label: "hook manifest events",
116
126
  status: "error",
117
- detail: packageJson.error
127
+ detail: err?.message || "Unable to parse HOOK.md frontmatter"
118
128
  };
119
129
  }
120
- const openclaw = packageJson.parsed.openclaw;
121
- const extensions = Array.isArray(openclaw?.extensions) ? openclaw.extensions : [];
122
- if (extensions.includes(REQUIRED_OPENCLAW_EXTENSION)) {
130
+ }
131
+ function checkHookManifestRequirements(options) {
132
+ const hookRaw = readOptionalFile(resolveProjectFile("hooks/clawvault/HOOK.md", options.baseDir));
133
+ if (!hookRaw) {
123
134
  return {
124
- label: "package extension registration",
125
- status: "ok",
126
- detail: REQUIRED_OPENCLAW_EXTENSION
135
+ label: "hook manifest requirements",
136
+ status: "error",
137
+ detail: "HOOK.md not found"
127
138
  };
128
139
  }
129
- return {
130
- label: "package extension registration",
131
- status: "error",
132
- detail: `Missing ${REQUIRED_OPENCLAW_EXTENSION} in package openclaw.extensions`,
133
- hint: `Add "${REQUIRED_OPENCLAW_EXTENSION}" to package.json openclaw.extensions.`
134
- };
140
+ try {
141
+ const parsed = matter(hookRaw);
142
+ const requiresBins = parsed.data?.metadata?.openclaw?.requires?.bins;
143
+ const bins = Array.isArray(requiresBins) ? requiresBins : [];
144
+ if (bins.includes(REQUIRED_HOOK_BIN)) {
145
+ return {
146
+ label: "hook manifest requirements",
147
+ status: "ok",
148
+ detail: `bins: ${bins.join(", ")}`
149
+ };
150
+ }
151
+ return {
152
+ label: "hook manifest requirements",
153
+ status: "warn",
154
+ detail: `Missing required hook bin "${REQUIRED_HOOK_BIN}"`,
155
+ hint: 'Add metadata.openclaw.requires.bins: ["clawvault"] to hooks/clawvault/HOOK.md.'
156
+ };
157
+ } catch (err) {
158
+ return {
159
+ label: "hook manifest requirements",
160
+ status: "error",
161
+ detail: err?.message || "Unable to parse HOOK.md frontmatter"
162
+ };
163
+ }
164
+ }
165
+ function checkHookHandlerSafety(options) {
166
+ const handlerRaw = readOptionalFile(resolveProjectFile("hooks/clawvault/handler.js", options.baseDir));
167
+ if (!handlerRaw) {
168
+ return {
169
+ label: "hook handler script",
170
+ status: "error",
171
+ detail: "handler.js not found"
172
+ };
173
+ }
174
+ const usesExecFileSync = handlerRaw.includes("execFileSync");
175
+ const usesExecSync = /\bexecSync\b/.test(handlerRaw);
176
+ const enablesShell = /\bshell\s*:\s*true\b/.test(handlerRaw);
177
+ const delegatesAutoProfile = /['"]--profile['"]\s*,\s*['"]auto['"]/.test(handlerRaw);
178
+ const violations = [];
179
+ if (!usesExecFileSync || usesExecSync) {
180
+ violations.push("execFileSync-only execution path");
181
+ }
182
+ if (enablesShell) {
183
+ violations.push("shell:false execution option");
184
+ }
185
+ if (!delegatesAutoProfile) {
186
+ violations.push("shared context profile delegation (--profile auto)");
187
+ }
188
+ if (violations.length > 0) {
189
+ return {
190
+ label: "hook handler safety",
191
+ status: "warn",
192
+ detail: `Missing conventions: ${violations.join(", ")}`,
193
+ hint: "Use execFileSync (no shell), avoid execSync, and delegate profile inference via --profile auto."
194
+ };
195
+ }
196
+ return { label: "hook handler safety", status: "ok" };
197
+ }
198
+ function resolvePluginManifestPath(options) {
199
+ const packageRaw = readOptionalFile(resolveProjectFile("package.json", options.baseDir));
200
+ if (packageRaw) {
201
+ try {
202
+ const parsed = JSON.parse(packageRaw);
203
+ if (typeof parsed.openclaw?.plugin === "string") {
204
+ return parsed.openclaw.plugin.replace(/^\.\//, "");
205
+ }
206
+ } catch {
207
+ }
208
+ }
209
+ return "openclaw.plugin.json";
135
210
  }
136
211
  function checkPluginManifest(options) {
137
- const manifestRaw = readOptionalFile(resolveProjectFile("openclaw.plugin.json", options.baseDir));
212
+ const manifestRelPath = resolvePluginManifestPath(options);
213
+ const manifestRaw = readOptionalFile(resolveProjectFile(manifestRelPath, options.baseDir));
138
214
  if (!manifestRaw) {
139
215
  return {
140
216
  label: "plugin manifest",
141
217
  status: "error",
142
- detail: "openclaw.plugin.json not found",
143
- hint: "Add root openclaw.plugin.json for OpenClaw plugin config schema."
218
+ detail: `${manifestRelPath} not found`,
219
+ hint: "Ensure openclaw.plugin.json exists at the path declared in package.json openclaw.plugin."
144
220
  };
145
221
  }
146
222
  try {
147
223
  const parsed = JSON.parse(manifestRaw);
148
- if (parsed.id !== "clawvault") {
224
+ if (!parsed.id || parsed.id !== "clawvault") {
149
225
  return {
150
226
  label: "plugin manifest",
151
227
  status: "error",
@@ -156,10 +232,12 @@ function checkPluginManifest(options) {
156
232
  return {
157
233
  label: "plugin manifest",
158
234
  status: "error",
159
- detail: "Missing configSchema in plugin manifest"
235
+ detail: "Missing configSchema in plugin manifest",
236
+ hint: "Add configSchema to openclaw.plugin.json for config validation."
160
237
  };
161
238
  }
162
- if (!parsed.configSchema.properties?.vaultPath) {
239
+ const hasVaultPath = Boolean(parsed.configSchema.properties?.vaultPath);
240
+ if (!hasVaultPath) {
163
241
  return {
164
242
  label: "plugin manifest",
165
243
  status: "warn",
@@ -180,56 +258,6 @@ function checkPluginManifest(options) {
180
258
  };
181
259
  }
182
260
  }
183
- function checkPackageFilesHygiene(options) {
184
- const packageJson = loadPackageJson(options);
185
- if (!packageJson.parsed) {
186
- return {
187
- label: "package files hygiene",
188
- status: "error",
189
- detail: packageJson.error
190
- };
191
- }
192
- const files = Array.isArray(packageJson.parsed.files) ? packageJson.parsed.files : [];
193
- if (files.length === 0) {
194
- return {
195
- label: "package files hygiene",
196
- status: "warn",
197
- detail: "package.json files allowlist is missing",
198
- hint: "Define package.json files to avoid publishing non-runtime directories."
199
- };
200
- }
201
- const normalized = files.map((entry) => String(entry).replace(/\\/g, "/").replace(/\/+$/, ""));
202
- const forbidden = normalized.filter(
203
- (entry) => FORBIDDEN_PACKAGE_FILE_ENTRIES.some((name) => entry === name || entry.startsWith(`${name}/`))
204
- );
205
- if (forbidden.length > 0) {
206
- return {
207
- label: "package files hygiene",
208
- status: "warn",
209
- detail: `Forbidden publish entries: ${forbidden.join(", ")}`,
210
- hint: "Remove legacy/test/research directories from package.json files."
211
- };
212
- }
213
- return {
214
- label: "package files hygiene",
215
- status: "ok"
216
- };
217
- }
218
- function checkLegacyHooksRemoved(options) {
219
- const hooksPath = resolveProjectFile(LEGACY_HOOK_DIR, options.baseDir);
220
- if (fs.existsSync(hooksPath)) {
221
- return {
222
- label: "legacy hooks removed",
223
- status: "warn",
224
- detail: `${LEGACY_HOOK_DIR} still exists`,
225
- hint: "Remove hooks/clawvault legacy handler artifacts in plugin-first architecture."
226
- };
227
- }
228
- return {
229
- label: "legacy hooks removed",
230
- status: "ok"
231
- };
232
- }
233
261
  function checkSkillMetadata(options) {
234
262
  const skillRaw = readOptionalFile(resolveProjectFile("SKILL.md", options.baseDir));
235
263
  if (!skillRaw) {
@@ -270,11 +298,11 @@ function checkSkillMetadata(options) {
270
298
  function checkOpenClawCompatibility(options = {}) {
271
299
  const checks = [
272
300
  checkOpenClawCli(),
273
- checkPackagePluginRegistration(options),
274
- checkPackageExtensionRegistration(options),
301
+ checkPackageHookRegistration(options),
275
302
  checkPluginManifest(options),
276
- checkPackageFilesHygiene(options),
277
- checkLegacyHooksRemoved(options),
303
+ checkHookManifest(options),
304
+ checkHookManifestRequirements(options),
305
+ checkHookHandlerSafety(options),
278
306
  checkSkillMetadata(options)
279
307
  ];
280
308
  const warnings = checks.filter((check) => check.status === "warn").length;
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  Observer
3
- } from "./chunk-7SWP5FKU.js";
3
+ } from "./chunk-FSYISBTU.js";
4
4
  import {
5
5
  resolveVaultPath
6
6
  } from "./chunk-GJO3CFUN.js";
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  Observer
3
- } from "./chunk-7SWP5FKU.js";
3
+ } from "./chunk-FSYISBTU.js";
4
4
  import {
5
5
  getSessionsDir
6
6
  } from "./chunk-HRLWZGMA.js";
@@ -1,9 +1,9 @@
1
+ import {
2
+ Observer
3
+ } from "./chunk-FSYISBTU.js";
1
4
  import {
2
5
  runReflection
3
6
  } from "./chunk-TWMI3SNN.js";
4
- import {
5
- Observer
6
- } from "./chunk-7SWP5FKU.js";
7
7
  import {
8
8
  resolveVaultPath
9
9
  } from "./chunk-GJO3CFUN.js";