@soleri/cli 9.4.0 → 9.6.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 (73) hide show
  1. package/dist/commands/create.js +3 -6
  2. package/dist/commands/create.js.map +1 -1
  3. package/dist/commands/hooks.js +126 -0
  4. package/dist/commands/hooks.js.map +1 -1
  5. package/dist/commands/install.js +5 -0
  6. package/dist/commands/install.js.map +1 -1
  7. package/dist/hook-packs/converter/README.md +99 -0
  8. package/dist/hook-packs/converter/template.d.ts +36 -0
  9. package/dist/hook-packs/converter/template.js +127 -0
  10. package/dist/hook-packs/converter/template.js.map +1 -0
  11. package/dist/hook-packs/converter/template.test.ts +133 -0
  12. package/dist/hook-packs/converter/template.ts +163 -0
  13. package/dist/hook-packs/flock-guard/README.md +65 -0
  14. package/dist/hook-packs/flock-guard/manifest.json +36 -0
  15. package/dist/hook-packs/flock-guard/scripts/flock-guard-post.sh +48 -0
  16. package/dist/hook-packs/flock-guard/scripts/flock-guard-pre.sh +85 -0
  17. package/dist/hook-packs/full/manifest.json +8 -1
  18. package/dist/hook-packs/graduation.d.ts +11 -0
  19. package/dist/hook-packs/graduation.js +48 -0
  20. package/dist/hook-packs/graduation.js.map +1 -0
  21. package/dist/hook-packs/graduation.ts +65 -0
  22. package/dist/hook-packs/installer.js +3 -1
  23. package/dist/hook-packs/installer.js.map +1 -1
  24. package/dist/hook-packs/installer.ts +3 -1
  25. package/dist/hook-packs/marketing-research/README.md +37 -0
  26. package/dist/hook-packs/marketing-research/manifest.json +24 -0
  27. package/dist/hook-packs/marketing-research/scripts/marketing-research.sh +70 -0
  28. package/dist/hook-packs/registry.d.ts +1 -0
  29. package/dist/hook-packs/registry.js +14 -4
  30. package/dist/hook-packs/registry.js.map +1 -1
  31. package/dist/hook-packs/registry.ts +18 -4
  32. package/dist/hook-packs/safety/README.md +50 -0
  33. package/dist/hook-packs/safety/manifest.json +23 -0
  34. package/{src/hook-packs/yolo-safety → dist/hook-packs/safety}/scripts/anti-deletion.sh +7 -1
  35. package/dist/hook-packs/validator.d.ts +32 -0
  36. package/dist/hook-packs/validator.js +126 -0
  37. package/dist/hook-packs/validator.js.map +1 -0
  38. package/dist/hook-packs/validator.ts +158 -0
  39. package/dist/hook-packs/yolo-safety/manifest.json +3 -19
  40. package/dist/prompts/create-wizard.js +1 -1
  41. package/dist/prompts/create-wizard.js.map +1 -1
  42. package/dist/utils/checks.js +6 -1
  43. package/dist/utils/checks.js.map +1 -1
  44. package/package.json +1 -1
  45. package/src/__tests__/flock-guard.test.ts +232 -0
  46. package/src/__tests__/graduation.test.ts +199 -0
  47. package/src/__tests__/hook-packs.test.ts +44 -19
  48. package/src/__tests__/hooks-convert.test.ts +344 -0
  49. package/src/__tests__/validator.test.ts +265 -0
  50. package/src/commands/create.ts +3 -7
  51. package/src/commands/hooks.ts +172 -0
  52. package/src/commands/install.ts +6 -0
  53. package/src/hook-packs/converter/README.md +99 -0
  54. package/src/hook-packs/converter/template.ts +163 -0
  55. package/src/hook-packs/flock-guard/README.md +65 -0
  56. package/src/hook-packs/flock-guard/manifest.json +36 -0
  57. package/src/hook-packs/flock-guard/scripts/flock-guard-post.sh +48 -0
  58. package/src/hook-packs/flock-guard/scripts/flock-guard-pre.sh +85 -0
  59. package/src/hook-packs/full/manifest.json +8 -1
  60. package/src/hook-packs/graduation.ts +65 -0
  61. package/src/hook-packs/installer.ts +3 -1
  62. package/src/hook-packs/marketing-research/README.md +37 -0
  63. package/src/hook-packs/marketing-research/manifest.json +24 -0
  64. package/src/hook-packs/marketing-research/scripts/marketing-research.sh +70 -0
  65. package/src/hook-packs/registry.ts +18 -4
  66. package/src/hook-packs/safety/README.md +50 -0
  67. package/src/hook-packs/safety/manifest.json +23 -0
  68. package/src/hook-packs/safety/scripts/anti-deletion.sh +280 -0
  69. package/src/hook-packs/validator.ts +158 -0
  70. package/src/hook-packs/yolo-safety/manifest.json +3 -19
  71. package/src/prompts/create-wizard.ts +1 -1
  72. package/src/utils/checks.ts +6 -1
  73. package/src/prompts/playbook.ts +0 -487
@@ -0,0 +1,158 @@
1
+ /**
2
+ * Hook pack validation framework.
3
+ * Generates test fixtures, runs dry-run tests, reports false positives/negatives.
4
+ */
5
+ import { execSync } from 'node:child_process';
6
+ import type { HookEvent } from './converter/template.js';
7
+
8
+ export interface TestFixture {
9
+ name: string;
10
+ event: HookEvent;
11
+ payload: Record<string, unknown>;
12
+ shouldMatch: boolean;
13
+ }
14
+
15
+ export interface DryRunResult {
16
+ fixture: TestFixture;
17
+ exitCode: number;
18
+ stdout: string;
19
+ matched: boolean;
20
+ }
21
+
22
+ export interface ValidationReport {
23
+ total: number;
24
+ passed: number;
25
+ falsePositives: DryRunResult[];
26
+ falseNegatives: DryRunResult[];
27
+ }
28
+
29
+ /**
30
+ * Generate test fixtures for a hook event.
31
+ * Returns 5 matching + 10 non-matching payloads.
32
+ */
33
+ export function generateFixtures(
34
+ event: HookEvent,
35
+ toolMatcher?: string,
36
+ filePatterns?: string[],
37
+ ): TestFixture[] {
38
+ const fixtures: TestFixture[] = [];
39
+
40
+ if (event === 'PreToolUse' || event === 'PostToolUse') {
41
+ const matchTools = toolMatcher ? toolMatcher.split('|').map((t) => t.trim()) : ['Write'];
42
+ const matchPath = filePatterns?.[0] ?? '**/src/**';
43
+ // Convert glob to a sample path
44
+ const samplePath = matchPath
45
+ .replace('**/', 'src/')
46
+ .replace('**', 'components')
47
+ .replace('*', 'file.tsx');
48
+
49
+ // 5 matching fixtures
50
+ for (let i = 0; i < 5; i++) {
51
+ const tool = matchTools[i % matchTools.length];
52
+ fixtures.push({
53
+ name: `match-${tool}-${i}`,
54
+ event,
55
+ payload: {
56
+ tool_name: tool,
57
+ tool_input: {
58
+ file_path: `${samplePath.replace('file.tsx', `file-${i}.tsx`)}`,
59
+ command: `echo test-${i}`,
60
+ },
61
+ },
62
+ shouldMatch: true,
63
+ });
64
+ }
65
+
66
+ // 10 non-matching fixtures
67
+ const nonMatchTools = [
68
+ 'Bash',
69
+ 'Read',
70
+ 'Glob',
71
+ 'Grep',
72
+ 'Agent',
73
+ 'WebSearch',
74
+ 'WebFetch',
75
+ 'TaskCreate',
76
+ 'Skill',
77
+ 'ToolSearch',
78
+ ];
79
+ for (let i = 0; i < 10; i++) {
80
+ fixtures.push({
81
+ name: `no-match-${nonMatchTools[i]}-${i}`,
82
+ event,
83
+ payload: {
84
+ tool_name: nonMatchTools[i],
85
+ tool_input: {
86
+ file_path: `/unrelated/path/other-${i}.js`,
87
+ command: `ls -la`,
88
+ },
89
+ },
90
+ shouldMatch: false,
91
+ });
92
+ }
93
+ } else {
94
+ // PreCompact, Notification, Stop — simpler payloads
95
+ // 5 matching (any invocation matches these events)
96
+ for (let i = 0; i < 5; i++) {
97
+ fixtures.push({
98
+ name: `match-event-${i}`,
99
+ event,
100
+ payload: { session_id: `test-session-${i}`, context: `test context ${i}` },
101
+ shouldMatch: true,
102
+ });
103
+ }
104
+ // 10 non-matching (empty/malformed payloads)
105
+ for (let i = 0; i < 10; i++) {
106
+ fixtures.push({
107
+ name: `no-match-empty-${i}`,
108
+ event,
109
+ payload: {},
110
+ shouldMatch: false,
111
+ });
112
+ }
113
+ }
114
+
115
+ return fixtures;
116
+ }
117
+
118
+ /**
119
+ * Run a hook script against a single fixture in dry-run mode.
120
+ */
121
+ export function runSingleDryRun(scriptPath: string, fixture: TestFixture): DryRunResult {
122
+ const input = JSON.stringify(fixture.payload);
123
+ try {
124
+ const stdout = execSync(`printf '%s' '${input.replace(/'/g, "'\\''")}' | sh "${scriptPath}"`, {
125
+ encoding: 'utf-8',
126
+ timeout: 5000,
127
+ stdio: ['pipe', 'pipe', 'pipe'],
128
+ });
129
+ const matched = stdout.trim().length > 0 && stdout.includes('"continue"');
130
+ return { fixture, exitCode: 0, stdout: stdout.trim(), matched };
131
+ } catch (err: unknown) {
132
+ const error = err as { status?: number; stdout?: string };
133
+ return {
134
+ fixture,
135
+ exitCode: error.status ?? 1,
136
+ stdout: (error.stdout as string) ?? '',
137
+ matched: false,
138
+ };
139
+ }
140
+ }
141
+
142
+ /**
143
+ * Run all fixtures against a script and produce a validation report.
144
+ */
145
+ export function validateHookScript(scriptPath: string, fixtures: TestFixture[]): ValidationReport {
146
+ const results = fixtures.map((f) => runSingleDryRun(scriptPath, f));
147
+
148
+ const falsePositives = results.filter((r) => !r.fixture.shouldMatch && r.matched);
149
+ const falseNegatives = results.filter((r) => r.fixture.shouldMatch && !r.matched);
150
+ const passed = results.length - falsePositives.length - falseNegatives.length;
151
+
152
+ return {
153
+ total: results.length,
154
+ passed,
155
+ falsePositives,
156
+ falseNegatives,
157
+ };
158
+ }
@@ -1,23 +1,7 @@
1
1
  {
2
2
  "name": "yolo-safety",
3
- "version": "1.0.0",
4
- "description": "Safety guardrails for YOLO mode — intercepts destructive commands (rm, git push --force, git reset --hard, git clean, drop table, docker rm), stages files before deletion, blocks everything else",
3
+ "version": "1.1.0",
4
+ "description": "Safety guardrails for YOLO mode — composes the safety pack (anti-deletion, staging) with YOLO-specific defaults",
5
5
  "hooks": [],
6
- "scripts": [
7
- {
8
- "name": "anti-deletion",
9
- "file": "anti-deletion.sh",
10
- "targetDir": "hooks"
11
- }
12
- ],
13
- "lifecycleHooks": [
14
- {
15
- "event": "PreToolUse",
16
- "matcher": "Bash",
17
- "type": "command",
18
- "command": "sh ~/.claude/hooks/anti-deletion.sh",
19
- "timeout": 10,
20
- "statusMessage": "Checking for destructive commands..."
21
- }
22
- ]
6
+ "composedFrom": ["safety"]
23
7
  }
@@ -6,7 +6,7 @@
6
6
  * from usage, not configured upfront.
7
7
  */
8
8
  import * as p from '@clack/prompts';
9
- import { ITALIAN_CRAFTSPERSON } from '@soleri/core';
9
+ import { ITALIAN_CRAFTSPERSON } from '@soleri/core/personas';
10
10
  /** Slugify a display name into a kebab-case ID. */
11
11
  function slugify(name) {
12
12
  return name
@@ -1 +1 @@
1
- {"version":3,"file":"create-wizard.js","sourceRoot":"","sources":["../../src/prompts/create-wizard.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"create-wizard.js","sourceRoot":"","sources":["../../src/prompts/create-wizard.ts"],"names":[],"mappings":"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"}
@@ -16,7 +16,11 @@ export function checkNodeVersion() {
16
16
  }
17
17
  export function checkNpm() {
18
18
  try {
19
- const version = execFileSync('npm', ['--version'], { encoding: 'utf-8' }).trim();
19
+ // shell: true is needed on Windows where npm is installed as npm.cmd
20
+ const version = execFileSync('npm', ['--version'], {
21
+ encoding: 'utf-8',
22
+ shell: process.platform === 'win32',
23
+ }).trim();
20
24
  return { status: 'pass', label: 'npm', detail: `v${version}` };
21
25
  }
22
26
  catch {
@@ -28,6 +32,7 @@ function checkTsx() {
28
32
  const version = execFileSync('npx', ['tsx', '--version'], {
29
33
  encoding: 'utf-8',
30
34
  timeout: 10_000,
35
+ shell: process.platform === 'win32',
31
36
  }).trim();
32
37
  return { status: 'pass', label: 'tsx', detail: `v${version}` };
33
38
  }
@@ -1 +1 @@
1
- {"version":3,"file":"checks.js","sourceRoot":"","sources":["../../src/utils/checks.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"checks.js","sourceRoot":"","sources":["../../src/utils/checks.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@soleri/cli",
3
- "version": "9.4.0",
3
+ "version": "9.6.0",
4
4
  "description": "Developer CLI for creating and managing Soleri AI agents.",
5
5
  "keywords": [
6
6
  "agent",
@@ -0,0 +1,232 @@
1
+ import { describe, it, expect, afterEach } from 'vitest';
2
+ import { execSync } from 'node:child_process';
3
+ import { mkdirSync, rmSync, existsSync, readFileSync, writeFileSync } from 'node:fs';
4
+ import { join } from 'node:path';
5
+ import { createHash } from 'node:crypto';
6
+ import { tmpdir } from 'node:os';
7
+
8
+ const SCRIPTS_DIR = join(__dirname, '..', 'hook-packs', 'flock-guard', 'scripts');
9
+ // Normalize to forward slashes for use in shell commands on Windows (Git Bash)
10
+ const PRE_SCRIPT = join(SCRIPTS_DIR, 'flock-guard-pre.sh').replace(/\\/g, '/');
11
+ const POST_SCRIPT = join(SCRIPTS_DIR, 'flock-guard-post.sh').replace(/\\/g, '/');
12
+
13
+ // The scripts use `git rev-parse --show-toplevel` which resolves to the repo root.
14
+ // Compute the same hash the scripts will produce.
15
+ const PROJECT_ROOT = execSync('git rev-parse --show-toplevel', {
16
+ cwd: join(__dirname, '..'),
17
+ encoding: 'utf-8',
18
+ }).trim();
19
+ const PROJECT_HASH = execSync(`printf '%s' '${PROJECT_ROOT}' | shasum | cut -c1-8`, {
20
+ encoding: 'utf-8',
21
+ }).trim();
22
+ // Scripts use ${TMPDIR:-${TEMP:-/tmp}} — match that resolution for the test environment.
23
+ // Normalize backslashes to forward slashes so Node.js paths match POSIX shell paths on Windows.
24
+ const TMP_BASE = (process.env.TMPDIR || process.env.TEMP || tmpdir()).replace(/\\/g, '/');
25
+ const LOCK_DIR = `${TMP_BASE}/soleri-guard-${PROJECT_HASH}.lock`;
26
+
27
+ function makePayload(command: string): string {
28
+ return JSON.stringify({ tool_name: 'Bash', tool_input: { command } });
29
+ }
30
+
31
+ function runPre(
32
+ command: string,
33
+ env?: Record<string, string>,
34
+ ): { stdout: string; exitCode: number } {
35
+ try {
36
+ const stdout = execSync(
37
+ `printf '%s' '${escapeShell(makePayload(command))}' | sh '${PRE_SCRIPT}'`,
38
+ {
39
+ encoding: 'utf-8',
40
+ stdio: 'pipe',
41
+ cwd: PROJECT_ROOT,
42
+ env: { ...process.env, TMPDIR: TMP_BASE, ...env },
43
+ },
44
+ );
45
+ return { stdout, exitCode: 0 };
46
+ } catch (err: any) {
47
+ return { stdout: err.stdout ?? '', exitCode: err.status ?? 1 };
48
+ }
49
+ }
50
+
51
+ function runPost(
52
+ command: string,
53
+ env?: Record<string, string>,
54
+ ): { stdout: string; exitCode: number } {
55
+ try {
56
+ const stdout = execSync(
57
+ `printf '%s' '${escapeShell(makePayload(command))}' | sh '${POST_SCRIPT}'`,
58
+ {
59
+ encoding: 'utf-8',
60
+ stdio: 'pipe',
61
+ cwd: PROJECT_ROOT,
62
+ env: { ...process.env, TMPDIR: TMP_BASE, ...env },
63
+ },
64
+ );
65
+ return { stdout, exitCode: 0 };
66
+ } catch (err: any) {
67
+ return { stdout: err.stdout ?? '', exitCode: err.status ?? 1 };
68
+ }
69
+ }
70
+
71
+ function escapeShell(s: string): string {
72
+ // Escape single quotes for use inside single-quoted shell string
73
+ return s.replace(/'/g, "'\\''");
74
+ }
75
+
76
+ function cleanLock(): void {
77
+ if (existsSync(LOCK_DIR)) {
78
+ rmSync(LOCK_DIR, { recursive: true, force: true });
79
+ }
80
+ }
81
+
82
+ // flock-guard uses POSIX shell scripts (sh, mkdir, jq, shasum, grep -qE) that
83
+ // are incompatible with Windows even under Git Bash due to path separator issues.
84
+ const isWindows = process.platform === 'win32';
85
+
86
+ describe.skipIf(isWindows)('flock-guard hook pack', () => {
87
+ afterEach(() => {
88
+ cleanLock();
89
+ });
90
+
91
+ // 1. Pre: allows non-lockfile commands
92
+ it('pre: allows non-lockfile commands (exit 0, no output)', () => {
93
+ const { stdout, exitCode } = runPre('echo hello');
94
+ expect(exitCode).toBe(0);
95
+ expect(stdout.trim()).toBe('');
96
+ expect(existsSync(LOCK_DIR)).toBe(false);
97
+ });
98
+
99
+ // 2. Pre: acquires lock on npm install
100
+ it('pre: acquires lock on npm install', () => {
101
+ const sessionId = `test-acquire-${Date.now()}`;
102
+ const { exitCode } = runPre('npm install', { CLAUDE_SESSION_ID: sessionId });
103
+ expect(exitCode).toBe(0);
104
+ expect(existsSync(LOCK_DIR)).toBe(true);
105
+ });
106
+
107
+ // 3. Pre: lock dir contains valid JSON with agentId and timestamp
108
+ it('pre: lock dir contains valid JSON with agentId and timestamp', () => {
109
+ const sessionId = `test-json-${Date.now()}`;
110
+ runPre('npm install', { CLAUDE_SESSION_ID: sessionId });
111
+
112
+ const lockJson = JSON.parse(readFileSync(join(LOCK_DIR, 'lock.json'), 'utf-8'));
113
+ expect(lockJson).toHaveProperty('agentId', sessionId);
114
+ expect(lockJson).toHaveProperty('timestamp');
115
+ expect(typeof lockJson.timestamp).toBe('number');
116
+ expect(lockJson.timestamp).toBeGreaterThan(0);
117
+ });
118
+
119
+ // 4. Post: releases lock after npm install
120
+ it('post: releases lock after npm install', () => {
121
+ const sessionId = `test-release-${Date.now()}`;
122
+ runPre('npm install', { CLAUDE_SESSION_ID: sessionId });
123
+ expect(existsSync(LOCK_DIR)).toBe(true);
124
+
125
+ const { exitCode } = runPost('npm install', { CLAUDE_SESSION_ID: sessionId });
126
+ expect(exitCode).toBe(0);
127
+ expect(existsSync(LOCK_DIR)).toBe(false);
128
+ });
129
+
130
+ // 5. Pre: blocks when lock held by another agent
131
+ it('pre: blocks when lock held by another agent', () => {
132
+ // Manually create lock with a different agentId
133
+ mkdirSync(LOCK_DIR, { recursive: true });
134
+ const now = Math.floor(Date.now() / 1000);
135
+ writeFileSync(
136
+ join(LOCK_DIR, 'lock.json'),
137
+ JSON.stringify({ agentId: 'other-agent-999', timestamp: now, command: 'npm install' }),
138
+ );
139
+
140
+ const mySession = `test-blocked-${Date.now()}`;
141
+ const { stdout, exitCode } = runPre('npm install', { CLAUDE_SESSION_ID: mySession });
142
+
143
+ // Script exits 0 but outputs JSON with continue: false
144
+ expect(exitCode).toBe(0);
145
+ const output = JSON.parse(stdout.trim());
146
+ expect(output.continue).toBe(false);
147
+ expect(output.stopReason).toContain('BLOCKED');
148
+ expect(output.stopReason).toContain('other-agent-999');
149
+ });
150
+
151
+ // 6. Pre: cleans stale lock (timestamp older than 30s)
152
+ it('pre: cleans stale lock and acquires', () => {
153
+ mkdirSync(LOCK_DIR, { recursive: true });
154
+ const staleTime = Math.floor(Date.now() / 1000) - 60; // 60s ago
155
+ writeFileSync(
156
+ join(LOCK_DIR, 'lock.json'),
157
+ JSON.stringify({ agentId: 'stale-agent', timestamp: staleTime, command: 'npm install' }),
158
+ );
159
+
160
+ const mySession = `test-stale-${Date.now()}`;
161
+ const { stdout, exitCode } = runPre('npm install', { CLAUDE_SESSION_ID: mySession });
162
+ expect(exitCode).toBe(0);
163
+ // Should not contain "continue: false" — lock was stale and cleaned
164
+ if (stdout.trim()) {
165
+ const output = JSON.parse(stdout.trim());
166
+ expect(output.continue).not.toBe(false);
167
+ }
168
+ // Lock should now be held by our session
169
+ expect(existsSync(LOCK_DIR)).toBe(true);
170
+ const lockJson = JSON.parse(readFileSync(join(LOCK_DIR, 'lock.json'), 'utf-8'));
171
+ expect(lockJson.agentId).toBe(mySession);
172
+ });
173
+
174
+ // 7. Pre: allows same agent reentry
175
+ it('pre: allows same agent reentry', () => {
176
+ const sessionId = `test-reentry-${Date.now()}`;
177
+ const env = { CLAUDE_SESSION_ID: sessionId };
178
+
179
+ // First acquisition
180
+ const first = runPre('npm install', env);
181
+ expect(first.exitCode).toBe(0);
182
+ expect(existsSync(LOCK_DIR)).toBe(true);
183
+
184
+ // Second acquisition with same session — should succeed (reentry)
185
+ const second = runPre('npm install', env);
186
+ expect(second.exitCode).toBe(0);
187
+ // No "continue: false" in output
188
+ if (second.stdout.trim()) {
189
+ const output = JSON.parse(second.stdout.trim());
190
+ expect(output.continue).not.toBe(false);
191
+ }
192
+ });
193
+
194
+ // 8. Post: only releases own lock (does not release lock held by other agent)
195
+ it('post: only releases own lock — does not remove lock held by another agent', () => {
196
+ // Create lock with a different agent
197
+ mkdirSync(LOCK_DIR, { recursive: true });
198
+ const now = Math.floor(Date.now() / 1000);
199
+ writeFileSync(
200
+ join(LOCK_DIR, 'lock.json'),
201
+ JSON.stringify({ agentId: 'other-agent-777', timestamp: now, command: 'npm install' }),
202
+ );
203
+
204
+ const mySession = `test-norelease-${Date.now()}`;
205
+ const { exitCode } = runPost('npm install', { CLAUDE_SESSION_ID: mySession });
206
+ expect(exitCode).toBe(0);
207
+ // Lock dir should still exist — we don't own it
208
+ expect(existsSync(LOCK_DIR)).toBe(true);
209
+ const lockJson = JSON.parse(readFileSync(join(LOCK_DIR, 'lock.json'), 'utf-8'));
210
+ expect(lockJson.agentId).toBe('other-agent-777');
211
+ });
212
+
213
+ // 9. Pre: detects other lockfile commands (yarn, pnpm, cargo, pip)
214
+ it('pre: detects yarn, pnpm install, cargo build, pip install', () => {
215
+ const commands = ['yarn', 'yarn install', 'pnpm install', 'cargo build', 'pip install'];
216
+ for (const cmd of commands) {
217
+ cleanLock();
218
+ const sessionId = `test-detect-${Date.now()}`;
219
+ const { exitCode } = runPre(cmd, { CLAUDE_SESSION_ID: sessionId });
220
+ expect(exitCode).toBe(0);
221
+ expect(existsSync(LOCK_DIR)).toBe(true);
222
+ cleanLock();
223
+ }
224
+ });
225
+
226
+ // 10. Post: ignores non-lockfile commands
227
+ it('post: ignores non-lockfile commands (no crash, no lock interaction)', () => {
228
+ const { exitCode, stdout } = runPost('echo hello');
229
+ expect(exitCode).toBe(0);
230
+ expect(stdout.trim()).toBe('');
231
+ });
232
+ });