parrat 0.1.0-beta.5

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.
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts","../src/cli/audit-query.ts","../src/cli/doctor.ts","../src/core/config/loader.ts","../src/core/errors.ts","../src/core/config/overrides.ts","../src/core/config/schema.ts","../src/cli/init.ts","../src/cli/replay.ts","../src/cli/run.ts","../src/core/audit/idempotency.ts","../src/core/audit/logger.ts","../src/core/audit/retention.ts","../src/core/runtime.ts","../src/core/keys.ts","../src/core/llm/client.ts","../src/core/telemetry.ts","../src/core/types.ts","../src/core/skills/registry.ts","../src/core/llm/skill-executor.ts","../src/core/mcp/client.ts","../src/core/mcp/filter.ts","../src/core/skills/Skill.ts","../src/skills/freshness-investigation/freshness-context-provider.ts","../src/skills/freshness-investigation/input-schema.ts","../src/skills/freshness-investigation/output-schema.ts","../src/skills/freshness-investigation/prompt.ts","../src/skills/freshness-investigation/index.ts","../src/skills/lineage-analysis/input-schema.ts","../src/skills/lineage-analysis/output-schema.ts","../src/skills/lineage-analysis/prompt.ts","../src/skills/lineage-analysis/index.ts","../src/skills/metric-drop-rca/input-schema.ts","../src/skills/metric-drop-rca/output-schema.ts","../src/skills/metric-drop-rca/prompt.ts","../src/skills/metric-drop-rca/index.ts","../src/skills/index.ts","../src/cli/skills.ts","../src/cli/watch.ts","../src/core/notify/slack.ts","../src/cli/webhook.ts"],"sourcesContent":["import 'dotenv/config';\nimport { Command } from 'commander';\nimport { auditCommand } from './cli/audit-query.js';\nimport { doctorCommand } from './cli/doctor.js';\nimport { initCommand } from './cli/init.js';\nimport { replayCommand } from './cli/replay.js';\nimport { runCommand } from './cli/run.js';\nimport { skillsCommand } from './cli/skills.js';\nimport { watchCommand } from './cli/watch.js';\nimport { webhookCommand } from './cli/webhook.js';\n\nconst program = new Command()\n .name('parrat')\n .description('Claude-native cross-stack agent for data ops')\n .version('0.1.0-beta.5');\n\nprogram.addCommand(doctorCommand);\nprogram.addCommand(initCommand);\nprogram.addCommand(runCommand);\nprogram.addCommand(skillsCommand);\nprogram.addCommand(replayCommand);\nprogram.addCommand(watchCommand);\nprogram.addCommand(auditCommand);\nprogram.addCommand(webhookCommand);\n\nawait program.parseAsync(process.argv);\n","import { readFileSync } from 'node:fs';\nimport { Command } from 'commander';\n\nexport interface AuditQueryOptions {\n auditPath: string;\n runId?: string;\n eventType?: string;\n since?: string;\n limit?: number;\n json?: boolean;\n}\n\nexport interface AuditQueryResult {\n exitCode: number;\n lines?: string[];\n error?: string;\n}\n\nfunction formatRecord(record: Record<string, unknown>): string {\n const ts = typeof record.timestamp === 'string' ? record.timestamp : '?';\n const type = typeof record.event_type === 'string' ? record.event_type : '?';\n const runId = typeof record.run_id === 'string' ? record.run_id.slice(0, 8) : '?';\n const skill = typeof record.skill === 'string' ? ` skill=${record.skill}` : '';\n\n let detail = '';\n const payload = record.payload;\n if (type === 'mcp_call' && typeof payload === 'object' && payload !== null) {\n const p = payload as Record<string, unknown>;\n const tool = typeof p.tool === 'string' ? p.tool : '';\n const server = typeof p.server === 'string' ? p.server : '';\n const ms = typeof p.duration_ms === 'number' ? ` (${p.duration_ms}ms)` : '';\n detail = ` ${server}.${tool}${ms}`;\n } else if (type === 'claude_call' && typeof payload === 'object' && payload !== null) {\n const p = payload as Record<string, unknown>;\n const tokens = typeof p.output_tokens === 'number' ? ` ${p.output_tokens} tokens` : '';\n const cost =\n typeof p.cost_estimate_usd === 'number' ? ` $${p.cost_estimate_usd.toFixed(4)}` : '';\n detail = `${tokens}${cost}`;\n } else if (type === 'error' && typeof payload === 'object' && payload !== null) {\n const p = payload as Record<string, unknown>;\n detail = ` ${typeof p.error_message === 'string' ? p.error_message : ''}`;\n }\n\n return `[${ts}] ${type.padEnd(22)} run=${runId}${skill}${detail}`;\n}\n\nexport async function queryAuditLog(options: AuditQueryOptions): Promise<AuditQueryResult> {\n let raw: string;\n try {\n raw = readFileSync(options.auditPath, 'utf8');\n } catch {\n return { exitCode: 1, error: `Audit log not found: ${options.auditPath}` };\n }\n\n const sinceMs = options.since ? Date.parse(options.since) : undefined;\n if (options.since && sinceMs !== undefined && Number.isNaN(sinceMs)) {\n return { exitCode: 1, error: `Invalid --since value: ${options.since}` };\n }\n\n const records: Record<string, unknown>[] = [];\n for (const line of raw.split('\\n')) {\n const trimmed = line.trim();\n if (!trimmed) continue;\n try {\n records.push(JSON.parse(trimmed) as Record<string, unknown>);\n } catch {}\n }\n\n let filtered = records.filter((r) => {\n if (options.runId && r.run_id !== options.runId) return false;\n if (options.eventType && r.event_type !== options.eventType) return false;\n if (sinceMs !== undefined && typeof r.timestamp === 'string') {\n if (Date.parse(r.timestamp) < sinceMs) return false;\n }\n return true;\n });\n\n filtered.sort((a, b) => {\n const ta = typeof a.timestamp === 'string' ? a.timestamp : '';\n const tb = typeof b.timestamp === 'string' ? b.timestamp : '';\n return ta.localeCompare(tb);\n });\n\n if (options.limit !== undefined && options.limit > 0) {\n filtered = filtered.slice(0, options.limit);\n }\n\n if (filtered.length === 0) {\n return { exitCode: 1, lines: [], error: 'No matching events found.' };\n }\n\n const lines = options.json ? filtered.map((r) => JSON.stringify(r)) : filtered.map(formatRecord);\n\n return { exitCode: 0, lines };\n}\n\nexport const auditCommand = new Command('audit').description('Audit log tools');\n\nauditCommand.addCommand(\n new Command('query')\n .description('Query the audit log')\n .option('--audit-path <path>', 'Path to audit log file', '.parrat/audit.jsonl')\n .option('--run-id <id>', 'Filter by run ID')\n .option('--event-type <type>', 'Filter by event type (trigger, mcp_call, claude_call, ...)')\n .option('--since <iso>', 'Only show events after this ISO 8601 timestamp')\n .option('--limit <n>', 'Maximum number of events to show', (v) => Number.parseInt(v, 10))\n .option('--json', 'Output raw NDJSON instead of human-readable format')\n .action(\n async (opts: {\n auditPath: string;\n runId?: string;\n eventType?: string;\n since?: string;\n limit?: number;\n json?: boolean;\n }) => {\n const result = await queryAuditLog({\n auditPath: opts.auditPath,\n ...(opts.runId ? { runId: opts.runId } : {}),\n ...(opts.eventType ? { eventType: opts.eventType } : {}),\n ...(opts.since ? { since: opts.since } : {}),\n ...(opts.limit !== undefined ? { limit: opts.limit } : {}),\n ...(opts.json ? { json: opts.json } : {}),\n });\n\n if (result.lines) {\n for (const line of result.lines) {\n console.log(line);\n }\n }\n if (result.error && result.lines?.length === 0) {\n console.error(result.error);\n }\n if (result.exitCode !== 0) process.exit(result.exitCode);\n },\n ),\n);\n","import { execFile } from 'node:child_process';\nimport { constants, access, mkdir } from 'node:fs/promises';\nimport { dirname } from 'node:path';\nimport { Command } from 'commander';\nimport { loadConfig } from '../core/config/index.js';\n\nfunction execFileAsync(\n cmd: string,\n args: string[],\n opts: { timeout: number },\n): Promise<{ stdout: string; stderr: string }> {\n return new Promise((resolve, reject) => {\n execFile(cmd, args, opts, (err, stdout, stderr) => {\n if (err) reject(err);\n else resolve({ stdout, stderr });\n });\n });\n}\n\nexport type DoctorStatus = 'ok' | 'warn' | 'fail';\n\nexport interface DoctorCheck {\n name: string;\n status: DoctorStatus;\n message: string;\n}\n\n/**\n * The dbt-mcp version Parrat is tested and pinned against.\n * Update this constant when upgrading dbt-mcp.\n */\nexport const PINNED_DBT_MCP_VERSION = '0.4.3';\n\nasync function checkApiKey(): Promise<DoctorCheck> {\n const key = process.env.ANTHROPIC_API_KEY;\n return key && key.length > 0\n ? { name: 'ANTHROPIC_API_KEY', status: 'ok', message: 'Present' }\n : {\n name: 'ANTHROPIC_API_KEY',\n status: 'fail',\n message: 'Missing — set ANTHROPIC_API_KEY in your environment or .env file',\n };\n}\n\nasync function checkConfig(): Promise<DoctorCheck> {\n try {\n await loadConfig();\n return { name: 'Config file', status: 'ok', message: 'Loaded and valid' };\n } catch (e) {\n return {\n name: 'Config file',\n status: 'fail',\n message: `${e instanceof Error ? e.message : String(e)}`,\n };\n }\n}\n\nasync function checkDbtMcpVersion(): Promise<DoctorCheck> {\n let config: Awaited<ReturnType<typeof loadConfig>>;\n try {\n config = await loadConfig();\n } catch {\n return { name: 'dbt-mcp version', status: 'warn', message: 'Skipped — config not loadable' };\n }\n\n const usesDbtMcp = Object.values(config.mcpServers).some(\n (s) => s.command.includes('dbt-mcp') || s.args.some((a) => a.includes('dbt-mcp')),\n );\n\n if (!usesDbtMcp) {\n return { name: 'dbt-mcp version', status: 'ok', message: 'dbt-mcp not configured — skipped' };\n }\n\n try {\n const { stdout } = await execFileAsync('uvx', ['dbt-mcp', '--version'], { timeout: 10_000 });\n const version = stdout.trim().split(/\\s+/).pop() ?? '';\n if (version === PINNED_DBT_MCP_VERSION) {\n return { name: 'dbt-mcp version', status: 'ok', message: `${version} (matches pinned)` };\n }\n return {\n name: 'dbt-mcp version',\n status: 'fail',\n message: `Found ${version || '(unknown)'}, expected ${PINNED_DBT_MCP_VERSION} — run: uvx install dbt-mcp==${PINNED_DBT_MCP_VERSION}`,\n };\n } catch (e) {\n return {\n name: 'dbt-mcp version',\n status: 'fail',\n message: `Could not run 'uvx dbt-mcp --version': ${e instanceof Error ? e.message : String(e)}`,\n };\n }\n}\n\nasync function checkAuditDir(auditPath: string): Promise<DoctorCheck> {\n const dir = dirname(auditPath);\n try {\n await access(dir, constants.W_OK);\n return { name: 'Audit directory', status: 'ok', message: `${dir} is writable` };\n } catch {\n try {\n await mkdir(dir, { recursive: true });\n return { name: 'Audit directory', status: 'ok', message: `${dir} created` };\n } catch (e) {\n return {\n name: 'Audit directory',\n status: 'warn',\n message: `Could not create ${dir}: ${e instanceof Error ? e.message : String(e)}`,\n };\n }\n }\n}\n\nexport async function runDoctor(auditPath: string): Promise<DoctorCheck[]> {\n return Promise.all([\n checkApiKey(),\n checkConfig(),\n checkDbtMcpVersion(),\n checkAuditDir(auditPath),\n ]);\n}\n\nfunction formatCheck(check: DoctorCheck): string {\n const icon = check.status === 'ok' ? '✓' : check.status === 'warn' ? '!' : '✗';\n return `${icon} ${check.name.padEnd(22)} ${check.status.padEnd(6)} ${check.message}`;\n}\n\nexport const doctorCommand = new Command('doctor')\n .description('Check Parrat configuration and dependencies')\n .option('--audit-path <path>', 'Path to audit log file', '.parrat/audit.jsonl')\n .action(async (opts: { auditPath: string }) => {\n const checks = await runDoctor(opts.auditPath);\n for (const check of checks) {\n console.log(formatCheck(check));\n }\n const hasFail = checks.some((c) => c.status === 'fail');\n if (hasFail) process.exit(1);\n });\n","import { existsSync, readFileSync } from 'node:fs';\nimport { homedir } from 'node:os';\nimport { isAbsolute, resolve } from 'node:path';\nimport { parse as parseYaml } from 'yaml';\nimport { ConfigNotFoundError, ConfigValidationError } from '../errors.js';\nimport { applyEnvOverrides } from './overrides.js';\nimport { type Config, configSchema } from './schema.js';\n\nconst ENV_VAR_PATTERN = /\\$([A-Z_][A-Z0-9_]*)|\\$\\{([A-Z_][A-Z0-9_]*)\\}/g;\n\n/**\n * Resolve which config file to load. Precedence:\n * 1. PARRAT_CONFIG_PATH env var (absolute or cwd-relative)\n * 2. ./.parrat/config.yaml relative to cwd\n *\n * Throws ConfigNotFoundError if neither exists.\n */\nexport function resolveConfigPath(\n env: NodeJS.ProcessEnv = process.env,\n cwd: string = process.cwd(),\n): string {\n const fromEnv = env.PARRAT_CONFIG_PATH;\n if (fromEnv) {\n const absolute = isAbsolute(fromEnv) ? fromEnv : resolve(cwd, fromEnv);\n if (!existsSync(absolute)) {\n throw new ConfigNotFoundError(absolute, 'PARRAT_CONFIG_PATH');\n }\n return absolute;\n }\n\n const defaultPath = resolve(cwd, '.parrat', 'config.yaml');\n if (!existsSync(defaultPath)) {\n throw new ConfigNotFoundError(defaultPath, 'default');\n }\n return defaultPath;\n}\n\n/**\n * Load + validate + resolve a Parrat config from disk. Performs:\n * 1. File read\n * 2. YAML parse\n * 3. Zod schema validation\n * 4. $ENV_VAR resolution in string fields\n * 5. ~ tilde expansion in path fields\n * 6. PARRAT_<DOTTED> env var overrides\n *\n * Returns a frozen Config. Throws ConfigValidationError with field-path detail\n * on schema failure.\n */\nexport async function loadConfig(\n env: NodeJS.ProcessEnv = process.env,\n cwd: string = process.cwd(),\n): Promise<Readonly<Config>> {\n const path = resolveConfigPath(env, cwd);\n const raw = readFileSync(path, 'utf-8');\n\n let parsed: unknown;\n try {\n parsed = parseYaml(raw);\n } catch (cause) {\n throw new ConfigValidationError(path, 'YAML parse failed', cause);\n }\n\n const result = configSchema.safeParse(parsed);\n if (!result.success) {\n throw new ConfigValidationError(path, 'schema validation failed', result.error);\n }\n\n // Walk the validated config; resolve $ENV_VAR + tilde expansion in string leaves.\n const resolved = walkAndTransform(result.data, env, homedir()) as Config;\n\n // Apply PARRAT_<DOTTED> env var overrides on top.\n const withOverrides = applyEnvOverrides(resolved, env);\n\n return Object.freeze(withOverrides);\n}\n\n/**\n * Substitute $VAR / ${VAR} references in a string against env. Throws if a\n * referenced variable is unset.\n */\nexport function resolveEnvVars(value: string, env: NodeJS.ProcessEnv): string {\n return value.replace(\n ENV_VAR_PATTERN,\n (_match, bare: string | undefined, braced: string | undefined) => {\n const name = bare ?? braced ?? '';\n const resolved = env[name];\n if (resolved === undefined) {\n throw new ConfigValidationError(\n '<env-resolution>',\n `Environment variable '${name}' referenced in config but not set`,\n undefined,\n );\n }\n return resolved;\n },\n );\n}\n\n/**\n * Expand a leading ~ to the user's home directory. Leaves other paths\n * untouched.\n */\nexport function expandTilde(path: string, home: string): string {\n if (path === '~') return home;\n if (path.startsWith('~/') || path.startsWith('~\\\\')) {\n return resolve(home, path.slice(2));\n }\n return path;\n}\n\n/**\n * Walk an arbitrary value (object/array/primitive) and apply $ENV_VAR and\n * tilde-expansion to every string leaf. Used after schema validation so we\n * only transform shapes we know about.\n */\nfunction walkAndTransform(value: unknown, env: NodeJS.ProcessEnv, home: string): unknown {\n if (typeof value === 'string') {\n const envResolved = resolveEnvVars(value, env);\n return expandTilde(envResolved, home);\n }\n if (Array.isArray(value)) {\n return value.map((item) => walkAndTransform(item, env, home));\n }\n if (value !== null && typeof value === 'object') {\n const out: Record<string, unknown> = {};\n for (const [key, v] of Object.entries(value)) {\n out[key] = walkAndTransform(v, env, home);\n }\n return out;\n }\n return value;\n}\n","import type { ZodError } from 'zod';\n\n/**\n * Base class for all Parrat-thrown errors. Catch ParratError to handle anything\n * Parrat raises uniformly; catch a specific subclass for typed handling.\n *\n * All subclasses preserve the original Error message format used in v1.0 so\n * regex-based assertions and human-readable logs continue to work.\n */\nexport class ParratError extends Error {\n override readonly name: string = 'ParratError';\n}\n\n/**\n * Thrown by the Skill registry when a name is requested that wasn't registered.\n * Carries the requested name and the available names for programmatic recovery.\n */\nexport class SkillNotFoundError extends ParratError {\n override readonly name = 'SkillNotFoundError';\n\n constructor(\n public readonly skillName: string,\n public readonly available: readonly string[],\n ) {\n const list = available.join(', ') || '(none)';\n super(`Skill not found: '${skillName}'. Available skills: ${list}.`);\n }\n}\n\n/**\n * Thrown at registry creation when two Skills share the same name.\n */\nexport class DuplicateSkillError extends ParratError {\n override readonly name = 'DuplicateSkillError';\n\n constructor(public readonly skillName: string) {\n super(`Duplicate skill name: ${skillName}`);\n }\n}\n\n/**\n * Thrown when a Skill's input or output fails Zod validation.\n * Carries the direction (which boundary failed), the Skill name, and the\n * underlying ZodError accessible via `.cause`.\n */\nexport class SchemaValidationError extends ParratError {\n override readonly name = 'SchemaValidationError';\n\n constructor(\n public readonly direction: 'input' | 'output',\n public readonly skillName: string,\n cause: ZodError,\n ) {\n super(`Skill '${skillName}' ${direction} failed schema validation: ${cause.message}`, {\n cause,\n });\n }\n}\n\n/**\n * Thrown when the audit logger fails to write to disk (permissions, disk full,\n * EISDIR, etc.). The underlying error is accessible via `.cause`.\n */\nexport class AuditWriteError extends ParratError {\n override readonly name = 'AuditWriteError';\n\n constructor(\n public readonly filePath: string,\n cause: unknown,\n ) {\n const causeMsg = cause instanceof Error ? cause.message : String(cause);\n super(`Failed to write audit log to '${filePath}': ${causeMsg}`, { cause });\n }\n}\n\n/**\n * Thrown by getClaudeKey when no Claude API key is available in the environment.\n * In v1 (OSS), the only resolution path is the ANTHROPIC_API_KEY env var.\n */\nexport class MissingClaudeKeyError extends ParratError {\n override readonly name = 'MissingClaudeKeyError';\n\n constructor() {\n super(\n 'Claude API key not found. Set ANTHROPIC_API_KEY in your environment, or run `parrat init` to configure.',\n );\n }\n}\n\n/**\n * Thrown when the config loader can't find a config file at any expected path.\n * `source` indicates which lookup attempt failed: 'PARRAT_CONFIG_PATH' (env-var\n * override) or 'default' (./.parrat/config.yaml).\n */\nexport class ConfigNotFoundError extends ParratError {\n override readonly name = 'ConfigNotFoundError';\n\n constructor(\n public readonly path: string,\n public readonly source: 'PARRAT_CONFIG_PATH' | 'default',\n ) {\n const hint =\n source === 'PARRAT_CONFIG_PATH'\n ? `Expected a config at PARRAT_CONFIG_PATH='${path}' but no file exists there.`\n : `No config found at '${path}'. Run \\`parrat init\\` to scaffold one.`;\n super(`Parrat config not found: ${hint}`);\n }\n}\n\n/**\n * Thrown when a config file fails YAML parsing, schema validation, or env-var\n * resolution. The underlying error is accessible via `.cause`.\n */\nexport class ConfigValidationError extends ParratError {\n override readonly name = 'ConfigValidationError';\n\n constructor(\n public readonly path: string,\n public readonly stage: string,\n cause: unknown,\n ) {\n const causeMsg = cause instanceof Error ? cause.message : String(cause ?? '');\n super(`Config validation failed at '${path}' (${stage}): ${causeMsg}`, { cause });\n }\n}\n\n/**\n * Thrown when Claude attempts to invoke an MCP tool that isn't in the Skill's\n * allowlist. Should never trigger if Agent SDK filtering is configured\n * correctly; defensive check for Phase 1+ when custom MCP servers are added.\n */\nexport class McpToolDeniedError extends ParratError {\n override readonly name = 'McpToolDeniedError';\n\n constructor(\n public readonly toolName: string,\n public readonly allowlist: readonly string[],\n ) {\n const allowed = allowlist.join(', ') || '(none)';\n super(`MCP tool '${toolName}' is not in this Skill's allowlist. Allowed: ${allowed}.`);\n }\n}\n\n/**\n * Thrown when the LLM tool-call loop exceeds the Skill's max_turns budget\n * without returning a final answer. The Skill should be redesigned with a\n * higher budget OR a tighter prompt if this fires.\n */\nexport class MaxTurnsExceededError extends ParratError {\n override readonly name = 'MaxTurnsExceededError';\n\n constructor(\n public readonly skillName: string,\n public readonly maxTurns: number,\n ) {\n super(\n `Skill '${skillName}' did not converge within max_turns=${maxTurns}. Increase max_turns or refine the system prompt.`,\n );\n }\n}\n\n/**\n * Thrown on Anthropic API failures after retry exhaustion (transient errors)\n * or on the first 4xx response (auth, malformed request). The underlying\n * Anthropic error is accessible via `.cause`.\n */\nexport class LlmApiError extends ParratError {\n override readonly name = 'LlmApiError';\n\n constructor(message: string, cause: unknown) {\n super(message, { cause });\n }\n}\n","import type { Config } from './schema.js';\n\n/**\n * Apply PARRAT_<UPPERCASE_DOTTED> env vars as config overrides. The dotted\n * suffix maps to the config path, with underscores becoming dots and snake-case\n * preserved.\n *\n * Examples:\n * PARRAT_TENANT_ID=acme → config.tenant_id\n * PARRAT_CLAUDE_MODEL=opus-4-7 → config.claude.model\n * PARRAT_AUDIT_LOG_PATH=/var/log/ → config.audit.log_path\n *\n * Only string-leaf fields are overridable; numeric fields are coerced if the\n * env var parses cleanly. Unknown paths are ignored (no errors) so users can\n * have unrelated PARRAT_*-prefixed env vars without breakage.\n */\nexport function applyEnvOverrides(config: Config, env: NodeJS.ProcessEnv): Config {\n // Map of override path → known field type (so we can coerce correctly)\n const overrideMap: Record<\n string,\n { type: 'string' | 'number' | 'tenant'; apply: (cfg: Config, val: string) => Config }\n > = {\n PARRAT_TENANT_ID: {\n type: 'tenant',\n apply: (cfg, val) => ({ ...cfg, tenant_id: val }),\n },\n PARRAT_CLAUDE_MODEL: {\n type: 'string',\n apply: (cfg, val) => ({ ...cfg, claude: { ...cfg.claude, model: val } }),\n },\n PARRAT_CLAUDE_MAX_TURNS: {\n type: 'number',\n apply: (cfg, val) => ({\n ...cfg,\n claude: { ...cfg.claude, max_turns: Number.parseInt(val, 10) },\n }),\n },\n PARRAT_CLAUDE_MAX_TOKENS: {\n type: 'number',\n apply: (cfg, val) => ({\n ...cfg,\n claude: { ...cfg.claude, max_tokens: Number.parseInt(val, 10) },\n }),\n },\n PARRAT_CLAUDE_TEMPERATURE: {\n type: 'number',\n apply: (cfg, val) => ({\n ...cfg,\n claude: { ...cfg.claude, temperature: Number.parseFloat(val) },\n }),\n },\n PARRAT_AUDIT_LOG_PATH: {\n type: 'string',\n apply: (cfg, val) => ({ ...cfg, audit: { ...cfg.audit, log_path: val } }),\n },\n PARRAT_AUDIT_RETENTION_DAYS: {\n type: 'number',\n apply: (cfg, val) => ({\n ...cfg,\n audit: { ...cfg.audit, retention_days: Number.parseInt(val, 10) },\n }),\n },\n };\n\n let result = config;\n for (const [envVar, descriptor] of Object.entries(overrideMap)) {\n const value = env[envVar];\n if (value === undefined || value === '') continue;\n if (descriptor.type === 'number' && Number.isNaN(Number.parseFloat(value))) continue;\n result = descriptor.apply(result, value);\n }\n return result;\n}\n","import { z } from 'zod';\n\n/**\n * Per-MCP-server configuration. The map key (e.g., \"dbt\") becomes the server\n * name in the Agent SDK's `mcp__{name}__{tool}` tool naming convention.\n *\n * Skills declare their own tool allowlist; the optional `tools` field on this\n * config can pre-restrict at the server level (Skill allowlists then intersect).\n */\nexport const mcpServerConfigSchema = z\n .object({\n command: z.string().min(1),\n args: z.array(z.string()).default([]),\n env: z.record(z.string(), z.string()).default({}),\n tools: z.array(z.string()).optional(),\n })\n .strict();\n\n/**\n * Defaults applied to every Skill invocation; per-Skill overrides go on the\n * Skill spec itself.\n */\nexport const skillDefaultsSchema = z\n .object({\n timeout_seconds: z.number().int().positive().default(60),\n max_retries: z.number().int().nonnegative().default(2),\n })\n .strict();\n\n/**\n * Audit log configuration. Hashing/redaction fields ship in v1 schema but\n * are M4 deliverables; v1 ignores them at runtime.\n */\nexport const auditConfigSchema = z\n .object({\n log_path: z.string().default('.parrat/audit.jsonl'),\n hash_algorithm: z.literal('sha256').default('sha256'),\n retention_days: z.number().int().positive().default(90),\n redact_fields: z.array(z.string()).default([]),\n idempotency_window_hours: z.number().int().positive().default(24),\n })\n .strict();\n\n/**\n * Claude / LLM client configuration. API key is sourced from ANTHROPIC_API_KEY\n * env var only — never declared here (avoids accidental commit).\n */\nexport const claudeConfigSchema = z\n .object({\n model: z.string().default('claude-sonnet-4-6'),\n max_turns: z.number().int().positive().default(6),\n max_tokens: z.number().int().positive().default(4096),\n temperature: z.number().min(0).max(1).default(0.0),\n })\n .strict();\n\n/**\n * watch — which skill to run and what input to pass when `parrat watch` is invoked.\n * The schedule string is informational; actual scheduling is handled by the OS\n * (Task Scheduler / cron). Both fields are required when `watch` is present.\n */\nexport const watchConfigSchema = z\n .object({\n skill: z.string().min(1),\n input: z.record(z.string(), z.unknown()).default({}),\n })\n .strict();\n\n/**\n * notify.slack — incoming webhook for Slack delivery. No OAuth; just a POST\n * to the webhook_url with a JSON body.\n */\nexport const slackNotifyConfigSchema = z\n .object({\n webhook_url: z.string().url(),\n })\n .strict();\n\nexport const notifyConfigSchema = z\n .object({\n slack: slackNotifyConfigSchema.optional(),\n })\n .strict();\n\n/**\n * webhook — HTTP listener for external alert triggers (Monte Carlo, dbt Cloud).\n * `secret` is checked against the X-Parrat-Secret header if set.\n */\nexport const webhookConfigSchema = z\n .object({\n port: z.number().int().positive().default(8080),\n secret: z.string().optional(),\n })\n .strict();\n\n/**\n * Top-level Parrat configuration. Versioned so future schema changes can\n * evolve without breaking existing config files.\n */\nexport const configSchema = z\n .object({\n version: z.literal(1),\n tenant_id: z.string().default('default'),\n mcpServers: z.record(z.string(), mcpServerConfigSchema).default({}),\n skills: z\n .object({\n defaults: skillDefaultsSchema.default({}),\n })\n .strict()\n .default({}),\n audit: auditConfigSchema.default({}),\n claude: claudeConfigSchema.default({}),\n watch: watchConfigSchema.optional(),\n notify: notifyConfigSchema.optional(),\n webhook: webhookConfigSchema.optional(),\n })\n .strict();\n\nexport type Config = z.infer<typeof configSchema>;\nexport type McpServerConfig = z.infer<typeof mcpServerConfigSchema>;\nexport type SkillDefaults = z.infer<typeof skillDefaultsSchema>;\nexport type AuditConfig = z.infer<typeof auditConfigSchema>;\nexport type ClaudeConfig = z.infer<typeof claudeConfigSchema>;\nexport type WatchConfig = z.infer<typeof watchConfigSchema>;\nexport type NotifyConfig = z.infer<typeof notifyConfigSchema>;\nexport type SlackNotifyConfig = z.infer<typeof slackNotifyConfigSchema>;\nexport type WebhookConfig = z.infer<typeof webhookConfigSchema>;\n","import { mkdir, stat, writeFile } from 'node:fs/promises';\nimport { dirname, resolve } from 'node:path';\nimport { stdin, stdout } from 'node:process';\nimport { createInterface } from 'node:readline/promises';\nimport { Command } from 'commander';\n\nconst DEFAULT_CONFIG_PATH = '.parrat/config.yaml';\n\n/**\n * Returns true if the path exists (file, directory, or any other entry),\n * false otherwise. Used by `parrat init` to detect existing config.\n */\nexport async function pathExists(path: string): Promise<boolean> {\n try {\n await stat(path);\n return true;\n } catch {\n return false;\n }\n}\n\n/**\n * Write a default `.parrat/config.yaml` to `configPath`.\n * Creates parent directories as needed.\n *\n * The default ships the v1 schema (version: 1) with tenant_id and audit\n * settings populated. MCP server config + claude settings are commented-out\n * templates the user fills in for LLM-driven Skills.\n */\nexport async function writeDefaultConfig(configPath: string): Promise<void> {\n const yaml = [\n '# Parrat configuration',\n `# Generated by \\`parrat init\\` on ${new Date().toISOString()}`,\n '',\n 'version: 1',\n 'tenant_id: default',\n '',\n '# MCP servers — uncomment and configure for LLM-driven Skills',\n '# (e.g., freshness-investigation needs a `dbt` server).',\n 'mcpServers: {}',\n '# dbt:',\n '# command: uvx',\n '# args: [dbt-mcp]',\n '# env:',\n '# DBT_PROJECT_DIR: /absolute/path/to/dbt/project',\n '# DBT_PATH: /absolute/path/to/dbt',\n '# PYTHONUTF8: \"1\" # Required on Windows',\n '',\n 'audit:',\n ' log_path: .parrat/audit.jsonl',\n ' retention_days: 90',\n '',\n 'claude:',\n ' # API key from ANTHROPIC_API_KEY env var (never declared here).',\n ' model: claude-sonnet-4-6',\n ' max_turns: 6',\n ' max_tokens: 4096',\n ' temperature: 0.0',\n '',\n ].join('\\n');\n\n await mkdir(dirname(configPath), { recursive: true });\n await writeFile(configPath, yaml, 'utf8');\n}\n\nexport const initCommand = new Command('init')\n .description('Initialize a Parrat configuration in the current directory')\n .option('--config-path <path>', 'Path to write the config file', DEFAULT_CONFIG_PATH)\n .option('-f, --force', 'Overwrite existing config without prompting', false)\n .action(async (opts: { configPath: string; force: boolean }) => {\n const configPath = resolve(opts.configPath);\n console.log('Welcome to Parrat.');\n console.log('Run `parrat skills list` to see available Skills.');\n console.log('');\n\n if ((await pathExists(configPath)) && !opts.force) {\n const rl = createInterface({ input: stdin, output: stdout });\n try {\n const answer = await rl.question(`${configPath} already exists. Overwrite? (y/N) `);\n if (!/^y(es)?$/i.test(answer.trim())) {\n console.log('Aborted. No changes written.');\n return;\n }\n } finally {\n rl.close();\n }\n }\n\n await writeDefaultConfig(configPath);\n console.log(`Configuration written to ${configPath}`);\n\n if (process.env.ANTHROPIC_API_KEY) {\n console.log('ANTHROPIC_API_KEY is set in your environment.');\n } else {\n console.log(\n 'ANTHROPIC_API_KEY not set. Run `export ANTHROPIC_API_KEY=...` before invoking Skills that call Claude.',\n );\n }\n });\n","import { readFileSync } from 'node:fs';\nimport { resolve } from 'node:path';\nimport { Command } from 'commander';\n\nexport interface ReplayOptions {\n runId: string;\n auditPath: string;\n}\n\nexport interface ReplayResult {\n exitCode: number;\n lines?: string[];\n error?: string;\n}\n\ninterface AuditRecord {\n event_id: string;\n timestamp: string;\n run_id: string;\n skill?: string;\n event_type: string;\n actor: string;\n payload: Record<string, unknown>;\n}\n\nfunction formatTime(iso: string): string {\n return iso.slice(11, 19); // HH:MM:SS\n}\n\nfunction formatRecord(r: AuditRecord): string {\n const t = formatTime(r.timestamp);\n switch (r.event_type) {\n case 'trigger':\n return `[${t}] TRIGGER skill=${r.skill ?? '?'} actor=${r.actor}`;\n case 'claude_call': {\n const p = r.payload;\n const cost = typeof p.cost_estimate_usd === 'number' ? p.cost_estimate_usd.toFixed(4) : '?';\n const dur = typeof p.duration_ms === 'number' ? (p.duration_ms / 1000).toFixed(1) : '?';\n return `[${t}] CLAUDE turn=${p.turn_index} in=${p.input_tokens} out=${p.output_tokens} cost=$${cost} dur=${dur}s`;\n }\n case 'mcp_call': {\n const p = r.payload;\n const dur = typeof p.duration_ms === 'number' ? (p.duration_ms / 1000).toFixed(1) : '?';\n const status = p.is_error ? ' ERROR' : '';\n return `[${t}] MCP server=${p.server} tool=${p.tool} dur=${dur}s${status}`;\n }\n case 'skill_output_captured':\n return `[${t}] OUTPUT turn=${r.payload.turn_index}`;\n case 'skill_complete': {\n const dur =\n typeof r.payload.duration_ms === 'number'\n ? ` dur=${(r.payload.duration_ms / 1000).toFixed(1)}s`\n : '';\n return `[${t}] COMPLETE${dur}`;\n }\n case 'error':\n return `[${t}] ERROR ${r.payload.error_name}: ${r.payload.error_message}`;\n default:\n return `[${t}] ${r.event_type.toUpperCase().padEnd(9)}`;\n }\n}\n\n/**\n * Pure handler for `parrat replay`. Reads the audit log, filters by run_id,\n * and returns formatted lines. Returns exitCode 1 if the run_id is not found.\n */\nexport function replayRun(options: ReplayOptions): ReplayResult {\n let raw: string;\n try {\n raw = readFileSync(resolve(options.auditPath), 'utf8');\n } catch (e) {\n return {\n exitCode: 1,\n error: `Cannot read audit log at '${options.auditPath}': ${e instanceof Error ? e.message : String(e)}`,\n };\n }\n\n const records = raw\n .split('\\n')\n .filter(Boolean)\n .flatMap((line) => {\n try {\n return [JSON.parse(line) as AuditRecord];\n } catch {\n return [];\n }\n })\n .filter((r) => r.run_id === options.runId)\n .sort((a, b) => a.timestamp.localeCompare(b.timestamp));\n\n if (records.length === 0) {\n return {\n exitCode: 1,\n error: `No events found for run_id '${options.runId}' in '${options.auditPath}'`,\n };\n }\n\n return { exitCode: 0, lines: records.map(formatRecord) };\n}\n\nexport const replayCommand = new Command('replay')\n .description('Print a human-readable trace of a past Skill run from the audit log')\n .argument('<run_id>', 'The run ID to replay')\n .option('--audit-path <path>', 'Path to audit log file', '.parrat/audit.jsonl')\n .action(async (runId: string, opts: { auditPath: string }) => {\n const result = replayRun({ runId, auditPath: opts.auditPath });\n\n if (result.error) {\n console.error(result.error);\n }\n if (result.lines) {\n for (const line of result.lines) {\n console.log(line);\n }\n }\n if (result.exitCode !== 0) {\n process.exit(result.exitCode);\n }\n });\n","import { readFileSync } from 'node:fs';\nimport { resolve } from 'node:path';\nimport { Command } from 'commander';\nimport { isDuplicateRun } from '../core/audit/idempotency.js';\nimport { createAuditLogger } from '../core/audit/logger.js';\nimport { sweepAuditLog } from '../core/audit/retention.js';\nimport { loadConfig } from '../core/config/index.js';\nimport { createRuntime } from '../core/runtime.js';\nimport { createRegistry } from '../core/skills/registry.js';\nimport { skills } from '../skills/index.js';\n\nexport interface RunOptions {\n skillName: string;\n inputJson: string;\n auditPath: string;\n /**\n * External orchestrator's workflow run ID — set as `workflow_id` on every\n * audit event for cross-tool correlation. Sourced from `correlation_id` env\n * var by the Commander wrapper.\n */\n correlationId?: string;\n}\n\nexport interface RunResult {\n exitCode: number;\n output?: unknown;\n error?: string;\n}\n\n/**\n * Pure handler for `parrat run`. Returns a structured result so the Commander\n * wrapper (or tests) can render it without subprocess plumbing.\n *\n * Exit codes (locked for v1):\n * 0 — success\n * 1 — Skill execution error (SkillNotFoundError, SchemaValidationError, MaxTurnsExceededError, etc.)\n * 2 — invalid JSON input\n * 3 — approval-pending / --resume requested (reserved for Phase 1+ composite Skills)\n * 4 — internal/config error (MissingClaudeKeyError, ConfigValidationError, ConfigNotFoundError)\n */\nexport async function runSkill(options: RunOptions): Promise<RunResult> {\n let input: unknown;\n try {\n input = JSON.parse(options.inputJson);\n } catch (e) {\n return {\n exitCode: 2,\n error: `Invalid JSON input: ${e instanceof Error ? e.message : String(e)}`,\n };\n }\n\n const registry = createRegistry(skills);\n const auditLogger = createAuditLogger({ filePath: resolve(options.auditPath) });\n const runtime = createRuntime({ registry, auditLogger });\n\n try {\n const output = await runtime.invoke({\n skill: options.skillName,\n input,\n actor: 'user',\n ...(options.correlationId ? { correlationId: options.correlationId } : {}),\n });\n return { exitCode: 0, output };\n } catch (e) {\n const errorName = e instanceof Error ? e.name : 'UnknownError';\n const message = e instanceof Error ? `${e.name}: ${e.message}` : String(e);\n\n // Internal/config errors → exit code 4\n if (\n errorName === 'MissingClaudeKeyError' ||\n errorName === 'ConfigValidationError' ||\n errorName === 'ConfigNotFoundError'\n ) {\n return { exitCode: 4, error: message };\n }\n return { exitCode: 1, error: message };\n }\n}\n\nexport const runCommand = new Command('run')\n .description('Run a Skill with the given input')\n .argument('<skill>', 'Name of the Skill to run (e.g. hello-world)')\n .argument('[input]', 'JSON input string for the Skill', '{}')\n .option('--audit-path <path>', 'Path to audit log file', '.parrat/audit.jsonl')\n .option(\n '--input-file <path>',\n 'Read Skill input from JSON file (alternative to positional argument)',\n )\n .option('--resume <workflow_id>', 'Resume a paused workflow (Phase 1+ feature; v1 stub)')\n .action(\n async (\n skillName: string,\n positionalInputJson: string,\n opts: { auditPath: string; resume?: string; inputFile?: string },\n ) => {\n // --resume is reserved for Phase 1+ composite Skills (paused on approval).\n // v1 errors clearly with exit code 3 so external callers know this is a\n // \"approval-pending / not-yet-implemented\" signal, not a generic failure.\n if (opts.resume) {\n console.error(\n 'parrat run --resume is reserved for Phase 1+ composite Skills. v1 has no resumable workflows.',\n );\n process.exit(3);\n }\n\n let inputJson = positionalInputJson;\n if (opts.inputFile) {\n if (positionalInputJson !== '{}') {\n console.error('Cannot pass both a positional JSON argument and --input-file. Pick one.');\n process.exit(2);\n }\n try {\n inputJson = readFileSync(opts.inputFile, 'utf8');\n } catch (e) {\n console.error(\n `Failed to read --input-file '${opts.inputFile}': ${e instanceof Error ? e.message : String(e)}`,\n );\n process.exit(2);\n }\n }\n\n // Read correlation_id from env (both casings supported; orchestrators vary).\n const correlationId = process.env.correlation_id ?? process.env.CORRELATION_ID;\n\n const config = await loadConfig().catch(() => null);\n if (config) {\n sweepAuditLog(opts.auditPath, config.audit.retention_days).catch(() => {});\n }\n if (correlationId && config) {\n const isDup = await isDuplicateRun(\n opts.auditPath,\n correlationId,\n config.audit.idempotency_window_hours,\n );\n if (isDup) {\n console.log(`Skipped: duplicate correlation_id ${correlationId}`);\n process.exit(0);\n }\n }\n\n const result = await runSkill({\n skillName,\n inputJson,\n auditPath: opts.auditPath,\n ...(correlationId ? { correlationId } : {}),\n });\n\n if (result.error) {\n console.error(result.error);\n }\n if (result.output !== undefined) {\n console.log(JSON.stringify(result.output, null, 2));\n }\n if (result.exitCode !== 0) {\n process.exit(result.exitCode);\n }\n },\n );\n","import { readFileSync } from 'node:fs';\n\n/**\n * Returns true if a trigger event with workflow_id === correlationId\n * already exists in the audit log within the past windowHours.\n * Returns false if the file does not exist or no match is found.\n */\nexport async function isDuplicateRun(\n auditPath: string,\n correlationId: string,\n windowHours: number,\n): Promise<boolean> {\n let raw: string;\n try {\n raw = readFileSync(auditPath, 'utf8');\n } catch {\n return false;\n }\n\n const cutoff = Date.now() - windowHours * 3600 * 1000;\n\n for (const line of raw.split('\\n')) {\n const trimmed = line.trim();\n if (!trimmed) continue;\n let record: Record<string, unknown>;\n try {\n record = JSON.parse(trimmed) as Record<string, unknown>;\n } catch {\n continue;\n }\n if (\n record.event_type === 'trigger' &&\n record.workflow_id === correlationId &&\n typeof record.timestamp === 'string' &&\n Date.parse(record.timestamp) >= cutoff\n ) {\n return true;\n }\n }\n\n return false;\n}\n","import { createHash, randomUUID } from 'node:crypto';\nimport { appendFile, mkdir } from 'node:fs/promises';\nimport { dirname } from 'node:path';\nimport type { AuditConfig } from '../config/types.js';\nimport { AuditWriteError } from '../errors.js';\nimport type { TenantId } from '../types.js';\n\n/**\n * The event categories Parrat audits. Step 6 emits `trigger`, `skill_complete`,\n * and `error` directly. `claude_call` is added in step 7 (runtime + Claude);\n * `mcp_call` lands in M1 onward as MCPs come online.\n */\nexport type AuditEventType =\n | 'trigger'\n | 'skill_complete'\n | 'claude_call'\n | 'mcp_call'\n | 'skill_output_captured'\n | 'error';\n\n/**\n * Who initiated the event. `scheduler` for cron-triggered runs, `webhook` for\n * external alerts, `user` for CLI-invoked runs, `system` for internal events\n * (errors not tied to a specific user request).\n */\nexport type AuditActor = 'scheduler' | 'webhook' | 'user' | 'system';\n\n/**\n * What the caller passes to the logger (camelCase, ergonomic). The logger\n * fills in event_id, timestamp, and redaction_applied automatically.\n *\n * `workflowId` (M1) is the cross-Skill correlation identifier — for composite\n * Skills (Phase 1+) and external orchestrators (Airflow, Step Functions). If\n * omitted, the logger sets it to `runId` so single-Skill runs default to\n * `workflow_id == run_id` per Option C v1 forward-compat.\n */\nexport interface AuditEventInput {\n type: AuditEventType;\n tenantId: TenantId;\n runId: string;\n workflowId?: string;\n skill?: string;\n actor: AuditActor;\n payload: Record<string, unknown>;\n}\n\n/**\n * Payload shapes per event type. NOT enforced at write time (payload field is\n * generic Record<string, unknown>); these interfaces document the convention\n * for downstream consumers — replay (M2), observability tools, audit query.\n */\nexport interface TriggerPayload {\n input: unknown;\n triggerMetadata?: Record<string, unknown>;\n}\n\nexport interface ClaudeCallPayload {\n model: string;\n input_tokens: number;\n output_tokens: number;\n cost_estimate_usd: number;\n duration_ms: number;\n turn_index: number;\n}\n\nexport interface McpCallPayload {\n server: string;\n tool: string;\n args: Record<string, unknown>;\n result: unknown;\n duration_ms: number;\n turn_index: number;\n}\n\nexport interface SkillOutputCapturedPayload {\n output: unknown;\n turn_index: number;\n}\n\nexport interface SkillCompletePayload {\n output: unknown;\n duration_ms?: number;\n total_turns?: number;\n}\n\nexport interface ErrorPayload {\n error_name: string;\n error_message: string;\n stack?: string;\n}\n\nexport interface AuditLogger {\n write(input: AuditEventInput): Promise<void>;\n}\n\nexport interface CreateAuditLoggerOptions {\n filePath: string;\n auditConfig?: Pick<AuditConfig, 'hash_algorithm' | 'redact_fields'>;\n}\n\n// Fields to hash per event type: [sourceKey, replacementKey]\nconst HASH_FIELDS: Partial<Record<AuditEventType, [string, string][]>> = {\n mcp_call: [\n ['args', 'args_hash'],\n ['result', 'result_hash'],\n ],\n trigger: [['input', 'input_hash']],\n};\n\nfunction sha256hex(value: unknown): string {\n return createHash('sha256').update(JSON.stringify(value)).digest('hex');\n}\n\nfunction applyHashing(\n eventType: AuditEventType,\n payload: Record<string, unknown>,\n): Record<string, unknown> {\n const targets = HASH_FIELDS[eventType];\n if (!targets) return payload;\n const result = { ...payload };\n for (const [src, dest] of targets) {\n if (src in result) {\n result[dest] = sha256hex(result[src]);\n delete result[src];\n }\n }\n return result;\n}\n\nfunction applyRedaction(\n payload: Record<string, unknown>,\n redactFields: string[],\n): { payload: Record<string, unknown>; redacted: boolean } {\n if (redactFields.length === 0) return { payload, redacted: false };\n let redacted = false;\n const walk = (obj: Record<string, unknown>): Record<string, unknown> => {\n const out: Record<string, unknown> = {};\n for (const [k, v] of Object.entries(obj)) {\n if (redactFields.includes(k)) {\n out[k] = '[REDACTED]';\n redacted = true;\n } else if (v !== null && typeof v === 'object' && !Array.isArray(v)) {\n out[k] = walk(v as Record<string, unknown>);\n } else {\n out[k] = v;\n }\n }\n return out;\n };\n return { payload: walk(payload), redacted };\n}\n\n/**\n * Create an audit logger that appends NDJSON events to `options.filePath`.\n *\n * Each call to `write()` opens the file in append mode, writes one line, and\n * closes it. The parent directory is created lazily on first write. Write\n * failures (disk full, permissions, EISDIR) are wrapped in AuditWriteError\n * with the underlying error accessible via `.cause`.\n *\n * The on-disk record uses snake_case keys (`tenant_id`, `run_id`,\n * `event_type`, etc.) — this is THE wire format. Cloud and Enterprise audit\n * sinks (Phase 3+) will write the same record shape to S3 / BigQuery / SIEM.\n */\n/**\n * No-op audit logger — useful for unit tests that don't care about audit writes,\n * and for debug paths where audit needs to be temporarily disabled.\n * Returns a logger whose write() resolves immediately and emits nothing.\n */\nexport function createNoopAuditLogger(): AuditLogger {\n return { write: async () => {} };\n}\n\nexport function createAuditLogger(options: CreateAuditLoggerOptions): AuditLogger {\n return {\n write: async (input) => {\n const cfg = options.auditConfig;\n let payload = input.payload;\n let redactionApplied = false;\n\n if (cfg?.hash_algorithm) {\n payload = applyHashing(input.type, payload);\n }\n\n if (cfg?.redact_fields && cfg.redact_fields.length > 0) {\n const result = applyRedaction(payload, cfg.redact_fields);\n payload = result.payload;\n redactionApplied = result.redacted;\n }\n\n const record = {\n event_id: randomUUID(),\n timestamp: new Date().toISOString(),\n tenant_id: input.tenantId,\n run_id: input.runId,\n workflow_id: input.workflowId ?? input.runId,\n skill: input.skill,\n event_type: input.type,\n actor: input.actor,\n payload,\n redaction_applied: redactionApplied,\n };\n try {\n await mkdir(dirname(options.filePath), { recursive: true });\n await appendFile(options.filePath, `${JSON.stringify(record)}\\n`, 'utf8');\n } catch (e) {\n throw new AuditWriteError(options.filePath, e);\n }\n },\n };\n}\n","import { readFileSync } from 'node:fs';\nimport { writeFile } from 'node:fs/promises';\n\n/**\n * Deletes audit events older than retentionDays from the audit log.\n * Rewrites the file in-place with surviving events.\n * Returns the count of removed events. No-ops if the file does not exist.\n */\nexport async function sweepAuditLog(\n auditPath: string,\n retentionDays: number,\n): Promise<{ removed: number }> {\n let raw: string;\n try {\n raw = readFileSync(auditPath, 'utf8');\n } catch {\n return { removed: 0 };\n }\n\n const cutoff = Date.now() - retentionDays * 86400 * 1000;\n const survivors: string[] = [];\n let removed = 0;\n\n for (const line of raw.split('\\n')) {\n const trimmed = line.trim();\n if (!trimmed) continue;\n let record: Record<string, unknown>;\n try {\n record = JSON.parse(trimmed) as Record<string, unknown>;\n } catch {\n survivors.push(trimmed);\n continue;\n }\n if (typeof record.timestamp === 'string' && Date.parse(record.timestamp) < cutoff) {\n removed++;\n } else {\n survivors.push(trimmed);\n }\n }\n\n if (removed > 0) {\n await writeFile(auditPath, survivors.join('\\n') + (survivors.length > 0 ? '\\n' : ''), 'utf8');\n }\n\n return { removed };\n}\n","import 'dotenv/config';\nimport { randomUUID } from 'node:crypto';\nimport type { AuditActor, AuditLogger } from './audit/logger.js';\nimport { loadConfig } from './config/index.js';\nimport type { Config } from './config/types.js';\nimport { getClaudeKey } from './keys.js';\nimport { type LlmClient, createLlmClient } from './llm/client.js';\nimport type { SkillContext, SkillSpec } from './skills/Skill.js';\nimport type { SkillRegistry } from './skills/registry.js';\nimport { isTelemetryEnabled, track } from './telemetry.js';\nimport { DEFAULT_TENANT_ID, type TenantId } from './types.js';\n\n/**\n * What the caller passes to runtime.invoke. Input is `unknown` because the\n * caller (CLI parsing JSON, a webhook handler, etc.) typically doesn't know\n * the Skill's compile-time input shape — the Skill's Zod inputSchema validates\n * it at the boundary.\n *\n * `correlationId` (M1) — when an external orchestrator invokes Parrat, it\n * passes its workflow run identifier here. Parrat sets `workflow_id` on every\n * audit event to this value so audit logs correlate across systems. If\n * omitted, `workflow_id` defaults to `run_id`.\n */\nexport interface InvokeOptions {\n skill: string;\n input: unknown;\n actor: AuditActor;\n triggerMetadata?: Record<string, unknown>;\n correlationId?: string;\n}\n\nexport interface CreateRuntimeOptions {\n registry: SkillRegistry;\n auditLogger: AuditLogger;\n tenantId?: TenantId;\n /**\n * Pre-loaded config. If omitted, runtime calls loadConfig() lazily on first\n * invoke. Tests typically inject a config directly to avoid filesystem\n * dependencies.\n */\n config?: Config;\n /**\n * Pre-built LLM client. If omitted, runtime constructs one lazily when an\n * invoked Skill declares `mcpServers` (i.e., needs to talk to Claude).\n * Tests can inject a mock client.\n */\n llmClient?: LlmClient;\n}\n\nexport interface Runtime {\n invoke(options: InvokeOptions): Promise<unknown>;\n}\n\n/**\n * The runtime orchestrates a Skill invocation:\n *\n * 1. Look up the Skill by name (throws SkillNotFoundError if missing —\n * pre-flight failure, NOT audited)\n * 2. Generate a fresh runId (UUID); set workflowId = correlationId ?? runId\n * 3. If the Skill needs config / LLM (declares `mcpServers`), lazily load\n * config + construct an LlmClient. Otherwise leave undefined for\n * pure-function Skills like hello-world.\n * 4. Build a SkillContext with tenantId, runId, workflowId, auditLogger,\n * actor, config, llmClient\n * 5. Emit a `trigger` audit event capturing the input\n * 6. Run the Skill (input/output schema validation happens inside the Skill)\n * 7. On success: emit `skill_complete` with the output, return the output\n * On failure: emit `error` with name + message, then rethrow\n *\n * Skills MAY write their own audit events via ctx.auditLogger (e.g., the\n * skill-executor emits `mcp_call` events on every Claude tool invocation).\n */\nexport function createRuntime(options: CreateRuntimeOptions): Runtime {\n const { registry, auditLogger } = options;\n const tenantId = options.tenantId ?? DEFAULT_TENANT_ID;\n let cachedConfig: Config | undefined = options.config;\n let cachedLlmClient: LlmClient | undefined = options.llmClient;\n\n async function getConfig(): Promise<Config> {\n if (cachedConfig) return cachedConfig;\n cachedConfig = await loadConfig();\n return cachedConfig;\n }\n\n async function getLlmClient(): Promise<LlmClient> {\n if (cachedLlmClient) return cachedLlmClient;\n const apiKey = await getClaudeKey(tenantId);\n cachedLlmClient = createLlmClient({ apiKey });\n return cachedLlmClient;\n }\n\n return {\n invoke: async ({ skill: skillName, input, actor, triggerMetadata, correlationId }) => {\n const skill = registry.lookup(skillName);\n const runId = randomUUID();\n const workflowId = correlationId ?? runId;\n const skillSpec = skill as SkillSpec<typeof skill.inputSchema, typeof skill.outputSchema>;\n const skillNeedsLlm = !!skillSpec.mcpServers && Object.keys(skillSpec.mcpServers).length > 0;\n\n // Lazy: only load config / construct LLM client when the Skill needs them.\n const config = skillNeedsLlm ? await getConfig() : undefined;\n const llmClient = skillNeedsLlm ? await getLlmClient() : undefined;\n\n const ctx: SkillContext = {\n tenantId,\n runId,\n workflowId,\n auditLogger,\n actor,\n ...(config ? { config } : {}),\n ...(llmClient ? { llmClient } : {}),\n };\n\n await auditLogger.write({\n type: 'trigger',\n tenantId,\n runId,\n workflowId,\n skill: skillName,\n actor,\n payload: { input, triggerMetadata: triggerMetadata ?? {} },\n });\n\n try {\n const output = await skill.run(input, ctx);\n await auditLogger.write({\n type: 'skill_complete',\n tenantId,\n runId,\n workflowId,\n skill: skillName,\n actor,\n payload: { output },\n });\n if (config && isTelemetryEnabled(config)) {\n await track({ event: 'skill_complete', properties: { skill: skillName } });\n }\n return output;\n } catch (e) {\n await auditLogger.write({\n type: 'error',\n tenantId,\n runId,\n workflowId,\n skill: skillName,\n actor,\n payload: {\n error_name: e instanceof Error ? e.name : 'UnknownError',\n error_message: e instanceof Error ? e.message : String(e),\n },\n });\n throw e;\n }\n },\n };\n}\n","import { MissingClaudeKeyError } from './errors.js';\nimport type { TenantId } from './types.js';\n\n/**\n * Resolve the Claude API key for a given tenant.\n *\n * v1 (OSS): reads ANTHROPIC_API_KEY from env, ignoring tenantId.\n * Phase 3+ (Cloud): looks up by tenantId in a secrets backend.\n * Phase 3+ (Enterprise): KMS / HSM lookup.\n *\n * Async because Phase 3+ implementations involve network calls; committing\n * to async now avoids a breaking-change refactor later.\n *\n * Throws MissingClaudeKeyError if no key is available.\n */\nexport async function getClaudeKey(_tenantId: TenantId): Promise<string> {\n const key = process.env.ANTHROPIC_API_KEY;\n if (!key) {\n throw new MissingClaudeKeyError();\n }\n return key;\n}\n","import Anthropic from '@anthropic-ai/sdk';\nimport type { Message, MessageParam, Tool } from '@anthropic-ai/sdk/resources';\nimport { LlmApiError } from '../errors.js';\n\n/**\n * Per-call options for the LLM client. Mirrors the Anthropic SDK's\n * messages.create shape but exposes only what skill-executor needs to drive.\n */\nexport interface LlmCallOptions {\n model: string;\n maxTokens: number;\n temperature: number;\n system: string;\n messages: MessageParam[];\n tools: Tool[];\n}\n\nexport interface LlmClient {\n call(options: LlmCallOptions): Promise<Message>;\n}\n\nexport interface CreateLlmClientOptions {\n apiKey: string;\n /** Maximum retries on transient failures. Defaults to 3 (1s, 2s, 4s). */\n maxRetries?: number;\n}\n\n/**\n * Build an LLM client around the Anthropic SDK. The retry wrapper handles\n * transient failures (5xx, network timeouts, rate-limit) with exponential\n * backoff. 4xx errors are NOT retried — they're misuse, not flake.\n */\nexport function createLlmClient(options: CreateLlmClientOptions): LlmClient {\n const sdk = new Anthropic({ apiKey: options.apiKey });\n const maxRetries = options.maxRetries ?? 3;\n\n return {\n call: async (callOptions) => {\n let lastError: unknown;\n for (let attempt = 0; attempt <= maxRetries; attempt++) {\n try {\n return await sdk.messages.create({\n model: callOptions.model,\n max_tokens: callOptions.maxTokens,\n temperature: callOptions.temperature,\n system: callOptions.system,\n messages: callOptions.messages,\n tools: callOptions.tools,\n });\n } catch (error) {\n lastError = error;\n if (!isTransient(error) || attempt === maxRetries) break;\n await sleep(2 ** attempt * 1000);\n }\n }\n throw new LlmApiError(\n `Anthropic API call failed after ${maxRetries + 1} attempts`,\n lastError,\n );\n },\n };\n}\n\nfunction isTransient(error: unknown): boolean {\n if (!(error instanceof Anthropic.APIError)) return false;\n // 5xx, 408 (timeout), 429 (rate limit) are transient. 4xx other than 408/429 are not.\n const status = error.status ?? 0;\n return status >= 500 || status === 408 || status === 429;\n}\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n","import type { Config } from './config/types.js';\n\nexport interface TelemetryEvent {\n event: string;\n properties?: Record<string, unknown>;\n}\n\n// Always false in v1. Phase 1 reads config.telemetry.enabled when the\n// telemetry config section and backend endpoint are added.\nexport function isTelemetryEnabled(_config: Config): boolean {\n return false;\n}\n\n// No-op in v1. Phase 1 POSTs to the Parrat telemetry endpoint.\nexport async function track(_event: TelemetryEvent): Promise<void> {}\n","/**\n * Default tenant identifier used in OSS / single-tenant deployments.\n * In v1 every entity carries this value; multi-tenant runtime (Enterprise,\n * Phase 3+) replaces it without changing the data shape.\n */\nexport const DEFAULT_TENANT_ID = 'default' as const;\n\nexport type TenantId = string;\n\n/**\n * Base shape carried by every Parrat domain object.\n */\nexport interface ParratEntity {\n tenantId: TenantId;\n}\n","import { DuplicateSkillError, SkillNotFoundError } from '../errors.js';\nimport type { Skill } from './Skill.js';\n\n/**\n * Read-only Skill registry — created once at startup from the manifest in\n * src/skills/index.ts, consumed by the runtime to dispatch Skills by name.\n *\n * v1 design: static manifest. The runtime calls createRegistry(skills) once\n * with the full set of Skills it knows about; the registry is immutable after.\n *\n * Phase 1+ deferred: replace static creation with dynamic loading\n * (filesystem-based discovery, NPM-published Skill packages, or\n * import.meta.glob-style) when third-party Skills become a real use case.\n * See business/plan/v1-technical-spec.md §14 for the deferred-decision record.\n */\nexport interface SkillRegistry {\n list(): string[];\n has(name: string): boolean;\n lookup(name: string): Skill;\n}\n\nexport function createRegistry(skills: readonly Skill[]): SkillRegistry {\n const byName = new Map<string, Skill>();\n for (const skill of skills) {\n if (byName.has(skill.name)) {\n throw new DuplicateSkillError(skill.name);\n }\n byName.set(skill.name, skill);\n }\n\n return {\n list: () => Array.from(byName.keys()).sort(),\n has: (name) => byName.has(name),\n lookup: (name) => {\n const skill = byName.get(name);\n if (!skill) {\n throw new SkillNotFoundError(name, Array.from(byName.keys()).sort());\n }\n return skill;\n },\n };\n}\n","import type { MessageParam, Tool, ToolUseBlock } from '@anthropic-ai/sdk/resources';\nimport { ZodError, type ZodTypeAny, type z } from 'zod';\nimport { zodToJsonSchema } from 'zod-to-json-schema';\nimport type { AuditActor, AuditLogger } from '../audit/logger.js';\nimport { MaxTurnsExceededError, SchemaValidationError } from '../errors.js';\nimport {\n type McpClient,\n type McpServerConfig,\n type McpToolDefinition,\n connectMcpClient,\n resolveAllowlist,\n} from '../mcp/index.js';\nimport type { TenantId } from '../types.js';\nimport type { LlmClient } from './client.js';\n\n/**\n * Per-Skill MCP server declaration: the server config + the bare tool names\n * the Skill allows Claude to invoke.\n */\nexport interface SkillMcpServer {\n config: McpServerConfig;\n tools: string[];\n}\n\nexport interface SkillExecutorOptions<TOutputSchema extends ZodTypeAny> {\n skillName: string;\n llm: LlmClient;\n systemPrompt: string;\n userMessage: string;\n mcpServers: Record<string, SkillMcpServer>;\n outputSchema: TOutputSchema;\n model: string;\n maxTurns: number;\n maxTokens: number;\n temperature: number;\n auditLogger: AuditLogger;\n runId: string;\n workflowId: string;\n tenantId: TenantId;\n actor: AuditActor;\n}\n\nexport interface SkillExecutorResult<TOutput> {\n output: TOutput;\n totalTurns: number;\n inputTokens: number;\n outputTokens: number;\n totalCostUsd: number;\n durationMs: number;\n}\n\nconst COST_PER_MTOK: Record<string, { input: number; output: number }> = {\n 'claude-opus-4': { input: 15.0, output: 75.0 },\n 'claude-sonnet-4-6': { input: 3.0, output: 15.0 },\n 'claude-haiku-4-5': { input: 0.8, output: 4.0 },\n};\n\nfunction estimateCost(model: string, inputTokens: number, outputTokens: number): number {\n const entry = Object.entries(COST_PER_MTOK).find(([prefix]) => model.startsWith(prefix));\n if (!entry) return 0;\n const [, rates] = entry;\n return (inputTokens * rates.input + outputTokens * rates.output) / 1_000_000;\n}\n\n/**\n * Orchestrates the agentic execution of a Parrat Skill:\n * 1. Spawn declared MCP servers; list and filter their tools to the allowlist\n * 2. Inject emit_findings as the structured output channel (derived from outputSchema)\n * 3. Drive the Claude conversation across multiple turns:\n * — on tool_use: route MCP calls to the right server, emit mcp_call audit events,\n * feed tool_result back; handle emit_findings by capturing and validating output\n * — on end_turn: return captured output if emit_findings was called, else throw\n * 4. Emit claude_call audit events per turn and skill_output_captured on emit_findings\n * 5. Close all MCP servers in try/finally regardless of outcome\n *\n * The Skill's run() function calls this once and receives the validated output.\n * All MCP lifecycle, Claude conversation management, audit emission, and turn\n * budgeting happen here — the Skill itself has no knowledge of any of it.\n */\nexport async function executeSkill<TOutputSchema extends ZodTypeAny>(\n options: SkillExecutorOptions<TOutputSchema>,\n): Promise<SkillExecutorResult<z.infer<TOutputSchema>>> {\n const startedAt = Date.now();\n const clients: McpClient[] = [];\n\n // Build the Anthropic tool definitions + a lookup map from\n // fully-qualified tool name -> { client, bareName }\n const toolRouting = new Map<string, { client: McpClient; bareName: string }>();\n const tools: Tool[] = [];\n\n // Inject emit_findings as a synthetic tool — Claude must call it to produce output.\n // Input schema is derived from the Skill's outputSchema so the two stay in sync.\n const emitFindingsName = 'emit_findings';\n tools.push({\n name: emitFindingsName,\n description:\n 'Report your investigation findings. Call this exactly once when your investigation is complete.',\n input_schema: zodToJsonSchema(options.outputSchema) as Tool['input_schema'],\n });\n\n try {\n for (const [serverName, server] of Object.entries(options.mcpServers)) {\n const client = await connectMcpClient(serverName, server.config);\n clients.push(client);\n\n const allowlist = resolveAllowlist(serverName, server.tools);\n const allowedSet = new Set(server.tools);\n const allTools = await client.listTools();\n\n for (const tool of allTools) {\n if (!allowedSet.has(tool.name)) continue;\n const fqName = `mcp__${serverName}__${tool.name}`;\n toolRouting.set(fqName, { client, bareName: tool.name });\n tools.push({\n name: fqName,\n description: tool.description ?? `Tool ${tool.name} from MCP server ${serverName}`,\n input_schema: tool.inputSchema as Tool['input_schema'],\n });\n }\n // Sanity — fail loud if the allowlist names a tool the server didn't expose\n const exposedNames = new Set(allTools.map((t) => t.name));\n for (const expected of server.tools) {\n if (!exposedNames.has(expected)) {\n throw new Error(\n `MCP server '${serverName}' did not expose required tool '${expected}'. ` +\n `Available: ${[...exposedNames].join(', ') || '(none)'}.`,\n );\n }\n }\n // Mark allowlist as referenced (silence the unused warning)\n void allowlist;\n }\n\n const messages: MessageParam[] = [{ role: 'user', content: options.userMessage }];\n let inputTokens = 0;\n let outputTokens = 0;\n let totalCostUsd = 0;\n let capturedOutput: z.infer<TOutputSchema> | undefined;\n\n for (let turn = 0; turn < options.maxTurns; turn++) {\n const turnStartedAt = Date.now();\n const response = await options.llm.call({\n model: options.model,\n maxTokens: options.maxTokens,\n temperature: options.temperature,\n system: options.systemPrompt,\n messages,\n tools,\n });\n const turnDurationMs = Date.now() - turnStartedAt;\n\n inputTokens += response.usage.input_tokens;\n outputTokens += response.usage.output_tokens;\n const turnCostUsd = estimateCost(\n options.model,\n response.usage.input_tokens,\n response.usage.output_tokens,\n );\n totalCostUsd += turnCostUsd;\n\n await options.auditLogger.write({\n type: 'claude_call',\n tenantId: options.tenantId,\n runId: options.runId,\n workflowId: options.workflowId,\n skill: options.skillName,\n actor: options.actor,\n payload: {\n model: options.model,\n input_tokens: response.usage.input_tokens,\n output_tokens: response.usage.output_tokens,\n cost_estimate_usd: turnCostUsd,\n duration_ms: turnDurationMs,\n turn_index: turn,\n },\n });\n\n if (response.stop_reason === 'end_turn') {\n if (capturedOutput !== undefined) {\n return {\n output: capturedOutput,\n totalTurns: turn + 1,\n inputTokens,\n outputTokens,\n totalCostUsd,\n durationMs: Date.now() - startedAt,\n };\n }\n throw new MaxTurnsExceededError(options.skillName, options.maxTurns);\n }\n\n if (response.stop_reason === 'tool_use') {\n const toolUses = response.content.filter(\n (block): block is ToolUseBlock => block.type === 'tool_use',\n );\n\n messages.push({ role: 'assistant', content: response.content });\n\n const toolResultBlocks: { type: 'tool_result'; tool_use_id: string; content: string }[] =\n [];\n\n for (const toolUse of toolUses) {\n // emit_findings is handled here; not routed to any MCP client\n if (toolUse.name === emitFindingsName) {\n try {\n capturedOutput = options.outputSchema.parse(toolUse.input) as z.infer<TOutputSchema>;\n await options.auditLogger.write({\n type: 'skill_output_captured',\n tenantId: options.tenantId,\n runId: options.runId,\n workflowId: options.workflowId,\n skill: options.skillName,\n actor: options.actor,\n payload: { output: capturedOutput, turn_index: turn },\n });\n toolResultBlocks.push({\n type: 'tool_result',\n tool_use_id: toolUse.id,\n content: 'Findings recorded.',\n });\n } catch (e) {\n const message = e instanceof ZodError ? e.message : String(e);\n toolResultBlocks.push({\n type: 'tool_result',\n tool_use_id: toolUse.id,\n content: `Validation error — revise and call emit_findings again: ${message}`,\n });\n }\n continue;\n }\n\n const route = toolRouting.get(toolUse.name);\n if (!route) {\n throw new Error(\n `Claude requested tool '${toolUse.name}' which is not in the allowlist. This indicates a bug — the API SDK should never expose disallowed tools.`,\n );\n }\n\n const callStartedAt = Date.now();\n const args = toolUse.input as Record<string, unknown>;\n const result = await route.client.callTool(route.bareName, args);\n const durationMs = Date.now() - callStartedAt;\n\n await options.auditLogger.write({\n type: 'mcp_call',\n tenantId: options.tenantId,\n runId: options.runId,\n workflowId: options.workflowId,\n skill: options.skillName,\n actor: options.actor,\n payload: {\n server: route.client.serverName,\n tool: route.bareName,\n args,\n result: result.content,\n is_error: result.isError ?? false,\n duration_ms: durationMs,\n turn_index: turn,\n },\n });\n\n toolResultBlocks.push({\n type: 'tool_result',\n tool_use_id: toolUse.id,\n content: JSON.stringify(result.content),\n });\n }\n\n // If emit_findings was the only tool call and output is captured, we're done\n if (\n capturedOutput !== undefined &&\n toolResultBlocks.every((b) => b.content === 'Findings recorded.')\n ) {\n return {\n output: capturedOutput,\n totalTurns: turn + 1,\n inputTokens,\n outputTokens,\n totalCostUsd,\n durationMs: Date.now() - startedAt,\n };\n }\n\n messages.push({ role: 'user', content: toolResultBlocks });\n continue;\n }\n\n // Any other stop_reason (e.g., max_tokens, refusal) — bail\n throw new Error(\n `Unexpected stop_reason '${response.stop_reason}' from Claude in skill '${options.skillName}'`,\n );\n }\n\n throw new MaxTurnsExceededError(options.skillName, options.maxTurns);\n } finally {\n for (const client of clients) {\n try {\n await client.close();\n } catch {\n // Best-effort cleanup; don't shadow the original error\n }\n }\n }\n}\n","import { Client as McpSdkClient } from '@modelcontextprotocol/sdk/client/index.js';\nimport { StdioClientTransport } from '@modelcontextprotocol/sdk/client/stdio.js';\nimport type { McpServerConfig } from './types.js';\n\n/**\n * Tool definition exposed by an MCP server. Matches the @modelcontextprotocol/sdk\n * shape for `tools/list` results.\n */\nexport interface McpToolDefinition {\n name: string;\n description?: string | undefined;\n inputSchema: Record<string, unknown>;\n}\n\nexport interface McpToolCallResult {\n content: unknown;\n isError?: boolean | undefined;\n}\n\n/**\n * Connected MCP client — wraps the @modelcontextprotocol/sdk Client + a\n * StdioClientTransport. The lifecycle is tied to a single Skill invocation:\n * spawn → use → close in a try/finally.\n */\nexport interface McpClient {\n readonly serverName: string;\n listTools(): Promise<McpToolDefinition[]>;\n callTool(name: string, args: Record<string, unknown>): Promise<McpToolCallResult>;\n close(): Promise<void>;\n}\n\n/**\n * Spawn an MCP server as a subprocess and connect to it over stdio. Returns\n * a connected McpClient ready for listTools / callTool. Caller MUST call\n * close() when done (use try/finally).\n *\n * Inherits parent process env when `config.env` is not set, mirroring the\n * @modelcontextprotocol/sdk default. Concrete env values from config are\n * merged on top.\n */\nexport async function connectMcpClient(\n serverName: string,\n config: McpServerConfig,\n): Promise<McpClient> {\n const transport = new StdioClientTransport({\n command: config.command,\n args: [...config.args],\n env: { ...process.env, ...config.env } as Record<string, string>,\n });\n\n const sdkClient = new McpSdkClient(\n { name: 'parrat-mcp-client', version: '0.0.0' },\n { capabilities: {} },\n );\n\n await sdkClient.connect(transport);\n\n return {\n serverName,\n async listTools() {\n const result = await sdkClient.listTools();\n return result.tools.map((t) => ({\n name: t.name,\n description: t.description,\n inputSchema: t.inputSchema as Record<string, unknown>,\n }));\n },\n async callTool(name, args) {\n const result = await sdkClient.callTool({ name, arguments: args });\n return {\n content: result.content,\n isError: typeof result.isError === 'boolean' ? result.isError : undefined,\n };\n },\n async close() {\n await sdkClient.close();\n },\n };\n}\n","import { McpToolDeniedError } from '../errors.js';\nimport type { ResolvedToolAllowlist } from './types.js';\n\n/**\n * Compute the fully-qualified tool names for a Skill's tool allowlist. Used\n * to construct the Claude Agent SDK's `allowedTools` array — see\n * https://platform.claude.com/docs/en/agent-sdk/mcp for naming convention.\n *\n * Example: resolveAllowlist('dbt', ['list', 'get_node_details_dev']) →\n * { fullyQualified: ['mcp__dbt__list', 'mcp__dbt__get_node_details_dev'] }\n */\nexport function resolveAllowlist(\n serverName: string,\n toolNames: readonly string[],\n): ResolvedToolAllowlist {\n return {\n serverName,\n toolNames,\n fullyQualified: toolNames.map((name) => `mcp__${serverName}__${name}`),\n };\n}\n\n/**\n * Aggregate multiple resolved allowlists into a single deduplicated array of\n * fully-qualified tool names suitable for the Agent SDK's `allowedTools`.\n */\nexport function aggregateAllowlists(\n allowlists: readonly ResolvedToolAllowlist[],\n): readonly string[] {\n const seen = new Set<string>();\n for (const list of allowlists) {\n for (const name of list.fullyQualified) {\n seen.add(name);\n }\n }\n return [...seen];\n}\n\n/**\n * Defensive assertion — throws McpToolDeniedError if a tool call surfaces\n * outside the allowlist. The Agent SDK's `allowedTools` should prevent this\n * at the source; this is a defense-in-depth check for Phase 1+ when custom\n * MCP proxies or non-SDK clients may be involved.\n */\nexport function assertToolAllowed(toolName: string, allowlist: readonly string[]): void {\n if (!allowlist.includes(toolName)) {\n throw new McpToolDeniedError(toolName, allowlist);\n }\n}\n","import { ZodError, type ZodTypeAny, type z } from 'zod';\nimport type { AuditActor, AuditLogger } from '../audit/logger.js';\nimport type { Config, McpServerConfig } from '../config/types.js';\nimport { SchemaValidationError } from '../errors.js';\nimport type { LlmClient } from '../llm/client.js';\nimport type { TenantId } from '../types.js';\n\n/**\n * Categorizes a Skill's role in the level model (see action-layer-design-notes.md).\n * v1 ships only `'investigation'` Skills; `'action'` and `'composite'` are\n * Phase 1+ concepts but the type ships in v1 for forward-compat.\n */\nexport type SkillKind = 'investigation' | 'action' | 'composite';\n\n/**\n * Declares how an action Skill handles rollback. Type-only in v1; populated\n * by Phase 1+ action Skills.\n */\nexport type RollbackStrategy = 'best-effort' | 'transactional' | 'forward-only';\n\n/**\n * Approval gate policy for action Skills (Phase 1+). Type-only in v1.\n */\nexport interface ApprovalPolicy {\n channel?: string;\n timeout?: string;\n approvers?: string[];\n}\n\n/**\n * Approval request issued by a composite Skill via SkillContext.requestApproval.\n * Type-only in v1; runtime stub is undefined.\n */\nexport interface ApprovalRequest {\n message: string;\n scope?: 'one-time' | 'session' | 'persistent';\n}\n\nexport interface ApprovalResult {\n granted: boolean;\n approver_identity?: string;\n granted_at?: string;\n reason?: string;\n}\n\n/**\n * Per-Skill MCP server declaration. The `tools` array is the Skill's tool\n * allowlist (bare names; runtime resolves to `mcp__{server_name}__{tool_name}`\n * for the Claude Agent SDK's `allowedTools` parameter).\n */\nexport interface SkillMcpServer {\n config: McpServerConfig;\n tools: string[];\n}\n\n/**\n * Runtime context passed to every Skill invocation.\n *\n * - `workflowId` (M1) — defaults to `runId` for single-Skill executions; differs\n * when invoked by a composite Skill (Phase 1+) or when an external orchestrator\n * passes a `correlation_id` env var.\n * - `invokeSkill` / `requestApproval` — Phase 1+ stubs. Undefined in v1.\n */\nexport interface SkillContext {\n tenantId: TenantId;\n runId: string;\n workflowId: string;\n auditLogger: AuditLogger;\n /**\n * Who triggered the run (CLI user, webhook, scheduler, etc.). M1: runtime\n * always populates this. v1 Skills that don't need actor metadata can ignore.\n */\n actor?: AuditActor;\n /**\n * Loaded Parrat config — populated by runtime when present. Skills that\n * need config (LLM-driven Skills) read `ctx.config?.claude` etc.\n */\n config?: Config;\n /**\n * LLM client constructed by the runtime when the Skill declares\n * `mcpServers` in its spec. Pure-function Skills (e.g., hello-world) ignore.\n */\n llmClient?: LlmClient;\n invokeSkill?: (name: string, input: unknown) => Promise<unknown>;\n requestApproval?: (request: ApprovalRequest) => Promise<ApprovalResult>;\n}\n\n/**\n * The typed Skill specification — input/output Zod schemas + an async run method.\n * Generic over input/output schemas so TS can infer run's parameter and return types.\n *\n * M1 additions (all optional):\n * - `mcpServers` — declare which MCP servers + tool allowlist this Skill uses\n * - `systemPrompt` — system prompt for Skills that invoke the LLM client\n * - `kind` — defaults to 'investigation' when omitted\n * - `approvalRequired` / `rollbackStrategy` — type-only in v1; Phase 1+ usage\n */\nexport interface SkillSpec<TInputSchema extends ZodTypeAny, TOutputSchema extends ZodTypeAny> {\n name: string;\n inputSchema: TInputSchema;\n outputSchema: TOutputSchema;\n run(input: z.infer<TInputSchema>, ctx: SkillContext): Promise<z.infer<TOutputSchema>>;\n mcpServers?: Record<string, SkillMcpServer>;\n systemPrompt?: string;\n kind?: SkillKind;\n approvalRequired?: boolean | ApprovalPolicy;\n rollbackStrategy?: RollbackStrategy;\n}\n\n/**\n * Erased Skill type for registries holding heterogeneous Skills.\n * The registry doesn't know each Skill's specific schemas at compile time;\n * this type lets it store and dispatch them uniformly.\n */\nexport type Skill = SkillSpec<ZodTypeAny, ZodTypeAny>;\n\n/**\n * Define a Skill with type inference and automatic input/output validation.\n *\n * The returned Skill's run() validates input against inputSchema before invoking\n * the user's run logic, then validates output against outputSchema before returning.\n * Validation failures are wrapped in SchemaValidationError (with the original\n * ZodError accessible via .cause) so callers can handle them uniformly.\n */\nexport function defineSkill<TInputSchema extends ZodTypeAny, TOutputSchema extends ZodTypeAny>(\n spec: SkillSpec<TInputSchema, TOutputSchema>,\n): SkillSpec<TInputSchema, TOutputSchema> {\n const validate = <T>(schema: ZodTypeAny, value: unknown, direction: 'input' | 'output'): T => {\n try {\n return schema.parse(value) as T;\n } catch (e) {\n if (e instanceof ZodError) {\n throw new SchemaValidationError(direction, spec.name, e);\n }\n throw e;\n }\n };\n\n return {\n ...spec,\n run: async (input, ctx) => {\n const validInput = validate<z.infer<TInputSchema>>(spec.inputSchema, input, 'input');\n const output = await spec.run(validInput, ctx);\n return validate<z.infer<TOutputSchema>>(spec.outputSchema, output, 'output');\n },\n };\n}\n","import { readFile } from 'node:fs/promises';\nimport { join } from 'node:path';\n\nexport interface FreshnessContext {\n source: string;\n lastLoadedAt: string | null;\n status: 'fresh' | 'stale_warn' | 'stale_error' | 'unknown';\n thresholdBreached?: 'warn' | 'error';\n}\n\nexport interface FreshnessContextProvider {\n getContext(sources?: string[]): Promise<FreshnessContext[]>;\n}\n\ninterface SourcesJsonResult {\n unique_id: string;\n max_loaded_at?: string | null;\n status?: string;\n}\n\ninterface SourcesJson {\n results: SourcesJsonResult[];\n}\n\nexport class DbtFreshnessContextProvider implements FreshnessContextProvider {\n constructor(private readonly dbtProjectDir: string) {}\n\n async getContext(sources?: string[]): Promise<FreshnessContext[]> {\n const filePath = join(this.dbtProjectDir, 'target', 'sources.json');\n\n let raw: string;\n try {\n raw = await readFile(filePath, 'utf8');\n } catch (e) {\n if ((e as NodeJS.ErrnoException).code === 'ENOENT') return [];\n throw e;\n }\n\n let parsed: SourcesJson;\n try {\n parsed = JSON.parse(raw) as SourcesJson;\n } catch (e) {\n throw new Error(`sources.json parse error: ${(e as Error).message}`);\n }\n\n const results = parsed.results ?? [];\n const all = results.map(mapResult);\n\n if (!sources || sources.length === 0) return all;\n\n return all.filter((ctx) =>\n sources.some((s) => ctx.source === s || ctx.source.endsWith(`.${s}`)),\n );\n }\n}\n\nfunction mapResult(r: SourcesJsonResult): FreshnessContext {\n const lastLoadedAt = r.max_loaded_at ?? null;\n\n if (r.status === 'pass') {\n return { source: r.unique_id, lastLoadedAt, status: 'fresh' };\n }\n if (r.status === 'warn') {\n return { source: r.unique_id, lastLoadedAt, status: 'stale_warn', thresholdBreached: 'warn' };\n }\n if (r.status === 'error') {\n return {\n source: r.unique_id,\n lastLoadedAt,\n status: 'stale_error',\n thresholdBreached: 'error',\n };\n }\n return { source: r.unique_id, lastLoadedAt, status: 'unknown' };\n}\n","import { z } from 'zod';\n\n/**\n * Input shape for the freshness-investigation Skill.\n *\n * `source` is a dbt source identifier in 'source_name.table_name' format\n * (e.g., 'tpch.orders'). Omit to investigate all sources with freshness\n * configs in the project.\n *\n * `threshold` controls which level of staleness counts as a violation:\n * 'warn' surfaces sources past their warn_after threshold; 'error' (default)\n * surfaces only sources past error_after.\n */\nexport const inputSchema = z.object({\n source: z.string().min(1).optional(),\n threshold: z.enum(['warn', 'error']).default('error'),\n});\n\nexport type FreshnessInvestigationInput = z.infer<typeof inputSchema>;\n","import { z } from 'zod';\n\nconst staleSourceSchema = z.object({\n source: z.string().describe(\"Source identifier in 'source_name.table_name' format\"),\n last_loaded_at: z.string().describe('ISO 8601 timestamp of the most recent loaded data'),\n threshold_breached: z.enum(['warn', 'error']).describe('Which threshold was crossed'),\n summary: z.string().describe('One-paragraph explanation of why this specific source is stale'),\n});\n\nconst downstreamImpactSchema = z.object({\n models: z\n .array(z.string())\n .describe('Fully-qualified model names that depend on stale source(s)'),\n severity: z\n .enum(['high', 'medium', 'low'])\n .describe(\n 'Estimated business impact: high = critical mart; medium = intermediate; low = limited',\n ),\n});\n\nconst evidenceSchema = z.object({\n tool: z.string().describe('Which MCP tool produced this evidence'),\n finding: z.string().describe(\"What the tool returned that's load-bearing for the conclusion\"),\n});\n\n/**\n * Output shape for the freshness-investigation Skill.\n *\n * `status` covers all observed states including 'no_freshness_config' (source\n * has no freshness rules; can't be classified as fresh/stale) and 'unknown'\n * (Claude couldn't determine; evidence is incomplete).\n *\n * `confidence` calibration:\n * high — ≥2 corroborating tool results agree\n * medium — 1 tool result + plausible reasoning fills gaps\n * low — inferred from incomplete evidence; status='unknown' is preferred\n */\nexport const outputSchema = z.object({\n status: z.enum(['fresh', 'stale_warn', 'stale_error', 'no_freshness_config', 'unknown']),\n stale_sources: z.array(staleSourceSchema).default([]),\n confidence: z.enum(['high', 'medium', 'low']),\n root_cause_summary: z.string(),\n evidence: z.array(evidenceSchema).default([]),\n recommended_action: z.string().nullable(),\n downstream_impact: downstreamImpactSchema,\n});\n\nexport type FreshnessInvestigationOutput = z.infer<typeof outputSchema>;\n","import type { FreshnessContext } from './freshness-context-provider.js';\n\nexport function buildSystemPrompt(contexts: FreshnessContext[]): string {\n if (contexts.length === 0) return BASE_PROMPT;\n return `${BASE_PROMPT}\\n\\n${buildFreshnessBlock(contexts)}`;\n}\n\nfunction buildFreshnessBlock(contexts: FreshnessContext[]): string {\n const rows = contexts\n .map((c) => {\n const loaded = c.lastLoadedAt ?? 'unknown';\n const threshold = c.thresholdBreached ?? '—';\n return `| ${c.source} | ${loaded} | ${c.status} | ${threshold} |`;\n })\n .join('\\n');\n\n return `## Known freshness state\n\nThe following freshness verdicts were read from dbt's sources.json before this investigation started. Use these as your starting point — your tool calls should confirm root cause and trace downstream impact, not re-discover what is already known.\n\n| Source | Last Loaded | Status | Threshold Breached |\n|---|---|---|---|\n${rows}`;\n}\n\nconst BASE_PROMPT = `You are Parrat's freshness investigation agent. Your job: when given a stale dbt source (or asked to check all sources), determine the root cause by examining the dbt project's source freshness configs and last-loaded timestamps, then verify the warehouse state directly. Return a structured finding.\n\nYou have exactly four tools available:\n- mcp__dbt__list — enumerate sources/models in the dbt project\n- mcp__dbt__get_node_details_dev — pull a specific node's details, including freshness config and last_loaded_at\n- mcp__dbt__get_lineage_dev — trace downstream models that depend on a given source\n- mcp__dbt__show — execute a SELECT query against the connected warehouse via dbt's existing connection. Returns tabular results. Use for: row counts, last-ingested timestamps, spot-checks on raw tables.\n\nYou do NOT have other tools. Do not attempt to call dbt CLI commands directly or modify state.\n\n## CRITICAL: dbt naming conventions\n\nThe dbt-mcp tools use TWO different node-naming conventions for parameters that conceptually identify the same node. If you mix them up, every call fails:\n\n| Tool / Parameter | Format | Example |\n|---|---|---|\n| list.node_selection | dbt selector (COLON between type and name) | source:tpch.orders |\n| get_node_details_dev.node_id | dbt selector (COLON between type and name) | source:tpch.orders |\n| get_lineage_dev.unique_id | manifest unique_id (DOTS, includes project name) | source.parrat_dogfood.tpch.orders |\n\nDiagnostic tip: if you see \"No node found for **selector**: ...\" → you sent unique_id format to a tool expecting selector. Switch the type-separator from \".\" to \":\".\n\nIf you see \"No node found for **unique_id**: ...\" → you sent selector format to a tool expecting unique_id. Switch \":\" to \".\" and add the project name.\n\n## Investigation strategy\n\n1. ALWAYS call list({resource_type: [\"source\"]}) first to enumerate sources. It returns a newline-separated list of selectors in format source:<project>.<source>.<table> (e.g., source:parrat_dogfood.tpch.orders). This both confirms the source exists AND gives you the project name needed downstream.\n2. For each source under investigation: pass the selector verbatim to get_node_details_dev({node_id: \"...\"}) to retrieve freshness config + last_loaded_at.\n3. To trace lineage, convert the selector to unique_id by replacing \":\" with \".\": e.g., source:parrat_dogfood.tpch.orders → source.parrat_dogfood.tpch.orders. Pass to get_lineage_dev({unique_id: \"...\"}).\n4. Compare last_loaded_at against the user's threshold ('warn' or 'error', default 'error'). For stale sources, use show to verify at the warehouse layer: query the underlying table's row count and MAX(<timestamp_column>) to confirm the warehouse state matches dbt's freshness verdict. Example: show({sql: \"SELECT COUNT(*) as row_count, MAX(o_orderdate) as last_date FROM ORDERS LIMIT 1\"}).\n5. If show returns an error result: record it in evidence[] as { tool: \"show\", finding: \"warehouse query failed: <error message>\" }, fall back to the dbt-only freshness verdict, and set confidence: \"medium\". Do not retry show. Continue with what you have.\n6. Synthesize all findings into the structured output schema.\n\n## Confidence calibration\n\nYou must assign a confidence level for each investigation:\n- **high** — at least two tool results corroborate the conclusion (e.g., source freshness IS configured AND last_loaded_at IS past threshold AND lineage shows confirmed downstream models AND warehouse query confirms row delta)\n- **medium** — one tool result clearly supports the conclusion AND your reasoning fills any gaps (e.g., freshness config found, but warehouse query failed or lineage trace failed)\n- **low** — conclusion inferred from incomplete evidence (e.g., freshness config not found; you're guessing based on the source's apparent age)\n\nIf confidence would be low, prefer status='unknown' and explain in root_cause_summary what evidence is missing.\n\n## Anti-hallucination rules\n\n- If a source has no freshness configuration, set status='no_freshness_config' and explain.\n- If you cannot determine freshness from the available tools, set status='unknown'. Do not fabricate timestamps or guess values.\n- evidence[] must reference real tool results — do not invent findings.\n- recommended_action may be null if no clear action is appropriate.\n\n## Tool budget\n\nYou have at most 6 tool-call turns. Plan accordingly:\n- Single source: typically 2-3 turns (get_node_details_dev → get_lineage_dev → show)\n- All sources: 1 turn for list + 1 turn per source detail (batch where possible) + 1 lineage trace per stale source + 1 show per stale source\n\nIf you exceed the budget without a final answer, the system will throw an error.\n\n## Output\n\nWhen your investigation is complete, call 'emit_findings' with your structured findings. The schema is provided in the tool definition.`;\n","import { executeSkill } from '../../core/llm/skill-executor.js';\nimport { type SkillContext, defineSkill } from '../../core/skills/Skill.js';\nimport { DbtFreshnessContextProvider } from './freshness-context-provider.js';\nimport { type FreshnessInvestigationInput, inputSchema } from './input-schema.js';\nimport { type FreshnessInvestigationOutput, outputSchema } from './output-schema.js';\nimport { buildSystemPrompt } from './prompt.js';\n\n/**\n * The freshness-investigation Skill — Parrat's M1 wedge.\n *\n * Given a stale dbt source (or asked to check all sources), investigates the\n * root cause by walking the dbt project's freshness configs + lineage via\n * dbt-mcp tools, returning a structured finding.\n *\n * Tool allowlist (4 of dbt-mcp's 47 tools): `list`, `get_node_details_dev`,\n * `get_lineage_dev`, `show`. The thin tool surface is the methodology — see\n * business/plan/learning-dbt-vs-airbyte.md Lesson #7.\n *\n * The MCP server config (command, args, env) comes from the user's\n * parrat.config.yaml at runtime. Here we declare which tools the Skill\n * allowlists. The runtime merges Skill allowlist + user MCP server config\n * before spawning.\n */\nconst allowedDbtTools = ['list', 'get_node_details_dev', 'get_lineage_dev', 'show'];\n\nexport const freshnessInvestigationSkill = defineSkill({\n name: 'freshness-investigation',\n inputSchema,\n outputSchema,\n kind: 'investigation',\n // Tool allowlist — declared at the Skill level. The runtime resolves the\n // actual MCP server config (command, args, env) from the user's\n // parrat.config.yaml at invocation time.\n mcpServers: {\n dbt: {\n // The `config` field is filled in by the runtime from parrat.config.yaml\n // when the Skill is invoked. We use a placeholder here that the runtime\n // overrides; declaring it satisfies the SkillSpec type.\n config: { command: '', args: [], env: {} },\n tools: allowedDbtTools,\n },\n },\n\n async run(\n input: FreshnessInvestigationInput,\n ctx: SkillContext,\n ): Promise<FreshnessInvestigationOutput> {\n if (!ctx.config) {\n throw new Error(\n 'freshness-investigation requires runtime-provided config. Did the runtime forget to load it?',\n );\n }\n if (!ctx.llmClient) {\n throw new Error(\n 'freshness-investigation requires an LLM client. Did the runtime forget to construct one?',\n );\n }\n const userMcpServers = ctx.config.mcpServers;\n const dbtUserConfig = userMcpServers.dbt;\n if (!dbtUserConfig) {\n throw new Error(\n \"freshness-investigation requires an 'dbt' MCP server in parrat.config.yaml.\",\n );\n }\n\n const dbtProjectDir = ctx.config.mcpServers.dbt?.env?.DBT_PROJECT_DIR;\n if (!dbtProjectDir) {\n throw new Error(\n 'freshness-investigation: DBT_PROJECT_DIR must be set in mcpServers.dbt.env in parrat.config.yaml',\n );\n }\n const provider = new DbtFreshnessContextProvider(dbtProjectDir);\n const contexts = await provider.getContext(input.source ? [input.source] : undefined);\n const prompt = buildSystemPrompt(contexts);\n\n const result = await executeSkill({\n skillName: 'freshness-investigation',\n llm: ctx.llmClient,\n systemPrompt: prompt,\n userMessage: JSON.stringify(input),\n mcpServers: {\n dbt: {\n config: dbtUserConfig,\n tools: allowedDbtTools,\n },\n },\n outputSchema,\n model: ctx.config.claude.model,\n maxTurns: ctx.config.claude.max_turns,\n maxTokens: ctx.config.claude.max_tokens,\n temperature: ctx.config.claude.temperature,\n auditLogger: ctx.auditLogger,\n runId: ctx.runId,\n workflowId: ctx.workflowId,\n tenantId: ctx.tenantId,\n actor: ctx.actor ?? 'user',\n });\n\n return result.output;\n },\n});\n","import { z } from 'zod';\n\nexport const inputSchema = z.object({\n node_id: z.string().min(1),\n direction: z.enum(['upstream', 'downstream', 'both']).default('both'),\n depth: z.number().int().min(1).max(5).default(3),\n project_path: z.string().optional(),\n});\n\nexport type LineageAnalysisInput = z.infer<typeof inputSchema>;\n","import { z } from 'zod';\n\nconst evidenceSchema = z.object({\n tool: z.string().describe('Which MCP tool produced this evidence'),\n finding: z.string().describe(\"What the tool returned that's load-bearing for the conclusion\"),\n});\n\nexport const outputSchema = z.object({\n node_id: z.string(),\n upstream_nodes: z.array(z.string()),\n downstream_nodes: z.array(z.string()),\n impact_count: z.number().int(),\n impact_summary: z.string(),\n critical_path: z.array(z.string()).optional(),\n truncated: z.boolean().default(false),\n confidence: z.enum(['high', 'medium', 'low']),\n evidence: z.array(evidenceSchema).default([]),\n});\n\nexport type LineageAnalysisOutput = z.infer<typeof outputSchema>;\n","export const BASE_PROMPT = `You are Parrat's lineage analysis agent. You are given a dbt node identifier, a direction (upstream, downstream, or both), and a depth limit. Your job: map the lineage graph for that node, summarise the impact, and identify the critical path if one exists.\n\nYou have exactly three tools available:\n- mcp__dbt__list — enumerate models/sources to confirm a node exists and retrieve its selector\n- mcp__dbt__get_node_details_dev — pull a node's details to verify it exists and get its type\n- mcp__dbt__get_lineage_dev — retrieve the upstream and/or downstream lineage graph\n\nYou do NOT have other tools. Do not attempt to call dbt CLI directly or modify state.\n\n## CRITICAL: dbt naming conventions\n\nThe dbt-mcp tools use TWO different node-naming conventions. If you mix them up, every call fails:\n\n| Tool / Parameter | Format | Example |\n|---|---|---|\n| list.node_selection | dbt selector (COLON between type and name) | model:fct_orders |\n| get_node_details_dev.node_id | dbt selector (COLON between type and name) | model:fct_orders |\n| get_lineage_dev.unique_id | manifest unique_id (DOTS, includes project name) | model.parrat_dogfood.fct_orders |\n\nDiagnostic tip: \"No node found for selector\" → switch \".\" to \":\". \"No node found for unique_id\" → switch \":\" to \".\" and add the project name.\n\n## Investigation strategy\n\n1. Call list() to confirm the target node exists and retrieve its selector. This also gives you the project name needed for unique_id construction.\n2. Call get_lineage_dev({unique_id: \"<node_unique_id>\", depth: <depth>}) with the direction from input. Convert selector to unique_id by replacing \":\" with \".\" and prepending the project name.\n3. From the returned graph, extract:\n - upstream_nodes: all nodes that feed into the target (empty array if direction is 'downstream')\n - downstream_nodes: all nodes the target feeds into (empty array if direction is 'upstream')\n - impact_count: total count of upstream_nodes + downstream_nodes\n4. If impact_count exceeds 50, set truncated: true and limit each list to the 25 closest nodes (by graph distance). Note the truncation in impact_summary.\n5. Identify critical_path if one is apparent: the longest chain of high-fan-out models, or the chain connecting the target to a known mart or reporting layer. Omit critical_path entirely if the graph is shallow or no clear path stands out.\n6. Write impact_summary: one paragraph describing what the lineage means in plain English — which upstream sources feed this node, which downstream reports or marts depend on it, and the blast radius of a change to this node.\n\n## Confidence calibration\n\n- **high** — get_lineage_dev returned a non-empty graph and node details confirmed the node type\n- **medium** — get_lineage_dev succeeded but node details call failed, or graph was truncated\n- **low** — could not retrieve lineage; impact inferred from node name alone\n\n## Anti-hallucination rules\n\n- upstream_nodes and downstream_nodes must contain only unique_ids returned by get_lineage_dev — do not invent nodes.\n- If get_lineage_dev returns an empty graph, return empty arrays and set confidence: \"medium\" with a note in impact_summary explaining why the graph is empty.\n- critical_path may be omitted (undefined) if no clear path exists — do not fabricate one.\n\n## Tool budget\n\nYou have at most 4 tool-call turns:\n- list (1) → get_lineage_dev (1) → optional get_node_details_dev for critical path clarification (1) → emit_findings (1)\n\n## Output\n\nWhen your analysis is complete, call 'emit_findings' with your structured findings. The schema is provided in the tool definition.`;\n","import { executeSkill } from '../../core/llm/skill-executor.js';\nimport { type SkillContext, defineSkill } from '../../core/skills/Skill.js';\nimport { type LineageAnalysisInput, inputSchema } from './input-schema.js';\nimport { type LineageAnalysisOutput, outputSchema } from './output-schema.js';\nimport { BASE_PROMPT } from './prompt.js';\n\nconst allowedDbtTools = ['list', 'get_node_details_dev', 'get_lineage_dev'];\n\nexport const lineageAnalysisSkill = defineSkill({\n name: 'lineage-analysis',\n inputSchema,\n outputSchema,\n kind: 'investigation',\n mcpServers: {\n dbt: {\n config: { command: '', args: [], env: {} },\n tools: allowedDbtTools,\n },\n },\n\n async run(input: LineageAnalysisInput, ctx: SkillContext): Promise<LineageAnalysisOutput> {\n if (!ctx.config) throw new Error('lineage-analysis requires runtime-provided config.');\n if (!ctx.llmClient) throw new Error('lineage-analysis requires an LLM client.');\n\n const dbtUserConfig = ctx.config.mcpServers.dbt;\n if (!dbtUserConfig) {\n throw new Error(\"lineage-analysis requires a 'dbt' MCP server in parrat.config.yaml.\");\n }\n\n const result = await executeSkill({\n skillName: 'lineage-analysis',\n llm: ctx.llmClient,\n systemPrompt: BASE_PROMPT,\n userMessage: JSON.stringify(input),\n mcpServers: { dbt: { config: dbtUserConfig, tools: allowedDbtTools } },\n outputSchema,\n model: ctx.config.claude.model,\n maxTurns: ctx.config.claude.max_turns,\n maxTokens: ctx.config.claude.max_tokens,\n temperature: ctx.config.claude.temperature,\n auditLogger: ctx.auditLogger,\n runId: ctx.runId,\n workflowId: ctx.workflowId,\n tenantId: ctx.tenantId,\n actor: ctx.actor ?? 'user',\n });\n\n return result.output;\n },\n});\n","import { z } from 'zod';\n\nexport const inputSchema = z.object({\n metric_name: z.string().min(1),\n model_id: z.string().min(1),\n metric_column: z.string().min(1),\n drop_percent: z.number().min(0).max(100),\n time_window_hours: z.number().positive().default(24),\n project_path: z.string().optional(),\n});\n\nexport type MetricDropRcaInput = z.infer<typeof inputSchema>;\n","import { z } from 'zod';\n\nconst evidenceSchema = z.object({\n tool: z.string().describe('Which MCP tool produced this evidence'),\n finding: z.string().describe(\"What the tool returned that's load-bearing for the conclusion\"),\n});\n\nexport const outputSchema = z.object({\n metric_name: z.string(),\n drop_percent: z.number(),\n status: z.enum([\n 'data_missing',\n 'volume_drop',\n 'upstream_model_issue',\n 'pipeline_failure',\n 'schema_change',\n 'unknown',\n ]),\n root_cause: z.string(),\n suspect_models: z.array(z.string()),\n confidence: z.enum(['high', 'medium', 'low']),\n recommended_action: z.string().nullable(),\n evidence: z.array(evidenceSchema).default([]),\n});\n\nexport type MetricDropRcaOutput = z.infer<typeof outputSchema>;\n","export const BASE_PROMPT = `You are Parrat's metric drop RCA agent. You are given a metric name, the dbt model that computes it, the affected column, and an observed drop percentage. Your job: determine the root cause of the metric drop by examining the model's SQL, its upstream dependencies, and the current warehouse data.\n\nYou have exactly four tools available:\n- mcp__dbt__list — enumerate models/sources in the dbt project\n- mcp__dbt__get_node_details_dev — pull a node's details, including compiled SQL and database relation info\n- mcp__dbt__get_lineage_dev — trace upstream/downstream dependencies for a given node\n- mcp__dbt__show — execute a SELECT query against the connected warehouse via dbt's existing connection. Returns tabular results. Use for: current vs historical aggregates, upstream row counts, data volume checks.\n\nYou do NOT have other tools. Do not attempt to call dbt CLI directly or modify state.\n\n## CRITICAL: dbt naming conventions\n\nThe dbt-mcp tools use TWO different node-naming conventions. If you mix them up, every call fails:\n\n| Tool / Parameter | Format | Example |\n|---|---|---|\n| list.node_selection | dbt selector (COLON between type and name) | model:fct_orders |\n| get_node_details_dev.node_id | dbt selector (COLON between type and name) | model:fct_orders |\n| get_lineage_dev.unique_id | manifest unique_id (DOTS, includes project name) | model.parrat_dogfood.fct_orders |\n\nDiagnostic tip: \"No node found for selector\" → switch \".\" to \":\". \"No node found for unique_id\" → switch \":\" to \".\" and add the project name.\n\n## Investigation strategy\n\n1. Call list({resource_type: [\"model\"]}) to confirm the target model exists and retrieve its selector. This also gives you the project name needed for unique_id construction downstream.\n2. Call get_node_details_dev to retrieve the model's compiled SQL and database relation (schema + table name). Read the SQL carefully: identify which column feeds the metric, which date/timestamp column drives time partitioning.\n3. Use show to run two comparison queries against the warehouse table from step 2:\n - Current window: SELECT <agg>(<metric_column>) FROM <schema>.<table> WHERE <date_col> >= <now minus time_window_hours>\n - Previous window: same query shifted back one full time_window_hours period\n Compare values to confirm the drop magnitude matches the reported drop_percent.\n4. Call get_lineage_dev({unique_id: \"<model_unique_id>\", direction: \"upstream\"}) to find upstream models and sources. Convert selector to unique_id by replacing \":\" with \".\" and prepending the project name.\n5. For the most likely upstream contributors, use show to check row counts and MAX(<timestamp>) for both time windows. A sudden volume drop or missing rows upstream is the most common root cause.\n6. Synthesize: identify suspect_models (dbt unique_ids), classify status, write root_cause.\n\n## Confidence calibration\n\n- **high** — warehouse queries confirmed the drop magnitude AND an upstream volume or quality change was identified\n- **medium** — warehouse query succeeded but upstream cause is unclear; or upstream identified but warehouse query failed\n- **low** — could not query warehouse; root cause inferred from model structure alone\n\nIf confidence would be low, set status='unknown' and explain what evidence is missing in root_cause.\n\n## Anti-hallucination rules\n\n- Do not fabricate table names or column names. Read them from get_node_details_dev output.\n- If show returns an error: record it in evidence[] as { tool: \"show\", finding: \"query failed: <error>\" }, set confidence to at most \"medium\", and continue without retrying.\n- suspect_models must contain dbt unique_ids from actual tool results — do not invent them.\n- recommended_action may be null if no clear action is appropriate.\n\n## Tool budget\n\nYou have at most 8 tool-call turns. Plan accordingly:\n- Typical path: list (1) → get_node_details_dev (1) → show × 2 (2) → get_lineage_dev (1) → show upstream × 1–2 (1–2) → emit_findings (1)\n\n## Output\n\nWhen your investigation is complete, call 'emit_findings' with your structured findings. The schema is provided in the tool definition.`;\n","import { executeSkill } from '../../core/llm/skill-executor.js';\nimport { type SkillContext, defineSkill } from '../../core/skills/Skill.js';\nimport { type MetricDropRcaInput, inputSchema } from './input-schema.js';\nimport { type MetricDropRcaOutput, outputSchema } from './output-schema.js';\nimport { BASE_PROMPT } from './prompt.js';\n\nconst allowedDbtTools = ['list', 'get_node_details_dev', 'get_lineage_dev', 'show'];\n\nexport const metricDropRcaSkill = defineSkill({\n name: 'metric-drop-rca',\n inputSchema,\n outputSchema,\n kind: 'investigation',\n mcpServers: {\n dbt: {\n config: { command: '', args: [], env: {} },\n tools: allowedDbtTools,\n },\n },\n\n async run(input: MetricDropRcaInput, ctx: SkillContext): Promise<MetricDropRcaOutput> {\n if (!ctx.config) throw new Error('metric-drop-rca requires runtime-provided config.');\n if (!ctx.llmClient) throw new Error('metric-drop-rca requires an LLM client.');\n\n const dbtUserConfig = ctx.config.mcpServers.dbt;\n if (!dbtUserConfig) {\n throw new Error(\"metric-drop-rca requires a 'dbt' MCP server in parrat.config.yaml.\");\n }\n\n const result = await executeSkill({\n skillName: 'metric-drop-rca',\n llm: ctx.llmClient,\n systemPrompt: BASE_PROMPT,\n userMessage: JSON.stringify(input),\n mcpServers: { dbt: { config: dbtUserConfig, tools: allowedDbtTools } },\n outputSchema,\n model: ctx.config.claude.model,\n maxTurns: ctx.config.claude.max_turns,\n maxTokens: ctx.config.claude.max_tokens,\n temperature: ctx.config.claude.temperature,\n auditLogger: ctx.auditLogger,\n runId: ctx.runId,\n workflowId: ctx.workflowId,\n tenantId: ctx.tenantId,\n actor: ctx.actor ?? 'user',\n });\n\n return result.output;\n },\n});\n","import type { Skill } from '../core/skills/Skill.js';\nimport { freshnessInvestigationSkill } from './freshness-investigation/index.js';\nimport { lineageAnalysisSkill } from './lineage-analysis/index.js';\nimport { metricDropRcaSkill } from './metric-drop-rca/index.js';\n\nexport const skills: readonly Skill[] = [\n freshnessInvestigationSkill,\n metricDropRcaSkill,\n lineageAnalysisSkill,\n];\n","import { Command } from 'commander';\nimport { createRegistry } from '../core/skills/registry.js';\nimport { skills } from '../skills/index.js';\n\n/**\n * Returns the list of installed Skill names, sorted alphabetically.\n * Pure function — used by the CLI handler and unit tests alike.\n */\nexport function listSkillNames(): string[] {\n return createRegistry(skills).list();\n}\n\nconst listCommand = new Command('list').description('List all installed Skills').action(() => {\n const names = listSkillNames();\n if (names.length === 0) {\n console.log('No Skills installed.');\n return;\n }\n for (const name of names) {\n console.log(name);\n }\n});\n\nexport const skillsCommand = new Command('skills')\n .description('Manage Parrat Skills')\n .addCommand(listCommand);\n","import { resolve } from 'node:path';\nimport { Command } from 'commander';\nimport { isDuplicateRun } from '../core/audit/idempotency.js';\nimport { sweepAuditLog } from '../core/audit/retention.js';\nimport { loadConfig } from '../core/config/loader.js';\nimport type { Config } from '../core/config/types.js';\nimport { SlackNotifier } from '../core/notify/slack.js';\nimport { runSkill } from './run.js';\n\nexport interface WatchOptions {\n config: Config;\n auditPath: string;\n}\n\nexport interface WatchResult {\n exitCode: number;\n error?: string;\n}\n\nfunction formatSlackMessage(skillName: string, output: unknown, error: string | undefined): string {\n if (error) {\n return `[parrat] ${skillName} | FAILED\\n${error}`;\n }\n const json = JSON.stringify(output, null, 2);\n const body = json.length > 2000 ? `${json.slice(0, 2000)}\\n...(truncated)` : json;\n return `[parrat] ${skillName} | OK\\n${body}`;\n}\n\n/**\n * Pure handler for `parrat watch`. Reads skill + input from config.watch,\n * runs the skill, then delivers to config.notify.slack if configured.\n * Throws are not caught here — the Commander wrapper handles process.exit.\n */\nexport async function watchSkill(options: WatchOptions): Promise<WatchResult> {\n const { config, auditPath } = options;\n\n if (!config.watch) {\n return {\n exitCode: 1,\n error:\n \"No 'watch' section in config. Add watch.skill and watch.input to .parrat/config.yaml.\",\n };\n }\n\n const { skill, input } = config.watch;\n\n const runResult = await runSkill({\n skillName: skill,\n inputJson: JSON.stringify(input),\n auditPath: resolve(auditPath),\n });\n\n const slackWebhookUrl = config.notify?.slack?.webhook_url;\n if (slackWebhookUrl) {\n const message = formatSlackMessage(skill, runResult.output, runResult.error);\n const notifier = new SlackNotifier(slackWebhookUrl);\n try {\n await notifier.send({ text: message });\n } catch (e) {\n const notifyError = e instanceof Error ? e.message : String(e);\n return {\n exitCode: 1,\n error: `Skill ${runResult.exitCode === 0 ? 'succeeded' : 'failed'} but Slack notification failed: ${notifyError}`,\n };\n }\n }\n\n return {\n exitCode: runResult.exitCode,\n ...(runResult.error ? { error: runResult.error } : {}),\n };\n}\n\nexport const watchCommand = new Command('watch')\n .description('Run the configured watch skill once (schedule via cron / Task Scheduler)')\n .option('--audit-path <path>', 'Path to audit log file', '.parrat/audit.jsonl')\n .action(async (opts: { auditPath: string }) => {\n let config: Config;\n try {\n config = await loadConfig();\n } catch (e) {\n console.error(e instanceof Error ? e.message : String(e));\n process.exit(4);\n }\n\n sweepAuditLog(opts.auditPath, config.audit.retention_days).catch(() => {});\n\n const correlationId = process.env.correlation_id ?? process.env.CORRELATION_ID;\n if (correlationId) {\n const isDup = await isDuplicateRun(\n opts.auditPath,\n correlationId,\n config.audit.idempotency_window_hours,\n );\n if (isDup) {\n console.log(`Skipped: duplicate correlation_id ${correlationId}`);\n process.exit(0);\n }\n }\n\n const result = await watchSkill({ config, auditPath: opts.auditPath });\n\n if (result.error) {\n console.error(result.error);\n }\n if (result.exitCode !== 0) {\n process.exit(result.exitCode);\n }\n });\n","export interface SlackMessage {\n text: string;\n}\n\n/**\n * Delivers a message to a Slack incoming webhook. No OAuth — just a POST.\n * Throws on network failure or non-2xx response so callers can exit non-zero.\n */\nexport class SlackNotifier {\n constructor(private readonly webhookUrl: string) {}\n\n async send(message: SlackMessage): Promise<void> {\n let response: Response;\n try {\n response = await fetch(this.webhookUrl, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(message),\n });\n } catch (e) {\n throw new Error(`Slack webhook POST failed: ${e instanceof Error ? e.message : String(e)}`);\n }\n\n if (!response.ok) {\n const body = await response.text().catch(() => '');\n throw new Error(`Slack webhook returned ${response.status}: ${body}`);\n }\n }\n}\n","import { createServer } from 'node:http';\nimport type { IncomingMessage, Server, ServerResponse } from 'node:http';\nimport { Command } from 'commander';\nimport { loadConfig } from '../core/config/index.js';\nimport type { Config } from '../core/config/types.js';\nimport type { FreshnessInvestigationInput } from '../skills/freshness-investigation/input-schema.js';\nimport { runSkill } from './run.js';\n\nexport interface WebhookOptions {\n config: Config;\n auditPath: string;\n port: number;\n secret?: string;\n}\n\nexport interface WebhookServer {\n port: number;\n close(): void;\n}\n\n/**\n * Maps a Monte Carlo freshness alert payload to a FreshnessInvestigationInput.\n * Returns null if the payload is not a recognised Monte Carlo freshness alert.\n */\nexport function mapMonteCarloPayload(\n body: unknown,\n): { skill: string; input: FreshnessInvestigationInput } | null {\n if (typeof body !== 'object' || body === null) return null;\n const b = body as Record<string, unknown>;\n if (b.alert_type !== 'freshness') return null;\n\n const sourceRaw = b.table ?? b.source_name;\n const input: FreshnessInvestigationInput = {\n threshold: 'error',\n ...(typeof sourceRaw === 'string' && sourceRaw.length > 0 ? { source: sourceRaw } : {}),\n };\n\n return { skill: 'freshness-investigation', input };\n}\n\nfunction readBody(req: IncomingMessage): Promise<string> {\n return new Promise((resolve, reject) => {\n const chunks: Buffer[] = [];\n req.on('data', (chunk: Buffer) => chunks.push(chunk));\n req.on('end', () => resolve(Buffer.concat(chunks).toString('utf8')));\n req.on('error', reject);\n });\n}\n\nfunction send(res: ServerResponse, status: number, body: unknown): void {\n const json = JSON.stringify(body);\n res.writeHead(status, { 'Content-Type': 'application/json' });\n res.end(json);\n}\n\nexport function startWebhook(options: WebhookOptions): Promise<WebhookServer> {\n const { config, auditPath, port, secret } = options;\n\n const server: Server = createServer(async (req, res) => {\n if (req.method !== 'POST' || req.url !== '/trigger') {\n send(res, 404, { error: 'Not found. Only POST /trigger is supported.' });\n return;\n }\n\n if (secret) {\n const header = req.headers['x-parrat-secret'];\n if (header !== secret) {\n send(res, 401, { error: 'Unauthorized. X-Parrat-Secret header missing or incorrect.' });\n return;\n }\n }\n\n let body: unknown;\n try {\n const raw = await readBody(req);\n body = JSON.parse(raw);\n } catch {\n send(res, 400, { error: 'Invalid JSON body.' });\n return;\n }\n\n const mapped = mapMonteCarloPayload(body);\n if (!mapped) {\n send(res, 400, {\n error: 'Unrecognised payload format. Expected a Monte Carlo freshness alert.',\n });\n return;\n }\n\n const notifyConfig = config.notify;\n const slackWebhookUrl = notifyConfig?.slack?.webhook_url;\n\n const result = await runSkill({\n skillName: mapped.skill,\n inputJson: JSON.stringify(mapped.input),\n auditPath,\n ...(slackWebhookUrl ? {} : {}),\n });\n\n if (result.exitCode === 0) {\n send(res, 200, { ok: true, output: result.output });\n } else {\n send(res, 500, { error: result.error ?? 'Skill execution failed.' });\n }\n });\n\n return new Promise((resolve) => {\n server.listen(port, () => {\n const addr = server.address() as { port: number };\n console.log(`parrat webhook listening on port ${addr.port}`);\n resolve({\n port: addr.port,\n close() {\n server.close();\n },\n });\n });\n });\n}\n\nexport const webhookCommand = new Command('webhook')\n .description('Start an HTTP listener that accepts external alert triggers (e.g. Monte Carlo)')\n .option('--port <number>', 'Port to listen on (overrides config)', (v) => Number.parseInt(v, 10))\n .option('--audit-path <path>', 'Path to audit log file', '.parrat/audit.jsonl')\n .action(async (opts: { port?: number; auditPath: string }) => {\n const config = await loadConfig();\n const port = opts.port ?? config.webhook?.port ?? 8080;\n const secret = config.webhook?.secret;\n\n const webhook = await startWebhook({\n config,\n auditPath: opts.auditPath,\n port,\n ...(secret ? { secret } : {}),\n });\n\n process.on('SIGINT', () => {\n webhook.close();\n process.exit(0);\n });\n process.on('SIGTERM', () => {\n webhook.close();\n process.exit(0);\n });\n });\n"],"mappings":"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","names":["Command","Command","readFileSync","readFileSync","resolve","stdout","Command","mkdir","dirname","resolve","Command","readFileSync","resolve","Command","formatRecord","readFileSync","resolve","Command","readFileSync","mkdir","dirname","mkdir","dirname","readFileSync","writeFile","randomUUID","resolve","randomUUID","skills","ZodError","ZodError","z","z","z","inputSchema","z","evidenceSchema","outputSchema","BASE_PROMPT","allowedDbtTools","inputSchema","outputSchema","BASE_PROMPT","z","inputSchema","z","evidenceSchema","outputSchema","BASE_PROMPT","allowedDbtTools","inputSchema","outputSchema","BASE_PROMPT","resolve","Command","readFileSync","Command","Command","resolve","Command","resolve","Command","Command","resolve","Command","Command"]}
@@ -0,0 +1,25 @@
1
+ # Custom Skill example — row-count-check
2
+
3
+ A minimal Parrat Skill that checks whether a dbt model has at least a minimum number of rows.
4
+
5
+ ## What it demonstrates
6
+
7
+ - The full Skill contract: `inputSchema`, `outputSchema`, `run()`
8
+ - A thin tool surface: 1 tool (`show`) is all this investigation needs
9
+ - How to write a system prompt that drives `emit_findings`
10
+
11
+ ## How to use it
12
+
13
+ 1. Copy `index.ts` to `.parrat/skills/row-count-check/index.ts` in your project
14
+ 2. Run it:
15
+
16
+ ```bash
17
+ parrat run row-count-check '{"model": "mart_orders", "min_rows": 1000}'
18
+ ```
19
+
20
+ ## The thin tool surface pattern
21
+
22
+ Each Skill declares `allowedDbtTools` — the exact set of tools Claude can call.
23
+ This Skill uses 1 tool. The built-in Skills use 3–4 of the 47 available dbt-mcp tools.
24
+
25
+ Fewer tools = more predictable reasoning + readable audit logs.
@@ -0,0 +1,87 @@
1
+ import { defineSkill, type SkillContext } from 'parrat/core';
2
+ import { z } from 'zod';
3
+
4
+ // --- Input schema ---
5
+
6
+ const inputSchema = z.object({
7
+ model: z.string().min(1).describe("dbt model name to check (e.g. 'mart_orders')"),
8
+ min_rows: z.number().int().positive().describe('Minimum expected row count'),
9
+ });
10
+
11
+ type Input = z.infer<typeof inputSchema>;
12
+
13
+ // --- Output schema ---
14
+
15
+ const outputSchema = z.object({
16
+ model: z.string(),
17
+ row_count: z.number().nullable().describe('Actual row count, or null if query failed'),
18
+ passed: z.boolean().describe('True if row_count >= min_rows'),
19
+ confidence: z.enum(['high', 'medium', 'low']),
20
+ summary: z.string(),
21
+ });
22
+
23
+ type Output = z.infer<typeof outputSchema>;
24
+
25
+ // --- Skill definition ---
26
+
27
+ // Thin tool surface: 1 tool. `show` runs arbitrary SQL against the warehouse
28
+ // via dbt's existing connection — no separate warehouse credentials needed.
29
+ const allowedDbtTools = ['show'];
30
+
31
+ export const rowCountCheckSkill = defineSkill({
32
+ name: 'row-count-check',
33
+ inputSchema,
34
+ outputSchema,
35
+ kind: 'investigation',
36
+ mcpServers: {
37
+ dbt: {
38
+ config: { command: '', args: [], env: {} }, // filled in at runtime from parrat.config.yaml
39
+ tools: allowedDbtTools,
40
+ },
41
+ },
42
+
43
+ async run(input: Input, ctx: SkillContext): Promise<Output> {
44
+ if (!ctx.config) throw new Error('row-count-check requires runtime config.');
45
+ if (!ctx.llmClient) throw new Error('row-count-check requires an LLM client.');
46
+
47
+ const dbtUserConfig = ctx.config.mcpServers.dbt;
48
+ if (!dbtUserConfig) {
49
+ throw new Error("row-count-check requires a 'dbt' MCP server in parrat.config.yaml.");
50
+ }
51
+
52
+ const { executeSkill } = await import('parrat/core');
53
+
54
+ const result = await executeSkill({
55
+ skillName: 'row-count-check',
56
+ llm: ctx.llmClient,
57
+ systemPrompt: `
58
+ You are a data quality agent. Your job is to check whether a dbt model has at least a minimum
59
+ number of rows.
60
+
61
+ Use the \`show\` tool to run: SELECT COUNT(*) AS row_count FROM {{ ref('MODEL_NAME') }}
62
+ Replace MODEL_NAME with the model name provided in the input.
63
+
64
+ Then call emit_findings with:
65
+ - model: the model name
66
+ - row_count: the integer result from the query (null if the query failed)
67
+ - passed: true if row_count >= min_rows
68
+ - confidence: "high" if the query succeeded, "low" if it failed
69
+ - summary: one sentence describing the result
70
+ `.trim(),
71
+ userMessage: JSON.stringify(input),
72
+ mcpServers: { dbt: { config: dbtUserConfig, tools: allowedDbtTools } },
73
+ outputSchema,
74
+ model: ctx.config.claude.model,
75
+ maxTurns: ctx.config.claude.max_turns,
76
+ maxTokens: ctx.config.claude.max_tokens,
77
+ temperature: ctx.config.claude.temperature,
78
+ auditLogger: ctx.auditLogger,
79
+ runId: ctx.runId,
80
+ workflowId: ctx.workflowId,
81
+ tenantId: ctx.tenantId,
82
+ actor: ctx.actor ?? 'user',
83
+ });
84
+
85
+ return result.output;
86
+ },
87
+ });
package/package.json ADDED
@@ -0,0 +1,69 @@
1
+ {
2
+ "name": "parrat",
3
+ "version": "0.1.0-beta.5",
4
+ "description": "AI-powered root cause analysis for data incidents",
5
+ "type": "module",
6
+ "license": "Apache-2.0",
7
+ "bin": {
8
+ "parrat": "./dist/index.js"
9
+ },
10
+ "engines": {
11
+ "node": ">=20"
12
+ },
13
+ "publishConfig": {
14
+ "access": "public"
15
+ },
16
+ "files": [
17
+ "dist",
18
+ "README.md",
19
+ "examples",
20
+ "assets"
21
+ ],
22
+ "repository": {
23
+ "type": "git",
24
+ "url": "https://github.com/RaguvindTharanitharan/parrat.git"
25
+ },
26
+ "homepage": "https://github.com/RaguvindTharanitharan/parrat",
27
+ "keywords": [
28
+ "data-engineering",
29
+ "dbt",
30
+ "ai",
31
+ "agents",
32
+ "mcp",
33
+ "root-cause-analysis",
34
+ "data-ops"
35
+ ],
36
+ "dependencies": {
37
+ "@anthropic-ai/sdk": "^0.92.0",
38
+ "@modelcontextprotocol/sdk": "^1.29.0",
39
+ "commander": "^12.0.0",
40
+ "dotenv": "^16.4.0",
41
+ "tsx": "^4.19.0",
42
+ "yaml": "^2.5.0",
43
+ "zod": "^3.23.0",
44
+ "zod-to-json-schema": "^3.23.0"
45
+ },
46
+ "devDependencies": {
47
+ "@biomejs/biome": "^1.9.0",
48
+ "@types/node": "^22.0.0",
49
+ "@vitest/coverage-v8": "^2.0.0",
50
+ "tsup": "^8.0.0",
51
+ "typescript": "^5.5.0",
52
+ "vitest": "^2.0.0"
53
+ },
54
+ "scripts": {
55
+ "lint": "biome check src tests",
56
+ "format": "biome format --write src tests",
57
+ "typecheck": "tsc --noEmit",
58
+ "test": "vitest run",
59
+ "test:watch": "vitest",
60
+ "test:coverage": "vitest run --coverage",
61
+ "test:unit": "vitest run tests/unit tests/core",
62
+ "test:component": "vitest run tests/component",
63
+ "test:skill": "vitest run tests/skill tests/skills",
64
+ "test:e2e": "vitest run tests/e2e",
65
+ "test:live": "vitest run tests/live",
66
+ "build": "tsup",
67
+ "dev": "tsup --watch"
68
+ }
69
+ }