clawvault 1.11.2 → 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.
Files changed (52) hide show
  1. package/README.md +135 -1
  2. package/bin/clawvault.js +51 -1252
  3. package/bin/command-registration.test.js +148 -0
  4. package/bin/command-runtime.js +42 -0
  5. package/bin/command-runtime.test.js +102 -0
  6. package/bin/help-contract.test.js +23 -0
  7. package/bin/register-core-commands.js +139 -0
  8. package/bin/register-maintenance-commands.js +137 -0
  9. package/bin/register-query-commands.js +225 -0
  10. package/bin/register-resilience-commands.js +147 -0
  11. package/bin/register-session-lifecycle-commands.js +204 -0
  12. package/bin/register-template-commands.js +72 -0
  13. package/bin/register-vault-operations-commands.js +295 -0
  14. package/bin/test-helpers/cli-command-fixtures.js +94 -0
  15. package/dashboard/lib/graph-diff.js +3 -1
  16. package/dashboard/lib/graph-diff.test.js +19 -0
  17. package/dashboard/lib/vault-parser.js +330 -26
  18. package/dashboard/lib/vault-parser.test.js +191 -11
  19. package/dashboard/public/app.js +22 -9
  20. package/dist/chunk-MXSSG3QU.js +42 -0
  21. package/dist/chunk-O5V7SD5C.js +398 -0
  22. package/dist/chunk-PAYUH64O.js +284 -0
  23. package/dist/{chunk-3HFB7EMU.js → chunk-QFBKWDYR.js} +12 -0
  24. package/dist/{chunk-UBRYOIII.js → chunk-TBVI4N53.js} +210 -21
  25. package/dist/chunk-TXO34J3O.js +56 -0
  26. package/dist/commands/compat.d.ts +28 -0
  27. package/dist/commands/compat.js +10 -0
  28. package/dist/commands/context.d.ts +2 -33
  29. package/dist/commands/context.js +3 -2
  30. package/dist/commands/doctor.js +61 -3
  31. package/dist/commands/entities.d.ts +1 -0
  32. package/dist/commands/entities.js +4 -4
  33. package/dist/commands/graph.d.ts +21 -0
  34. package/dist/commands/graph.js +10 -0
  35. package/dist/commands/link.d.ts +1 -0
  36. package/dist/commands/link.js +14 -5
  37. package/dist/commands/sleep.js +7 -6
  38. package/dist/commands/status.d.ts +6 -0
  39. package/dist/commands/status.js +63 -3
  40. package/dist/commands/wake.js +5 -4
  41. package/dist/context-COo8oq1k.d.ts +45 -0
  42. package/dist/index.d.ts +63 -2
  43. package/dist/index.js +53 -15
  44. package/dist/lib/config.d.ts +6 -1
  45. package/dist/lib/config.js +7 -3
  46. package/hooks/clawvault/HOOK.md +6 -1
  47. package/hooks/clawvault/handler.js +44 -3
  48. package/hooks/clawvault/handler.test.js +161 -0
  49. package/package.json +34 -2
  50. package/dashboard/public/graph.js +0 -376
  51. package/dashboard/public/style.css +0 -154
  52. package/dist/chunk-4KDZZW4X.js +0 -13
package/dist/index.js CHANGED
@@ -1,3 +1,12 @@
1
+ import {
2
+ buildTemplateVariables,
3
+ renderTemplate
4
+ } from "./chunk-7766SIJP.js";
5
+ import {
6
+ SessionWatcher,
7
+ observeCommand,
8
+ registerObserveCommand
9
+ } from "./chunk-GZOBCUVO.js";
1
10
  import {
2
11
  buildSessionRecap,
3
12
  formatSessionRecapMarkdown,
@@ -8,20 +17,30 @@ import {
8
17
  setupCommand
9
18
  } from "./chunk-PIJGYMQZ.js";
10
19
  import {
11
- buildTemplateVariables,
12
- renderTemplate
13
- } from "./chunk-7766SIJP.js";
20
+ Compressor,
21
+ Observer,
22
+ Reflector,
23
+ parseSessionFile
24
+ } from "./chunk-KOF3HYSL.js";
14
25
  import {
15
26
  buildContext,
16
27
  contextCommand,
17
28
  formatContextMarkdown,
18
- registerContextCommand
19
- } from "./chunk-UBRYOIII.js";
29
+ inferContextProfile,
30
+ normalizeContextProfileInput,
31
+ registerContextCommand,
32
+ resolveContextProfile
33
+ } from "./chunk-TBVI4N53.js";
34
+ import {
35
+ checkOpenClawCompatibility,
36
+ compatCommand,
37
+ compatibilityExitCode
38
+ } from "./chunk-PAYUH64O.js";
20
39
  import {
21
40
  ClawVault,
22
41
  createVault,
23
42
  findVault
24
- } from "./chunk-3HFB7EMU.js";
43
+ } from "./chunk-QFBKWDYR.js";
25
44
  import {
26
45
  DEFAULT_CATEGORIES,
27
46
  DEFAULT_CONFIG,
@@ -38,16 +57,20 @@ import {
38
57
  qmdUpdate
39
58
  } from "./chunk-MIIXBNO3.js";
40
59
  import {
41
- SessionWatcher,
42
- observeCommand,
43
- registerObserveCommand
44
- } from "./chunk-GZOBCUVO.js";
60
+ graphCommand,
61
+ graphSummary
62
+ } from "./chunk-TXO34J3O.js";
45
63
  import {
46
- Compressor,
47
- Observer,
48
- Reflector,
49
- parseSessionFile
50
- } from "./chunk-KOF3HYSL.js";
64
+ MEMORY_GRAPH_SCHEMA_VERSION,
65
+ buildOrUpdateMemoryGraphIndex,
66
+ getMemoryGraph,
67
+ loadMemoryGraphIndex
68
+ } from "./chunk-O5V7SD5C.js";
69
+ import {
70
+ findNearestVaultPath,
71
+ getVaultPath,
72
+ resolveVaultPath
73
+ } from "./chunk-MXSSG3QU.js";
51
74
 
52
75
  // src/index.ts
53
76
  import * as fs from "fs";
@@ -71,6 +94,7 @@ export {
71
94
  Compressor,
72
95
  DEFAULT_CATEGORIES,
73
96
  DEFAULT_CONFIG,
97
+ MEMORY_GRAPH_SCHEMA_VERSION,
74
98
  MEMORY_TYPES,
75
99
  Observer,
76
100
  QMD_INSTALL_COMMAND,
@@ -82,16 +106,28 @@ export {
82
106
  TYPE_TO_CATEGORY,
83
107
  VERSION,
84
108
  buildContext,
109
+ buildOrUpdateMemoryGraphIndex,
85
110
  buildSessionRecap,
86
111
  buildTemplateVariables,
112
+ checkOpenClawCompatibility,
113
+ compatCommand,
114
+ compatibilityExitCode,
87
115
  contextCommand,
88
116
  createVault,
89
117
  extractTags,
90
118
  extractWikiLinks,
119
+ findNearestVaultPath,
91
120
  findVault,
92
121
  formatContextMarkdown,
93
122
  formatSessionRecapMarkdown,
123
+ getMemoryGraph,
124
+ getVaultPath,
125
+ graphCommand,
126
+ graphSummary,
94
127
  hasQmd,
128
+ inferContextProfile,
129
+ loadMemoryGraphIndex,
130
+ normalizeContextProfileInput,
95
131
  observeCommand,
96
132
  parseSessionFile,
97
133
  qmdEmbed,
@@ -100,6 +136,8 @@ export {
100
136
  registerContextCommand,
101
137
  registerObserveCommand,
102
138
  renderTemplate,
139
+ resolveContextProfile,
140
+ resolveVaultPath,
103
141
  sessionRecapCommand,
104
142
  setupCommand
105
143
  };
@@ -2,5 +2,10 @@
2
2
  * Get the vault path from CLAWVAULT_PATH env var or throw
3
3
  */
4
4
  declare function getVaultPath(): string;
5
+ declare function findNearestVaultPath(startPath?: string): string | null;
6
+ declare function resolveVaultPath(options?: {
7
+ explicitPath?: string;
8
+ cwd?: string;
9
+ }): string;
5
10
 
6
- export { getVaultPath };
11
+ export { findNearestVaultPath, getVaultPath, resolveVaultPath };
@@ -1,6 +1,10 @@
1
1
  import {
2
- getVaultPath
3
- } from "../chunk-4KDZZW4X.js";
2
+ findNearestVaultPath,
3
+ getVaultPath,
4
+ resolveVaultPath
5
+ } from "../chunk-MXSSG3QU.js";
4
6
  export {
5
- getVaultPath
7
+ findNearestVaultPath,
8
+ getVaultPath,
9
+ resolveVaultPath
6
10
  };
@@ -47,7 +47,8 @@ openclaw hooks enable clawvault
47
47
  ### Session Start
48
48
 
49
49
  1. Extracts the initial user prompt (`context.initialPrompt` or first user message)
50
- 2. Runs `clawvault context "<prompt>" --format json -v <vaultPath>`
50
+ 2. Runs `clawvault context "<prompt>" --format json --profile auto -v <vaultPath>`
51
+ - Delegates profile selection to the shared context intent policy (`incident`, `planning`, `handoff`, or `default`)
51
52
  3. Injects up to 4 relevant context bullets into session messages
52
53
 
53
54
  Injection format:
@@ -58,6 +59,10 @@ Injection format:
58
59
  - <title> (<age>): <snippet>
59
60
  ```
60
61
 
62
+ ### Event Compatibility
63
+
64
+ The hook accepts canonical OpenClaw events (`gateway:startup`, `command:new`, `session:start`) and tolerates alias payload shapes (`event`, `eventName`, `name`, `hook`, `trigger`) to remain robust across runtime wrappers.
65
+
61
66
  ## No Configuration Needed
62
67
 
63
68
  Just enable the hook. It auto-detects vault path via:
@@ -18,6 +18,7 @@ const MAX_CONTEXT_PROMPT_LENGTH = 500;
18
18
  const MAX_CONTEXT_SNIPPET_LENGTH = 220;
19
19
  const MAX_RECAP_RESULTS = 6;
20
20
  const MAX_RECAP_SNIPPET_LENGTH = 220;
21
+ const EVENT_NAME_SEPARATOR_RE = /[.:/]/g;
21
22
 
22
23
  // Sanitize string for safe display (prevent prompt injection via control chars)
23
24
  function sanitizeForDisplay(str) {
@@ -228,6 +229,45 @@ function injectSystemMessage(event, message) {
228
229
  return true;
229
230
  }
230
231
 
232
+ function normalizeEventToken(value) {
233
+ if (typeof value !== 'string') return '';
234
+ return value
235
+ .trim()
236
+ .toLowerCase()
237
+ .replace(/\s+/g, '')
238
+ .replace(EVENT_NAME_SEPARATOR_RE, ':');
239
+ }
240
+
241
+ function eventMatches(event, type, action) {
242
+ const normalizedExpected = `${normalizeEventToken(type)}:${normalizeEventToken(action)}`;
243
+ const normalizedType = normalizeEventToken(event?.type);
244
+ const normalizedAction = normalizeEventToken(event?.action);
245
+
246
+ if (normalizedType && normalizedAction) {
247
+ if (`${normalizedType}:${normalizedAction}` === normalizedExpected) {
248
+ return true;
249
+ }
250
+ }
251
+
252
+ const aliases = [
253
+ event?.event,
254
+ event?.name,
255
+ event?.hook,
256
+ event?.trigger,
257
+ event?.eventName
258
+ ];
259
+
260
+ for (const alias of aliases) {
261
+ const normalizedAlias = normalizeEventToken(alias);
262
+ if (!normalizedAlias) continue;
263
+ if (normalizedAlias === normalizedExpected) {
264
+ return true;
265
+ }
266
+ }
267
+
268
+ return false;
269
+ }
270
+
231
271
  // Validate vault path - must be absolute and exist
232
272
  function validateVaultPath(vaultPath) {
233
273
  if (!vaultPath || typeof vaultPath !== 'string') return null;
@@ -435,6 +475,7 @@ async function handleSessionStart(event) {
435
475
  'context',
436
476
  prompt,
437
477
  '--format', 'json',
478
+ '--profile', 'auto',
438
479
  '-v', vaultPath
439
480
  ]);
440
481
 
@@ -462,17 +503,17 @@ async function handleSessionStart(event) {
462
503
  // Main handler - route events
463
504
  const handler = async (event) => {
464
505
  try {
465
- if (event.type === 'gateway' && event.action === 'startup') {
506
+ if (eventMatches(event, 'gateway', 'startup')) {
466
507
  await handleStartup(event);
467
508
  return;
468
509
  }
469
510
 
470
- if (event.type === 'command' && event.action === 'new') {
511
+ if (eventMatches(event, 'command', 'new')) {
471
512
  await handleNew(event);
472
513
  return;
473
514
  }
474
515
 
475
- if (event.type === 'session' && event.action === 'start') {
516
+ if (eventMatches(event, 'session', 'start')) {
476
517
  await handleSessionStart(event);
477
518
  return;
478
519
  }
@@ -0,0 +1,161 @@
1
+ import { afterEach, describe, expect, it, vi } from 'vitest';
2
+ import * as fs from 'fs';
3
+ import * as os from 'os';
4
+ import * as path from 'path';
5
+
6
+ const { execFileSyncMock } = vi.hoisted(() => ({
7
+ execFileSyncMock: vi.fn()
8
+ }));
9
+
10
+ vi.mock('child_process', () => ({
11
+ execFileSync: execFileSyncMock
12
+ }));
13
+
14
+ function makeVaultFixture() {
15
+ const root = fs.mkdtempSync(path.join(os.tmpdir(), 'clawvault-hook-'));
16
+ fs.writeFileSync(path.join(root, '.clawvault.json'), JSON.stringify({ name: 'test' }), 'utf-8');
17
+ return root;
18
+ }
19
+
20
+ async function loadHandler() {
21
+ vi.resetModules();
22
+ const mod = await import('./handler.js');
23
+ return mod.default;
24
+ }
25
+
26
+ afterEach(() => {
27
+ vi.clearAllMocks();
28
+ delete process.env.CLAWVAULT_PATH;
29
+ });
30
+
31
+ describe('clawvault hook handler', () => {
32
+ it('injects recovery warning on gateway startup when death detected', async () => {
33
+ const vaultPath = makeVaultFixture();
34
+ process.env.CLAWVAULT_PATH = vaultPath;
35
+
36
+ execFileSyncMock.mockImplementation((_command, args) => {
37
+ if (args[0] === 'recover') {
38
+ return '⚠️ CONTEXT DEATH DETECTED\nWorking on: ship memory graph';
39
+ }
40
+ return '';
41
+ });
42
+
43
+ const handler = await loadHandler();
44
+ const event = {
45
+ type: 'gateway',
46
+ action: 'startup',
47
+ messages: [{ role: 'user', content: 'hello' }]
48
+ };
49
+
50
+ await handler(event);
51
+
52
+ expect(execFileSyncMock).toHaveBeenCalledWith(
53
+ 'clawvault',
54
+ expect.arrayContaining(['recover', '--clear', '-v', vaultPath]),
55
+ expect.objectContaining({ shell: false })
56
+ );
57
+ const injected = event.messages.find((message) => message.role === 'system');
58
+ expect(injected?.content).toContain('Context death detected');
59
+ expect(injected?.content).toContain('ship memory graph');
60
+
61
+ fs.rmSync(vaultPath, { recursive: true, force: true });
62
+ });
63
+
64
+ it('supports alias event names for command:new', async () => {
65
+ const vaultPath = makeVaultFixture();
66
+ process.env.CLAWVAULT_PATH = vaultPath;
67
+ execFileSyncMock.mockReturnValue('');
68
+
69
+ const handler = await loadHandler();
70
+ await handler({
71
+ event: 'command:new',
72
+ sessionKey: 'agent:clawdious:main',
73
+ context: { commandSource: 'cli' }
74
+ });
75
+
76
+ expect(execFileSyncMock).toHaveBeenCalledWith(
77
+ 'clawvault',
78
+ expect.arrayContaining(['checkpoint', '--working-on']),
79
+ expect.objectContaining({ shell: false })
80
+ );
81
+
82
+ fs.rmSync(vaultPath, { recursive: true, force: true });
83
+ });
84
+
85
+ it('injects recap and memory context on session start alias event', async () => {
86
+ const vaultPath = makeVaultFixture();
87
+ process.env.CLAWVAULT_PATH = vaultPath;
88
+
89
+ execFileSyncMock.mockImplementation((_command, args) => {
90
+ if (args[0] === 'session-recap') {
91
+ return JSON.stringify({
92
+ messages: [
93
+ { role: 'user', text: 'Need a migration plan.' },
94
+ { role: 'assistant', text: 'Suggested phased rollout.' }
95
+ ]
96
+ });
97
+ }
98
+ if (args[0] === 'context') {
99
+ return JSON.stringify({
100
+ context: [
101
+ {
102
+ title: 'Use Postgres',
103
+ age: '1 day ago',
104
+ snippet: 'Selected Postgres for durability.'
105
+ }
106
+ ]
107
+ });
108
+ }
109
+ return '';
110
+ });
111
+
112
+ const handler = await loadHandler();
113
+ const event = {
114
+ eventName: 'session:start',
115
+ sessionKey: 'agent:clawdious:main',
116
+ context: { initialPrompt: 'Need migration plan' },
117
+ messages: [{ role: 'user', content: 'Need migration plan' }]
118
+ };
119
+
120
+ await handler(event);
121
+
122
+ const contextCall = execFileSyncMock.mock.calls.find((call) => call[1]?.[0] === 'context');
123
+ expect(contextCall?.[1]).toEqual(expect.arrayContaining(['--profile', 'auto']));
124
+
125
+ const injected = event.messages.find((message) => message.role === 'system');
126
+ expect(injected?.content).toContain('Session context restored');
127
+ expect(injected?.content).toContain('Recent conversation');
128
+ expect(injected?.content).toContain('Relevant memories');
129
+ expect(injected?.content).toContain('Use Postgres');
130
+
131
+ fs.rmSync(vaultPath, { recursive: true, force: true });
132
+ });
133
+
134
+ it('delegates profile selection to context auto mode for urgent prompts', async () => {
135
+ const vaultPath = makeVaultFixture();
136
+ process.env.CLAWVAULT_PATH = vaultPath;
137
+
138
+ execFileSyncMock.mockImplementation((_command, args) => {
139
+ if (args[0] === 'session-recap') {
140
+ return JSON.stringify({ messages: [] });
141
+ }
142
+ if (args[0] === 'context') {
143
+ return JSON.stringify({ context: [] });
144
+ }
145
+ return '';
146
+ });
147
+
148
+ const handler = await loadHandler();
149
+ await handler({
150
+ eventName: 'session:start',
151
+ sessionKey: 'agent:clawdious:main',
152
+ context: { initialPrompt: 'URGENT outage: rollback failed in production' },
153
+ messages: [{ role: 'user', content: 'URGENT outage: rollback failed in production' }]
154
+ });
155
+
156
+ const contextCall = execFileSyncMock.mock.calls.find((call) => call[1]?.[0] === 'context');
157
+ expect(contextCall?.[1]).toEqual(expect.arrayContaining(['--profile', 'auto']));
158
+
159
+ fs.rmSync(vaultPath, { recursive: true, force: true });
160
+ });
161
+ });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "clawvault",
3
- "version": "1.11.2",
3
+ "version": "2.0.0",
4
4
  "description": "ClawVault™ - 🐘 An elephant never forgets. Structured memory for OpenClaw agents. Context death resilience, Obsidian-compatible markdown, local semantic search.",
5
5
  "type": "module",
6
6
  "main": "dist/index.cjs",
@@ -29,11 +29,42 @@
29
29
  ]
30
30
  },
31
31
  "scripts": {
32
- "build": "tsup src/index.ts src/commands/entities.ts src/commands/link.ts src/commands/checkpoint.ts src/commands/recover.ts src/commands/status.ts src/commands/template.ts src/commands/setup.ts src/commands/context.ts src/commands/observe.ts src/commands/session-recap.ts src/commands/wake.ts src/commands/sleep.ts src/commands/doctor.ts src/commands/shell-init.ts src/commands/repair-session.ts src/lib/entity-index.ts src/lib/auto-linker.ts src/lib/config.ts src/lib/template-engine.ts src/lib/session-utils.ts src/lib/session-repair.ts --format esm --dts --clean",
32
+ "build": "tsup src/index.ts src/commands/entities.ts src/commands/link.ts src/commands/checkpoint.ts src/commands/recover.ts src/commands/status.ts src/commands/template.ts src/commands/setup.ts src/commands/context.ts src/commands/observe.ts src/commands/session-recap.ts src/commands/wake.ts src/commands/sleep.ts src/commands/doctor.ts src/commands/compat.ts src/commands/graph.ts src/commands/shell-init.ts src/commands/repair-session.ts src/lib/entity-index.ts src/lib/auto-linker.ts src/lib/config.ts src/lib/template-engine.ts src/lib/session-utils.ts src/lib/session-repair.ts --format esm --dts --clean",
33
33
  "dev": "tsup src/index.ts src/commands/*.ts src/lib/*.ts --format esm --dts --watch",
34
34
  "lint": "eslint src",
35
35
  "typecheck": "tsc --noEmit",
36
36
  "test": "vitest run",
37
+ "test:compat-fixtures": "npm run build && node scripts/check-compat-fixtures.mjs",
38
+ "test:compat-fixtures:fast": "node scripts/check-compat-fixtures.mjs",
39
+ "test:compat-fixtures:fast:report": "COMPAT_REPORT_DIR=${COMPAT_REPORT_DIR:-.compat-reports} npm run test:compat-fixtures:fast",
40
+ "test:compat-contract": "npm run build && COMPAT_VALIDATE_ONLY=1 node scripts/check-compat-fixtures.mjs",
41
+ "test:compat-contract:fast": "COMPAT_VALIDATE_ONLY=1 node scripts/check-compat-fixtures.mjs",
42
+ "test:compat-summary:verify": "node scripts/validate-compat-summary.mjs",
43
+ "test:compat-report-schemas:verify": "COMPAT_REPORT_DIR=${COMPAT_REPORT_DIR:-.compat-reports} node scripts/validate-compat-report-schemas.mjs",
44
+ "test:compat-report-schemas:verify:report": "COMPAT_REPORT_DIR=${COMPAT_REPORT_DIR:-.compat-reports} node scripts/validate-compat-report-schemas.mjs --out ${COMPAT_REPORT_DIR:-.compat-reports}/report-schema-validator-result.json",
45
+ "test:compat-report-schemas:verify:schema": "COMPAT_REPORT_DIR=${COMPAT_REPORT_DIR:-.compat-reports} node scripts/validate-json-schema.mjs --schema schemas/compat-report-schema-validator-output.schema.json --data ${COMPAT_REPORT_DIR:-.compat-reports}/report-schema-validator-result.json",
46
+ "test:compat-artifact-bundle:verify": "COMPAT_REPORT_DIR=${COMPAT_REPORT_DIR:-.compat-reports} node scripts/validate-compat-artifact-bundle.mjs",
47
+ "test:compat-artifact-bundle:verify:report": "COMPAT_REPORT_DIR=${COMPAT_REPORT_DIR:-.compat-reports} node scripts/validate-compat-artifact-bundle.mjs --out ${COMPAT_REPORT_DIR:-.compat-reports}/artifact-bundle-validator-result.json",
48
+ "test:compat-artifact-bundle:verify:schema": "COMPAT_REPORT_DIR=${COMPAT_REPORT_DIR:-.compat-reports} node scripts/validate-json-schema.mjs --schema schemas/compat-artifact-bundle-validator-output.schema.json --data ${COMPAT_REPORT_DIR:-.compat-reports}/artifact-bundle-validator-result.json",
49
+ "test:compat-artifact-bundle:manifest:schema": "node scripts/validate-json-schema.mjs --schema schemas/compat-artifact-bundle.manifest.schema.json --data schemas/compat-artifact-bundle.manifest.json",
50
+ "test:compat-artifact-bundle:manifest:verify": "node scripts/validate-compat-artifact-bundle-manifest.mjs",
51
+ "test:compat-artifact-bundle:manifest:verify:report": "COMPAT_REPORT_DIR=${COMPAT_REPORT_DIR:-.compat-reports} node scripts/validate-compat-artifact-bundle-manifest.mjs --out ${COMPAT_REPORT_DIR:-.compat-reports}/artifact-bundle-manifest-validator-result.json",
52
+ "test:compat-artifact-bundle:manifest:verify:schema": "COMPAT_REPORT_DIR=${COMPAT_REPORT_DIR:-.compat-reports} node scripts/validate-json-schema.mjs --schema schemas/compat-artifact-bundle-manifest-validator-output.schema.json --data ${COMPAT_REPORT_DIR:-.compat-reports}/artifact-bundle-manifest-validator-result.json",
53
+ "test:compat-artifact-alignment:fast": "vitest run scripts/lib/compat-artifact-bundle-manifest-contract-alignment.test.js scripts/lib/compat-artifact-bundle-manifest-schema-alignment.test.js scripts/lib/compat-artifact-bundle-output-schema-alignment.test.js",
54
+ "test:compat-artifact-cli-drift:fast": "vitest run scripts/validate-compat-artifact-bundle-manifest.test.js scripts/validate-compat-artifact-bundle.test.js",
55
+ "test:compat-script-stack-contract:fast": "vitest run scripts/lib/compat-contract-assertion-test-utils.test.js scripts/lib/compat-contract-test-utils.test.js scripts/lib/compat-npm-script-contracts.test.js scripts/lib/compat-npm-script-graph-utils.test.js scripts/lib/compat-npm-script-stack-contract.test.js scripts/lib/compat-readme-contract.test.js scripts/lib/compat-ci-workflow-test-utils.test.js scripts/lib/compat-ci-workflow-contracts.test.js scripts/lib/compat-ci-workflow-contract.test.js",
56
+ "test:compat-validator-stack:fast": "COMPAT_REPORT_DIR=${COMPAT_REPORT_DIR:-.compat-reports} npm run test:compat-validator-result:verify:report && COMPAT_REPORT_DIR=${COMPAT_REPORT_DIR:-.compat-reports} npm run test:compat-validator-result:schema && COMPAT_REPORT_DIR=${COMPAT_REPORT_DIR:-.compat-reports} npm run test:compat-schema-validator-result:verify && COMPAT_REPORT_DIR=${COMPAT_REPORT_DIR:-.compat-reports} npm run test:compat-validator-result:verify:schema",
57
+ "test:compat-artifact-stack:fast": "npm run test:compat-artifact-alignment:fast && npm run test:compat-artifact-cli-drift:fast && npm run test:compat-artifact-bundle:manifest:schema && COMPAT_REPORT_DIR=${COMPAT_REPORT_DIR:-.compat-reports} npm run test:compat-artifact-bundle:manifest:verify:report && COMPAT_REPORT_DIR=${COMPAT_REPORT_DIR:-.compat-reports} npm run test:compat-artifact-bundle:manifest:verify:schema && COMPAT_REPORT_DIR=${COMPAT_REPORT_DIR:-.compat-reports} npm run test:compat-artifact-bundle:verify:report && COMPAT_REPORT_DIR=${COMPAT_REPORT_DIR:-.compat-reports} npm run test:compat-artifact-bundle:verify:schema",
58
+ "test:compat-report-stack:fast": "COMPAT_REPORT_DIR=${COMPAT_REPORT_DIR:-.compat-reports} npm run test:compat-report-schemas:verify:report && COMPAT_REPORT_DIR=${COMPAT_REPORT_DIR:-.compat-reports} npm run test:compat-report-schemas:verify:schema && COMPAT_REPORT_DIR=${COMPAT_REPORT_DIR:-.compat-reports} npm run test:compat-validator-stack:fast && COMPAT_REPORT_DIR=${COMPAT_REPORT_DIR:-.compat-reports} npm run test:compat-artifact-stack:fast",
59
+ "test:compat-validator-result:verify": "node scripts/validate-compat-validator-result.mjs --require-ok",
60
+ "test:compat-validator-result:verify:report": "COMPAT_REPORT_DIR=${COMPAT_REPORT_DIR:-.compat-reports} node scripts/validate-compat-validator-result.mjs --require-ok --out ${COMPAT_REPORT_DIR:-.compat-reports}/validator-result-verifier-result.json",
61
+ "test:compat-validator-result:verify:schema": "COMPAT_REPORT_DIR=${COMPAT_REPORT_DIR:-.compat-reports} node scripts/validate-json-schema.mjs --schema schemas/compat-validator-result-verifier-output.schema.json --data ${COMPAT_REPORT_DIR:-.compat-reports}/validator-result-verifier-result.json",
62
+ "test:compat-validator-result:schema": "COMPAT_REPORT_DIR=${COMPAT_REPORT_DIR:-.compat-reports} node scripts/validate-json-schema.mjs --schema schemas/compat-summary-validator-output.schema.json --data ${COMPAT_REPORT_DIR:-.compat-reports}/validator-result.json --out ${COMPAT_REPORT_DIR:-.compat-reports}/schema-validator-result.json",
63
+ "test:compat-schema-validator-result:verify": "COMPAT_REPORT_DIR=${COMPAT_REPORT_DIR:-.compat-reports} node scripts/validate-json-schema.mjs --schema schemas/json-schema-validator-output.schema.json --data ${COMPAT_REPORT_DIR:-.compat-reports}/schema-validator-result.json",
64
+ "test:compat-summary": "COMPAT_REPORT_DIR=${COMPAT_REPORT_DIR:-.compat-reports} npm run test:compat-fixtures && COMPAT_REPORT_DIR=${COMPAT_REPORT_DIR:-.compat-reports} node scripts/validate-compat-summary.mjs --out ${COMPAT_REPORT_DIR:-.compat-reports}/validator-result.json",
65
+ "test:compat-summary:fast": "npm run test:compat-script-stack-contract:fast && COMPAT_REPORT_DIR=${COMPAT_REPORT_DIR:-.compat-reports} npm run test:compat-fixtures:fast && COMPAT_REPORT_DIR=${COMPAT_REPORT_DIR:-.compat-reports} node scripts/validate-compat-summary.mjs --out ${COMPAT_REPORT_DIR:-.compat-reports}/validator-result.json && COMPAT_REPORT_DIR=${COMPAT_REPORT_DIR:-.compat-reports} npm run test:compat-report-stack:fast",
66
+ "test:compat-smoke": "npm run test:compat-contract:fast && COMPAT_CASES=healthy npm run test:compat-fixtures:fast",
67
+ "ci": "npm run test:compat-script-stack-contract:fast && npm run typecheck && npm test && npm run build && npm run test:compat-contract:fast && npm run test:compat-summary:fast",
37
68
  "seo:generate": "node generate-seo-assets.mjs",
38
69
  "prepublishOnly": "npm run build"
39
70
  },
@@ -83,6 +114,7 @@
83
114
  },
84
115
  "devDependencies": {
85
116
  "@types/node": "^20.11.0",
117
+ "ajv": "^8.17.1",
86
118
  "puppeteer": "^24.37.2",
87
119
  "tsup": "^8.0.1",
88
120
  "typescript": "^5.3.3",