ralph-gate 0.3.0 → 0.3.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.
package/dist/cli.js CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  // src/cli.ts
4
- import path3 from "path";
4
+ import path4 from "path";
5
5
 
6
6
  // src/config.ts
7
7
  import { promises as fs } from "fs";
@@ -287,9 +287,9 @@ function inferPythonGates(requirements, warnings) {
287
287
  }
288
288
  async function updateGitignore(cwd) {
289
289
  const gitignorePath = path2.join(cwd, ".gitignore");
290
- const pattern = "gate-results-*.json";
290
+ const filePattern = "gate-results-*.json";
291
291
  try {
292
- execSync("git check-ignore -q gate-results-test.json", {
292
+ execSync("git check-ignore -q gate-results-12345.json", {
293
293
  cwd,
294
294
  stdio: "ignore"
295
295
  });
@@ -300,7 +300,7 @@ async function updateGitignore(cwd) {
300
300
  const exists = await fileExists(gitignorePath);
301
301
  const content = exists ? await fs2.readFile(gitignorePath, "utf8") : "";
302
302
  const newline = content && !content.endsWith("\n") ? "\n" : "";
303
- await fs2.appendFile(gitignorePath, `${newline}${pattern}
303
+ await fs2.appendFile(gitignorePath, `${newline}${filePattern}
304
304
  `, "utf8");
305
305
  return true;
306
306
  } catch {
@@ -331,16 +331,28 @@ async function setupClaudeHook(cwd) {
331
331
  if (!settings.hooks.Stop) {
332
332
  settings.hooks.Stop = [];
333
333
  }
334
- const hookExists = settings.hooks.Stop.some(
335
- (config) => config.hooks?.some(
336
- (hook) => hook.type === "command" && hook.command === RALPH_GATE_HOOK_COMMAND
337
- )
338
- );
334
+ settings.hooks.Stop = settings.hooks.Stop.filter((item) => {
335
+ if (!item || typeof item !== "object") return false;
336
+ if (!("hooks" in item) || !Array.isArray(item.hooks)) return false;
337
+ if ("matcher" in item && typeof item.matcher === "object") return false;
338
+ return true;
339
+ });
340
+ let hookExists = false;
341
+ for (const item of settings.hooks.Stop) {
342
+ if ("hooks" in item && Array.isArray(item.hooks)) {
343
+ const hasRalphGate = item.hooks.some(
344
+ (hook) => hook.type === "command" && hook.command === RALPH_GATE_HOOK_COMMAND
345
+ );
346
+ if (hasRalphGate) {
347
+ hookExists = true;
348
+ break;
349
+ }
350
+ }
351
+ }
339
352
  if (hookExists) {
340
353
  return { configured: false, alreadyExists: true };
341
354
  }
342
355
  settings.hooks.Stop.push({
343
- matcher: {},
344
356
  hooks: [
345
357
  {
346
358
  type: "command",
@@ -568,6 +580,7 @@ async function runGates(gates, options = {}) {
568
580
 
569
581
  // src/output.ts
570
582
  import { promises as fs3 } from "fs";
583
+ import path3 from "path";
571
584
  var MAX_FAILURE_CHARS = 4e3;
572
585
  var HEAD_RATIO = 0.6;
573
586
  function truncateOutput(text, maxChars) {
@@ -682,6 +695,11 @@ function formatConsoleOutput(summary) {
682
695
  }
683
696
  async function writeResultsFile(summary, outputPath) {
684
697
  const data = JSON.stringify(summary, null, 2);
698
+ const dir = path3.dirname(outputPath);
699
+ try {
700
+ await fs3.mkdir(dir, { recursive: true });
701
+ } catch {
702
+ }
685
703
  await fs3.writeFile(outputPath, data, "utf8");
686
704
  }
687
705
 
@@ -764,7 +782,7 @@ function parseInitArgs(args) {
764
782
  return { options };
765
783
  }
766
784
  function defaultOutputPath() {
767
- return `gate-results-${process.pid}.json`;
785
+ return path4.join("gate-results", `gate-results-${process.pid}.json`);
768
786
  }
769
787
  function createEmptySummary(passed) {
770
788
  return {
@@ -841,10 +859,10 @@ async function main() {
841
859
  return;
842
860
  }
843
861
  console.log(
844
- `Created ${path3.basename(result.configPath)} with ${result.config.gates.length} gate(s).`
862
+ `Created ${path4.basename(result.configPath)} with ${result.config.gates.length} gate(s).`
845
863
  );
846
864
  if (result.gitignoreUpdated) {
847
- console.log("Updated .gitignore to exclude gate-results-*.json files.");
865
+ console.log("Updated .gitignore to exclude gate-results/ folder.");
848
866
  }
849
867
  if (result.hookConfigured) {
850
868
  console.log(
package/dist/cli.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/cli.ts","../src/config.ts","../src/init.ts","../src/runner.ts","../src/output.ts","../src/hook.ts"],"sourcesContent":["#!/usr/bin/env node\nimport path from 'node:path';\nimport type { Gate, GateRunSummary } from './types.js';\nimport { loadConfig } from './config.js';\nimport { initConfigFile } from './init.js';\nimport { runGates, type RunGatesOptions } from './runner.js';\nimport { formatConsoleOutput, writeResultsFile } from './output.js';\nimport { generateHookResponse, outputHookResponse } from './hook.js';\n\ninterface CliOptions {\n hook: boolean;\n dryRun: boolean;\n only?: string;\n verbose: boolean;\n}\n\ninterface InitCliOptions {\n force: boolean;\n print: boolean;\n skipHook: boolean;\n}\n\nfunction parseArgs(args: string[]): { options: CliOptions; error?: string } {\n const options: CliOptions = {\n hook: false,\n dryRun: false,\n verbose: false,\n };\n\n for (let i = 0; i < args.length; i += 1) {\n const arg = args[i];\n switch (arg) {\n case '--hook':\n options.hook = true;\n break;\n case '--dry-run':\n options.dryRun = true;\n break;\n case '--verbose':\n options.verbose = true;\n break;\n case '--only': {\n const value = args[i + 1];\n if (!value) {\n return { options, error: 'Missing value for --only.' };\n }\n options.only = value;\n i += 1;\n break;\n }\n default:\n return { options, error: `Unknown argument: ${arg}` };\n }\n }\n\n return { options };\n}\n\nfunction parseInitArgs(args: string[]): {\n options: InitCliOptions;\n error?: string;\n} {\n const options: InitCliOptions = {\n force: false,\n print: false,\n skipHook: false,\n };\n\n for (let i = 0; i < args.length; i += 1) {\n const arg = args[i];\n switch (arg) {\n case '--force':\n options.force = true;\n break;\n case '--print':\n options.print = true;\n break;\n case '--skip-hook':\n options.skipHook = true;\n break;\n default:\n return { options, error: `Unknown argument: ${arg}` };\n }\n }\n\n return { options };\n}\n\nfunction defaultOutputPath(): string {\n return `gate-results-${process.pid}.json`;\n}\n\nfunction createEmptySummary(passed: boolean): GateRunSummary {\n return {\n passed,\n timestamp: new Date().toISOString(),\n totalDurationMs: 0,\n results: [],\n firstFailure: null,\n warnings: [],\n };\n}\n\nfunction formatDryRun(gates: Gate[], shell: string): string {\n const lines = [`SHELL: ${shell}`];\n if (gates.length === 0) {\n lines.push('No gates to run.');\n return lines.join('\\n');\n }\n for (const gate of gates) {\n const order = typeof gate.order === 'number' ? gate.order : 100;\n lines.push(`- ${gate.name} (order ${order}): ${gate.command}`);\n }\n return lines.join('\\n');\n}\n\nfunction createHookProgressReporter(\n enabled: boolean,\n): Partial<RunGatesOptions> {\n if (!enabled) {\n return {};\n }\n\n const prefix = '[ralph-gate]';\n const writeLine = (line: string) => {\n process.stderr.write(`${prefix} ${line}\\n`);\n };\n\n return {\n onGateStart: (gate) => {\n writeLine(`running ${gate.name}: ${gate.command}`);\n },\n onGateOutput: (_gate, _stream, text) => {\n process.stderr.write(text);\n },\n onGateComplete: (result) => {\n if (result.skipped) {\n writeLine(`${result.name} skipped`);\n return;\n }\n const status = result.passed\n ? `passed in ${result.durationMs}ms`\n : `failed (exit ${result.exitCode ?? 'null'}) in ${result.durationMs}ms`;\n writeLine(`${result.name} ${status}`);\n },\n };\n}\n\nasync function main(): Promise<void> {\n const argv = process.argv.slice(2);\n\n if (argv[0] === 'init') {\n const { options, error } = parseInitArgs(argv.slice(1));\n if (error) {\n console.error(error);\n process.exitCode = 1;\n return;\n }\n\n const result = await initConfigFile({\n force: options.force,\n print: options.print,\n skipHook: options.skipHook,\n });\n if (result.error) {\n console.error(result.error);\n process.exitCode = 1;\n return;\n }\n\n for (const warning of result.warnings) {\n console.error(`Warning: ${warning}`);\n }\n\n if (options.print) {\n console.log(JSON.stringify(result.config, null, 2));\n return;\n }\n\n console.log(\n `Created ${path.basename(result.configPath)} with ${result.config.gates.length} gate(s).`,\n );\n if (result.gitignoreUpdated) {\n console.log('Updated .gitignore to exclude gate-results-*.json files.');\n }\n if (result.hookConfigured) {\n console.log(\n 'Added Stop hook to .claude/settings.local.json for automatic gate runs.',\n );\n } else if (result.hookAlreadyExists) {\n console.log('Stop hook already exists in .claude/settings.local.json.');\n }\n return;\n }\n\n const { options, error } = parseArgs(argv);\n if (error) {\n console.error(error);\n process.exitCode = 1;\n return;\n }\n\n const configResult = await loadConfig();\n if (configResult.error) {\n const summary = createEmptySummary(false);\n const outputPath = defaultOutputPath();\n await writeResultsFile(summary, outputPath);\n\n if (options.hook) {\n outputHookResponse({ decision: 'block', reason: configResult.error });\n process.exitCode = 0;\n return;\n }\n\n console.error(configResult.error);\n process.exitCode = 1;\n return;\n }\n\n if (!configResult.config) {\n if (options.dryRun) {\n const shellLabel = process.env.SHELL ?? '(default)';\n console.log(formatDryRun([], shellLabel));\n }\n if (options.hook) {\n outputHookResponse({});\n process.exitCode = 0;\n }\n return;\n }\n\n const config = configResult.config;\n const shellLabel = process.env.SHELL ?? '(default)';\n\n if (options.dryRun) {\n console.log(formatDryRun(config.gates, shellLabel));\n return;\n }\n\n let gates = config.gates;\n if (options.only) {\n const match = gates.find((gate) => gate.name === options.only);\n if (!match) {\n console.error(`Gate not found: ${options.only}`);\n process.exitCode = 1;\n return;\n }\n gates = [match];\n }\n\n const outputPath = config.outputPath ?? defaultOutputPath();\n\n if (gates.length === 0) {\n const summary = createEmptySummary(true);\n await writeResultsFile(summary, outputPath);\n if (options.hook) {\n outputHookResponse({});\n process.exitCode = 0;\n return;\n }\n console.log(formatConsoleOutput(summary));\n return;\n }\n\n const hookProgress = createHookProgressReporter(options.hook);\n const summary = await runGates(gates, {\n failFast: config.failFast,\n verbose: options.verbose && !options.hook,\n ...hookProgress,\n });\n\n await writeResultsFile(summary, outputPath);\n\n if (options.hook) {\n outputHookResponse(generateHookResponse(summary));\n process.exitCode = 0;\n return;\n }\n\n console.log(formatConsoleOutput(summary));\n if (!summary.passed) {\n process.exitCode = 1;\n }\n}\n\nmain().catch((error) => {\n console.error(error instanceof Error ? error.message : String(error));\n process.exitCode = 1;\n});\n","import { promises as fs } from 'node:fs';\nimport path from 'node:path';\nimport type { Gate, GateConfig } from './types.js';\n\nconst CONFIG_FILES = ['gate.config.json', '.gaterc.json', '.gaterc'];\n\nexport interface LoadConfigResult {\n config: GateConfig | null;\n configPath?: string;\n error?: string;\n}\n\nfunction normalizeGate(gate: Gate): Gate {\n const order = typeof gate.order === 'number' ? gate.order : 100;\n const enabled = typeof gate.enabled === 'boolean' ? gate.enabled : true;\n const blocking = typeof gate.blocking === 'boolean' ? gate.blocking : true;\n\n return {\n ...gate,\n order,\n enabled,\n blocking,\n description:\n typeof gate.description === 'string' ? gate.description : undefined,\n name: gate.name,\n command: gate.command,\n } as Gate;\n}\n\nfunction validateGate(gate: unknown): string | null {\n if (!gate || typeof gate !== 'object') {\n return 'Gate entries must be objects.';\n }\n const maybeGate = gate as Gate;\n if (typeof maybeGate.name !== 'string' || maybeGate.name.trim() === '') {\n return 'Gate is missing required field: name.';\n }\n if (\n typeof maybeGate.command !== 'string' ||\n maybeGate.command.trim() === ''\n ) {\n return `Gate '${maybeGate.name}' is missing required field: command.`;\n }\n return null;\n}\n\nfunction sortGates(gates: Gate[]): Gate[] {\n const withIndex = gates.map((gate, index) => ({ gate, index }));\n withIndex.sort((a, b) => {\n const orderA = typeof a.gate.order === 'number' ? a.gate.order : 100;\n const orderB = typeof b.gate.order === 'number' ? b.gate.order : 100;\n if (orderA !== orderB) {\n return orderA - orderB;\n }\n return a.index - b.index;\n });\n return withIndex.map((entry) => entry.gate);\n}\n\nexport async function loadConfig(\n cwd: string = process.cwd(),\n): Promise<LoadConfigResult> {\n for (const filename of CONFIG_FILES) {\n const filePath = path.join(cwd, filename);\n try {\n await fs.access(filePath);\n } catch {\n continue;\n }\n\n let raw: string;\n try {\n raw = await fs.readFile(filePath, 'utf8');\n } catch (error) {\n return {\n config: null,\n configPath: filePath,\n error: `Invalid config: unable to read ${filename}: ${(error as Error).message}`,\n };\n }\n\n let parsed: unknown;\n try {\n parsed = JSON.parse(raw);\n } catch (error) {\n return {\n config: null,\n configPath: filePath,\n error: `Invalid config: malformed JSON in ${filename}: ${(error as Error).message}`,\n };\n }\n\n if (!parsed || typeof parsed !== 'object') {\n return {\n config: null,\n configPath: filePath,\n error: `Invalid config: expected object in ${filename}.`,\n };\n }\n\n const config = parsed as GateConfig;\n if (!Array.isArray(config.gates)) {\n return {\n config: null,\n configPath: filePath,\n error: `Invalid config: missing required 'gates' array in ${filename}.`,\n };\n }\n\n const normalized: Gate[] = [];\n for (const gate of config.gates) {\n const gateError = validateGate(gate as Gate);\n if (gateError) {\n return {\n config: null,\n configPath: filePath,\n error: `Invalid config: ${gateError}`,\n };\n }\n const normalizedGate = normalizeGate(gate as Gate);\n if (normalizedGate.enabled === false) {\n continue;\n }\n normalized.push(normalizedGate);\n }\n\n const failFast =\n typeof config.failFast === 'boolean' ? config.failFast : true;\n const outputPath =\n typeof config.outputPath === 'string' ? config.outputPath : undefined;\n\n const sorted = sortGates(normalized);\n\n return {\n config: {\n gates: sorted,\n failFast,\n outputPath,\n },\n configPath: filePath,\n };\n }\n\n return { config: null };\n}\n","import { promises as fs } from 'node:fs';\nimport { execSync } from 'node:child_process';\nimport path from 'node:path';\nimport type { Gate, GateConfig } from './types.js';\n\nexport const DEFAULT_CONFIG_FILENAME = 'gate.config.json';\n\ntype PackageManager = 'npm' | 'yarn' | 'pnpm';\n\ntype DetectedProject =\n | {\n kind: 'node';\n packageManager: PackageManager;\n packageJson: Record<string, unknown>;\n }\n | { kind: 'python'; requirements: Set<string> }\n | { kind: 'unknown' };\n\nexport interface InitOptions {\n cwd?: string;\n filename?: string;\n force?: boolean;\n print?: boolean;\n skipHook?: boolean;\n}\n\nexport interface InitResult {\n config: GateConfig;\n configPath: string;\n created: boolean;\n projectKind: DetectedProject['kind'];\n warnings: string[];\n error?: string;\n gitignoreUpdated?: boolean;\n hookConfigured?: boolean;\n hookAlreadyExists?: boolean;\n}\n\nasync function fileExists(filePath: string): Promise<boolean> {\n try {\n await fs.access(filePath);\n return true;\n } catch {\n return false;\n }\n}\n\nasync function detectPackageManager(cwd: string): Promise<PackageManager> {\n if (await fileExists(path.join(cwd, 'pnpm-lock.yaml'))) {\n return 'pnpm';\n }\n if (await fileExists(path.join(cwd, 'yarn.lock'))) {\n return 'yarn';\n }\n return 'npm';\n}\n\nfunction runScriptCommand(\n packageManager: PackageManager,\n scriptName: string,\n): string {\n switch (packageManager) {\n case 'yarn':\n return `yarn ${scriptName}`;\n case 'pnpm':\n return `pnpm run ${scriptName}`;\n default:\n return `npm run ${scriptName}`;\n }\n}\n\nasync function readJsonFile(filePath: string): Promise<unknown> {\n const raw = await fs.readFile(filePath, 'utf8');\n return JSON.parse(raw);\n}\n\nfunction extractRequirementName(line: string): string | null {\n const trimmed = line.trim();\n if (trimmed.length === 0 || trimmed.startsWith('#')) {\n return null;\n }\n const noEnvMarker = trimmed.split(';', 1)[0]?.trim() ?? '';\n if (noEnvMarker.length === 0) {\n return null;\n }\n const name = noEnvMarker.split(/[<>=\\[]/, 1)[0]?.trim();\n if (!name) {\n return null;\n }\n return name.toLowerCase();\n}\n\nasync function detectProject(cwd: string): Promise<DetectedProject> {\n const packageJsonPath = path.join(cwd, 'package.json');\n if (await fileExists(packageJsonPath)) {\n const pm = await detectPackageManager(cwd);\n const parsed = await readJsonFile(packageJsonPath);\n if (!parsed || typeof parsed !== 'object') {\n throw new Error('package.json is not a JSON object.');\n }\n return {\n kind: 'node',\n packageManager: pm,\n packageJson: parsed as Record<string, unknown>,\n };\n }\n\n const requirementsPath = path.join(cwd, 'requirements.txt');\n const pyprojectPath = path.join(cwd, 'pyproject.toml');\n\n if (\n (await fileExists(requirementsPath)) ||\n (await fileExists(pyprojectPath))\n ) {\n const requirements = new Set<string>();\n if (await fileExists(requirementsPath)) {\n const raw = await fs.readFile(requirementsPath, 'utf8');\n for (const line of raw.split(/\\r?\\n/)) {\n const name = extractRequirementName(line);\n if (name) {\n requirements.add(name);\n }\n }\n }\n return { kind: 'python', requirements };\n }\n\n return { kind: 'unknown' };\n}\n\nfunction getPackageJsonScripts(\n packageJson: Record<string, unknown>,\n): Record<string, string> {\n const scripts = packageJson.scripts;\n if (!scripts || typeof scripts !== 'object') {\n return {};\n }\n const result: Record<string, string> = {};\n for (const [key, value] of Object.entries(\n scripts as Record<string, unknown>,\n )) {\n if (typeof value === 'string') {\n result[key] = value;\n }\n }\n return result;\n}\n\nfunction getPackageJsonDeps(packageJson: Record<string, unknown>): Set<string> {\n const deps = new Set<string>();\n const sections = [\n 'dependencies',\n 'devDependencies',\n 'peerDependencies',\n 'optionalDependencies',\n ] as const;\n for (const section of sections) {\n const values = packageJson[section];\n if (!values || typeof values !== 'object') {\n continue;\n }\n for (const name of Object.keys(values as Record<string, unknown>)) {\n deps.add(name);\n }\n }\n return deps;\n}\n\nasync function inferNodeGates(\n cwd: string,\n packageManager: PackageManager,\n packageJson: Record<string, unknown>,\n warnings: string[],\n): Promise<Gate[]> {\n const gates: Gate[] = [];\n const scripts = getPackageJsonScripts(packageJson);\n const deps = getPackageJsonDeps(packageJson);\n\n const addIfScript = (name: string, order: number) => {\n if (!scripts[name]) {\n return;\n }\n gates.push({\n name,\n order,\n command: runScriptCommand(packageManager, name),\n });\n };\n\n addIfScript('lint', 10);\n addIfScript('typecheck', 20);\n addIfScript('test', 30);\n addIfScript('build', 40);\n\n const tsconfigPath = path.join(cwd, 'tsconfig.json');\n const hasTypescript = deps.has('typescript');\n if (hasTypescript && (await fileExists(tsconfigPath)) && !scripts.typecheck) {\n warnings.push(\n \"No 'typecheck' script found; generating a default tsc gate.\",\n );\n gates.splice(1, 0, {\n name: 'typecheck',\n order: 20,\n command: 'npx tsc -p tsconfig.json --noEmit',\n });\n }\n\n return gates;\n}\n\nfunction inferPythonGates(\n requirements: Set<string>,\n warnings: string[],\n): Gate[] {\n const gates: Gate[] = [];\n\n if (requirements.has('ruff')) {\n gates.push({ name: 'lint', order: 10, command: 'python -m ruff check .' });\n }\n\n if (requirements.has('mypy')) {\n gates.push({ name: 'typecheck', order: 20, command: 'python -m mypy .' });\n }\n\n if (requirements.has('pytest')) {\n gates.push({ name: 'test', order: 30, command: 'python -m pytest -q' });\n } else {\n warnings.push(\n 'pytest not detected in requirements; no test gate generated.',\n );\n }\n\n return gates;\n}\n\nasync function updateGitignore(cwd: string): Promise<boolean> {\n const gitignorePath = path.join(cwd, '.gitignore');\n const pattern = 'gate-results-*.json';\n\n try {\n // Use git check-ignore to test if the pattern would already cover gate result files\n // This handles all edge cases: broader patterns, multiple patterns, etc.\n execSync('git check-ignore -q gate-results-test.json', {\n cwd,\n stdio: 'ignore',\n });\n // Exit code 0 means the file would be ignored - pattern already covered\n return false;\n } catch {\n // Not ignored or git not available - need to add the pattern\n }\n\n try {\n // Check if .gitignore exists and read it\n const exists = await fileExists(gitignorePath);\n const content = exists ? await fs.readFile(gitignorePath, 'utf8') : '';\n\n // Append the pattern with proper newline handling\n const newline = content && !content.endsWith('\\n') ? '\\n' : '';\n await fs.appendFile(gitignorePath, `${newline}${pattern}\\n`, 'utf8');\n return true;\n } catch {\n // Silently fail if we can't update .gitignore\n return false;\n }\n}\n\ninterface ClaudeHookCommand {\n type: string;\n command: string;\n}\n\ninterface ClaudeHookConfig {\n matcher?: Record<string, unknown>;\n hooks: ClaudeHookCommand[];\n}\n\ninterface ClaudeSettings {\n hooks?: {\n Stop?: ClaudeHookConfig[];\n [key: string]: ClaudeHookConfig[] | undefined;\n };\n [key: string]: unknown;\n}\n\nconst RALPH_GATE_HOOK_COMMAND = 'npx ralph-gate --hook';\n\nasync function setupClaudeHook(\n cwd: string,\n): Promise<{ configured: boolean; alreadyExists: boolean }> {\n const claudeDir = path.join(cwd, '.claude');\n const settingsPath = path.join(claudeDir, 'settings.local.json');\n\n try {\n // Ensure .claude directory exists\n await fs.mkdir(claudeDir, { recursive: true });\n\n let settings: ClaudeSettings = {};\n\n // Read existing settings if file exists\n if (await fileExists(settingsPath)) {\n try {\n const content = await fs.readFile(settingsPath, 'utf8');\n const parsed = JSON.parse(content);\n if (parsed && typeof parsed === 'object') {\n settings = parsed as ClaudeSettings;\n }\n } catch {\n // If we can't parse existing file, start fresh but warn\n settings = {};\n }\n }\n\n // Initialize hooks structure if needed\n if (!settings.hooks) {\n settings.hooks = {};\n }\n if (!settings.hooks.Stop) {\n settings.hooks.Stop = [];\n }\n\n // Check if ralph-gate hook already exists\n const hookExists = settings.hooks.Stop.some((config) =>\n config.hooks?.some(\n (hook) =>\n hook.type === 'command' && hook.command === RALPH_GATE_HOOK_COMMAND,\n ),\n );\n\n if (hookExists) {\n return { configured: false, alreadyExists: true };\n }\n\n // Add the ralph-gate hook with matcher\n settings.hooks.Stop.push({\n matcher: {},\n hooks: [\n {\n type: 'command',\n command: RALPH_GATE_HOOK_COMMAND,\n },\n ],\n });\n\n // Write updated settings\n await fs.writeFile(\n settingsPath,\n JSON.stringify(settings, null, 2) + '\\n',\n 'utf8',\n );\n\n return { configured: true, alreadyExists: false };\n } catch {\n // Silently fail if we can't setup hook\n return { configured: false, alreadyExists: false };\n }\n}\n\nexport async function generateGateConfig(cwd: string): Promise<{\n config: GateConfig;\n projectKind: DetectedProject['kind'];\n warnings: string[];\n}> {\n const warnings: string[] = [];\n const detected = await detectProject(cwd);\n\n let gates: Gate[] = [];\n\n if (detected.kind === 'node') {\n gates = await inferNodeGates(\n cwd,\n detected.packageManager,\n detected.packageJson,\n warnings,\n );\n } else if (detected.kind === 'python') {\n gates = inferPythonGates(detected.requirements, warnings);\n } else {\n warnings.push(\n 'No supported project markers found; creating an empty gate config.',\n );\n }\n\n return {\n config: {\n gates,\n failFast: true,\n },\n projectKind: detected.kind,\n warnings,\n };\n}\n\nexport async function initConfigFile(\n options: InitOptions = {},\n): Promise<InitResult> {\n const cwd = options.cwd ?? process.cwd();\n const filename = options.filename ?? DEFAULT_CONFIG_FILENAME;\n const configPath = path.join(cwd, filename);\n\n const { config, projectKind, warnings } = await generateGateConfig(cwd);\n\n if (options.print) {\n return {\n config,\n configPath,\n created: false,\n projectKind,\n warnings,\n };\n }\n\n const writeFlag = options.force ? 'w' : 'wx';\n\n try {\n await fs.writeFile(configPath, JSON.stringify(config, null, 2) + '\\n', {\n encoding: 'utf8',\n flag: writeFlag,\n });\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n const code = (error as NodeJS.ErrnoException | null)?.code;\n if (!options.force && code === 'EEXIST') {\n return {\n config,\n configPath,\n created: false,\n projectKind,\n warnings,\n error: `Config already exists at ${configPath}. Use --force to overwrite.`,\n };\n }\n return {\n config,\n configPath,\n created: false,\n projectKind,\n warnings,\n error: `Unable to write config at ${configPath}: ${message}`,\n };\n }\n\n // Update .gitignore to include gate result files\n const gitignoreUpdated = await updateGitignore(cwd);\n\n // Setup Claude hook unless skipped\n let hookConfigured: boolean | undefined;\n let hookAlreadyExists: boolean | undefined;\n\n if (!options.skipHook) {\n const hookResult = await setupClaudeHook(cwd);\n hookConfigured = hookResult.configured;\n hookAlreadyExists = hookResult.alreadyExists;\n }\n\n return {\n config,\n configPath,\n created: true,\n projectKind,\n warnings,\n gitignoreUpdated,\n hookConfigured,\n hookAlreadyExists,\n };\n}\n","import { spawn } from 'node:child_process';\nimport type { Gate, GateResult, GateRunSummary } from './types.js';\n\nexport interface RunGatesOptions {\n failFast?: boolean;\n verbose?: boolean;\n shell?: string;\n cwd?: string;\n onGateStart?: (gate: Gate) => void;\n onGateOutput?: (\n gate: Gate,\n stream: 'stdout' | 'stderr',\n text: string,\n ) => void;\n onGateComplete?: (result: GateResult) => void;\n}\n\nfunction resolveShell(shell?: string): string | boolean {\n if (typeof shell === 'string' && shell.length > 0) {\n return shell;\n }\n const envShell = process.env.SHELL;\n return envShell && envShell.length > 0 ? envShell : true;\n}\n\nasync function runGate(\n gate: Gate,\n options: RunGatesOptions,\n): Promise<GateResult> {\n const start = Date.now();\n const shell = resolveShell(options.shell);\n const env = process.env;\n\n return new Promise((resolve) => {\n options.onGateStart?.(gate);\n const child = spawn(gate.command, {\n shell,\n env,\n cwd: options.cwd,\n });\n\n let stdout = '';\n let stderr = '';\n let exitCode: number | null = null;\n let resolved = false;\n\n const finalize = () => {\n if (resolved) {\n return;\n }\n resolved = true;\n const durationMs = Date.now() - start;\n const result: GateResult = {\n name: gate.name,\n passed: exitCode === 0,\n exitCode,\n stdout,\n stderr,\n durationMs,\n skipped: false,\n blocking: gate.blocking !== false,\n timestamp: new Date().toISOString(),\n };\n options.onGateComplete?.(result);\n resolve(result);\n };\n\n child.stdout?.on('data', (chunk) => {\n const text = chunk.toString();\n stdout += text;\n if (options.verbose) {\n process.stdout.write(text);\n }\n options.onGateOutput?.(gate, 'stdout', text);\n });\n\n child.stderr?.on('data', (chunk) => {\n const text = chunk.toString();\n stderr += text;\n if (options.verbose) {\n process.stderr.write(text);\n }\n options.onGateOutput?.(gate, 'stderr', text);\n });\n\n child.on('error', (error) => {\n stderr += error.message;\n exitCode = null;\n finalize();\n });\n\n child.on('close', (code) => {\n exitCode = code;\n finalize();\n });\n });\n}\n\nexport async function runGates(\n gates: Gate[],\n options: RunGatesOptions = {},\n): Promise<GateRunSummary> {\n const results: GateResult[] = [];\n const warnings: string[] = [];\n const start = Date.now();\n let firstFailure: GateResult | null = null;\n\n for (const gate of gates) {\n const isBlocking = gate.blocking !== false;\n const shouldSkip =\n options.failFast !== false && firstFailure !== null && isBlocking;\n\n if (shouldSkip) {\n const skippedResult: GateResult = {\n name: gate.name,\n passed: true,\n exitCode: null,\n stdout: '',\n stderr: '',\n durationMs: 0,\n skipped: true,\n blocking: isBlocking,\n timestamp: new Date().toISOString(),\n };\n results.push(skippedResult);\n options.onGateComplete?.(skippedResult);\n continue;\n }\n\n const result = await runGate(gate, options);\n results.push(result);\n\n if (!result.passed) {\n if (result.blocking) {\n if (!firstFailure) {\n firstFailure = result;\n }\n } else {\n warnings.push(result.name);\n }\n }\n }\n\n const totalDurationMs = Date.now() - start;\n const passed = firstFailure === null;\n\n return {\n passed,\n timestamp: new Date().toISOString(),\n totalDurationMs,\n results,\n firstFailure,\n warnings,\n };\n}\n","import { promises as fs } from 'node:fs';\nimport type { GateResult, GateRunSummary } from './types.js';\n\nconst MAX_FAILURE_CHARS = 4000;\nconst HEAD_RATIO = 0.6;\n\nfunction truncateOutput(text: string, maxChars: number): string {\n const trimmed = text.trimEnd();\n if (maxChars <= 0 || trimmed.length === 0) {\n return '';\n }\n if (trimmed.length <= maxChars) {\n return trimmed;\n }\n const headChars = Math.max(1, Math.floor(maxChars * HEAD_RATIO));\n const tailChars = Math.max(0, maxChars - headChars);\n const omitted = trimmed.length - maxChars;\n const marker = `\\n...<${omitted} chars omitted>...\\n`;\n const head = trimmed.slice(0, headChars);\n const tail = tailChars > 0 ? trimmed.slice(-tailChars) : '';\n return `${head}${marker}${tail}`;\n}\n\nfunction splitBudget(\n stdoutLen: number,\n stderrLen: number,\n): {\n stdout: number;\n stderr: number;\n} {\n if (stdoutLen === 0 && stderrLen === 0) {\n return { stdout: 0, stderr: 0 };\n }\n if (stdoutLen === 0) {\n return { stdout: 0, stderr: MAX_FAILURE_CHARS };\n }\n if (stderrLen === 0) {\n return { stdout: MAX_FAILURE_CHARS, stderr: 0 };\n }\n\n const base = Math.floor(MAX_FAILURE_CHARS / 2);\n let stdout = Math.min(stdoutLen, base);\n let stderr = Math.min(stderrLen, base);\n let remaining = MAX_FAILURE_CHARS - stdout - stderr;\n\n if (remaining > 0) {\n const stdoutRemaining = stdoutLen - stdout;\n const stderrRemaining = stderrLen - stderr;\n if (stdoutRemaining === 0) {\n stderr += remaining;\n } else if (stderrRemaining === 0) {\n stdout += remaining;\n } else {\n const totalRemaining = stdoutRemaining + stderrRemaining;\n const stdoutExtra = Math.round(\n (stdoutRemaining / totalRemaining) * remaining,\n );\n stdout += stdoutExtra;\n stderr += remaining - stdoutExtra;\n }\n }\n\n return { stdout, stderr };\n}\n\nfunction colorize(text: string, code: string, enabled: boolean): string {\n if (!enabled) {\n return text;\n }\n return `\\u001b[${code}m${text}\\u001b[0m`;\n}\n\nfunction formatResultLine(result: GateResult, color: boolean): string {\n const statusSymbol = result.skipped ? '⊘' : result.passed ? '✓' : '✗';\n\n let symbolColor = '32';\n if (result.skipped) {\n symbolColor = '90';\n } else if (!result.passed) {\n symbolColor = '31';\n }\n\n const parts: string[] = [];\n if (result.skipped) {\n parts.push('skipped');\n } else if (!result.passed) {\n parts.push(`exit ${result.exitCode ?? 'null'}`);\n }\n parts.push(`${result.durationMs}ms`);\n\n const details = parts.length > 0 ? ` (${parts.join(', ')})` : '';\n const coloredSymbol = colorize(statusSymbol, symbolColor, color);\n return `${coloredSymbol} ${result.name}${details}`;\n}\n\nexport function formatFailureContext(stderr: string, stdout = ''): string {\n const trimmedStderr = stderr.trimEnd();\n const trimmedStdout = stdout.trimEnd();\n\n if (trimmedStderr.length === 0 && trimmedStdout.length === 0) {\n return 'No output captured.';\n }\n\n if (trimmedStderr.length > 0 && trimmedStdout.length > 0) {\n const budget = splitBudget(trimmedStdout.length, trimmedStderr.length);\n const stderrSection = truncateOutput(trimmedStderr, budget.stderr);\n const stdoutSection = truncateOutput(trimmedStdout, budget.stdout);\n return `STDERR:\\n${stderrSection}\\n\\nSTDOUT:\\n${stdoutSection}`;\n }\n\n if (trimmedStderr.length > 0) {\n return truncateOutput(trimmedStderr, MAX_FAILURE_CHARS);\n }\n\n return truncateOutput(trimmedStdout, MAX_FAILURE_CHARS);\n}\n\nexport function formatConsoleOutput(summary: GateRunSummary): string {\n const color = Boolean(process.stdout.isTTY);\n const lines: string[] = [];\n\n for (const result of summary.results) {\n lines.push(formatResultLine(result, color));\n }\n\n if (summary.warnings.length > 0) {\n const warningLine = `Warnings: ${summary.warnings.join(', ')}`;\n lines.push(colorize(warningLine, '33', color));\n }\n\n const finalLine = summary.passed\n ? colorize('All blocking gates passed.', '32', color)\n : colorize('Blocking gate failed.', '31', color);\n lines.push(finalLine);\n\n return lines.join('\\n');\n}\n\nexport async function writeResultsFile(\n summary: GateRunSummary,\n outputPath: string,\n): Promise<void> {\n const data = JSON.stringify(summary, null, 2);\n await fs.writeFile(outputPath, data, 'utf8');\n}\n","import type { GateRunSummary, HookOutput } from './types.js';\nimport { formatFailureContext } from './output.js';\n\nexport function generateHookResponse(summary: GateRunSummary): HookOutput {\n const warnings = summary.warnings.length > 0 ? summary.warnings : undefined;\n\n if (summary.passed) {\n return warnings ? { warnings } : {};\n }\n\n const failure = summary.firstFailure;\n if (failure) {\n const reason = `Gate '${failure.name}' failed (exit ${failure.exitCode ?? 'null'}):\\n${formatFailureContext(failure.stderr, failure.stdout)}`;\n return warnings\n ? { decision: 'block', reason, warnings }\n : { decision: 'block', reason };\n }\n\n const reason = 'Gate run failed without a blocking gate result.';\n return warnings\n ? { decision: 'block', reason, warnings }\n : { decision: 'block', reason };\n}\n\nexport function outputHookResponse(output: HookOutput): void {\n process.stdout.write(`${JSON.stringify(output)}\\n`);\n}\n"],"mappings":"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","names":["path","fs","path","fs","reason","options","error","path","summary","outputPath","shellLabel"]}
1
+ {"version":3,"sources":["../src/cli.ts","../src/config.ts","../src/init.ts","../src/runner.ts","../src/output.ts","../src/hook.ts"],"sourcesContent":["#!/usr/bin/env node\nimport path from 'node:path';\nimport type { Gate, GateRunSummary } from './types.js';\nimport { loadConfig } from './config.js';\nimport { initConfigFile } from './init.js';\nimport { runGates, type RunGatesOptions } from './runner.js';\nimport { formatConsoleOutput, writeResultsFile } from './output.js';\nimport { generateHookResponse, outputHookResponse } from './hook.js';\n\ninterface CliOptions {\n hook: boolean;\n dryRun: boolean;\n only?: string;\n verbose: boolean;\n}\n\ninterface InitCliOptions {\n force: boolean;\n print: boolean;\n skipHook: boolean;\n}\n\nfunction parseArgs(args: string[]): { options: CliOptions; error?: string } {\n const options: CliOptions = {\n hook: false,\n dryRun: false,\n verbose: false,\n };\n\n for (let i = 0; i < args.length; i += 1) {\n const arg = args[i];\n switch (arg) {\n case '--hook':\n options.hook = true;\n break;\n case '--dry-run':\n options.dryRun = true;\n break;\n case '--verbose':\n options.verbose = true;\n break;\n case '--only': {\n const value = args[i + 1];\n if (!value) {\n return { options, error: 'Missing value for --only.' };\n }\n options.only = value;\n i += 1;\n break;\n }\n default:\n return { options, error: `Unknown argument: ${arg}` };\n }\n }\n\n return { options };\n}\n\nfunction parseInitArgs(args: string[]): {\n options: InitCliOptions;\n error?: string;\n} {\n const options: InitCliOptions = {\n force: false,\n print: false,\n skipHook: false,\n };\n\n for (let i = 0; i < args.length; i += 1) {\n const arg = args[i];\n switch (arg) {\n case '--force':\n options.force = true;\n break;\n case '--print':\n options.print = true;\n break;\n case '--skip-hook':\n options.skipHook = true;\n break;\n default:\n return { options, error: `Unknown argument: ${arg}` };\n }\n }\n\n return { options };\n}\n\nfunction defaultOutputPath(): string {\n return path.join('gate-results', `gate-results-${process.pid}.json`);\n}\n\nfunction createEmptySummary(passed: boolean): GateRunSummary {\n return {\n passed,\n timestamp: new Date().toISOString(),\n totalDurationMs: 0,\n results: [],\n firstFailure: null,\n warnings: [],\n };\n}\n\nfunction formatDryRun(gates: Gate[], shell: string): string {\n const lines = [`SHELL: ${shell}`];\n if (gates.length === 0) {\n lines.push('No gates to run.');\n return lines.join('\\n');\n }\n for (const gate of gates) {\n const order = typeof gate.order === 'number' ? gate.order : 100;\n lines.push(`- ${gate.name} (order ${order}): ${gate.command}`);\n }\n return lines.join('\\n');\n}\n\nfunction createHookProgressReporter(\n enabled: boolean,\n): Partial<RunGatesOptions> {\n if (!enabled) {\n return {};\n }\n\n const prefix = '[ralph-gate]';\n const writeLine = (line: string) => {\n process.stderr.write(`${prefix} ${line}\\n`);\n };\n\n return {\n onGateStart: (gate) => {\n writeLine(`running ${gate.name}: ${gate.command}`);\n },\n onGateOutput: (_gate, _stream, text) => {\n process.stderr.write(text);\n },\n onGateComplete: (result) => {\n if (result.skipped) {\n writeLine(`${result.name} skipped`);\n return;\n }\n const status = result.passed\n ? `passed in ${result.durationMs}ms`\n : `failed (exit ${result.exitCode ?? 'null'}) in ${result.durationMs}ms`;\n writeLine(`${result.name} ${status}`);\n },\n };\n}\n\nasync function main(): Promise<void> {\n const argv = process.argv.slice(2);\n\n if (argv[0] === 'init') {\n const { options, error } = parseInitArgs(argv.slice(1));\n if (error) {\n console.error(error);\n process.exitCode = 1;\n return;\n }\n\n const result = await initConfigFile({\n force: options.force,\n print: options.print,\n skipHook: options.skipHook,\n });\n if (result.error) {\n console.error(result.error);\n process.exitCode = 1;\n return;\n }\n\n for (const warning of result.warnings) {\n console.error(`Warning: ${warning}`);\n }\n\n if (options.print) {\n console.log(JSON.stringify(result.config, null, 2));\n return;\n }\n\n console.log(\n `Created ${path.basename(result.configPath)} with ${result.config.gates.length} gate(s).`,\n );\n if (result.gitignoreUpdated) {\n console.log('Updated .gitignore to exclude gate-results/ folder.');\n }\n if (result.hookConfigured) {\n console.log(\n 'Added Stop hook to .claude/settings.local.json for automatic gate runs.',\n );\n } else if (result.hookAlreadyExists) {\n console.log('Stop hook already exists in .claude/settings.local.json.');\n }\n return;\n }\n\n const { options, error } = parseArgs(argv);\n if (error) {\n console.error(error);\n process.exitCode = 1;\n return;\n }\n\n const configResult = await loadConfig();\n if (configResult.error) {\n const summary = createEmptySummary(false);\n const outputPath = defaultOutputPath();\n await writeResultsFile(summary, outputPath);\n\n if (options.hook) {\n outputHookResponse({ decision: 'block', reason: configResult.error });\n process.exitCode = 0;\n return;\n }\n\n console.error(configResult.error);\n process.exitCode = 1;\n return;\n }\n\n if (!configResult.config) {\n if (options.dryRun) {\n const shellLabel = process.env.SHELL ?? '(default)';\n console.log(formatDryRun([], shellLabel));\n }\n if (options.hook) {\n outputHookResponse({});\n process.exitCode = 0;\n }\n return;\n }\n\n const config = configResult.config;\n const shellLabel = process.env.SHELL ?? '(default)';\n\n if (options.dryRun) {\n console.log(formatDryRun(config.gates, shellLabel));\n return;\n }\n\n let gates = config.gates;\n if (options.only) {\n const match = gates.find((gate) => gate.name === options.only);\n if (!match) {\n console.error(`Gate not found: ${options.only}`);\n process.exitCode = 1;\n return;\n }\n gates = [match];\n }\n\n const outputPath = config.outputPath ?? defaultOutputPath();\n\n if (gates.length === 0) {\n const summary = createEmptySummary(true);\n await writeResultsFile(summary, outputPath);\n if (options.hook) {\n outputHookResponse({});\n process.exitCode = 0;\n return;\n }\n console.log(formatConsoleOutput(summary));\n return;\n }\n\n const hookProgress = createHookProgressReporter(options.hook);\n const summary = await runGates(gates, {\n failFast: config.failFast,\n verbose: options.verbose && !options.hook,\n ...hookProgress,\n });\n\n await writeResultsFile(summary, outputPath);\n\n if (options.hook) {\n outputHookResponse(generateHookResponse(summary));\n process.exitCode = 0;\n return;\n }\n\n console.log(formatConsoleOutput(summary));\n if (!summary.passed) {\n process.exitCode = 1;\n }\n}\n\nmain().catch((error) => {\n console.error(error instanceof Error ? error.message : String(error));\n process.exitCode = 1;\n});\n","import { promises as fs } from 'node:fs';\nimport path from 'node:path';\nimport type { Gate, GateConfig } from './types.js';\n\nconst CONFIG_FILES = ['gate.config.json', '.gaterc.json', '.gaterc'];\n\nexport interface LoadConfigResult {\n config: GateConfig | null;\n configPath?: string;\n error?: string;\n}\n\nfunction normalizeGate(gate: Gate): Gate {\n const order = typeof gate.order === 'number' ? gate.order : 100;\n const enabled = typeof gate.enabled === 'boolean' ? gate.enabled : true;\n const blocking = typeof gate.blocking === 'boolean' ? gate.blocking : true;\n\n return {\n ...gate,\n order,\n enabled,\n blocking,\n description:\n typeof gate.description === 'string' ? gate.description : undefined,\n name: gate.name,\n command: gate.command,\n } as Gate;\n}\n\nfunction validateGate(gate: unknown): string | null {\n if (!gate || typeof gate !== 'object') {\n return 'Gate entries must be objects.';\n }\n const maybeGate = gate as Gate;\n if (typeof maybeGate.name !== 'string' || maybeGate.name.trim() === '') {\n return 'Gate is missing required field: name.';\n }\n if (\n typeof maybeGate.command !== 'string' ||\n maybeGate.command.trim() === ''\n ) {\n return `Gate '${maybeGate.name}' is missing required field: command.`;\n }\n return null;\n}\n\nfunction sortGates(gates: Gate[]): Gate[] {\n const withIndex = gates.map((gate, index) => ({ gate, index }));\n withIndex.sort((a, b) => {\n const orderA = typeof a.gate.order === 'number' ? a.gate.order : 100;\n const orderB = typeof b.gate.order === 'number' ? b.gate.order : 100;\n if (orderA !== orderB) {\n return orderA - orderB;\n }\n return a.index - b.index;\n });\n return withIndex.map((entry) => entry.gate);\n}\n\nexport async function loadConfig(\n cwd: string = process.cwd(),\n): Promise<LoadConfigResult> {\n for (const filename of CONFIG_FILES) {\n const filePath = path.join(cwd, filename);\n try {\n await fs.access(filePath);\n } catch {\n continue;\n }\n\n let raw: string;\n try {\n raw = await fs.readFile(filePath, 'utf8');\n } catch (error) {\n return {\n config: null,\n configPath: filePath,\n error: `Invalid config: unable to read ${filename}: ${(error as Error).message}`,\n };\n }\n\n let parsed: unknown;\n try {\n parsed = JSON.parse(raw);\n } catch (error) {\n return {\n config: null,\n configPath: filePath,\n error: `Invalid config: malformed JSON in ${filename}: ${(error as Error).message}`,\n };\n }\n\n if (!parsed || typeof parsed !== 'object') {\n return {\n config: null,\n configPath: filePath,\n error: `Invalid config: expected object in ${filename}.`,\n };\n }\n\n const config = parsed as GateConfig;\n if (!Array.isArray(config.gates)) {\n return {\n config: null,\n configPath: filePath,\n error: `Invalid config: missing required 'gates' array in ${filename}.`,\n };\n }\n\n const normalized: Gate[] = [];\n for (const gate of config.gates) {\n const gateError = validateGate(gate as Gate);\n if (gateError) {\n return {\n config: null,\n configPath: filePath,\n error: `Invalid config: ${gateError}`,\n };\n }\n const normalizedGate = normalizeGate(gate as Gate);\n if (normalizedGate.enabled === false) {\n continue;\n }\n normalized.push(normalizedGate);\n }\n\n const failFast =\n typeof config.failFast === 'boolean' ? config.failFast : true;\n const outputPath =\n typeof config.outputPath === 'string' ? config.outputPath : undefined;\n\n const sorted = sortGates(normalized);\n\n return {\n config: {\n gates: sorted,\n failFast,\n outputPath,\n },\n configPath: filePath,\n };\n }\n\n return { config: null };\n}\n","import { promises as fs } from 'node:fs';\nimport { execSync } from 'node:child_process';\nimport path from 'node:path';\nimport type { Gate, GateConfig } from './types.js';\n\nexport const DEFAULT_CONFIG_FILENAME = 'gate.config.json';\n\ntype PackageManager = 'npm' | 'yarn' | 'pnpm';\n\ntype DetectedProject =\n | {\n kind: 'node';\n packageManager: PackageManager;\n packageJson: Record<string, unknown>;\n }\n | { kind: 'python'; requirements: Set<string> }\n | { kind: 'unknown' };\n\nexport interface InitOptions {\n cwd?: string;\n filename?: string;\n force?: boolean;\n print?: boolean;\n skipHook?: boolean;\n}\n\nexport interface InitResult {\n config: GateConfig;\n configPath: string;\n created: boolean;\n projectKind: DetectedProject['kind'];\n warnings: string[];\n error?: string;\n gitignoreUpdated?: boolean;\n hookConfigured?: boolean;\n hookAlreadyExists?: boolean;\n}\n\nasync function fileExists(filePath: string): Promise<boolean> {\n try {\n await fs.access(filePath);\n return true;\n } catch {\n return false;\n }\n}\n\nasync function detectPackageManager(cwd: string): Promise<PackageManager> {\n if (await fileExists(path.join(cwd, 'pnpm-lock.yaml'))) {\n return 'pnpm';\n }\n if (await fileExists(path.join(cwd, 'yarn.lock'))) {\n return 'yarn';\n }\n return 'npm';\n}\n\nfunction runScriptCommand(\n packageManager: PackageManager,\n scriptName: string,\n): string {\n switch (packageManager) {\n case 'yarn':\n return `yarn ${scriptName}`;\n case 'pnpm':\n return `pnpm run ${scriptName}`;\n default:\n return `npm run ${scriptName}`;\n }\n}\n\nasync function readJsonFile(filePath: string): Promise<unknown> {\n const raw = await fs.readFile(filePath, 'utf8');\n return JSON.parse(raw);\n}\n\nfunction extractRequirementName(line: string): string | null {\n const trimmed = line.trim();\n if (trimmed.length === 0 || trimmed.startsWith('#')) {\n return null;\n }\n const noEnvMarker = trimmed.split(';', 1)[0]?.trim() ?? '';\n if (noEnvMarker.length === 0) {\n return null;\n }\n const name = noEnvMarker.split(/[<>=\\[]/, 1)[0]?.trim();\n if (!name) {\n return null;\n }\n return name.toLowerCase();\n}\n\nasync function detectProject(cwd: string): Promise<DetectedProject> {\n const packageJsonPath = path.join(cwd, 'package.json');\n if (await fileExists(packageJsonPath)) {\n const pm = await detectPackageManager(cwd);\n const parsed = await readJsonFile(packageJsonPath);\n if (!parsed || typeof parsed !== 'object') {\n throw new Error('package.json is not a JSON object.');\n }\n return {\n kind: 'node',\n packageManager: pm,\n packageJson: parsed as Record<string, unknown>,\n };\n }\n\n const requirementsPath = path.join(cwd, 'requirements.txt');\n const pyprojectPath = path.join(cwd, 'pyproject.toml');\n\n if (\n (await fileExists(requirementsPath)) ||\n (await fileExists(pyprojectPath))\n ) {\n const requirements = new Set<string>();\n if (await fileExists(requirementsPath)) {\n const raw = await fs.readFile(requirementsPath, 'utf8');\n for (const line of raw.split(/\\r?\\n/)) {\n const name = extractRequirementName(line);\n if (name) {\n requirements.add(name);\n }\n }\n }\n return { kind: 'python', requirements };\n }\n\n return { kind: 'unknown' };\n}\n\nfunction getPackageJsonScripts(\n packageJson: Record<string, unknown>,\n): Record<string, string> {\n const scripts = packageJson.scripts;\n if (!scripts || typeof scripts !== 'object') {\n return {};\n }\n const result: Record<string, string> = {};\n for (const [key, value] of Object.entries(\n scripts as Record<string, unknown>,\n )) {\n if (typeof value === 'string') {\n result[key] = value;\n }\n }\n return result;\n}\n\nfunction getPackageJsonDeps(packageJson: Record<string, unknown>): Set<string> {\n const deps = new Set<string>();\n const sections = [\n 'dependencies',\n 'devDependencies',\n 'peerDependencies',\n 'optionalDependencies',\n ] as const;\n for (const section of sections) {\n const values = packageJson[section];\n if (!values || typeof values !== 'object') {\n continue;\n }\n for (const name of Object.keys(values as Record<string, unknown>)) {\n deps.add(name);\n }\n }\n return deps;\n}\n\nasync function inferNodeGates(\n cwd: string,\n packageManager: PackageManager,\n packageJson: Record<string, unknown>,\n warnings: string[],\n): Promise<Gate[]> {\n const gates: Gate[] = [];\n const scripts = getPackageJsonScripts(packageJson);\n const deps = getPackageJsonDeps(packageJson);\n\n const addIfScript = (name: string, order: number) => {\n if (!scripts[name]) {\n return;\n }\n gates.push({\n name,\n order,\n command: runScriptCommand(packageManager, name),\n });\n };\n\n addIfScript('lint', 10);\n addIfScript('typecheck', 20);\n addIfScript('test', 30);\n addIfScript('build', 40);\n\n const tsconfigPath = path.join(cwd, 'tsconfig.json');\n const hasTypescript = deps.has('typescript');\n if (hasTypescript && (await fileExists(tsconfigPath)) && !scripts.typecheck) {\n warnings.push(\n \"No 'typecheck' script found; generating a default tsc gate.\",\n );\n gates.splice(1, 0, {\n name: 'typecheck',\n order: 20,\n command: 'npx tsc -p tsconfig.json --noEmit',\n });\n }\n\n return gates;\n}\n\nfunction inferPythonGates(\n requirements: Set<string>,\n warnings: string[],\n): Gate[] {\n const gates: Gate[] = [];\n\n if (requirements.has('ruff')) {\n gates.push({ name: 'lint', order: 10, command: 'python -m ruff check .' });\n }\n\n if (requirements.has('mypy')) {\n gates.push({ name: 'typecheck', order: 20, command: 'python -m mypy .' });\n }\n\n if (requirements.has('pytest')) {\n gates.push({ name: 'test', order: 30, command: 'python -m pytest -q' });\n } else {\n warnings.push(\n 'pytest not detected in requirements; no test gate generated.',\n );\n }\n\n return gates;\n}\n\nasync function updateGitignore(cwd: string): Promise<boolean> {\n const gitignorePath = path.join(cwd, '.gitignore');\n const filePattern = 'gate-results-*.json';\n\n try {\n // Use git check-ignore to test if the pattern would already cover gate result files\n // This handles all edge cases: broader patterns, multiple patterns, etc.\n execSync('git check-ignore -q gate-results-12345.json', {\n cwd,\n stdio: 'ignore',\n });\n // Exit code 0 means the file would be ignored - pattern already covered\n return false;\n } catch {\n // Not ignored or git not available - need to add the pattern\n }\n\n try {\n // Check if .gitignore exists and read it\n const exists = await fileExists(gitignorePath);\n const content = exists ? await fs.readFile(gitignorePath, 'utf8') : '';\n\n // Append the pattern with proper newline handling\n const newline = content && !content.endsWith('\\n') ? '\\n' : '';\n await fs.appendFile(gitignorePath, `${newline}${filePattern}\\n`, 'utf8');\n return true;\n } catch {\n // Silently fail if we can't update .gitignore\n return false;\n }\n}\n\ninterface ClaudeHookCommand {\n type: string;\n command: string;\n}\n\ninterface ClaudeHookConfig {\n matcher?: Record<string, unknown> | string;\n hooks: ClaudeHookCommand[];\n}\n\ninterface ClaudeSettings {\n hooks?: {\n Stop?: ClaudeHookConfig[];\n [key: string]: ClaudeHookConfig[] | undefined;\n };\n [key: string]: unknown;\n}\n\nconst RALPH_GATE_HOOK_COMMAND = 'npx ralph-gate --hook';\n\nasync function setupClaudeHook(\n cwd: string,\n): Promise<{ configured: boolean; alreadyExists: boolean }> {\n const claudeDir = path.join(cwd, '.claude');\n const settingsPath = path.join(claudeDir, 'settings.local.json');\n\n try {\n // Ensure .claude directory exists\n await fs.mkdir(claudeDir, { recursive: true });\n\n let settings: ClaudeSettings = {};\n\n // Read existing settings if file exists\n if (await fileExists(settingsPath)) {\n try {\n const content = await fs.readFile(settingsPath, 'utf8');\n const parsed = JSON.parse(content);\n if (parsed && typeof parsed === 'object') {\n settings = parsed as ClaudeSettings;\n }\n } catch {\n // If we can't parse existing file, start fresh but warn\n settings = {};\n }\n }\n\n // Initialize hooks structure if needed\n if (!settings.hooks) {\n settings.hooks = {};\n }\n if (!settings.hooks.Stop) {\n settings.hooks.Stop = [];\n }\n\n // Filter out invalid/legacy hook configurations\n // 1. Must have 'hooks' array\n // 2. Must NOT have a matcher that is an object (Stop hooks expect string or undefined)\n settings.hooks.Stop = settings.hooks.Stop.filter((item) => {\n if (!item || typeof item !== 'object') return false;\n if (!('hooks' in item) || !Array.isArray(item.hooks)) return false;\n if ('matcher' in item && typeof item.matcher === 'object') return false; // Remove legacy object matchers\n return true;\n });\n\n // Check if ralph-gate hook already exists in new format\n let hookExists = false;\n for (const item of settings.hooks.Stop) {\n if ('hooks' in item && Array.isArray(item.hooks)) {\n const hasRalphGate = item.hooks.some(\n (hook) =>\n hook.type === 'command' && hook.command === RALPH_GATE_HOOK_COMMAND,\n );\n if (hasRalphGate) {\n hookExists = true;\n break;\n }\n }\n }\n\n if (hookExists) {\n return { configured: false, alreadyExists: true };\n }\n\n // Append the ralph-gate hook with new format (no matcher for Stop hooks)\n settings.hooks.Stop.push({\n hooks: [\n {\n type: 'command',\n command: RALPH_GATE_HOOK_COMMAND,\n },\n ],\n });\n\n // Write updated settings\n await fs.writeFile(\n settingsPath,\n JSON.stringify(settings, null, 2) + '\\n',\n 'utf8',\n );\n\n return { configured: true, alreadyExists: false };\n } catch {\n // Silently fail if we can't setup hook\n return { configured: false, alreadyExists: false };\n }\n}\n\nexport async function generateGateConfig(cwd: string): Promise<{\n config: GateConfig;\n projectKind: DetectedProject['kind'];\n warnings: string[];\n}> {\n const warnings: string[] = [];\n const detected = await detectProject(cwd);\n\n let gates: Gate[] = [];\n\n if (detected.kind === 'node') {\n gates = await inferNodeGates(\n cwd,\n detected.packageManager,\n detected.packageJson,\n warnings,\n );\n } else if (detected.kind === 'python') {\n gates = inferPythonGates(detected.requirements, warnings);\n } else {\n warnings.push(\n 'No supported project markers found; creating an empty gate config.',\n );\n }\n\n return {\n config: {\n gates,\n failFast: true,\n },\n projectKind: detected.kind,\n warnings,\n };\n}\n\nexport async function initConfigFile(\n options: InitOptions = {},\n): Promise<InitResult> {\n const cwd = options.cwd ?? process.cwd();\n const filename = options.filename ?? DEFAULT_CONFIG_FILENAME;\n const configPath = path.join(cwd, filename);\n\n const { config, projectKind, warnings } = await generateGateConfig(cwd);\n\n if (options.print) {\n return {\n config,\n configPath,\n created: false,\n projectKind,\n warnings,\n };\n }\n\n const writeFlag = options.force ? 'w' : 'wx';\n\n try {\n await fs.writeFile(configPath, JSON.stringify(config, null, 2) + '\\n', {\n encoding: 'utf8',\n flag: writeFlag,\n });\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n const code = (error as NodeJS.ErrnoException | null)?.code;\n if (!options.force && code === 'EEXIST') {\n return {\n config,\n configPath,\n created: false,\n projectKind,\n warnings,\n error: `Config already exists at ${configPath}. Use --force to overwrite.`,\n };\n }\n return {\n config,\n configPath,\n created: false,\n projectKind,\n warnings,\n error: `Unable to write config at ${configPath}: ${message}`,\n };\n }\n\n // Update .gitignore to include gate result files\n const gitignoreUpdated = await updateGitignore(cwd);\n\n // Setup Claude hook unless skipped\n let hookConfigured: boolean | undefined;\n let hookAlreadyExists: boolean | undefined;\n\n if (!options.skipHook) {\n const hookResult = await setupClaudeHook(cwd);\n hookConfigured = hookResult.configured;\n hookAlreadyExists = hookResult.alreadyExists;\n }\n\n return {\n config,\n configPath,\n created: true,\n projectKind,\n warnings,\n gitignoreUpdated,\n hookConfigured,\n hookAlreadyExists,\n };\n}\n","import { spawn } from 'node:child_process';\nimport type { Gate, GateResult, GateRunSummary } from './types.js';\n\nexport interface RunGatesOptions {\n failFast?: boolean;\n verbose?: boolean;\n shell?: string;\n cwd?: string;\n onGateStart?: (gate: Gate) => void;\n onGateOutput?: (\n gate: Gate,\n stream: 'stdout' | 'stderr',\n text: string,\n ) => void;\n onGateComplete?: (result: GateResult) => void;\n}\n\nfunction resolveShell(shell?: string): string | boolean {\n if (typeof shell === 'string' && shell.length > 0) {\n return shell;\n }\n const envShell = process.env.SHELL;\n return envShell && envShell.length > 0 ? envShell : true;\n}\n\nasync function runGate(\n gate: Gate,\n options: RunGatesOptions,\n): Promise<GateResult> {\n const start = Date.now();\n const shell = resolveShell(options.shell);\n const env = process.env;\n\n return new Promise((resolve) => {\n options.onGateStart?.(gate);\n const child = spawn(gate.command, {\n shell,\n env,\n cwd: options.cwd,\n });\n\n let stdout = '';\n let stderr = '';\n let exitCode: number | null = null;\n let resolved = false;\n\n const finalize = () => {\n if (resolved) {\n return;\n }\n resolved = true;\n const durationMs = Date.now() - start;\n const result: GateResult = {\n name: gate.name,\n passed: exitCode === 0,\n exitCode,\n stdout,\n stderr,\n durationMs,\n skipped: false,\n blocking: gate.blocking !== false,\n timestamp: new Date().toISOString(),\n };\n options.onGateComplete?.(result);\n resolve(result);\n };\n\n child.stdout?.on('data', (chunk) => {\n const text = chunk.toString();\n stdout += text;\n if (options.verbose) {\n process.stdout.write(text);\n }\n options.onGateOutput?.(gate, 'stdout', text);\n });\n\n child.stderr?.on('data', (chunk) => {\n const text = chunk.toString();\n stderr += text;\n if (options.verbose) {\n process.stderr.write(text);\n }\n options.onGateOutput?.(gate, 'stderr', text);\n });\n\n child.on('error', (error) => {\n stderr += error.message;\n exitCode = null;\n finalize();\n });\n\n child.on('close', (code) => {\n exitCode = code;\n finalize();\n });\n });\n}\n\nexport async function runGates(\n gates: Gate[],\n options: RunGatesOptions = {},\n): Promise<GateRunSummary> {\n const results: GateResult[] = [];\n const warnings: string[] = [];\n const start = Date.now();\n let firstFailure: GateResult | null = null;\n\n for (const gate of gates) {\n const isBlocking = gate.blocking !== false;\n const shouldSkip =\n options.failFast !== false && firstFailure !== null && isBlocking;\n\n if (shouldSkip) {\n const skippedResult: GateResult = {\n name: gate.name,\n passed: true,\n exitCode: null,\n stdout: '',\n stderr: '',\n durationMs: 0,\n skipped: true,\n blocking: isBlocking,\n timestamp: new Date().toISOString(),\n };\n results.push(skippedResult);\n options.onGateComplete?.(skippedResult);\n continue;\n }\n\n const result = await runGate(gate, options);\n results.push(result);\n\n if (!result.passed) {\n if (result.blocking) {\n if (!firstFailure) {\n firstFailure = result;\n }\n } else {\n warnings.push(result.name);\n }\n }\n }\n\n const totalDurationMs = Date.now() - start;\n const passed = firstFailure === null;\n\n return {\n passed,\n timestamp: new Date().toISOString(),\n totalDurationMs,\n results,\n firstFailure,\n warnings,\n };\n}\n","import { promises as fs } from 'node:fs';\nimport path from 'node:path';\nimport type { GateResult, GateRunSummary } from './types.js';\n\nconst MAX_FAILURE_CHARS = 4000;\nconst HEAD_RATIO = 0.6;\n\nfunction truncateOutput(text: string, maxChars: number): string {\n const trimmed = text.trimEnd();\n if (maxChars <= 0 || trimmed.length === 0) {\n return '';\n }\n if (trimmed.length <= maxChars) {\n return trimmed;\n }\n const headChars = Math.max(1, Math.floor(maxChars * HEAD_RATIO));\n const tailChars = Math.max(0, maxChars - headChars);\n const omitted = trimmed.length - maxChars;\n const marker = `\\n...<${omitted} chars omitted>...\\n`;\n const head = trimmed.slice(0, headChars);\n const tail = tailChars > 0 ? trimmed.slice(-tailChars) : '';\n return `${head}${marker}${tail}`;\n}\n\nfunction splitBudget(\n stdoutLen: number,\n stderrLen: number,\n): {\n stdout: number;\n stderr: number;\n} {\n if (stdoutLen === 0 && stderrLen === 0) {\n return { stdout: 0, stderr: 0 };\n }\n if (stdoutLen === 0) {\n return { stdout: 0, stderr: MAX_FAILURE_CHARS };\n }\n if (stderrLen === 0) {\n return { stdout: MAX_FAILURE_CHARS, stderr: 0 };\n }\n\n const base = Math.floor(MAX_FAILURE_CHARS / 2);\n let stdout = Math.min(stdoutLen, base);\n let stderr = Math.min(stderrLen, base);\n let remaining = MAX_FAILURE_CHARS - stdout - stderr;\n\n if (remaining > 0) {\n const stdoutRemaining = stdoutLen - stdout;\n const stderrRemaining = stderrLen - stderr;\n if (stdoutRemaining === 0) {\n stderr += remaining;\n } else if (stderrRemaining === 0) {\n stdout += remaining;\n } else {\n const totalRemaining = stdoutRemaining + stderrRemaining;\n const stdoutExtra = Math.round(\n (stdoutRemaining / totalRemaining) * remaining,\n );\n stdout += stdoutExtra;\n stderr += remaining - stdoutExtra;\n }\n }\n\n return { stdout, stderr };\n}\n\nfunction colorize(text: string, code: string, enabled: boolean): string {\n if (!enabled) {\n return text;\n }\n return `\\u001b[${code}m${text}\\u001b[0m`;\n}\n\nfunction formatResultLine(result: GateResult, color: boolean): string {\n const statusSymbol = result.skipped ? '⊘' : result.passed ? '✓' : '✗';\n\n let symbolColor = '32';\n if (result.skipped) {\n symbolColor = '90';\n } else if (!result.passed) {\n symbolColor = '31';\n }\n\n const parts: string[] = [];\n if (result.skipped) {\n parts.push('skipped');\n } else if (!result.passed) {\n parts.push(`exit ${result.exitCode ?? 'null'}`);\n }\n parts.push(`${result.durationMs}ms`);\n\n const details = parts.length > 0 ? ` (${parts.join(', ')})` : '';\n const coloredSymbol = colorize(statusSymbol, symbolColor, color);\n return `${coloredSymbol} ${result.name}${details}`;\n}\n\nexport function formatFailureContext(stderr: string, stdout = ''): string {\n const trimmedStderr = stderr.trimEnd();\n const trimmedStdout = stdout.trimEnd();\n\n if (trimmedStderr.length === 0 && trimmedStdout.length === 0) {\n return 'No output captured.';\n }\n\n if (trimmedStderr.length > 0 && trimmedStdout.length > 0) {\n const budget = splitBudget(trimmedStdout.length, trimmedStderr.length);\n const stderrSection = truncateOutput(trimmedStderr, budget.stderr);\n const stdoutSection = truncateOutput(trimmedStdout, budget.stdout);\n return `STDERR:\\n${stderrSection}\\n\\nSTDOUT:\\n${stdoutSection}`;\n }\n\n if (trimmedStderr.length > 0) {\n return truncateOutput(trimmedStderr, MAX_FAILURE_CHARS);\n }\n\n return truncateOutput(trimmedStdout, MAX_FAILURE_CHARS);\n}\n\nexport function formatConsoleOutput(summary: GateRunSummary): string {\n const color = Boolean(process.stdout.isTTY);\n const lines: string[] = [];\n\n for (const result of summary.results) {\n lines.push(formatResultLine(result, color));\n }\n\n if (summary.warnings.length > 0) {\n const warningLine = `Warnings: ${summary.warnings.join(', ')}`;\n lines.push(colorize(warningLine, '33', color));\n }\n\n const finalLine = summary.passed\n ? colorize('All blocking gates passed.', '32', color)\n : colorize('Blocking gate failed.', '31', color);\n lines.push(finalLine);\n\n return lines.join('\\n');\n}\n\nexport async function writeResultsFile(\n summary: GateRunSummary,\n outputPath: string,\n): Promise<void> {\n const data = JSON.stringify(summary, null, 2);\n const dir = path.dirname(outputPath);\n try {\n await fs.mkdir(dir, { recursive: true });\n } catch {\n // Ignore error if directory already exists or can't be created\n }\n await fs.writeFile(outputPath, data, 'utf8');\n}\n","import type { GateRunSummary, HookOutput } from './types.js';\nimport { formatFailureContext } from './output.js';\n\nexport function generateHookResponse(summary: GateRunSummary): HookOutput {\n const warnings = summary.warnings.length > 0 ? summary.warnings : undefined;\n\n if (summary.passed) {\n return warnings ? { warnings } : {};\n }\n\n const failure = summary.firstFailure;\n if (failure) {\n const reason = `Gate '${failure.name}' failed (exit ${failure.exitCode ?? 'null'}):\\n${formatFailureContext(failure.stderr, failure.stdout)}`;\n return warnings\n ? { decision: 'block', reason, warnings }\n : { decision: 'block', reason };\n }\n\n const reason = 'Gate run failed without a blocking gate result.';\n return warnings\n ? { decision: 'block', reason, warnings }\n : { decision: 'block', reason };\n}\n\nexport function outputHookResponse(output: HookOutput): void {\n process.stdout.write(`${JSON.stringify(output)}\\n`);\n}\n"],"mappings":"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","names":["path","fs","path","fs","path","reason","path","options","error","summary","outputPath","shellLabel"]}
package/dist/index.js CHANGED
@@ -236,6 +236,7 @@ async function loadConfig(cwd = process.cwd()) {
236
236
 
237
237
  // src/output.ts
238
238
  import { promises as fs2 } from "fs";
239
+ import path2 from "path";
239
240
  var MAX_FAILURE_CHARS = 4e3;
240
241
  var HEAD_RATIO = 0.6;
241
242
  function truncateOutput(text, maxChars) {
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/runner.ts","../src/config.ts","../src/output.ts","../src/hook.ts"],"sourcesContent":["import { spawn } from 'node:child_process';\nimport type { Gate, GateResult, GateRunSummary } from './types.js';\n\nexport interface RunGatesOptions {\n failFast?: boolean;\n verbose?: boolean;\n shell?: string;\n cwd?: string;\n onGateStart?: (gate: Gate) => void;\n onGateOutput?: (\n gate: Gate,\n stream: 'stdout' | 'stderr',\n text: string,\n ) => void;\n onGateComplete?: (result: GateResult) => void;\n}\n\nfunction resolveShell(shell?: string): string | boolean {\n if (typeof shell === 'string' && shell.length > 0) {\n return shell;\n }\n const envShell = process.env.SHELL;\n return envShell && envShell.length > 0 ? envShell : true;\n}\n\nasync function runGate(\n gate: Gate,\n options: RunGatesOptions,\n): Promise<GateResult> {\n const start = Date.now();\n const shell = resolveShell(options.shell);\n const env = process.env;\n\n return new Promise((resolve) => {\n options.onGateStart?.(gate);\n const child = spawn(gate.command, {\n shell,\n env,\n cwd: options.cwd,\n });\n\n let stdout = '';\n let stderr = '';\n let exitCode: number | null = null;\n let resolved = false;\n\n const finalize = () => {\n if (resolved) {\n return;\n }\n resolved = true;\n const durationMs = Date.now() - start;\n const result: GateResult = {\n name: gate.name,\n passed: exitCode === 0,\n exitCode,\n stdout,\n stderr,\n durationMs,\n skipped: false,\n blocking: gate.blocking !== false,\n timestamp: new Date().toISOString(),\n };\n options.onGateComplete?.(result);\n resolve(result);\n };\n\n child.stdout?.on('data', (chunk) => {\n const text = chunk.toString();\n stdout += text;\n if (options.verbose) {\n process.stdout.write(text);\n }\n options.onGateOutput?.(gate, 'stdout', text);\n });\n\n child.stderr?.on('data', (chunk) => {\n const text = chunk.toString();\n stderr += text;\n if (options.verbose) {\n process.stderr.write(text);\n }\n options.onGateOutput?.(gate, 'stderr', text);\n });\n\n child.on('error', (error) => {\n stderr += error.message;\n exitCode = null;\n finalize();\n });\n\n child.on('close', (code) => {\n exitCode = code;\n finalize();\n });\n });\n}\n\nexport async function runGates(\n gates: Gate[],\n options: RunGatesOptions = {},\n): Promise<GateRunSummary> {\n const results: GateResult[] = [];\n const warnings: string[] = [];\n const start = Date.now();\n let firstFailure: GateResult | null = null;\n\n for (const gate of gates) {\n const isBlocking = gate.blocking !== false;\n const shouldSkip =\n options.failFast !== false && firstFailure !== null && isBlocking;\n\n if (shouldSkip) {\n const skippedResult: GateResult = {\n name: gate.name,\n passed: true,\n exitCode: null,\n stdout: '',\n stderr: '',\n durationMs: 0,\n skipped: true,\n blocking: isBlocking,\n timestamp: new Date().toISOString(),\n };\n results.push(skippedResult);\n options.onGateComplete?.(skippedResult);\n continue;\n }\n\n const result = await runGate(gate, options);\n results.push(result);\n\n if (!result.passed) {\n if (result.blocking) {\n if (!firstFailure) {\n firstFailure = result;\n }\n } else {\n warnings.push(result.name);\n }\n }\n }\n\n const totalDurationMs = Date.now() - start;\n const passed = firstFailure === null;\n\n return {\n passed,\n timestamp: new Date().toISOString(),\n totalDurationMs,\n results,\n firstFailure,\n warnings,\n };\n}\n","import { promises as fs } from 'node:fs';\nimport path from 'node:path';\nimport type { Gate, GateConfig } from './types.js';\n\nconst CONFIG_FILES = ['gate.config.json', '.gaterc.json', '.gaterc'];\n\nexport interface LoadConfigResult {\n config: GateConfig | null;\n configPath?: string;\n error?: string;\n}\n\nfunction normalizeGate(gate: Gate): Gate {\n const order = typeof gate.order === 'number' ? gate.order : 100;\n const enabled = typeof gate.enabled === 'boolean' ? gate.enabled : true;\n const blocking = typeof gate.blocking === 'boolean' ? gate.blocking : true;\n\n return {\n ...gate,\n order,\n enabled,\n blocking,\n description:\n typeof gate.description === 'string' ? gate.description : undefined,\n name: gate.name,\n command: gate.command,\n } as Gate;\n}\n\nfunction validateGate(gate: unknown): string | null {\n if (!gate || typeof gate !== 'object') {\n return 'Gate entries must be objects.';\n }\n const maybeGate = gate as Gate;\n if (typeof maybeGate.name !== 'string' || maybeGate.name.trim() === '') {\n return 'Gate is missing required field: name.';\n }\n if (\n typeof maybeGate.command !== 'string' ||\n maybeGate.command.trim() === ''\n ) {\n return `Gate '${maybeGate.name}' is missing required field: command.`;\n }\n return null;\n}\n\nfunction sortGates(gates: Gate[]): Gate[] {\n const withIndex = gates.map((gate, index) => ({ gate, index }));\n withIndex.sort((a, b) => {\n const orderA = typeof a.gate.order === 'number' ? a.gate.order : 100;\n const orderB = typeof b.gate.order === 'number' ? b.gate.order : 100;\n if (orderA !== orderB) {\n return orderA - orderB;\n }\n return a.index - b.index;\n });\n return withIndex.map((entry) => entry.gate);\n}\n\nexport async function loadConfig(\n cwd: string = process.cwd(),\n): Promise<LoadConfigResult> {\n for (const filename of CONFIG_FILES) {\n const filePath = path.join(cwd, filename);\n try {\n await fs.access(filePath);\n } catch {\n continue;\n }\n\n let raw: string;\n try {\n raw = await fs.readFile(filePath, 'utf8');\n } catch (error) {\n return {\n config: null,\n configPath: filePath,\n error: `Invalid config: unable to read ${filename}: ${(error as Error).message}`,\n };\n }\n\n let parsed: unknown;\n try {\n parsed = JSON.parse(raw);\n } catch (error) {\n return {\n config: null,\n configPath: filePath,\n error: `Invalid config: malformed JSON in ${filename}: ${(error as Error).message}`,\n };\n }\n\n if (!parsed || typeof parsed !== 'object') {\n return {\n config: null,\n configPath: filePath,\n error: `Invalid config: expected object in ${filename}.`,\n };\n }\n\n const config = parsed as GateConfig;\n if (!Array.isArray(config.gates)) {\n return {\n config: null,\n configPath: filePath,\n error: `Invalid config: missing required 'gates' array in ${filename}.`,\n };\n }\n\n const normalized: Gate[] = [];\n for (const gate of config.gates) {\n const gateError = validateGate(gate as Gate);\n if (gateError) {\n return {\n config: null,\n configPath: filePath,\n error: `Invalid config: ${gateError}`,\n };\n }\n const normalizedGate = normalizeGate(gate as Gate);\n if (normalizedGate.enabled === false) {\n continue;\n }\n normalized.push(normalizedGate);\n }\n\n const failFast =\n typeof config.failFast === 'boolean' ? config.failFast : true;\n const outputPath =\n typeof config.outputPath === 'string' ? config.outputPath : undefined;\n\n const sorted = sortGates(normalized);\n\n return {\n config: {\n gates: sorted,\n failFast,\n outputPath,\n },\n configPath: filePath,\n };\n }\n\n return { config: null };\n}\n","import { promises as fs } from 'node:fs';\nimport type { GateResult, GateRunSummary } from './types.js';\n\nconst MAX_FAILURE_CHARS = 4000;\nconst HEAD_RATIO = 0.6;\n\nfunction truncateOutput(text: string, maxChars: number): string {\n const trimmed = text.trimEnd();\n if (maxChars <= 0 || trimmed.length === 0) {\n return '';\n }\n if (trimmed.length <= maxChars) {\n return trimmed;\n }\n const headChars = Math.max(1, Math.floor(maxChars * HEAD_RATIO));\n const tailChars = Math.max(0, maxChars - headChars);\n const omitted = trimmed.length - maxChars;\n const marker = `\\n...<${omitted} chars omitted>...\\n`;\n const head = trimmed.slice(0, headChars);\n const tail = tailChars > 0 ? trimmed.slice(-tailChars) : '';\n return `${head}${marker}${tail}`;\n}\n\nfunction splitBudget(\n stdoutLen: number,\n stderrLen: number,\n): {\n stdout: number;\n stderr: number;\n} {\n if (stdoutLen === 0 && stderrLen === 0) {\n return { stdout: 0, stderr: 0 };\n }\n if (stdoutLen === 0) {\n return { stdout: 0, stderr: MAX_FAILURE_CHARS };\n }\n if (stderrLen === 0) {\n return { stdout: MAX_FAILURE_CHARS, stderr: 0 };\n }\n\n const base = Math.floor(MAX_FAILURE_CHARS / 2);\n let stdout = Math.min(stdoutLen, base);\n let stderr = Math.min(stderrLen, base);\n let remaining = MAX_FAILURE_CHARS - stdout - stderr;\n\n if (remaining > 0) {\n const stdoutRemaining = stdoutLen - stdout;\n const stderrRemaining = stderrLen - stderr;\n if (stdoutRemaining === 0) {\n stderr += remaining;\n } else if (stderrRemaining === 0) {\n stdout += remaining;\n } else {\n const totalRemaining = stdoutRemaining + stderrRemaining;\n const stdoutExtra = Math.round(\n (stdoutRemaining / totalRemaining) * remaining,\n );\n stdout += stdoutExtra;\n stderr += remaining - stdoutExtra;\n }\n }\n\n return { stdout, stderr };\n}\n\nfunction colorize(text: string, code: string, enabled: boolean): string {\n if (!enabled) {\n return text;\n }\n return `\\u001b[${code}m${text}\\u001b[0m`;\n}\n\nfunction formatResultLine(result: GateResult, color: boolean): string {\n const statusSymbol = result.skipped ? '⊘' : result.passed ? '✓' : '✗';\n\n let symbolColor = '32';\n if (result.skipped) {\n symbolColor = '90';\n } else if (!result.passed) {\n symbolColor = '31';\n }\n\n const parts: string[] = [];\n if (result.skipped) {\n parts.push('skipped');\n } else if (!result.passed) {\n parts.push(`exit ${result.exitCode ?? 'null'}`);\n }\n parts.push(`${result.durationMs}ms`);\n\n const details = parts.length > 0 ? ` (${parts.join(', ')})` : '';\n const coloredSymbol = colorize(statusSymbol, symbolColor, color);\n return `${coloredSymbol} ${result.name}${details}`;\n}\n\nexport function formatFailureContext(stderr: string, stdout = ''): string {\n const trimmedStderr = stderr.trimEnd();\n const trimmedStdout = stdout.trimEnd();\n\n if (trimmedStderr.length === 0 && trimmedStdout.length === 0) {\n return 'No output captured.';\n }\n\n if (trimmedStderr.length > 0 && trimmedStdout.length > 0) {\n const budget = splitBudget(trimmedStdout.length, trimmedStderr.length);\n const stderrSection = truncateOutput(trimmedStderr, budget.stderr);\n const stdoutSection = truncateOutput(trimmedStdout, budget.stdout);\n return `STDERR:\\n${stderrSection}\\n\\nSTDOUT:\\n${stdoutSection}`;\n }\n\n if (trimmedStderr.length > 0) {\n return truncateOutput(trimmedStderr, MAX_FAILURE_CHARS);\n }\n\n return truncateOutput(trimmedStdout, MAX_FAILURE_CHARS);\n}\n\nexport function formatConsoleOutput(summary: GateRunSummary): string {\n const color = Boolean(process.stdout.isTTY);\n const lines: string[] = [];\n\n for (const result of summary.results) {\n lines.push(formatResultLine(result, color));\n }\n\n if (summary.warnings.length > 0) {\n const warningLine = `Warnings: ${summary.warnings.join(', ')}`;\n lines.push(colorize(warningLine, '33', color));\n }\n\n const finalLine = summary.passed\n ? colorize('All blocking gates passed.', '32', color)\n : colorize('Blocking gate failed.', '31', color);\n lines.push(finalLine);\n\n return lines.join('\\n');\n}\n\nexport async function writeResultsFile(\n summary: GateRunSummary,\n outputPath: string,\n): Promise<void> {\n const data = JSON.stringify(summary, null, 2);\n await fs.writeFile(outputPath, data, 'utf8');\n}\n","import type { GateRunSummary, HookOutput } from './types.js';\nimport { formatFailureContext } from './output.js';\n\nexport function generateHookResponse(summary: GateRunSummary): HookOutput {\n const warnings = summary.warnings.length > 0 ? summary.warnings : undefined;\n\n if (summary.passed) {\n return warnings ? { warnings } : {};\n }\n\n const failure = summary.firstFailure;\n if (failure) {\n const reason = `Gate '${failure.name}' failed (exit ${failure.exitCode ?? 'null'}):\\n${formatFailureContext(failure.stderr, failure.stdout)}`;\n return warnings\n ? { decision: 'block', reason, warnings }\n : { decision: 'block', reason };\n }\n\n const reason = 'Gate run failed without a blocking gate result.';\n return warnings\n ? { decision: 'block', reason, warnings }\n : { decision: 'block', reason };\n}\n\nexport function outputHookResponse(output: HookOutput): void {\n process.stdout.write(`${JSON.stringify(output)}\\n`);\n}\n"],"mappings":"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","names":["fs","reason"]}
1
+ {"version":3,"sources":["../src/runner.ts","../src/config.ts","../src/output.ts","../src/hook.ts"],"sourcesContent":["import { spawn } from 'node:child_process';\nimport type { Gate, GateResult, GateRunSummary } from './types.js';\n\nexport interface RunGatesOptions {\n failFast?: boolean;\n verbose?: boolean;\n shell?: string;\n cwd?: string;\n onGateStart?: (gate: Gate) => void;\n onGateOutput?: (\n gate: Gate,\n stream: 'stdout' | 'stderr',\n text: string,\n ) => void;\n onGateComplete?: (result: GateResult) => void;\n}\n\nfunction resolveShell(shell?: string): string | boolean {\n if (typeof shell === 'string' && shell.length > 0) {\n return shell;\n }\n const envShell = process.env.SHELL;\n return envShell && envShell.length > 0 ? envShell : true;\n}\n\nasync function runGate(\n gate: Gate,\n options: RunGatesOptions,\n): Promise<GateResult> {\n const start = Date.now();\n const shell = resolveShell(options.shell);\n const env = process.env;\n\n return new Promise((resolve) => {\n options.onGateStart?.(gate);\n const child = spawn(gate.command, {\n shell,\n env,\n cwd: options.cwd,\n });\n\n let stdout = '';\n let stderr = '';\n let exitCode: number | null = null;\n let resolved = false;\n\n const finalize = () => {\n if (resolved) {\n return;\n }\n resolved = true;\n const durationMs = Date.now() - start;\n const result: GateResult = {\n name: gate.name,\n passed: exitCode === 0,\n exitCode,\n stdout,\n stderr,\n durationMs,\n skipped: false,\n blocking: gate.blocking !== false,\n timestamp: new Date().toISOString(),\n };\n options.onGateComplete?.(result);\n resolve(result);\n };\n\n child.stdout?.on('data', (chunk) => {\n const text = chunk.toString();\n stdout += text;\n if (options.verbose) {\n process.stdout.write(text);\n }\n options.onGateOutput?.(gate, 'stdout', text);\n });\n\n child.stderr?.on('data', (chunk) => {\n const text = chunk.toString();\n stderr += text;\n if (options.verbose) {\n process.stderr.write(text);\n }\n options.onGateOutput?.(gate, 'stderr', text);\n });\n\n child.on('error', (error) => {\n stderr += error.message;\n exitCode = null;\n finalize();\n });\n\n child.on('close', (code) => {\n exitCode = code;\n finalize();\n });\n });\n}\n\nexport async function runGates(\n gates: Gate[],\n options: RunGatesOptions = {},\n): Promise<GateRunSummary> {\n const results: GateResult[] = [];\n const warnings: string[] = [];\n const start = Date.now();\n let firstFailure: GateResult | null = null;\n\n for (const gate of gates) {\n const isBlocking = gate.blocking !== false;\n const shouldSkip =\n options.failFast !== false && firstFailure !== null && isBlocking;\n\n if (shouldSkip) {\n const skippedResult: GateResult = {\n name: gate.name,\n passed: true,\n exitCode: null,\n stdout: '',\n stderr: '',\n durationMs: 0,\n skipped: true,\n blocking: isBlocking,\n timestamp: new Date().toISOString(),\n };\n results.push(skippedResult);\n options.onGateComplete?.(skippedResult);\n continue;\n }\n\n const result = await runGate(gate, options);\n results.push(result);\n\n if (!result.passed) {\n if (result.blocking) {\n if (!firstFailure) {\n firstFailure = result;\n }\n } else {\n warnings.push(result.name);\n }\n }\n }\n\n const totalDurationMs = Date.now() - start;\n const passed = firstFailure === null;\n\n return {\n passed,\n timestamp: new Date().toISOString(),\n totalDurationMs,\n results,\n firstFailure,\n warnings,\n };\n}\n","import { promises as fs } from 'node:fs';\nimport path from 'node:path';\nimport type { Gate, GateConfig } from './types.js';\n\nconst CONFIG_FILES = ['gate.config.json', '.gaterc.json', '.gaterc'];\n\nexport interface LoadConfigResult {\n config: GateConfig | null;\n configPath?: string;\n error?: string;\n}\n\nfunction normalizeGate(gate: Gate): Gate {\n const order = typeof gate.order === 'number' ? gate.order : 100;\n const enabled = typeof gate.enabled === 'boolean' ? gate.enabled : true;\n const blocking = typeof gate.blocking === 'boolean' ? gate.blocking : true;\n\n return {\n ...gate,\n order,\n enabled,\n blocking,\n description:\n typeof gate.description === 'string' ? gate.description : undefined,\n name: gate.name,\n command: gate.command,\n } as Gate;\n}\n\nfunction validateGate(gate: unknown): string | null {\n if (!gate || typeof gate !== 'object') {\n return 'Gate entries must be objects.';\n }\n const maybeGate = gate as Gate;\n if (typeof maybeGate.name !== 'string' || maybeGate.name.trim() === '') {\n return 'Gate is missing required field: name.';\n }\n if (\n typeof maybeGate.command !== 'string' ||\n maybeGate.command.trim() === ''\n ) {\n return `Gate '${maybeGate.name}' is missing required field: command.`;\n }\n return null;\n}\n\nfunction sortGates(gates: Gate[]): Gate[] {\n const withIndex = gates.map((gate, index) => ({ gate, index }));\n withIndex.sort((a, b) => {\n const orderA = typeof a.gate.order === 'number' ? a.gate.order : 100;\n const orderB = typeof b.gate.order === 'number' ? b.gate.order : 100;\n if (orderA !== orderB) {\n return orderA - orderB;\n }\n return a.index - b.index;\n });\n return withIndex.map((entry) => entry.gate);\n}\n\nexport async function loadConfig(\n cwd: string = process.cwd(),\n): Promise<LoadConfigResult> {\n for (const filename of CONFIG_FILES) {\n const filePath = path.join(cwd, filename);\n try {\n await fs.access(filePath);\n } catch {\n continue;\n }\n\n let raw: string;\n try {\n raw = await fs.readFile(filePath, 'utf8');\n } catch (error) {\n return {\n config: null,\n configPath: filePath,\n error: `Invalid config: unable to read ${filename}: ${(error as Error).message}`,\n };\n }\n\n let parsed: unknown;\n try {\n parsed = JSON.parse(raw);\n } catch (error) {\n return {\n config: null,\n configPath: filePath,\n error: `Invalid config: malformed JSON in ${filename}: ${(error as Error).message}`,\n };\n }\n\n if (!parsed || typeof parsed !== 'object') {\n return {\n config: null,\n configPath: filePath,\n error: `Invalid config: expected object in ${filename}.`,\n };\n }\n\n const config = parsed as GateConfig;\n if (!Array.isArray(config.gates)) {\n return {\n config: null,\n configPath: filePath,\n error: `Invalid config: missing required 'gates' array in ${filename}.`,\n };\n }\n\n const normalized: Gate[] = [];\n for (const gate of config.gates) {\n const gateError = validateGate(gate as Gate);\n if (gateError) {\n return {\n config: null,\n configPath: filePath,\n error: `Invalid config: ${gateError}`,\n };\n }\n const normalizedGate = normalizeGate(gate as Gate);\n if (normalizedGate.enabled === false) {\n continue;\n }\n normalized.push(normalizedGate);\n }\n\n const failFast =\n typeof config.failFast === 'boolean' ? config.failFast : true;\n const outputPath =\n typeof config.outputPath === 'string' ? config.outputPath : undefined;\n\n const sorted = sortGates(normalized);\n\n return {\n config: {\n gates: sorted,\n failFast,\n outputPath,\n },\n configPath: filePath,\n };\n }\n\n return { config: null };\n}\n","import { promises as fs } from 'node:fs';\nimport path from 'node:path';\nimport type { GateResult, GateRunSummary } from './types.js';\n\nconst MAX_FAILURE_CHARS = 4000;\nconst HEAD_RATIO = 0.6;\n\nfunction truncateOutput(text: string, maxChars: number): string {\n const trimmed = text.trimEnd();\n if (maxChars <= 0 || trimmed.length === 0) {\n return '';\n }\n if (trimmed.length <= maxChars) {\n return trimmed;\n }\n const headChars = Math.max(1, Math.floor(maxChars * HEAD_RATIO));\n const tailChars = Math.max(0, maxChars - headChars);\n const omitted = trimmed.length - maxChars;\n const marker = `\\n...<${omitted} chars omitted>...\\n`;\n const head = trimmed.slice(0, headChars);\n const tail = tailChars > 0 ? trimmed.slice(-tailChars) : '';\n return `${head}${marker}${tail}`;\n}\n\nfunction splitBudget(\n stdoutLen: number,\n stderrLen: number,\n): {\n stdout: number;\n stderr: number;\n} {\n if (stdoutLen === 0 && stderrLen === 0) {\n return { stdout: 0, stderr: 0 };\n }\n if (stdoutLen === 0) {\n return { stdout: 0, stderr: MAX_FAILURE_CHARS };\n }\n if (stderrLen === 0) {\n return { stdout: MAX_FAILURE_CHARS, stderr: 0 };\n }\n\n const base = Math.floor(MAX_FAILURE_CHARS / 2);\n let stdout = Math.min(stdoutLen, base);\n let stderr = Math.min(stderrLen, base);\n let remaining = MAX_FAILURE_CHARS - stdout - stderr;\n\n if (remaining > 0) {\n const stdoutRemaining = stdoutLen - stdout;\n const stderrRemaining = stderrLen - stderr;\n if (stdoutRemaining === 0) {\n stderr += remaining;\n } else if (stderrRemaining === 0) {\n stdout += remaining;\n } else {\n const totalRemaining = stdoutRemaining + stderrRemaining;\n const stdoutExtra = Math.round(\n (stdoutRemaining / totalRemaining) * remaining,\n );\n stdout += stdoutExtra;\n stderr += remaining - stdoutExtra;\n }\n }\n\n return { stdout, stderr };\n}\n\nfunction colorize(text: string, code: string, enabled: boolean): string {\n if (!enabled) {\n return text;\n }\n return `\\u001b[${code}m${text}\\u001b[0m`;\n}\n\nfunction formatResultLine(result: GateResult, color: boolean): string {\n const statusSymbol = result.skipped ? '⊘' : result.passed ? '✓' : '✗';\n\n let symbolColor = '32';\n if (result.skipped) {\n symbolColor = '90';\n } else if (!result.passed) {\n symbolColor = '31';\n }\n\n const parts: string[] = [];\n if (result.skipped) {\n parts.push('skipped');\n } else if (!result.passed) {\n parts.push(`exit ${result.exitCode ?? 'null'}`);\n }\n parts.push(`${result.durationMs}ms`);\n\n const details = parts.length > 0 ? ` (${parts.join(', ')})` : '';\n const coloredSymbol = colorize(statusSymbol, symbolColor, color);\n return `${coloredSymbol} ${result.name}${details}`;\n}\n\nexport function formatFailureContext(stderr: string, stdout = ''): string {\n const trimmedStderr = stderr.trimEnd();\n const trimmedStdout = stdout.trimEnd();\n\n if (trimmedStderr.length === 0 && trimmedStdout.length === 0) {\n return 'No output captured.';\n }\n\n if (trimmedStderr.length > 0 && trimmedStdout.length > 0) {\n const budget = splitBudget(trimmedStdout.length, trimmedStderr.length);\n const stderrSection = truncateOutput(trimmedStderr, budget.stderr);\n const stdoutSection = truncateOutput(trimmedStdout, budget.stdout);\n return `STDERR:\\n${stderrSection}\\n\\nSTDOUT:\\n${stdoutSection}`;\n }\n\n if (trimmedStderr.length > 0) {\n return truncateOutput(trimmedStderr, MAX_FAILURE_CHARS);\n }\n\n return truncateOutput(trimmedStdout, MAX_FAILURE_CHARS);\n}\n\nexport function formatConsoleOutput(summary: GateRunSummary): string {\n const color = Boolean(process.stdout.isTTY);\n const lines: string[] = [];\n\n for (const result of summary.results) {\n lines.push(formatResultLine(result, color));\n }\n\n if (summary.warnings.length > 0) {\n const warningLine = `Warnings: ${summary.warnings.join(', ')}`;\n lines.push(colorize(warningLine, '33', color));\n }\n\n const finalLine = summary.passed\n ? colorize('All blocking gates passed.', '32', color)\n : colorize('Blocking gate failed.', '31', color);\n lines.push(finalLine);\n\n return lines.join('\\n');\n}\n\nexport async function writeResultsFile(\n summary: GateRunSummary,\n outputPath: string,\n): Promise<void> {\n const data = JSON.stringify(summary, null, 2);\n const dir = path.dirname(outputPath);\n try {\n await fs.mkdir(dir, { recursive: true });\n } catch {\n // Ignore error if directory already exists or can't be created\n }\n await fs.writeFile(outputPath, data, 'utf8');\n}\n","import type { GateRunSummary, HookOutput } from './types.js';\nimport { formatFailureContext } from './output.js';\n\nexport function generateHookResponse(summary: GateRunSummary): HookOutput {\n const warnings = summary.warnings.length > 0 ? summary.warnings : undefined;\n\n if (summary.passed) {\n return warnings ? { warnings } : {};\n }\n\n const failure = summary.firstFailure;\n if (failure) {\n const reason = `Gate '${failure.name}' failed (exit ${failure.exitCode ?? 'null'}):\\n${formatFailureContext(failure.stderr, failure.stdout)}`;\n return warnings\n ? { decision: 'block', reason, warnings }\n : { decision: 'block', reason };\n }\n\n const reason = 'Gate run failed without a blocking gate result.';\n return warnings\n ? { decision: 'block', reason, warnings }\n : { decision: 'block', reason };\n}\n\nexport function outputHookResponse(output: HookOutput): void {\n process.stdout.write(`${JSON.stringify(output)}\\n`);\n}\n"],"mappings":";AAAA,SAAS,aAAa;AAiBtB,SAAS,aAAa,OAAkC;AACtD,MAAI,OAAO,UAAU,YAAY,MAAM,SAAS,GAAG;AACjD,WAAO;AAAA,EACT;AACA,QAAM,WAAW,QAAQ,IAAI;AAC7B,SAAO,YAAY,SAAS,SAAS,IAAI,WAAW;AACtD;AAEA,eAAe,QACb,MACA,SACqB;AACrB,QAAM,QAAQ,KAAK,IAAI;AACvB,QAAM,QAAQ,aAAa,QAAQ,KAAK;AACxC,QAAM,MAAM,QAAQ;AAEpB,SAAO,IAAI,QAAQ,CAAC,YAAY;AAC9B,YAAQ,cAAc,IAAI;AAC1B,UAAM,QAAQ,MAAM,KAAK,SAAS;AAAA,MAChC;AAAA,MACA;AAAA,MACA,KAAK,QAAQ;AAAA,IACf,CAAC;AAED,QAAI,SAAS;AACb,QAAI,SAAS;AACb,QAAI,WAA0B;AAC9B,QAAI,WAAW;AAEf,UAAM,WAAW,MAAM;AACrB,UAAI,UAAU;AACZ;AAAA,MACF;AACA,iBAAW;AACX,YAAM,aAAa,KAAK,IAAI,IAAI;AAChC,YAAM,SAAqB;AAAA,QACzB,MAAM,KAAK;AAAA,QACX,QAAQ,aAAa;AAAA,QACrB;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA,SAAS;AAAA,QACT,UAAU,KAAK,aAAa;AAAA,QAC5B,YAAW,oBAAI,KAAK,GAAE,YAAY;AAAA,MACpC;AACA,cAAQ,iBAAiB,MAAM;AAC/B,cAAQ,MAAM;AAAA,IAChB;AAEA,UAAM,QAAQ,GAAG,QAAQ,CAAC,UAAU;AAClC,YAAM,OAAO,MAAM,SAAS;AAC5B,gBAAU;AACV,UAAI,QAAQ,SAAS;AACnB,gBAAQ,OAAO,MAAM,IAAI;AAAA,MAC3B;AACA,cAAQ,eAAe,MAAM,UAAU,IAAI;AAAA,IAC7C,CAAC;AAED,UAAM,QAAQ,GAAG,QAAQ,CAAC,UAAU;AAClC,YAAM,OAAO,MAAM,SAAS;AAC5B,gBAAU;AACV,UAAI,QAAQ,SAAS;AACnB,gBAAQ,OAAO,MAAM,IAAI;AAAA,MAC3B;AACA,cAAQ,eAAe,MAAM,UAAU,IAAI;AAAA,IAC7C,CAAC;AAED,UAAM,GAAG,SAAS,CAAC,UAAU;AAC3B,gBAAU,MAAM;AAChB,iBAAW;AACX,eAAS;AAAA,IACX,CAAC;AAED,UAAM,GAAG,SAAS,CAAC,SAAS;AAC1B,iBAAW;AACX,eAAS;AAAA,IACX,CAAC;AAAA,EACH,CAAC;AACH;AAEA,eAAsB,SACpB,OACA,UAA2B,CAAC,GACH;AACzB,QAAM,UAAwB,CAAC;AAC/B,QAAM,WAAqB,CAAC;AAC5B,QAAM,QAAQ,KAAK,IAAI;AACvB,MAAI,eAAkC;AAEtC,aAAW,QAAQ,OAAO;AACxB,UAAM,aAAa,KAAK,aAAa;AACrC,UAAM,aACJ,QAAQ,aAAa,SAAS,iBAAiB,QAAQ;AAEzD,QAAI,YAAY;AACd,YAAM,gBAA4B;AAAA,QAChC,MAAM,KAAK;AAAA,QACX,QAAQ;AAAA,QACR,UAAU;AAAA,QACV,QAAQ;AAAA,QACR,QAAQ;AAAA,QACR,YAAY;AAAA,QACZ,SAAS;AAAA,QACT,UAAU;AAAA,QACV,YAAW,oBAAI,KAAK,GAAE,YAAY;AAAA,MACpC;AACA,cAAQ,KAAK,aAAa;AAC1B,cAAQ,iBAAiB,aAAa;AACtC;AAAA,IACF;AAEA,UAAM,SAAS,MAAM,QAAQ,MAAM,OAAO;AAC1C,YAAQ,KAAK,MAAM;AAEnB,QAAI,CAAC,OAAO,QAAQ;AAClB,UAAI,OAAO,UAAU;AACnB,YAAI,CAAC,cAAc;AACjB,yBAAe;AAAA,QACjB;AAAA,MACF,OAAO;AACL,iBAAS,KAAK,OAAO,IAAI;AAAA,MAC3B;AAAA,IACF;AAAA,EACF;AAEA,QAAM,kBAAkB,KAAK,IAAI,IAAI;AACrC,QAAM,SAAS,iBAAiB;AAEhC,SAAO;AAAA,IACL;AAAA,IACA,YAAW,oBAAI,KAAK,GAAE,YAAY;AAAA,IAClC;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACF;;;AC1JA,SAAS,YAAY,UAAU;AAC/B,OAAO,UAAU;AAGjB,IAAM,eAAe,CAAC,oBAAoB,gBAAgB,SAAS;AAQnE,SAAS,cAAc,MAAkB;AACvC,QAAM,QAAQ,OAAO,KAAK,UAAU,WAAW,KAAK,QAAQ;AAC5D,QAAM,UAAU,OAAO,KAAK,YAAY,YAAY,KAAK,UAAU;AACnE,QAAM,WAAW,OAAO,KAAK,aAAa,YAAY,KAAK,WAAW;AAEtE,SAAO;AAAA,IACL,GAAG;AAAA,IACH;AAAA,IACA;AAAA,IACA;AAAA,IACA,aACE,OAAO,KAAK,gBAAgB,WAAW,KAAK,cAAc;AAAA,IAC5D,MAAM,KAAK;AAAA,IACX,SAAS,KAAK;AAAA,EAChB;AACF;AAEA,SAAS,aAAa,MAA8B;AAClD,MAAI,CAAC,QAAQ,OAAO,SAAS,UAAU;AACrC,WAAO;AAAA,EACT;AACA,QAAM,YAAY;AAClB,MAAI,OAAO,UAAU,SAAS,YAAY,UAAU,KAAK,KAAK,MAAM,IAAI;AACtE,WAAO;AAAA,EACT;AACA,MACE,OAAO,UAAU,YAAY,YAC7B,UAAU,QAAQ,KAAK,MAAM,IAC7B;AACA,WAAO,SAAS,UAAU,IAAI;AAAA,EAChC;AACA,SAAO;AACT;AAEA,SAAS,UAAU,OAAuB;AACxC,QAAM,YAAY,MAAM,IAAI,CAAC,MAAM,WAAW,EAAE,MAAM,MAAM,EAAE;AAC9D,YAAU,KAAK,CAAC,GAAG,MAAM;AACvB,UAAM,SAAS,OAAO,EAAE,KAAK,UAAU,WAAW,EAAE,KAAK,QAAQ;AACjE,UAAM,SAAS,OAAO,EAAE,KAAK,UAAU,WAAW,EAAE,KAAK,QAAQ;AACjE,QAAI,WAAW,QAAQ;AACrB,aAAO,SAAS;AAAA,IAClB;AACA,WAAO,EAAE,QAAQ,EAAE;AAAA,EACrB,CAAC;AACD,SAAO,UAAU,IAAI,CAAC,UAAU,MAAM,IAAI;AAC5C;AAEA,eAAsB,WACpB,MAAc,QAAQ,IAAI,GACC;AAC3B,aAAW,YAAY,cAAc;AACnC,UAAM,WAAW,KAAK,KAAK,KAAK,QAAQ;AACxC,QAAI;AACF,YAAM,GAAG,OAAO,QAAQ;AAAA,IAC1B,QAAQ;AACN;AAAA,IACF;AAEA,QAAI;AACJ,QAAI;AACF,YAAM,MAAM,GAAG,SAAS,UAAU,MAAM;AAAA,IAC1C,SAAS,OAAO;AACd,aAAO;AAAA,QACL,QAAQ;AAAA,QACR,YAAY;AAAA,QACZ,OAAO,kCAAkC,QAAQ,KAAM,MAAgB,OAAO;AAAA,MAChF;AAAA,IACF;AAEA,QAAI;AACJ,QAAI;AACF,eAAS,KAAK,MAAM,GAAG;AAAA,IACzB,SAAS,OAAO;AACd,aAAO;AAAA,QACL,QAAQ;AAAA,QACR,YAAY;AAAA,QACZ,OAAO,qCAAqC,QAAQ,KAAM,MAAgB,OAAO;AAAA,MACnF;AAAA,IACF;AAEA,QAAI,CAAC,UAAU,OAAO,WAAW,UAAU;AACzC,aAAO;AAAA,QACL,QAAQ;AAAA,QACR,YAAY;AAAA,QACZ,OAAO,sCAAsC,QAAQ;AAAA,MACvD;AAAA,IACF;AAEA,UAAM,SAAS;AACf,QAAI,CAAC,MAAM,QAAQ,OAAO,KAAK,GAAG;AAChC,aAAO;AAAA,QACL,QAAQ;AAAA,QACR,YAAY;AAAA,QACZ,OAAO,qDAAqD,QAAQ;AAAA,MACtE;AAAA,IACF;AAEA,UAAM,aAAqB,CAAC;AAC5B,eAAW,QAAQ,OAAO,OAAO;AAC/B,YAAM,YAAY,aAAa,IAAY;AAC3C,UAAI,WAAW;AACb,eAAO;AAAA,UACL,QAAQ;AAAA,UACR,YAAY;AAAA,UACZ,OAAO,mBAAmB,SAAS;AAAA,QACrC;AAAA,MACF;AACA,YAAM,iBAAiB,cAAc,IAAY;AACjD,UAAI,eAAe,YAAY,OAAO;AACpC;AAAA,MACF;AACA,iBAAW,KAAK,cAAc;AAAA,IAChC;AAEA,UAAM,WACJ,OAAO,OAAO,aAAa,YAAY,OAAO,WAAW;AAC3D,UAAM,aACJ,OAAO,OAAO,eAAe,WAAW,OAAO,aAAa;AAE9D,UAAM,SAAS,UAAU,UAAU;AAEnC,WAAO;AAAA,MACL,QAAQ;AAAA,QACN,OAAO;AAAA,QACP;AAAA,QACA;AAAA,MACF;AAAA,MACA,YAAY;AAAA,IACd;AAAA,EACF;AAEA,SAAO,EAAE,QAAQ,KAAK;AACxB;;;AChJA,SAAS,YAAYA,WAAU;AAC/B,OAAOC,WAAU;AAGjB,IAAM,oBAAoB;AAC1B,IAAM,aAAa;AAEnB,SAAS,eAAe,MAAc,UAA0B;AAC9D,QAAM,UAAU,KAAK,QAAQ;AAC7B,MAAI,YAAY,KAAK,QAAQ,WAAW,GAAG;AACzC,WAAO;AAAA,EACT;AACA,MAAI,QAAQ,UAAU,UAAU;AAC9B,WAAO;AAAA,EACT;AACA,QAAM,YAAY,KAAK,IAAI,GAAG,KAAK,MAAM,WAAW,UAAU,CAAC;AAC/D,QAAM,YAAY,KAAK,IAAI,GAAG,WAAW,SAAS;AAClD,QAAM,UAAU,QAAQ,SAAS;AACjC,QAAM,SAAS;AAAA,MAAS,OAAO;AAAA;AAC/B,QAAM,OAAO,QAAQ,MAAM,GAAG,SAAS;AACvC,QAAM,OAAO,YAAY,IAAI,QAAQ,MAAM,CAAC,SAAS,IAAI;AACzD,SAAO,GAAG,IAAI,GAAG,MAAM,GAAG,IAAI;AAChC;AAEA,SAAS,YACP,WACA,WAIA;AACA,MAAI,cAAc,KAAK,cAAc,GAAG;AACtC,WAAO,EAAE,QAAQ,GAAG,QAAQ,EAAE;AAAA,EAChC;AACA,MAAI,cAAc,GAAG;AACnB,WAAO,EAAE,QAAQ,GAAG,QAAQ,kBAAkB;AAAA,EAChD;AACA,MAAI,cAAc,GAAG;AACnB,WAAO,EAAE,QAAQ,mBAAmB,QAAQ,EAAE;AAAA,EAChD;AAEA,QAAM,OAAO,KAAK,MAAM,oBAAoB,CAAC;AAC7C,MAAI,SAAS,KAAK,IAAI,WAAW,IAAI;AACrC,MAAI,SAAS,KAAK,IAAI,WAAW,IAAI;AACrC,MAAI,YAAY,oBAAoB,SAAS;AAE7C,MAAI,YAAY,GAAG;AACjB,UAAM,kBAAkB,YAAY;AACpC,UAAM,kBAAkB,YAAY;AACpC,QAAI,oBAAoB,GAAG;AACzB,gBAAU;AAAA,IACZ,WAAW,oBAAoB,GAAG;AAChC,gBAAU;AAAA,IACZ,OAAO;AACL,YAAM,iBAAiB,kBAAkB;AACzC,YAAM,cAAc,KAAK;AAAA,QACtB,kBAAkB,iBAAkB;AAAA,MACvC;AACA,gBAAU;AACV,gBAAU,YAAY;AAAA,IACxB;AAAA,EACF;AAEA,SAAO,EAAE,QAAQ,OAAO;AAC1B;AAEA,SAAS,SAAS,MAAc,MAAc,SAA0B;AACtE,MAAI,CAAC,SAAS;AACZ,WAAO;AAAA,EACT;AACA,SAAO,QAAU,IAAI,IAAI,IAAI;AAC/B;AAEA,SAAS,iBAAiB,QAAoB,OAAwB;AACpE,QAAM,eAAe,OAAO,UAAU,WAAM,OAAO,SAAS,WAAM;AAElE,MAAI,cAAc;AAClB,MAAI,OAAO,SAAS;AAClB,kBAAc;AAAA,EAChB,WAAW,CAAC,OAAO,QAAQ;AACzB,kBAAc;AAAA,EAChB;AAEA,QAAM,QAAkB,CAAC;AACzB,MAAI,OAAO,SAAS;AAClB,UAAM,KAAK,SAAS;AAAA,EACtB,WAAW,CAAC,OAAO,QAAQ;AACzB,UAAM,KAAK,QAAQ,OAAO,YAAY,MAAM,EAAE;AAAA,EAChD;AACA,QAAM,KAAK,GAAG,OAAO,UAAU,IAAI;AAEnC,QAAM,UAAU,MAAM,SAAS,IAAI,KAAK,MAAM,KAAK,IAAI,CAAC,MAAM;AAC9D,QAAM,gBAAgB,SAAS,cAAc,aAAa,KAAK;AAC/D,SAAO,GAAG,aAAa,IAAI,OAAO,IAAI,GAAG,OAAO;AAClD;AAEO,SAAS,qBAAqB,QAAgB,SAAS,IAAY;AACxE,QAAM,gBAAgB,OAAO,QAAQ;AACrC,QAAM,gBAAgB,OAAO,QAAQ;AAErC,MAAI,cAAc,WAAW,KAAK,cAAc,WAAW,GAAG;AAC5D,WAAO;AAAA,EACT;AAEA,MAAI,cAAc,SAAS,KAAK,cAAc,SAAS,GAAG;AACxD,UAAM,SAAS,YAAY,cAAc,QAAQ,cAAc,MAAM;AACrE,UAAM,gBAAgB,eAAe,eAAe,OAAO,MAAM;AACjE,UAAM,gBAAgB,eAAe,eAAe,OAAO,MAAM;AACjE,WAAO;AAAA,EAAY,aAAa;AAAA;AAAA;AAAA,EAAgB,aAAa;AAAA,EAC/D;AAEA,MAAI,cAAc,SAAS,GAAG;AAC5B,WAAO,eAAe,eAAe,iBAAiB;AAAA,EACxD;AAEA,SAAO,eAAe,eAAe,iBAAiB;AACxD;AAEO,SAAS,oBAAoB,SAAiC;AACnE,QAAM,QAAQ,QAAQ,QAAQ,OAAO,KAAK;AAC1C,QAAM,QAAkB,CAAC;AAEzB,aAAW,UAAU,QAAQ,SAAS;AACpC,UAAM,KAAK,iBAAiB,QAAQ,KAAK,CAAC;AAAA,EAC5C;AAEA,MAAI,QAAQ,SAAS,SAAS,GAAG;AAC/B,UAAM,cAAc,aAAa,QAAQ,SAAS,KAAK,IAAI,CAAC;AAC5D,UAAM,KAAK,SAAS,aAAa,MAAM,KAAK,CAAC;AAAA,EAC/C;AAEA,QAAM,YAAY,QAAQ,SACtB,SAAS,8BAA8B,MAAM,KAAK,IAClD,SAAS,yBAAyB,MAAM,KAAK;AACjD,QAAM,KAAK,SAAS;AAEpB,SAAO,MAAM,KAAK,IAAI;AACxB;;;ACtIO,SAAS,qBAAqB,SAAqC;AACxE,QAAM,WAAW,QAAQ,SAAS,SAAS,IAAI,QAAQ,WAAW;AAElE,MAAI,QAAQ,QAAQ;AAClB,WAAO,WAAW,EAAE,SAAS,IAAI,CAAC;AAAA,EACpC;AAEA,QAAM,UAAU,QAAQ;AACxB,MAAI,SAAS;AACX,UAAMC,UAAS,SAAS,QAAQ,IAAI,kBAAkB,QAAQ,YAAY,MAAM;AAAA,EAAO,qBAAqB,QAAQ,QAAQ,QAAQ,MAAM,CAAC;AAC3I,WAAO,WACH,EAAE,UAAU,SAAS,QAAAA,SAAQ,SAAS,IACtC,EAAE,UAAU,SAAS,QAAAA,QAAO;AAAA,EAClC;AAEA,QAAM,SAAS;AACf,SAAO,WACH,EAAE,UAAU,SAAS,QAAQ,SAAS,IACtC,EAAE,UAAU,SAAS,OAAO;AAClC;","names":["fs","path","reason"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ralph-gate",
3
- "version": "0.3.0",
3
+ "version": "0.3.2",
4
4
  "type": "module",
5
5
  "description": "Structured verification gates for Claude Code - prevent premature completion with ordered validation checks",
6
6
  "keywords": [