Merge origin/main into fix/570-monorepo-design-root

This commit is contained in:
Abdul Wahab
2026-08-25 06:58:14 +05:00
459 changed files with 15160 additions and 6778 deletions
+47 -82
View File
@@ -46,6 +46,7 @@ function stopEntry(command) {
],
};
}
const CLAUDE_PROJECT_HOOK = '${CLAUDE_PROJECT_DIR}/.claude/skills/impeccable/scripts/hook.mjs';
// The Node major the hook runtime requires, kept equal to the engines floor in
// package.json. The probe and the notice both derive from it so they cannot
@@ -70,7 +71,9 @@ const NODE_MAJOR_FLOOR = 22;
// Claude Code / Codex: `systemMessage` on stdout is shown to the user -> notice
// Cursor: preToolUse output is permission-shaped and its `user_message`
// renders only on DENY, so warning would block the edit -> probe only
// Grok Build: PostToolUse/Stop stdout is ignored outright -> probe only
// Grok Build: PostToolUse stdout is ignored; Stop additionalContext
// reaches the model, but the node-version notice has no systemMessage
// channel on this harness -> probe only
// Copilot: output contract unconfirmed; do not guess a shape -> probe only
//
// The clamp avoids `<` and `>` deliberately: Volta's Windows shims run through
@@ -85,6 +88,27 @@ const guardedNode = (hookPath, notice = '') => {
: `! ${NODE_PROBE}`;
return `[ ! -f "${hookPath}" ] || ${probe} || node "${hookPath}"`;
};
function buildClaudeCompatibleHooks(matcher, hookPath, notice = '') {
const command = guardedNode(hookPath, notice);
return {
PostToolUse: [
{
matcher,
hooks: [
{
type: 'command',
command,
timeout: TIMEOUT_SECONDS,
statusMessage: STATUS_MESSAGE,
},
],
},
],
Stop: [stopEntry(command)],
};
}
// The message says `on PATH` deliberately: the common cause is a hook shell
// whose PATH misses the version manager, so a user already running Node 22
// needs to know the hook's PATH is at issue and not their install. Apostrophes
@@ -115,23 +139,12 @@ const GROK_PROJECT_HOOK = '.grok/skills/impeccable/scripts/hook.mjs';
export function buildClaudeSettingsManifest() {
return {
description: 'Impeccable design detector: immediate-tier checks after Edit/Write/MultiEdit on UI files, full-rule deep pass on Stop.',
hooks: {
PostToolUse: [
{
matcher: 'Edit|Write|MultiEdit',
hooks: [
{
type: 'command',
command: guardedNode(CLAUDE_PROJECT_HOOK, SYSTEM_MESSAGE_NOTICE),
timeout: TIMEOUT_SECONDS,
statusMessage: STATUS_MESSAGE,
},
],
},
],
Stop: [stopEntry(guardedNode(CLAUDE_PROJECT_HOOK, SYSTEM_MESSAGE_NOTICE))],
},
description: 'Impeccable design detector: immediate-tier checks after Edit/Write on UI files, full-rule deep pass on Stop.',
hooks: buildClaudeCompatibleHooks(
'Edit|Write',
CLAUDE_PROJECT_HOOK,
SYSTEM_MESSAGE_NOTICE,
),
};
}
@@ -143,22 +156,11 @@ export function buildClaudeSettingsManifest() {
// than `hooks`, failing the whole manifest (issue #330).
export function buildClaudePluginHooksManifest() {
return {
hooks: {
PostToolUse: [
{
matcher: 'Edit|Write|MultiEdit',
hooks: [
{
type: 'command',
command: guardedNode(CLAUDE_PLUGIN_HOOK, SYSTEM_MESSAGE_NOTICE),
timeout: TIMEOUT_SECONDS,
statusMessage: STATUS_MESSAGE,
},
],
},
],
Stop: [stopEntry(guardedNode(CLAUDE_PLUGIN_HOOK, SYSTEM_MESSAGE_NOTICE))],
},
hooks: buildClaudeCompatibleHooks(
'Edit|Write',
CLAUDE_PLUGIN_HOOK,
SYSTEM_MESSAGE_NOTICE,
),
};
}
@@ -167,22 +169,11 @@ export function buildClaudePluginHooksManifest() {
// instead of relying on its Claude compatibility alias.
export function buildCodexPluginHooksManifest() {
return {
hooks: {
PostToolUse: [
{
matcher: 'Edit|Write|apply_patch',
hooks: [
{
type: 'command',
command: guardedNode(CODEX_PLUGIN_HOOK, SYSTEM_MESSAGE_NOTICE),
timeout: TIMEOUT_SECONDS,
statusMessage: STATUS_MESSAGE,
},
],
},
],
Stop: [stopEntry(guardedNode(CODEX_PLUGIN_HOOK, SYSTEM_MESSAGE_NOTICE))],
},
hooks: buildClaudeCompatibleHooks(
'Edit|Write|apply_patch',
CODEX_PLUGIN_HOOK,
SYSTEM_MESSAGE_NOTICE,
),
};
}
@@ -192,22 +183,11 @@ export function buildCodexPluginHooksManifest() {
export function buildCodexHooksManifest(skillDir = '.codex') {
const hookPath = codexProjectHook(skillDir);
return {
hooks: {
PostToolUse: [
{
matcher: 'Edit|Write|apply_patch',
hooks: [
{
type: 'command',
command: guardedNode(hookPath, SYSTEM_MESSAGE_NOTICE),
timeout: TIMEOUT_SECONDS,
statusMessage: STATUS_MESSAGE,
},
],
},
],
Stop: [stopEntry(guardedNode(hookPath, SYSTEM_MESSAGE_NOTICE))],
},
hooks: buildClaudeCompatibleHooks(
'Edit|Write|apply_patch',
hookPath,
SYSTEM_MESSAGE_NOTICE,
),
};
}
@@ -259,22 +239,7 @@ export function buildGitHubHooksManifest() {
// https://docs.x.ai/build/features/hooks
export function buildGrokHooksManifest() {
return {
hooks: {
PostToolUse: [
{
matcher: 'Edit|Write|MultiEdit',
hooks: [
{
type: 'command',
command: guardedNode(GROK_PROJECT_HOOK),
timeout: TIMEOUT_SECONDS,
statusMessage: STATUS_MESSAGE,
},
],
},
],
Stop: [stopEntry(guardedNode(GROK_PROJECT_HOOK))],
},
hooks: buildClaudeCompatibleHooks('Edit|Write|MultiEdit', GROK_PROJECT_HOOK),
};
}
+107 -151
View File
@@ -12,6 +12,8 @@ import { homedir, tmpdir } from 'node:os';
import { dirname, join, resolve } from 'node:path';
import { fileURLToPath } from 'node:url';
import { parseArgs } from './lib/cli-args.mjs';
const __dirname = dirname(fileURLToPath(import.meta.url));
const prRoot = resolve(__dirname, '..');
const defaultBundle = join(prRoot, 'dist', 'universal.zip');
@@ -32,31 +34,60 @@ if (args.help || args.h || !args.repo) {
process.exit(1);
}
const targetRepo = resolve(args.repo);
const bundlePath = resolve(args.bundle || defaultBundle);
const selectedProviders = (args.providers || defaultProviders.join(','))
const targetRepo = resolve(legacySmokeArg(args.repo));
const bundlePath = resolve(legacySmokeArg(args.bundle) || defaultBundle);
const selectedProviders = (legacySmokeArg(args.providers) || defaultProviders.join(','))
.split(',')
.map((provider) => provider.trim().toLowerCase())
.filter(Boolean);
const smokeDir = join(targetRepo, '.impeccable', 'provider-smoke');
const summaryPath = join(smokeDir, 'summary.json');
const smokeFiles = {
direct: 'src/__impeccable_provider_smoke_direct.html',
claude: 'src/__impeccable_provider_smoke_claude.html',
codex: 'src/__impeccable_provider_smoke_codex.html',
cursor: 'src/__impeccable_provider_smoke_cursor.html',
confirmedClaude: 'src/__impeccable_provider_smoke_confirmed_claude.html',
confirmedCodex: 'src/__impeccable_provider_smoke_confirmed_codex.html',
confirmedCursor: 'src/__impeccable_provider_smoke_confirmed_cursor.html',
agentChoiceClaude: 'src/__impeccable_provider_smoke_font_choice_claude.html',
agentChoiceCodex: 'src/__impeccable_provider_smoke_font_choice_codex.html',
agentChoiceCursor: 'src/__impeccable_provider_smoke_font_choice_cursor.html',
const directSmokeFile = 'src/__impeccable_provider_smoke_direct.html';
const providerSmoke = {
claude: {
fixture: 'src/__impeccable_provider_smoke_claude.html',
confirmedFixture: 'src/__impeccable_provider_smoke_confirmed_claude.html',
agentChoiceFixture: 'src/__impeccable_provider_smoke_font_choice_claude.html',
admin: '.claude/skills/impeccable/scripts/hook-admin.mjs',
hook: '.claude/skills/impeccable/scripts/hook.mjs',
event: (file) => postToolUseEvent('confirmed-claude', file, 'Edit'),
},
codex: {
fixture: 'src/__impeccable_provider_smoke_codex.html',
confirmedFixture: 'src/__impeccable_provider_smoke_confirmed_codex.html',
agentChoiceFixture: 'src/__impeccable_provider_smoke_font_choice_codex.html',
admin: '.agents/skills/impeccable/scripts/hook-admin.mjs',
hook: '.agents/skills/impeccable/scripts/hook.mjs',
event: (file) => postToolUseEvent('confirmed-codex', file, 'apply_patch'),
},
cursor: {
fixture: 'src/__impeccable_provider_smoke_cursor.html',
confirmedFixture: 'src/__impeccable_provider_smoke_confirmed_cursor.html',
agentChoiceFixture: 'src/__impeccable_provider_smoke_font_choice_cursor.html',
admin: '.cursor/skills/impeccable/scripts/hook-admin.mjs',
hook: '.cursor/skills/impeccable/scripts/hook-before-edit.mjs',
event: (file) => ({
hook_event_name: 'preToolUse',
cwd: targetRepo,
tool_name: 'Write',
tool_input: {
file_path: file,
content: readFileSync(file, 'utf8'),
},
}),
},
};
const results = [];
const hookConfigFiles = ['.impeccable/config.json', '.impeccable/config.local.json'];
const originalHookConfigFiles = new Map();
function legacySmokeArg(value) {
// The retired local parser represented a bare flag as the string "true".
// Preserve that CLI/error behavior while sharing the repository parser.
return value === true ? 'true' : value;
}
main().catch((error) => {
restoreHookConfigFiles();
if (!results.some((result) => !result.pass)) {
@@ -109,21 +140,6 @@ async function checked(name, classification, fn) {
}
}
function parseArgs(argv) {
const out = {};
for (let i = 0; i < argv.length; i++) {
const arg = argv[i];
if (!arg.startsWith('--')) continue;
const eq = arg.indexOf('=');
if (eq !== -1) {
out[arg.slice(2, eq)] = arg.slice(eq + 1);
} else {
out[arg.slice(2)] = argv[i + 1] && !argv[i + 1].startsWith('--') ? argv[++i] : 'true';
}
}
return out;
}
function assertPath(path, label) {
if (!existsSync(path)) throw new Error(`${label} does not exist: ${path}`);
}
@@ -466,7 +482,7 @@ function assertNoPluginInstall() {
function runDirectContractChecks() {
clearRuntimeState();
const file = writeBadFixture(smokeFiles.direct);
const file = writeBadFixture(directSmokeFile);
const env = { IMPECCABLE_HOOK_LOG: join(smokeDir, 'direct.ndjson') };
const claude = run('node', ['.claude/skills/impeccable/scripts/hook.mjs'], {
cwd: targetRepo,
@@ -495,7 +511,7 @@ function runDirectContractChecks() {
cwd: targetRepo,
tool_name: 'Write',
tool_input: {
file_path: join(targetRepo, smokeFiles.direct),
file_path: join(targetRepo, directSmokeFile),
content: badFixtureContent(),
},
}),
@@ -515,7 +531,7 @@ function runConfirmedExceptionPersistenceChecks() {
function runConfirmedExceptionForProvider(provider) {
clearRuntimeState();
const rel = confirmedSmokeFile(provider);
const rel = providerSmoke[provider].confirmedFixture;
const file = writeConfirmedFixture(rel);
const beforeLog = `${provider}-confirmed-before.ndjson`;
const afterLog = `${provider}-confirmed-after.ndjson`;
@@ -528,7 +544,7 @@ function runConfirmedExceptionForProvider(provider) {
assertNoSpecificFontIgnoreConfig(provider);
run('node', [
providerAdminScript(provider),
providerSmoke[provider].admin,
'ignore-value',
'overused-font',
'Roboto',
@@ -580,7 +596,7 @@ function runAgentChosenFontExceptionChecks() {
function runAgentChosenFontExceptionForProvider(provider) {
clearRuntimeState();
const rel = agentChoiceSmokeFile(provider);
const rel = providerSmoke[provider].agentChoiceFixture;
const file = writeConfirmedFixture(rel);
const beforeLog = `${provider}-agent-choice-before.ndjson`;
const afterLog = `${provider}-agent-choice-after.ndjson`;
@@ -612,27 +628,40 @@ function runAgentChosenFontExceptionForProvider(provider) {
}
function runProviderAgentFontException(provider, rel) {
const prompt = fontExceptionPrompt(provider, rel);
runProviderAgent(provider, fontExceptionPrompt(provider, rel), {
logName: `${provider}-agent-choice.log`,
claudeDebugLog: 'claude-agent-choice-debug.log',
});
}
function runProviderAgent(provider, prompt, {
logName,
env = {},
claudeDebugLog,
claudeTools = 'Read,Bash',
claudeAllowedTools = 'Read Bash',
cursorReady = false,
} = {}) {
if (provider === 'claude') {
run('claude', [
return run('claude', [
'-p',
'--setting-sources', 'project',
'--permission-mode', 'acceptEdits',
'--tools', 'Read,Bash',
'--allowedTools', 'Read Bash',
'--tools', claudeTools,
'--allowedTools', claudeAllowedTools,
'--debug', 'hooks',
'--debug-file', join(smokeDir, 'claude-agent-choice-debug.log'),
'--debug-file', join(smokeDir, claudeDebugLog),
prompt,
], {
cwd: targetRepo,
logName: 'claude-agent-choice.log',
env,
logName,
timeoutMs: 10 * 60 * 1000,
});
return;
}
if (provider === 'codex') {
run('codex', [
return run('codex', [
'exec',
'-C', targetRepo,
'--dangerously-bypass-hook-trust',
@@ -641,14 +670,14 @@ function runProviderAgentFontException(provider, rel) {
prompt,
], {
cwd: targetRepo,
logName: 'codex-agent-choice.log',
env,
logName,
timeoutMs: 10 * 60 * 1000,
});
return;
}
if (provider === 'cursor') {
ensureCursorAgent();
if (!cursorReady) ensureCursorAgent();
const res = run('agent', [
'-p',
'--force',
@@ -658,7 +687,8 @@ function runProviderAgentFontException(provider, rel) {
prompt,
], {
cwd: targetRepo,
logName: 'cursor-agent-choice.log',
env,
logName,
timeoutMs: 10 * 60 * 1000,
allowFailure: true,
});
@@ -671,10 +701,10 @@ function runProviderAgentFontException(provider, rel) {
}
throw new Error(res.error ? `agent failed: ${res.error.message}` : `agent exited ${res.status}`);
}
return;
return res;
}
throw new Error(`Unsupported agent-choice provider: ${provider}`);
throw new Error(`Unsupported provider agent: ${provider}`);
}
function assertSpecificFontIgnoreConfig(provider, config) {
@@ -713,84 +743,29 @@ function readSharedHookConfig() {
}
function runInstalledProviderHook(provider, file, logName) {
const smoke = providerSmoke[provider];
const env = { IMPECCABLE_HOOK_LOG: join(smokeDir, logName) };
if (provider === 'claude') {
return run('node', ['.claude/skills/impeccable/scripts/hook.mjs'], {
cwd: targetRepo,
env,
logName: `direct-${provider}-confirmed-${logName.replace(/\.ndjson$/, '.log')}`,
input: JSON.stringify(postToolUseEvent(`confirmed-${provider}`, file, 'Edit')),
});
}
if (provider === 'codex') {
return run('node', ['.agents/skills/impeccable/scripts/hook.mjs'], {
cwd: targetRepo,
env,
logName: `direct-${provider}-confirmed-${logName.replace(/\.ndjson$/, '.log')}`,
input: JSON.stringify(postToolUseEvent(`confirmed-${provider}`, file, 'apply_patch')),
});
}
if (provider === 'cursor') {
return run('node', ['.cursor/skills/impeccable/scripts/hook-before-edit.mjs'], {
cwd: targetRepo,
env,
logName: `direct-${provider}-confirmed-${logName.replace(/\.ndjson$/, '.log')}`,
input: JSON.stringify({
hook_event_name: 'preToolUse',
cwd: targetRepo,
tool_name: 'Write',
tool_input: {
file_path: file,
content: readFileSync(file, 'utf8'),
},
}),
});
}
throw new Error(`Unsupported confirmed exception provider: ${provider}`);
}
function confirmedSmokeFile(provider) {
if (provider === 'claude') return smokeFiles.confirmedClaude;
if (provider === 'codex') return smokeFiles.confirmedCodex;
if (provider === 'cursor') return smokeFiles.confirmedCursor;
throw new Error(`Unsupported confirmed exception provider: ${provider}`);
}
function agentChoiceSmokeFile(provider) {
if (provider === 'claude') return smokeFiles.agentChoiceClaude;
if (provider === 'codex') return smokeFiles.agentChoiceCodex;
if (provider === 'cursor') return smokeFiles.agentChoiceCursor;
throw new Error(`Unsupported agent-choice provider: ${provider}`);
}
function providerAdminScript(provider) {
if (provider === 'claude') return '.claude/skills/impeccable/scripts/hook-admin.mjs';
if (provider === 'codex') return '.agents/skills/impeccable/scripts/hook-admin.mjs';
if (provider === 'cursor') return '.cursor/skills/impeccable/scripts/hook-admin.mjs';
throw new Error(`Unsupported admin provider: ${provider}`);
return run('node', [smoke.hook], {
cwd: targetRepo,
env,
logName: `direct-${provider}-confirmed-${logName.replace(/\.ndjson$/, '.log')}`,
input: JSON.stringify(smoke.event(file)),
});
}
function runClaudeProviderSmoke() {
clearRuntimeState();
const env = { IMPECCABLE_HOOK_LOG: join(smokeDir, 'claude.ndjson') };
const prompt = providerPrompt(smokeFiles.claude);
const res = run('claude', [
'-p',
'--setting-sources', 'project',
'--permission-mode', 'acceptEdits',
'--tools', 'Read,Write,Edit',
'--allowedTools', 'Read Write Edit',
'--debug', 'hooks',
'--debug-file', join(smokeDir, 'claude-debug.log'),
prompt,
], {
cwd: targetRepo,
const prompt = providerPrompt(providerSmoke.claude.fixture);
const res = runProviderAgent('claude', prompt, {
env,
logName: 'claude-provider.log',
timeoutMs: 10 * 60 * 1000,
claudeDebugLog: 'claude-debug.log',
claudeTools: 'Read,Write,Edit',
claudeAllowedTools: 'Read Write Edit',
});
const evidence = `${res.stdout}\n${res.stderr}\n${readMaybe(join(smokeDir, 'claude.ndjson'))}\n${readMaybe(join(smokeDir, 'claude-debug.log'))}`;
requireFile(smokeFiles.claude, 'Claude provider fixture');
requireFile(providerSmoke.claude.fixture, 'Claude provider fixture');
requireFinding('Claude provider hook', evidence);
if (!/PostToolUse|hook/i.test(evidence)) throw new Error('Claude provider evidence lacks hook/PostToolUse marker');
record('claude provider', true, 'Claude edit triggered PostToolUse hook and side-tab detection');
@@ -799,23 +774,14 @@ function runClaudeProviderSmoke() {
function runCodexProviderSmoke() {
clearRuntimeState();
const env = { IMPECCABLE_HOOK_LOG: join(smokeDir, 'codex.ndjson') };
const prompt = `Use apply_patch to ${providerPrompt(smokeFiles.codex)}`;
const res = run('codex', [
'exec',
'-C', targetRepo,
'--dangerously-bypass-hook-trust',
'--dangerously-bypass-approvals-and-sandbox',
'--json',
prompt,
], {
cwd: targetRepo,
const prompt = `Use apply_patch to ${providerPrompt(providerSmoke.codex.fixture)}`;
const res = runProviderAgent('codex', prompt, {
env,
logName: 'codex-provider.log',
timeoutMs: 10 * 60 * 1000,
});
const evidence = `${res.stdout}\n${res.stderr}\n${readMaybe(join(smokeDir, 'codex.ndjson'))}`;
const cacheEvidence = `${readMaybe(join(targetRepo, '.impeccable', 'hook.cache.json'))}\n${readMaybe(join(targetRepo, '.impeccable', 'hook.pending.json'))}`;
requireFile(smokeFiles.codex, 'Codex provider fixture');
requireFile(providerSmoke.codex.fixture, 'Codex provider fixture');
requireFinding('Codex provider hook', `${evidence}\n${cacheEvidence}`);
record('codex provider', true, 'Codex apply_patch triggered project hook and side-tab detection');
}
@@ -824,37 +790,19 @@ function runCursorProviderSmoke() {
ensureCursorAgent();
clearRuntimeState();
const env = { IMPECCABLE_HOOK_LOG: join(smokeDir, 'cursor.ndjson') };
const prompt = providerPrompt(smokeFiles.cursor);
const res = run('agent', [
'-p',
'--force',
'--trust',
'--workspace', targetRepo,
'--output-format', 'stream-json',
prompt,
], {
cwd: targetRepo,
const prompt = providerPrompt(providerSmoke.cursor.fixture);
const res = runProviderAgent('cursor', prompt, {
env,
logName: 'cursor-provider.log',
timeoutMs: 10 * 60 * 1000,
allowFailure: true,
cursorReady: true,
});
if (res.error || res.status !== 0) {
const output = `${res.stdout}\n${res.stderr}\n${res.error?.message || ''}`;
if (/Authentication required|agent login|CURSOR_API_KEY/i.test(output)) {
const err = new Error('Cursor CLI authentication required. Run `agent login` or set CURSOR_API_KEY, then rerun `bun run smoke:hooks -- --providers=cursor`.');
err.classification = 'cursor auth required';
throw err;
}
throw new Error(res.error ? `agent failed: ${res.error.message}` : `agent exited ${res.status}`);
}
const evidence = `${res.stdout}\n${res.stderr}\n${readMaybe(join(smokeDir, 'cursor.ndjson'))}\n${readMaybe(join(targetRepo, '.impeccable', 'hook.pending.json'))}\n${readMaybe(join(targetRepo, '.impeccable', 'hook.cache.json'))}`;
requireFinding('Cursor provider hook', evidence);
const auditEvents = readAuditEvents(join(smokeDir, 'cursor.ndjson'));
if (!auditEvents.some((event) => event.event === 'preToolUse' && event.blocked === true)) {
throw new Error('Cursor provider evidence lacks a preToolUse audit entry with blocked=true');
}
const fixturePath = join(targetRepo, smokeFiles.cursor);
const fixturePath = join(targetRepo, providerSmoke.cursor.fixture);
const intentionalIgnore = auditEvents.some((event) =>
event.event === 'preToolUse'
&& event.file === fixturePath
@@ -1017,7 +965,15 @@ function cleanSmokeArtifacts() {
}
function cleanSmokeFiles() {
for (const rel of Object.values(smokeFiles)) {
const files = [
directSmokeFile,
...Object.values(providerSmoke).flatMap(({ fixture, confirmedFixture, agentChoiceFixture }) => [
fixture,
confirmedFixture,
agentChoiceFixture,
]),
];
for (const rel of files) {
rmSync(join(targetRepo, rel), { force: true });
}
}