fix(hooks): load js-yaml lazily so session-start runs without node_modules

The merge of #3470 moved the Flutter check in scripts/lib/project-detect.js
onto js-yaml with a top-level require. session-start.js loads that module
on every SessionStart, and plugin installs have no node_modules (#2822),
so the hook now exits 1 with "Cannot find module 'js-yaml'" in every
project, not only Dart ones.

Require js-yaml inside hasFlutterSdkDependency instead. When it is
missing, the existing catch returns false: Dart is still detected from
pubspec.yaml and only the Flutter refinement is skipped. Installs that
have their dependencies behave as before.

Add a case to the no-node_modules plugin install test that runs
session-start.js against a pubspec.yaml project. It fails on main.
This commit is contained in:
azank committed 2026-10-15 16:38:43 +05:00
1 parent b92f181b3b
commit 1d704e75e3
2 files changed
+36 -2

No files matched your search

@@ -13,7 +13,7 @@ const assert = require('assert');
const fs = require('fs');
const os = require('os');
const path = require('path');
const { execFileSync } = require('child_process');
const { execFileSync, spawnSync } = require('child_process');
const REPO_ROOT = path.join(__dirname, '..', '..');
@@ -109,6 +109,37 @@ function runTests() {
assert.ok(result.stdout.includes('Usage:'));
})) passed++; else failed++;
if (test('session-start.js runs without js-yaml installed', () => {
// The SessionStart hook loads scripts/lib/project-detect.js, so a
// top-level require of an npm package there fails every session start.
const sandbox = fs.mkdtempSync(path.join(os.tmpdir(), 'ecc-session-start-'));
try {
const project = path.join(sandbox, 'project');
const home = path.join(sandbox, 'home');
const data = path.join(home, '.claude');
fs.mkdirSync(project, { recursive: true });
fs.mkdirSync(data, { recursive: true });
fs.writeFileSync(path.join(project, 'pubspec.yaml'), 'name: app\ndependencies:\n flutter:\n sdk: flutter\n');
const result = spawnSync(process.execPath, [path.join(pluginDir, 'scripts', 'hooks', 'session-start.js')], {
cwd: project,
env: { ...process.env, HOME: home, USERPROFILE: home, ECC_AGENT_DATA_HOME: data,
CLAUDE_CONFIG_DIR: data, CLAUDE_PROJECT_DIR: project, XDG_DATA_HOME: path.join(home, '.local', 'share'),
ECC_HOMUNCULUS_DIR: path.join(data, 'homunculus'), ECC_SESSION_START_CONTEXT: 'true' },
input: JSON.stringify({ hook_event_name: 'SessionStart', source: 'startup', session_id: 'no-deps', cwd: project }),
encoding: 'utf8',
timeout: 15000,
});
assert.ifError(result.error);
assert.strictEqual(result.status, 0, `stderr: ${result.stderr}`);
assert.ok(!result.stderr.includes('Cannot find module'), `stderr: ${result.stderr}`);
const context = JSON.parse(result.stdout).hookSpecificOutput.additionalContext;
assert.ok(context.includes('"languages":["dart"]'), `additionalContext: ${context}`);
} finally {
fs.rmSync(sandbox, { recursive: true, force: true });
}
})) passed++; else failed++;
if (test('install-plan.js --config gives an actionable error when ajv is genuinely missing', () => {
const configPath = path.join(pluginDir, 'ecc-install.json');
fs.writeFileSync(configPath, JSON.stringify({ version: 1, profile: 'minimal' }));