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 +31 -13
- package/dist/cli.js.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
// src/cli.ts
|
|
4
|
-
import
|
|
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
|
|
290
|
+
const filePattern = "gate-results-*.json";
|
|
291
291
|
try {
|
|
292
|
-
execSync("git check-ignore -q gate-results-
|
|
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}${
|
|
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
|
-
|
|
335
|
-
(
|
|
336
|
-
|
|
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 ${
|
|
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
|
|
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
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":"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","names":["fs","path","reason"]}
|