@synkro-sh/cli 1.4.68 → 1.4.69
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bootstrap.js +234 -47
- package/dist/bootstrap.js.map +1 -1
- package/package.json +1 -1
package/dist/bootstrap.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../cli/installer/agentDetect.ts","../cli/installer/ccHookConfig.ts","../cli/installer/cursorHookConfig.ts","../cli/installer/mcpConfig.ts","../cli/installer/hookScripts.ts","../cli/installer/hookScriptsTs.ts","../cli/auth/stub.ts","../cli/auth/index.ts","../cli/api/projects.ts","../cli/installer/workflowTemplate.ts","../cli/installer/githubSetup.ts","../cli/commands/repoConnect.ts","../cli/commands/setupGithub.ts","../cli/installer/promptFetcher.ts","../cli/local-cc/settings.ts","../cli/local-cc/channelSource.ts","../cli/local-cc/install.ts","../cli/local-cc/pueue.ts","../cli/local-cc/prompts.ts","../cli/local-cc/turnLog.ts","../cli/local-cc/client.ts","../cli/commands/install.ts","../cli/commands/login.ts","../cli/commands/logout.ts","../cli/commands/status.ts","../cli/commands/link.ts","../cli/commands/unlink.ts","../cli/commands/config.ts","../cli/commands/scanPr.ts","../cli/commands/update.ts","../cli/commands/disconnect.ts","../cli/commands/uninstall.ts","../cli/commands/reinstall.ts","../cli/commands/localCc.ts","../cli/commands/grade.ts","../cli/bootstrap.js"],"sourcesContent":["/**\n * Detect which AI coding agents are installed on the user's machine.\n *\n * Returns a list of agents with their config paths so the installer\n * knows where to write hook configs.\n */\nimport { existsSync } from 'node:fs';\nimport { homedir } from 'node:os';\nimport { join } from 'node:path';\nimport { execSync } from 'node:child_process';\n\nexport type AgentKind = 'claude_code' | 'codex' | 'cursor';\n\nexport interface DetectedAgent {\n kind: AgentKind;\n name: string;\n binaryPath?: string;\n configDir: string;\n settingsPath: string;\n version?: string;\n}\n\nfunction which(cmd: string): string | undefined {\n try {\n const result = execSync(`which ${cmd}`, { encoding: 'utf-8' }).trim();\n return result || undefined;\n } catch {\n return undefined;\n }\n}\n\nfunction getVersion(cmd: string): string | undefined {\n try {\n const result = execSync(`${cmd} --version 2>&1`, { encoding: 'utf-8', timeout: 5000 }).trim();\n return result.split('\\n')[0];\n } catch {\n return undefined;\n }\n}\n\nexport function detectAgents(): DetectedAgent[] {\n const agents: DetectedAgent[] = [];\n const home = homedir();\n\n // Claude Code\n const claudeBinary = which('claude');\n const claudeConfigDir = join(home, '.claude');\n if (claudeBinary || existsSync(claudeConfigDir)) {\n agents.push({\n kind: 'claude_code',\n name: 'Claude Code',\n binaryPath: claudeBinary,\n configDir: claudeConfigDir,\n settingsPath: join(claudeConfigDir, 'settings.json'),\n version: claudeBinary ? getVersion('claude') : undefined,\n });\n }\n\n // Codex (OpenAI's CLI)\n const codexBinary = which('codex');\n const codexConfigDir = join(home, '.codex');\n if (codexBinary || existsSync(codexConfigDir)) {\n agents.push({\n kind: 'codex',\n name: 'Codex',\n binaryPath: codexBinary,\n configDir: codexConfigDir,\n settingsPath: join(codexConfigDir, 'config.toml'),\n version: codexBinary ? getVersion('codex') : undefined,\n });\n }\n\n // Cursor\n const cursorBinary = which('cursor');\n const cursorConfigDir = join(home, '.cursor');\n if (cursorBinary || existsSync(cursorConfigDir)) {\n agents.push({\n kind: 'cursor',\n name: 'Cursor',\n binaryPath: cursorBinary,\n configDir: cursorConfigDir,\n settingsPath: join(cursorConfigDir, 'hooks.json'),\n version: cursorBinary ? getVersion('cursor') : undefined,\n });\n }\n\n return agents;\n}\n\nexport function findClaudeAuth(): { path: string; exists: boolean } {\n // CC stores OAuth token in macOS keychain (item: \"Claude Code-credentials\")\n // or in ~/.claude/auth.json on Linux. We don't extract it; we ask user to\n // run `claude setup-token` for headless use.\n const authJsonPath = join(homedir(), '.claude', 'auth.json');\n return { path: authJsonPath, exists: existsSync(authJsonPath) };\n}\n","// :)\n/**\n * Atomically merge Synkro hook entries into ~/.claude/settings.json.\n *\n * Preserves any other hooks the user has configured (Corridor, Noma, custom\n * scripts, etc.) — we only add our entries, never replace the file.\n */\nimport { existsSync, readFileSync, writeFileSync, renameSync, mkdirSync, unlinkSync } from 'node:fs';\nimport { dirname } from 'node:path';\n\nexport interface SynkroHookConfig {\n bashJudgeScriptPath: string;\n bashFollowupScriptPath: string;\n editPrecheckScriptPath: string;\n cwePrecheckScriptPath: string;\n cvePrecheckScriptPath: string;\n planJudgeScriptPath: string;\n agentJudgeScriptPath: string;\n stopSummaryScriptPath: string;\n sessionStartScriptPath: string;\n transcriptSyncScriptPath: string;\n userPromptSubmitScriptPath: string;\n skipTranscriptSync?: boolean;\n}\n\nconst SYNKRO_MARKER = '__synkro_managed__';\n\ninterface HookEntry {\n matcher?: string;\n hooks: Array<Record<string, unknown>>;\n [SYNKRO_MARKER]?: boolean;\n}\n\ninterface CCSettings {\n hooks?: {\n PreToolUse?: HookEntry[];\n PostToolUse?: HookEntry[];\n SessionEnd?: HookEntry[];\n SessionStart?: HookEntry[];\n Stop?: HookEntry[];\n [k: string]: unknown;\n };\n [k: string]: unknown;\n}\n\nfunction readSettings(path: string): CCSettings {\n if (!existsSync(path)) return {};\n try {\n const raw = readFileSync(path, 'utf-8');\n return JSON.parse(raw) as CCSettings;\n } catch (err) {\n throw new Error(`Failed to parse ${path}: ${(err as Error).message}`);\n }\n}\n\nfunction writeSettingsAtomic(path: string, settings: CCSettings): void {\n mkdirSync(dirname(path), { recursive: true });\n const tmpPath = `${path}.synkro.tmp`;\n writeFileSync(tmpPath, JSON.stringify(settings, null, 2) + '\\n', 'utf-8');\n renameSync(tmpPath, path);\n}\n\nfunction isSynkroEntry(entry: any): boolean {\n if (entry?.[SYNKRO_MARKER]) return true;\n const hooks = Array.isArray(entry?.hooks) ? entry.hooks : [];\n return hooks.some((h: any) =>\n typeof h?.command === 'string' && h.command.includes('/.synkro/hooks/'),\n );\n}\n\nfunction removeSynkroEntries(events: CCSettings['hooks'] extends infer H ? H : never, eventName: string): void {\n if (!events) return;\n const arr = (events as any)[eventName];\n if (!Array.isArray(arr)) return;\n (events as any)[eventName] = arr.filter((entry: any) => !isSynkroEntry(entry));\n}\n\n/**\n * Merge Synkro hooks into the settings file. Idempotent — replaces any\n * existing Synkro-managed entries with the new versions.\n */\nexport function installCCHooks(settingsPath: string, config: SynkroHookConfig): void {\n const settings = readSettings(settingsPath);\n settings.hooks = settings.hooks ?? {};\n\n // Remove any prior Synkro entries (to support `synkro update`)\n removeSynkroEntries(settings.hooks as any, 'PreToolUse');\n removeSynkroEntries(settings.hooks as any, 'PostToolUse');\n removeSynkroEntries(settings.hooks as any, 'SessionEnd');\n removeSynkroEntries(settings.hooks as any, 'SessionStart');\n removeSynkroEntries(settings.hooks as any, 'UserPromptSubmit');\n // Also clean up any older `Stop`-event entry from earlier v1.6 builds.\n removeSynkroEntries(settings.hooks as any, 'Stop');\n\n settings.hooks.PreToolUse = settings.hooks.PreToolUse ?? [];\n settings.hooks.PostToolUse = settings.hooks.PostToolUse ?? [];\n settings.hooks.SessionEnd = settings.hooks.SessionEnd ?? [];\n settings.hooks.SessionStart = settings.hooks.SessionStart ?? [];\n settings.hooks.UserPromptSubmit = (settings.hooks.UserPromptSubmit as any[]) ?? [];\n\n // PreToolUse Bash/Read/Grep/Glob → command hook script (Cerebras-judged)\n settings.hooks.PreToolUse.push({\n matcher: 'Bash|Read|Grep|Glob',\n hooks: [\n {\n type: 'command',\n command: config.bashJudgeScriptPath,\n timeout: 30,\n },\n ],\n [SYNKRO_MARKER]: true,\n } as any);\n\n // PreToolUse Edit/Write → three hooks in ONE entry so CC waits for all\n // before processing deny decisions. Each hook runs in parallel:\n // 1. edit-precheck: org rules grading on channel 1 (port 8929)\n // 2. cwe-precheck: CWE Top 25 grading on channel 2 (port 8930)\n // 3. cve-precheck: CVE/OSV dependency scan (curl, no LLM)\n settings.hooks.PreToolUse.push({\n matcher: 'Edit|Write|MultiEdit|NotebookEdit',\n hooks: [\n {\n type: 'command',\n command: config.editPrecheckScriptPath,\n timeout: 30,\n },\n {\n type: 'command',\n command: config.cwePrecheckScriptPath,\n timeout: 30,\n },\n {\n type: 'command',\n command: config.cvePrecheckScriptPath,\n timeout: 10,\n },\n ],\n [SYNKRO_MARKER]: true,\n } as any);\n\n // PreToolUse Agent → scan subagent prompts against org rules.\n settings.hooks.PreToolUse.push({\n matcher: 'Agent',\n hooks: [\n {\n type: 'command',\n command: config.agentJudgeScriptPath,\n timeout: 30,\n },\n ],\n [SYNKRO_MARKER]: true,\n } as any);\n\n // PreToolUse ExitPlanMode → advisory plan review against org rules.\n settings.hooks.PreToolUse.push({\n matcher: 'ExitPlanMode',\n hooks: [\n {\n type: 'command',\n command: config.planJudgeScriptPath,\n timeout: 45,\n },\n ],\n [SYNKRO_MARKER]: true,\n } as any);\n\n // PostToolUse Edit/Write removed — PreToolUse hooks handle grading now.\n\n // PostToolUse Bash → flips pending precheck_corrections row to 'allow'\n // once the bash command actually executed. Required for the bash trendline\n // (approved-vs-rejected) the dashboard reads from precheck_corrections.\n settings.hooks.PostToolUse.push({\n matcher: 'Bash',\n hooks: [\n {\n type: 'command',\n command: config.bashFollowupScriptPath,\n },\n ],\n [SYNKRO_MARKER]: true,\n } as any);\n\n // SessionEnd → end-of-session summary line (`[synkro] stop → N findings: ...`).\n // We use SessionEnd, not Stop, because Stop fires after every agent turn\n // (which would spam the user in interactive mode); SessionEnd fires once\n // when the session itself terminates.\n settings.hooks.SessionEnd.push({\n hooks: [\n {\n type: 'command',\n command: config.stopSummaryScriptPath,\n },\n ],\n [SYNKRO_MARKER]: true,\n } as any);\n\n // SessionStart → \"[synkro] session start → N open findings in this repo\" if any.\n settings.hooks.SessionStart.push({\n hooks: [\n {\n type: 'command',\n command: config.sessionStartScriptPath,\n },\n ],\n [SYNKRO_MARKER]: true,\n } as any);\n\n // UserPromptSubmit → captures explicit consent keywords from user messages.\n // Writes a file-based grant so the next blocked tool call is allowed through.\n (settings.hooks.UserPromptSubmit as any[]).push({\n hooks: [\n {\n type: 'command',\n command: config.userPromptSubmitScriptPath,\n timeout: 5,\n },\n ],\n [SYNKRO_MARKER]: true,\n } as any);\n\n // Stop → usage telemetry + optional transcript sync (fires after every agent turn).\n // Always installed: usage tracking is ungated; transcript sync is gated inside the script.\n settings.hooks.Stop = settings.hooks.Stop ?? [];\n removeSynkroEntries(settings.hooks as any, 'Stop');\n settings.hooks.Stop.push({\n hooks: [\n {\n type: 'command',\n command: config.transcriptSyncScriptPath,\n timeout: 3,\n },\n ],\n [SYNKRO_MARKER]: true,\n } as any);\n\n writeSettingsAtomic(settingsPath, settings);\n}\n\n/**\n * Remove all Synkro-managed hook entries from settings.json.\n * Used by `synkro disconnect`.\n */\nexport function uninstallCCHooks(settingsPath: string): boolean {\n if (!existsSync(settingsPath)) return false;\n const settings = readSettings(settingsPath);\n if (!settings.hooks) return false;\n\n const events = ['PreToolUse', 'PostToolUse', 'SessionEnd', 'SessionStart', 'Stop', 'UserPromptSubmit'] as const;\n for (const evt of events) {\n removeSynkroEntries(settings.hooks as any, evt);\n }\n\n // If a hook event array is now empty, delete it\n for (const evt of events) {\n if (Array.isArray((settings.hooks as any)[evt]) && (settings.hooks as any)[evt].length === 0) {\n delete (settings.hooks as any)[evt];\n }\n }\n // If hooks object is now empty, delete it\n if (Object.keys(settings.hooks).length === 0) {\n delete settings.hooks;\n }\n\n writeSettingsAtomic(settingsPath, settings);\n return true;\n}\n\n/**\n * Check whether Synkro hooks are currently installed in settings.json.\n * Used by `synkro status`.\n */\nexport function inspectCCHooks(settingsPath: string): {\n installed: boolean;\n preToolUseBash: boolean;\n postToolUseEdit: boolean;\n sessionEnd: boolean;\n sessionStart: boolean;\n} {\n if (!existsSync(settingsPath)) {\n return { installed: false, preToolUseBash: false, postToolUseEdit: false, sessionEnd: false, sessionStart: false };\n }\n const settings = readSettings(settingsPath);\n const pre = (settings.hooks as any)?.PreToolUse ?? [];\n const post = (settings.hooks as any)?.PostToolUse ?? [];\n const sessionEndHooks = (settings.hooks as any)?.SessionEnd ?? [];\n const sessionStartHooks = (settings.hooks as any)?.SessionStart ?? [];\n const preToolUseBash = pre.some((e: any) => e?.[SYNKRO_MARKER] === true);\n const postToolUseEdit = post.some((e: any) => e?.[SYNKRO_MARKER] === true);\n const sessionEnd = sessionEndHooks.some((e: any) => e?.[SYNKRO_MARKER] === true);\n const sessionStart = sessionStartHooks.some((e: any) => e?.[SYNKRO_MARKER] === true);\n return {\n installed: preToolUseBash || postToolUseEdit || sessionEnd || sessionStart,\n preToolUseBash,\n postToolUseEdit,\n sessionEnd,\n sessionStart,\n };\n}\n","// :)\n/**\n * Atomically merge Synkro hook entries into ~/.cursor/hooks.json.\n *\n * Cursor hooks reuse the same TypeScript scripts as Claude Code (cc-*.ts),\n * run with SYNKRO_HOOK_FORMAT=cursor so _synkro-common translates CC JSON output\n * to Cursor's permission / additional_context format. CC install is unchanged.\n */\nimport { readFileSync, writeFileSync, renameSync, mkdirSync } from 'node:fs';\nimport { dirname, resolve, normalize } from 'node:path';\nimport { homedir } from 'node:os';\n\nexport interface CursorHookConfig {\n /** Cursor-specific adapter (beforeShellExecution + dual payload) */\n bashJudgeScriptPath: string;\n /** Cursor-specific post-edit capture (afterFileEdit event shape) */\n editCaptureScriptPath: string;\n /** Shared cc-*.ts scripts (invoked with SYNKRO_HOOK_FORMAT=cursor) */\n bashFollowupScriptPath: string;\n editPrecheckScriptPath: string;\n cwePrecheckScriptPath: string;\n cvePrecheckScriptPath: string;\n planJudgeScriptPath: string;\n agentJudgeScriptPath: string;\n stopSummaryScriptPath: string;\n sessionStartScriptPath: string;\n userPromptSubmitScriptPath: string;\n transcriptSyncScriptPath: string;\n}\n\nconst SYNKRO_MARKER = '__synkro_managed__';\n\nfunction shellQuote(s: string): string {\n return \"'\" + s.replace(/'/g, \"'\\\\''\") + \"'\";\n}\n\n/** Run a shared cc-*.ts hook with Cursor output translation. */\nfunction cursorCcCmd(scriptPath: string): string {\n return 'env SYNKRO_HOOK_FORMAT=cursor bun run ' + shellQuote(scriptPath);\n}\n\nfunction bunRunCmd(scriptPath: string): string {\n return 'bun run ' + shellQuote(scriptPath);\n}\n\nconst ALLOWED_PARENT_DIRS = [\n resolve(homedir(), '.cursor'),\n resolve(homedir(), '.config', 'cursor'),\n];\n\nfunction validateHooksPath(path: string): string {\n const resolved = resolve(normalize(path));\n if (!ALLOWED_PARENT_DIRS.some(dir => resolved.startsWith(dir + '/') || resolved === dir)) {\n throw new Error(`Hooks path must be under ~/.cursor or ~/.config/cursor, got: ${resolved}`);\n }\n return resolved;\n}\n\ninterface CursorHookEntry {\n command: string;\n timeout?: number;\n failClosed?: boolean;\n matcher?: string;\n [SYNKRO_MARKER]?: boolean;\n}\n\ninterface CursorHooksFile {\n version?: number;\n hooks?: {\n [event: string]: CursorHookEntry[];\n };\n}\n\nfunction readHooksFile(rawPath: string): CursorHooksFile {\n const safePath = validateHooksPath(rawPath);\n try {\n const raw = readFileSync(safePath, 'utf-8');\n return JSON.parse(raw) as CursorHooksFile;\n } catch (err: any) {\n if (err?.code === 'ENOENT') return { version: 1, hooks: {} };\n throw new Error(`Failed to parse ${safePath}: ${(err as Error).message}`);\n }\n}\n\nfunction writeHooksFileAtomic(rawPath: string, data: CursorHooksFile): void {\n const safePath = validateHooksPath(rawPath);\n mkdirSync(dirname(safePath), { recursive: true });\n const tmpPath = `${safePath}.synkro.tmp`;\n writeFileSync(tmpPath, JSON.stringify(data, null, 2) + '\\n', { encoding: 'utf-8', mode: 0o600 });\n renameSync(tmpPath, safePath);\n}\n\nfunction isSynkroEntry(entry: any): boolean {\n if (entry?.[SYNKRO_MARKER]) return true;\n return typeof entry?.command === 'string' && entry.command.includes('/.synkro/hooks/');\n}\n\nconst ALL_EVENTS = [\n 'sessionStart', 'sessionEnd', 'beforeSubmitPrompt', 'stop',\n 'beforeShellExecution', 'afterShellExecution',\n 'preToolUse', 'afterFileEdit', 'postToolUse',\n];\n\nfunction removeSynkroEntries(hooks: CursorHooksFile['hooks'], event: string): void {\n if (!hooks) return;\n const arr = hooks[event];\n if (!Array.isArray(arr)) return;\n hooks[event] = arr.filter((entry: any) => !isSynkroEntry(entry));\n}\n\nfunction pushCcHook(\n hooks: CursorHooksFile['hooks'],\n event: string,\n scriptPath: string,\n opts: { timeout: number; matcher?: string; failClosed?: boolean },\n): void {\n hooks![event] = hooks![event] ?? [];\n hooks![event]!.push({\n command: cursorCcCmd(scriptPath),\n timeout: opts.timeout,\n failClosed: opts.failClosed ?? false,\n ...(opts.matcher ? { matcher: opts.matcher } : {}),\n [SYNKRO_MARKER]: true,\n });\n}\n\nexport function installCursorHooks(hooksJsonPath: string, config: CursorHookConfig): void {\n const file = readHooksFile(hooksJsonPath);\n file.version = file.version ?? 1;\n file.hooks = file.hooks ?? {};\n\n for (const evt of ALL_EVENTS) {\n removeSynkroEntries(file.hooks, evt);\n }\n\n const h = file.hooks;\n\n pushCcHook(h, 'sessionStart', config.sessionStartScriptPath, { timeout: 5 });\n pushCcHook(h, 'sessionEnd', config.stopSummaryScriptPath, { timeout: 10 });\n pushCcHook(h, 'beforeSubmitPrompt', config.userPromptSubmitScriptPath, { timeout: 5 });\n pushCcHook(h, 'stop', config.transcriptSyncScriptPath, { timeout: 3 });\n\n h.beforeShellExecution = h.beforeShellExecution ?? [];\n h.beforeShellExecution.push({\n command: bunRunCmd(config.bashJudgeScriptPath),\n timeout: 15,\n failClosed: false,\n [SYNKRO_MARKER]: true,\n });\n\n pushCcHook(h, 'afterShellExecution', config.bashFollowupScriptPath, { timeout: 10 });\n\n // cursor-bash-judge.ts handles both beforeShellExecution payloads and preToolUse tool_input\n h.preToolUse = h.preToolUse ?? [];\n h.preToolUse.push({\n command: bunRunCmd(config.bashJudgeScriptPath),\n timeout: 15,\n failClosed: false,\n matcher: 'Shell|Bash|Read|Grep|Glob',\n [SYNKRO_MARKER]: true,\n });\n\n pushCcHook(h, 'preToolUse', config.editPrecheckScriptPath, {\n timeout: 15,\n matcher: 'Write|Edit|StrReplace|MultiEdit|NotebookEdit',\n });\n pushCcHook(h, 'preToolUse', config.cwePrecheckScriptPath, {\n timeout: 15,\n matcher: 'Write|Edit|StrReplace|MultiEdit|NotebookEdit',\n });\n pushCcHook(h, 'preToolUse', config.cvePrecheckScriptPath, {\n timeout: 10,\n matcher: 'Write|Edit|StrReplace|MultiEdit|NotebookEdit',\n });\n pushCcHook(h, 'preToolUse', config.agentJudgeScriptPath, {\n timeout: 15,\n matcher: 'Agent|Task',\n });\n pushCcHook(h, 'preToolUse', config.planJudgeScriptPath, {\n timeout: 20,\n matcher: 'ExitPlanMode|SwitchMode|CreatePlan',\n });\n\n h.afterFileEdit = h.afterFileEdit ?? [];\n h.afterFileEdit.push({\n command: bunRunCmd(config.editCaptureScriptPath),\n timeout: 15,\n failClosed: false,\n [SYNKRO_MARKER]: true,\n });\n\n pushCcHook(h, 'postToolUse', config.bashFollowupScriptPath, {\n timeout: 10,\n matcher: 'Shell|Bash',\n });\n\n writeHooksFileAtomic(hooksJsonPath, file);\n}\n\nexport function uninstallCursorHooks(hooksJsonPath: string): boolean {\n let file: CursorHooksFile;\n try {\n file = readHooksFile(hooksJsonPath);\n } catch {\n return false;\n }\n if (!file.hooks) return false;\n\n for (const evt of ALL_EVENTS) {\n removeSynkroEntries(file.hooks, evt);\n }\n\n for (const evt of ALL_EVENTS) {\n if (Array.isArray(file.hooks[evt]) && file.hooks[evt].length === 0) {\n delete file.hooks[evt];\n }\n }\n if (Object.keys(file.hooks).length === 0) {\n delete file.hooks;\n }\n\n writeHooksFileAtomic(hooksJsonPath, file);\n return true;\n}\n\nfunction preToolUseUsesScript(hooks: CursorHookEntry[] | undefined, scriptBasename: string): boolean {\n return (hooks ?? []).some((e) =>\n isSynkroEntry(e) && typeof e.command === 'string' && e.command.includes(scriptBasename),\n );\n}\n\nexport function inspectCursorHooks(hooksJsonPath: string): {\n installed: boolean;\n sessionStart: boolean;\n sessionEnd: boolean;\n beforeSubmitPrompt: boolean;\n stop: boolean;\n beforeShellExecution: boolean;\n afterShellExecution: boolean;\n preToolUse: boolean;\n preToolUseBash: boolean;\n preToolUseEdit: boolean;\n preToolUseCwe: boolean;\n preToolUseCve: boolean;\n preToolUseAgent: boolean;\n preToolUsePlan: boolean;\n afterFileEdit: boolean;\n postToolUse: boolean;\n} {\n let file: CursorHooksFile;\n try {\n file = readHooksFile(hooksJsonPath);\n } catch {\n return {\n installed: false,\n sessionStart: false, sessionEnd: false, beforeSubmitPrompt: false, stop: false,\n beforeShellExecution: false, afterShellExecution: false,\n preToolUse: false, preToolUseBash: false, preToolUseEdit: false,\n preToolUseCwe: false, preToolUseCve: false, preToolUseAgent: false, preToolUsePlan: false,\n afterFileEdit: false, postToolUse: false,\n };\n }\n const h = file.hooks ?? {};\n const sessionStart = (h.sessionStart ?? []).some((e) => isSynkroEntry(e));\n const sessionEnd = (h.sessionEnd ?? []).some((e) => isSynkroEntry(e));\n const beforeSubmitPrompt = (h.beforeSubmitPrompt ?? []).some((e) => isSynkroEntry(e));\n const stop = (h.stop ?? []).some((e) => isSynkroEntry(e));\n const beforeShellExecution = (h.beforeShellExecution ?? []).some((e) => isSynkroEntry(e));\n const afterShellExecution = (h.afterShellExecution ?? []).some((e) => isSynkroEntry(e));\n const pre = h.preToolUse ?? [];\n const preToolUseBash = preToolUseUsesScript(pre, 'cc-bash-judge') || preToolUseUsesScript(pre, 'cursor-bash-judge');\n const preToolUseEdit = preToolUseUsesScript(pre, 'cc-edit-precheck') || preToolUseUsesScript(pre, 'cursor-edit-precheck');\n const preToolUseCwe = preToolUseUsesScript(pre, 'cc-cwe-precheck');\n const preToolUseCve = preToolUseUsesScript(pre, 'cc-cve-precheck');\n const preToolUseAgent = preToolUseUsesScript(pre, 'cc-agent-judge');\n const preToolUsePlan = preToolUseUsesScript(pre, 'cc-plan-judge');\n const preToolUse = preToolUseBash || preToolUseEdit || preToolUseCwe || preToolUseCve || preToolUseAgent || preToolUsePlan;\n const afterFileEdit = (h.afterFileEdit ?? []).some((e) => isSynkroEntry(e));\n const postToolUse = (h.postToolUse ?? []).some((e) => isSynkroEntry(e));\n return {\n installed: sessionStart || sessionEnd || beforeSubmitPrompt || stop\n || beforeShellExecution || afterShellExecution || preToolUse || afterFileEdit || postToolUse,\n sessionStart, sessionEnd, beforeSubmitPrompt, stop,\n beforeShellExecution, afterShellExecution,\n preToolUse, preToolUseBash, preToolUseEdit, preToolUseCwe, preToolUseCve, preToolUseAgent, preToolUsePlan,\n afterFileEdit, postToolUse,\n };\n}\n","/**\n * Atomically merge the Synkro guardrails MCP server entry into ~/.claude.json.\n *\n * CC's MCP config lives in ~/.claude.json (NOT ~/.claude/settings.json — those\n * are different files). It accepts an HTTP-transport server with a static\n * Authorization header set at config-write time. We register one entry,\n * `synkro-guardrails`, marked with `__synkro_managed__: true` so we can safely\n * remove it on `synkro disconnect` without touching the user's other servers.\n *\n * Mirrors the pattern in ccHookConfig.ts — read-modify-write atomically via\n * tmpfile + rename; preserve any other top-level keys; never replace the file.\n */\nimport { existsSync, readFileSync, writeFileSync, renameSync, mkdirSync } from 'node:fs';\nimport { homedir } from 'node:os';\nimport { dirname, join } from 'node:path';\n\nconst SYNKRO_MARKER = '__synkro_managed__';\nconst SYNKRO_SERVER_NAME = 'synkro-guardrails';\nconst CC_CONFIG_PATH = join(homedir(), '.claude.json');\n\ninterface ClaudeJson {\n mcpServers?: Record<string, McpServerEntry>;\n [k: string]: unknown;\n}\n\ninterface McpServerEntry {\n type?: 'http' | 'stdio' | 'sse';\n url?: string;\n command?: string;\n args?: string[];\n headers?: Record<string, string>;\n env?: Record<string, string>;\n [SYNKRO_MARKER]?: boolean;\n [k: string]: unknown;\n}\n\nfunction readClaudeJson(): ClaudeJson {\n if (!existsSync(CC_CONFIG_PATH)) return {};\n try {\n const raw = readFileSync(CC_CONFIG_PATH, 'utf-8');\n return JSON.parse(raw) as ClaudeJson;\n } catch (err) {\n throw new Error(`Failed to parse ${CC_CONFIG_PATH}: ${(err as Error).message}`);\n }\n}\n\nfunction writeClaudeJsonAtomic(config: ClaudeJson): void {\n mkdirSync(dirname(CC_CONFIG_PATH), { recursive: true });\n const tmpPath = `${CC_CONFIG_PATH}.synkro.tmp`;\n writeFileSync(tmpPath, JSON.stringify(config, null, 2) + '\\n', 'utf-8');\n renameSync(tmpPath, CC_CONFIG_PATH);\n}\n\nexport interface InstallMcpOptions {\n gatewayUrl: string; // e.g. http://localhost:8788\n // Long-lived (1y) Synkro-signed JWT scoped to mcp:guardrails. Minted by\n // POST /api/v1/cli/mcp-token during install. We deliberately do NOT write\n // the WorkOS access token here anymore — that one expires in 5 min and\n // silently breaks the CC MCP connection.\n bearerToken: string;\n local?: boolean;\n}\n\n/**\n * Register the Synkro guardrails MCP server in ~/.claude.json.\n * Idempotent — replaces any prior Synkro-managed entry with the new one.\n */\nexport function installMcpConfig(opts: InstallMcpOptions): { path: string; url: string } {\n const config = readClaudeJson();\n config.mcpServers = config.mcpServers ?? {};\n\n // Remove any prior Synkro-managed entry (so re-running install picks up\n // a refreshed JWT). Leave non-Synkro entries alone.\n for (const [name, entry] of Object.entries(config.mcpServers)) {\n if (entry?.[SYNKRO_MARKER] === true) delete config.mcpServers[name];\n }\n\n if (opts.local) {\n const url = 'http://127.0.0.1:8931/';\n const tokenPath = join(homedir(), '.synkro', '.mcp-local-token');\n let localToken = '';\n try { localToken = readFileSync(tokenPath, 'utf-8').trim(); } catch {}\n config.mcpServers[SYNKRO_SERVER_NAME] = {\n type: 'http',\n url,\n ...(localToken ? { headers: { Authorization: `Bearer ${localToken}` } } : {}),\n [SYNKRO_MARKER]: true,\n };\n writeClaudeJsonAtomic(config);\n return { path: CC_CONFIG_PATH, url };\n }\n\n const url = `${opts.gatewayUrl.replace(/\\/$/, '')}/api/v1/mcp/guardrails`;\n config.mcpServers[SYNKRO_SERVER_NAME] = {\n type: 'http',\n url,\n headers: { Authorization: `Bearer ${opts.bearerToken}` },\n [SYNKRO_MARKER]: true,\n };\n\n writeClaudeJsonAtomic(config);\n return { path: CC_CONFIG_PATH, url };\n}\n\n/**\n * Remove all Synkro-managed MCP server entries from ~/.claude.json.\n * Returns true if anything was removed.\n */\nexport function uninstallMcpConfig(): boolean {\n if (!existsSync(CC_CONFIG_PATH)) return false;\n const config = readClaudeJson();\n if (!config.mcpServers || Object.keys(config.mcpServers).length === 0) return false;\n\n let removed = false;\n for (const [name, entry] of Object.entries(config.mcpServers)) {\n if (entry?.[SYNKRO_MARKER] === true) {\n delete config.mcpServers[name];\n removed = true;\n }\n }\n if (!removed) return false;\n\n // If the mcpServers object is now empty, drop it to keep the file tidy.\n if (Object.keys(config.mcpServers).length === 0) delete config.mcpServers;\n\n writeClaudeJsonAtomic(config);\n return true;\n}\n\n/**\n * Inspect whether the Synkro MCP server entry is currently registered.\n */\nexport function inspectMcpConfig(): {\n installed: boolean;\n configPath: string;\n url?: string;\n} {\n if (!existsSync(CC_CONFIG_PATH)) {\n return { installed: false, configPath: CC_CONFIG_PATH };\n }\n const config = readClaudeJson();\n const entry = config.mcpServers?.[SYNKRO_SERVER_NAME];\n if (!entry || entry[SYNKRO_MARKER] !== true) {\n return { installed: false, configPath: CC_CONFIG_PATH };\n }\n return { installed: true, configPath: CC_CONFIG_PATH, url: entry.url };\n}\n","// :)\n/**\n * Bash hook scripts for Cursor IDE adapter and shared common utilities.\n *\n * CC hooks have been moved to hookScriptsTs.ts (TypeScript + Bun runtime).\n * This file retains the bash common script (sourced by Cursor hooks) and\n * the Cursor-specific adapter scripts.\n */\n\nexport const SYNKRO_COMMON_SCRIPT = `#!/bin/bash\n# Shared Synkro hook utilities — sourced by all hook scripts.\n\nsynkro_log() { echo \"[synkro] $1\" >&2; }\n\n# Load config\n_SYNKRO_CONFIG=\"$HOME/.synkro/config.env\"\nif [ -f \"$_SYNKRO_CONFIG\" ]; then\n set -a; . \"$_SYNKRO_CONFIG\"; set +a\nfi\n\nGATEWAY_URL=\"\\${SYNKRO_GATEWAY_URL:-https://api.synkro.sh}\"\nCREDS_PATH=\"\\${SYNKRO_CREDENTIALS_PATH:-$HOME/.synkro/credentials.json}\"\n\nsynkro_load_jwt() {\n if [ ! -f \"$CREDS_PATH\" ]; then echo \"\"; return 1; fi\n jq -r '.access_token // empty' \"$CREDS_PATH\" 2>/dev/null\n}\n\nsynkro_refresh_jwt() {\n # Lock via mkdir (atomic on all Unix including macOS — no flock needed)\n local lockdir=\"\\${CREDS_PATH}.lockdir\"\n if ! mkdir \"$lockdir\" 2>/dev/null; then\n # Another hook is refreshing — wait and re-read\n local _w=0\n while [ -d \"$lockdir\" ] && [ $_w -lt 5 ]; do sleep 0.5; _w=$((_w+1)); done\n JWT=$(jq -r '.access_token // empty' \"$CREDS_PATH\" 2>/dev/null)\n return 0\n fi\n trap \"rmdir \\\\\"$lockdir\\\\\" 2>/dev/null\" RETURN\n\n # Re-check expiry — another hook may have just refreshed\n local p2 exp2 now2\n p2=$(printf '%s' \"$JWT\" | cut -d. -f2)\n case $((\\${#p2} % 4)) in 2) p2=\"\\${p2}==\";; 3) p2=\"\\${p2}=\";; esac\n exp2=$(printf '%s' \"$p2\" | tr '_-' '/+' | base64 -D 2>/dev/null | jq -r '.exp // 0' 2>/dev/null)\n now2=$(date -u +%s)\n if [ $((exp2 - now2)) -ge 60 ]; then return 0; fi\n\n local rt\n rt=$(jq -r '.refresh_token // empty' \"$CREDS_PATH\" 2>/dev/null)\n if [ -z \"$rt\" ]; then return 1; fi\n local resp\n resp=$(curl -sS -X POST \"\\${GATEWAY_URL}/api/auth/refresh\" \\\\\n -H \"Content-Type: application/json\" \\\\\n -d \"$(jq -n --arg rt \"$rt\" '{refresh_token:$rt}')\" \\\\\n --max-time 4 2>/dev/null)\n local new_at\n new_at=$(echo \"$resp\" | jq -r '.access_token // empty' 2>/dev/null)\n if [ -z \"$new_at\" ]; then return 1; fi\n local new_rt\n new_rt=$(echo \"$resp\" | jq -r '.refresh_token // empty' 2>/dev/null)\n [ -z \"$new_rt\" ] && new_rt=\"$rt\"\n local tmp=\"\\${CREDS_PATH}.synkro.tmp\"\n local existing\n existing=$(cat \"$CREDS_PATH\" 2>/dev/null)\n if [ -z \"$existing\" ] || ! echo \"$existing\" | jq -e '.' >/dev/null 2>&1; then\n existing='{}'\n fi\n echo \"$existing\" | jq --arg at \"$new_at\" --arg rt \"$new_rt\" '. + {access_token:$at,refresh_token:$rt}' > \"$tmp\" 2>/dev/null && mv \"$tmp\" \"$CREDS_PATH\"\n JWT=\"$new_at\"\n}\n\nsynkro_ensure_fresh_jwt() {\n [ -z \"$JWT\" ] && return 1\n local p exp now\n p=$(printf '%s' \"$JWT\" | cut -d. -f2)\n case $((\\${#p} % 4)) in 2) p=\"\\${p}==\";; 3) p=\"\\${p}=\";; esac\n exp=$(printf '%s' \"$p\" | tr '_-' '/+' | base64 -D 2>/dev/null | jq -r '.exp // 0' 2>/dev/null)\n now=$(date -u +%s)\n [ $((exp - now)) -lt 60 ] && synkro_refresh_jwt\n}\n\nsynkro_detect_repo() {\n local cwd=\"\\${1:-.}\"\n if command -v git >/dev/null 2>&1; then\n local r\n r=$(git -C \"$cwd\" remote get-url origin 2>/dev/null || true)\n [ -n \"$r\" ] && echo \"$r\" | sed -E 's|^git@[^:]+:||; s|^https?://[^/]+/||; s|\\\\.git$||' && return\n fi\n echo \"\"\n}\n\nsynkro_channel_up() {\n (exec 3<>/dev/tcp/127.0.0.1/\\${SYNKRO_CHANNEL_PORT:-8929}) 2>/dev/null && exec 3<&- 3>&-\n}\n\n# Fetch hook config. Sets SYNKRO_CAPTURE_DEPTH, SYNKRO_TIER, SYNKRO_RULES, SYNKRO_SILENT, SYNKRO_POLICY_NAME.\n_SYNKRO_RULES_FILE=\"$HOME/.synkro/rules.json\"\n_SYNKRO_TELEMETRY_FILE=\"$HOME/.synkro/telemetry.jsonl\"\n\nsynkro_load_config() {\n # Local-first: read from ~/.synkro/rules.json if it exists (zero latency, no network)\n if [ -f \"$_SYNKRO_RULES_FILE\" ]; then\n local rdata\n rdata=$(cat \"$_SYNKRO_RULES_FILE\" 2>/dev/null)\n if [ -n \"$rdata\" ]; then\n SYNKRO_CAPTURE_DEPTH=\"local_only\"\n SYNKRO_TIER=\"standard\"\n SYNKRO_SILENT=$(echo \"$rdata\" | jq -r '.config.silent // false' 2>/dev/null)\n local active_id\n active_id=$(echo \"$rdata\" | jq -r '.config.activePolicyId // empty' 2>/dev/null)\n if [ -n \"$active_id\" ]; then\n SYNKRO_POLICY_NAME=$(echo \"$rdata\" | jq -r --arg id \"$active_id\" '.policies[]? | select(.id == $id) | .name // empty' 2>/dev/null)\n SYNKRO_RULES=$(echo \"$rdata\" | jq -c --arg id \"$active_id\" '[.policies[]? | select(.id == $id) | .rules[]? | select(.hook_stage == \"pre\" or .hook_stage == \"both\" or .hook_stage == null) | {rule_id,text,severity,category,mode}]' 2>/dev/null || echo \"[]\")\n else\n SYNKRO_POLICY_NAME=$(echo \"$rdata\" | jq -r '.policies[0]?.name // empty' 2>/dev/null)\n SYNKRO_RULES=$(echo \"$rdata\" | jq -c '[.policies[0]?.rules[]? | select(.hook_stage == \"pre\" or .hook_stage == \"both\" or .hook_stage == null) | {rule_id,text,severity,category,mode}]' 2>/dev/null || echo \"[]\")\n fi\n return\n fi\n fi\n\n # Fallback: fetch from cloud API\n local resp\n resp=$(curl -sS \"\\${GATEWAY_URL}/api/v1/hook/config\\${1:+?$1}\" -H \"Authorization: Bearer $JWT\" --max-time 4 2>/dev/null || echo \"\")\n if [ -z \"$resp\" ]; then return; fi\n SYNKRO_CAPTURE_DEPTH=$(echo \"$resp\" | jq -r '.capture_depth // \"local_only\"' 2>/dev/null)\n SYNKRO_TIER=$(echo \"$resp\" | jq -r '.tier // \"standard\"' 2>/dev/null)\n SYNKRO_SILENT=$(echo \"$resp\" | jq -r '.silent_mode // false' 2>/dev/null)\n SYNKRO_POLICY_NAME=$(echo \"$resp\" | jq -r '.active_policy_name // empty' 2>/dev/null)\n SYNKRO_RULES=$(echo \"$resp\" | jq -c '[.rules[]? | select(.hook_stage == \"pre\" or .hook_stage == \"both\" or .hook_stage == null) | {rule_id,text,severity,category,mode}]' 2>/dev/null || echo \"[]\")\n}\n\nsynkro_local_capture() {\n local event_json=\"$1\"\n local ts\n ts=$(date -u +\"%Y-%m-%dT%H:%M:%S.000Z\")\n local line\n line=$(echo \"$event_json\" | jq -c --arg ts \"$ts\" '. + {_ts: $ts}' 2>/dev/null)\n [ -n \"$line\" ] && printf '%s\\\\n' \"$line\" >> \"$_SYNKRO_TELEMETRY_FILE\" 2>/dev/null\n}\n\nsynkro_tag() {\n if [ \"$SYNKRO_SILENT\" = \"true\" ]; then echo \"[synkro:silent]\"; return; fi\n local route=\"\\${1:-\\$(synkro_route)}\"\n local rs=\"\\${SYNKRO_POLICY_NAME:-all}\"\n echo \"[synkro:\\${route}:\\${rs}]\"\n}\n\nsynkro_route() {\n [ \"$SYNKRO_CAPTURE_DEPTH\" = \"local_only\" ] && echo \"local\" && return\n synkro_channel_up && echo \"local\" && return\n echo \"cloud\"\n}\n\nSYNKRO_CONSENT_FILE=\"$HOME/.synkro/.local-consent\"\n\n_TAB=\\$(printf '\\\\t')\n\nsynkro_consent_grant() {\n local sid=\"\\$1\" hash=\"\\$2\"\n printf '%s\\\\t%s\\\\tactive\\\\n' \"$sid\" \"$hash\" >> \"$SYNKRO_CONSENT_FILE\" 2>/dev/null || true\n}\n\nsynkro_consent_has_active() {\n local sid=\"\\$1\" hash=\"\\$2\"\n grep -q \"^\\${sid}\\${_TAB}\\${hash}\\${_TAB}active\\$\" \"$SYNKRO_CONSENT_FILE\" 2>/dev/null\n}\n\nsynkro_consent_consume() {\n local sid=\"\\$1\" hash=\"\\$2\"\n [ ! -f \"$SYNKRO_CONSENT_FILE\" ] && return\n local tmp=\"\\${SYNKRO_CONSENT_FILE}.tmp\"\n local pat=\"\\${sid}\\${_TAB}\\${hash}\\${_TAB}active\"\n local rep=\"\\${sid}\\${_TAB}\\${hash}\\${_TAB}consumed\"\n awk -v p=\"$pat\" -v r=\"$rep\" '{if(\\$0==p)print r;else print}' \"$SYNKRO_CONSENT_FILE\" > \"$tmp\" 2>/dev/null && mv \"$tmp\" \"$SYNKRO_CONSENT_FILE\" 2>/dev/null || true\n}\n\nsynkro_post_with_retry() {\n local url=\"$1\" body=\"$2\" timeout=\"\\${3:-8}\"\n local resp\n resp=$(curl -sS -X POST \"$url\" \\\\\n -H \"Content-Type: application/json\" \\\\\n -H \"Authorization: Bearer $JWT\" \\\\\n -d \"$body\" --max-time \"$timeout\" 2>/dev/null || echo \"\")\n if echo \"$resp\" | grep -qE '\"detail\":\"Token has expired|\"detail\":\"Invalid or expired token'; then\n if synkro_refresh_jwt; then\n resp=$(curl -sS -X POST \"$url\" \\\\\n -H \"Content-Type: application/json\" \\\\\n -H \"Authorization: Bearer $JWT\" \\\\\n -d \"$body\" --max-time \"$timeout\" 2>/dev/null || echo \"\")\n fi\n fi\n echo \"$resp\"\n}\n`;\n\n\n// ─── Cursor IDE adapter scripts (legacy bash — install writes TypeScript from hookScriptsTs.ts) ───\n\nexport const CURSOR_BASH_JUDGE_SCRIPT = `#!/bin/bash\nSCRIPT_DIR=\"$(cd \"$(dirname \"\\${BASH_SOURCE[0]}\")\" && pwd)\"\n. \"$SCRIPT_DIR/_synkro-common.sh\"\n\nJWT=$(synkro_load_jwt)\nif [ -z \"$JWT\" ]; then echo '{}'; exit 0; fi\nsynkro_ensure_fresh_jwt\n\nPAYLOAD=$(cat)\nif [ -z \"$PAYLOAD\" ]; then echo '{}'; exit 0; fi\n\nCOMMAND=$(echo \"$PAYLOAD\" | jq -r '.command // empty' 2>/dev/null)\nif [ -z \"$COMMAND\" ]; then echo '{}'; exit 0; fi\n\nCWD=$(echo \"$PAYLOAD\" | jq -r '.cwd // empty' 2>/dev/null)\nSESSION_ID=$(echo \"$PAYLOAD\" | jq -r '.conversation_id // empty' 2>/dev/null)\nGIT_REPO=$(synkro_detect_repo \"\\${CWD:-.}\")\n\nCMD_SHORT=$(printf '%s' \"$COMMAND\" | head -c 80)\nsynkro_log \"bashGuard checking: $CMD_SHORT\"\n\nsynkro_load_config\nif [ \"$SYNKRO_SILENT\" = \"true\" ]; then\n echo '{}'; exit 0\nfi\n\nBODY=$(jq -n \\\\\n --arg cmd \"$COMMAND\" \\\\\n --arg session_id \"$SESSION_ID\" \\\\\n --arg cwd \"$CWD\" \\\\\n --arg repo \"$GIT_REPO\" \\\\\n '{\n hook_event: \"PreToolUse\",\n tool_name: \"Bash\",\n tool_input: {command: $cmd},\n response_format: \"cursor\",\n session_id: (if ($session_id | length) > 0 then $session_id else null end),\n cwd: (if ($cwd | length) > 0 then $cwd else null end),\n repo: (if ($repo | length) > 0 then $repo else null end)\n }')\n\nRESP=$(synkro_post_with_retry \"\\${GATEWAY_URL}/api/v1/hook/judge\" \"$BODY\" 6)\n\nif [ -z \"$RESP\" ]; then\n synkro_log \"bashGuard $CMD_SHORT → error (timeout)\"\n echo '{}'; exit 0\nfi\n\n# Server returns cursor-format directly in hook_response\nif echo \"$RESP\" | jq -e '.hook_response' >/dev/null 2>&1; then\n echo \"$RESP\" | jq -c '.hook_response'\nelse\n echo '{}'\nfi\nexit 0\n`;\n\nexport const CURSOR_EDIT_PRECHECK_SCRIPT = `#!/bin/bash\nSCRIPT_DIR=\"$(cd \"$(dirname \"\\${BASH_SOURCE[0]}\")\" && pwd)\"\n. \"$SCRIPT_DIR/_synkro-common.sh\"\n\nJWT=$(synkro_load_jwt)\nif [ -z \"$JWT\" ]; then echo '{}'; exit 0; fi\nsynkro_ensure_fresh_jwt\n\nPAYLOAD=$(cat)\nif [ -z \"$PAYLOAD\" ]; then echo '{}'; exit 0; fi\n\nTOOL_NAME=$(echo \"$PAYLOAD\" | jq -r '.tool_name // empty' 2>/dev/null)\nCWD=$(echo \"$PAYLOAD\" | jq -r '.cwd // empty' 2>/dev/null)\nSESSION_ID=$(echo \"$PAYLOAD\" | jq -r '.conversation_id // empty' 2>/dev/null)\nGIT_REPO=$(synkro_detect_repo \"\\${CWD:-.}\")\n\nFILE_PATH=$(echo \"$PAYLOAD\" | jq -r '.tool_input.file_path // .tool_input.path // .tool_input.target_file // empty' 2>/dev/null)\nCONTENT=$(echo \"$PAYLOAD\" | jq -r '.tool_input.content // .tool_input.new_string // .tool_input.code_edit // empty' 2>/dev/null)\nif [ -z \"$FILE_PATH\" ]; then echo '{}'; exit 0; fi\n\nBASENAME=$(basename \"$FILE_PATH\" 2>/dev/null || echo \"$FILE_PATH\")\nsynkro_log \"editGuard checking: $BASENAME\"\n\nsynkro_load_config\nif [ \"$SYNKRO_SILENT\" = \"true\" ]; then\n echo '{}'; exit 0\nfi\n\nBODY=$(jq -n \\\\\n --arg file_path \"$FILE_PATH\" \\\\\n --arg content \"$CONTENT\" \\\\\n --arg session_id \"$SESSION_ID\" \\\\\n --arg cwd \"$CWD\" \\\\\n --arg repo \"$GIT_REPO\" \\\\\n '{\n hook_event: \"PreToolUse\",\n tool_name: \"Edit\",\n tool_input: {file_path: $file_path, content: $content},\n file_path: $file_path,\n content: $content,\n response_format: \"cursor\",\n session_id: (if ($session_id | length) > 0 then $session_id else null end),\n cwd: (if ($cwd | length) > 0 then $cwd else null end),\n repo: (if ($repo | length) > 0 then $repo else null end)\n }')\n\nRESP=$(synkro_post_with_retry \"\\${GATEWAY_URL}/api/v1/hook/judge\" \"$BODY\" 8)\n\nif [ -z \"$RESP\" ]; then\n synkro_log \"editGuard $BASENAME → error (timeout)\"\n echo '{}'; exit 0\nfi\n\nif echo \"$RESP\" | jq -e '.hook_response' >/dev/null 2>&1; then\n echo \"$RESP\" | jq -c '.hook_response'\nelse\n echo '{}'\nfi\nexit 0\n`;\n\nexport const CURSOR_EDIT_CAPTURE_SCRIPT = `#!/bin/bash\nSCRIPT_DIR=\"$(cd \"$(dirname \"\\${BASH_SOURCE[0]}\")\" && pwd)\"\n. \"$SCRIPT_DIR/_synkro-common.sh\"\n\nJWT=$(synkro_load_jwt)\nif [ -z \"$JWT\" ]; then echo '{}'; exit 0; fi\n\nPAYLOAD=$(cat)\nif [ -z \"$PAYLOAD\" ]; then echo '{}'; exit 0; fi\n\nFILE_PATH=$(echo \"$PAYLOAD\" | jq -r '.file_path // empty' 2>/dev/null)\nif [ -z \"$FILE_PATH\" ]; then echo '{}'; exit 0; fi\n\nCWD=$(echo \"$PAYLOAD\" | jq -r '.cwd // .workspace_roots[0] // empty' 2>/dev/null)\nSESSION_ID=$(echo \"$PAYLOAD\" | jq -r '.conversation_id // empty' 2>/dev/null)\nGIT_REPO=$(synkro_detect_repo \"\\${CWD:-.}\")\nBASENAME=$(basename \"$FILE_PATH\" 2>/dev/null || echo \"$FILE_PATH\")\n\nFULL_PATH=\"$FILE_PATH\"\n[ -n \"$CWD\" ] && FULL_PATH=\"$CWD/$FILE_PATH\"\nFULL_CONTENT=\"\"\n[ -f \"$FULL_PATH\" ] && FULL_CONTENT=$(head -c 50000 \"$FULL_PATH\" 2>/dev/null || true)\n\nDEPS_JSON=\"{}\"\n_PKG_DIR=\"\\${CWD:-.}\"\nwhile [ \"$_PKG_DIR\" != \"/\" ]; do\n if [ -f \"$_PKG_DIR/package.json\" ]; then\n DEPS_JSON=$(jq -c '(.dependencies // {}) + (.devDependencies // {})' \"$_PKG_DIR/package.json\" 2>/dev/null || echo \"{}\")\n break\n fi\n _PKG_DIR=$(dirname \"$_PKG_DIR\")\ndone\n\nsynkro_log \"editScan $BASENAME\"\n\n(\n BODY=$(jq -n \\\\\n --arg file_path \"$FILE_PATH\" --arg content \"$FULL_CONTENT\" \\\\\n --arg session_id \"$SESSION_ID\" --arg cwd \"$CWD\" --arg repo \"$GIT_REPO\" \\\\\n --argjson deps \"$DEPS_JSON\" \\\\\n '{capture_type:\"edit_scan\",tool_input:{file_path:$file_path,content:$content},edit_verdict:{ok:true},dependencies:$deps}\n + (if ($session_id | length) > 0 then {session_id:$session_id} else {} end)\n + (if ($cwd | length) > 0 then {cwd:$cwd} else {} end)\n + (if ($repo | length) > 0 then {repo:$repo} else {} end)')\n curl -sS -X POST \"\\${GATEWAY_URL}/api/v1/hook/capture\" \\\\\n -H \"Content-Type: application/json\" -H \"Authorization: Bearer $JWT\" \\\\\n -d \"$BODY\" --max-time 10 >/dev/null 2>&1 || true\n) &\ndisown 2>/dev/null || true\n\necho '{}'\nexit 0\n`;\n\nexport const CURSOR_BASH_FOLLOWUP_SCRIPT = `#!/bin/bash\nSCRIPT_DIR=\"$(cd \"$(dirname \"\\${BASH_SOURCE[0]}\")\" && pwd)\"\n. \"$SCRIPT_DIR/_synkro-common.sh\"\n\nJWT=$(synkro_load_jwt)\nif [ -z \"$JWT\" ]; then echo '{}'; exit 0; fi\n\nPAYLOAD=$(cat)\nTOOL_NAME=$(echo \"$PAYLOAD\" | jq -r '.tool_name // empty' 2>/dev/null)\ncase \"$TOOL_NAME\" in Shell|Bash|terminal|run_terminal_cmd|execute_command) ;; *) echo '{}'; exit 0 ;; esac\n\nSESSION_ID=$(echo \"$PAYLOAD\" | jq -r '.conversation_id // empty' 2>/dev/null)\nTOOL_USE_ID=$(echo \"$PAYLOAD\" | jq -r '.tool_use_id // empty' 2>/dev/null)\n\nIS_ERROR=$(echo \"$PAYLOAD\" | jq -r '.tool_result.is_error // false' 2>/dev/null)\nCMD=$(echo \"$PAYLOAD\" | jq -r '.tool_input.command // empty' 2>/dev/null)\nCMD_HASH=\"\"\nif [ -n \"$CMD\" ]; then\n CMD_HASH=$(printf '%s' \"$CMD\" | shasum -a 256 | cut -c1-16)\nfi\n\nif [ -n \"$CMD_HASH\" ] && [ -n \"$SESSION_ID\" ]; then\n if [ \"$IS_ERROR\" = \"false\" ]; then\n synkro_consent_consume \"$SESSION_ID\" \"$CMD_HASH\"\n else\n if ! synkro_consent_has_active \"$SESSION_ID\" \"$CMD_HASH\"; then\n synkro_consent_grant \"$SESSION_ID\" \"$CMD_HASH\"\n fi\n fi\nfi\n\nif [ -n \"$SESSION_ID\" ] && [ -n \"$TOOL_USE_ID\" ]; then\n (\n BODY=$(jq -n --arg sid \"$SESSION_ID\" --arg tid \"$TOOL_USE_ID\" \\\\\n --argjson err \"$IS_ERROR\" --arg ch \"$CMD_HASH\" \\\\\n '{capture_type:\"bash_followup\",session_id:$sid,tool_use_id:$tid,is_error:$err,command_hash:$ch}')\n curl -sS -X POST \"\\${GATEWAY_URL}/api/v1/hook/capture\" \\\\\n -H \"Content-Type: application/json\" -H \"Authorization: Bearer $JWT\" \\\\\n -d \"$BODY\" --max-time 3 >/dev/null 2>&1 || true\n ) &\n disown 2>/dev/null || true\nfi\n\necho '{}'\nexit 0\n`;\n","// :)\n/**\n * TypeScript hook scripts for Bun runtime — written to ~/.synkro/hooks/ during `synkro install`.\n *\n * Each export is the full source code of a TypeScript file that runs via `#!/usr/bin/env bun`.\n * All grading, classification, CVE scanning, and persistence lives server-side.\n * Local mode: grade via local-cc channel with prompts from /v1/hook/config, send anonymized metadata to /v1/hook/capture.\n */\n\nexport const SYNKRO_COMMON_TS = `\n// Shared Synkro hook utilities — imported by all hook scripts.\nimport { readFileSync, writeFileSync, appendFileSync, mkdirSync, existsSync, renameSync, openSync, closeSync, unlinkSync } from 'node:fs';\nimport { join, dirname, basename, extname, resolve as resolvePath } from 'node:path';\nimport { homedir } from 'node:os';\nimport { execSync } from 'node:child_process';\nimport { constants as FS_CONSTANTS } from 'node:fs';\n\n// ─── Config ───\n\nconst HOME = homedir();\nconst CONFIG_PATH = join(HOME, '.synkro', 'config.env');\n\n// Load config.env into process.env\nif (existsSync(CONFIG_PATH)) {\n try {\n const lines = readFileSync(CONFIG_PATH, 'utf-8').split('\\\\n');\n for (const line of lines) {\n const trimmed = line.trim();\n if (!trimmed || trimmed.startsWith('#')) continue;\n const eqIdx = trimmed.indexOf('=');\n if (eqIdx < 1) continue;\n const key = trimmed.slice(0, eqIdx).trim();\n let val = trimmed.slice(eqIdx + 1).trim();\n // Strip surrounding quotes\n if ((val.startsWith('\"') && val.endsWith('\"')) || (val.startsWith(\"'\") && val.endsWith(\"'\"))) {\n val = val.slice(1, -1);\n }\n process.env[key] = val;\n }\n } catch {}\n}\n\nconst ALLOWED_GATEWAY_HOSTS = new Set(['api.synkro.sh', 'localhost', '127.0.0.1']);\nfunction validateGatewayUrl(raw: string): string {\n try {\n const u = new URL(raw);\n if (!ALLOWED_GATEWAY_HOSTS.has(u.hostname)) return 'https://api.synkro.sh';\n return raw.replace(/\\\\/+$/, '');\n } catch {\n return 'https://api.synkro.sh';\n }\n}\nexport const GATEWAY_URL = validateGatewayUrl(process.env.SYNKRO_GATEWAY_URL || 'https://api.synkro.sh');\nexport const CREDS_PATH = process.env.SYNKRO_CREDENTIALS_PATH || join(HOME, '.synkro', 'credentials.json');\nconst LAST_PROMPT_FILE = join(HOME, '.synkro', '.last-prompt');\n\n// ─── Path Validation ───\n\nexport function isPathUnder(filePath: string, cwd: string): boolean {\n if (!filePath || !cwd) return false;\n const resolved = resolvePath(filePath);\n const base = resolvePath(cwd);\n return resolved.startsWith(base + '/') || resolved === base;\n}\n\n// ─── Logging ───\n\nexport function log(msg: string): void {\n process.stderr.write('[synkro] ' + msg + '\\\\n');\n}\n\n// ─── JWT Management ───\n\nexport function loadJwt(): string | null {\n try {\n if (!existsSync(CREDS_PATH)) return null;\n const creds = JSON.parse(readFileSync(CREDS_PATH, 'utf-8'));\n return creds.access_token || null;\n } catch {\n return null;\n }\n}\n\nfunction decodeJwtExp(jwt: string): number {\n try {\n const parts = jwt.split('.');\n if (parts.length < 2) return 0;\n let payload = parts[1].replace(/-/g, '+').replace(/_/g, '/');\n while (payload.length % 4) payload += '=';\n const decoded = Buffer.from(payload, 'base64').toString('utf-8');\n const obj = JSON.parse(decoded);\n return obj.exp || 0;\n } catch {\n return 0;\n }\n}\n\nexport async function refreshJwt(jwt: string): Promise<string> {\n const creds = JSON.parse(readFileSync(CREDS_PATH, 'utf-8'));\n const rt = creds.refresh_token;\n if (!rt) return jwt;\n\n const resp = await fetch(GATEWAY_URL + '/api/auth/refresh', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ refresh_token: rt }),\n signal: AbortSignal.timeout(4000),\n });\n\n if (!resp.ok) {\n log('refresh failed: HTTP ' + resp.status);\n return jwt;\n }\n\n const data = await resp.json() as any;\n const newAt = data.access_token;\n if (!newAt) return jwt;\n\n const newRt = data.refresh_token || rt;\n const existing = (() => {\n try { return JSON.parse(readFileSync(CREDS_PATH, 'utf-8')); } catch { return {}; }\n })();\n const updated = { ...existing, access_token: newAt, refresh_token: newRt };\n const tmp = CREDS_PATH + '.synkro.tmp';\n writeFileSync(tmp, JSON.stringify(updated, null, 2));\n renameSync(tmp, CREDS_PATH);\n return newAt;\n}\n\nfunction jwtIsExpired(jwt: string): boolean {\n const exp = decodeJwtExp(jwt);\n return exp - Math.floor(Date.now() / 1000) < 60;\n}\n\nfunction lockIsStale(lockfile: string, maxAgeMs = 8000): boolean {\n try {\n const stat = require('node:fs').statSync(lockfile);\n return Date.now() - stat.mtimeMs > maxAgeMs;\n } catch {\n return false;\n }\n}\n\nexport async function ensureFreshJwt(jwt: string): Promise<string> {\n if (!jwt) return jwt;\n if (!jwtIsExpired(jwt)) return jwt;\n\n const lockfile = CREDS_PATH + '.lock';\n\n // Clean stale lock left by a killed hook\n if (existsSync(lockfile) && lockIsStale(lockfile)) {\n try { unlinkSync(lockfile); } catch {}\n }\n\n let fd = -1;\n try {\n fd = openSync(lockfile, FS_CONSTANTS.O_WRONLY | FS_CONSTANTS.O_CREAT | FS_CONSTANTS.O_EXCL, 0o644);\n } catch {\n // Another process is refreshing — wait for it\n for (let i = 0; i < 8; i++) {\n await new Promise(r => setTimeout(r, 500));\n if (!existsSync(lockfile)) break;\n }\n // Stale lock after full wait — force-remove\n if (existsSync(lockfile) && lockIsStale(lockfile)) {\n try { unlinkSync(lockfile); } catch {}\n }\n // Re-read — the winner should have written a fresh JWT\n const fresh = loadJwt();\n if (fresh && !jwtIsExpired(fresh)) return fresh;\n // Winner's refresh failed — try to acquire lock ourselves\n try {\n fd = openSync(lockfile, FS_CONSTANTS.O_WRONLY | FS_CONSTANTS.O_CREAT | FS_CONSTANTS.O_EXCL, 0o644);\n } catch {\n return fresh || jwt;\n }\n }\n\n try {\n // Re-check — another hook may have written fresh creds while we waited for lock\n const freshJwt = loadJwt();\n if (freshJwt && !jwtIsExpired(freshJwt)) return freshJwt;\n return await refreshJwt(jwt);\n } catch {\n return jwt;\n } finally {\n try { closeSync(fd); } catch {}\n try { unlinkSync(lockfile); } catch {}\n }\n}\n\n// ─── Repo Detection ───\n\nexport function detectRepo(cwd: string): string {\n try {\n const url = execSync('git remote get-url origin 2>/dev/null', { cwd, timeout: 3000, encoding: 'utf-8', stdio: ['pipe', 'pipe', 'pipe'] }).trim();\n if (!url) return '';\n return url\n .replace(/^git@[^:]+:/, '')\n .replace(/^https?:\\\\/\\\\/[^/]+\\\\//, '')\n .replace(/\\\\.git$/, '');\n } catch {\n return '';\n }\n}\n\n// ─── Channel Health ───\n\nexport async function channelUp(port = 8929): Promise<boolean> {\n return new Promise(resolve => {\n const sock = require('node:net').connect(port, '127.0.0.1');\n const done = (ok: boolean) => { try { sock.destroy(); } catch {} resolve(ok); };\n sock.once('connect', () => done(true));\n sock.once('error', () => done(false));\n sock.setTimeout(500, () => done(false));\n });\n}\n\nexport async function cweChannelUp(): Promise<boolean> {\n return channelUp(8930);\n}\n\n// ─── Config Loading ───\n\nexport interface Rule {\n rule_id: string;\n text: string;\n severity: string;\n category: string;\n mode: string;\n}\n\nexport interface HookConfig {\n captureDepth: string;\n tier: string;\n silent: boolean;\n policyName: string;\n rules: Rule[];\n scanExemptions: Array<{ path: string; cwe_id: string }>;\n}\n\nexport async function loadConfig(jwt: string, query?: string): Promise<HookConfig> {\n const config: HookConfig = {\n captureDepth: 'local_only',\n tier: 'standard',\n silent: false,\n policyName: '',\n rules: [],\n scanExemptions: [],\n };\n\n // Local-first: read from ~/.synkro/rules.json if it exists (zero latency, no network)\n const localRulesPath = join(HOME, '.synkro', 'rules.json');\n try {\n if (existsSync(localRulesPath)) {\n const raw = JSON.parse(readFileSync(localRulesPath, 'utf-8'));\n const activePolicyId = raw.config?.activePolicyId || 'local-policy';\n const policy = (raw.policies || []).find((p: any) => p.id === activePolicyId) || raw.policies?.[0];\n if (policy) {\n config.policyName = policy.name || '';\n config.rules = (policy.rules || [])\n .filter((r: any) => r.hook_stage === 'pre' || r.hook_stage === 'both' || r.hook_stage == null)\n .map((r: any) => ({\n rule_id: r.rule_id || '',\n text: r.text || '',\n severity: r.severity || '',\n category: r.category || '',\n mode: r.mode || 'blocking',\n }));\n }\n config.silent = raw.config?.silent === true;\n if (Array.isArray(raw.scanExemptions)) {\n config.scanExemptions = raw.scanExemptions\n .filter((e: any) => e && typeof e.path === 'string')\n .map((e: any) => ({ path: e.path, cwe_id: e.cwe_id || '' }));\n }\n return config;\n }\n } catch {}\n\n // Fallback: fetch from cloud API\n try {\n const url = GATEWAY_URL + '/api/v1/hook/config' + (query ? '?' + query : '');\n const resp = await fetch(url, {\n headers: { Authorization: 'Bearer ' + jwt },\n signal: AbortSignal.timeout(4000),\n });\n const data = await resp.json() as any;\n config.captureDepth = data.capture_depth || 'local_only';\n config.tier = data.tier || 'standard';\n config.silent = data.silent_mode === true || data.silent_mode === 'true';\n config.policyName = data.active_policy_name || '';\n if (Array.isArray(data.scan_exemptions)) {\n config.scanExemptions = data.scan_exemptions\n .filter((e: any) => e && typeof e.path === 'string')\n .map((e: any) => ({ path: e.path, cwe_id: e.cwe_id || '' }));\n }\n if (Array.isArray(data.rules)) {\n config.rules = data.rules\n .filter((r: any) => r.hook_stage === 'pre' || r.hook_stage === 'both' || r.hook_stage == null)\n .map((r: any) => ({\n rule_id: r.rule_id || '',\n text: r.text || '',\n severity: r.severity || '',\n category: r.category || '',\n mode: r.mode || 'blocking',\n }));\n }\n } catch {}\n return config;\n}\n\n// ─── Routing ───\n\nexport async function route(config: HookConfig): Promise<'local' | 'cloud'> {\n if (config.captureDepth === 'local_only') return 'local';\n if (await channelUp()) return 'local';\n return 'cloud';\n}\n\nexport async function cweRoute(config: HookConfig): Promise<'local' | 'cloud'> {\n if (config.captureDepth === 'local_only') return 'local';\n if (await cweChannelUp()) return 'local';\n return 'cloud';\n}\n\n// ─── Tag Building ───\n\nexport function tag(rt: string, config: HookConfig): string {\n if (config.silent) return '[synkro:silent]';\n const rs = config.policyName || 'all';\n return '[synkro:' + rt + ':' + rs + ']';\n}\n\n// ─── Local Grading (direct channel call) ───\n\ntype GradeRole = 'grade-edit' | 'grade-bash' | 'grade-plan' | 'grade-cwe';\n\nconst ROLE_MAP: Record<string, GradeRole> = {\n edit: 'grade-edit', bash: 'grade-bash', plan: 'grade-plan', cwe: 'grade-cwe',\n};\n\nconst PRIMER_KEY: Record<GradeRole, string> = {\n 'grade-edit': 'grader_primer_edit',\n 'grade-bash': 'grader_primer_bash',\n 'grade-plan': 'grader_primer_plan',\n 'grade-cwe': 'grader_primer_cwe',\n};\n\nlet primerCache: { data: Record<string, string>; ts: number } | null = null;\n\nasync function fetchPrimer(role: GradeRole, jwt: string): Promise<string> {\n if (primerCache && Date.now() - primerCache.ts < 300_000) {\n const cached = primerCache.data[PRIMER_KEY[role]];\n if (cached) return cached;\n }\n const resp = await fetch(GATEWAY_URL + '/api/v1/cli/judge-prompts', {\n headers: { Authorization: 'Bearer ' + jwt },\n signal: AbortSignal.timeout(4000),\n });\n if (!resp.ok) throw new Error('primer fetch failed: ' + resp.status);\n const data = await resp.json() as Record<string, string>;\n primerCache = { data, ts: Date.now() };\n return data[PRIMER_KEY[role]] || '';\n}\n\nconst CHANNEL_REPLY_INSTRUCTIONS = \\`\nDELIVERY METHOD — MANDATORY, OVERRIDES ALL OTHER OUTPUT RULES:\nYou are running inside a Synkro MCP channel. Do NOT output your verdict as text.\nInstead, after generating your verdict, call the reply tool EXACTLY ONCE with:\n - req_id: the req_id from this channel event's meta\n - result: your complete verdict block as a string (the <synkro-verdict>…</synkro-verdict> XML)\nAny text output is silently discarded. Only the reply tool call is captured.\\`;\n\nasync function channelGrade(role: GradeRole, prompt: string, jwt: string, port: number, timeoutMs = 20000): Promise<string> {\n const primer = await fetchPrimer(role, jwt);\n const content = primer + '\\\\n\\\\n' + CHANNEL_REPLY_INSTRUCTIONS + '\\\\n\\\\n---\\\\nPAYLOAD (the input to evaluate):\\\\n\\\\n' + prompt;\n const body = JSON.stringify({ role, content });\n\n const resp = await fetch('http://127.0.0.1:' + port + '/submit', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body,\n signal: AbortSignal.timeout(timeoutMs),\n });\n\n if (!resp.ok) {\n const text = await resp.text().catch(() => '');\n throw new Error('channel ' + resp.status + ': ' + text.slice(0, 200));\n }\n\n const data = await resp.json() as { result?: string; error?: string };\n if (data.error) throw new Error(data.error);\n return String(data.result || '');\n}\n\nexport async function localGrade(surface: string, prompt: string, timeoutMs = 20000): Promise<string> {\n if (!(await channelUp())) throw new Error('SYNKRO_CHANNEL_DOWN');\n const jwt = loadJwt();\n if (!jwt) throw new Error('NO_JWT');\n return channelGrade(ROLE_MAP[surface] || 'grade-edit', prompt, jwt, 8929, timeoutMs);\n}\n\nexport async function localGradeCwe(prompt: string): Promise<string> {\n const jwt = loadJwt();\n if (!jwt) throw new Error('NO_JWT');\n return channelGrade('grade-cwe', prompt, jwt, 8930, 45000);\n}\n\n// ─── Verdict Parsing ───\n\nexport interface Verdict {\n ok: boolean;\n reason: string;\n suggestedFix: string;\n ruleId: string;\n ruleMode: string;\n severity: string;\n category: string;\n}\n\nexport function parseVerdict(resp: string): Verdict {\n const verdict: Verdict = {\n ok: true,\n reason: '',\n suggestedFix: '',\n ruleId: '',\n ruleMode: '',\n severity: 'low',\n category: 'clean',\n };\n\n // Flatten newlines for easier regex\n const flat = resp.replace(/\\\\n/g, ' ');\n const outerMatch = flat.match(/<synkro-verdict>(.*)<\\\\/synkro-verdict>/);\n if (!outerMatch) return verdict;\n const inner = outerMatch[1];\n\n const okMatch = inner.match(/<ok>(.*?)<\\\\/ok>/);\n if (okMatch) verdict.ok = okMatch[1].trim() !== 'false';\n\n const reasonMatch = inner.match(/<reason>(.*?)<\\\\/reason>/) || inner.match(/<reasoning>(.*?)<\\\\/reasoning>/);\n if (reasonMatch) verdict.reason = reasonMatch[1].trim();\n\n const fixMatch = inner.match(/<suggested_fix>(.*?)<\\\\/suggested_fix>/);\n if (fixMatch) verdict.suggestedFix = fixMatch[1].trim();\n\n if (!verdict.ok) {\n const ruleIdMatch = inner.match(/<rule_id>(.*?)<\\\\/rule_id>/);\n const ruleModeMatch = inner.match(/<rule_mode>(.*?)<\\\\/rule_mode>/);\n const sevMatch = inner.match(/<risk_level>(.*?)<\\\\/risk_level>/);\n\n if (ruleIdMatch) {\n verdict.ruleId = ruleIdMatch[1].trim();\n } else {\n // Try to find inside a <violation> block\n const violationMatch = inner.match(/<violation>(.*?)<\\\\/violation>/);\n if (violationMatch) {\n const vBlock = violationMatch[1];\n const vRuleId = vBlock.match(/<rule_id>(.*?)<\\\\/rule_id>/);\n if (vRuleId) verdict.ruleId = vRuleId[1].trim();\n if (!verdict.reason) {\n const vReason = vBlock.match(/<reason>(.*?)<\\\\/reason>/);\n if (vReason) verdict.reason = vReason[1].trim();\n }\n if (!verdict.suggestedFix) {\n const vFix = vBlock.match(/<suggested_fix>(.*?)<\\\\/suggested_fix>/);\n if (vFix) verdict.suggestedFix = vFix[1].trim();\n }\n if (!sevMatch) {\n const vSev = vBlock.match(/<severity>(.*?)<\\\\/severity>/);\n if (vSev) verdict.severity = vSev[1].trim();\n }\n }\n }\n\n if (ruleModeMatch) verdict.ruleMode = ruleModeMatch[1].trim();\n if (sevMatch) verdict.severity = sevMatch[1].trim();\n verdict.severity = verdict.severity || 'high';\n\n const catMatch = inner.match(/<category>(.*?)<\\\\/category>/);\n verdict.category = catMatch ? catMatch[1].trim() : 'uncategorized';\n\n // Fallback: extract rule ID from reason text\n if (!verdict.ruleId && verdict.reason) {\n const rMatch = verdict.reason.match(/[Rr]\\\\d{3}/);\n if (rMatch) verdict.ruleId = rMatch[0];\n }\n }\n\n return verdict;\n}\n\n// ─── Telemetry Dispatch ───\n\nexport function dispatchCapture(\n jwt: string,\n hookType: string,\n verdictStr: string,\n severity: string,\n category: string,\n toolName: string,\n repo: string,\n sessionId: string,\n captureDepth: string,\n opts?: {\n command?: string;\n reasoning?: string;\n rulesChecked?: Rule[] | string;\n violatedRules?: string[];\n recentUserMessages?: string[];\n ccModel?: string;\n },\n): void {\n // Fire-and-forget\n const eventId = 'evt_' + Date.now() + '_' + process.pid;\n const model = opts?.ccModel || 'unknown';\n const sendFull =\n captureDepth === 'full' ||\n (captureDepth === 'evidence_on_violation' && ['block', 'warning', 'deny'].includes(verdictStr));\n\n const body: Record<string, any> = {\n capture_type: 'local_verdict',\n event_id: eventId,\n hook_type: hookType,\n verdict: verdictStr,\n severity,\n category,\n cc_model: model,\n model,\n tool_name: toolName,\n };\n if (repo) body.repo = repo;\n if (sessionId) body.session_id = sessionId;\n\n // Local telemetry always gets full content — data never leaves the machine\n const localBody = { ...body };\n if (opts) {\n if (opts.command) localBody.command = opts.command;\n if (opts.reasoning) localBody.reasoning = opts.reasoning;\n if (opts.rulesChecked) localBody.rules_checked = opts.rulesChecked;\n if (opts.violatedRules) localBody.violated_rules = opts.violatedRules;\n if (opts.recentUserMessages) localBody.recent_user_messages = opts.recentUserMessages;\n }\n appendLocalTelemetry(localBody);\n\n // local_only: no data leaves the machine\n if (captureDepth === 'local_only') return;\n\n if (sendFull && opts) {\n body.capture_depth = captureDepth;\n if (opts.command) body.command = opts.command;\n if (opts.reasoning) body.reasoning = opts.reasoning;\n if (opts.rulesChecked) body.rules_checked = opts.rulesChecked;\n if (opts.violatedRules) body.violated_rules = opts.violatedRules;\n if (opts.recentUserMessages) body.recent_user_messages = opts.recentUserMessages;\n }\n\n fetch(GATEWAY_URL + '/api/v1/hook/capture', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json', Authorization: 'Bearer ' + jwt },\n body: JSON.stringify(body),\n signal: AbortSignal.timeout(3000),\n }).catch(() => {});\n}\n\nexport function appendLocalTelemetry(body: Record<string, any>): void {\n try {\n const telPath = join(HOME, '.synkro', 'telemetry.jsonl');\n appendFileSync(telPath, JSON.stringify({ ...body, _ts: new Date().toISOString() }) + '\\\\n', 'utf-8');\n } catch {}\n}\n\n// ─── Rule Mode Lookup ───\n\nexport function ruleMode(ruleId: string, rules: Rule[]): 'blocking' | 'audit' {\n if (!ruleId || !rules.length) return 'blocking';\n const matched = rules.filter(r => r.rule_id === ruleId);\n if (matched.some(r => r.mode === 'blocking')) return 'blocking';\n return (matched[0]?.mode as 'blocking' | 'audit') || 'blocking';\n}\n\n// ─── Content Reconstruction ───\n\nexport function reconstructContent(toolName: string, toolInput: any, filePath: string, cwd?: string): string {\n const canRead = filePath && cwd && isPathUnder(filePath, cwd);\n switch (toolName) {\n case 'Write':\n return toolInput.content || '';\n case 'Edit': {\n let content = '';\n try {\n if (canRead && existsSync(filePath)) {\n content = readFileSync(filePath, 'utf-8').slice(0, 65536);\n }\n } catch {}\n const oldStr = toolInput.old_string || '';\n const newStr = toolInput.new_string || '';\n if (oldStr && content.includes(oldStr)) {\n return content.replace(oldStr, newStr);\n }\n return content || newStr;\n }\n case 'MultiEdit': {\n let content = '';\n try {\n if (canRead && existsSync(filePath)) {\n content = readFileSync(filePath, 'utf-8').slice(0, 65536);\n }\n } catch {}\n const edits = Array.isArray(toolInput.edits) ? toolInput.edits : [];\n for (const edit of edits) {\n if (!edit || typeof edit !== 'object') continue;\n const old = edit.old_string || '';\n const nw = edit.new_string || '';\n if (old && content.includes(old)) {\n content = content.replace(old, nw);\n }\n }\n return content;\n }\n case 'NotebookEdit':\n return toolInput.new_source || '';\n case 'StrReplace':\n return toolInput.new_string || toolInput.content || toolInput.code_edit || '';\n default:\n return toolInput.content || toolInput.new_string || toolInput.code_edit || '';\n }\n}\n\n// ─── HTTP with Retry ───\n\nexport async function postWithRetry(url: string, body: any, jwt: string, timeout = 8000): Promise<any> {\n let currentJwt = jwt;\n let resp: Response;\n try {\n resp = await fetch(url, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json', Authorization: 'Bearer ' + currentJwt },\n body: JSON.stringify(body),\n signal: AbortSignal.timeout(timeout),\n });\n } catch {\n return null;\n }\n\n let data: any;\n try { data = await resp.json(); } catch { return null; }\n\n // Retry on token expiry\n if (data?.detail && (data.detail.includes('Token has expired') || data.detail.includes('Invalid or expired token'))) {\n try {\n currentJwt = await refreshJwt(currentJwt);\n const resp2 = await fetch(url, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json', Authorization: 'Bearer ' + currentJwt },\n body: JSON.stringify(body),\n signal: AbortSignal.timeout(timeout),\n });\n data = await resp2.json();\n } catch {\n return null;\n }\n }\n\n return data;\n}\n\n// ─── Read Stdin ───\n\nexport async function readStdin(): Promise<string> {\n const chunks: Buffer[] = [];\n for await (const chunk of process.stdin) {\n chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));\n }\n return Buffer.concat(chunks).toString('utf-8');\n}\n\n// ─── Transcript Extraction ───\n\nexport interface TranscriptContext {\n userIntent: string;\n recentUserMessages: string[];\n recentMessages: Array<{ type: string; text: string }>;\n recentActions: Array<{ tool: string; input: string }>;\n sessionSummary: string;\n ccModel: string;\n ccUsage: Record<string, any>;\n}\n\nexport function extractTranscript(transcriptPath: string | undefined): TranscriptContext {\n const ctx: TranscriptContext = {\n userIntent: '',\n recentUserMessages: [],\n recentMessages: [],\n recentActions: [],\n sessionSummary: '',\n ccModel: '',\n ccUsage: {},\n };\n\n if (!transcriptPath || !existsSync(transcriptPath)) return ctx;\n\n try {\n const raw = readFileSync(transcriptPath, 'utf-8');\n const lines = raw.split('\\\\n').filter(l => l.trim());\n // Take the last 400 lines\n const tail = lines.slice(-400);\n\n const parsed: any[] = [];\n for (const line of tail) {\n try { parsed.push(JSON.parse(line)); } catch {}\n }\n\n // Recent user messages (last 5)\n const userMsgs: string[] = [];\n for (const entry of parsed) {\n if (entry.type !== 'user') continue;\n const content = entry.message?.content;\n let text = '';\n if (typeof content === 'string') text = content;\n else if (Array.isArray(content)) text = content.map((c: any) => c.text || '').join(' ');\n if (text) userMsgs.push(text);\n }\n ctx.recentUserMessages = userMsgs.slice(-5);\n ctx.userIntent = ctx.recentUserMessages[ctx.recentUserMessages.length - 1] || '';\n\n // Recent messages (last 10, user + assistant)\n const msgs: Array<{ type: string; text: string }> = [];\n for (const entry of parsed) {\n if (entry.type !== 'user' && entry.type !== 'assistant') continue;\n const content = entry.message?.content;\n let text = '';\n if (typeof content === 'string') text = content.slice(0, 500);\n else if (Array.isArray(content)) text = content.map((c: any) => (c.text || '').slice(0, 300)).join(' ');\n msgs.push({ type: entry.type, text });\n }\n ctx.recentMessages = msgs.slice(-10);\n\n // Recent tool calls (last 5)\n const actions: Array<{ tool: string; input: string }> = [];\n for (const entry of parsed) {\n if (entry.type !== 'assistant') continue;\n const content = entry.message?.content;\n if (!Array.isArray(content)) continue;\n for (const block of content) {\n if (block.type !== 'tool_use') continue;\n actions.push({\n tool: block.name || '',\n input: JSON.stringify(block.input || {}).slice(0, 200),\n });\n }\n }\n ctx.recentActions = actions.slice(-5);\n\n // Session summary\n for (const entry of parsed) {\n if (entry.type === 'summary' && entry.summary) {\n ctx.sessionSummary = entry.summary;\n }\n }\n\n // CC model\n const assistantEntries = parsed.filter(e => e.type === 'assistant');\n if (assistantEntries.length > 0) {\n const last = assistantEntries[assistantEntries.length - 1];\n ctx.ccModel = last.message?.model || '';\n const usage = last.message?.usage;\n if (usage) {\n ctx.ccUsage = {\n input_tokens: usage.input_tokens,\n output_tokens: usage.output_tokens,\n cache_creation_input_tokens: usage.cache_creation_input_tokens,\n cache_read_input_tokens: usage.cache_read_input_tokens,\n };\n }\n }\n } catch {}\n\n return ctx;\n}\n\n// ─── Last Prompt ───\n\nexport function readLastPrompt(): string {\n try {\n if (!existsSync(LAST_PROMPT_FILE)) return '';\n return readFileSync(LAST_PROMPT_FILE, 'utf-8').trim();\n } catch {\n return '';\n }\n}\n\n// ─── Find Nearest Package Dependencies ───\n\nexport function findNearestDeps(filePath: string): Record<string, string> {\n let dir = dirname(filePath);\n while (dir !== '/' && dir !== '.') {\n const pkg = join(dir, 'package.json');\n if (existsSync(pkg)) {\n try {\n const data = JSON.parse(readFileSync(pkg, 'utf-8'));\n return { ...(data.dependencies || {}), ...(data.devDependencies || {}) };\n } catch {}\n }\n const parent = dirname(dir);\n if (parent === dir) break;\n dir = parent;\n }\n return {};\n}\n\n// ─── Consent Tracking ───\n\nconst CONSENT_FILE = join(HOME, '.synkro', '.local-consent');\n\nexport function consentGrant(sessionId: string, hash: string): void {\n try {\n const dir = dirname(CONSENT_FILE);\n if (!existsSync(dir)) mkdirSync(dir, { recursive: true });\n const line = sessionId + '\\\\t' + hash + '\\\\tactive\\\\n';\n const { appendFileSync } = require('node:fs');\n appendFileSync(CONSENT_FILE, line, 'utf-8');\n } catch {}\n}\n\nexport function consentHasActive(sessionId: string, hash: string): boolean {\n try {\n if (!existsSync(CONSENT_FILE)) return false;\n const content = readFileSync(CONSENT_FILE, 'utf-8');\n return content.includes(sessionId + '\\\\t' + hash + '\\\\tactive');\n } catch {\n return false;\n }\n}\n\nexport function consentConsume(sessionId: string, hash: string): void {\n try {\n if (!existsSync(CONSENT_FILE)) return;\n const content = readFileSync(CONSENT_FILE, 'utf-8');\n const target = sessionId + '\\\\t' + hash + '\\\\tactive';\n const replacement = sessionId + '\\\\t' + hash + '\\\\tconsumed';\n const updated = content.split('\\\\n').map(l => l === target ? replacement : l).join('\\\\n');\n writeFileSync(CONSENT_FILE, updated, 'utf-8');\n } catch {}\n}\n\n// ─── Crypto Hash ───\n\nexport function hashCommand(cmd: string): string {\n const { createHash } = require('node:crypto');\n return createHash('sha256').update(cmd).digest('hex').slice(0, 16);\n}\n\n// ─── Transcript Usage Aggregation ───\n\nexport function aggregateUsage(transcriptPath: string): { model: string; totals: Record<string, number> } {\n const result = { model: '', totals: { in: 0, out: 0, cw: 0, cr: 0 } };\n if (!transcriptPath || !existsSync(transcriptPath)) return result;\n try {\n const raw = readFileSync(transcriptPath, 'utf-8');\n const lines = raw.split('\\\\n').filter(l => l.trim());\n for (const line of lines) {\n try {\n const entry = JSON.parse(line);\n if (entry.type !== 'assistant') continue;\n result.model = entry.message?.model || result.model;\n const u = entry.message?.usage;\n if (u) {\n result.totals.in += u.input_tokens || 0;\n result.totals.out += u.output_tokens || 0;\n result.totals.cw += u.cache_creation_input_tokens || 0;\n result.totals.cr += u.cache_read_input_tokens || 0;\n }\n } catch {}\n }\n } catch {}\n return result;\n}\n\n// ─── Scan Finding Dispatch ───\n\nexport function dispatchFinding(\n jwt: string,\n finding: {\n session_id: string;\n file_path: string;\n finding_type: 'cwe' | 'cve';\n finding_id: string;\n severity?: string;\n status: 'open' | 'resolved' | 'exempted';\n detail?: string;\n description?: string;\n package_name?: string;\n package_version?: string;\n fixed_version?: string;\n aliases?: string[];\n references?: Array<{ type: string; url: string }>;\n cwe_name?: string;\n },\n captureDepth: string,\n): void {\n const localEntry: Record<string, any> = {\n capture_type: 'scan_finding',\n ...finding,\n };\n appendLocalTelemetry(localEntry);\n\n if (captureDepth === 'local_only') return;\n\n const cloudBody: Record<string, any> = {\n finding_type: finding.finding_type,\n finding_id: finding.finding_id,\n severity: finding.severity,\n status: finding.status,\n session_id: finding.session_id,\n };\n\n if (captureDepth === 'evidence_on_violation' || captureDepth === 'full') {\n cloudBody.file_path = finding.file_path;\n cloudBody.package_name = finding.package_name;\n cloudBody.package_version = finding.package_version;\n cloudBody.fixed_version = finding.fixed_version;\n cloudBody.aliases = finding.aliases;\n cloudBody.references = finding.references;\n cloudBody.cwe_name = finding.cwe_name;\n }\n if (captureDepth === 'full') {\n cloudBody.detail = finding.detail;\n cloudBody.description = finding.description;\n }\n\n fetch(GATEWAY_URL + '/api/v1/hook/finding', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json', Authorization: 'Bearer ' + jwt },\n body: JSON.stringify(cloudBody),\n signal: AbortSignal.timeout(3000),\n }).catch(() => {});\n}\n\n// ─── Hook tool-name sets (CC + Cursor) ───\n\nexport const EDIT_TOOL_NAMES = new Set([\n 'Edit', 'Write', 'MultiEdit', 'NotebookEdit', 'StrReplace',\n]);\nexport const SHELL_TOOL_NAMES = new Set([\n 'Bash', 'Shell', 'Read', 'Grep', 'Glob', 'terminal', 'run_terminal_cmd', 'execute_command',\n]);\nexport const AGENT_TOOL_NAMES = new Set(['Agent', 'Task']);\nexport const PLAN_TOOL_NAMES = new Set(['ExitPlanMode', 'SwitchMode', 'CreatePlan']);\n\nexport function isEditTool(toolName: string): boolean {\n return EDIT_TOOL_NAMES.has(toolName);\n}\nexport function isShellTool(toolName: string): boolean {\n return SHELL_TOOL_NAMES.has(toolName);\n}\nexport function isAgentTool(toolName: string): boolean {\n return AGENT_TOOL_NAMES.has(toolName);\n}\nexport function isPlanTool(toolName: string): boolean {\n return PLAN_TOOL_NAMES.has(toolName);\n}\n\nexport function hookSessionId(payload: Record<string, unknown>): string {\n return String(payload.session_id ?? payload.conversation_id ?? '');\n}\n\nexport function isCursorHookFormat(): boolean {\n return process.env.SYNKRO_HOOK_FORMAT === 'cursor';\n}\n\nlet cursorHookExited = false;\n\nexport function setupCursorHookSignals(): void {\n if (!isCursorHookFormat()) return;\n process.on('SIGTERM', () => outputEmpty());\n}\n\nfunction cursorHookExit(): never {\n cursorHookExited = true;\n process.exit(0);\n}\n\n// ─── Output Helpers ───\n\nexport function outputJson(obj: any): void {\n if (isCursorHookFormat()) {\n const hso = obj?.hookSpecificOutput;\n const sys = typeof obj?.systemMessage === 'string' ? obj.systemMessage : '';\n if (hso?.permissionDecision === 'deny') {\n const reason = hso.permissionDecisionReason || hso.additionalContext || sys;\n if (!cursorHookExited) {\n cursorHookExited = true;\n process.stdout.write(JSON.stringify({\n permission: 'deny',\n user_message: sys || reason,\n agent_message: hso.additionalContext || reason,\n }) + '\\\\n');\n }\n cursorHookExit();\n }\n const ctx = sys || hso?.additionalContext;\n if (ctx) {\n if (!cursorHookExited) {\n cursorHookExited = true;\n process.stdout.write(JSON.stringify({ additional_context: ctx }) + '\\\\n');\n }\n cursorHookExit();\n }\n outputEmpty();\n return;\n }\n console.log(JSON.stringify(obj));\n}\n\nexport function outputEmpty(): void {\n if (isCursorHookFormat()) {\n if (!cursorHookExited) {\n cursorHookExited = true;\n try { process.stdout.write('{}\\\\n'); } catch {}\n }\n cursorHookExit();\n }\n console.log('{}');\n}\n`;\n\n\n// ─── CC PreToolUse Edit/Write/MultiEdit/NotebookEdit pre-check (TypeScript) ───\n\nexport const EDIT_PRECHECK_TS = `#!/usr/bin/env bun\nimport {\n loadJwt, ensureFreshJwt, detectRepo, loadConfig, route, tag, localGrade,\n parseVerdict, dispatchCapture, ruleMode, reconstructContent, isPathUnder, postWithRetry,\n readStdin, extractTranscript, readLastPrompt, findNearestDeps, log,\n outputJson, outputEmpty, setupCursorHookSignals, isEditTool, hookSessionId, GATEWAY_URL,\n type HookConfig, type Rule,\n} from './_synkro-common.ts';\nimport { existsSync, readFileSync } from 'node:fs';\nimport { basename, dirname, join } from 'node:path';\n\nasync function main() {\n setupCursorHookSignals();\n try {\n const input = await readStdin();\n if (!input.trim()) { outputEmpty(); return; }\n\n const payload = JSON.parse(input);\n const toolName = payload.tool_name || '';\n if (!isEditTool(toolName)) {\n outputEmpty();\n return;\n }\n\n const toolInput = payload.tool_input || {};\n const sessionId = hookSessionId(payload);\n const toolUseId = payload.tool_use_id || '';\n const cwd = payload.cwd || '';\n const permissionMode = payload.permission_mode || '';\n const transcriptPath = payload.transcript_path || '';\n\n const filePath = toolInput.file_path || toolInput.notebook_path || toolInput.path || '';\n if (!filePath) { outputEmpty(); return; }\n\n if (filePath.includes('/.synkro/hooks/')) { outputEmpty(); return; }\n\n const fileShort = basename(filePath);\n log('editGuard checking: ' + fileShort);\n\n const gitRepo = detectRepo(cwd || '.');\n\n let jwt = loadJwt();\n if (!jwt) { outputEmpty(); return; }\n jwt = await ensureFreshJwt(jwt);\n\n // Reconstruct proposed content\n const proposed = reconstructContent(toolName, toolInput, filePath, cwd);\n if (!proposed) { outputEmpty(); return; }\n\n // Build diff field\n let diffField: any = null;\n if (toolInput.old_string != null || toolInput.new_string != null || toolInput.edits != null) {\n diffField = {};\n if (toolInput.old_string != null) diffField.old_string = toolInput.old_string;\n if (toolInput.new_string != null) diffField.new_string = toolInput.new_string;\n if (toolInput.edits != null) diffField.edits = toolInput.edits;\n }\n\n // Read file before edit for cloud payload\n let fileBefore = '';\n if (toolName !== 'Write' && filePath && isPathUnder(filePath, cwd || '.') && existsSync(filePath)) {\n try { fileBefore = readFileSync(filePath, 'utf-8').slice(0, 65536); } catch {}\n }\n\n // Extract transcript context\n const transcript = extractTranscript(transcriptPath);\n const lastPrompt = readLastPrompt();\n\n // Load config and decide route\n const config = await loadConfig(jwt);\n const rt = await route(config);\n const tagStr = tag(rt, config);\n\n if (config.silent) {\n outputJson({ systemMessage: tagStr + ' editGuard \\\\u2192 skipped (silent mode)' });\n return;\n }\n\n if (rt === 'local') {\n // ─── Local grading: org rules ONLY (channel 1, port 8929) ───\n const proposedShort = proposed.slice(0, 4000);\n const graderPrompt = [\n 'Working directory: ' + (cwd || '.'),\n 'Repo: ' + (gitRepo || 'unknown'),\n 'File: ' + filePath,\n 'Proposed content (first 4000 chars):',\n proposedShort,\n 'User intent (last human message): ' + (transcript.userIntent || 'none stated'),\n 'Last user prompt: ' + (lastPrompt || 'none'),\n 'Org rules: ' + JSON.stringify(config.rules),\n ].join('\\\\n');\n\n let gradeResp: string;\n try {\n gradeResp = await localGrade('edit', graderPrompt);\n } catch {\n outputEmpty();\n return;\n }\n\n const verdict = parseVerdict(gradeResp);\n const editContent = 'file=' + filePath + ' content=' + proposed.slice(0, 2000);\n const violatedRules = verdict.ruleId ? [verdict.ruleId] : [];\n\n if (!verdict.ok) {\n const mode = verdict.ruleMode || ruleMode(verdict.ruleId, config.rules);\n const guardReason = (verdict.ruleId ? '(' + verdict.ruleId + ') ' : '') + (verdict.reason || 'policy violation');\n\n if (mode !== 'audit') {\n const denyReason = 'Guard: ' + guardReason + '\\\\nFix all issues before retrying. Do NOT ask the user to make the edit manually — resolve the violation in code yourself.';\n dispatchCapture(jwt, 'edit', 'block', verdict.severity || 'critical', verdict.category || 'security',\n toolName, gitRepo, sessionId, config.captureDepth, {\n command: editContent, reasoning: guardReason,\n rulesChecked: config.rules, violatedRules,\n ccModel: transcript.ccModel,\n });\n outputJson({\n systemMessage: tagStr + ' editGuard ' + fileShort + ' \\\\u2192 blocked: ' + guardReason,\n hookSpecificOutput: { hookEventName: 'PreToolUse', permissionDecision: 'deny', permissionDecisionReason: denyReason, additionalContext: denyReason },\n });\n return;\n }\n\n // Audit mode — warn but allow\n dispatchCapture(jwt, 'edit', 'warning', verdict.severity || 'medium', verdict.category || 'security',\n toolName, gitRepo, sessionId, config.captureDepth, {\n command: editContent, reasoning: guardReason,\n rulesChecked: config.rules, violatedRules,\n ccModel: transcript.ccModel,\n });\n outputJson({ systemMessage: tagStr + ' editGuard ' + fileShort + ' \\\\u2192 warning: ' + guardReason });\n return;\n }\n\n // Clean\n dispatchCapture(jwt, 'edit', 'pass', 'audit', verdict.category || 'trivial_edit',\n toolName, gitRepo, sessionId, config.captureDepth, {\n command: editContent, reasoning: verdict.reason || 'no policy violations detected',\n rulesChecked: config.rules, violatedRules: [],\n ccModel: transcript.ccModel,\n });\n outputJson({ systemMessage: tagStr + ' editGuard ' + fileShort + ' \\\\u2192 pass: ' + (verdict.reason || 'no policy violations detected') });\n return;\n }\n\n // ─── Cloud grading ───\n const deps = findNearestDeps(filePath);\n const isHeadless = ['acceptEdits', 'bypassPermissions', 'plan', 'auto'].includes(permissionMode)\n || process.env.SYNKRO_HEADLESS === '1';\n\n const body = {\n hook_event: 'PreToolUse',\n tool_name: toolName,\n tool_input: toolInput,\n file_path: filePath,\n content: proposed,\n file_before: fileBefore || null,\n diff: diffField,\n dependencies: deps,\n user_intent: transcript.userIntent || null,\n last_user_message: lastPrompt || null,\n recent_user_messages: transcript.recentUserMessages,\n recent_messages: transcript.recentMessages,\n recent_actions: transcript.recentActions,\n session_id: sessionId || null,\n tool_use_id: toolUseId || null,\n cwd: cwd || null,\n repo: gitRepo || null,\n permission_mode: permissionMode || null,\n headless: isHeadless,\n };\n\n const resp = await postWithRetry(GATEWAY_URL + '/api/v1/hook/judge', body, jwt, 8000);\n\n if (!resp) {\n log('editGuard ' + fileShort + ' \\\\u2192 error (timeout)');\n outputEmpty();\n return;\n }\n\n if (!resp.hook_response || typeof resp.hook_response !== 'object') {\n log('editGuard ' + fileShort + ' \\\\u2192 pass (no hook_response)');\n outputEmpty();\n return;\n }\n\n const hookResp = resp.hook_response;\n const decision = hookResp?.hookSpecificOutput?.permissionDecision;\n\n if (decision === 'deny' || decision === 'ask') {\n log('editGuard ' + fileShort + ' \\\\u2192 BLOCKED');\n // Strip permissionDecision — we use systemMessage only\n const cleaned = { ...hookResp };\n if (cleaned.hookSpecificOutput) {\n cleaned.hookSpecificOutput = { ...cleaned.hookSpecificOutput };\n delete cleaned.hookSpecificOutput.permissionDecision;\n delete cleaned.hookSpecificOutput.permissionDecisionReason;\n }\n outputJson(cleaned);\n } else {\n const reason = hookResp.reason || '';\n log('editGuard ' + fileShort + ' \\\\u2192 pass' + (reason ? ': ' + reason : ''));\n outputJson(hookResp);\n }\n } catch (err) {\n process.stderr.write('[synkro] editGuard error: ' + String(err) + '\\\\n');\n outputEmpty();\n }\n}\n\nmain();\n`;\n\n\n// ─── CC PreToolUse CWE scan (standalone, channel 2) (TypeScript) ───\n\nexport const CWE_PRECHECK_TS = `#!/usr/bin/env bun\nimport {\n loadJwt, ensureFreshJwt, detectRepo, loadConfig, cweRoute, tag,\n localGradeCwe, parseVerdict, reconstructContent, readStdin, log,\n outputJson, outputEmpty, setupCursorHookSignals, isEditTool, hookSessionId, dispatchFinding, GATEWAY_URL,\n} from './_synkro-common.ts';\nimport { basename, extname } from 'node:path';\n\nasync function main() {\n setupCursorHookSignals();\n try {\n const input = await readStdin();\n if (!input.trim()) { outputEmpty(); return; }\n\n const payload = JSON.parse(input);\n const toolName = payload.tool_name || '';\n if (!isEditTool(toolName)) {\n outputEmpty();\n return;\n }\n\n const toolInput = payload.tool_input || {};\n const sessionId = hookSessionId(payload);\n const cwd = payload.cwd || '';\n const gitRepo = detectRepo(cwd || '.');\n\n const filePath = toolInput.file_path || toolInput.notebook_path || toolInput.path || '';\n if (!filePath) { outputEmpty(); return; }\n\n if (filePath.includes('/.synkro/hooks/')) { outputEmpty(); return; }\n\n const fileShort = basename(filePath);\n const fileExt = extname(filePath); // e.g. \".ts\"\n\n let jwt = loadJwt();\n if (!jwt) { outputEmpty(); return; }\n jwt = await ensureFreshJwt(jwt);\n\n // Reconstruct proposed content\n const proposed = reconstructContent(toolName, toolInput, filePath, cwd);\n if (!proposed) { outputEmpty(); return; }\n\n // Change-anchored window: for Edit/MultiEdit send context around the diff,\n // for Write send first 4000 chars (new files have patterns at the top).\n let cweContent: string;\n if (toolName === 'Edit' || toolName === 'MultiEdit') {\n const newStr = toolName === 'Edit'\n ? (toolInput.new_string || '')\n : (Array.isArray(toolInput.edits) ? toolInput.edits.map((e: any) => e?.new_string || '').join('\\\\n') : '');\n const changeIdx = proposed.indexOf(newStr);\n if (changeIdx >= 0 && proposed.length > 6000) {\n const start = Math.max(0, changeIdx - 2000);\n const end = Math.min(proposed.length, changeIdx + newStr.length + 2000);\n cweContent = proposed.slice(start, end);\n } else {\n cweContent = proposed.slice(0, 6000);\n }\n } else {\n cweContent = proposed.slice(0, 4000);\n }\n\n const config = await loadConfig(jwt);\n const rt = await cweRoute(config);\n\n // Build set of exempted CWE IDs for this file path\n const exemptedCwes = new Set<string>();\n for (const ex of config.scanExemptions) {\n if (ex.cwe_id && filePath.includes(ex.path)) {\n exemptedCwes.add(ex.cwe_id.toUpperCase());\n }\n }\n if (config.silent) {\n outputJson({ systemMessage: '[synkro:' + rt + ':cweScan] ' + fileShort + ' \\\\u2192 skipped (silent mode)' });\n return;\n }\n\n const cweTag = '[synkro:' + rt + ':cweScan]';\n\n if (rt === 'local') {\n // ─── Local CWE grading on channel 2 (port 8930) ───\n let cweRules: any[] = [];\n try {\n const resp = await fetch(GATEWAY_URL + '/api/v1/cwe-rules?ext=' + encodeURIComponent(fileExt), {\n headers: { Authorization: 'Bearer ' + jwt },\n signal: AbortSignal.timeout(4000),\n });\n const data = await resp.json() as any;\n cweRules = data.rules || [];\n } catch {}\n\n if (cweRules.length === 0) {\n outputJson({ systemMessage: cweTag + ' ' + fileShort + ' \\\\u2192 clean (no CWE rules for ' + fileExt + ')' });\n return;\n }\n\n const graderPrompt = [\n 'File: ' + filePath,\n 'Content:',\n cweContent,\n '',\n 'CWE rules to check against:',\n JSON.stringify(cweRules),\n ].join('\\\\n');\n\n let gradeResp: string;\n try {\n gradeResp = await localGradeCwe(graderPrompt);\n } catch (gradeErr: any) {\n const reason = gradeErr?.message || String(gradeErr);\n outputJson({ systemMessage: cweTag + ' ' + fileShort + ' \\\\u2192 grader unavailable (' + reason + '), skipped' });\n return;\n }\n\n const verdict = parseVerdict(gradeResp);\n\n if (!verdict.ok) {\n const ruleIdMatches = gradeResp.match(/<rule_id>([^<]+)<\\\\/rule_id>/g) || [];\n const cweIds: string[] = [];\n for (const match of ruleIdMatches.slice(0, 5)) {\n const id = match.replace(/<\\\\/?rule_id>/g, '').trim().replace(/^cwe-/, 'CWE-');\n if (id && !cweIds.includes(id)) cweIds.push(id);\n }\n\n const fixMatches = gradeResp.match(/<suggested_fix>([^<]+)<\\\\/suggested_fix>/g) || [];\n const fixes: Record<string, string> = {};\n for (let i = 0; i < Math.min(cweIds.length, fixMatches.length); i++) {\n fixes[cweIds[i]] = fixMatches[i].replace(/<\\\\/?suggested_fix>/g, '').trim();\n }\n\n // Filter out exempted CWEs for this file\n const activeCweIds = cweIds.filter(id => !exemptedCwes.has(id.toUpperCase()));\n\n if (activeCweIds.length === 0) {\n outputJson({ systemMessage: cweTag + ' ' + fileShort + ' \\\\u2192 clean (exempted: ' + cweIds.join(', ') + ')' });\n return;\n }\n\n const cweNameMap = new Map<string, string>();\n for (const r of cweRules) {\n if (r.cwe && r.name) cweNameMap.set(r.cwe.toUpperCase(), r.name);\n }\n\n const displayIds = activeCweIds.slice(0, 3).join(', ');\n const count = activeCweIds.length;\n const label = count === 1 ? 'match' : 'matches';\n const cweMsg = cweTag + ' ' + fileShort + ' \\\\u2192 ' + count + ' CWE ' + label + ' (' + displayIds + ')';\n const denyDetail = '[' + displayIds + '] ' + (verdict.reason || 'code weakness detected');\n const fixLines = activeCweIds\n .filter(id => fixes[id])\n .map(id => '[' + id + '] Fix: ' + fixes[id]);\n const fixHint = fixLines.length > 0 ? '\\\\n' + fixLines.join('\\\\n') : '';\n const ctx = 'CWE: ' + denyDetail + fixHint + '\\\\nFix all issues before retrying. Do NOT ask the user to make the edit manually — resolve the weakness in code yourself.';\n\n for (const cweId of activeCweIds) {\n dispatchFinding(jwt, {\n session_id: sessionId,\n file_path: filePath,\n finding_type: 'cwe',\n finding_id: cweId,\n severity: verdict.severity || 'high',\n status: 'open',\n detail: verdict.reason || 'code weakness detected',\n cwe_name: cweNameMap.get(cweId.toUpperCase()) || undefined,\n }, config.captureDepth);\n }\n\n outputJson({\n systemMessage: cweMsg,\n hookSpecificOutput: { hookEventName: 'PreToolUse', permissionDecision: 'deny', permissionDecisionReason: ctx, additionalContext: ctx },\n });\n return;\n }\n\n dispatchFinding(jwt, {\n session_id: sessionId,\n file_path: filePath,\n finding_type: 'cwe',\n finding_id: 'pass',\n status: 'resolved',\n }, config.captureDepth);\n\n outputJson({ systemMessage: cweTag + ' ' + fileShort + ' \\\\u2192 clean' });\n return;\n }\n\n // ─── Cloud CWE grading (handled by server) ───\n // Cloud edit precheck already includes CWE — this hook is a no-op for cloud.\n outputEmpty();\n } catch (err) {\n process.stderr.write('[synkro] cweGuard error: ' + String(err) + '\\\\n');\n outputEmpty();\n }\n}\n\nmain();\n`;\n\n\n// ─── CC PreToolUse CVE scan (standalone, curl only) (TypeScript) ───\n\nexport const CVE_PRECHECK_TS = `#!/usr/bin/env bun\nimport {\n loadJwt, ensureFreshJwt, detectRepo, loadConfig, route, tag,\n reconstructContent, readStdin, findNearestDeps, log,\n outputJson, outputEmpty, setupCursorHookSignals, isEditTool, hookSessionId, dispatchFinding, GATEWAY_URL,\n} from './_synkro-common.ts';\nimport { basename } from 'node:path';\n\nconst MANIFEST_NAMES = new Set([\n 'package.json', 'requirements.txt', 'requirements-dev.txt', 'requirements-test.txt',\n 'Pipfile', 'go.mod', 'go.sum', 'Gemfile', 'pom.xml', 'Cargo.toml', 'composer.json', 'pyproject.toml',\n]);\n\nfunction isManifest(filename: string): boolean {\n if (MANIFEST_NAMES.has(filename)) return true;\n if (filename.startsWith('requirements') && filename.endsWith('.txt')) return true;\n if (filename.startsWith('build.gradle')) return true;\n if (filename.endsWith('.cabal')) return true;\n return false;\n}\n\nasync function main() {\n setupCursorHookSignals();\n try {\n const input = await readStdin();\n if (!input.trim()) { outputEmpty(); return; }\n\n const payload = JSON.parse(input);\n const toolName = payload.tool_name || '';\n if (!isEditTool(toolName)) {\n outputEmpty();\n return;\n }\n\n const toolInput = payload.tool_input || {};\n const sessionId = hookSessionId(payload);\n const cwd = payload.cwd || '';\n\n const filePath = toolInput.file_path || toolInput.notebook_path || toolInput.path || '';\n if (!filePath) { outputEmpty(); return; }\n\n if (filePath.includes('/.synkro/hooks/')) { outputEmpty(); return; }\n\n const fileShort = basename(filePath);\n\n let jwt = loadJwt();\n if (!jwt) { outputEmpty(); return; }\n jwt = await ensureFreshJwt(jwt);\n\n const config = await loadConfig(jwt);\n const rt = await route(config);\n\n if (config.silent) {\n outputJson({ systemMessage: '[synkro:' + rt + ':cveScan] ' + fileShort + ' \\\\u2192 skipped (silent mode)' });\n return;\n }\n\n const cveTag = '[synkro:' + rt + ':cveScan]';\n\n // Reconstruct proposed content\n const proposed = reconstructContent(toolName, toolInput, filePath, cwd);\n if (!proposed) {\n outputJson({ systemMessage: cveTag + ' ' + fileShort + ' \\\\u2192 skip (no content)' });\n return;\n }\n\n const proposedShort = proposed.slice(0, 4000);\n\n // For code files, find nearest package.json and extract deps\n let deps: Record<string, string> = {};\n if (!isManifest(fileShort)) {\n deps = findNearestDeps(filePath);\n }\n\n // CVE scan via OSV API\n const cveBody = {\n file_path: filePath,\n content: proposedShort,\n dependencies: deps,\n };\n\n let cveResp: any;\n try {\n const resp = await fetch(GATEWAY_URL + '/api/v1/cve-scan', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json', Authorization: 'Bearer ' + jwt },\n body: JSON.stringify(cveBody),\n signal: AbortSignal.timeout(8000),\n });\n cveResp = await resp.json();\n } catch {\n outputJson({ systemMessage: cveTag + ' ' + fileShort + ' \\\\u2192 error (timeout)' });\n return;\n }\n\n const findings = Array.isArray(cveResp?.findings) ? cveResp.findings : [];\n if (findings.length > 0) {\n for (const f of findings.slice(0, 10)) {\n const cveId = (f.aliases || []).find((a: string) => a.startsWith('CVE-')) || f.id || 'unknown';\n dispatchFinding(jwt, {\n session_id: sessionId,\n file_path: filePath,\n finding_type: 'cve',\n finding_id: cveId,\n severity: f.severity || 'high',\n status: 'open',\n detail: f.summary || f.title || 'vulnerable dependency',\n description: f.details || undefined,\n package_name: f.package || undefined,\n package_version: f.version || undefined,\n fixed_version: f.fixed || undefined,\n aliases: f.aliases || undefined,\n references: f.references || undefined,\n }, config.captureDepth);\n }\n\n const formatFinding = (f: any): string => {\n const id = (f.aliases || []).find((a: string) => a.startsWith('CVE-')) || f.id || '?';\n const pkg = f.package || '?';\n const ver = f.version || '?';\n const title = f.title || f.summary || 'vulnerable';\n const fix = f.fixed ? ' (fix: >=' + f.fixed + ')' : ' (no safe version)';\n return '[' + id + '] ' + pkg + '@' + ver + ': ' + title + fix;\n };\n\n const top3 = findings.slice(0, 3).map(formatFinding).join('; ');\n const count = findings.length;\n const label = count === 1 ? 'advisory' : 'advisories';\n const cveMsg = cveTag + ' ' + fileShort + ' \\\\u2192 ' + count + ' ' + label;\n const ctx = 'CVE: ' + top3 + '\\\\nFix all issues before retrying. Do NOT ask the user to make the edit manually — upgrade the vulnerable dependencies yourself.';\n\n outputJson({\n systemMessage: cveMsg,\n hookSpecificOutput: { hookEventName: 'PreToolUse', permissionDecision: 'deny', permissionDecisionReason: ctx, additionalContext: ctx },\n });\n return;\n }\n\n outputJson({ systemMessage: cveTag + ' ' + fileShort + ' \\\\u2192 clean' });\n } catch (err) {\n process.stderr.write('[synkro] cveGuard error: ' + String(err) + '\\\\n');\n outputEmpty();\n }\n}\n\nmain();\n`;\n\n\n// ─── CC PreToolUse Bash/Read/Grep/Glob judge (TypeScript) ───\n\nexport const BASH_JUDGE_TS = `#!/usr/bin/env bun\nimport {\n loadJwt, ensureFreshJwt, detectRepo, loadConfig, route, tag, localGrade,\n parseVerdict, dispatchCapture, dispatchFinding, ruleMode, postWithRetry, readStdin,\n extractTranscript, readLastPrompt, log,\n outputJson, outputEmpty, setupCursorHookSignals, isShellTool, hookSessionId, GATEWAY_URL,\n type HookConfig, type Rule,\n} from './_synkro-common.ts';\n\nconst TOP_NPM_PKGS = new Set([\n 'express','react','lodash','axios','chalk','commander','debug','dotenv','webpack',\n 'typescript','moment','uuid','cors','body-parser','mongoose','jsonwebtoken','bcrypt',\n 'nodemon','eslint','prettier','jest','mocha','chai','sinon','supertest','request',\n 'async','bluebird','underscore','ramda','rxjs','socket.io','redis','pg','mysql',\n 'sequelize','knex','prisma','next','nuxt','vue','svelte','angular','ember',\n 'react-dom','react-router','react-redux','redux','mobx','formik','yup','zod',\n 'ajv','joi','helmet','morgan','passport','cookie-parser','express-session',\n 'multer','sharp','jimp','puppeteer','playwright','cheerio','got','node-fetch',\n 'superagent','inquirer','ora','yargs','minimist','glob','rimraf','mkdirp',\n 'fs-extra','chokidar','ws','graphql','apollo-server','fastify','koa','hapi',\n 'nest','drizzle-orm','typeorm','mikro-orm','bull','bullmq','ioredis','kafkajs',\n 'amqplib','nodemailer','handlebars','ejs','pug','marked','highlight.js',\n 'dayjs','date-fns','luxon','nanoid','cuid','short-uuid','colors','picocolors',\n 'winston','pino','bunyan','semver','tar','archiver','unzipper','crypto-js',\n 'bcryptjs','argon2','jose','openai','anthropic','langchain','tensorflow',\n 'onnxruntime-node','sharp','canvas','three','d3','chart.js','echarts',\n 'tailwindcss','postcss','autoprefixer','sass','less','styled-components',\n 'emotion','framer-motion','gsap','lottie-web','swiper','i18next',\n]);\n\nconst TOP_PYPI_PKGS = new Set([\n 'requests','flask','django','numpy','pandas','scipy','matplotlib','scikit-learn',\n 'tensorflow','torch','pytorch','keras','fastapi','uvicorn','gunicorn','celery',\n 'redis','sqlalchemy','alembic','pydantic','httpx','aiohttp','beautifulsoup4',\n 'scrapy','selenium','playwright','pillow','opencv-python','boto3','awscli',\n 'google-cloud-storage','azure-storage-blob','psycopg2','pymongo','motor',\n 'pytest','unittest2','mock','coverage','tox','black','flake8','mypy','ruff',\n 'isort','pylint','bandit','cryptography','paramiko','fabric','click','typer',\n 'rich','colorama','tqdm','loguru','python-dotenv','pyyaml','toml','orjson',\n 'ujson','marshmallow','attrs','dataclasses-json','jinja2','mako','arrow',\n 'pendulum','dateutil','pytz','regex','chardet','charset-normalizer',\n 'langchain','openai','anthropic','transformers','huggingface-hub','tokenizers',\n 'gradio','streamlit','dash','plotly','seaborn','bokeh','altair',\n]);\n\nfunction levenshtein(a: string, b: string): number {\n const m = a.length, n = b.length;\n if (Math.abs(m - n) > 2) return 3;\n const dp: number[][] = Array.from({ length: m + 1 }, (_, i) => {\n const row = new Array(n + 1).fill(0);\n row[0] = i;\n return row;\n });\n for (let j = 1; j <= n; j++) dp[0][j] = j;\n for (let i = 1; i <= m; i++) {\n for (let j = 1; j <= n; j++) {\n dp[i][j] = a[i - 1] === b[j - 1]\n ? dp[i - 1][j - 1]\n : 1 + Math.min(dp[i - 1][j], dp[i][j - 1], dp[i - 1][j - 1]);\n }\n }\n return dp[m][n];\n}\n\nfunction checkTyposquat(pkg: string, isPip: boolean): string | null {\n const topPkgs = isPip ? TOP_PYPI_PKGS : TOP_NPM_PKGS;\n if (topPkgs.has(pkg)) return null;\n const pkgLower = pkg.toLowerCase();\n for (const known of topPkgs) {\n const dist = levenshtein(pkgLower, known);\n if (dist > 0 && dist <= 2) return known;\n }\n return null;\n}\n\ninterface PkgMeta {\n deprecated?: string;\n weeklyDownloads?: number;\n}\n\nasync function main() {\n setupCursorHookSignals();\n try {\n const input = await readStdin();\n if (!input.trim()) { outputEmpty(); return; }\n\n const payload = JSON.parse(input);\n const toolName = payload.tool_name || '';\n if (!isShellTool(toolName)) {\n outputEmpty();\n return;\n }\n\n const toolInput = payload.tool_input || {};\n const sessionId = hookSessionId(payload);\n const toolUseId = payload.tool_use_id || '';\n const cwd = payload.cwd || '';\n const permissionMode = payload.permission_mode || '';\n const transcriptPath = payload.transcript_path || '';\n const gitRepo = detectRepo(cwd || '.');\n\n let command = '';\n switch (toolName) {\n case 'Bash':\n case 'Shell':\n case 'terminal':\n case 'run_terminal_cmd':\n case 'execute_command':\n command = toolInput.command || ''; break;\n case 'Read': command = 'cat ' + (toolInput.file_path || ''); break;\n case 'Grep': command = \"grep -r '\" + (toolInput.pattern || '') + \"' \" + (toolInput.path || '.'); break;\n case 'Glob': command = \"find . -name '\" + (toolInput.pattern || '') + \"'\"; break;\n }\n if (!command) { outputEmpty(); return; }\n\n const cmdShort = command.slice(0, 80);\n log('bashGuard checking: ' + cmdShort);\n\n let jwt = loadJwt();\n if (!jwt) { outputEmpty(); return; }\n jwt = await ensureFreshJwt(jwt);\n\n // ─── Install protection: CVE + typosquat + deprecated + popularity ───\n let installScanMsg = '';\n if (toolName === 'Bash') {\n const pkgInstallMatch = command.match(\n /(?:npm\\\\s+(?:install|i|add)|pnpm\\\\s+(?:add|install|i)|yarn\\\\s+add|bun\\\\s+(?:add|install|i)|(?:uv\\\\s+)?pip3?\\\\s+install|go\\\\s+get|cargo\\\\s+add|gem\\\\s+install|composer\\\\s+require)\\\\s+([^|;&><]+)/\n );\n const isPip = /(?:uv\\\\s+)?pip3?\\\\s+install/.test(command);\n const isGo = command.match(/^go\\\\s+get/);\n const isCargo = command.match(/^cargo\\\\s+add/);\n const isGem = command.match(/^gem\\\\s+install/);\n const isComposer = command.match(/^composer\\\\s+require/);\n if (pkgInstallMatch) {\n const rawArgs = pkgInstallMatch[1];\n const deps: Record<string, string> = {};\n const tokens = rawArgs.split(/\\\\s+/);\n let skipNext = false;\n for (const token of tokens) {\n if (skipNext) { skipNext = false; continue; }\n if (!token || !/^[@a-zA-Z]/.test(token)) continue;\n if (token.startsWith('-')) {\n if (/^--(python|target|prefix|root|constraint|requirement|index-url|extra-index-url|find-links|build|src|cache-dir|filter|workspace)$/.test(token)) skipNext = true;\n continue;\n }\n if (isPip) {\n const pipMatch = token.match(/^([a-zA-Z0-9_.-]+)(?:[=~!<>]=?(.+))?$/);\n if (pipMatch) {\n deps[pipMatch[1]] = pipMatch[2]?.replace(/^=/, '') || '*';\n continue;\n }\n }\n const atIdx = token.lastIndexOf('@');\n if (atIdx > 0) {\n deps[token.slice(0, atIdx)] = token.slice(atIdx + 1);\n } else {\n deps[token] = '*';\n }\n }\n\n if (Object.keys(deps).length > 0) {\n const warnings: string[] = [];\n const pkgMeta: Record<string, PkgMeta> = {};\n\n const metaLookups = Object.keys(deps).map(async (pkg) => {\n try {\n if (isPip) {\n const r = await fetch('https://pypi.org/pypi/' + encodeURIComponent(pkg) + '/json', { signal: AbortSignal.timeout(4000) });\n if (r.ok) {\n const d = await r.json() as any;\n if (deps[pkg] === '*' && d?.info?.version) deps[pkg] = d.info.version;\n const classifiers: string[] = d?.info?.classifiers || [];\n const isInactive = classifiers.some((c: string) => /Development Status :: [67]/.test(c));\n if (isInactive) pkgMeta[pkg] = { ...pkgMeta[pkg], deprecated: 'package marked as inactive/obsolete' };\n if (d?.info?.yanked) pkgMeta[pkg] = { ...pkgMeta[pkg], deprecated: d.info.yanked_reason || 'yanked from PyPI' };\n } else if (r.status === 404) {\n warnings.push('\\\\u26a0 ' + pkg + ': package not found on PyPI \\\\u2014 may not exist');\n }\n } else {\n const verSlug = deps[pkg] !== '*' ? deps[pkg] : 'latest';\n const [metaResp, dlResp] = await Promise.all([\n fetch('https://registry.npmjs.org/' + encodeURIComponent(pkg) + '/' + verSlug, { signal: AbortSignal.timeout(4000) }),\n fetch('https://api.npmjs.org/downloads/point/last-week/' + encodeURIComponent(pkg), { signal: AbortSignal.timeout(4000) }),\n ]);\n if (metaResp.ok) {\n const d = await metaResp.json() as any;\n if (deps[pkg] === '*' && d?.version) deps[pkg] = d.version;\n if (d?.deprecated) pkgMeta[pkg] = { ...pkgMeta[pkg], deprecated: d.deprecated };\n } else if (metaResp.status === 404) {\n warnings.push('\\\\u26a0 ' + pkg + ': package not found on npm \\\\u2014 may not exist');\n }\n if (dlResp.ok) {\n const d = await dlResp.json() as any;\n if (typeof d?.downloads === 'number') pkgMeta[pkg] = { ...pkgMeta[pkg], weeklyDownloads: d.downloads };\n }\n }\n } catch {}\n });\n await Promise.all(metaLookups);\n\n for (const pkg of Object.keys(deps)) {\n const similar = checkTyposquat(pkg, isPip);\n if (similar) {\n const dl = pkgMeta[pkg]?.weeklyDownloads;\n if (dl === undefined || dl < 1000) {\n warnings.push('\\\\u26a0 ' + pkg + ': possible typosquat of \"' + similar + '\"' + (dl !== undefined ? ' (' + dl + ' weekly downloads)' : '') + ' \\\\u2014 verify package name');\n }\n }\n }\n\n for (const [pkg, meta] of Object.entries(pkgMeta)) {\n if (meta.deprecated) {\n warnings.push('\\\\u26a0 ' + pkg + ': deprecated \\\\u2014 ' + meta.deprecated);\n }\n }\n\n if (!isPip) {\n for (const [pkg, meta] of Object.entries(pkgMeta)) {\n if (meta.weeklyDownloads !== undefined && meta.weeklyDownloads < 50 && !warnings.some(w => w.includes(pkg))) {\n warnings.push('\\\\u26a0 ' + pkg + ': very low adoption (' + meta.weeklyDownloads + ' weekly downloads) \\\\u2014 consider a more established alternative');\n }\n }\n }\n\n const manifestFile = isPip ? 'requirements.txt'\n : isGo ? 'go.mod'\n : isCargo ? 'Cargo.toml'\n : isGem ? 'Gemfile'\n : isComposer ? 'composer.json'\n : 'package.json';\n const manifestContent = isPip\n ? Object.entries(deps).map(([k, v]) => v === '*' ? k : k + '==' + v).join('\\\\n')\n : JSON.stringify({ dependencies: deps });\n\n try {\n const cveBody = { file_path: manifestFile, content: manifestContent, dependencies: deps };\n const cveResp = await fetch(GATEWAY_URL + '/api/v1/cve-scan', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json', Authorization: 'Bearer ' + jwt },\n body: JSON.stringify(cveBody),\n signal: AbortSignal.timeout(8000),\n }).then(r => r.json()) as any;\n\n const findings = Array.isArray(cveResp?.findings) ? cveResp.findings : [];\n const scannedPkgs = Object.entries(deps).map(([k, v]) => k + '@' + v).join(', ');\n\n if (findings.length > 0) {\n const top3 = findings.slice(0, 3).map((f: any) => {\n const id = f.cve || f.id || '?';\n const pkg = f.package || '?';\n const ver = f.version || '?';\n const title = f.title || f.summary || 'vulnerable';\n return '[' + id + '] ' + pkg + '@' + ver + ': ' + title;\n }).join('; ');\n const count = findings.length;\n const label = count === 1 ? 'advisory' : 'advisories';\n const cveMsg = '[synkro:installScan] ' + cmdShort + ' \\\\u2192 ' + count + ' ' + label;\n const ctx = 'CVE: ' + top3 + '\\\\nDo NOT install packages with known vulnerabilities. Use a patched version or a different package.'\n + (warnings.length > 0 ? '\\\\n' + warnings.join('\\\\n') : '');\n\n const config = await loadConfig(jwt);\n for (const f of findings) {\n dispatchFinding(jwt, {\n session_id: sessionId,\n file_path: command,\n finding_type: 'cve',\n finding_id: f.cve || f.id || f.package,\n severity: typeof f.severity === 'number' ? (f.severity >= 9 ? 'critical' : f.severity >= 7 ? 'high' : f.severity >= 4 ? 'medium' : 'low') : (f.severity || 'medium'),\n status: 'open',\n detail: f.details || f.summary || null,\n description: f.summary || null,\n package_name: f.package || null,\n package_version: f.version || null,\n fixed_version: f.fixed || null,\n aliases: f.aliases || [],\n references: f.references || [],\n }, config.captureDepth);\n }\n\n outputJson({\n systemMessage: cveMsg,\n hookSpecificOutput: { hookEventName: 'PreToolUse', permissionDecision: 'deny', permissionDecisionReason: ctx, additionalContext: ctx },\n });\n return;\n }\n\n const parts: string[] = ['[synkro:installScan] ' + scannedPkgs + ' \\\\u2192 clean, no known vulnerabilities'];\n if (warnings.length > 0) parts.push(...warnings);\n installScanMsg = parts.join('\\\\n');\n } catch (e) {\n log('bashGuard install scan failed: ' + String(e));\n if (warnings.length > 0) {\n installScanMsg = '[synkro:installScan] ' + warnings.join('\\\\n');\n }\n }\n }\n }\n }\n\n const transcript = extractTranscript(transcriptPath);\n const lastPrompt = readLastPrompt();\n\n const config = await loadConfig(jwt);\n const rt = await route(config);\n const tagStr = tag(rt, config);\n\n if (config.silent) {\n const msg = (installScanMsg ? installScanMsg + '\\\\n' : '') + tagStr + ' bashGuard \\\\u2192 skipped (silent mode)';\n outputJson({ systemMessage: msg, hookSpecificOutput: { hookEventName: 'PreToolUse', additionalContext: msg } });\n return;\n }\n\n if (rt === 'local') {\n const graderPrompt = [\n 'Working directory: ' + (cwd || '.'),\n 'Repo: ' + (gitRepo || 'unknown'),\n 'Command: ' + command,\n 'User intent (last human message): ' + (transcript.userIntent || 'none stated'),\n 'Last user prompt: ' + (lastPrompt || 'none'),\n 'Org rules: ' + JSON.stringify(config.rules),\n ].join('\\\\n');\n\n let gradeResp: string;\n try {\n gradeResp = await localGrade('bash', graderPrompt);\n } catch {\n outputEmpty();\n return;\n }\n\n const verdict = parseVerdict(gradeResp);\n const violatedRules = verdict.ruleId ? [verdict.ruleId] : [];\n\n if (!verdict.ok) {\n const mode = verdict.ruleMode || ruleMode(verdict.ruleId, config.rules);\n const guardReason = (verdict.ruleId ? '(' + verdict.ruleId + ') ' : '') + (verdict.reason || 'policy violation');\n\n if (mode === 'audit') {\n const reason = tagStr + ' bashGuard \\\\u2192 warning' + (verdict.ruleId ? ' (' + verdict.ruleId + ')' : '') + ': ' + (verdict.reason || 'policy violation');\n const combined = (installScanMsg ? installScanMsg + '\\\\n' : '') + reason;\n outputJson({ systemMessage: combined, hookSpecificOutput: { hookEventName: 'PreToolUse', additionalContext: combined } });\n dispatchCapture(jwt, 'bash', 'warning', verdict.severity || 'medium', verdict.category || 'security',\n toolName, gitRepo, sessionId, config.captureDepth, {\n command, reasoning: guardReason, rulesChecked: config.rules, violatedRules,\n recentUserMessages: transcript.recentUserMessages, ccModel: transcript.ccModel,\n });\n } else {\n const reason = tagStr + ' bashGuard \\\\u2192 blocked' + (verdict.ruleId ? ' (' + verdict.ruleId + ')' : '') + ': ' + (verdict.reason || 'policy violation') + '. Ask the user for explicit consent before retrying.';\n const combined = (installScanMsg ? installScanMsg + '\\\\n' : '') + reason;\n outputJson({\n systemMessage: combined,\n hookSpecificOutput: { hookEventName: 'PreToolUse', permissionDecision: 'deny', permissionDecisionReason: reason, additionalContext: combined },\n });\n dispatchCapture(jwt, 'bash', 'block', verdict.severity || 'critical', verdict.category || 'security',\n toolName, gitRepo, sessionId, config.captureDepth, {\n command, reasoning: guardReason, rulesChecked: config.rules, violatedRules,\n recentUserMessages: transcript.recentUserMessages, ccModel: transcript.ccModel,\n });\n }\n } else {\n const reason = tagStr + ' bashGuard \\\\u2192 pass: ' + (verdict.reason || 'no policy violations detected');\n const combined = (installScanMsg ? installScanMsg + '\\\\n' : '') + reason;\n outputJson({ systemMessage: combined, hookSpecificOutput: { hookEventName: 'PreToolUse', additionalContext: combined } });\n dispatchCapture(jwt, 'bash', 'pass', 'audit', verdict.category || 'trivial_utility',\n toolName, gitRepo, sessionId, config.captureDepth, {\n command, reasoning: verdict.reason || 'no policy violations detected',\n rulesChecked: config.rules, violatedRules: [],\n recentUserMessages: transcript.recentUserMessages, ccModel: transcript.ccModel,\n });\n }\n return;\n }\n\n // ─── Cloud grading ───\n const isHeadless = ['acceptEdits', 'bypassPermissions', 'plan', 'auto'].includes(permissionMode)\n || process.env.SYNKRO_HEADLESS === '1';\n\n const body: Record<string, any> = {\n hook_event: 'PreToolUse',\n tool_name: toolName,\n tool_input: toolInput,\n user_intent: transcript.userIntent || null,\n last_user_message: lastPrompt || null,\n recent_user_messages: transcript.recentUserMessages,\n recent_messages: transcript.recentMessages,\n recent_actions: transcript.recentActions,\n session_id: sessionId || null,\n tool_use_id: toolUseId || null,\n cwd: cwd || null,\n repo: gitRepo || null,\n permission_mode: permissionMode || null,\n headless: isHeadless,\n cc_model: transcript.ccModel || null,\n cc_usage: transcript.ccUsage || {},\n session_summary: transcript.sessionSummary || null,\n };\n\n const resp = await postWithRetry(GATEWAY_URL + '/api/v1/hook/judge', body, jwt, 8000);\n\n if (!resp) {\n log('bashGuard ' + cmdShort + ' \\\\u2192 error (timeout)');\n if (installScanMsg) {\n outputJson({ systemMessage: installScanMsg, hookSpecificOutput: { hookEventName: 'PreToolUse', additionalContext: installScanMsg } });\n } else { outputEmpty(); }\n return;\n }\n\n if (!resp.hook_response || typeof resp.hook_response !== 'object') {\n log('bashGuard ' + cmdShort + ' \\\\u2192 pass (no hook_response)');\n if (installScanMsg) {\n outputJson({ systemMessage: installScanMsg, hookSpecificOutput: { hookEventName: 'PreToolUse', additionalContext: installScanMsg } });\n } else { outputEmpty(); }\n return;\n }\n\n if (installScanMsg) {\n const existing = resp.hook_response.systemMessage || '';\n resp.hook_response.systemMessage = installScanMsg + (existing ? '\\\\n' + existing : '');\n if (resp.hook_response.hookSpecificOutput) {\n const existingCtx = resp.hook_response.hookSpecificOutput.additionalContext || '';\n resp.hook_response.hookSpecificOutput.additionalContext = installScanMsg + (existingCtx ? '\\\\n' + existingCtx : '');\n } else {\n resp.hook_response.hookSpecificOutput = { hookEventName: 'PreToolUse', additionalContext: resp.hook_response.systemMessage };\n }\n }\n outputJson(resp.hook_response);\n } catch (err) {\n process.stderr.write('[synkro] bashGuard error: ' + String(err) + '\\\\n');\n outputEmpty();\n }\n}\n\nmain();\n`;\n\n\n// ─── CC PreToolUse Agent subagent judge (TypeScript) ───\n\nexport const AGENT_JUDGE_TS = `#!/usr/bin/env bun\nimport {\n loadJwt, ensureFreshJwt, detectRepo, loadConfig, route, tag, localGrade,\n parseVerdict, dispatchCapture, ruleMode, postWithRetry, readStdin,\n extractTranscript, readLastPrompt, log,\n outputJson, outputEmpty, setupCursorHookSignals, isAgentTool, hookSessionId, GATEWAY_URL,\n type HookConfig, type Rule,\n} from './_synkro-common.ts';\n\nasync function main() {\n setupCursorHookSignals();\n try {\n const input = await readStdin();\n if (!input.trim()) { outputEmpty(); return; }\n\n const payload = JSON.parse(input);\n const toolName = payload.tool_name || '';\n if (!isAgentTool(toolName)) {\n outputEmpty();\n return;\n }\n\n const toolInput = payload.tool_input || {};\n const sessionId = hookSessionId(payload);\n const toolUseId = payload.tool_use_id || '';\n const cwd = payload.cwd || '';\n const permissionMode = payload.permission_mode || '';\n const transcriptPath = payload.transcript_path || '';\n const gitRepo = detectRepo(cwd || '.');\n\n const prompt = toolInput.prompt || '';\n const description = toolInput.description || '';\n const subagentType = toolInput.subagent_type || 'general-purpose';\n if (!prompt) { outputEmpty(); return; }\n\n const promptShort = prompt.slice(0, 80);\n log('agentGuard checking: ' + description + ' (' + subagentType + ')');\n\n let jwt = loadJwt();\n if (!jwt) { outputEmpty(); return; }\n jwt = await ensureFreshJwt(jwt);\n\n const transcript = extractTranscript(transcriptPath);\n const lastPrompt = readLastPrompt();\n\n const config = await loadConfig(jwt);\n const rt = await route(config);\n const tagStr = tag(rt, config);\n\n if (config.silent) {\n const msg = tagStr + ' agentGuard \\\\u2192 skipped (silent mode)';\n outputJson({ systemMessage: msg, hookSpecificOutput: { hookEventName: 'PreToolUse', additionalContext: msg } });\n return;\n }\n\n if (rt === 'local') {\n const graderPrompt = [\n 'Working directory: ' + (cwd || '.'),\n 'Repo: ' + (gitRepo || 'unknown'),\n 'Tool: Agent (subagent spawn)',\n 'Subagent type: ' + subagentType,\n 'Description: ' + description,\n 'Subagent prompt (first 4000 chars):',\n prompt.slice(0, 4000),\n 'User intent (last human message): ' + (transcript.userIntent || 'none stated'),\n 'Last user prompt: ' + (lastPrompt || 'none'),\n 'Org rules: ' + JSON.stringify(config.rules),\n ].join('\\\\n');\n\n let gradeResp: string;\n try {\n gradeResp = await localGrade('bash', graderPrompt);\n } catch {\n outputEmpty();\n return;\n }\n\n const verdict = parseVerdict(gradeResp);\n const agentContent = 'agent=' + subagentType + ' desc=' + description + ' prompt=' + prompt.slice(0, 2000);\n const violatedRules = verdict.ruleId ? [verdict.ruleId] : [];\n\n if (!verdict.ok) {\n const mode = verdict.ruleMode || ruleMode(verdict.ruleId, config.rules);\n const guardReason = (verdict.ruleId ? '(' + verdict.ruleId + ') ' : '') + (verdict.reason || 'policy violation');\n\n if (mode === 'audit') {\n const reason = tagStr + ' agentGuard \\\\u2192 warning' + (verdict.ruleId ? ' (' + verdict.ruleId + ')' : '') + ': ' + (verdict.reason || 'policy violation');\n outputJson({ systemMessage: reason, hookSpecificOutput: { hookEventName: 'PreToolUse', additionalContext: reason } });\n dispatchCapture(jwt, 'agent', 'warning', verdict.severity || 'medium', verdict.category || 'security',\n toolName, gitRepo, sessionId, config.captureDepth, {\n command: agentContent, reasoning: guardReason, rulesChecked: config.rules, violatedRules,\n recentUserMessages: transcript.recentUserMessages, ccModel: transcript.ccModel,\n });\n } else {\n const reason = tagStr + ' agentGuard \\\\u2192 blocked' + (verdict.ruleId ? ' (' + verdict.ruleId + ')' : '') + ': ' + (verdict.reason || 'policy violation') + '. Ask the user for explicit consent before retrying.';\n outputJson({\n systemMessage: reason,\n hookSpecificOutput: { hookEventName: 'PreToolUse', permissionDecision: 'deny', permissionDecisionReason: reason, additionalContext: reason },\n });\n dispatchCapture(jwt, 'agent', 'block', verdict.severity || 'critical', verdict.category || 'security',\n toolName, gitRepo, sessionId, config.captureDepth, {\n command: agentContent, reasoning: guardReason, rulesChecked: config.rules, violatedRules,\n recentUserMessages: transcript.recentUserMessages, ccModel: transcript.ccModel,\n });\n }\n } else {\n const reason = tagStr + ' agentGuard \\\\u2192 pass: ' + (verdict.reason || 'no policy violations detected');\n outputJson({ systemMessage: reason, hookSpecificOutput: { hookEventName: 'PreToolUse', additionalContext: reason } });\n dispatchCapture(jwt, 'agent', 'pass', 'audit', verdict.category || 'subagent_spawn',\n toolName, gitRepo, sessionId, config.captureDepth, {\n command: agentContent, reasoning: verdict.reason || 'no policy violations detected',\n rulesChecked: config.rules, violatedRules: [],\n recentUserMessages: transcript.recentUserMessages, ccModel: transcript.ccModel,\n });\n }\n return;\n }\n\n // ─── Cloud grading ───\n const isHeadless = ['acceptEdits', 'bypassPermissions', 'plan', 'auto'].includes(permissionMode)\n || process.env.SYNKRO_HEADLESS === '1';\n\n const body: Record<string, any> = {\n hook_event: 'PreToolUse',\n tool_name: toolName,\n tool_input: toolInput,\n user_intent: transcript.userIntent || null,\n last_user_message: lastPrompt || null,\n recent_user_messages: transcript.recentUserMessages,\n recent_messages: transcript.recentMessages,\n recent_actions: transcript.recentActions,\n session_id: sessionId || null,\n tool_use_id: toolUseId || null,\n cwd: cwd || null,\n repo: gitRepo || null,\n permission_mode: permissionMode || null,\n headless: isHeadless,\n cc_model: transcript.ccModel || null,\n cc_usage: transcript.ccUsage || {},\n session_summary: transcript.sessionSummary || null,\n };\n\n const resp = await postWithRetry(GATEWAY_URL + '/api/v1/hook/judge', body, jwt, 8000);\n\n if (!resp) {\n log('agentGuard ' + promptShort + ' \\\\u2192 error (timeout)');\n outputEmpty();\n return;\n }\n\n if (!resp.hook_response || typeof resp.hook_response !== 'object') {\n log('agentGuard ' + promptShort + ' \\\\u2192 pass (no hook_response)');\n outputEmpty();\n return;\n }\n\n outputJson(resp.hook_response);\n } catch (err) {\n process.stderr.write('[synkro] agentGuard error: ' + String(err) + '\\\\n');\n outputEmpty();\n }\n}\n\nmain();\n`;\n\n\n// ─── CC PreToolUse ExitPlanMode plan review (TypeScript) ───\n\nexport const PLAN_JUDGE_TS = `#!/usr/bin/env bun\nimport {\n loadJwt, ensureFreshJwt, detectRepo, loadConfig, route, tag, localGrade,\n parseVerdict, dispatchCapture, postWithRetry, readStdin, log,\n outputJson, outputEmpty, setupCursorHookSignals, isPlanTool, hookSessionId, GATEWAY_URL,\n} from './_synkro-common.ts';\nimport { existsSync, readFileSync, writeFileSync, readdirSync, statSync } from 'node:fs';\nimport { join } from 'node:path';\nimport { homedir } from 'node:os';\n\nfunction findLatestPlanInDir(plansDir: string): string | null {\n if (!existsSync(plansDir)) return null;\n try {\n const files = readdirSync(plansDir)\n .filter(f => f.endsWith('.md'))\n .map(f => ({ name: f, mtime: statSync(join(plansDir, f)).mtimeMs }))\n .sort((a, b) => b.mtime - a.mtime);\n return files.length > 0 ? join(plansDir, files[0].name) : null;\n } catch {\n return null;\n }\n}\n\nfunction findLatestPlan(): string | null {\n const dirs = [\n join(homedir(), '.claude', 'plans'),\n join(homedir(), '.cursor', 'plans'),\n ];\n let best: { path: string; mtime: number } | null = null;\n for (const dir of dirs) {\n const p = findLatestPlanInDir(dir);\n if (!p) continue;\n try {\n const mtime = statSync(p).mtimeMs;\n if (!best || mtime > best.mtime) best = { path: p, mtime };\n } catch {}\n }\n return best?.path ?? null;\n}\n\nfunction appendReviewToPlan(planFile: string, verdict: string): void {\n try {\n let content = readFileSync(planFile, 'utf-8');\n content = content.replace(/<!-- synkro-plan-review -->[\\\\s\\\\S]*?<!-- \\\\/synkro-plan-review -->/g, '').trimEnd();\n const now = new Date().toISOString().replace('T', ' ').slice(0, 16);\n content += '\\\\n\\\\n<!-- synkro-plan-review -->\\\\n\\\\n---\\\\n\\\\n**Synkro Plan Review** \\\\u2014 ' + now + '\\\\n\\\\n' + verdict + '\\\\n\\\\n<!-- /synkro-plan-review -->\\\\n';\n writeFileSync(planFile, content, 'utf-8');\n } catch {}\n}\n\nasync function main() {\n setupCursorHookSignals();\n try {\n const input = await readStdin();\n if (!input.trim()) { outputEmpty(); return; }\n\n const payload = JSON.parse(input);\n const toolName = payload.tool_name || '';\n if (!isPlanTool(toolName)) { outputEmpty(); return; }\n\n const planFile = findLatestPlan();\n if (!planFile) { outputEmpty(); return; }\n const plan = readFileSync(planFile, 'utf-8');\n if (plan.length < 20) { outputEmpty(); return; }\n\n const sessionId = hookSessionId(payload);\n const cwd = payload.cwd || '';\n const gitRepo = detectRepo(cwd || '.');\n\n const planShort = plan.slice(0, 80);\n log('planReview checking: ' + planShort + '...');\n\n let jwt = loadJwt();\n if (!jwt) { outputEmpty(); return; }\n jwt = await ensureFreshJwt(jwt);\n\n const config = await loadConfig(jwt);\n const rt = await route(config);\n const tagStr = tag(rt, config);\n\n if (config.silent) {\n outputJson({ systemMessage: tagStr + ' planReview \\\\u2192 skipped (silent mode)' });\n return;\n }\n\n if (rt === 'local') {\n const graderPrompt = [\n 'Working directory: ' + (cwd || '.'),\n 'Repo: ' + (gitRepo || 'unknown'),\n 'Plan:',\n plan.slice(0, 8000),\n 'Org rules: ' + JSON.stringify(config.rules),\n ].join('\\\\n');\n\n let gradeResp: string;\n try {\n gradeResp = await localGrade('plan', graderPrompt);\n } catch {\n outputEmpty();\n return;\n }\n\n const verdict = parseVerdict(gradeResp);\n const planContent = plan.slice(0, 2000);\n const violatedRules = verdict.ruleId ? [verdict.ruleId] : [];\n\n if (!verdict.ok) {\n const reviewMsg = (verdict.ruleId ? '(first: ' + verdict.ruleId + ') ' : '') + (verdict.reason || 'check org rules during implementation');\n appendReviewToPlan(planFile, '\\\\u26a0\\\\ufe0f Advisory \\\\u2014 ' + reviewMsg);\n outputJson({ systemMessage: tagStr + ' planReview \\\\u2192 ' + reviewMsg });\n dispatchCapture(jwt, 'plan_review', 'advisory', verdict.severity || 'medium', verdict.category || 'general',\n 'ExitPlanMode', gitRepo, sessionId, config.captureDepth, {\n command: planContent, reasoning: verdict.reason || 'check org rules',\n rulesChecked: config.rules, violatedRules,\n });\n } else {\n const reviewMsg = verdict.reason || 'no relevant org rules for this plan';\n appendReviewToPlan(planFile, '\\\\u2705 Clean \\\\u2014 ' + reviewMsg);\n outputJson({ systemMessage: tagStr + ' planReview \\\\u2192 clean: ' + reviewMsg });\n dispatchCapture(jwt, 'plan_review', 'clean', 'audit', verdict.category || 'general',\n 'ExitPlanMode', gitRepo, sessionId, config.captureDepth, {\n command: planContent, reasoning: reviewMsg,\n rulesChecked: config.rules, violatedRules: [],\n });\n }\n return;\n }\n\n // ─── Cloud grading ───\n const body = {\n hook_event: 'PreToolUse',\n tool_name: 'ExitPlanMode',\n tool_input: { plan: plan.slice(0, 16000) },\n session_id: sessionId || null,\n cwd: cwd || null,\n repo: gitRepo || null,\n };\n\n const resp = await postWithRetry(GATEWAY_URL + '/api/v1/hook/judge', body, jwt, 12000);\n\n if (!resp) {\n log('planReview \\\\u2192 error (timeout)');\n outputEmpty();\n return;\n }\n\n const hookResp = resp?.hook_response;\n if (!hookResp) { outputEmpty(); return; }\n\n const decision = hookResp?.hookSpecificOutput?.permissionDecision;\n if (decision) {\n const reason = hookResp?.hookSpecificOutput?.permissionDecisionReason || 'check org rules';\n appendReviewToPlan(planFile, '\\\\u26a0\\\\ufe0f Advisory \\\\u2014 ' + reason);\n outputJson({ systemMessage: tagStr + ' planReview \\\\u2192 advisory: ' + reason });\n } else {\n const cloudMsg = hookResp.systemMessage || '';\n if (cloudMsg) appendReviewToPlan(planFile, '\\\\u2705 ' + cloudMsg);\n outputJson(hookResp);\n }\n } catch (err) {\n process.stderr.write('[synkro] planReview error: ' + String(err) + '\\\\n');\n outputEmpty();\n }\n}\n\nmain();\n`;\n\n\n// ─── CC SessionEnd stop summary (TypeScript) ───\n\nexport const STOP_SUMMARY_TS = `#!/usr/bin/env bun\nimport {\n loadJwt, detectRepo, loadConfig, tag, readStdin, aggregateUsage,\n outputJson, outputEmpty, appendLocalTelemetry, setupCursorHookSignals, hookSessionId, GATEWAY_URL,\n} from './_synkro-common.ts';\n\nasync function main() {\n setupCursorHookSignals();\n try {\n const input = await readStdin();\n if (!input.trim()) { outputEmpty(); return; }\n\n const payload = JSON.parse(input);\n const sessionId = hookSessionId(payload);\n if (!sessionId) { outputEmpty(); return; }\n\n const cwd = payload.cwd || '';\n const transcriptPath = payload.transcript_path || '';\n const gitRepo = detectRepo(cwd || '.');\n\n let jwt = loadJwt();\n if (!jwt) { outputEmpty(); return; }\n\n if (transcriptPath) {\n const usage = aggregateUsage(transcriptPath);\n if (usage.totals.in + usage.totals.out > 0) {\n const usageBody = {\n capture_type: 'usage_tick',\n event_id: 'usage_' + Date.now() + '_' + process.pid,\n hook_type: 'stop',\n verdict: 'allow',\n severity: 'none',\n model: usage.model || 'unknown',\n cc_model: usage.model || '',\n cc_usage: {\n input_tokens: usage.totals.in,\n output_tokens: usage.totals.out,\n cache_creation_input_tokens: usage.totals.cw,\n cache_read_input_tokens: usage.totals.cr,\n },\n ...(gitRepo ? { repo: gitRepo } : {}),\n ...(sessionId ? { session_id: sessionId } : {}),\n };\n appendLocalTelemetry(usageBody);\n fetch(GATEWAY_URL + '/api/v1/hook/capture', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json', Authorization: 'Bearer ' + jwt },\n body: JSON.stringify(usageBody),\n signal: AbortSignal.timeout(3000),\n }).catch(() => {});\n }\n }\n\n let resp: any;\n try {\n const r = await fetch(GATEWAY_URL + '/api/v1/cli/session-summary?session_id=' + encodeURIComponent(sessionId), {\n headers: { Authorization: 'Bearer ' + jwt },\n signal: AbortSignal.timeout(3000),\n });\n resp = await r.json();\n } catch {\n outputEmpty();\n return;\n }\n\n const edits = resp?.edits_scanned || 0;\n const findings = resp?.findings || 0;\n const autoFixed = resp?.auto_fixed || 0;\n const open = resp?.open || 0;\n\n if (!edits) { outputEmpty(); return; }\n\n const config = await loadConfig(jwt);\n const tagStr = tag('local', config);\n\n if (!findings) {\n outputJson({ systemMessage: tagStr + ' stop \\\\u2192 0 issues across ' + edits + ' edit(s), session complete' });\n } else {\n outputJson({ systemMessage: tagStr + ' stop \\\\u2192 ' + findings + ' finding(s): ' + autoFixed + ' auto-fixed, ' + open + ' open' });\n }\n } catch (err) {\n process.stderr.write('[synkro] stopSummary error: ' + String(err) + '\\\\n');\n outputEmpty();\n }\n}\n\nmain();\n`;\n\n\n// ─── CC SessionStart (TypeScript) ───\n\nexport const SESSION_START_TS = `#!/usr/bin/env bun\nimport {\n loadJwt, detectRepo, channelUp, tag, readStdin,\n outputJson, outputEmpty, setupCursorHookSignals, hookSessionId, GATEWAY_URL,\n type HookConfig,\n} from './_synkro-common.ts';\n\nasync function main() {\n setupCursorHookSignals();\n try {\n const input = await readStdin();\n if (!input.trim()) { outputEmpty(); return; }\n\n const payload = JSON.parse(input);\n const cwd = payload.cwd || '';\n const sessionId = hookSessionId(payload);\n const gitRepo = detectRepo(cwd || '.');\n\n let jwt = loadJwt();\n\n const isChannelUp = await channelUp();\n const rt = isChannelUp ? 'local' : 'cloud';\n\n let policyName = '';\n let silent = false;\n let openFindings = 0;\n\n if (jwt) {\n try {\n const url = GATEWAY_URL + '/api/v1/hook/config?session_id=' + encodeURIComponent(sessionId || '') + '&repo=' + encodeURIComponent(gitRepo || '');\n const r = await fetch(url, {\n headers: { Authorization: 'Bearer ' + jwt },\n signal: AbortSignal.timeout(3000),\n });\n const data = await r.json() as any;\n silent = data.silent_mode === true || data.silent_mode === 'true';\n policyName = data.active_policy_name || '';\n openFindings = data.session_context?.open_findings || 0;\n } catch {}\n }\n\n const fakeConfig: HookConfig = { captureDepth: 'local_only', tier: 'standard', silent, policyName, rules: [] };\n const tagStr = tag(rt, fakeConfig);\n const routeLine = tagStr + ' inference: ' + (isChannelUp ? 'local-cc (channel reachable on 127.0.0.1:8929)' : 'cloud (local-cc channel not reachable)');\n\n if (!jwt) {\n outputJson({ systemMessage: routeLine });\n return;\n }\n\n if (!openFindings) {\n outputJson({ systemMessage: routeLine });\n } else if (openFindings === 1) {\n outputJson({ systemMessage: routeLine + '\\\\n' + tagStr + ' session start \\\\u2192 1 open finding in this repo from a prior session.' });\n } else {\n outputJson({ systemMessage: routeLine + '\\\\n' + tagStr + ' session start \\\\u2192 ' + openFindings + ' open findings in this repo from prior sessions.' });\n }\n } catch (err) {\n process.stderr.write('[synkro] sessionStart error: ' + String(err) + '\\\\n');\n outputEmpty();\n }\n}\n\nmain();\n`;\n\n\n// ─── CC PostToolUse Bash followup (TypeScript) ───\n\nexport const BASH_FOLLOWUP_TS = `#!/usr/bin/env bun\nimport {\n loadJwt, loadConfig, readStdin, hashCommand, consentGrant, consentHasActive, consentConsume,\n outputEmpty, appendLocalTelemetry, setupCursorHookSignals, isShellTool, hookSessionId, GATEWAY_URL,\n} from './_synkro-common.ts';\n\nasync function main() {\n setupCursorHookSignals();\n try {\n const input = await readStdin();\n if (!input.trim()) { outputEmpty(); return; }\n\n const payload = JSON.parse(input);\n const toolName = payload.tool_name || '';\n const shellCmd = typeof payload.command === 'string' ? payload.command : (payload.tool_input?.command || '');\n if (!isShellTool(toolName) && !shellCmd) { outputEmpty(); return; }\n\n const jwt = loadJwt();\n if (!jwt) { outputEmpty(); return; }\n\n const sessionId = hookSessionId(payload);\n const toolUseId = payload.tool_use_id || payload.tool_call_id || 'cursor-shell';\n if (!sessionId) { outputEmpty(); return; }\n\n let isError = payload.tool_result?.is_error === true;\n try {\n const out = JSON.parse(payload.tool_output || '{}');\n if (out.exitCode !== 0 || out.is_error === true) isError = true;\n } catch {}\n const cmd = shellCmd;\n const cmdHash = cmd ? hashCommand(cmd) : '';\n\n if (cmdHash && sessionId) {\n if (!isError) {\n consentConsume(sessionId, cmdHash);\n } else {\n if (!consentHasActive(sessionId, cmdHash)) {\n consentGrant(sessionId, cmdHash);\n }\n }\n }\n\n const body = {\n capture_type: 'bash_followup',\n session_id: sessionId,\n tool_use_id: toolUseId,\n is_error: isError,\n command_hash: cmdHash,\n };\n\n appendLocalTelemetry(body);\n\n const config = await loadConfig(jwt);\n if (config.captureDepth !== 'local_only') {\n fetch(GATEWAY_URL + '/api/v1/hook/capture', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json', Authorization: 'Bearer ' + jwt },\n body: JSON.stringify(body),\n signal: AbortSignal.timeout(3000),\n }).catch(() => {});\n }\n\n outputEmpty();\n } catch {\n outputEmpty();\n }\n}\n\nmain();\n`;\n\n\n// ─── CC Stop transcript sync (TypeScript) ───\n\nexport const TRANSCRIPT_SYNC_TS = `#!/usr/bin/env bun\nimport {\n loadJwt, detectRepo, readStdin, aggregateUsage, appendLocalTelemetry,\n outputEmpty, setupCursorHookSignals, hookSessionId, GATEWAY_URL,\n} from './_synkro-common.ts';\nimport { existsSync, readFileSync, writeFileSync, mkdirSync } from 'node:fs';\nimport { join, dirname } from 'node:path';\nimport { homedir } from 'node:os';\n\nasync function main() {\n setupCursorHookSignals();\n try {\n const input = await readStdin();\n if (!input.trim()) { outputEmpty(); return; }\n\n const payload = JSON.parse(input);\n const sessionId = hookSessionId(payload);\n const transcriptPath = payload.transcript_path || '';\n const cwd = payload.cwd || '';\n\n if (!sessionId || !transcriptPath || !existsSync(transcriptPath)) {\n outputEmpty();\n return;\n }\n\n const jwt = loadJwt();\n if (!jwt) { outputEmpty(); return; }\n\n const usage = aggregateUsage(transcriptPath);\n if (usage.totals.in + usage.totals.out > 0) {\n const usageBody = {\n capture_type: 'usage_tick',\n event_id: 'usage_' + Date.now() + '_' + process.pid,\n hook_type: 'stop',\n verdict: 'allow',\n severity: 'none',\n model: usage.model || 'unknown',\n cc_model: usage.model || '',\n cc_usage: {\n input_tokens: usage.totals.in,\n output_tokens: usage.totals.out,\n cache_creation_input_tokens: usage.totals.cw,\n cache_read_input_tokens: usage.totals.cr,\n },\n session_id: sessionId,\n };\n appendLocalTelemetry(usageBody);\n fetch(GATEWAY_URL + '/api/v1/hook/capture', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json', Authorization: 'Bearer ' + jwt },\n body: JSON.stringify(usageBody),\n signal: AbortSignal.timeout(3000),\n }).catch(() => {});\n }\n\n if (process.env.SYNKRO_TRANSCRIPT_CONSENT === 'no') { outputEmpty(); return; }\n\n const gitRepo = detectRepo(cwd || '.');\n if (!gitRepo) { outputEmpty(); return; }\n\n let captureDepth = 'local_only';\n try {\n const r = await fetch(GATEWAY_URL + '/api/v1/hook/config', {\n headers: { Authorization: 'Bearer ' + jwt },\n signal: AbortSignal.timeout(3000),\n });\n const data = await r.json() as any;\n captureDepth = data.capture_depth || 'local_only';\n } catch {}\n\n if (captureDepth === 'local_only') { outputEmpty(); return; }\n\n const offsetDir = join(homedir(), '.synkro', '.transcript-offsets');\n mkdirSync(offsetDir, { recursive: true });\n const offsetFile = join(offsetDir, sessionId);\n let offset = 0;\n if (existsSync(offsetFile)) {\n try { offset = parseInt(readFileSync(offsetFile, 'utf-8').trim(), 10) || 0; } catch {}\n }\n\n const raw = readFileSync(transcriptPath, 'utf-8');\n const allLines = raw.split('\\\\n').filter(l => l.trim());\n const totalLines = allLines.length;\n\n if (totalLines <= offset) { outputEmpty(); return; }\n\n let startIdx = offset;\n const delta = totalLines - offset;\n if (delta > 200) startIdx = totalLines - 200;\n\n const messages: any[] = [];\n for (let i = startIdx; i < totalLines; i++) {\n try {\n const entry = JSON.parse(allLines[i]);\n if (entry.type !== 'user' && entry.type !== 'assistant') continue;\n const content = entry.message?.content;\n let text = '';\n if (typeof content === 'string') text = content.slice(0, 8000);\n else if (Array.isArray(content)) {\n text = content.map((c: any) => {\n if (typeof c === 'string') return c;\n if (c?.type === 'text') return c.text || '';\n return '';\n }).join(' ').slice(0, 8000);\n }\n\n const msg: any = { message_index: i, type: entry.type, content: text };\n if (entry.type === 'assistant') {\n const toolCalls = (Array.isArray(content) ? content : [])\n .filter((c: any) => c?.type === 'tool_use')\n .map((c: any) => ({ name: c.name, input: JSON.stringify(c.input || {}).slice(0, 500), id: c.id }));\n if (toolCalls.length > 0) msg.tool_calls = toolCalls;\n msg.model = entry.message?.model || null;\n const u = entry.message?.usage;\n if (u) msg.usage = { input_tokens: u.input_tokens, output_tokens: u.output_tokens, cache_creation_input_tokens: u.cache_creation_input_tokens, cache_read_input_tokens: u.cache_read_input_tokens };\n }\n messages.push(msg);\n } catch {}\n }\n\n writeFileSync(offsetFile, String(totalLines), 'utf-8');\n\n if (messages.length === 0) { outputEmpty(); return; }\n\n const syncBody = {\n repo: gitRepo,\n sessions: [{ cc_session_id: sessionId, messages }],\n };\n fetch(GATEWAY_URL + '/api/v1/cli/sync-transcripts', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json', Authorization: 'Bearer ' + jwt },\n body: JSON.stringify(syncBody),\n signal: AbortSignal.timeout(10000),\n }).catch(() => {});\n\n outputEmpty();\n } catch {\n outputEmpty();\n }\n}\n\nmain();\n`;\n\n\n// ─── CC UserPromptSubmit (TypeScript) ───\n\nexport const USER_PROMPT_SUBMIT_TS = `#!/usr/bin/env bun\nimport { readStdin, appendLocalTelemetry, aggregateUsage, outputEmpty, setupCursorHookSignals, hookSessionId } from './_synkro-common.ts';\nimport { writeFileSync, mkdirSync } from 'node:fs';\nimport { join, dirname } from 'node:path';\nimport { homedir } from 'node:os';\n\nasync function main() {\n setupCursorHookSignals();\n try {\n const input = await readStdin();\n if (!input.trim()) { outputEmpty(); return; }\n const payload = JSON.parse(input);\n const msg = payload.message || payload.prompt || payload.content || '';\n if (msg) {\n const promptFile = join(homedir(), '.synkro', '.last-prompt');\n mkdirSync(dirname(promptFile), { recursive: true });\n writeFileSync(promptFile, msg, 'utf-8');\n }\n\n const sessionId = hookSessionId(payload);\n const transcriptPath = payload.transcript_path || '';\n if (sessionId && transcriptPath) {\n const usage = aggregateUsage(transcriptPath);\n if (usage.totals.in + usage.totals.out > 0) {\n appendLocalTelemetry({\n capture_type: 'usage_tick',\n event_id: 'usage_' + Date.now() + '_' + process.pid,\n hook_type: 'prompt_submit',\n session_id: sessionId,\n model: usage.model || 'unknown',\n cc_model: usage.model || '',\n cc_usage: {\n input_tokens: usage.totals.in,\n output_tokens: usage.totals.out,\n cache_creation_input_tokens: usage.totals.cw,\n cache_read_input_tokens: usage.totals.cr,\n },\n });\n }\n }\n outputEmpty();\n } catch {\n outputEmpty();\n }\n}\n\nmain();\n`;\n\n\n// ─── Cursor IDE TypeScript adapter scripts ───\n\nexport const CURSOR_BASH_JUDGE_TS = `#!/usr/bin/env bun\nimport {\n loadJwt, ensureFreshJwt, detectRepo, loadConfig, route, tag, localGrade,\n parseVerdict, dispatchCapture, ruleMode, postWithRetry, readStdin,\n extractTranscript, readLastPrompt, log, GATEWAY_URL,\n type Rule,\n} from './_synkro-common.ts';\n\n// Cursor beforeShellExecution timeout is 15s; stay under it (JWT refresh + grade).\nconst CURSOR_GRADE_TIMEOUT_MS = 7500;\nconst CURSOR_CLOUD_TIMEOUT_MS = 6000;\n\nlet hookDone = false;\n\nfunction finishAllow(): never {\n if (!hookDone) {\n hookDone = true;\n try { process.stdout.write('{}\\\\n'); } catch {}\n }\n process.exit(0);\n}\n\nfunction finishWith(payload: Record<string, unknown>): never {\n hookDone = true;\n process.stdout.write(JSON.stringify(payload) + '\\\\n');\n process.exit(0);\n}\n\nprocess.on('SIGTERM', () => finishAllow());\n\nconst SHELL_TOOL_NAMES = new Set(['Bash', 'Shell', 'terminal', 'run_terminal_cmd', 'execute_command']);\nconst BASH_PRE_TOOL_NAMES = new Set(['Bash', 'Shell', 'Read', 'Grep', 'Glob', ...SHELL_TOOL_NAMES]);\n\nfunction extractCommand(payload: Record<string, unknown>): { command: string; toolName: string } {\n const direct = typeof payload.command === 'string' ? payload.command : '';\n if (direct) return { command: direct, toolName: 'Bash' };\n\n const toolName = typeof payload.tool_name === 'string' ? payload.tool_name : '';\n if (!BASH_PRE_TOOL_NAMES.has(toolName)) return { command: '', toolName };\n\n const toolInput = (payload.tool_input && typeof payload.tool_input === 'object')\n ? payload.tool_input as Record<string, unknown>\n : {};\n\n let command = '';\n switch (toolName) {\n case 'Bash':\n case 'Shell':\n case 'terminal':\n case 'run_terminal_cmd':\n case 'execute_command':\n command = String(toolInput.command ?? '');\n break;\n case 'Read':\n command = 'cat ' + String(toolInput.file_path ?? toolInput.path ?? '');\n break;\n case 'Grep':\n command = \"grep -r '\" + String(toolInput.pattern ?? '') + \"' \" + String(toolInput.path ?? '.');\n break;\n case 'Glob':\n command = \"find . -name '\" + String(toolInput.pattern ?? '') + \"'\";\n break;\n }\n return { command, toolName: toolName || 'Bash' };\n}\n\nasync function main() {\n try {\n const input = await readStdin();\n if (!input.trim()) finishAllow();\n\n const payload = JSON.parse(input) as Record<string, unknown>;\n const { command, toolName } = extractCommand(payload);\n if (!command) finishAllow();\n\n const cwd = typeof payload.cwd === 'string' ? payload.cwd : '';\n const sessionId = String(payload.conversation_id ?? payload.session_id ?? '');\n const transcriptPath = typeof payload.transcript_path === 'string' ? payload.transcript_path : '';\n const repo = detectRepo(cwd || '.');\n\n const cmdShort = command.slice(0, 80);\n log('bashGuard checking: ' + cmdShort);\n\n let jwt = loadJwt();\n if (!jwt) finishAllow();\n jwt = await ensureFreshJwt(jwt);\n\n const transcript = extractTranscript(transcriptPath);\n const lastPrompt = readLastPrompt();\n\n const config = await loadConfig(jwt);\n if (config.silent) finishAllow();\n\n const rt = await route(config);\n const tagStr = tag(rt, config);\n\n if (rt === 'local') {\n const rulesBlock = config.rules.map((r: Rule, i: number) =>\n (i + 1) + '. [' + r.rule_id + '] (' + r.severity + '/' + r.mode + ') ' + r.text\n ).join('\\\\n');\n\n const graderPrompt = [\n 'RULES:',\n rulesBlock || '(none)',\n '',\n 'COMMAND TO EVALUATE:',\n command,\n '',\n 'User intent (last human message): ' + (transcript.userIntent || lastPrompt || 'none stated'),\n 'Last user prompt: ' + (lastPrompt || 'none'),\n ].join('\\\\n');\n\n let gradeResp: string;\n try {\n gradeResp = await localGrade('bash', graderPrompt, CURSOR_GRADE_TIMEOUT_MS);\n } catch (e) {\n log('bashGuard ' + cmdShort + ' → pass (grade unavailable): ' + String(e));\n finishAllow();\n }\n\n const verdict = parseVerdict(gradeResp);\n\n if (!verdict.ok) {\n const mode = verdict.ruleMode || ruleMode(verdict.ruleId, config.rules);\n const guardReason = (verdict.ruleId ? '(' + verdict.ruleId + ') ' : '') + (verdict.reason || 'policy violation');\n\n if (mode !== 'audit') {\n dispatchCapture(jwt, 'bash', 'block', verdict.severity || 'critical', verdict.category || 'security',\n 'Bash', repo, sessionId, config.captureDepth, {\n command, reasoning: guardReason,\n rulesChecked: config.rules, violatedRules: verdict.ruleId ? [verdict.ruleId] : [],\n });\n finishWith({\n permission: 'deny',\n user_message: tagStr + ' bashGuard → block: ' + guardReason,\n agent_message: 'Synkro safety judge. Reasoning: ' + (verdict.reason || guardReason),\n });\n }\n\n dispatchCapture(jwt, 'bash', 'warning', verdict.severity || 'medium', verdict.category || 'security',\n 'Bash', repo, sessionId, config.captureDepth, {\n command, reasoning: guardReason,\n rulesChecked: config.rules, violatedRules: verdict.ruleId ? [verdict.ruleId] : [],\n });\n } else {\n dispatchCapture(jwt, 'bash', 'pass', 'audit', verdict.category || 'clean',\n 'Bash', repo, sessionId, config.captureDepth, {\n command, reasoning: verdict.reason || 'no policy violations detected',\n rulesChecked: config.rules, violatedRules: [],\n });\n }\n\n log('bashGuard ' + cmdShort + ' → pass');\n finishAllow();\n }\n\n const body: Record<string, any> = {\n hook_event: 'PreToolUse',\n tool_name: toolName || 'Bash',\n tool_input: { command },\n response_format: 'cursor',\n user_intent: transcript.userIntent || null,\n last_user_message: lastPrompt || null,\n recent_user_messages: transcript.recentUserMessages,\n recent_messages: transcript.recentMessages,\n session_id: sessionId || null,\n cwd: cwd || null,\n repo: repo || null,\n };\n\n const resp = await postWithRetry(GATEWAY_URL + '/api/v1/hook/judge', body, jwt, CURSOR_CLOUD_TIMEOUT_MS);\n\n if (!resp) {\n log('bashGuard ' + cmdShort + ' → pass (cloud timeout)');\n finishAllow();\n }\n\n if (resp.hook_response) {\n finishWith(resp.hook_response as Record<string, unknown>);\n }\n log('bashGuard ' + cmdShort + ' → pass (no hook_response)');\n finishAllow();\n } catch (e) {\n log('bashGuard error: ' + String(e));\n finishAllow();\n }\n}\n\nmain().catch((e) => {\n log('bashGuard fatal: ' + String(e));\n finishAllow();\n});`;\n\nexport const CURSOR_EDIT_PRECHECK_TS = `#!/usr/bin/env bun\nimport {\n loadJwt, ensureFreshJwt, detectRepo, loadConfig, route, tag, localGrade,\n parseVerdict, dispatchCapture, ruleMode, postWithRetry, readStdin,\n log, GATEWAY_URL,\n type Rule,\n} from './_synkro-common.ts';\nimport { basename } from 'node:path';\n\nconst CURSOR_GRADE_TIMEOUT_MS = 7500;\nconst CURSOR_CLOUD_TIMEOUT_MS = 8000;\n\nlet hookDone = false;\n\nfunction finishAllow(): never {\n if (!hookDone) {\n hookDone = true;\n try { process.stdout.write('{}\\\\n'); } catch {}\n }\n process.exit(0);\n}\n\nfunction finishWith(payload: Record<string, unknown>): never {\n hookDone = true;\n process.stdout.write(JSON.stringify(payload) + '\\\\n');\n process.exit(0);\n}\n\nprocess.on('SIGTERM', () => finishAllow());\n\nasync function main() {\n try {\n const input = await readStdin();\n if (!input.trim()) finishAllow();\n\n const payload = JSON.parse(input);\n const toolName = payload.tool_name || '';\n const toolInput = payload.tool_input || {};\n const cwd = payload.cwd || '';\n const sessionId = payload.conversation_id || '';\n\n const filePath = toolInput.file_path || toolInput.path || toolInput.target_file || '';\n const content = toolInput.content || toolInput.new_string || toolInput.code_edit || '';\n if (!filePath) finishAllow();\n\n const fileShort = basename(filePath);\n log('editGuard checking: ' + fileShort);\n\n const repo = detectRepo(cwd || '.');\n\n let jwt = loadJwt();\n if (!jwt) finishAllow();\n jwt = await ensureFreshJwt(jwt);\n\n const config = await loadConfig(jwt);\n if (config.silent) finishAllow();\n\n const rt = await route(config);\n const tagStr = tag(rt, config);\n\n if (rt === 'local') {\n const contentShort = content.slice(0, 4000);\n const rulesBlock = config.rules.map((r: Rule, i: number) =>\n (i + 1) + '. [' + r.rule_id + '] (' + r.severity + '/' + r.mode + ') ' + r.text\n ).join('\\\\n');\n\n const graderPrompt = [\n 'RULES:',\n rulesBlock || '(none)',\n '',\n 'FILE: ' + filePath,\n '',\n 'CONTENT TO EVALUATE (first 4000 chars):',\n contentShort,\n ].join('\\\\n');\n\n let gradeResp: string;\n try {\n gradeResp = await localGrade('edit', graderPrompt, CURSOR_GRADE_TIMEOUT_MS);\n } catch (e) {\n log('editGuard ' + fileShort + ' → pass (grade unavailable): ' + String(e));\n finishAllow();\n }\n\n const verdict = parseVerdict(gradeResp);\n const editContent = 'file=' + filePath + ' content=' + content.slice(0, 2000);\n\n if (!verdict.ok) {\n const mode = verdict.ruleMode || ruleMode(verdict.ruleId, config.rules);\n const guardReason = (verdict.ruleId ? '(' + verdict.ruleId + ') ' : '') + (verdict.reason || 'policy violation');\n\n if (mode !== 'audit') {\n dispatchCapture(jwt, 'edit', 'block', verdict.severity || 'critical', verdict.category || 'security',\n toolName || 'Edit', repo, sessionId, config.captureDepth, {\n command: editContent, reasoning: guardReason,\n rulesChecked: config.rules, violatedRules: verdict.ruleId ? [verdict.ruleId] : [],\n });\n finishWith({\n permission: 'deny',\n user_message: tagStr + ' editGuard ' + fileShort + ' → block: ' + guardReason,\n agent_message: 'Synkro safety judge. Reasoning: ' + (verdict.reason || guardReason),\n });\n }\n\n dispatchCapture(jwt, 'edit', 'warning', verdict.severity || 'medium', verdict.category || 'security',\n toolName || 'Edit', repo, sessionId, config.captureDepth, {\n command: editContent, reasoning: guardReason,\n rulesChecked: config.rules, violatedRules: verdict.ruleId ? [verdict.ruleId] : [],\n });\n } else {\n dispatchCapture(jwt, 'edit', 'pass', 'audit', verdict.category || 'trivial_edit',\n toolName || 'Edit', repo, sessionId, config.captureDepth, {\n command: editContent, reasoning: verdict.reason || 'no policy violations detected',\n rulesChecked: config.rules, violatedRules: [],\n });\n }\n\n log('editGuard ' + fileShort + ' → pass');\n finishAllow();\n }\n\n const body = {\n hook_event: 'PreToolUse',\n tool_name: toolName || 'Edit',\n tool_input: { file_path: filePath, content },\n file_path: filePath,\n content,\n response_format: 'cursor',\n session_id: sessionId || null,\n cwd: cwd || null,\n repo: repo || null,\n };\n\n const resp = await postWithRetry(GATEWAY_URL + '/api/v1/hook/judge', body, jwt, CURSOR_CLOUD_TIMEOUT_MS);\n\n if (!resp) {\n log('editGuard ' + fileShort + ' → pass (cloud timeout)');\n finishAllow();\n }\n\n if (resp.hook_response) {\n finishWith(resp.hook_response as Record<string, unknown>);\n }\n log('editGuard ' + fileShort + ' → pass (no hook_response)');\n finishAllow();\n } catch (e) {\n log('editGuard error: ' + String(e));\n finishAllow();\n }\n}\n\nmain().catch((e) => {\n log('editGuard fatal: ' + String(e));\n finishAllow();\n});`;\n\nexport const CURSOR_EDIT_CAPTURE_TS = `#!/usr/bin/env bun\nimport {\n loadJwt, ensureFreshJwt, detectRepo, readStdin,\n appendLocalTelemetry, log, GATEWAY_URL,\n} from './_synkro-common.ts';\nimport { existsSync, readFileSync } from 'node:fs';\nimport { basename, dirname, join } from 'node:path';\nimport { homedir } from 'node:os';\n\nlet hookDone = false;\n\nfunction finish(): never {\n if (!hookDone) {\n hookDone = true;\n try { process.stdout.write('{}\\\\n'); } catch {}\n }\n process.exit(0);\n}\n\nprocess.on('SIGTERM', () => finish());\n\nasync function main() {\n try {\n const input = await readStdin();\n if (!input.trim()) finish();\n\n const payload = JSON.parse(input);\n const filePath = payload.file_path || '';\n if (!filePath) finish();\n\n const cwd = payload.cwd || payload.workspace_roots?.[0] || '';\n const sessionId = payload.conversation_id || '';\n const repo = detectRepo(cwd || '.');\n\n log('editScan ' + basename(filePath));\n\n let jwt = loadJwt();\n if (!jwt) finish();\n jwt = await ensureFreshJwt(jwt);\n\n let fileContent = '';\n const fullPath = filePath.startsWith('/') ? filePath : (cwd ? join(cwd, filePath) : filePath);\n try {\n if (existsSync(fullPath)) {\n const buf = readFileSync(fullPath);\n fileContent = buf.slice(0, 50000).toString('utf-8');\n }\n } catch {}\n\n let dependencies: Record<string, string> = {};\n let pkgDir = cwd || dirname(fullPath);\n while (pkgDir !== '/' && pkgDir !== '.') {\n const pkgPath = join(pkgDir, 'package.json');\n if (existsSync(pkgPath)) {\n try {\n const pkg = JSON.parse(readFileSync(pkgPath, 'utf-8'));\n dependencies = { ...(pkg.dependencies || {}), ...(pkg.devDependencies || {}) };\n } catch {}\n break;\n }\n const parent = dirname(pkgDir);\n if (parent === pkgDir) break;\n pkgDir = parent;\n }\n\n const captureBody: Record<string, any> = {\n capture_type: 'edit_scan',\n tool_input: { file_path: filePath, content: fileContent },\n edit_verdict: { ok: true },\n dependencies,\n };\n if (sessionId) captureBody.session_id = sessionId;\n if (cwd) captureBody.cwd = cwd;\n if (repo) captureBody.repo = repo;\n\n const rulesPath = join(homedir(), '.synkro', 'rules.json');\n if (existsSync(rulesPath)) {\n appendLocalTelemetry(captureBody);\n } else {\n fetch(GATEWAY_URL + '/api/v1/hook/capture', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json', Authorization: 'Bearer ' + jwt },\n body: JSON.stringify(captureBody),\n signal: AbortSignal.timeout(10000),\n }).catch(() => {});\n appendLocalTelemetry(captureBody);\n }\n\n finish();\n } catch (e) {\n log('editScan error: ' + String(e));\n finish();\n }\n}\n\nmain().catch((e) => {\n log('editScan fatal: ' + String(e));\n finish();\n});`;\n\nexport const CURSOR_BASH_FOLLOWUP_TS = `#!/usr/bin/env bun\nimport {\n loadJwt, readStdin, appendLocalTelemetry, log, GATEWAY_URL,\n} from './_synkro-common.ts';\nimport { existsSync, readFileSync, writeFileSync, appendFileSync, mkdirSync } from 'node:fs';\nimport { join, dirname } from 'node:path';\nimport { createHash } from 'node:crypto';\nimport { homedir } from 'node:os';\n\nconst CONSENT_FILE = join(homedir(), '.synkro', '.local-consent');\n\nlet hookDone = false;\n\nfunction finish(): never {\n if (!hookDone) {\n hookDone = true;\n try { process.stdout.write('{}\\\\n'); } catch {}\n }\n process.exit(0);\n}\n\nprocess.on('SIGTERM', () => finish());\n\nfunction hashCmd(cmd: string): string {\n return createHash('sha256').update(cmd).digest('hex').slice(0, 16);\n}\n\nfunction consentGrant(sid: string, hash: string): void {\n try {\n const dir = dirname(CONSENT_FILE);\n if (!existsSync(dir)) mkdirSync(dir, { recursive: true });\n appendFileSync(CONSENT_FILE, sid + '\\\\t' + hash + '\\\\tactive\\\\n', 'utf-8');\n } catch {}\n}\n\nfunction consentHasActive(sid: string, hash: string): boolean {\n try {\n if (!existsSync(CONSENT_FILE)) return false;\n const content = readFileSync(CONSENT_FILE, 'utf-8');\n return content.includes(sid + '\\\\t' + hash + '\\\\tactive');\n } catch {\n return false;\n }\n}\n\nfunction consentConsume(sid: string, hash: string): void {\n try {\n if (!existsSync(CONSENT_FILE)) return;\n const content = readFileSync(CONSENT_FILE, 'utf-8');\n const target = sid + '\\\\t' + hash + '\\\\tactive';\n const replacement = sid + '\\\\t' + hash + '\\\\tconsumed';\n const updated = content.split('\\\\n').map((l: string) => l === target ? replacement : l).join('\\\\n');\n writeFileSync(CONSENT_FILE, updated, 'utf-8');\n } catch {}\n}\n\nasync function main() {\n try {\n const input = await readStdin();\n if (!input.trim()) finish();\n\n const payload = JSON.parse(input);\n const toolName = payload.tool_name || '';\n\n const shellTools = ['Shell', 'Bash', 'terminal', 'run_terminal_cmd', 'execute_command'];\n if (!shellTools.includes(toolName)) finish();\n\n const sessionId = payload.conversation_id || '';\n const toolUseId = payload.tool_use_id || '';\n const command = payload.tool_input?.command || '';\n\n let isError = false;\n try {\n const output = JSON.parse(payload.tool_output || '{}');\n isError = output.exitCode !== 0 || output.is_error === true;\n } catch { isError = false; }\n\n const cmdHash = command ? hashCmd(command) : '';\n\n if (cmdHash && sessionId) {\n if (!isError) {\n consentConsume(sessionId, cmdHash);\n } else {\n if (!consentHasActive(sessionId, cmdHash)) {\n consentGrant(sessionId, cmdHash);\n }\n }\n }\n\n const captureBody: Record<string, any> = {\n capture_type: 'bash_followup',\n session_id: sessionId || null,\n tool_use_id: toolUseId || null,\n is_error: isError,\n command_hash: cmdHash,\n };\n\n const rulesPath = join(homedir(), '.synkro', 'rules.json');\n if (existsSync(rulesPath)) {\n appendLocalTelemetry(captureBody);\n } else {\n const jwt = loadJwt();\n if (jwt && sessionId && toolUseId) {\n fetch(GATEWAY_URL + '/api/v1/hook/capture', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json', Authorization: 'Bearer ' + jwt },\n body: JSON.stringify(captureBody),\n signal: AbortSignal.timeout(3000),\n }).catch(() => {});\n }\n appendLocalTelemetry(captureBody);\n }\n\n finish();\n } catch (e) {\n log('bashFollowup error: ' + String(e));\n finish();\n }\n}\n\nmain().catch((e) => {\n log('bashFollowup fatal: ' + String(e));\n finish();\n});`;\n\nexport const CURSOR_SESSION_START_TS = `#!/usr/bin/env bun\nimport {\n loadJwt, loadConfig, readStdin, log,\n type HookConfig,\n} from './_synkro-common.ts';\n\nlet hookDone = false;\n\nfunction finishAllow(): never {\n if (!hookDone) {\n hookDone = true;\n try { process.stdout.write('{}\\\\n'); } catch {}\n }\n process.exit(0);\n}\n\nfunction finishWith(payload: Record<string, unknown>): never {\n hookDone = true;\n process.stdout.write(JSON.stringify(payload) + '\\\\n');\n process.exit(0);\n}\n\nprocess.on('SIGTERM', () => finishAllow());\n\nasync function main() {\n try {\n const input = await readStdin();\n\n let jwt = loadJwt();\n const config: HookConfig = jwt ? await loadConfig(jwt) : {\n captureDepth: 'local_only', tier: 'standard', silent: false,\n policyName: '', rules: [], scanExemptions: [],\n };\n\n const policyName = config.policyName || 'default';\n const ruleCount = config.rules.length;\n const mode = config.silent ? 'silent' : 'active';\n\n const context = [\n 'This session is monitored by Synkro (' + mode + ' mode, policy: \"' + policyName + '\", ' + ruleCount + ' rules).',\n 'Synkro enforces security and compliance rules on tool calls (shell commands, file edits).',\n 'If a tool call is blocked, Synkro will explain which rule was violated and why.',\n 'Do not suggest workarounds to bypass Synkro hooks — fix the underlying issue instead.',\n ].join(' ');\n\n finishWith({ additional_context: context });\n } catch (e) {\n log('sessionStart error: ' + String(e));\n finishAllow();\n }\n}\n\nmain().catch((e) => {\n log('sessionStart fatal: ' + String(e));\n finishAllow();\n});`;\n\n","/**\n * Synkro CLI Authentication\n *\n * OAuth-style authentication flow for CLI integration with Synkro web platform.\n * Mirrors the Node.js reference implementation.\n */\n\nimport { createServer, IncomingMessage, ServerResponse } from \"node:http\";\nimport { writeFileSync, readFileSync, existsSync, mkdirSync, unlinkSync } from \"node:fs\";\nimport { homedir, platform } from \"node:os\";\nimport { join, dirname } from \"node:path\";\nimport { execFile } from \"node:child_process\";\nimport jwt from \"jsonwebtoken\";\n\n// Types\ninterface WorkOSJwtPayload {\n iss: string; // \"https://api.workos.com/\"\n sub: string; // user ID\n aud?: string; // client ID\n exp: number;\n iat: number;\n email?: string;\n org_id?: string;\n role?: string;\n permissions?: string[];\n sid?: string; // session ID\n}\n\n// Configuration — matches the desktop app pattern (packages/desktop/src-tauri/src/auth.rs).\n// Dev dashboard runs on :4322; CLI listens on 8100 for the OAuth callback.\nconst PORT = 8100;\n// Same poisoning concern as the gateway URL: a developer's local .env or\n// op:// expansion can land in SYNKRO_WEB_AUTH_URL. Only honor http(s) values;\n// fall through to the prod dashboard otherwise so the OAuth callback always\n// has a real origin to open the browser at.\nconst RAW_WEB_AUTH_URL = process.env.SYNKRO_WEB_AUTH_URL;\nconst SYNKRO_WEB_AUTH_URL = (RAW_WEB_AUTH_URL && /^https?:\\/\\//.test(RAW_WEB_AUTH_URL))\n ? RAW_WEB_AUTH_URL\n : \"https://app.synkro.sh\";\nconst AUTH_FILE = process.env.SYNKRO_AUTH_FILE || join(homedir(), \".synkro\", \"credentials.json\");\nconst RAW_API_URL = process.env.SYNKRO_CRUD_URL || process.env.SYNKRO_API_URL;\nconst SYNKRO_API_URL = (RAW_API_URL && /^https?:\\/\\//.test(RAW_API_URL))\n ? RAW_API_URL\n : \"https://api.synkro.sh\";\n\n// Types — matches the AuthCredentials shape returned by the dashboard's\n// /api/auth/cli-callback (see packages/app/src/pages/api/auth/cli-callback.ts).\nexport interface AuthCredentials {\n access_token: string;\n refresh_token: string;\n user_id?: string;\n email?: string;\n org_id?: string;\n state?: string;\n}\n\nexport interface UserInfo {\n id: string;\n email: string;\n org_id?: string;\n}\n\n// HTML responses\nconst SUCCESS_HTML = `\n<!DOCTYPE html>\n<html>\n<head>\n <title>Authentication Successful - Synkro CLI</title>\n <style>\n body {\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;\n display: flex;\n justify-content: center;\n align-items: center;\n height: 100vh;\n margin: 0;\n background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);\n }\n .container {\n background: white;\n padding: 3rem;\n border-radius: 1rem;\n box-shadow: 0 20px 60px rgba(0,0,0,0.3);\n text-align: center;\n max-width: 400px;\n }\n .checkmark {\n width: 80px;\n height: 80px;\n border-radius: 50%;\n background: #10b981;\n margin: 0 auto 1.5rem;\n display: flex;\n align-items: center;\n justify-content: center;\n }\n .checkmark svg {\n width: 50px;\n height: 50px;\n stroke: white;\n }\n h1 {\n color: #1f2937;\n margin: 0 0 0.5rem;\n }\n p {\n color: #6b7280;\n margin: 0;\n }\n .close-note {\n margin-top: 1.5rem;\n font-size: 0.875rem;\n color: #9ca3af;\n }\n </style>\n</head>\n<body>\n <div class=\"container\">\n <div class=\"checkmark\">\n <svg fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"3\" d=\"M5 13l4 4L19 7\"></path>\n </svg>\n </div>\n <h1>Authentication Successful!</h1>\n <p>Your Synkro CLI has been authenticated.</p>\n <p class=\"close-note\">You can close this window and return to your terminal.</p>\n </div>\n</body>\n</html>\n`;\n\nconst ERROR_HTML = `\n<!DOCTYPE html>\n<html>\n<head>\n <title>Authentication Failed - Synkro CLI</title>\n <style>\n body {\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;\n display: flex;\n justify-content: center;\n align-items: center;\n height: 100vh;\n margin: 0;\n background: linear-gradient(135deg, #f87171 0%, #dc2626 100%);\n }\n .container {\n background: white;\n padding: 3rem;\n border-radius: 1rem;\n box-shadow: 0 20px 60px rgba(0,0,0,0.3);\n text-align: center;\n max-width: 400px;\n }\n h1 {\n color: #1f2937;\n margin: 0 0 0.5rem;\n }\n p {\n color: #6b7280;\n }\n </style>\n</head>\n<body>\n <div class=\"container\">\n <h1>Authentication Failed</h1>\n <p>Please try again from your terminal.</p>\n </div>\n</body>\n</html>\n`;\n\n/**\n * Open URL in default browser\n */\nfunction openBrowser(url: string): void {\n const os = platform();\n let bin: string;\n let args: string[];\n\n switch (os) {\n case \"darwin\":\n bin = \"open\";\n args = [url];\n break;\n case \"win32\":\n // `start` is a cmd built-in, so we host it via cmd /c, but pass the URL\n // as a literal arg through execFile (no shell parsing) to keep shell\n // metacharacters from being interpreted.\n bin = \"cmd\";\n args = [\"/c\", \"start\", \"\", url];\n break;\n default:\n bin = \"xdg-open\";\n args = [url];\n }\n\n // execFile (vs exec) does NOT spawn a shell, so url is treated as a single\n // argv entry regardless of contents. Removes shell-injection risk if url\n // ever ends up containing $/`/;/&/etc.\n execFile(bin, args, (error) => {\n if (error) {\n console.error(\"Failed to open browser automatically.\");\n console.log(`Please open this URL manually: ${url}`);\n }\n });\n}\n\n/**\n * Save authentication credentials to file\n */\nexport function saveCredentials(data: AuthCredentials): void {\n const dir = dirname(AUTH_FILE);\n\n if (!existsSync(dir)) {\n mkdirSync(dir, { recursive: true, mode: 0o700 });\n }\n\n writeFileSync(AUTH_FILE, JSON.stringify(data, null, 2), { mode: 0o600 });\n}\n\n/**\n * Load saved authentication credentials\n */\nexport function loadCredentials(): AuthCredentials | null {\n if (!existsSync(AUTH_FILE)) {\n return null;\n }\n\n try {\n const content = readFileSync(AUTH_FILE, \"utf8\");\n return JSON.parse(content);\n } catch (error) {\n return null;\n }\n}\n\n/**\n * Create HTTP server to receive OAuth callback.\n *\n * Mirrors packages/desktop/src-tauri/src/auth.rs:\n * - Listens on PORT\n * - Handles OPTIONS preflight (CORS)\n * - Catches /auth?token=...&refresh_token=...&user_id=...&email=...&org_id=...&state=...\n * - Returns success HTML on token receipt\n */\nfunction createCallbackServer(): Promise<AuthCredentials> {\n // Tokens land via POST body (JSON), never query params, so they don't get\n // logged into req.url, browser DevTools URL bars, server access logs, or\n // tooling that snapshots URLs. CORS origin is pinned to the configured\n // dashboard so a random page on a different origin can't post forged\n // credentials at the local listener.\n const CORS_HEADERS = {\n \"Access-Control-Allow-Origin\": SYNKRO_WEB_AUTH_URL,\n \"Access-Control-Allow-Methods\": \"POST, OPTIONS\",\n \"Access-Control-Allow-Headers\": \"Content-Type\",\n \"Vary\": \"Origin\",\n };\n\n return new Promise((resolve, reject) => {\n const server = createServer((req: IncomingMessage, res: ServerResponse) => {\n // CORS preflight — only echo the pinned origin if the request actually\n // comes from there; otherwise omit ACAO so the browser blocks the call.\n if (req.method === \"OPTIONS\") {\n const origin = req.headers.origin;\n if (origin === SYNKRO_WEB_AUTH_URL) {\n res.writeHead(204, CORS_HEADERS);\n } else {\n res.writeHead(204, {\n \"Access-Control-Allow-Methods\": \"POST, OPTIONS\",\n \"Access-Control-Allow-Headers\": \"Content-Type\",\n \"Vary\": \"Origin\",\n });\n }\n res.end();\n return;\n }\n\n // Reject requests whose Origin header isn't the dashboard. Browsers\n // send Origin on cross-origin POSTs, so this stops a hostile page from\n // forging credentials at our local listener even if it bypasses CORS.\n const reqOrigin = req.headers.origin;\n if (reqOrigin && reqOrigin !== SYNKRO_WEB_AUTH_URL) {\n res.writeHead(403, { \"Vary\": \"Origin\" });\n res.end();\n return;\n }\n\n if (!req.url) {\n res.writeHead(404, CORS_HEADERS);\n res.end();\n return;\n }\n\n const url = new URL(req.url, `http://localhost:${PORT}`);\n\n if (url.pathname !== \"/auth\") {\n res.writeHead(404, CORS_HEADERS);\n res.end();\n return;\n }\n\n if (req.method !== \"POST\") {\n // Reject GET so a stale/older dashboard build can't deliver tokens\n // via query-string. Forces upgrade. CLI 1.0.4+ requires the v1.6+\n // dashboard build.\n res.writeHead(405, { ...CORS_HEADERS, \"Allow\": \"POST, OPTIONS\", \"Content-Type\": \"text/html\" });\n res.end(ERROR_HTML);\n return;\n }\n\n // Cap body at 16 KB — JWT pairs are ~4–8 KB; anything bigger is junk.\n const MAX_BODY = 16 * 1024;\n const chunks: Buffer[] = [];\n let total = 0;\n let aborted = false;\n req.on(\"data\", (chunk: Buffer) => {\n if (aborted) return;\n total += chunk.length;\n if (total > MAX_BODY) {\n aborted = true;\n res.writeHead(413, CORS_HEADERS);\n res.end();\n return;\n }\n chunks.push(chunk);\n });\n req.on(\"end\", () => {\n if (aborted) return;\n let parsed: any;\n try {\n parsed = JSON.parse(Buffer.concat(chunks).toString(\"utf8\"));\n } catch {\n res.writeHead(400, { ...CORS_HEADERS, \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ error: \"invalid_json\" }));\n setTimeout(() => {\n server.close();\n reject(new Error(\"Authentication failed: invalid JSON body\"));\n }, 200);\n return;\n }\n\n const token: string | undefined = parsed?.token;\n if (!token || typeof token !== \"string\") {\n res.writeHead(400, { ...CORS_HEADERS, \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ error: \"missing_token\" }));\n setTimeout(() => {\n server.close();\n reject(new Error(\"Authentication failed: missing token\"));\n }, 200);\n return;\n }\n\n const authData: AuthCredentials = {\n access_token: token,\n refresh_token: typeof parsed.refresh_token === \"string\" ? parsed.refresh_token : \"\",\n user_id: typeof parsed.user_id === \"string\" ? parsed.user_id : undefined,\n email: typeof parsed.email === \"string\" ? parsed.email : undefined,\n org_id: typeof parsed.org_id === \"string\" ? parsed.org_id : undefined,\n state: typeof parsed.state === \"string\" ? parsed.state : undefined,\n };\n\n res.writeHead(200, { ...CORS_HEADERS, \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ ok: true }));\n\n setTimeout(() => {\n server.close();\n resolve(authData);\n }, 200);\n });\n req.on(\"error\", (e) => {\n if (aborted) return;\n aborted = true;\n try { res.writeHead(500, CORS_HEADERS); res.end(); } catch {}\n setTimeout(() => {\n server.close();\n reject(e);\n }, 200);\n });\n });\n\n server.listen(PORT);\n\n server.on(\"error\", (error: NodeJS.ErrnoException) => {\n if (error.code === \"EADDRINUSE\") {\n reject(\n new Error(\n `Port ${PORT} is already in use. Close any other Synkro CLI instance and retry.`,\n ),\n );\n } else {\n reject(error);\n }\n });\n });\n}\n\nexport type AuthStatus =\n | { phase: 'starting' }\n | { phase: 'browser-opened'; url: string }\n | { phase: 'waiting' }\n | { phase: 'success' }\n | { phase: 'error'; message: string };\n\n/**\n * Initiate the OAuth-style authentication flow\n */\nexport async function authenticate(\n onStatus?: (status: AuthStatus) => void,\n): Promise<AuthCredentials | null> {\n const emit = onStatus || (() => {});\n\n try {\n emit({ phase: 'starting' });\n\n // Start local server to receive the callback\n const serverPromise = createCallbackServer();\n\n // Open browser to the CLI auth page\n const authUrl = `${SYNKRO_WEB_AUTH_URL}/cli-auth?port=${PORT}`;\n openBrowser(authUrl);\n\n emit({ phase: 'browser-opened', url: authUrl });\n emit({ phase: 'waiting' });\n\n // Wait for authentication callback\n const data = await serverPromise;\n\n emit({ phase: 'success' });\n saveCredentials(data);\n return data;\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n emit({ phase: 'error', message });\n return null;\n }\n}\n\n/**\n * Check if user is authenticated (credentials exist and token not expired)\n */\nexport function isAuthenticated(): boolean {\n const creds = loadCredentials();\n if (!creds) return false;\n\n // Also check token expiry\n try {\n const decoded = jwt.decode(creds.access_token) as { exp?: number } | null;\n if (!decoded?.exp) return true; // Can't decode, assume valid (refresh will handle it)\n\n // Consider expired if past expiration (no buffer here — ensureValidToken handles refresh buffer)\n return Date.now() < decoded.exp * 1000;\n } catch {\n return true; // Decode failed, let ensureValidToken handle it\n }\n}\n\n/**\n * Get current user ID from JWT token\n */\nexport function getCurrentUserId(): string {\n const creds = loadCredentials();\n if (!creds) {\n throw new Error(\"Not authenticated\");\n }\n\n const decoded = jwt.decode(creds.access_token) as WorkOSJwtPayload | null;\n if (!decoded?.sub) {\n throw new Error(\"Invalid token\");\n }\n\n return decoded.sub;\n}\n\n/**\n * Get user info from JWT\n */\nexport function getUserInfo(): UserInfo {\n const creds = loadCredentials();\n if (!creds) {\n throw new Error(\"Not authenticated\");\n }\n\n // Prefer the explicit user_id/email/org_id stashed during the OAuth callback\n // (the dashboard returns them as query params). Fall back to JWT decode if\n // we somehow received older creds without those fields.\n if (creds.user_id) {\n return {\n id: creds.user_id,\n email: creds.email ?? '',\n org_id: creds.org_id,\n };\n }\n\n const decoded = jwt.decode(creds.access_token) as WorkOSJwtPayload | null;\n if (!decoded) {\n throw new Error(\"Invalid token\");\n }\n\n return {\n id: decoded.sub,\n email: decoded.email ?? '',\n org_id: decoded.org_id,\n };\n}\n\n/**\n * Get access token for API calls\n */\nexport function getAccessToken(): string | null {\n const creds = loadCredentials();\n return creds?.access_token || null;\n}\n\n/**\n * Check if token is expired (with 5 min buffer)\n */\nexport function isTokenExpired(): boolean {\n const creds = loadCredentials();\n if (!creds) return true;\n\n try {\n const decoded = jwt.decode(creds.access_token) as { exp?: number } | null;\n if (!decoded?.exp) return true;\n\n // Expired if less than 5 minutes remaining\n const expiresAt = decoded.exp * 1000;\n const buffer = 5 * 60 * 1000; // 5 minutes\n return Date.now() > expiresAt - buffer;\n } catch {\n return true;\n }\n}\n\n/**\n * Refresh the access token using refresh_token\n */\nexport async function refreshToken(): Promise<boolean> {\n const creds = loadCredentials();\n if (!creds?.refresh_token) return false;\n\n try {\n const response = await fetch(`${SYNKRO_API_URL}/api/auth/refresh`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ refresh_token: creds.refresh_token }),\n });\n\n if (!response.ok) return false;\n\n const data = await response.json();\n if (data.access_token) {\n saveCredentials({\n ...creds,\n access_token: data.access_token,\n refresh_token: data.refresh_token || creds.refresh_token,\n });\n return true;\n }\n return false;\n } catch {\n return false;\n }\n}\n\n// Mutex: prevent concurrent token refresh races\nlet refreshPromise: Promise<boolean> | null = null;\n\n/**\n * Ensure we have a valid token, refreshing if needed\n */\nexport async function ensureValidToken(): Promise<boolean> {\n if (!isAuthenticated()) return false;\n\n if (isTokenExpired()) {\n if (!refreshPromise) {\n refreshPromise = refreshToken().finally(() => { refreshPromise = null; });\n }\n const refreshed = await refreshPromise;\n if (!refreshed) {\n clearCredentials();\n return false;\n }\n }\n\n return true;\n}\n\n/**\n * Clear saved credentials (logout)\n */\nexport function clearCredentials(): void {\n if (existsSync(AUTH_FILE)) {\n unlinkSync(AUTH_FILE);\n }\n}\n\n/**\n * Get secrets for a user's integrations\n * In production, this would fetch from Infisical vault using the access token\n *\n * These are secrets the USER provides for THEIR integrations:\n * - AWS credentials (for their S3 buckets)\n * - HuggingFace token (for their datasets)\n * - Langsmith API key (for their projects)\n */\nexport async function getSecrets(\n userId: string,\n integrationId: string,\n): Promise<Record<string, string>> {\n // TODO: In production, use access token to fetch from Infisical\n // For now, return from environment (dev mode)\n return {\n AWS_ACCESS_KEY_ID: process.env.USER_AWS_KEY || \"\",\n AWS_SECRET_ACCESS_KEY: process.env.USER_AWS_SECRET || \"\",\n AWS_REGION: process.env.USER_AWS_REGION || \"us-east-1\",\n HF_TOKEN: process.env.USER_HF_TOKEN || \"\",\n LANGSMITH_API_KEY: process.env.USER_LANGSMITH_KEY || \"\",\n };\n}\n","/**\n * Synkro CLI Authentication Module\n *\n * Exports authentication functions for use throughout the CLI.\n */\n\nexport {\n authenticate,\n isAuthenticated,\n getCurrentUserId,\n getUserInfo,\n getAccessToken,\n loadCredentials,\n saveCredentials,\n clearCredentials,\n getSecrets,\n isTokenExpired,\n refreshToken,\n ensureValidToken,\n} from './stub.js';\n\nexport type { AuthCredentials, UserInfo, AuthStatus } from './stub.js';\n","// :)\n/**\n * CLI API client for project and violation endpoints.\n *\n * Calls the Synkro API (Hono/Cloudflare Workers) for project management, violations, and policy upsert.\n * SYNKRO_CRUD_URL points to the API server (defaults to http://localhost:8788/api).\n */\n\nimport { getAccessToken, ensureValidToken } from '../auth/index.js';\n\nlet API_URL = 'https://api.synkro.sh/api';\n\nexport function setApiBaseUrl(url: string): void {\n API_URL = url;\n}\n\n// Types\n\nexport interface Project {\n id: string;\n slug: string;\n name: string;\n provider: string | null;\n is_active: boolean;\n api_key_count: number;\n created_at: string | null;\n repos?: Array<{ id: string; github_repo_id: number | null; full_name: string }>;\n}\n\nexport interface Violation {\n id: string | null;\n run_id: string | null;\n score: number | null;\n severity: string | null;\n issues: string[];\n rules_violated: string[];\n messages: Array<{ role: string; content: string }>;\n model: string | null;\n comment: string | null;\n created_at: string | null;\n}\n\nexport interface ViolationsResponse {\n violations: Violation[];\n total: number;\n project_id: string;\n project_slug: string | null;\n policy_text: string | null;\n rules: Array<Record<string, unknown>>;\n}\n\nexport interface GetViolationsOptions {\n limit?: number;\n offset?: number;\n severity?: string;\n}\n\nexport interface PolicyUpsertResponse {\n ok: boolean;\n policy_id: string;\n}\n\n// API helper\n\nasync function callApi<T>(\n method: string,\n endpoint: string,\n body?: Record<string, unknown>\n): Promise<T> {\n if (!API_URL) {\n throw new Error('API URL not configured. Run `synkro install` first.');\n }\n\n const url = `${API_URL}${endpoint}`;\n await ensureValidToken();\n const accessToken = getAccessToken();\n\n const headers: Record<string, string> = {\n 'Content-Type': 'application/json',\n };\n\n if (accessToken) {\n headers['Authorization'] = `Bearer ${accessToken}`;\n }\n\n const response = await fetch(url, {\n method,\n headers,\n body: body ? JSON.stringify(body) : undefined,\n });\n\n if (!response.ok) {\n const error = await response.json().catch(() => ({ detail: response.statusText }));\n throw new Error(error.detail || `API error: ${response.status}`);\n }\n\n return response.json();\n}\n\nexport interface CreateProjectResponse {\n id: string;\n slug: string;\n name: string;\n provider: string | null;\n is_active: boolean;\n created_at: string | null;\n}\n\nexport interface CreateApiKeyResponse {\n id: string;\n key: string;\n key_prefix: string;\n name: string;\n project_id: string;\n created_at: string | null;\n}\n\n// Project API functions\n\n/**\n * Create a new project.\n */\nexport async function createProject(\n name: string,\n repos?: Array<{ github_repo_id?: number; full_name: string; default_branch?: string; private?: boolean }>,\n): Promise<CreateProjectResponse> {\n const body: Record<string, unknown> = { name };\n if (repos && repos.length > 0) body.repos = repos;\n return callApi<CreateProjectResponse>('POST', '/projects', body);\n}\n\n/**\n * Create an API key for a project. Returns plaintext key (shown once).\n */\nexport async function createApiKey(\n projectId: string,\n name?: string\n): Promise<CreateApiKeyResponse> {\n return callApi<CreateApiKeyResponse>('POST', '/api-keys', {\n project_id: projectId,\n name: name || 'CLI Key',\n });\n}\n\n/**\n * List all projects for the authenticated user.\n */\nexport async function listProjects(): Promise<Project[]> {\n return callApi<Project[]>('GET', '/projects');\n}\n\n/**\n * Get violations for a project with optional filtering.\n * Also returns the active policy text and rules.\n */\nexport async function getViolations(\n projectId: string,\n options: GetViolationsOptions = {}\n): Promise<ViolationsResponse> {\n const params = new URLSearchParams();\n if (options.limit) params.set('limit', String(options.limit));\n if (options.offset) params.set('offset', String(options.offset));\n if (options.severity) params.set('severity', options.severity);\n\n const qs = params.toString();\n const endpoint = `/projects/${projectId}/violations${qs ? `?${qs}` : ''}`;\n return callApi<ViolationsResponse>('GET', endpoint);\n}\n\n/**\n * Upsert a policy for a project (deploy rules to gateway).\n */\nexport async function upsertPolicy(\n projectId: string,\n policyText: string,\n rules: Array<Record<string, unknown>>,\n ruleCount: number\n): Promise<PolicyUpsertResponse> {\n return callApi<PolicyUpsertResponse>('POST', `/projects/${projectId}/policies`, {\n policy_text: policyText,\n rules,\n rule_count: ruleCount,\n });\n}\n\n/**\n * Update a project's configuration.\n */\nexport async function updateProject(\n projectId: string,\n updates: {\n name?: string;\n provider?: string;\n langsmith_project?: string;\n is_active?: boolean;\n }\n): Promise<{ ok: boolean }> {\n return callApi<{ ok: boolean }>('PATCH', `/projects/${projectId}`, updates as Record<string, unknown>);\n}\n\n/**\n * Unlink a repo from a project.\n */\nexport async function unlinkRepo(\n projectId: string,\n repoId: string,\n): Promise<{ ok: boolean }> {\n return callApi<{ ok: boolean }>('DELETE', `/projects/${projectId}/repos/${repoId}`);\n}\n\n/**\n * Resolve a project by name or slug (fuzzy match against user's projects).\n * Returns the first matching project or null.\n */\nexport async function resolveProject(nameOrSlug: string): Promise<Project | null> {\n const projects = await listProjects();\n const query = nameOrSlug.toLowerCase();\n\n // Exact slug match\n const exactSlug = projects.find(p => p.slug === nameOrSlug);\n if (exactSlug) return exactSlug;\n\n // Exact name match (case-insensitive)\n const exactName = projects.find(p => p.name.toLowerCase() === query);\n if (exactName) return exactName;\n\n // Partial name match\n const partial = projects.find(p => p.name.toLowerCase().includes(query));\n if (partial) return partial;\n\n // Partial slug match\n const partialSlug = projects.find(p => p.slug.includes(query));\n if (partialSlug) return partialSlug;\n\n return null;\n}\n","// :)\n/**\n * GitHub Actions workflow YAML template for Synkro PR scanning.\n *\n * Customer commits this to .github/workflows/synkro.yml. Triggers on\n * pull_request open/synchronize/reopened. Runs `synkro scan-pr` which\n * spawns claude --print with their CLAUDE_CODE_OAUTH_TOKEN per file.\n *\n * Both CLIs install via npm (npm registry attestations + signed packages\n * are verified by npm itself). No curl-pipe-to-bash; we publish to npm\n * specifically so CI can install us through a trusted package manager.\n */\n\nexport const SYNKRO_WORKFLOW_YAML = `name: Synkro Security Review\non:\n pull_request:\n types: [opened, synchronize, reopened]\n workflow_dispatch:\n inputs:\n pr_number:\n description: PR number to scan\n required: true\n sha:\n description: Commit SHA to scan\n required: true\n\njobs:\n scan:\n runs-on: ubuntu-latest\n permissions:\n contents: write\n pull-requests: write\n checks: write\n steps:\n - uses: actions/checkout@v4\n with:\n fetch-depth: 0\n ref: \\${{ inputs.sha || github.event.pull_request.head.sha }}\n\n - name: Cache npm globals\n id: cache-npm-global\n uses: actions/cache@v4\n with:\n path: ~/.npm-global\n key: synkro-cli-\\${{ runner.os }}-v1\n\n - name: Install Synkro CLI + Claude Code CLI\n run: |\n npm config set prefix ~/.npm-global\n npm install -g @synkro-sh/cli @anthropic-ai/claude-code\n echo \"$HOME/.npm-global/bin\" >> $GITHUB_PATH\n\n - name: Run Synkro PR scan\n run: synkro-cli scan-pr\n env:\n CLAUDE_CODE_OAUTH_TOKEN: \\${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}\n SYNKRO_API_KEY: \\${{ secrets.SYNKRO_API_KEY }}\n GH_TOKEN: \\${{ secrets.GITHUB_TOKEN }}\n SYNKRO_PR_NUMBER: \\${{ inputs.pr_number || github.event.pull_request.number }}\n SYNKRO_REPO: \\${{ github.repository }}\n SYNKRO_SHA: \\${{ inputs.sha || github.event.pull_request.head.sha }}\n SYNKRO_GATEWAY_URL: \\${{ vars.SYNKRO_GATEWAY_URL || 'https://api.synkro.sh' }}\n`;\n\nexport const WORKFLOW_FILENAME = 'synkro.yml';\nexport const WORKFLOW_PATH = '.github/workflows/synkro.yml';\n","/**\n * GitHub repo setup for PR scanning.\n *\n * Uses `gh secret set` to push CLAUDE_CODE_OAUTH_TOKEN + SYNKRO_API_KEY\n * as repo secrets, then writes .github/workflows/synkro.yml to the local\n * clone if present.\n */\nimport { existsSync, mkdirSync, writeFileSync } from 'node:fs';\nimport { execSync } from 'node:child_process';\nimport { join } from 'node:path';\nimport { SYNKRO_WORKFLOW_YAML, WORKFLOW_PATH } from './workflowTemplate.js';\n\nexport interface GitHubAuthOptions {\n token: string;\n}\n\nfunction ghSecretSet(token: string, owner: string, repo: string, name: string, value: string): void {\n execSync(`gh secret set ${name} --repo ${owner}/${repo} --body -`, {\n input: value,\n env: { ...process.env, GH_TOKEN: token },\n stdio: ['pipe', 'ignore', 'pipe'],\n timeout: 30_000,\n });\n}\n\n/**\n * List repos the token has access to.\n */\nexport async function listAccessibleRepos(opts: GitHubAuthOptions): Promise<Array<{ owner: string; repo: string; full_name: string }>> {\n const repos: Array<{ owner: string; repo: string; full_name: string }> = [];\n let page = 1;\n while (page <= 5) { // cap pagination\n const url = `https://api.github.com/user/repos?per_page=100&page=${page}&affiliation=owner,collaborator`;\n const resp = await fetch(url, {\n headers: {\n Authorization: `Bearer ${opts.token}`,\n Accept: 'application/vnd.github+json',\n 'X-GitHub-Api-Version': '2022-11-28',\n },\n });\n if (!resp.ok) {\n throw new Error(`GitHub API ${resp.status} listing repos`);\n }\n const data = await resp.json() as Array<{ full_name: string; owner: { login: string }; name: string }>;\n if (data.length === 0) break;\n for (const r of data) {\n repos.push({ owner: r.owner.login, repo: r.name, full_name: r.full_name });\n }\n if (data.length < 100) break;\n page++;\n }\n return repos;\n}\n\nexport async function pushSecretsToRepo(\n opts: GitHubAuthOptions,\n owner: string,\n repo: string,\n secrets: { claudeCodeOauthToken?: string; synkroApiKey: string },\n): Promise<void> {\n try { execSync('gh --version', { stdio: 'ignore', timeout: 5000 }); } catch {\n throw new Error('GitHub CLI (gh) not found. Install it: https://cli.github.com');\n }\n if (secrets.claudeCodeOauthToken) {\n ghSecretSet(opts.token, owner, repo, 'CLAUDE_CODE_OAUTH_TOKEN', secrets.claudeCodeOauthToken);\n }\n ghSecretSet(opts.token, owner, repo, 'SYNKRO_API_KEY', secrets.synkroApiKey);\n}\n\n/**\n * Write the workflow YAML to a local repo clone (if the user is in one).\n * Returns the absolute path written, or null if cwd isn't a git repo.\n */\nexport function writeWorkflowFile(repoRootPath: string): string | null {\n const workflowDir = join(repoRootPath, '.github', 'workflows');\n mkdirSync(workflowDir, { recursive: true });\n const workflowFile = join(workflowDir, 'synkro.yml');\n writeFileSync(workflowFile, SYNKRO_WORKFLOW_YAML, 'utf-8');\n return workflowFile;\n}\n\n/**\n * Find the git repo root for a given cwd. Returns null if not in a git repo.\n */\nexport function findGitRoot(startCwd: string): string | null {\n let cur = startCwd;\n while (cur && cur !== '/') {\n if (existsSync(join(cur, '.git'))) return cur;\n const parent = join(cur, '..');\n if (parent === cur) break;\n cur = parent;\n }\n return null;\n}\n\nexport const SECRET_NAMES = {\n CLAUDE_OAUTH: 'CLAUDE_CODE_OAUTH_TOKEN',\n SYNKRO_API_KEY: 'SYNKRO_API_KEY',\n} as const;\n\nexport const WORKFLOW_RELATIVE_PATH = WORKFLOW_PATH;\n","// :)\n/**\n * Shared helpers for repo connection during install and `synkro link`.\n *\n * Two paths:\n * 1. Local git repo — detect from `git remote get-url origin`, any provider\n * 2. GitHub OAuth — browser flow, list repos, interactive picker\n */\nimport { execSync } from 'node:child_process';\nimport { createServer } from 'node:http';\nimport { createInterface } from 'node:readline';\nimport { createProject, listProjects } from '../api/projects.js';\nimport { listAccessibleRepos } from '../installer/githubSetup.js';\n\nconst RAW_WEB_AUTH_URL = process.env.SYNKRO_WEB_AUTH_URL;\nconst SYNKRO_WEB_AUTH_URL = (RAW_WEB_AUTH_URL && /^https?:\\/\\//.test(RAW_WEB_AUTH_URL))\n ? RAW_WEB_AUTH_URL\n : 'https://app.synkro.sh';\nconst GITHUB_PORT = 8101;\n\nfunction detectGitRepo(): { fullName: string; shortName: string } | null {\n try {\n const remoteUrl = execSync('git remote get-url origin', { encoding: 'utf-8', timeout: 5000 }).trim();\n // Match any git host — github, gitlab, bitbucket, self-hosted, etc.\n const sshMatch = remoteUrl.match(/^git@[^:]+:(.+?)(?:\\.git)?$/);\n const httpMatch = remoteUrl.match(/^https?:\\/\\/[^/]+\\/(.+?)(?:\\.git)?$/);\n const match = sshMatch || httpMatch;\n if (!match) return null;\n const fullName = match[1];\n return { fullName, shortName: fullName.split('/').pop() || fullName };\n } catch { return null; }\n}\n\nfunction ask(rl: ReturnType<typeof createInterface>, question: string): Promise<string> {\n return new Promise((resolve) => rl.question(question, resolve));\n}\n\nfunction waitForGithubToken(): Promise<string> {\n return new Promise((resolve, reject) => {\n const server = createServer((req, res) => {\n if (req.method === 'OPTIONS') {\n res.writeHead(204, {\n 'Access-Control-Allow-Origin': SYNKRO_WEB_AUTH_URL,\n 'Access-Control-Allow-Methods': 'POST, OPTIONS',\n 'Access-Control-Allow-Headers': 'Content-Type',\n });\n res.end();\n return;\n }\n\n if (req.url !== '/auth' || req.method !== 'POST') {\n res.writeHead(404);\n res.end();\n return;\n }\n\n let body = '';\n req.on('data', (chunk) => { body += chunk; });\n req.on('end', () => {\n try {\n const parsed = JSON.parse(body);\n if (!parsed.github_token) {\n res.writeHead(400, {\n 'Content-Type': 'application/json',\n 'Access-Control-Allow-Origin': SYNKRO_WEB_AUTH_URL,\n });\n res.end(JSON.stringify({ error: 'missing github_token' }));\n return;\n }\n res.writeHead(200, {\n 'Content-Type': 'application/json',\n 'Access-Control-Allow-Origin': SYNKRO_WEB_AUTH_URL,\n });\n res.end(JSON.stringify({ ok: true }));\n setTimeout(() => server.close(), 200);\n resolve(parsed.github_token);\n } catch {\n res.writeHead(400, { 'Content-Type': 'application/json' });\n res.end(JSON.stringify({ error: 'invalid json' }));\n }\n });\n });\n\n server.on('error', (err: NodeJS.ErrnoException) => {\n if (err.code === 'EADDRINUSE') {\n reject(new Error(`Port ${GITHUB_PORT} is in use. Close other processes and try again.`));\n } else {\n reject(err);\n }\n });\n\n server.listen(GITHUB_PORT);\n });\n}\n\nfunction openBrowser(url: string): void {\n const { execFile } = require('node:child_process');\n const plat = process.platform;\n const cb = (err: Error | null) => {\n if (err) console.log(` Open this URL manually: ${url}`);\n };\n if (plat === 'darwin') execFile('open', [url], cb);\n else if (plat === 'win32') execFile('cmd', ['/c', 'start', '', url], cb);\n else execFile('xdg-open', [url], cb);\n}\n\nasync function connectGithubAndSelectRepos(): Promise<Array<{ full_name: string }>> {\n const url = `${SYNKRO_WEB_AUTH_URL}/cli-github?port=${GITHUB_PORT}`;\n console.log(' Opening browser for GitHub authorization...');\n openBrowser(url);\n\n console.log(' Waiting for GitHub authorization...');\n const ghToken = await waitForGithubToken();\n console.log(' ✓ GitHub connected\\n');\n\n const repos = await listAccessibleRepos({ token: ghToken });\n if (repos.length === 0) {\n console.log(' No accessible repos found on GitHub.');\n return [];\n }\n\n console.log(` Found ${repos.length} repos:\\n`);\n repos.forEach((r: any, i: number) => {\n console.log(` ${String(i + 1).padStart(3)}. ${r.full_name}`);\n });\n console.log();\n\n const rl = createInterface({ input: process.stdin, output: process.stdout });\n try {\n const selection = await ask(rl, ' Select repos (comma-separated numbers, e.g. 1,3,5): ');\n const indices = selection\n .split(',')\n .map((s) => parseInt(s.trim(), 10) - 1)\n .filter((n) => !isNaN(n) && n >= 0 && n < repos.length);\n\n if (indices.length === 0) {\n console.log(' No repos selected.');\n return [];\n }\n\n return indices.map((i) => ({ full_name: repos[i].full_name }));\n } finally {\n rl.close();\n }\n}\n\nexport async function promptRepoConnection(opts?: { linkRepo?: boolean }): Promise<void> {\n const localRepo = detectGitRepo();\n\n if (opts?.linkRepo && localRepo) {\n console.log('Connect repos to Synkro:\\n');\n try {\n const existing = await listProjects();\n const alreadyLinked = existing.some((p: any) =>\n p.repos?.some((r: any) => r.full_name === localRepo.fullName),\n );\n if (!alreadyLinked) {\n await createProject(localRepo.shortName, [{ full_name: localRepo.fullName }]);\n console.log(` ✓ Created project \"${localRepo.shortName}\" linked to ${localRepo.fullName}`);\n } else {\n console.log(` ✓ ${localRepo.fullName} is already linked to a Synkro project.`);\n }\n } catch (err) {\n console.warn(` ⚠ Could not link repo: ${(err as Error).message}`);\n }\n console.log();\n return;\n }\n\n const rl = createInterface({ input: process.stdin, output: process.stdout });\n\n try {\n console.log('Connect repos to Synkro:\\n');\n const options: string[] = [];\n if (localRepo) {\n options.push(`Link this repo (${localRepo.fullName})`);\n }\n options.push('Connect GitHub to select repos');\n options.push('Skip for now');\n\n options.forEach((opt, i) => {\n console.log(` ${i + 1}. ${opt}`);\n });\n console.log();\n\n const choice = await ask(rl, ' Choose (number): ');\n const choiceNum = parseInt(choice.trim(), 10);\n console.log();\n rl.close();\n\n const localIdx = localRepo ? 1 : -1;\n const githubIdx = localRepo ? 2 : 1;\n const skipIdx = localRepo ? 3 : 2;\n\n if (choiceNum === localIdx && localRepo) {\n try {\n const existing = await listProjects();\n const alreadyLinked = existing.some((p: any) =>\n p.repos?.some((r: any) => r.full_name === localRepo.fullName),\n );\n if (!alreadyLinked) {\n await createProject(localRepo.shortName, [{ full_name: localRepo.fullName }]);\n console.log(` ✓ Created project \"${localRepo.shortName}\" linked to ${localRepo.fullName}`);\n } else {\n console.log(` ✓ ${localRepo.fullName} is already linked to a Synkro project.`);\n }\n } catch (err) {\n console.warn(` ⚠ Could not link repo: ${(err as Error).message}`);\n }\n } else if (choiceNum === githubIdx) {\n const selectedRepos = await connectGithubAndSelectRepos();\n if (selectedRepos.length > 0) {\n try {\n const existing = await listProjects();\n const existingFullNames = new Set(\n existing.flatMap((p: any) => (p.repos || []).map((r: any) => r.full_name)),\n );\n const newRepos = selectedRepos.filter((r) => !existingFullNames.has(r.full_name));\n\n if (newRepos.length === 0) {\n console.log(' ✓ All selected repos are already linked.');\n } else {\n const projectName = newRepos.length === 1\n ? newRepos[0].full_name.split('/').pop() || 'Project'\n : 'Multi-Repo Project';\n await createProject(projectName, newRepos);\n console.log(` ✓ Linked ${newRepos.length} repo(s) to project \"${projectName}\"`);\n }\n } catch (err) {\n console.warn(` ⚠ Could not link repos: ${(err as Error).message}`);\n }\n }\n } else if (choiceNum === skipIdx) {\n console.log(' Skipped. Run `synkro link` later to connect repos.');\n } else {\n console.log(' Invalid choice. Skipping repo connection.');\n }\n } catch {\n rl.close();\n }\n console.log();\n}\n","/**\n * synkro setup-github — interactive setup for PR scanning.\n *\n * Flow:\n * 1. Ensure user is logged in (has JWT + user_id + org_id in ~/.synkro/credentials.json).\n * 2. Check if GitHub is connected via WorkOS Pipes.\n * 3. If not, get Pipes OAuth URL and open browser for the user to authorize.\n * 4. Poll until GitHub connection is established.\n * 5. Run `claude setup-token` to get Claude Code OAuth token.\n * 6. List accessible repos via GitHub API (using Pipes token).\n * 7. Interactive multi-select.\n * 8. Push secrets (SYNKRO_API_KEY + CLAUDE_CODE_OAUTH_TOKEN) to each repo.\n * 9. Write .github/workflows/synkro.yml to the local repo if cwd is one.\n */\nimport { createInterface } from 'node:readline/promises';\nimport { stdin as input, stdout as output } from 'node:process';\nimport { execSync, spawn as nodeSpawn } from 'node:child_process';\nimport { existsSync, readFileSync, unlinkSync } from 'node:fs';\nimport { homedir, platform } from 'node:os';\nimport { join } from 'node:path';\nimport { execFile } from 'node:child_process';\nimport {\n listAccessibleRepos,\n pushSecretsToRepo,\n writeWorkflowFile,\n findGitRoot,\n SECRET_NAMES,\n WORKFLOW_RELATIVE_PATH,\n} from '../installer/githubSetup.js';\nimport { isAuthenticated, getAccessToken, getUserInfo } from '../auth/stub.js';\n\nconst SYNKRO_DIR = join(homedir(), '.synkro');\nconst CONFIG_PATH = join(SYNKRO_DIR, 'config.env');\n\nfunction readConfig(): Record<string, string> {\n if (!existsSync(CONFIG_PATH)) return {};\n const out: Record<string, string> = {};\n for (const line of readFileSync(CONFIG_PATH, 'utf-8').split('\\n')) {\n const t = line.trim();\n if (!t || t.startsWith('#')) continue;\n const eq = t.indexOf('=');\n if (eq > 0) out[t.slice(0, eq).trim()] = t.slice(eq + 1).trim().replace(/^['\"]|['\"]$/g, '');\n }\n return out;\n}\n\nasync function prompt(rl: ReturnType<typeof createInterface>, q: string, opts: { silent?: boolean } = {}): Promise<string> {\n if (opts.silent) {\n process.stdout.write(q);\n const wasRaw = (process.stdin as any).isRaw;\n if ((process.stdin as any).setRawMode) (process.stdin as any).setRawMode(true);\n return await new Promise<string>((resolve) => {\n let chunk = '';\n const onData = (data: Buffer) => {\n const s = data.toString('utf-8');\n if (s === '\\r' || s === '\\n' || s === '\\r\\n') {\n process.stdin.removeListener('data', onData);\n if ((process.stdin as any).setRawMode) (process.stdin as any).setRawMode(wasRaw ?? false);\n process.stdout.write('\\n');\n resolve(chunk);\n return;\n }\n if (s === '\u0003') process.exit(130);\n if (s === '' || s === '\\b') { chunk = chunk.slice(0, -1); return; }\n chunk += s;\n };\n process.stdin.on('data', onData);\n });\n }\n return await rl.question(q);\n}\n\nfunction openBrowser(url: string): void {\n const os = platform();\n let bin: string;\n let args: string[];\n switch (os) {\n case 'darwin': bin = 'open'; args = [url]; break;\n case 'win32': bin = 'cmd'; args = ['/c', 'start', '', url]; break;\n default: bin = 'xdg-open'; args = [url]; break;\n }\n execFile(bin, args, () => {});\n}\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise(r => setTimeout(r, ms));\n}\n\nfunction captureClaudeSetupToken(): Promise<string> {\n const tmpFile = join(SYNKRO_DIR, `token-capture-${Date.now()}.raw`);\n return new Promise((resolve, reject) => {\n const proc = nodeSpawn('script', ['-q', tmpFile, 'claude', 'setup-token'], {\n stdio: 'inherit',\n });\n proc.on('error', (err) => reject(new Error(`Failed to spawn claude setup-token: ${err.message}`)));\n proc.on('close', (code) => {\n let raw = '';\n try { raw = readFileSync(tmpFile, 'utf-8'); } catch (e) {\n reject(new Error(`Could not read script output file: ${(e as Error).message}`));\n return;\n }\n try { unlinkSync(tmpFile); } catch {}\n if (code !== 0) { reject(new Error(`claude setup-token exited with code ${code}`)); return; }\n // Grab yellow-colored text segments (token is rendered in RGB 255,193,7)\n const yellowRe = /\\x1B\\[38;2;255;193;7m([^\\x1B]*)/g;\n let yellow = '';\n let m: RegExpExecArray | null;\n while ((m = yellowRe.exec(raw)) !== null) yellow += m[1];\n const token = yellow.replace(/\\s/g, '').match(/sk-ant-oat01-[A-Za-z0-9_-]+/);\n if (!token) { reject(new Error(`Could not find token in claude setup-token output (file=${raw.length}b, yellow=${yellow.length}b)`)); return; }\n resolve(token[0]);\n });\n });\n}\n\nasync function apiCall<T = any>(gatewayUrl: string, jwt: string, path: string, opts: RequestInit = {}): Promise<T> {\n const resp = await fetch(`${gatewayUrl}${path}`, {\n ...opts,\n headers: {\n 'Authorization': `Bearer ${jwt}`,\n 'Content-Type': 'application/json',\n ...(opts.headers || {}),\n },\n });\n if (!resp.ok) {\n const text = await resp.text().catch(() => '');\n throw new Error(`API ${resp.status}: ${text.slice(0, 200)}`);\n }\n return resp.json() as Promise<T>;\n}\n\n/**\n * Connect GitHub via WorkOS Pipes OAuth. Returns the token if connected, null if user\n * declines or times out. Exported so `install` can embed this in its flow.\n */\nexport async function connectGitHub(gatewayUrl: string, jwt: string, opts: { silent?: boolean } = {}): Promise<string | null> {\n // Check if already connected\n try {\n const result = await apiCall<{ connected: boolean; token?: string }>(\n gatewayUrl, jwt, '/api/v1/cli/github-token',\n );\n if (result.connected && result.token) {\n if (!opts.silent) console.log(' ✓ GitHub already connected via Synkro.');\n return result.token;\n }\n } catch {}\n\n // Get Pipes OAuth URL and open browser\n if (!opts.silent) console.log(' Opening browser to authorize GitHub...');\n try {\n const authResp = await apiCall<{ url: string }>(\n gatewayUrl, jwt, '/api/pipes-widget/authorize/github', { method: 'POST', body: '{}' },\n );\n openBrowser(authResp.url);\n if (!opts.silent) console.log(' Waiting for authorization...');\n } catch (err) {\n if (!opts.silent) console.error(` Failed to start GitHub authorization: ${(err as Error).message}`);\n return null;\n }\n\n // Poll until connected (max 2 minutes)\n const deadline = Date.now() + 120_000;\n while (Date.now() < deadline) {\n await sleep(2000);\n try {\n const result = await apiCall<{ connected: boolean; token?: string }>(\n gatewayUrl, jwt, '/api/v1/cli/github-token',\n );\n if (result.connected && result.token) {\n if (!opts.silent) console.log('\\n ✓ GitHub connected!');\n return result.token;\n }\n } catch {}\n if (!opts.silent) process.stdout.write('.');\n }\n if (!opts.silent) console.error('\\n Timed out waiting for GitHub authorization.');\n return null;\n}\n\nexport interface SetupGithubOptions {\n nonInteractive?: boolean;\n githubToken?: string;\n skipClaudeToken?: boolean;\n}\n\nexport async function setupGithubCommand(opts: SetupGithubOptions = {}): Promise<void> {\n if (!isAuthenticated()) {\n console.error('Not authenticated. Run `synkro-cli login` first.');\n process.exit(1);\n }\n const config = readConfig();\n const gatewayUrl = (config.SYNKRO_GATEWAY_URL || process.env.SYNKRO_GATEWAY_URL || 'https://api.synkro.sh').replace(/\\/$/, '');\n const jwt = getAccessToken();\n if (!jwt) {\n console.error('Could not load access token from ~/.synkro/credentials.json. Run `synkro-cli login`.');\n process.exit(1);\n }\n\n // ── 1. Mint CI API key ──────────────────────────────────────────────\n console.log('Requesting CI API key from Synkro...');\n let synkroCiApiKey: string;\n try {\n const minted = await apiCall<{ api_key: string; expires_at: string }>(\n gatewayUrl, jwt, '/api/v1/cli/ci-api-key', { method: 'POST', body: '{}' },\n );\n synkroCiApiKey = minted.api_key;\n console.log(` ✓ Issued CI key (${synkroCiApiKey.slice(0, 18)}…), expires ${minted.expires_at.slice(0, 10)}`);\n } catch (err) {\n console.error(`Failed to mint CI API key: ${(err as Error).message}`);\n process.exit(1);\n }\n\n // ── 2. Get GitHub token via WorkOS Pipes ────────────────────────────\n let ghToken: string;\n\n if (opts.githubToken) {\n ghToken = opts.githubToken;\n } else if (opts.nonInteractive) {\n // In non-interactive mode (CI), try Pipes first, fall back to gh CLI\n try {\n const result = await apiCall<{ connected: boolean; token?: string }>(\n gatewayUrl, jwt, '/api/v1/cli/github-token',\n );\n if (result.connected && result.token) {\n ghToken = result.token;\n } else {\n throw new Error('not connected');\n }\n } catch {\n try {\n ghToken = execSync('gh auth token', { encoding: 'utf-8', timeout: 5000 }).trim();\n } catch {\n console.error('GitHub not connected. Run `synkro-cli setup-github` interactively to connect.');\n return;\n }\n }\n } else {\n // Interactive mode — use Pipes OAuth\n console.log('\\nConnecting to GitHub...');\n const token = await connectGitHub(gatewayUrl, jwt);\n if (!token) {\n console.error('GitHub connection failed. Try again.');\n process.exit(1);\n }\n ghToken = token;\n console.log();\n }\n\n // ── 3. Claude Code OAuth token ──────────────────────────────────────\n let claudeToken: string | undefined;\n if (!opts.skipClaudeToken) {\n console.log('Generating Claude Code OAuth token...');\n console.log(' A browser window will open — authorize with your Claude account.\\n');\n try {\n claudeToken = await captureClaudeSetupToken();\n } catch (err) {\n console.error(`Failed to get Claude token: ${err instanceof Error ? err.message : String(err)}`);\n if (opts.nonInteractive) return;\n process.exit(1);\n }\n if (!claudeToken.startsWith('sk-ant-oat01-')) {\n console.error('Invalid token received from `claude setup-token`. Expected sk-ant-oat01-...');\n if (opts.nonInteractive) return;\n process.exit(1);\n }\n console.log(' Validating token...');\n try {\n const validateResult = execSync(\n 'claude --print --output-format json \"say ok\"',\n { env: { ...process.env, CLAUDE_CODE_OAUTH_TOKEN: claudeToken }, encoding: 'utf-8', timeout: 30_000, stdio: ['ignore', 'pipe', 'pipe'] },\n );\n const result = JSON.parse(validateResult);\n if (result.is_error) throw new Error(result.result || 'auth failed');\n console.log(' ✓ Token validated.\\n');\n } catch (err) {\n console.error(`Token validation failed: ${err instanceof Error ? err.message : String(err)}`);\n if (opts.nonInteractive) return;\n process.exit(1);\n }\n }\n\n // ── 4. Select repos ─────────────────────────────────────────────────\n let selected: Array<{ owner: string; repo: string; full_name: string }>;\n if (opts.nonInteractive) {\n let currentFullName: string | null = null;\n try {\n const remoteUrl = execSync('git remote get-url origin', { encoding: 'utf-8', timeout: 5000 }).trim();\n const m = remoteUrl.match(/(?:github\\.com)[:/](.+?)(?:\\.git)?$/);\n if (m) currentFullName = m[1];\n } catch {}\n if (!currentFullName) {\n console.warn(' ⚠ Not in a GitHub repo. Skipping PR scan setup.');\n return;\n }\n const [owner, repo] = currentFullName.split('/');\n selected = [{ owner, repo, full_name: currentFullName }];\n console.log(` Auto-selected repo: ${currentFullName}`);\n } else {\n console.log('Fetching accessible repos...');\n const repos = await listAccessibleRepos({ token: ghToken! });\n if (repos.length === 0) {\n console.error('No accessible repos found. Check your GitHub permissions.');\n process.exit(1);\n }\n console.log(`\\nFound ${repos.length} accessible repo(s):\\n`);\n repos.slice(0, 100).forEach((r, i) => {\n console.log(` ${String(i + 1).padStart(3)}. ${r.full_name}`);\n });\n console.log();\n const rl2 = createInterface({ input, output });\n const selectionRaw = await prompt(rl2, 'Select repos to enable (comma-separated numbers, e.g. 1,3,5): ');\n const selectedIdx = selectionRaw\n .split(',')\n .map((s) => parseInt(s.trim(), 10) - 1)\n .filter((n) => !isNaN(n) && n >= 0 && n < repos.length);\n if (selectedIdx.length === 0) {\n console.error('No valid selections.');\n rl2.close();\n process.exit(1);\n }\n selected = selectedIdx.map((i) => repos[i]);\n console.log(`\\nWill push secrets to ${selected.length} repo(s):`);\n for (const r of selected) console.log(` • ${r.full_name}`);\n console.log();\n const confirm = (await prompt(rl2, 'Continue? (yes/no): ')).trim().toLowerCase();\n if (confirm !== 'yes' && confirm !== 'y') {\n console.log('Cancelled.');\n rl2.close();\n process.exit(0);\n }\n rl2.close();\n }\n\n // ── 5. Push secrets ─────────────────────────────────────────────────\n console.log();\n for (const r of selected) {\n process.stdout.write(`Pushing secrets to ${r.full_name}... `);\n try {\n await pushSecretsToRepo(\n { token: ghToken! },\n r.owner,\n r.repo,\n {\n claudeCodeOauthToken: claudeToken,\n synkroApiKey: synkroCiApiKey,\n },\n );\n console.log('✓');\n } catch (err) {\n console.log(`✗ (${(err as Error).message})`);\n }\n }\n\n // ── 6. Write workflow file ──────────────────────────────────────────\n console.log();\n const gitRoot = findGitRoot(process.cwd());\n if (gitRoot) {\n const written = writeWorkflowFile(gitRoot);\n if (written) {\n console.log(`Wrote workflow: ${written}`);\n console.log('Commit and push it to enable PR scanning.');\n }\n } else {\n console.log('Not in a git repo. To enable scanning, add this file to your repo:');\n console.log(` Path: ${WORKFLOW_RELATIVE_PATH}`);\n console.log(` Content: run \\`synkro-cli setup-github\\` from inside a repo to write it automatically`);\n }\n\n console.log();\n console.log('✓ PR scan setup complete.');\n console.log(`Secrets pushed: ${SECRET_NAMES.CLAUDE_OAUTH}, ${SECRET_NAMES.SYNKRO_API_KEY}`);\n console.log('Open a PR on any selected repo to trigger your first Synkro scan.');\n}\n","/**\n * Fetch judge prompt metadata from the Synkro API.\n * Prompts are fetched live on every grade call — never cached to disk.\n * This module only fetches the version string for display during install.\n */\n\ninterface PromptsMetaResponse {\n version: string;\n}\n\nexport async function fetchJudgePrompts(opts: {\n gatewayUrl: string;\n jwt: string;\n forceRefresh?: boolean;\n}): Promise<{\n version: string;\n}> {\n const url = `${opts.gatewayUrl.replace(/\\/$/, '')}/api/v1/hook/config`;\n const resp = await fetch(url, {\n method: 'GET',\n headers: {\n 'Authorization': `Bearer ${opts.jwt}`,\n 'User-Agent': 'synkro-cli/1.0',\n },\n signal: AbortSignal.timeout(5000),\n });\n\n if (!resp.ok) {\n return { version: 'unknown' };\n }\n\n const data = await resp.json() as { prompts?: { version?: string } };\n return { version: data.prompts?.version ?? 'unknown' };\n}\n","/**\n * Inference provider detection.\n *\n * The source of truth is the user's inference_settings in TimescaleDB.\n * At install time, `/api/v1/cli/me` returns `local_inference: true` when\n * `gradingProvider = 'claude-code'`. The install flow writes\n * `SYNKRO_LOCAL_INFERENCE='yes'` into ~/.synkro/config.env.\n *\n * At runtime the hook scripts use a TCP probe (synkro_channel_up) to decide\n * routing — this module is only used by CLI commands and intent routers.\n */\n\nimport { existsSync, readFileSync } from 'node:fs';\nimport { homedir } from 'node:os';\nimport { join } from 'node:path';\n\nconst CONFIG_PATH = join(homedir(), '.synkro', 'config.env');\n\nexport function isLocalCCEnabled(): boolean {\n if (!existsSync(CONFIG_PATH)) return false;\n try {\n const content = readFileSync(CONFIG_PATH, 'utf-8');\n const match = content.match(/^SYNKRO_LOCAL_INFERENCE='([^']*)'/m);\n return match?.[1] === 'yes';\n } catch {\n return false;\n }\n}\n","/**\n * Synkro local-CC channel plugin source.\n *\n * Embedded as a string so `synkro install` can drop it on disk at\n * `~/.synkro/cc_sessions/synkro-channel.ts` without bundling extra files.\n *\n * The plugin:\n * - Registers as an MCP channel (`claude/channel` capability) so events\n * pushed via `mcp.notification()` arrive in Claude's context as\n * `<channel source=\"synkro-local\" req_id=\"...\" role=\"...\">...</channel>` tags.\n * - Listens on TCP at 127.0.0.1:SYNKRO_CHANNEL_PORT (default 8929) — pinned\n * to loopback so nothing leaves the host.\n * - Exposes a `reply` MCP tool. When Claude calls it with a `req_id` and\n * `result`, the plugin matches the open request and returns the result\n * to the HTTP caller.\n *\n * Run by Claude Code as a subprocess (stdio MCP transport). Started indirectly\n * by `claude --dangerously-load-development-channels server:synkro-local`,\n * which the pueue task wraps.\n */\n\nexport const SYNKRO_CHANNEL_DEFAULT_PORT = 8929;\n\nexport const CHANNEL_PLUGIN_SOURCE = `#!/usr/bin/env bun\n/**\n * Synkro local-CC channel plugin (auto-generated by \\`synkro install\\`).\n * DO NOT EDIT — your changes will be overwritten on next install.\n */\nimport { Server } from '@modelcontextprotocol/sdk/server/index.js';\nimport { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';\nimport { ListToolsRequestSchema, CallToolRequestSchema } from '@modelcontextprotocol/sdk/types.js';\n\nconst PORT = parseInt(process.env.SYNKRO_CHANNEL_PORT || '8929', 10);\nconst HOSTNAME = '127.0.0.1';\n\nconst REQUEST_TIMEOUT_MS = parseInt(process.env.SYNKRO_CHANNEL_TIMEOUT_MS || '120000', 10);\n\ninterface PendingRequest {\n resolve: (result: string) => void;\n reject: (err: Error) => void;\n timer: ReturnType<typeof setTimeout>;\n}\n\nconst pending = new Map<string, PendingRequest>();\nlet nextRequestId = 1;\n\nconst mcp = new Server(\n { name: 'synkro-local', version: '0.1.0' },\n {\n capabilities: {\n experimental: { 'claude/channel': {} },\n tools: {},\n },\n instructions: [\n 'Synkro local inference channel.',\n 'Each <channel source=\"synkro-local\" req_id=\"...\" role=\"...\"> event contains a',\n 'self-contained instruction block followed by the payload to evaluate. Treat it',\n 'as a fresh isolated request — IGNORE any prior conversation turns or context.',\n 'Do not call Read, Edit, Write, Bash, or any other tool. Do exactly one thing:',\n 'parse the request, produce the structured response described inside it, then',\n 'call the \\\\\\`reply\\\\\\` tool exactly once with the same req_id and the response',\n 'wrapped as the \\\\\\`result\\\\\\` argument (a string). Output no other text.',\n ].join(' '),\n },\n);\n\nmcp.setRequestHandler(ListToolsRequestSchema, async () => ({\n tools: [{\n name: 'reply',\n description: 'Return the response for a Synkro local-inference request',\n inputSchema: {\n type: 'object',\n properties: {\n req_id: { type: 'string', description: 'The req_id from the channel event being answered' },\n result: { type: 'string', description: 'The response text. For grading/classification roles, the JSON-tagged verdict the role requested.' },\n },\n required: ['req_id', 'result'],\n },\n }],\n}));\n\nmcp.setRequestHandler(CallToolRequestSchema, async req => {\n if (req.params.name !== 'reply') {\n throw new Error('unknown tool: ' + req.params.name);\n }\n const args = req.params.arguments as { req_id?: string; result?: string };\n const reqId = String(args.req_id ?? '');\n const result = String(args.result ?? '');\n const p = pending.get(reqId);\n if (p) {\n clearTimeout(p.timer);\n pending.delete(reqId);\n p.resolve(result);\n return { content: [{ type: 'text', text: 'ok' }] };\n }\n return { content: [{ type: 'text', text: 'unknown req_id (likely already timed out)' }] };\n});\n\n// Bind the listener BEFORE awaiting mcp.connect — Bun.serve is synchronous\n// and must run on the script's first tick, otherwise the stdio transport's\n// read loop can starve the serve setup.\nBun.serve({\n port: PORT,\n hostname: HOSTNAME,\n idleTimeout: 0,\n async fetch(req) {\n const url = new URL(req.url);\n if (url.pathname === '/healthz') {\n return new Response(JSON.stringify({ ok: true, pending: pending.size }), {\n headers: { 'Content-Type': 'application/json' },\n });\n }\n if (req.method !== 'POST' || url.pathname !== '/submit') {\n return new Response('not found', { status: 404 });\n }\n let body: { role?: string; content?: string };\n try {\n body = await req.json() as typeof body;\n } catch {\n return new Response('invalid json', { status: 400 });\n }\n const role = String(body.role ?? '');\n const content = String(body.content ?? '');\n if (!role || !content) {\n return new Response('missing role/content', { status: 400 });\n }\n const reqId = 'r' + (nextRequestId++) + Date.now().toString(36);\n const result = await new Promise<string>((resolve, reject) => {\n const timer = setTimeout(() => {\n pending.delete(reqId);\n reject(new Error('timeout waiting for reply (' + REQUEST_TIMEOUT_MS + 'ms)'));\n }, REQUEST_TIMEOUT_MS);\n pending.set(reqId, { resolve, reject, timer });\n mcp.notification({\n method: 'notifications/claude/channel',\n params: {\n content,\n meta: { req_id: reqId, role },\n },\n }).catch(err => {\n clearTimeout(timer);\n pending.delete(reqId);\n reject(err instanceof Error ? err : new Error(String(err)));\n });\n }).catch(err => {\n return JSON.stringify({ error: err instanceof Error ? err.message : String(err) });\n });\n if (typeof result === 'string' && result.startsWith('{\"error\":')) {\n return new Response(result, { status: 504, headers: { 'Content-Type': 'application/json' } });\n }\n return new Response(JSON.stringify({ result }), {\n headers: { 'Content-Type': 'application/json' },\n });\n },\n});\n\nprocess.on('SIGTERM', () => process.exit(0));\nprocess.on('SIGINT', () => process.exit(0));\n\n// MCP stdio handshake last. The transport's read loop keeps the process\n// alive; the TCP listener is already bound at this point so the CLI can\n// hit it as soon as Claude finishes its end of the handshake.\nawait mcp.connect(new StdioServerTransport());\n`;\n","/**\n * Filesystem setup for the local-CC channel plugin.\n *\n * Idempotent: safe to call multiple times. Writes:\n * ~/.synkro/cc_sessions/synkro-channel.ts (the Bun MCP plugin)\n * ~/.synkro/cc_sessions/package.json (deps for the plugin)\n * ~/.synkro/cc_sessions/.claude/settings.json (fastMode:true scoped to this cwd)\n *\n * Then patches ~/.claude.json to register the plugin under mcpServers, and runs\n * `bun install` in the session dir so @modelcontextprotocol/sdk is on disk.\n */\n\nimport { existsSync, mkdirSync, writeFileSync, readFileSync, chmodSync, copyFileSync, renameSync, unlinkSync, openSync, fsyncSync, closeSync } from 'node:fs';\nimport { join } from 'node:path';\nimport { homedir } from 'node:os';\nimport { spawnSync } from 'node:child_process';\nimport { CHANNEL_PLUGIN_SOURCE } from './channelSource.js';\n\nexport const CLAUDE_JSON_BACKUP_PATH = join(homedir(), '.claude.json.synkro-bak');\n\nexport const SESSION_DIR = join(homedir(), '.synkro', 'cc_sessions');\nexport const PLUGIN_PATH = join(SESSION_DIR, 'synkro-channel.ts');\nexport const PLUGIN_PKG_PATH = join(SESSION_DIR, 'package.json');\nexport const PLUGIN_SETTINGS_DIR = join(SESSION_DIR, '.claude');\nexport const PLUGIN_SETTINGS_PATH = join(PLUGIN_SETTINGS_DIR, 'settings.json');\nexport const PROJECT_MCP_PATH = join(SESSION_DIR, '.mcp.json');\nexport const CLAUDE_JSON_PATH = join(homedir(), '.claude.json');\nexport const RUN_SCRIPT_PATH = join(SESSION_DIR, 'run-claude.sh');\nexport const TMUX_SESSION_NAME = 'synkro-local-cc';\n\nexport const SESSION_DIR_2 = join(homedir(), '.synkro', 'cc_sessions_2');\nexport const PLUGIN_PATH_2 = join(SESSION_DIR_2, 'synkro-channel.ts');\nexport const PLUGIN_PKG_PATH_2 = join(SESSION_DIR_2, 'package.json');\nexport const PLUGIN_SETTINGS_DIR_2 = join(SESSION_DIR_2, '.claude');\nexport const PLUGIN_SETTINGS_PATH_2 = join(PLUGIN_SETTINGS_DIR_2, 'settings.json');\nexport const PROJECT_MCP_PATH_2 = join(SESSION_DIR_2, '.mcp.json');\nexport const RUN_SCRIPT_PATH_2 = join(SESSION_DIR_2, 'run-claude.sh');\nexport const TMUX_SESSION_NAME_2 = 'synkro-local-cc-2';\nexport const CHANNEL_2_PORT = 8930;\n\n/**\n * Bash wrapper that owns the tmux session hosting `claude`. Pueue runs this\n * script as its task. The script:\n * 1. Kills any prior tmux session by the same name (idempotent restart).\n * 2. Starts a detached tmux session running claude. tmux gives claude a\n * real pty, so it stays in interactive mode (vs. dropping into --print).\n * 3. Blocks on tmux's lifetime so pueue's task duration mirrors claude's.\n * `synkro local-cc stop` kills the tmux session, which unblocks the\n * poll and lets pueue mark the task done.\n *\n * Dismissal of startup dialogs (workspace trust / dev-channels /\n * MCP consent) is owned by the TS-side waitForChannelReady, which polls\n * the channel TCP port and injects \\`tmux send-keys ... Enter\\` each tick\n * the probe fails. That's adaptive (no fixed window) and self-terminating\n * (stops the moment the port binds).\n */\nconst RUN_SCRIPT_SOURCE = `#!/usr/bin/env bash\n# Auto-generated by \\`synkro install\\`. Do not edit.\nset -uo pipefail\n\nSESSION=${TMUX_SESSION_NAME}\nLOG=\"$HOME/.synkro/cc_sessions/run-claude.log\"\n\nlog() { echo \"[$(date '+%H:%M:%S')] $*\" >> \"$LOG\"; echo \"$*\"; }\n\n# Pre-flight checks\nif ! command -v claude >/dev/null 2>&1; then\n log \"ERROR: claude CLI not found on PATH. Install Claude Code first.\"\n exit 1\nfi\n\nif ! command -v tmux >/dev/null 2>&1; then\n log \"ERROR: tmux not found on PATH.\"\n exit 1\nfi\n\n# Check claude is authenticated\nif ! claude --version >/dev/null 2>&1; then\n log \"ERROR: claude --version failed. Is Claude Code installed correctly?\"\n exit 1\nfi\n\nlog \"Starting local-CC session...\"\nlog \"claude version: $(claude --version 2>&1 | head -1)\"\n\n# Kill any previous session so restarts come up clean.\ntmux kill-session -t \"=$SESSION\" 2>/dev/null || true\n\n# Start claude inside a detached tmux session so it has a real pty.\n# Redirect stderr to the log so we can see why it dies.\ntmux new-session -d -s \"$SESSION\" \\\\\n \"claude --dangerously-load-development-channels server:synkro-local --dangerously-skip-permissions --setting-sources project,local --model claude-sonnet-4-6 2>>$LOG; echo 'claude exited with code '\\$'?' >> $LOG\"\n\n# Claude's --dangerously-load-development-channels shows a confirmation\n# prompt: option 1 = \"I am using this for local development\" (accept),\n# option 2 = \"Exit\". Auto-accept by sending '1' + Enter.\nsleep 3\nif tmux has-session -t \"=$SESSION\" 2>/dev/null; then\n tmux send-keys -t \"$SESSION\" '1' 2>/dev/null || true\n sleep 1\n tmux send-keys -t \"$SESSION\" Enter 2>/dev/null || true\n sleep 1\n # Additional Enter for any follow-up prompts (workspace trust, MCP consent)\n tmux send-keys -t \"$SESSION\" Enter 2>/dev/null || true\n log \"Sent auto-accept keys to claude session.\"\nfi\n\nsleep 2\nif ! tmux has-session -t \"$SESSION\" 2>/dev/null; then\n log \"ERROR: tmux session died immediately. Check $LOG for details.\"\n log \"Try running claude manually to verify auth: claude --print 'say ok'\"\n exit 1\nfi\n\nlog \"tmux session started successfully.\"\n\n# Block on the tmux session so pueue's task lifetime tracks claude's.\nwhile tmux has-session -t \"=$SESSION\" 2>/dev/null; do\n sleep 5\ndone\n\nlog \"tmux session ended.\"\n`;\n\nconst RUN_SCRIPT_SOURCE_2 = `#!/usr/bin/env bash\n# Auto-generated by \\`synkro install\\`. Channel 2 (CWE scan, port ${CHANNEL_2_PORT}).\nset -uo pipefail\n\nSESSION=${TMUX_SESSION_NAME_2}\nLOG=\"$HOME/.synkro/cc_sessions_2/run-claude.log\"\n\nlog() { echo \"[$(date '+%H:%M:%S')] $*\" >> \"$LOG\"; echo \"$*\"; }\n\nif ! command -v claude >/dev/null 2>&1; then\n log \"ERROR: claude CLI not found on PATH.\"\n exit 1\nfi\n\nif ! command -v tmux >/dev/null 2>&1; then\n log \"ERROR: tmux not found on PATH.\"\n exit 1\nfi\n\nif ! claude --version >/dev/null 2>&1; then\n log \"ERROR: claude --version failed.\"\n exit 1\nfi\n\nlog \"Starting local-CC channel 2 (port ${CHANNEL_2_PORT})...\"\nlog \"claude version: $(claude --version 2>&1 | head -1)\"\n\ntmux kill-session -t \"=$SESSION\" 2>/dev/null || true\n\ntmux new-session -d -s \"$SESSION\" \\\\\n \"SYNKRO_CHANNEL_PORT=${CHANNEL_2_PORT} claude --dangerously-load-development-channels server:synkro-local --dangerously-skip-permissions --setting-sources project,local --model claude-sonnet-4-6 2>>$LOG; echo 'claude exited with code '\\$'?' >> $LOG\"\n\nsleep 3\nif tmux has-session -t \"=$SESSION\" 2>/dev/null; then\n tmux send-keys -t \"$SESSION\" '1' 2>/dev/null || true\n sleep 1\n tmux send-keys -t \"$SESSION\" Enter 2>/dev/null || true\n sleep 1\n tmux send-keys -t \"$SESSION\" Enter 2>/dev/null || true\n log \"Sent auto-accept keys to channel 2 session.\"\nfi\n\nsleep 2\nif ! tmux has-session -t \"$SESSION\" 2>/dev/null; then\n log \"ERROR: tmux session died immediately. Check $LOG for details.\"\n exit 1\nfi\n\nlog \"tmux session started successfully (port ${CHANNEL_2_PORT}).\"\n\nwhile tmux has-session -t \"=$SESSION\" 2>/dev/null; do\n sleep 5\ndone\n\nlog \"tmux session ended.\"\n`;\n\nconst MCP_SERVER_NAME = 'synkro-local';\n\nconst PLUGIN_PACKAGE_JSON = JSON.stringify(\n {\n name: 'synkro-local-channel',\n private: true,\n version: '0.1.0',\n type: 'module',\n dependencies: {\n '@modelcontextprotocol/sdk': '^1.0.0',\n },\n },\n null,\n 2,\n) + '\\n';\n\nexport class LocalCCInstallError extends Error {\n constructor(message: string, public readonly cause?: unknown) {\n super(message);\n this.name = 'LocalCCInstallError';\n }\n}\n\nfunction writePluginFiles(): void {\n mkdirSync(SESSION_DIR, { recursive: true });\n mkdirSync(PLUGIN_SETTINGS_DIR, { recursive: true });\n writeFileSync(PLUGIN_PATH, CHANNEL_PLUGIN_SOURCE, 'utf-8');\n chmodSync(PLUGIN_PATH, 0o755);\n writeFileSync(PLUGIN_PKG_PATH, PLUGIN_PACKAGE_JSON, 'utf-8');\n writeFileSync(\n PLUGIN_SETTINGS_PATH,\n JSON.stringify({\n fastMode: true,\n enabledMcpjsonServers: ['synkro-local'],\n }, null, 2) + '\\n',\n 'utf-8',\n );\n writeFileSync(RUN_SCRIPT_PATH, RUN_SCRIPT_SOURCE, 'utf-8');\n chmodSync(RUN_SCRIPT_PATH, 0o755);\n\n // Channel 2 — identical plugin, different port + tmux session\n mkdirSync(SESSION_DIR_2, { recursive: true });\n mkdirSync(PLUGIN_SETTINGS_DIR_2, { recursive: true });\n writeFileSync(PLUGIN_PATH_2, CHANNEL_PLUGIN_SOURCE, 'utf-8');\n chmodSync(PLUGIN_PATH_2, 0o755);\n writeFileSync(PLUGIN_PKG_PATH_2, PLUGIN_PACKAGE_JSON, 'utf-8');\n writeFileSync(\n PLUGIN_SETTINGS_PATH_2,\n JSON.stringify({\n fastMode: true,\n enabledMcpjsonServers: ['synkro-local'],\n }, null, 2) + '\\n',\n 'utf-8',\n );\n writeFileSync(RUN_SCRIPT_PATH_2, RUN_SCRIPT_SOURCE_2, 'utf-8');\n chmodSync(RUN_SCRIPT_PATH_2, 0o755);\n}\n\nfunction runBunInstall(): void {\n for (const dir of [SESSION_DIR, SESSION_DIR_2]) {\n const r = spawnSync('bun', ['install', '--silent'], {\n cwd: dir,\n encoding: 'utf-8',\n timeout: 120_000,\n });\n if (r.status !== 0) {\n throw new LocalCCInstallError(\n `bun install failed in ${dir}: ${r.stderr || r.stdout || 'unknown'}`,\n );\n }\n }\n}\n\ninterface ClaudeJson {\n mcpServers?: Record<string, { command: string; args?: string[]; env?: Record<string, string> }>;\n projects?: Record<string, Record<string, unknown>>;\n [k: string]: unknown;\n}\n\n/**\n * Safely apply `mutator` to ~/.claude.json with five layers of protection:\n * 1. Refuse to operate on malformed input — never try to \"fix\" a corrupted file.\n * 2. Take a rolling backup at ~/.claude.json.synkro-bak before any write.\n * 3. Validate the post-mutation result is still well-formed JSON and didn't\n * lose any top-level keys that existed in the original.\n * 4. Atomic write via tmp + fsync + rename(2) (POSIX-atomic).\n * 5. Restore from backup on any error in the write phase.\n *\n * If the file doesn't exist (no claude install on this machine), the mutation\n * is silently skipped — we don't create the file, since claude owns it.\n */\nfunction safelyMutateClaudeJson(mutator: (json: ClaudeJson) => boolean): void {\n if (!existsSync(CLAUDE_JSON_PATH)) {\n // claude.json doesn't exist; nothing to mutate. We don't create it.\n return;\n }\n\n // (1) Read + parse, refuse to proceed on malformed input.\n const originalText = readFileSync(CLAUDE_JSON_PATH, 'utf-8');\n let parsed: ClaudeJson;\n try {\n parsed = JSON.parse(originalText) as ClaudeJson;\n } catch (err) {\n throw new LocalCCInstallError(\n `refusing to modify malformed ${CLAUDE_JSON_PATH}: ${(err as Error).message}. ` +\n `Please fix the JSON manually before retrying.`,\n err,\n );\n }\n\n const originalKeys = new Set(Object.keys(parsed));\n\n // (Mutator returns true if it changed anything; false means no-op.)\n const dirty = mutator(parsed);\n if (!dirty) return;\n\n // (3a) Validate the in-memory mutation didn't drop a top-level key.\n for (const k of originalKeys) {\n if (!(k in parsed)) {\n throw new LocalCCInstallError(\n `refusing to write ${CLAUDE_JSON_PATH}: mutator dropped top-level key \"${k}\". ` +\n `This is a bug — please report.`,\n );\n }\n }\n\n const newText = JSON.stringify(parsed, null, 2) + '\\n';\n\n // (3b) Round-trip parse to guarantee well-formed output before any write.\n try {\n JSON.parse(newText);\n } catch (err) {\n throw new LocalCCInstallError(\n `refusing to write ${CLAUDE_JSON_PATH}: serialized result is not valid JSON. ` +\n `This is a bug — please report.`,\n err,\n );\n }\n\n // (2) Take a rolling backup of the *original* (pre-mutation) bytes.\n copyFileSync(CLAUDE_JSON_PATH, CLAUDE_JSON_BACKUP_PATH);\n\n // (4) Atomic write: tmp + fsync + rename. This guarantees a reader\n // never sees a half-written file — they see either the old or new.\n const tmpPath = `${CLAUDE_JSON_PATH}.synkro-tmp.${process.pid}`;\n try {\n writeFileSync(tmpPath, newText, 'utf-8');\n // fsync the tmp file so the bytes are durably on disk before rename.\n const fd = openSync(tmpPath, 'r');\n try { fsyncSync(fd); } finally { closeSync(fd); }\n renameSync(tmpPath, CLAUDE_JSON_PATH);\n } catch (err) {\n // (5) Best-effort recovery: restore the original from backup.\n try { unlinkSync(tmpPath); } catch { /* ignore */ }\n try { copyFileSync(CLAUDE_JSON_BACKUP_PATH, CLAUDE_JSON_PATH); } catch { /* ignore */ }\n throw new LocalCCInstallError(\n `failed to write ${CLAUDE_JSON_PATH}: ${(err as Error).message}. ` +\n `Backup at ${CLAUDE_JSON_BACKUP_PATH} preserves the prior state.`,\n err,\n );\n }\n}\n\n/**\n * Register the synkro-local MCP server at PROJECT scope (.mcp.json inside\n * ~/.synkro/cc_sessions) — NOT at user scope in ~/.claude.json. User scope\n * would make every claude session on the machine spawn the plugin and race\n * to bind the UDS, which corrupts request routing.\n */\nfunction writeProjectMcpJson(): void {\n const mcp = {\n mcpServers: {\n [MCP_SERVER_NAME]: {\n command: 'bun',\n args: [PLUGIN_PATH],\n },\n },\n };\n writeFileSync(PROJECT_MCP_PATH, JSON.stringify(mcp, null, 2) + '\\n', 'utf-8');\n\n // Channel 2 uses the same plugin source but on a different port\n const mcp2 = {\n mcpServers: {\n [MCP_SERVER_NAME]: {\n command: 'bun',\n args: [PLUGIN_PATH_2],\n },\n },\n };\n writeFileSync(PROJECT_MCP_PATH_2, JSON.stringify(mcp2, null, 2) + '\\n', 'utf-8');\n}\n\n/**\n * Pre-accept the workspace trust dialog and approve the project-scoped MCP\n * server in ~/.claude.json so claude doesn't block on those prompts under\n * tmux. Also strips any stale top-level mcpServers[\"synkro-local\"] left over\n * from previous (incorrect) installs.\n *\n * All mutations go through safelyMutateClaudeJson — see that function for\n * the integrity guarantees.\n */\nfunction patchClaudeJson(): void {\n safelyMutateClaudeJson(parsed => {\n let dirty = false;\n\n // Remove any stale user-scope registration from previous installs.\n if (parsed.mcpServers && typeof parsed.mcpServers === 'object' && parsed.mcpServers[MCP_SERVER_NAME]) {\n delete parsed.mcpServers[MCP_SERVER_NAME];\n dirty = true;\n }\n\n if (!parsed.projects || typeof parsed.projects !== 'object') {\n parsed.projects = {};\n }\n const projects = parsed.projects as Record<string, Record<string, unknown>>;\n\n for (const dir of [SESSION_DIR, SESSION_DIR_2]) {\n const existing = projects[dir] && typeof projects[dir] === 'object'\n ? projects[dir]\n : {};\n const wantEnabled = Array.from(new Set([\n ...((existing.enabledMcpjsonServers as string[] | undefined) ?? []),\n MCP_SERVER_NAME,\n ]));\n const next = {\n ...existing,\n hasTrustDialogAccepted: true,\n hasCompletedProjectOnboarding: true,\n enabledMcpjsonServers: wantEnabled,\n };\n if (\n existing.hasTrustDialogAccepted !== true ||\n existing.hasCompletedProjectOnboarding !== true ||\n JSON.stringify(existing.enabledMcpjsonServers ?? []) !== JSON.stringify(wantEnabled)\n ) {\n projects[dir] = next;\n dirty = true;\n }\n }\n return dirty;\n });\n}\n\n/**\n * Run the full local-CC install. Throws LocalCCInstallError on any failure\n * the caller is expected to surface. Does NOT start the pueue task — call\n * `pueue.ensureRunning()` after for that.\n */\nexport function installLocalCC(): { sessionDir: string; pluginPath: string } {\n // bun is required for the plugin runtime — auto-install if missing.\n let bunCheck = spawnSync('bun', ['--version'], { encoding: 'utf-8' });\n if (bunCheck.status !== 0) {\n if (process.platform === 'darwin') {\n console.log(' Installing bun via brew...');\n const brewR = spawnSync('brew', ['install', 'oven-sh/bun/bun'], { encoding: 'utf-8', stdio: 'inherit', timeout: 120_000 });\n if (brewR.status !== 0) {\n throw new LocalCCInstallError('bun auto-install failed. Install manually: curl -fsSL https://bun.sh/install | bash');\n }\n bunCheck = spawnSync('bun', ['--version'], { encoding: 'utf-8' });\n if (bunCheck.status !== 0) {\n throw new LocalCCInstallError('bun installed but not found on PATH. Restart your terminal and re-run install.');\n }\n } else {\n throw new LocalCCInstallError('bun is required. Install it: curl -fsSL https://bun.sh/install | bash');\n }\n }\n\n writePluginFiles();\n runBunInstall();\n writeProjectMcpJson();\n patchClaudeJson();\n\n return { sessionDir: SESSION_DIR, pluginPath: PLUGIN_PATH };\n}\n\n/**\n * Strip every trace of local-cc from ~/.claude.json:\n * - user-scope mcpServers[\"synkro-local\"] (legacy from older installs)\n * - projects[SESSION_DIR] entry (workspace trust + enabledMcpjsonServers we added)\n *\n * Goes through safelyMutateClaudeJson for the same integrity guarantees as\n * patchClaudeJson — atomic write, backup, post-write validation. Errors are\n * surfaced so the caller sees them, not silently swallowed.\n *\n * Plugin files in ~/.synkro/cc_sessions/ are removed by the caller via\n * `rm -rf ~/.synkro` when uninstalling with --purge.\n */\nexport function uninstallLocalCC(): void {\n safelyMutateClaudeJson(parsed => {\n let dirty = false;\n if (parsed.mcpServers && parsed.mcpServers[MCP_SERVER_NAME]) {\n delete parsed.mcpServers[MCP_SERVER_NAME];\n dirty = true;\n }\n for (const dir of [SESSION_DIR, SESSION_DIR_2]) {\n if (parsed.projects && typeof parsed.projects === 'object' && parsed.projects[dir]) {\n delete parsed.projects[dir];\n dirty = true;\n }\n }\n return dirty;\n });\n}\n","/**\n * Pueue lifecycle for the local-CC `claude` process.\n *\n * One labelled pueue task per user, started at install (or on demand) and\n * managed via the `pueue` CLI. We assume `pueue` and `pueued` are installed\n * and the daemon is running — surface a friendly error otherwise.\n */\n\nimport { execFileSync, spawnSync, spawn } from 'node:child_process';\nimport { homedir } from 'node:os';\nimport { join } from 'node:path';\nimport { connect } from 'node:net';\n\nexport const TASK_LABEL = 'synkro-local-cc';\nexport const TMUX_SESSION = 'synkro-local-cc';\nexport const SESSION_DIR = join(homedir(), '.synkro', 'cc_sessions');\n\nexport const TASK_LABEL_2 = 'synkro-local-cc-2';\nexport const TMUX_SESSION_2 = 'synkro-local-cc-2';\nexport const SESSION_DIR_2 = join(homedir(), '.synkro', 'cc_sessions_2');\n\nexport class PueueError extends Error {\n constructor(message: string, public readonly cause?: unknown) {\n super(message);\n this.name = 'PueueError';\n }\n}\n\ninterface PueueStatusEntry {\n id: number;\n label?: string;\n status: string | { Running?: unknown; Done?: { result?: string } };\n command: string;\n path: string;\n}\n\ninterface PueueStatus {\n tasks: Record<string, PueueStatusEntry>;\n}\n\nfunction pueueAvailable(): void {\n const r = spawnSync('pueue', ['--version'], { encoding: 'utf-8' });\n if (r.status !== 0) {\n throw new PueueError('pueue CLI not found on PATH. Install pueue (https://github.com/Nukesor/pueue) and start `pueued`.');\n }\n}\n\nfunction statusJson(): PueueStatus {\n pueueAvailable();\n const r = spawnSync('pueue', ['status', '--json'], { encoding: 'utf-8' });\n if (r.status !== 0) {\n throw new PueueError(`pueue status failed: ${r.stderr || r.stdout || 'unknown error'} — is pueued running?`);\n }\n try {\n return JSON.parse(r.stdout) as PueueStatus;\n } catch (err) {\n throw new PueueError(`pueue status returned non-JSON output: ${r.stdout.slice(0, 200)}`, err);\n }\n}\n\nexport interface TaskInfo {\n id: number;\n label: string;\n status: string;\n command: string;\n cwd: string;\n}\n\nfunction statusName(s: PueueStatusEntry['status']): string {\n if (typeof s === 'string') return s;\n if (s && typeof s === 'object') {\n if ('Running' in s) return 'Running';\n if ('Done' in s) {\n const result = (s as { Done?: { result?: string | object } }).Done?.result;\n if (typeof result === 'string') return `Done (${result})`;\n if (result && typeof result === 'object') return `Done (${Object.keys(result)[0] ?? 'unknown'})`;\n return 'Done';\n }\n return Object.keys(s)[0] ?? 'unknown';\n }\n return 'unknown';\n}\n\nexport interface ChannelIdentity {\n taskLabel: string;\n tmuxSession: string;\n sessionDir: string;\n}\n\nexport const CHANNEL_PRIMARY: ChannelIdentity = { taskLabel: TASK_LABEL, tmuxSession: TMUX_SESSION, sessionDir: SESSION_DIR };\nexport const CHANNEL_SECONDARY: ChannelIdentity = { taskLabel: TASK_LABEL_2, tmuxSession: TMUX_SESSION_2, sessionDir: SESSION_DIR_2 };\n\n/** Find a synkro local-cc task by label. Returns null if absent. */\nexport function findTask(channel: ChannelIdentity = CHANNEL_PRIMARY): TaskInfo | null {\n const data = statusJson();\n for (const [id, t] of Object.entries(data.tasks)) {\n if (t.label === channel.taskLabel) {\n return {\n id: Number(id),\n label: t.label,\n status: statusName(t.status),\n command: t.command,\n cwd: t.path,\n };\n }\n }\n return null;\n}\n\n/** True if a synkro local-cc task is currently Running. */\nexport function isRunning(channel: ChannelIdentity = CHANNEL_PRIMARY): boolean {\n const t = findTask(channel);\n return t?.status === 'Running';\n}\n\nexport interface StartOptions {\n /** Override the channel plugin script path. */\n pluginPath?: string;\n /** Working directory for the claude process. */\n cwd?: string;\n /** Which channel to start (default: primary). */\n channel?: ChannelIdentity;\n}\n\n/**\n * Add a fresh pueue task for the local-cc claude session. Removes any prior\n * task with the same label first (Done or otherwise) so subsequent calls\n * always replace cleanly.\n */\nexport function startTask(opts: StartOptions = {}): TaskInfo {\n const ch = opts.channel ?? CHANNEL_PRIMARY;\n const cwd = opts.cwd ?? ch.sessionDir;\n // Remove ALL tasks with this label (not just the first) to clear stale duplicates\n let existing = findTask(ch);\n while (existing) {\n if (existing.status === 'Running' || existing.status === 'Queued') {\n spawnSync('tmux', ['kill-session', '-t', `=${ch.tmuxSession}`], { encoding: 'utf-8' });\n spawnSync('pueue', ['kill', String(existing.id)], { encoding: 'utf-8' });\n for (let i = 0; i < 10; i++) {\n const check = findTask(ch);\n if (!check || check.id !== existing.id || (check.status !== 'Running' && check.status !== 'Queued')) break;\n spawnSync('sleep', ['0.5'], { encoding: 'utf-8' });\n }\n }\n spawnSync('pueue', ['remove', String(existing.id)], { encoding: 'utf-8' });\n existing = findTask(ch);\n }\n\n const runScript = join(cwd, 'run-claude.sh');\n const args = [\n 'add',\n '--label', ch.taskLabel,\n '--working-directory', cwd,\n '--',\n 'bash', runScript,\n ];\n\n const r = spawnSync('pueue', args, { encoding: 'utf-8' });\n if (r.status !== 0) {\n throw new PueueError(`pueue add failed: ${r.stderr || r.stdout}`);\n }\n const created = findTask(ch);\n if (!created) {\n throw new PueueError(`pueue add succeeded but no task with label ${ch.taskLabel} found`);\n }\n return created;\n}\n\n/** Stop and remove the synkro local-cc task. Also kills the tmux session\n * that owns the underlying claude process. No-op if neither is present. */\nexport function stopTask(channel: ChannelIdentity = CHANNEL_PRIMARY): void {\n spawnSync('tmux', ['kill-session', '-t', `=${channel.tmuxSession}`], { encoding: 'utf-8' });\n // Remove ALL tasks with this label, waiting for each kill to land\n let t = findTask(channel);\n while (t) {\n if (t.status === 'Running' || t.status === 'Queued') {\n spawnSync('pueue', ['kill', String(t.id)], { encoding: 'utf-8' });\n // Wait for the task to leave Running state before removing\n for (let i = 0; i < 10; i++) {\n const check = findTask(channel);\n if (!check || check.id !== t.id || (check.status !== 'Running' && check.status !== 'Queued')) break;\n spawnSync('sleep', ['0.5'], { encoding: 'utf-8' });\n }\n }\n spawnSync('pueue', ['remove', String(t.id)], { encoding: 'utf-8' });\n t = findTask(channel);\n }\n}\n\n/** Print the last N log lines for the task. Returns the raw text. */\nexport function tailLogs(lines = 80, channel: ChannelIdentity = CHANNEL_PRIMARY): string {\n const t = findTask(channel);\n if (!t) return `(no ${channel.taskLabel} task)`;\n const r = spawnSync('pueue', ['log', '--lines', String(lines), String(t.id)], { encoding: 'utf-8' });\n return r.stdout || r.stderr || '(no output)';\n}\n\n/** Ensure the task is running; start it if not. Returns the task info. */\nexport function ensureRunning(opts: StartOptions = {}): TaskInfo {\n const ch = opts.channel ?? CHANNEL_PRIMARY;\n const t = findTask(ch);\n if (t && t.status === 'Running') return t;\n return startTask(opts);\n}\n\n/** Probe a TCP port — true if something accepts a connection within timeoutMs. */\nfunction probePort(host: string, port: number, timeoutMs = 500): Promise<boolean> {\n return new Promise(resolve => {\n const sock = connect(port, host);\n const done = (ok: boolean) => {\n try { sock.destroy(); } catch { /* noop */ }\n resolve(ok);\n };\n sock.once('connect', () => done(true));\n sock.once('error', () => done(false));\n sock.setTimeout(timeoutMs, () => done(false));\n });\n}\n\n/** Dismiss startup prompts in the tmux session. Best-effort.\n * Sends '1' (dev-channels accept) then Enter (other prompts). */\nfunction tmuxDismissPrompts(tmuxSession: string = TMUX_SESSION): void {\n spawnSync('tmux', ['send-keys', '-t', tmuxSession, '1'], { encoding: 'utf-8' });\n spawnSync('tmux', ['send-keys', '-t', tmuxSession, 'Enter'], { encoding: 'utf-8' });\n}\n\n/**\n * Wait up to `timeoutMs` for the channel TCP listener to come up. Each tick\n * the probe fails, send an Enter into the tmux session — that walks claude\n * past any startup confirmation dialogs (workspace trust, dev-channels,\n * MCP consent). The loop self-terminates the moment the port binds, so\n * we don't keep spamming Enters once claude is at the live `❯` prompt.\n */\nexport async function waitForChannelReady(\n port: number,\n timeoutMs = 60_000,\n host = '127.0.0.1',\n tmuxSession: string = TMUX_SESSION,\n): Promise<boolean> {\n const deadline = Date.now() + timeoutMs;\n while (Date.now() < deadline) {\n if (await probePort(host, port)) return true;\n tmuxDismissPrompts(tmuxSession);\n await new Promise(r => setTimeout(r, 1000));\n }\n return probePort(host, port);\n}\n\nfunction brewInstall(pkg: string): boolean {\n const brew = spawnSync('brew', ['--version'], { encoding: 'utf-8' });\n if (brew.status !== 0) return false;\n console.log(` Installing ${pkg} via brew...`);\n const r = spawnSync('brew', ['install', pkg], { encoding: 'utf-8', stdio: 'inherit', timeout: 120_000 });\n return r.status === 0;\n}\n\n/** Ensure pueue is installed and pueued daemon is running. Auto-installs on macOS. */\nexport function assertPueueInstalled(): void {\n let r = spawnSync('pueue', ['--version'], { encoding: 'utf-8' });\n if (r.status !== 0) {\n if (process.platform === 'darwin' && brewInstall('pueue')) {\n r = spawnSync('pueue', ['--version'], { encoding: 'utf-8' });\n if (r.status !== 0) throw new PueueError('pueue install succeeded but binary not found on PATH.');\n } else {\n throw new PueueError('pueue not found. Install it: brew install pueue (macOS) or https://github.com/Nukesor/pueue');\n }\n }\n // Ensure pueued daemon is running\n const status = spawnSync('pueue', ['status', '--json'], { encoding: 'utf-8', timeout: 5_000 });\n if (status.status !== 0) {\n console.log(' Starting pueued daemon...');\n const child = spawn('pueued', ['-d'], { stdio: 'ignore', detached: true });\n child.unref();\n // Give the daemon a moment to bind its socket\n spawnSync('sleep', ['1']);\n const retry = spawnSync('pueue', ['status', '--json'], { encoding: 'utf-8', timeout: 5_000 });\n if (retry.status !== 0) {\n throw new PueueError('pueue daemon not reachable after starting pueued. Check `pueued` manually.');\n }\n }\n spawnSync('pueue', ['parallel', '2'], { encoding: 'utf-8' });\n}\n\n/** Ensure claude CLI is installed. */\nexport function assertClaudeInstalled(): void {\n const r = spawnSync('claude', ['--version'], { encoding: 'utf-8' });\n if (r.status !== 0) {\n throw new PueueError('claude CLI not found on PATH. Install Claude Code first: https://docs.claude.com/claude-code');\n }\n}\n\n/** Ensure tmux is installed. Auto-installs on macOS. */\nexport function assertTmuxInstalled(): void {\n let r = spawnSync('tmux', ['-V'], { encoding: 'utf-8' });\n if (r.status !== 0) {\n if (process.platform === 'darwin' && brewInstall('tmux')) {\n r = spawnSync('tmux', ['-V'], { encoding: 'utf-8' });\n if (r.status !== 0) throw new PueueError('tmux install succeeded but binary not found on PATH.');\n } else {\n throw new PueueError('tmux not found. Install it: brew install tmux (macOS) or apt install tmux (Linux)');\n }\n }\n}\n\n/** No-throw helper used by the install command for status messages. */\nexport function readVersion(bin: string): string | null {\n try {\n return execFileSync(bin, ['--version'], { encoding: 'utf-8', timeout: 3000 }).trim();\n } catch {\n return null;\n }\n}\n","/**\n * Role-specific prompts for the local-CC channel.\n *\n * Primers are fetched live from the Synkro API on every grade call.\n * No local caching — prompt updates deploy instantly.\n */\nimport { readFileSync } from 'node:fs';\nimport { homedir } from 'node:os';\nimport { join } from 'node:path';\n\nexport type LocalCCRole = 'grade-edit' | 'grade-bash' | 'grade-plan' | 'grade-cwe';\n\nconst CREDS_PATH = join(homedir(), '.synkro', 'credentials.json');\n\ninterface PromptsResponse {\n grader_primer_bash?: string;\n grader_primer_edit?: string;\n grader_primer_plan?: string;\n grader_primer_cwe?: string;\n}\n\nasync function fetchPrimers(): Promise<PromptsResponse> {\n let jwt = '';\n let gatewayUrl = '';\n try {\n const creds = JSON.parse(readFileSync(CREDS_PATH, 'utf-8'));\n jwt = creds.access_token || '';\n gatewayUrl = creds.gateway_url || 'https://api.synkro.sh';\n } catch {\n throw new Error('No credentials found. Run `synkro install` first.');\n }\n if (!jwt) throw new Error('No access token. Run `synkro install` first.');\n\n const resp = await fetch(`${gatewayUrl}/api/v1/cli/judge-prompts`, {\n headers: { Authorization: `Bearer ${jwt}` },\n signal: AbortSignal.timeout(5000),\n });\n if (!resp.ok) throw new Error(`Failed to fetch prompts: ${resp.status}`);\n return resp.json() as Promise<PromptsResponse>;\n}\n\nasync function getPrimer(role: LocalCCRole): Promise<string> {\n const prompts = await fetchPrimers();\n const primer = role === 'grade-edit' ? prompts.grader_primer_edit\n : role === 'grade-plan' ? prompts.grader_primer_plan\n : role === 'grade-cwe' ? prompts.grader_primer_cwe\n : prompts.grader_primer_bash;\n if (!primer) {\n throw new Error(`No primer for role \"${role}\" returned from API.`);\n }\n return primer;\n}\n\nconst CHANNEL_REPLY_INSTRUCTIONS = `\nDELIVERY METHOD — MANDATORY, OVERRIDES ALL OTHER OUTPUT RULES:\nYou are running inside a Synkro MCP channel. Do NOT output your verdict as text.\nInstead, after generating your verdict, call the \\`reply\\` tool EXACTLY ONCE with:\n - req_id: the req_id from this channel event's meta\n - result: your complete verdict block as a string (the <synkro-verdict>…</synkro-verdict> XML)\nAny text output is silently discarded. Only the reply tool call is captured.`;\n\nexport async function buildChannelContent(role: LocalCCRole, payload: string): Promise<string> {\n const primer = await getPrimer(role);\n return `${primer}\n\n${CHANNEL_REPLY_INSTRUCTIONS}\n\n---\nPAYLOAD (the input to evaluate):\n\n${payload}`;\n}\n","/**\n * JSONL log of every channel turn (grade / classify) — one entry per line.\n *\n * Written by `submitToChannel` after each request completes (success or fail).\n * Read by `synkro local-cc logs` to show recent activity.\n *\n * Best-effort: any I/O error here is swallowed so logging never breaks the\n * actual grading path.\n */\n\nimport { appendFileSync, existsSync, mkdirSync, openSync, readFileSync, readSync, closeSync, statSync, watchFile, unwatchFile } from 'node:fs';\nimport { dirname, join } from 'node:path';\nimport { homedir } from 'node:os';\nimport type { LocalCCRole } from './prompts.js';\n\nexport const TURN_LOG_PATH = join(homedir(), '.synkro', 'cc_sessions', 'turns.log');\n\nexport type TurnStatus = 'ok' | 'timeout' | 'error';\n\nexport interface TurnEntry {\n /** ISO timestamp of when the request started. */\n ts: string;\n role: LocalCCRole;\n /** Wall-clock ms from submit to result/error. */\n duration_ms: number;\n status: TurnStatus;\n /** Truncated request payload (first ~400 chars of caller-supplied text). */\n request_preview: string;\n /** Truncated response result (first ~400 chars). Empty on non-ok. */\n response_preview: string;\n /** Parsed severity if we can extract one from the verdict. Optional. */\n severity?: string;\n /** Error message if status is timeout/error. */\n error?: string;\n}\n\nconst PREVIEW_MAX = 400;\n\nfunction truncate(s: string, max = PREVIEW_MAX): string {\n if (s.length <= max) return s;\n return s.slice(0, max) + '… [+' + (s.length - max) + ' chars]';\n}\n\n/** Best-effort extraction of severity from the result text. */\nfunction extractSeverity(result: string): string | undefined {\n // <synkro-verdict>{\"severity\":\"audit\",…}</synkro-verdict>\n const m = result.match(/<synkro-(?:verdict|intent)>([\\s\\S]*?)<\\/synkro-(?:verdict|intent)>/);\n if (!m) return undefined;\n try {\n const obj = JSON.parse(m[1]) as { severity?: string; verdict?: string; type?: string; ok?: boolean };\n if (obj.severity) return String(obj.severity);\n if (typeof obj.ok === 'boolean') return obj.ok ? 'ok' : 'violations';\n if (obj.type) return String(obj.type);\n if (obj.verdict) return String(obj.verdict);\n } catch {\n // not parseable JSON inside the tag — ignore\n }\n return undefined;\n}\n\n/** Append one turn to the JSONL log. Best-effort — never throws. */\nexport function appendTurn(args: {\n startedAt: number;\n role: LocalCCRole;\n request: string;\n result?: string;\n status: TurnStatus;\n error?: string;\n}): void {\n try {\n mkdirSync(dirname(TURN_LOG_PATH), { recursive: true });\n const entry: TurnEntry = {\n ts: new Date(args.startedAt).toISOString(),\n role: args.role,\n duration_ms: Date.now() - args.startedAt,\n status: args.status,\n request_preview: truncate(args.request),\n response_preview: args.result ? truncate(args.result) : '',\n severity: args.result ? extractSeverity(args.result) : undefined,\n error: args.error,\n };\n appendFileSync(TURN_LOG_PATH, JSON.stringify(entry) + '\\n', 'utf-8');\n } catch {\n // never let logging fail the call site\n }\n}\n\n/**\n * Read the most recent N turn entries (newest first). Returns an empty array\n * on any error.\n */\nexport function readRecentTurns(n = 20): TurnEntry[] {\n if (!existsSync(TURN_LOG_PATH)) return [];\n try {\n // For typical sizes this is fine; if the file grows huge we'd switch to\n // tail-style reverse reading. Cap by stat size for safety.\n const size = statSync(TURN_LOG_PATH).size;\n if (size === 0) return [];\n const text = readFileSync(TURN_LOG_PATH, 'utf-8');\n const lines = text.split('\\n').filter(Boolean);\n const lastN = lines.slice(-n).reverse();\n return lastN\n .map(line => {\n try { return JSON.parse(line) as TurnEntry; } catch { return null; }\n })\n .filter((x): x is TurnEntry => x !== null);\n } catch {\n return [];\n }\n}\n\n/**\n * Tail the turn log: invoke `onEntry` for each new entry appended after the\n * call. Handles file truncation/rotation by re-syncing offset to 0 when size\n * shrinks. Returns a `stop()` to detach the watcher.\n */\nexport function followTurns(onEntry: (t: TurnEntry) => void): () => void {\n // Make sure the file exists so watchFile has something to poll. Creating\n // an empty file is harmless if it already exists.\n try {\n mkdirSync(dirname(TURN_LOG_PATH), { recursive: true });\n if (!existsSync(TURN_LOG_PATH)) {\n appendFileSync(TURN_LOG_PATH, '', 'utf-8');\n }\n } catch {\n // best-effort\n }\n\n let lastSize = (() => {\n try { return statSync(TURN_LOG_PATH).size; } catch { return 0; }\n })();\n let pendingPartial = ''; // bytes after the last \\n if a write split on a line boundary\n\n const drainNewBytes = (from: number, to: number): void => {\n if (to <= from) return;\n let fd: number | null = null;\n try {\n fd = openSync(TURN_LOG_PATH, 'r');\n const len = to - from;\n const buf = Buffer.alloc(len);\n readSync(fd, buf, 0, len, from);\n const text = pendingPartial + buf.toString('utf-8');\n const lastNewline = text.lastIndexOf('\\n');\n if (lastNewline === -1) {\n pendingPartial = text;\n return;\n }\n const complete = text.slice(0, lastNewline);\n pendingPartial = text.slice(lastNewline + 1);\n for (const line of complete.split('\\n')) {\n if (!line) continue;\n try {\n onEntry(JSON.parse(line) as TurnEntry);\n } catch {\n // skip malformed lines\n }\n }\n } catch {\n // best-effort\n } finally {\n if (fd !== null) {\n try { closeSync(fd); } catch { /* noop */ }\n }\n }\n };\n\n // Poll-based watcher (cross-platform reliable). 250ms is responsive\n // enough for tail-f UX without burning CPU.\n watchFile(TURN_LOG_PATH, { interval: 250 }, (curr, prev) => {\n if (curr.size < lastSize) {\n // file truncated/rotated — reset.\n lastSize = 0;\n pendingPartial = '';\n }\n if (curr.size > lastSize) {\n drainNewBytes(lastSize, curr.size);\n lastSize = curr.size;\n }\n });\n\n return () => unwatchFile(TURN_LOG_PATH);\n}\n","/**\n * Client for the local-CC channel plugin.\n *\n * POSTs to a TCP port on 127.0.0.1 exposed by the channel plugin running\n * inside the pueue-managed `claude` process. Returns the raw text Claude\n * wrote into the `reply` tool's `result` argument.\n */\n\nimport { request as httpRequest } from 'node:http';\nimport { connect } from 'node:net';\nimport { buildChannelContent, type LocalCCRole } from './prompts.js';\nimport { appendTurn } from './turnLog.js';\n\nexport const CHANNEL_HOST = '127.0.0.1';\nexport const CHANNEL_PORT = parseInt(process.env.SYNKRO_CHANNEL_PORT || '8929', 10);\n\nconst DEFAULT_TIMEOUT_MS = 90_000;\n\nexport class LocalCCError extends Error {\n constructor(message: string, public readonly cause?: unknown) {\n super(message);\n this.name = 'LocalCCError';\n }\n}\n\nexport interface SubmitOptions {\n /** Timeout for the round-trip in ms (default 90s). */\n timeoutMs?: number;\n /** Override the channel port (default: CHANNEL_PORT / 8929). */\n port?: number;\n}\n\n/**\n * Send a request through the channel and wait for Claude's `reply` tool output.\n * Throws LocalCCError if the channel isn't reachable, the channel times out, or\n * the plugin returns a non-2xx status.\n */\nexport async function submitToChannel(\n role: LocalCCRole,\n payload: string,\n opts: SubmitOptions = {},\n): Promise<string> {\n const content = await buildChannelContent(role, payload);\n const body = JSON.stringify({ role, content });\n const timeoutMs = opts.timeoutMs ?? DEFAULT_TIMEOUT_MS;\n const port = opts.port ?? CHANNEL_PORT;\n const startedAt = Date.now();\n\n try {\n const result = await new Promise<string>((resolve, reject) => {\n const req = httpRequest({\n host: CHANNEL_HOST,\n port,\n method: 'POST',\n path: '/submit',\n headers: {\n 'Content-Type': 'application/json',\n 'Content-Length': Buffer.byteLength(body),\n },\n timeout: timeoutMs,\n }, res => {\n const chunks: Buffer[] = [];\n res.on('data', c => chunks.push(c));\n res.on('end', () => {\n const text = Buffer.concat(chunks).toString('utf-8');\n if (res.statusCode !== 200) {\n reject(new LocalCCError(`channel returned ${res.statusCode}: ${text.slice(0, 500)}`));\n return;\n }\n try {\n const parsed = JSON.parse(text) as { result?: string; error?: string };\n if (parsed.error) {\n reject(new LocalCCError(parsed.error));\n return;\n }\n resolve(String(parsed.result ?? ''));\n } catch (err) {\n reject(new LocalCCError(`malformed channel response: ${text.slice(0, 200)}`, err));\n }\n });\n });\n req.on('timeout', () => {\n req.destroy(new LocalCCError(`channel request timed out after ${timeoutMs}ms`));\n });\n req.on('error', err => {\n const msg = (err as NodeJS.ErrnoException).code === 'ECONNREFUSED'\n ? `channel connection refused at ${CHANNEL_HOST}:${CHANNEL_PORT} (is the pueue task running?)`\n : `channel request failed: ${(err as Error).message}`;\n reject(new LocalCCError(msg, err));\n });\n req.write(body);\n req.end();\n });\n appendTurn({ startedAt, role, request: payload, result, status: 'ok' });\n return result;\n } catch (err) {\n const message = (err as Error).message ?? String(err);\n const status = /timed out/i.test(message) ? 'timeout' : 'error';\n appendTurn({ startedAt, role, request: payload, status, error: message });\n throw err;\n }\n}\n\n/** Quick TCP probe — true if anything is listening on the given port (default: CHANNEL_PORT). */\nexport function isChannelAvailable(port = CHANNEL_PORT, timeoutMs = 500): Promise<boolean> {\n return new Promise(resolve => {\n const sock = connect(port, CHANNEL_HOST);\n const done = (ok: boolean) => {\n try { sock.destroy(); } catch { /* noop */ }\n resolve(ok);\n };\n sock.once('connect', () => done(true));\n sock.once('error', () => done(false));\n sock.setTimeout(timeoutMs, () => done(false));\n });\n}\n","// :)\n/**\n * synkro install — first-time setup on customer's machine.\n *\n * Detects installed AI agents (CC, Codex), drops hook scripts in\n * ~/.synkro/hooks/, writes settings.json hook entries, fetches the latest\n * Edit/Write prompt from the gateway and inlines it into settings, writes\n * config.env. Triggers `synkro login` if not already authed.\n */\nimport { existsSync, mkdirSync, writeFileSync, chmodSync, readFileSync, readdirSync, appendFileSync, renameSync } from 'node:fs';\nimport { homedir } from 'node:os';\nimport { join } from 'node:path';\nimport { execSync, spawnSync, spawn } from 'node:child_process';\nimport { createInterface } from 'node:readline';\nimport { detectAgents } from '../installer/agentDetect.js';\nimport { installCCHooks } from '../installer/ccHookConfig.js';\nimport { installCursorHooks } from '../installer/cursorHookConfig.js';\nimport { installMcpConfig } from '../installer/mcpConfig.js';\nimport { SYNKRO_COMMON_SCRIPT } from '../installer/hookScripts.js';\nimport { SYNKRO_COMMON_TS, EDIT_PRECHECK_TS, CWE_PRECHECK_TS, CVE_PRECHECK_TS, BASH_JUDGE_TS, AGENT_JUDGE_TS, PLAN_JUDGE_TS, STOP_SUMMARY_TS, SESSION_START_TS, BASH_FOLLOWUP_TS, TRANSCRIPT_SYNC_TS, USER_PROMPT_SUBMIT_TS, CURSOR_BASH_JUDGE_TS, CURSOR_EDIT_CAPTURE_TS } from '../installer/hookScriptsTs.js';\nimport { isAuthenticated, getAccessToken, authenticate, getUserInfo, ensureValidToken } from '../auth/stub.js';\nimport { promptRepoConnection } from './repoConnect.js';\nimport { setApiBaseUrl, listProjects } from '../api/projects.js';\nimport { connectGitHub } from './setupGithub.js';\nimport { fetchJudgePrompts } from '../installer/promptFetcher.js';\nimport { isLocalCCEnabled } from '../local-cc/settings.js';\nimport { installLocalCC, CHANNEL_2_PORT } from '../local-cc/install.js';\nimport { ensureRunning as ensurePueueRunning, startTask as startPueueTask, stopTask as stopPueueTask, assertClaudeInstalled, assertPueueInstalled, assertTmuxInstalled, waitForChannelReady, findTask, tailLogs, CHANNEL_SECONDARY } from '../local-cc/pueue.js';\nimport { CHANNEL_HOST, CHANNEL_PORT, submitToChannel } from '../local-cc/client.js';\n\n// Replaced by tsup `define` at build time.\ndeclare const __SYNKRO_CLI_VERSION__: string;\ndeclare const __MCP_LOCAL_SERVER_SRC__: string;\n\nconst SYNKRO_DIR = join(homedir(), '.synkro');\nconst HOOKS_DIR = join(SYNKRO_DIR, 'hooks');\nconst BIN_DIR = join(SYNKRO_DIR, 'bin');\nconst CONFIG_PATH = join(SYNKRO_DIR, 'config.env');\n\ninterface InstallOptions {\n gatewayUrl?: string; // override default\n apiKey?: string; // skip prompting if provided (for tests/CI)\n skipAuth?: boolean;\n noMcp?: boolean; // skip registering the guardrails MCP server\n force?: boolean; // bypass the \"already installed\" short-circuit\n linkRepo?: boolean; // auto-link current repo (non-interactive)\n}\n\n// Accept a gateway URL only if it's a plain http(s) URL. The published CLI is\n// invoked from arbitrary cwds where a developer's monorepo .env / op://\n// reference / direnv export may have poisoned SYNKRO_GATEWAY_URL with a value\n// the CLI can't actually call (e.g. \"op://dev/synkro/gateway-url\"). Silently\n// ignoring those falls through to the prod default so customers never have to\n// wrestle with shell hygiene before `synkro install` works.\nfunction sanitizeGatewayCandidate(raw: string | undefined): string | undefined {\n if (!raw) return undefined;\n return /^https?:\\/\\//.test(raw) ? raw : undefined;\n}\n\nexport function parseArgs(argv: string[]): InstallOptions {\n const opts: InstallOptions = {};\n for (const a of argv) {\n if (a.startsWith('--api-key=')) opts.apiKey = a.slice('--api-key='.length);\n else if (a.startsWith('--gateway=')) opts.gatewayUrl = a.slice('--gateway='.length);\n else if (a === '--skip-auth') opts.skipAuth = true;\n else if (a === '--no-mcp') opts.noMcp = true;\n else if (a === '--force' || a === '-f') opts.force = true;\n else if (a === '--link-repo') opts.linkRepo = true;\n }\n if (!opts.gatewayUrl) {\n const fromEnv = sanitizeGatewayCandidate(process.env.SYNKRO_GATEWAY_URL);\n if (fromEnv) opts.gatewayUrl = fromEnv;\n }\n // NOTE: we deliberately do NOT pick up SYNKRO_API_KEY from process.env. The\n // root .env / global env may contain a stale or unrelated SYNKRO_API_KEY.\n // The legitimate sources are: --api-key flag, or fresh OAuth via authenticate().\n return opts;\n}\n\nasync function promptTranscriptConsent(): Promise<boolean> {\n const rl = createInterface({ input: process.stdin, output: process.stdout });\n return new Promise((resolve) => {\n rl.question(\n 'Would you like Synkro to use Claude Code session transcripts\\n' +\n 'to generate guardrail rules and policies for your team? (Y/n) ',\n (answer) => {\n rl.close();\n const trimmed = answer.trim().toLowerCase();\n resolve(trimmed === '' || trimmed === 'y' || trimmed === 'yes');\n },\n );\n });\n}\n\nconst OFFSETS_DIR = join(SYNKRO_DIR, '.transcript-offsets');\n\nfunction ensureSynkroDir(): void {\n mkdirSync(SYNKRO_DIR, { recursive: true });\n mkdirSync(HOOKS_DIR, { recursive: true });\n mkdirSync(BIN_DIR, { recursive: true });\n mkdirSync(OFFSETS_DIR, { recursive: true });\n}\n\nfunction writeHookScripts(): {\n bashScript: string;\n bashFollowupScript: string;\n editPrecheckScript: string;\n cwePrecheckScript: string;\n cvePrecheckScript: string;\n planJudgeScript: string;\n agentJudgeScript: string;\n stopSummaryScript: string;\n sessionStartScript: string;\n transcriptSyncScript: string;\n userPromptSubmitScript: string;\n cursorBashJudgeScript: string;\n cursorEditCaptureScript: string;\n mcpLocalServerScript: string;\n} {\n const bashScriptPath = join(HOOKS_DIR, 'cc-bash-judge.ts');\n const bashFollowupScriptPath = join(HOOKS_DIR, 'cc-bash-followup.ts');\n const editPrecheckScriptPath = join(HOOKS_DIR, 'cc-edit-precheck.ts');\n const cwePrecheckScriptPath = join(HOOKS_DIR, 'cc-cwe-precheck.ts');\n const cvePrecheckScriptPath = join(HOOKS_DIR, 'cc-cve-precheck.ts');\n const planJudgeScriptPath = join(HOOKS_DIR, 'cc-plan-judge.ts');\n const agentJudgeScriptPath = join(HOOKS_DIR, 'cc-agent-judge.ts');\n const stopSummaryScriptPath = join(HOOKS_DIR, 'cc-stop-summary.ts');\n const sessionStartScriptPath = join(HOOKS_DIR, 'cc-session-start.ts');\n const transcriptSyncScriptPath = join(HOOKS_DIR, 'cc-transcript-sync.ts');\n const userPromptSubmitScriptPath = join(HOOKS_DIR, 'cc-user-prompt-submit.ts');\n const commonScriptPath = join(HOOKS_DIR, '_synkro-common.ts');\n const commonBashScriptPath = join(HOOKS_DIR, '_synkro-common.sh');\n const cursorBashJudgePath = join(HOOKS_DIR, 'cursor-bash-judge.ts');\n const cursorEditCapturePath = join(HOOKS_DIR, 'cursor-edit-capture.ts');\n const mcpLocalServerPath = join(HOOKS_DIR, 'mcp-local-server.ts');\n\n writeFileSync(bashScriptPath, BASH_JUDGE_TS, 'utf-8');\n writeFileSync(bashFollowupScriptPath, BASH_FOLLOWUP_TS, 'utf-8');\n writeFileSync(editPrecheckScriptPath, EDIT_PRECHECK_TS, 'utf-8');\n writeFileSync(cwePrecheckScriptPath, CWE_PRECHECK_TS, 'utf-8');\n writeFileSync(cvePrecheckScriptPath, CVE_PRECHECK_TS, 'utf-8');\n writeFileSync(planJudgeScriptPath, PLAN_JUDGE_TS, 'utf-8');\n writeFileSync(agentJudgeScriptPath, AGENT_JUDGE_TS, 'utf-8');\n writeFileSync(stopSummaryScriptPath, STOP_SUMMARY_TS, 'utf-8');\n writeFileSync(sessionStartScriptPath, SESSION_START_TS, 'utf-8');\n writeFileSync(transcriptSyncScriptPath, TRANSCRIPT_SYNC_TS, 'utf-8');\n writeFileSync(userPromptSubmitScriptPath, USER_PROMPT_SUBMIT_TS, 'utf-8');\n writeFileSync(commonScriptPath, SYNKRO_COMMON_TS, 'utf-8');\n writeFileSync(commonBashScriptPath, SYNKRO_COMMON_SCRIPT, 'utf-8');\n writeFileSync(cursorBashJudgePath, CURSOR_BASH_JUDGE_TS, 'utf-8');\n writeFileSync(cursorEditCapturePath, CURSOR_EDIT_CAPTURE_TS, 'utf-8');\n writeFileSync(mcpLocalServerPath, __MCP_LOCAL_SERVER_SRC__, 'utf-8');\n\n chmodSync(bashScriptPath, 0o755);\n chmodSync(bashFollowupScriptPath, 0o755);\n chmodSync(editPrecheckScriptPath, 0o755);\n chmodSync(cwePrecheckScriptPath, 0o755);\n chmodSync(cvePrecheckScriptPath, 0o755);\n chmodSync(planJudgeScriptPath, 0o755);\n chmodSync(agentJudgeScriptPath, 0o755);\n chmodSync(stopSummaryScriptPath, 0o755);\n chmodSync(sessionStartScriptPath, 0o755);\n chmodSync(transcriptSyncScriptPath, 0o755);\n chmodSync(userPromptSubmitScriptPath, 0o755);\n chmodSync(commonScriptPath, 0o755);\n chmodSync(commonBashScriptPath, 0o755);\n chmodSync(cursorBashJudgePath, 0o755);\n chmodSync(cursorEditCapturePath, 0o755);\n chmodSync(mcpLocalServerPath, 0o755);\n\n return {\n bashScript: bashScriptPath,\n bashFollowupScript: bashFollowupScriptPath,\n editPrecheckScript: editPrecheckScriptPath,\n cwePrecheckScript: cwePrecheckScriptPath,\n cvePrecheckScript: cvePrecheckScriptPath,\n planJudgeScript: planJudgeScriptPath,\n agentJudgeScript: agentJudgeScriptPath,\n stopSummaryScript: stopSummaryScriptPath,\n sessionStartScript: sessionStartScriptPath,\n transcriptSyncScript: transcriptSyncScriptPath,\n userPromptSubmitScript: userPromptSubmitScriptPath,\n cursorBashJudgeScript: cursorBashJudgePath,\n cursorEditCaptureScript: cursorEditCapturePath,\n mcpLocalServerScript: mcpLocalServerPath,\n };\n}\n\n// Sanitize values before writing into config.env — the file is sourced as a\n// shell script, so any unquoted shell metacharacter in a value is dangerous\n// (newlines smuggle new assignments; (){}#`$ etc. let an attacker run code\n// on `source config.env`). Two defenses:\n// 1. Strip non-printable ASCII (drops newlines, tabs, control chars)\n// 2. Wrap the result in single-quotes — single-quoted shell strings are\n// entirely literal, so anything inside is safe regardless of contents.\n// Internal single-quotes are escaped using the standard '\\'' trick.\nfunction sanitizeConfigValue(raw: string | undefined, maxLen = 256): string {\n if (!raw) return '';\n return raw\n .replace(/[^\\x20-\\x7E]/g, '') // drop non-printable (newlines/tabs/control)\n .slice(0, maxLen);\n}\n\nfunction shellQuoteSingle(value: string): string {\n // Escape any single quote in `value` for inclusion inside a single-quoted\n // shell string: 'foo'\"'\"'bar' renders as foo'bar.\n return `'${value.replace(/'/g, \"'\\\\''\")}'`;\n}\n\n// Resolve the absolute path to the synkro bundle (dist/bootstrap.js) so hook\n// scripts can invoke `node \"$SYNKRO_CLI_BIN\" grade ...` without relying on\n// the user's shell PATH. Returning a single path (vs a command string) keeps\n// the hook invocation a single quoted arg — no shell-injection surface.\n//\n// process.argv[1] is the script the user executed: with the pnpm/npm shim\n// it's the absolute path to dist/bootstrap.js. With `just synkro install`\n// it's also the absolute bundle path (the just recipe runs `node bootstrap.js`).\nfunction resolveSynkroBundle(): string | null {\n const scriptPath = process.argv[1];\n if (scriptPath && existsSync(scriptPath)) return scriptPath;\n return null;\n}\n\nfunction writeConfigEnv(opts: { gatewayUrl: string; userId?: string; orgId?: string; email?: string; tier?: string; inference?: string; synkroBin?: string | null; transcriptConsent?: boolean; localInference?: boolean }): void {\n const credsPath = join(SYNKRO_DIR, 'credentials.json');\n const safeGateway = sanitizeConfigValue(opts.gatewayUrl);\n const safeUserId = sanitizeConfigValue(opts.userId);\n const safeOrgId = sanitizeConfigValue(opts.orgId);\n const safeEmail = sanitizeConfigValue(opts.email);\n const safeTier = sanitizeConfigValue(opts.tier ?? 'pro', 32);\n const safeInference = sanitizeConfigValue(opts.inference ?? 'fast', 16);\n // Allow up to 1KB for the bin command since it may be `node /long/path/to/bootstrap.js`.\n const safeSynkroBin = sanitizeConfigValue(opts.synkroBin ?? '', 1024);\n const lines = [\n '# Synkro CLI config (managed by synkro install)',\n '# JWT auth — the hook scripts read SYNKRO_CREDENTIALS_PATH at runtime',\n '# and send Authorization: Bearer <access_token> on every gateway call.',\n `SYNKRO_GATEWAY_URL=${shellQuoteSingle(safeGateway)}`,\n `SYNKRO_CREDENTIALS_PATH=${shellQuoteSingle(credsPath)}`,\n `SYNKRO_TIER=${shellQuoteSingle(safeTier)}`,\n `SYNKRO_INFERENCE=${shellQuoteSingle(safeInference)}`,\n `SYNKRO_VERSION=${shellQuoteSingle(__SYNKRO_CLI_VERSION__)}`,\n ];\n if (safeSynkroBin) lines.push(`SYNKRO_CLI_BIN=${shellQuoteSingle(safeSynkroBin)}`);\n if (safeUserId) lines.push(`SYNKRO_USER_ID=${shellQuoteSingle(safeUserId)}`);\n if (safeOrgId) lines.push(`SYNKRO_ORG_ID=${shellQuoteSingle(safeOrgId)}`);\n if (safeEmail) lines.push(`SYNKRO_EMAIL=${shellQuoteSingle(safeEmail)}`);\n if (opts.transcriptConsent !== undefined) {\n lines.push(`SYNKRO_TRANSCRIPT_CONSENT=${shellQuoteSingle(opts.transcriptConsent ? 'yes' : 'no')}`);\n }\n lines.push(`SYNKRO_LOCAL_INFERENCE=${shellQuoteSingle(opts.localInference ? 'yes' : 'no')}`);\n lines.push('');\n writeFileSync(CONFIG_PATH, lines.join('\\n'), 'utf-8');\n chmodSync(CONFIG_PATH, 0o600);\n}\n\nfunction updateLocalInferenceFlag(enabled: boolean): void {\n if (!existsSync(CONFIG_PATH)) return;\n let content = readFileSync(CONFIG_PATH, 'utf-8');\n const flag = enabled ? 'yes' : 'no';\n if (content.includes('SYNKRO_LOCAL_INFERENCE=')) {\n content = content.replace(/^SYNKRO_LOCAL_INFERENCE='[^']*'/m, `SYNKRO_LOCAL_INFERENCE='${flag}'`);\n } else {\n content = content.trimEnd() + `\\nSYNKRO_LOCAL_INFERENCE='${flag}'\\n`;\n }\n writeFileSync(CONFIG_PATH, content, 'utf-8');\n}\n\nfunction collectLocalMetadata(): Record<string, unknown> {\n const meta: Record<string, unknown> = { platform: process.platform };\n try {\n meta.display_name = execSync('git config user.name', { encoding: 'utf-8', timeout: 3000 }).trim();\n } catch {}\n try {\n const remote = execSync('git remote get-url origin', { encoding: 'utf-8', timeout: 3000 }).trim();\n const sshMatch = remote.match(/^git@[^:]+:(.+?)(?:\\.git)?$/);\n const httpMatch = remote.match(/^https?:\\/\\/[^/]+\\/(.+?)(?:\\.git)?$/);\n const m = sshMatch || httpMatch;\n if (m) meta.active_repo = m[1];\n } catch {}\n try {\n meta.cc_version = execSync('claude --version', { encoding: 'utf-8', timeout: 5000 }).trim().split('\\n')[0];\n } catch {}\n\n const claudeDir = join(homedir(), '.claude');\n\n try {\n const settings = JSON.parse(readFileSync(join(claudeDir, 'settings.json'), 'utf-8'));\n const plugins = Object.keys(settings.enabledPlugins ?? {}).filter(k => settings.enabledPlugins[k]);\n if (plugins.length) meta.enabled_plugins = plugins;\n if (settings.permissions?.defaultMode) meta.permissions_mode = settings.permissions.defaultMode;\n } catch {}\n\n try {\n const mcpCache = JSON.parse(readFileSync(join(claudeDir, 'mcp-needs-auth-cache.json'), 'utf-8'));\n const mcpNames = Object.keys(mcpCache);\n if (mcpNames.length) meta.mcp_servers = mcpNames;\n } catch {}\n\n try {\n const mcpList = execSync('claude mcp list 2>/dev/null', { encoding: 'utf-8', timeout: 10000 });\n const connected = mcpList.split('\\n')\n .filter(l => l.includes('Connected'))\n .map(l => l.split(':')[0].trim())\n .filter(Boolean);\n if (connected.length) meta.mcp_servers_connected = connected;\n } catch {}\n\n try {\n const sessionsDir = join(claudeDir, 'sessions');\n const files = readdirSync(sessionsDir).filter(f => f.endsWith('.json')).slice(-5);\n for (const f of files) {\n const s = JSON.parse(readFileSync(join(sessionsDir, f), 'utf-8'));\n if (s.version) { meta.cc_version = meta.cc_version || s.version; break; }\n }\n } catch {}\n\n return meta;\n}\n\nasync function fetchUserProfile(gatewayUrl: string, token: string): Promise<{ tier: string; inference: string; localInference: boolean; captureDepth: string }> {\n try {\n const resp = await fetch(`${gatewayUrl}/api/v1/cli/me`, {\n headers: { 'Authorization': `Bearer ${token}` },\n });\n if (!resp.ok) return { tier: 'pro', inference: 'fast', localInference: false, captureDepth: 'full' };\n const data = await resp.json() as { fast_inference?: boolean; plan_tier?: string; local_inference?: boolean; capture_depth?: string };\n\n const meta = collectLocalMetadata();\n fetch(`${gatewayUrl}/api/v1/cli/me`, {\n method: 'PATCH',\n headers: { 'Authorization': `Bearer ${token}`, 'Content-Type': 'application/json' },\n body: JSON.stringify(meta),\n }).catch(() => {});\n\n return {\n tier: data.plan_tier ?? 'pro',\n inference: data.fast_inference ? 'fast' : 'standard',\n localInference: !!data.local_inference,\n captureDepth: data.capture_depth ?? 'full',\n };\n } catch {\n return { tier: 'pro', inference: 'fast', localInference: false, captureDepth: 'full' };\n }\n}\n\n// JWT-only auth — no API key minting. The hook scripts read the JWT from\n// ~/.synkro/credentials.json and send it as Authorization: Bearer <jwt>.\n// Auth middleware on the gateway resolves user/org from JWT claims via JWKS.\n\n// The CLI ships the user's WorkOS Bearer JWT to the gateway URL. If a\n// hostile actor sets --gateway=http://evil.example.com on a user's machine,\n// the JWT lands at the attacker's server. Allow only:\n// - https://*.synkro.sh and synkro.sh apex\n// - http(s)://localhost or 127.0.0.1 (dev)\n// Anything else is rejected before any fetch is attempted.\nfunction assertGatewayAllowed(gatewayUrl: string): void {\n let parsed: URL;\n try { parsed = new URL(gatewayUrl); }\n catch { throw new Error(`Invalid gateway URL: ${gatewayUrl}`); }\n const proto = parsed.protocol;\n const host = parsed.hostname;\n if (proto !== 'http:' && proto !== 'https:') {\n throw new Error(`Gateway URL must be http(s); got ${proto}`);\n }\n const isLocalhost = host === 'localhost' || host === '127.0.0.1' || host === '::1';\n const isSynkro = host === 'synkro.sh' || host.endsWith('.synkro.sh');\n if (proto === 'http:' && !isLocalhost) {\n throw new Error(`Gateway URL must be HTTPS for non-localhost hosts; got ${gatewayUrl}`);\n }\n if (!isLocalhost && !isSynkro) {\n throw new Error(`Gateway host not in allowlist (synkro.sh or *.synkro.sh): ${host}`);\n }\n}\n\n// Detect a complete prior install: every hook script present, config.env\n// written, and CC settings.json carries our `__synkro_managed__` markers.\n// MCP registration is opt-out (--no-mcp) so we don't gate on it. Returns\n// true only when every required surface is present so we never short-\n// circuit a partial / broken install.\nfunction isAlreadyInstalled(): boolean {\n const requiredScripts = [\n join(HOOKS_DIR, 'cc-bash-judge.ts'),\n join(HOOKS_DIR, 'cc-bash-followup.ts'),\n join(HOOKS_DIR, 'cc-edit-precheck.ts'),\n join(HOOKS_DIR, 'cc-cwe-precheck.ts'),\n join(HOOKS_DIR, 'cc-cve-precheck.ts'),\n join(HOOKS_DIR, 'cc-plan-judge.ts'),\n join(HOOKS_DIR, 'cc-stop-summary.ts'),\n join(HOOKS_DIR, 'cc-session-start.ts'),\n ];\n if (!requiredScripts.every((p) => existsSync(p))) return false;\n if (!existsSync(CONFIG_PATH)) return false;\n\n const settingsPath = join(homedir(), '.claude', 'settings.json');\n if (!existsSync(settingsPath)) return false;\n try {\n const settings = JSON.parse(readFileSync(settingsPath, 'utf-8'));\n const hooks = settings?.hooks;\n if (!hooks || typeof hooks !== 'object') return false;\n const hasManaged = (kind: string) =>\n Array.isArray(hooks[kind]) &&\n hooks[kind].some((entry: Record<string, unknown>) => entry?.__synkro_managed__ === true);\n if (!hasManaged('PreToolUse')) return false;\n if (!hasManaged('PostToolUse')) return false;\n if (!hasManaged('SessionEnd')) return false;\n if (!hasManaged('SessionStart')) return false;\n } catch {\n return false;\n }\n return true;\n}\n\nfunction printChannelDiagnostics(): void {\n try {\n const tmuxCheck = spawnSync('tmux', ['has-session', '-t', 'synkro-local-cc'], { encoding: 'utf-8' });\n console.warn(` tmux session: ${tmuxCheck.status === 0 ? 'running' : 'not running'}`);\n const pueueTask = findTask();\n console.warn(` pueue task: ${pueueTask ? `id=${pueueTask.id} status=${pueueTask.status}` : 'not found'}`);\n const bunCheck = spawnSync('bun', ['--version'], { encoding: 'utf-8' });\n console.warn(` bun: ${bunCheck.status === 0 ? bunCheck.stdout.trim() : 'not found'}`);\n const claudeCheck = spawnSync('claude', ['--version'], { encoding: 'utf-8' });\n console.warn(` claude: ${claudeCheck.status === 0 ? claudeCheck.stdout.trim().split('\\n')[0] : 'not found'}`);\n if (pueueTask) {\n const logs = tailLogs(15);\n if (logs && logs !== '(no output)') {\n console.warn(` pueue logs (last 15 lines):`);\n for (const line of logs.split('\\n').slice(0, 15)) {\n console.warn(` ${line}`);\n }\n }\n }\n const logPath = join(homedir(), '.synkro', 'cc_sessions', 'run-claude.log');\n if (existsSync(logPath)) {\n const logContent = readFileSync(logPath, 'utf-8').trim().split('\\n').slice(-10);\n console.warn(` run-claude.log:`);\n for (const line of logContent) console.warn(` ${line}`);\n }\n } catch {}\n console.warn(` Run \\`synkro local-cc status\\` and \\`synkro local-cc logs --tmux\\` to debug.`);\n}\n\n\nconst RULES_PATH = join(SYNKRO_DIR, 'rules.json');\nconst MCP_LOCAL_PORT = 8931;\n\nasync function backfillLocalRules(gatewayUrl: string, token: string): Promise<void> {\n if (existsSync(RULES_PATH)) {\n console.log(' Local rules already exist — skipping cloud backfill.');\n return;\n }\n try {\n const resp = await fetch(`${gatewayUrl}/api/v1/hook/config`, {\n headers: { 'Authorization': `Bearer ${token}` },\n signal: AbortSignal.timeout(8000),\n });\n if (!resp.ok) {\n console.log(' No cloud rules to backfill.');\n return;\n }\n const data = await resp.json() as any;\n const rules: any[] = (data.rules || []).map((r: any) => ({\n rule_id: r.rule_id || `r_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`,\n text: r.text || '',\n category: r.category || 'custom',\n severity: r.severity || 'medium',\n mode: r.mode || 'blocking',\n hook_stage: r.hook_stage || 'both',\n scope: r.scope || 'user',\n }));\n const policyName = data.active_policy_name || 'My Rules';\n const policyId = data.active_policy_id || 'local-policy';\n const scanExemptions = (data.scan_exemptions || [])\n .filter((e: any) => e && typeof e.path === 'string')\n .map((e: any) => ({ path: e.path, cwe_id: e.cwe_id || '', reason: e.reason }));\n const silent = data.silent_mode === true || data.silent_mode === 'true';\n\n const rulesFile = {\n policies: [{\n id: policyId,\n name: policyName,\n rules,\n ruleCount: rules.length,\n scopeOwner: 'user',\n isActive: true,\n }],\n config: { silent, activePolicyId: policyId },\n scanExemptions,\n };\n\n const tmp = RULES_PATH + '.tmp';\n writeFileSync(tmp, JSON.stringify(rulesFile, null, 2) + '\\n', 'utf-8');\n renameSync(tmp, RULES_PATH);\n\n const telemetryPath = join(SYNKRO_DIR, 'telemetry.jsonl');\n const event = {\n capture_type: 'rule_sync',\n policy_id: policyId,\n policy_name: policyName,\n rules,\n rule_count: rules.length,\n scan_exemptions: scanExemptions,\n silent,\n _ts: new Date().toISOString(),\n };\n try { appendFileSync(telemetryPath, JSON.stringify(event) + '\\n', 'utf-8'); } catch {}\n\n console.log(` Backfilled ${rules.length} rules from cloud to ~/.synkro/rules.json`);\n } catch (err) {\n console.warn(` ⚠ Cloud backfill failed: ${(err as Error).message}`);\n }\n}\n\nasync function startLocalMcpServer(): Promise<void> {\n const serverScript = join(HOOKS_DIR, 'mcp-local-server.ts');\n if (!existsSync(serverScript)) {\n console.warn(' ⚠ Local MCP server script not found — skipping.');\n return;\n }\n\n // Check if already running\n try {\n const probe = await fetch(`http://127.0.0.1:${MCP_LOCAL_PORT}/`, { signal: AbortSignal.timeout(1000) });\n if (probe.ok) {\n console.log(` Local MCP server already running on port ${MCP_LOCAL_PORT}`);\n return;\n }\n } catch {}\n\n const proc = spawn('bun', ['run', serverScript], {\n stdio: 'ignore',\n detached: true,\n });\n proc.unref();\n\n // Wait up to 5s for the server to come up\n for (let i = 0; i < 25; i++) {\n await new Promise(r => setTimeout(r, 200));\n try {\n const probe = await fetch(`http://127.0.0.1:${MCP_LOCAL_PORT}/`, { signal: AbortSignal.timeout(500) });\n if (probe.ok) {\n console.log(` Local MCP server started on port ${MCP_LOCAL_PORT}`);\n return;\n }\n } catch {}\n }\n console.warn(` ⚠ Local MCP server did not start within 5s — it may need to be started manually.`);\n}\n\nexport async function installCommand(opts: InstallOptions = {}): Promise<void> {\n const gatewayUrl = opts.gatewayUrl\n || sanitizeGatewayCandidate(process.env.SYNKRO_GATEWAY_URL)\n || 'https://api.synkro.sh';\n\n // Reject hostile gateway overrides before we leak the JWT.\n try {\n assertGatewayAllowed(gatewayUrl);\n } catch (err) {\n console.error((err as Error).message);\n process.exit(1);\n }\n\n // Idempotent short-circuit. If creds are still valid AND every required\n // surface is in place, skip the heavy steps (auth, hooks, daemon) but\n // still check whether the current repo needs linking — the user may be\n // running install from a second repo folder.\n if (!opts.force && isAuthenticated() && isAlreadyInstalled()) {\n setApiBaseUrl(`${gatewayUrl}/api`);\n await ensureValidToken();\n const currentRepo = detectGitRepo();\n if (currentRepo) {\n try {\n const projects = await listProjects();\n const alreadyLinked = projects.some((p: any) =>\n p.repos?.some((r: any) => r.full_name === currentRepo),\n );\n if (!alreadyLinked) {\n console.log(`Synkro is installed. This repo (${currentRepo}) is not linked yet.\\n`);\n await promptRepoConnection();\n return;\n }\n } catch {\n // API unreachable — fall through to normal \"already installed\" message\n }\n }\n // Even on short-circuit, check if local-cc should be set up.\n const token = getAccessToken();\n if (token) {\n const profile = await fetchUserProfile(gatewayUrl, token);\n if (profile.localInference && !isLocalCCEnabled()) {\n console.log('Local inference enabled — setting up local-CC channels...');\n try {\n assertClaudeInstalled();\n assertPueueInstalled();\n assertTmuxInstalled();\n stopPueueTask();\n stopPueueTask(CHANNEL_SECONDARY);\n installLocalCC();\n const t1 = startPueueTask();\n const t2 = startPueueTask({ channel: CHANNEL_SECONDARY });\n console.log(` channel 1: pueue id=${t1.id} channel 2: pueue id=${t2.id}`);\n console.log(' Waiting for both channels...');\n const [ready1, ready2] = await Promise.all([\n waitForChannelReady(CHANNEL_PORT, 60_000, CHANNEL_HOST),\n waitForChannelReady(CHANNEL_2_PORT, 60_000, CHANNEL_HOST, CHANNEL_SECONDARY.tmuxSession),\n ]);\n if (ready1) console.log(` channel 1 ready at ${CHANNEL_HOST}:${CHANNEL_PORT}`);\n else console.warn(' ⚠ channel 1 did not come up within 60s — check `synkro local-cc logs`');\n if (ready2) console.log(` channel 2 ready at ${CHANNEL_HOST}:${CHANNEL_2_PORT}`);\n else console.warn(' ⚠ channel 2 did not come up within 60s');\n updateLocalInferenceFlag(true);\n } catch (err) {\n console.warn(` ⚠ Local-CC setup skipped: ${(err as Error).message}`);\n console.warn(' Install pueue, tmux, and claude, then re-run install.');\n }\n }\n }\n console.log('✓ Synkro is already installed and configured.');\n console.log(' Run `synkro-cli update` to refresh hook scripts and judge prompts.');\n console.log(' Run `synkro-cli install --force` to reinstall from scratch.');\n return;\n }\n\n console.log('Synkro install starting...\\n');\n\n // 1. Auth via browser OAuth (WorkOS). Persists JWT + refresh_token to\n // ~/.synkro/credentials.json. The hook scripts authenticate against\n // the gateway with `Authorization: Bearer <access_token>` — auth\n // middleware resolves user/org from the JWT claims directly.\n if (!isAuthenticated()) {\n console.log('Opening browser for Synkro auth...');\n const result = await authenticate((status) => {\n switch (status.phase) {\n case 'starting': console.log(' Starting local callback server...'); break;\n case 'browser-opened': console.log(` Browser opened: ${status.url}`); break;\n case 'waiting': console.log(' Waiting for browser auth to complete...'); break;\n case 'success': console.log(' ✓ Authenticated'); break;\n case 'error': console.error(` ✗ ${status.message}`); break;\n }\n });\n if (!result) {\n console.error('Authentication failed. If you are running a self-hosted dashboard, set SYNKRO_WEB_AUTH_URL to its origin.');\n process.exit(1);\n }\n }\n const token = getAccessToken();\n if (!token) {\n console.error('No access token available after auth.');\n process.exit(1);\n }\n\n // 1b. Connect GitHub via WorkOS Pipes (optional — skip if user declines or it fails)\n console.log('\\nConnecting to GitHub (optional — for PR scanning)...');\n let ghToken: string | null = null;\n try {\n ghToken = await connectGitHub(gatewayUrl, token);\n } catch {}\n if (ghToken) {\n console.log();\n } else {\n console.log(' Skipped. Run `synkro setup-github` later to enable PR scanning.\\n');\n }\n\n // 1c. Connect repos (local git or GitHub OAuth)\n setApiBaseUrl(`${gatewayUrl}/api`);\n await promptRepoConnection({ linkRepo: opts.linkRepo });\n\n // 2. Detect installed agents\n const agents = detectAgents();\n if (agents.length === 0) {\n console.error('No AI coding agents detected. Install Claude Code first: https://docs.claude.com/claude-code');\n process.exit(1);\n }\n console.log('Detected agents:');\n for (const a of agents) {\n console.log(` ✓ ${a.name}${a.version ? ` (${a.version})` : ''}`);\n }\n console.log();\n\n // 3. Set up Synkro directory + hook scripts + grader daemon\n ensureSynkroDir();\n const scripts = writeHookScripts();\n console.log('Wrote hook scripts:');\n console.log(` ${scripts.bashScript}`);\n console.log(` ${scripts.bashFollowupScript}`);\n console.log(` ${scripts.editPrecheckScript}`);\n console.log(` ${scripts.planJudgeScript}`);\n console.log(` ${scripts.agentJudgeScript}`);\n console.log(` ${scripts.stopSummaryScript}`);\n console.log(` ${scripts.sessionStartScript}`);\n console.log(` ${scripts.transcriptSyncScript}\\n`);\n\n // Kill any stale legacy Python grader daemons so they don't keep handling\n // requests after we've switched to the local-CC channel path.\n for (const mode of ['edit', 'bash']) {\n const pidFile = join(SYNKRO_DIR, 'daemon', mode, 'daemon.pid');\n try {\n const pid = parseInt(readFileSync(pidFile, 'utf-8').trim(), 10);\n if (pid > 0) {\n process.kill(pid, 'SIGTERM');\n console.log(`Stopped stale ${mode} grader daemon (pid ${pid})`);\n }\n } catch {}\n }\n\n\n // 3b. Transcript consent — ask whether the user wants session transcripts\n // used for generating guardrail rules. If declined, we skip transcript\n // ingestion (Steps 7/7b) and don't install the incremental sync hook.\n let transcriptConsent = true;\n if (process.stdin.isTTY) {\n transcriptConsent = await promptTranscriptConsent();\n if (transcriptConsent) {\n console.log(' ✓ Transcript collection enabled\\n');\n } else {\n console.log(' ✗ Transcript collection disabled — skipping transcript sync\\n');\n }\n }\n\n // 4. Configure CC hooks (atomic merge into settings.json).\n // Edit/Write/MultiEdit/NotebookEdit fires a thin command shim that POSTs\n // proposed content to /api/v1/precheck-edit. Server cosines the content\n // against the org's active agent_runtime rules and returns the deterministic\n // CC-hook JSON (deny + retry guidance, or empty allow). No LLM in the hook\n // path — agent retries with a safer version on its own inference when it\n // reads the denial reason.\n let hasClaudeCode = false;\n let hasCursor = false;\n for (const agent of agents) {\n if (agent.kind === 'claude_code') {\n hasClaudeCode = true;\n installCCHooks(agent.settingsPath, {\n bashJudgeScriptPath: scripts.bashScript,\n bashFollowupScriptPath: scripts.bashFollowupScript,\n editPrecheckScriptPath: scripts.editPrecheckScript,\n cwePrecheckScriptPath: scripts.cwePrecheckScript,\n cvePrecheckScriptPath: scripts.cvePrecheckScript,\n planJudgeScriptPath: scripts.planJudgeScript,\n agentJudgeScriptPath: scripts.agentJudgeScript,\n stopSummaryScriptPath: scripts.stopSummaryScript,\n sessionStartScriptPath: scripts.sessionStartScript,\n transcriptSyncScriptPath: scripts.transcriptSyncScript,\n userPromptSubmitScriptPath: scripts.userPromptSubmitScript,\n skipTranscriptSync: !transcriptConsent,\n });\n console.log(`Configured ${agent.name} hooks at ${agent.settingsPath}`);\n } else if (agent.kind === 'cursor') {\n hasCursor = true;\n installCursorHooks(agent.settingsPath, {\n bashJudgeScriptPath: scripts.cursorBashJudgeScript,\n editCaptureScriptPath: scripts.cursorEditCaptureScript,\n bashFollowupScriptPath: scripts.bashFollowupScript,\n editPrecheckScriptPath: scripts.editPrecheckScript,\n cwePrecheckScriptPath: scripts.cwePrecheckScript,\n cvePrecheckScriptPath: scripts.cvePrecheckScript,\n planJudgeScriptPath: scripts.planJudgeScript,\n agentJudgeScriptPath: scripts.agentJudgeScript,\n stopSummaryScriptPath: scripts.stopSummaryScript,\n sessionStartScriptPath: scripts.sessionStartScript,\n userPromptSubmitScriptPath: scripts.userPromptSubmitScript,\n transcriptSyncScriptPath: scripts.transcriptSyncScript,\n });\n console.log(`Configured ${agent.name} hooks at ${agent.settingsPath}`);\n }\n }\n console.log();\n\n // 5b. Fetch user profile early — we need captureDepth to decide local vs cloud MCP.\n let userId: string | undefined;\n let orgId: string | undefined;\n let email: string | undefined;\n try {\n const info = getUserInfo();\n userId = info.id;\n orgId = info.org_id;\n email = info.email;\n } catch {\n // unreachable — we just authenticated above\n }\n const profile = await fetchUserProfile(gatewayUrl, token);\n const useLocalMcp = profile.captureDepth === 'local_only' || profile.localInference;\n\n // 5c. Register the Synkro Guardrails MCP server in ~/.claude.json.\n // Local mode: point to localhost:8931, start local server, backfill rules.\n // Cloud mode: mint long-lived JWT, point to cloud endpoint.\n if (hasClaudeCode && !opts.noMcp) {\n if (useLocalMcp) {\n try {\n const mcp = installMcpConfig({ gatewayUrl, bearerToken: '', local: true });\n console.log(`Registered local MCP guardrails server in ${mcp.path}`);\n console.log(` url: ${mcp.url}`);\n console.log(' (rules stored in ~/.synkro/rules.json)');\n console.log();\n\n await backfillLocalRules(gatewayUrl, token);\n await startLocalMcpServer();\n } catch (err) {\n console.warn(` ⚠ Local MCP setup failed: ${(err as Error).message}`);\n console.warn(' Hooks are still installed. Re-run `synkro-cli install` to retry.');\n console.log();\n }\n } else {\n try {\n const mintResp = await fetch(`${gatewayUrl}/api/v1/cli/mcp-token`, {\n method: 'POST',\n headers: {\n 'Authorization': `Bearer ${token}`,\n 'Content-Type': 'application/json',\n },\n body: '{}',\n });\n if (!mintResp.ok) {\n const errText = await mintResp.text().catch(() => '');\n throw new Error(`mcp-token mint failed (${mintResp.status}): ${errText.slice(0, 200)}`);\n }\n const minted = await mintResp.json() as { token: string; expires_at: string };\n const mcp = installMcpConfig({ gatewayUrl, bearerToken: minted.token });\n console.log(`Registered Synkro guardrails MCP server in ${mcp.path}`);\n console.log(` url: ${mcp.url}`);\n console.log(` expires: ${minted.expires_at} (~1 year)`);\n console.log(' (restart any running Claude Code session for it to load)');\n console.log();\n } catch (err) {\n console.warn(` ⚠ MCP registration failed: ${(err as Error).message}`);\n console.warn(' Hooks are still installed. Re-run `synkro-cli install` to retry MCP setup.');\n console.log();\n }\n }\n }\n\n // 6. Write config.env — hook scripts read SYNKRO_GATEWAY_URL and the\n // credentials path. They auth via Bearer JWT (resolved from the\n // credentials file at runtime so refreshes Just Work).\n const priorLocalFlag = (() => {\n try {\n const content = readFileSync(CONFIG_PATH, 'utf-8');\n return content.includes(\"SYNKRO_LOCAL_INFERENCE='yes'\");\n } catch { return false; }\n })();\n const synkroBundle = resolveSynkroBundle();\n writeConfigEnv({ gatewayUrl, userId, orgId, email, tier: profile.tier, inference: profile.inference, synkroBin: synkroBundle, transcriptConsent, localInference: profile.localInference });\n console.log(`Wrote config to ${CONFIG_PATH}`);\n console.log(` inference: ${profile.inference} (server-side grading)`);\n if (profile.localInference) console.log(` local inference: enabled (gradingProvider=claude-code)`);\n if (synkroBundle) console.log(` SYNKRO_CLI_BIN=${synkroBundle}`);\n else console.warn(' ⚠ Could not resolve synkro bundle path; hooks will fall back to PATH lookup of `synkro`.');\n\n try {\n const prompts = await fetchJudgePrompts({ gatewayUrl, jwt: token });\n console.log(` prompts: ${prompts.version} (live)`);\n } catch (err) {\n console.warn(` ⚠ Could not cache judge prompts: ${(err as Error).message}`);\n }\n console.log();\n\n // Always install local-cc dependencies (tmux, pueue, bun) so `synkro local-cc enable`\n // works without a second install. On macOS these auto-install via brew.\n console.log('Setting up local-CC dependencies...');\n const localCcDeps: string[] = [];\n try {\n assertClaudeInstalled();\n localCcDeps.push('claude');\n } catch (err) {\n console.warn(` ⚠ claude: ${(err as Error).message}`);\n }\n try {\n assertPueueInstalled();\n localCcDeps.push('pueue');\n } catch (err) {\n console.warn(` ⚠ pueue: ${(err as Error).message}`);\n }\n try {\n assertTmuxInstalled();\n localCcDeps.push('tmux');\n } catch (err) {\n console.warn(` ⚠ tmux: ${(err as Error).message}`);\n }\n if (localCcDeps.length === 3) {\n console.log(` ✓ All local-CC dependencies ready (${localCcDeps.join(', ')})`);\n } else {\n console.warn(` ⚠ Some dependencies missing — \\`synkro local-cc enable\\` may not work until they're installed.`);\n }\n console.log();\n\n if (profile.localInference && localCcDeps.length === 3) {\n try {\n // Stop any running channels BEFORE overwriting plugin files — bun install\n // in the session dir corrupts node_modules under a live process.\n stopPueueTask();\n stopPueueTask(CHANNEL_SECONDARY);\n\n const r = installLocalCC();\n console.log(`Installed local-CC channel plugin at ${r.pluginPath}`);\n\n // Force-restart both channels — installLocalCC() just overwrote plugin\n // files, so any previously running channel is stale.\n const t1 = startPueueTask();\n const t2 = startPueueTask({ channel: CHANNEL_SECONDARY });\n console.log(`Channel 1 (org rules): pueue id=${t1.id} status=${t1.status}`);\n console.log(`Channel 2 (CWE scan): pueue id=${t2.id} status=${t2.status}`);\n\n // Wait for both channels in parallel\n console.log('Waiting for both channels (up to 60s)...');\n const [ready1, ready2] = await Promise.all([\n waitForChannelReady(CHANNEL_PORT, 60_000, CHANNEL_HOST),\n waitForChannelReady(CHANNEL_2_PORT, 60_000, CHANNEL_HOST, CHANNEL_SECONDARY.tmuxSession),\n ]);\n\n if (ready1) {\n console.log(` channel 1 ready at ${CHANNEL_HOST}:${CHANNEL_PORT}`);\n } else {\n console.warn(` ⚠ Channel 1 did not come up within 60s.`);\n printChannelDiagnostics();\n }\n\n if (ready2) {\n console.log(` channel 2 ready at ${CHANNEL_HOST}:${CHANNEL_2_PORT}`);\n } else {\n console.warn(` ⚠ Channel 2 did not come up within 60s.`);\n console.warn(` Run \\`synkro local-cc status\\` to debug.`);\n }\n\n // Warm both channels in parallel\n if (ready1 || ready2) {\n console.log('Warming up inference...');\n const warmups: Promise<void>[] = [];\n if (ready1) {\n warmups.push(\n submitToChannel('grade-bash', 'Proposed command: echo hello\\nUser intent: warmup\\nRecent user messages: []\\nRecent actions: []\\nOrg rules: []\\n', { timeoutMs: 30_000 })\n .then(() => { console.log(' channel 1 warm'); })\n .catch(() => { console.log(' channel 1 warmup skipped (non-fatal)'); }),\n );\n }\n if (ready2) {\n warmups.push(\n submitToChannel('grade-cwe', 'File: /tmp/warmup.ts\\nContent (first 4000 chars):\\nconsole.log(\"hello\");\\n\\nCWE rules to check against:\\n[]\\n', { timeoutMs: 30_000, port: CHANNEL_2_PORT })\n .then(() => { console.log(' channel 2 warm'); })\n .catch(() => { console.log(' channel 2 warmup skipped (non-fatal)'); }),\n );\n }\n await Promise.all(warmups);\n console.log();\n }\n } catch (err) {\n console.warn(` ⚠ Local-CC setup failed: ${(err as Error).message}\\n`);\n }\n }\n\n // 7. Ingest CC session transcripts (only if user consented).\n if (transcriptConsent) {\n try {\n const repo = detectGitRepo();\n if (repo) {\n const ingested = await ingestSessionTranscripts(gatewayUrl, token, repo);\n if (ingested > 0) {\n console.log(`Indexed ${ingested} session insights from Claude Code history for ${repo}.`);\n console.log(' This helps the safety judge understand your workflow.\\n');\n }\n }\n } catch (err) {\n console.warn(` ⚠ Session indexing skipped: ${(err as Error).message}\\n`);\n }\n\n // 7b. Bulk sync CC session transcripts into agent_sessions/agent_messages.\n try {\n const repo = detectGitRepo();\n if (repo) {\n const result = await syncTranscriptsBulk(gatewayUrl, token, repo);\n if (result.messages > 0) {\n console.log(`Synced ${result.sessions} sessions (${result.messages} messages) from Claude Code history.`);\n console.log(' This data will be used to suggest guardrail rules.\\n');\n }\n }\n } catch (err) {\n console.warn(` ⚠ Transcript sync skipped: ${(err as Error).message}\\n`);\n }\n }\n\n // 8. PR scan setup (secrets + workflow) — only if GitHub was connected in step 1b\n if (ghToken) {\n const { setupGithubCommand } = await import('./setupGithub.js');\n await setupGithubCommand({ nonInteractive: true, githubToken: ghToken });\n }\n\n // 9. Done\n console.log('✓ Synkro installed.');\n}\n\nfunction detectGitRepo(): string | null {\n try {\n const remoteUrl = execSync('git remote get-url origin', { encoding: 'utf-8', timeout: 5000 }).trim();\n const sshMatch = remoteUrl.match(/^git@[^:]+:(.+?)(?:\\.git)?$/);\n const httpMatch = remoteUrl.match(/^https?:\\/\\/[^/]+\\/(.+?)(?:\\.git)?$/);\n const match = sshMatch || httpMatch;\n return match ? match[1] : null;\n } catch {\n return null;\n }\n}\n\nfunction getClaudeProjectsFolder(): string | null {\n const cwd = process.cwd();\n // CC stores transcripts in ~/.claude/projects/{sanitized-path}/\n // where sanitized-path replaces / with -\n const sanitized = '-' + cwd.replace(/\\//g, '-');\n const projectsDir = join(homedir(), '.claude', 'projects', sanitized);\n return existsSync(projectsDir) ? projectsDir : null;\n}\n\ninterface SessionInsight {\n session_id: string;\n insight_type: 'summary' | 'user_message';\n content: string;\n metadata?: Record<string, unknown>;\n}\n\nfunction extractSessionInsights(projectsDir: string): SessionInsight[] {\n const insights: SessionInsight[] = [];\n const files = readdirSync(projectsDir).filter(f => f.endsWith('.jsonl'));\n\n for (const file of files) {\n const sessionId = file.replace('.jsonl', '');\n const filePath = join(projectsDir, file);\n\n try {\n const content = readFileSync(filePath, 'utf-8');\n const lines = content.split('\\n').filter(Boolean);\n\n // Extract compaction summaries\n for (let i = 0; i < lines.length; i++) {\n try {\n const entry = JSON.parse(lines[i]);\n if (entry.type === 'user' &&\n typeof entry.message?.content === 'string' &&\n entry.message.content.startsWith('This session is being continued')) {\n insights.push({\n session_id: sessionId,\n insight_type: 'summary',\n content: entry.message.content.slice(0, 4000),\n metadata: { source: 'compaction_summary' },\n });\n }\n } catch {}\n }\n\n // Extract user messages (last 20 per session — recent preferences)\n const userMessages: string[] = [];\n for (let i = lines.length - 1; i >= 0 && userMessages.length < 20; i--) {\n try {\n const entry = JSON.parse(lines[i]);\n if (entry.type === 'user') {\n const text = typeof entry.message?.content === 'string'\n ? entry.message.content\n : Array.isArray(entry.message?.content)\n ? entry.message.content.map((b: any) => b.text ?? b).filter((t: any) => typeof t === 'string').join(' ')\n : null;\n if (text && text.length > 10 && text.length < 2000 && !text.startsWith('This session is being continued')) {\n userMessages.push(text);\n }\n }\n } catch {}\n }\n for (const msg of userMessages.reverse()) {\n insights.push({\n session_id: sessionId,\n insight_type: 'user_message',\n content: msg.slice(0, 2000),\n });\n }\n } catch {}\n }\n\n return insights;\n}\n\nasync function ingestSessionTranscripts(gatewayUrl: string, token: string, repo: string): Promise<number> {\n const projectsDir = getClaudeProjectsFolder();\n if (!projectsDir) return 0;\n\n const insights = extractSessionInsights(projectsDir);\n if (insights.length === 0) return 0;\n\n console.log(`Found ${insights.length} session insights from Claude Code history...`);\n\n // Send in batches of 100\n let total = 0;\n for (let i = 0; i < insights.length; i += 100) {\n const batch = insights.slice(i, i + 100);\n try {\n const resp = await fetch(`${gatewayUrl}/api/v1/cli/ingest-sessions`, {\n method: 'POST',\n headers: {\n 'Authorization': `Bearer ${token}`,\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({ repo, sessions: batch }),\n });\n if (resp.ok) {\n const result = await resp.json() as { accepted: number };\n total += result.accepted;\n }\n } catch {}\n }\n\n return total;\n}\n\ninterface TranscriptMessage {\n message_index: number;\n type: 'user' | 'assistant';\n content: string;\n tool_calls?: Array<{ name: string; input: string; id: string }>;\n model?: string;\n usage?: {\n input_tokens?: number;\n output_tokens?: number;\n cache_creation_input_tokens?: number;\n cache_read_input_tokens?: number;\n };\n}\n\nfunction extractTextContent(content: any): string {\n if (typeof content === 'string') return content.slice(0, 8000);\n if (Array.isArray(content)) {\n return content\n .filter((b: any) => typeof b === 'string' || (b?.type === 'text'))\n .map((b: any) => typeof b === 'string' ? b : (b?.text || ''))\n .join(' ')\n .slice(0, 8000);\n }\n return '';\n}\n\nfunction parseTranscriptFile(filePath: string): TranscriptMessage[] {\n const content = readFileSync(filePath, 'utf-8');\n const lines = content.split('\\n').filter(Boolean);\n const messages: TranscriptMessage[] = [];\n\n for (let i = 0; i < lines.length; i++) {\n try {\n const entry = JSON.parse(lines[i]);\n if (entry.type !== 'user' && entry.type !== 'assistant') continue;\n\n const msg: TranscriptMessage = {\n message_index: i,\n type: entry.type,\n content: extractTextContent(entry.message?.content),\n };\n\n if (entry.type === 'assistant') {\n if (Array.isArray(entry.message?.content)) {\n const toolCalls = entry.message.content\n .filter((b: any) => b?.type === 'tool_use')\n .map((b: any) => ({\n name: b.name || '',\n input: JSON.stringify(b.input || {}).slice(0, 500),\n id: b.id || '',\n }));\n if (toolCalls.length > 0) msg.tool_calls = toolCalls;\n }\n if (entry.message?.model) msg.model = entry.message.model;\n if (entry.message?.usage) {\n msg.usage = {\n input_tokens: entry.message.usage.input_tokens,\n output_tokens: entry.message.usage.output_tokens,\n cache_creation_input_tokens: entry.message.usage.cache_creation_input_tokens,\n cache_read_input_tokens: entry.message.usage.cache_read_input_tokens,\n };\n }\n }\n\n if (msg.content.length > 0) messages.push(msg);\n } catch {}\n }\n\n return messages;\n}\n\nasync function syncTranscriptsBulk(gatewayUrl: string, token: string, repo: string): Promise<{ sessions: number; messages: number }> {\n const projectsDir = getClaudeProjectsFolder();\n if (!projectsDir) return { sessions: 0, messages: 0 };\n\n const files = readdirSync(projectsDir).filter(f => f.endsWith('.jsonl'));\n if (files.length === 0) return { sessions: 0, messages: 0 };\n\n console.log(`Found ${files.length} CC session transcripts, syncing...`);\n\n const maxMessagesPerSession = 500;\n let totalSessions = 0;\n let totalMessages = 0;\n\n // Batch sessions into groups of 5\n for (let i = 0; i < files.length; i += 5) {\n const batch = files.slice(i, i + 5);\n const sessions: Array<{ cc_session_id: string; messages: TranscriptMessage[] }> = [];\n\n for (const file of batch) {\n const sessionId = file.replace('.jsonl', '');\n const filePath = join(projectsDir, file);\n\n try {\n const allMessages = parseTranscriptFile(filePath);\n const messages = allMessages.length > maxMessagesPerSession\n ? allMessages.slice(-maxMessagesPerSession)\n : allMessages;\n\n if (messages.length > 0) {\n sessions.push({ cc_session_id: sessionId, messages });\n }\n } catch {}\n }\n\n if (sessions.length === 0) continue;\n\n try {\n const resp = await fetch(`${gatewayUrl}/api/v1/cli/sync-transcripts`, {\n method: 'POST',\n headers: {\n 'Authorization': `Bearer ${token}`,\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({ repo, sessions }),\n });\n if (resp.ok) {\n const result = await resp.json() as { accepted: number; sessions: number };\n totalMessages += result.accepted;\n totalSessions += result.sessions;\n }\n } catch {}\n\n // Write offset files so the Stop hook doesn't re-send\n for (const file of batch) {\n const sessionId = file.replace('.jsonl', '');\n const filePath = join(projectsDir, file);\n try {\n const content = readFileSync(filePath, 'utf-8');\n const lineCount = content.split('\\n').filter(Boolean).length;\n writeFileSync(join(OFFSETS_DIR, sessionId), String(lineCount), 'utf-8');\n } catch {}\n }\n }\n\n return { sessions: totalSessions, messages: totalMessages };\n}\n","/**\n * synkro login — browser OAuth via WorkOS (reuses existing auth flow).\n */\nimport { authenticate, isAuthenticated, getUserInfo } from '../auth/stub.js';\n\nexport async function loginCommand(args: string[] = []): Promise<void> {\n const force = args.includes('--force') || args.includes('-f');\n if (isAuthenticated() && !force) {\n const info = getUserInfo();\n console.log(`Already authenticated as ${info?.email ?? 'unknown'}.`);\n console.log('Use --force to re-authenticate.');\n return;\n }\n console.log('Opening browser for Synkro login...');\n const result = await authenticate((status) => {\n switch (status.phase) {\n case 'starting': console.log(' Starting local callback server...'); break;\n case 'browser-opened': console.log(` Browser opened: ${status.url}`); break;\n case 'waiting': console.log(' Waiting for browser auth to complete...'); break;\n case 'success': console.log(' ✓ Authenticated'); break;\n case 'error': console.error(` ✗ ${status.message}`); break;\n }\n });\n if (!result) {\n console.error('Login failed. Make sure the Synkro web app is running.');\n process.exit(1);\n }\n const info = getUserInfo();\n console.log(`✓ Logged in as ${info?.email ?? 'unknown'}.`);\n}\n","/**\n * synkro logout — clear local credentials.\n */\nimport { isAuthenticated, clearCredentials } from '../auth/stub.js';\n\nexport function logoutCommand(): void {\n if (!isAuthenticated()) {\n console.log('Not authenticated.');\n return;\n }\n clearCredentials();\n console.log('Logged out.');\n}\n","// :)\n/**\n * synkro status — show current setup state.\n */\nimport { existsSync, readFileSync } from 'node:fs';\nimport { homedir } from 'node:os';\nimport { join } from 'node:path';\nimport { isAuthenticated, getUserInfo, getAccessToken, ensureValidToken } from '../auth/stub.js';\nimport { detectAgents } from '../installer/agentDetect.js';\nimport { inspectCCHooks } from '../installer/ccHookConfig.js';\nimport { inspectCursorHooks } from '../installer/cursorHookConfig.js';\nimport { inspectMcpConfig } from '../installer/mcpConfig.js';\nimport { findTask, CHANNEL_PRIMARY, CHANNEL_SECONDARY } from '../local-cc/pueue.js';\n\nconst SYNKRO_DIR = join(homedir(), '.synkro');\nconst CONFIG_PATH = join(SYNKRO_DIR, 'config.env');\n\nfunction readConfigEnv(): Record<string, string> {\n if (!existsSync(CONFIG_PATH)) return {};\n const out: Record<string, string> = {};\n const raw = readFileSync(CONFIG_PATH, 'utf-8');\n for (const line of raw.split('\\n')) {\n const trimmed = line.trim();\n if (!trimmed || trimmed.startsWith('#')) continue;\n const eq = trimmed.indexOf('=');\n if (eq > 0) {\n const k = trimmed.slice(0, eq).trim();\n const v = trimmed.slice(eq + 1).trim();\n out[k] = v;\n }\n }\n return out;\n}\n\nexport async function statusCommand(): Promise<void> {\n console.log('Synkro CLI status\\n');\n\n // Auth\n if (isAuthenticated()) {\n const info = getUserInfo();\n console.log(`Authentication: ✓ logged in as ${info?.email ?? 'unknown'}`);\n if (info?.org_id) console.log(` org_id: ${info.org_id}`);\n if (info?.id) console.log(` user_id: ${info.id}`);\n } else {\n console.log('Authentication: ✗ not logged in (run: synkro-cli login)');\n }\n console.log();\n\n // Config — fetch live profile from server\n const config = readConfigEnv();\n const gatewayUrl = (config.SYNKRO_GATEWAY_URL || 'https://api.synkro.sh').replace(/^['\"]|['\"]$/g, '');\n let serverTier = config.SYNKRO_TIER || '(unset)';\n let serverInference = config.SYNKRO_INFERENCE || '(unset)';\n let localInference = false;\n await ensureValidToken();\n const token = getAccessToken();\n if (token) {\n try {\n const resp = await fetch(`${gatewayUrl}/api/v1/cli/me`, {\n headers: { 'Authorization': `Bearer ${token}` },\n signal: AbortSignal.timeout(5000),\n });\n if (resp.ok) {\n const data = await resp.json() as { fast_inference?: boolean; local_inference?: boolean; plan_tier?: string; tier?: string };\n serverInference = data.fast_inference ? 'fast' : 'standard';\n localInference = !!data.local_inference;\n serverTier = data.plan_tier ?? data.tier ?? serverTier;\n }\n } catch {}\n }\n\n console.log('Config:');\n console.log(` gateway: ${gatewayUrl}`);\n console.log(` credentials: ${config.SYNKRO_CREDENTIALS_PATH ?? '(unset)'}`);\n console.log(` tier: ${serverTier}`);\n const inferenceLabel = localInference\n ? \"'local' (grading via Claude Code channels)\"\n : \"'server' (grading via provider keys)\";\n console.log(` inference: ${inferenceLabel}`);\n console.log(` version: ${config.SYNKRO_VERSION ?? '(unset)'}`);\n console.log();\n\n // Agents\n const agents = detectAgents();\n console.log('Detected agents:');\n if (agents.length === 0) {\n console.log(' (none — install Claude Code first)');\n } else {\n for (const a of agents) {\n console.log(` ✓ ${a.name}${a.version ? ` (${a.version})` : ''}`);\n console.log(` settings: ${a.settingsPath}`);\n if (a.kind === 'claude_code') {\n const hooks = inspectCCHooks(a.settingsPath);\n console.log(` hooks installed: ${hooks.installed ? '✓' : '✗'}`);\n if (hooks.installed) {\n console.log(` • PreToolUse Bash: ${hooks.preToolUseBash ? '✓' : '✗'}`);\n console.log(` • PreToolUse Edit: ${hooks.postToolUseEdit ? '✓' : '✗'}`);\n console.log(` • SessionEnd summary: ${hooks.sessionEnd ? '✓' : '✗'}`);\n console.log(` • SessionStart: ${hooks.sessionStart ? '✓' : '✗'}`);\n }\n } else if (a.kind === 'cursor') {\n const hooks = inspectCursorHooks(a.settingsPath);\n console.log(` hooks installed: ${hooks.installed ? '✓' : '✗'}`);\n if (hooks.installed) {\n console.log(` • sessionStart: ${hooks.sessionStart ? '✓' : '✗'}`);\n console.log(` • sessionEnd: ${hooks.sessionEnd ? '✓' : '✗'}`);\n console.log(` • beforeSubmitPrompt: ${hooks.beforeSubmitPrompt ? '✓' : '✗'}`);\n console.log(` • beforeShellExecution: ${hooks.beforeShellExecution ? '✓' : '✗'}`);\n console.log(` • afterShellExecution: ${hooks.afterShellExecution ? '✓' : '✗'}`);\n console.log(` • PreToolUse Bash: ${hooks.preToolUseBash ? '✓' : '✗'}`);\n console.log(` • PreToolUse Edit: ${hooks.preToolUseEdit ? '✓' : '✗'}`);\n console.log(` • PreToolUse CWE: ${hooks.preToolUseCwe ? '✓' : '✗'}`);\n console.log(` • PreToolUse CVE: ${hooks.preToolUseCve ? '✓' : '✗'}`);\n console.log(` • PreToolUse Agent: ${hooks.preToolUseAgent ? '✓' : '✗'}`);\n console.log(` • PreToolUse Plan: ${hooks.preToolUsePlan ? '✓' : '✗'}`);\n console.log(` • afterFileEdit: ${hooks.afterFileEdit ? '✓' : '✗'}`);\n console.log(` • postToolUse: ${hooks.postToolUse ? '✓' : '✗'}`);\n console.log(` • stop (transcript): ${hooks.stop ? '✓' : '✗'}`);\n }\n }\n }\n }\n console.log();\n\n // Hook scripts — CC and Cursor hooks are TypeScript (bun); _synkro-common.sh is legacy CC fallback only\n const HOOKS_DIR = join(SYNKRO_DIR, 'hooks');\n const ccHooks = [\n 'cc-bash-judge.ts',\n 'cc-bash-followup.ts',\n 'cc-edit-precheck.ts',\n 'cc-cwe-precheck.ts',\n 'cc-cve-precheck.ts',\n 'cc-plan-judge.ts',\n 'cc-agent-judge.ts',\n 'cc-stop-summary.ts',\n 'cc-session-start.ts',\n 'cc-transcript-sync.ts',\n 'cc-user-prompt-submit.ts',\n '_synkro-common.ts',\n ];\n const cursorHooks = [\n 'cursor-bash-judge.ts',\n 'cursor-edit-capture.ts',\n 'cc-edit-precheck.ts',\n 'cc-cwe-precheck.ts',\n 'cc-cve-precheck.ts',\n 'cc-agent-judge.ts',\n 'cc-plan-judge.ts',\n 'cc-session-start.ts',\n 'cc-stop-summary.ts',\n 'cc-bash-followup.ts',\n 'cc-user-prompt-submit.ts',\n 'cc-transcript-sync.ts',\n '_synkro-common.ts',\n ];\n console.log('Hook scripts (Claude Code):');\n for (const f of ccHooks) {\n const p = join(HOOKS_DIR, f);\n console.log(` ${existsSync(p) ? '✓' : '✗'} ${p}`);\n }\n console.log('Hook scripts (Cursor):');\n for (const f of cursorHooks) {\n const p = join(HOOKS_DIR, f);\n console.log(` ${existsSync(p) ? '✓' : '✗'} ${p}`);\n }\n console.log();\n\n // Local-CC channels\n if (localInference) {\n console.log('Local-CC channels:');\n try {\n const t1 = findTask(CHANNEL_PRIMARY);\n if (t1) {\n console.log(` Channel 1 (org rules): pueue id=${t1.id} status=${t1.status}`);\n } else {\n console.log(' Channel 1 (org rules): not running');\n }\n const t2 = findTask(CHANNEL_SECONDARY);\n if (t2) {\n console.log(` Channel 2 (CWE scan): pueue id=${t2.id} status=${t2.status}`);\n } else {\n console.log(' Channel 2 (CWE scan): not running');\n }\n } catch {\n console.log(' (pueue not available)');\n }\n console.log();\n }\n\n // MCP guardrails server registration (CC's ~/.claude.json)\n const mcp = inspectMcpConfig();\n console.log('Guardrails MCP server (Claude Code):');\n if (mcp.installed) {\n console.log(` ✓ registered in ${mcp.configPath}`);\n console.log(` url: ${mcp.url}`);\n } else {\n console.log(` ✗ not registered (run: synkro-cli install)`);\n console.log(` expected at ${mcp.configPath} → mcpServers.synkro-guardrails`);\n }\n}\n","// :)\n/**\n * synkro link — connect repos to a Synkro project.\n *\n * Same two-path UX as install: link the local git repo or connect GitHub\n * to select from your accessible repos.\n */\nimport { isAuthenticated, ensureValidToken } from '../auth/stub.js';\nimport { promptRepoConnection } from './repoConnect.js';\n\nexport async function linkCommand(): Promise<void> {\n if (!isAuthenticated()) {\n console.error('Not authenticated. Run `synkro install` or `synkro login` first.');\n process.exit(1);\n }\n await ensureValidToken();\n await promptRepoConnection();\n}\n","// :)\n/**\n * synkro unlink — remove repo links from Synkro projects.\n *\n * Lists all linked repos across projects, lets the user pick which to remove.\n */\nimport { createInterface } from 'node:readline';\nimport { isAuthenticated, ensureValidToken } from '../auth/stub.js';\nimport { listProjects, unlinkRepo } from '../api/projects.js';\n\nfunction ask(rl: ReturnType<typeof createInterface>, question: string): Promise<string> {\n return new Promise((resolve) => rl.question(question, resolve));\n}\n\nexport async function unlinkCommand(): Promise<void> {\n if (!isAuthenticated()) {\n console.error('Not authenticated. Run `synkro install` or `synkro login` first.');\n process.exit(1);\n }\n await ensureValidToken();\n\n const projects = await listProjects();\n const linked: Array<{ projectId: string; projectName: string; repoId: string; fullName: string }> = [];\n\n for (const p of projects) {\n for (const r of (p as any).repos || []) {\n if (r.full_name && r.id) {\n linked.push({ projectId: p.id, projectName: p.name, repoId: r.id, fullName: r.full_name });\n }\n }\n }\n\n if (linked.length === 0) {\n console.log('No linked repos found.');\n return;\n }\n\n console.log('\\nLinked repos:\\n');\n linked.forEach((r, i) => {\n console.log(` ${i + 1}. ${r.fullName} (${r.projectName})`);\n });\n console.log();\n\n const rl = createInterface({ input: process.stdin, output: process.stdout });\n try {\n const selection = await ask(rl, ' Select repos to unlink (comma-separated numbers): ');\n const indices = selection\n .split(',')\n .map((s) => parseInt(s.trim(), 10) - 1)\n .filter((n) => !isNaN(n) && n >= 0 && n < linked.length);\n\n if (indices.length === 0) {\n console.log(' No repos selected.');\n return;\n }\n\n for (const idx of indices) {\n const r = linked[idx];\n await unlinkRepo(r.projectId, r.repoId);\n console.log(` ✓ Unlinked ${r.fullName} from ${r.projectName}`);\n }\n } finally {\n rl.close();\n }\n console.log();\n}\n","import { readFileSync, writeFileSync, existsSync } from 'node:fs';\nimport { join } from 'node:path';\nimport { homedir } from 'node:os';\nimport { isAuthenticated, getAccessToken } from '../auth/stub.js';\n\nconst SYNKRO_DIR = join(homedir(), '.synkro');\nconst CONFIG_PATH = join(SYNKRO_DIR, 'config.env');\n\nfunction readConfigEnv(): Record<string, string> {\n if (!existsSync(CONFIG_PATH)) return {};\n const out: Record<string, string> = {};\n for (const line of readFileSync(CONFIG_PATH, 'utf-8').split('\\n')) {\n const t = line.trim();\n if (!t || t.startsWith('#')) continue;\n const eq = t.indexOf('=');\n if (eq > 0) out[t.slice(0, eq).trim()] = t.slice(eq + 1).trim().replace(/^['\"]|['\"]$/g, '');\n }\n return out;\n}\n\nfunction updateConfigValue(key: string, value: string): void {\n if (!existsSync(CONFIG_PATH)) {\n console.error('No config found. Run `synkro install` first.');\n process.exit(1);\n }\n const lines = readFileSync(CONFIG_PATH, 'utf-8').split('\\n');\n const pattern = new RegExp(`^${key}=`);\n let found = false;\n const updated = lines.map((line) => {\n if (pattern.test(line.trim())) {\n found = true;\n return `${key}='${value}'`;\n }\n return line;\n });\n if (!found) updated.splice(updated.length - 1, 0, `${key}='${value}'`);\n writeFileSync(CONFIG_PATH, updated.join('\\n'), 'utf-8');\n}\n\nexport async function configCommand(args: string[]): Promise<void> {\n if (args.length === 0) {\n const config = readConfigEnv();\n console.log('Synkro config:\\n');\n console.log(` inference: ${config.SYNKRO_INFERENCE || 'fast'}`);\n console.log(` tier: ${config.SYNKRO_TIER || 'pro'}`);\n console.log(` gateway: ${config.SYNKRO_GATEWAY_URL || 'https://api.synkro.sh'}`);\n console.log(` version: ${config.SYNKRO_VERSION || '?'}`);\n console.log(`\\nTo change: synkro config --inference fast|standard`);\n return;\n }\n\n let inferenceValue: string | undefined;\n for (const a of args) {\n if (a.startsWith('--inference=')) inferenceValue = a.slice('--inference='.length);\n else if (a === '--inference' && args.indexOf(a) + 1 < args.length) inferenceValue = args[args.indexOf(a) + 1];\n }\n\n if (!inferenceValue || !['fast', 'standard'].includes(inferenceValue)) {\n console.error('Usage: synkro config --inference fast|standard');\n process.exit(1);\n }\n\n if (!isAuthenticated()) {\n console.error('Not authenticated. Run `synkro login` first.');\n process.exit(1);\n }\n\n const token = getAccessToken();\n const config = readConfigEnv();\n const gatewayUrl = (config.SYNKRO_GATEWAY_URL || 'https://api.synkro.sh').replace(/\\/$/, '');\n\n try {\n const resp = await fetch(`${gatewayUrl}/api/v1/cli/me`, {\n method: 'PATCH',\n headers: {\n 'Authorization': `Bearer ${token}`,\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({ fast_inference: inferenceValue === 'fast' }),\n });\n if (!resp.ok) {\n const errText = await resp.text().catch(() => '');\n console.error(`Failed to update: ${resp.status} ${errText.slice(0, 200)}`);\n process.exit(1);\n }\n } catch (err) {\n console.error(`Failed to reach server: ${(err as Error).message}`);\n process.exit(1);\n }\n\n updateConfigValue('SYNKRO_INFERENCE', inferenceValue);\n console.log(`✓ Inference set to '${inferenceValue}'.`);\n}\n","// :)\n/**\n * synkro scan-pr — runs inside a GitHub Actions runner.\n *\n * Reads PR context from env (set by the workflow YAML), fetches the PR diff\n * via `gh` CLI, spawns `claude --print` per changed file with the customer's\n * CLAUDE_CODE_OAUTH_TOKEN, aggregates findings, posts inline review comments,\n * sets a status check, and POSTs aggregate to /v1/events/pr-scan for logging.\n *\n * Auth chain (set by workflow YAML):\n * CLAUDE_CODE_OAUTH_TOKEN — long-lived OAuth token from `claude setup-token`\n * SYNKRO_API_KEY — auth for our backend\n * GH_TOKEN — GitHub-injected, for posting comments + checks\n * SYNKRO_PR_NUMBER, SYNKRO_REPO, SYNKRO_SHA — PR context\n */\nimport { execSync, spawn } from 'node:child_process';\nimport { readFileSync, existsSync } from 'node:fs';\nimport { dirname, join } from 'node:path';\n\nconst SKIP_FILE_PATTERNS = [\n /\\.lock$/i,\n /\\.min\\./i,\n /\\.map$/i,\n /^dist\\//,\n /^build\\//,\n /^vendor\\//,\n /^node_modules\\//,\n /^\\.next\\//,\n /package-lock\\.json$/,\n /yarn\\.lock$/,\n /pnpm-lock\\.yaml$/,\n /Cargo\\.lock$/,\n /go\\.sum$/,\n];\n\nconst MAX_DIFF_LINES_PER_FILE = 1000;\nconst MAX_PARALLEL_FILES = 10;\n\ninterface PrFile {\n filename: string;\n status: string; // 'added' | 'modified' | 'removed' | 'renamed'\n additions: number;\n deletions: number;\n patch?: string;\n}\n\ninterface Finding {\n file: string;\n line: number;\n severity: 'low' | 'medium' | 'high' | 'critical';\n category: string;\n description: string;\n fix: string;\n}\n\ninterface OrgRule {\n rule_id: string;\n text: string;\n category: string;\n severity: string;\n mode: 'audit' | 'literal_match';\n condition: string;\n}\n\ninterface LiteralMatchSpec {\n selector: string;\n requires: string;\n position: 'start' | 'anywhere';\n negate?: boolean;\n}\n\nfunction parseMatchSpec(condition: string): LiteralMatchSpec | null {\n if (!condition.startsWith('match_spec:')) return null;\n try {\n const parsed = JSON.parse(condition.slice('match_spec:'.length)) as Partial<LiteralMatchSpec>;\n if (\n typeof parsed?.selector !== 'string' ||\n typeof parsed?.requires !== 'string' ||\n (parsed.position !== 'start' && parsed.position !== 'anywhere')\n ) return null;\n return {\n selector: parsed.selector,\n requires: parsed.requires,\n position: parsed.position,\n negate: parsed.negate === true,\n };\n } catch {\n return null;\n }\n}\n\nfunction selectorMatches(selector: string, filePath: string): boolean {\n const m = selector.match(/^\\*\\*\\/\\*\\.([a-z0-9]+)$/i);\n if (!m) return false;\n return filePath.toLowerCase().endsWith('.' + m[1].toLowerCase());\n}\n\nasync function fetchOrgRules(gatewayUrl: string, apiKey: string): Promise<OrgRule[]> {\n try {\n const resp = await fetch(`${gatewayUrl.replace(/\\/$/, '')}/api/v1/cli/pr-rules`, {\n headers: { 'x-synkro-api-key': apiKey },\n });\n if (!resp.ok) {\n console.warn(`[scan-pr] failed to fetch org rules: HTTP ${resp.status}`);\n return [];\n }\n const data = await resp.json() as { rules?: OrgRule[] };\n return Array.isArray(data?.rules) ? data.rules : [];\n } catch (err) {\n console.warn(`[scan-pr] could not fetch org rules: ${(err as Error).message}`);\n return [];\n }\n}\n\n// Deterministic literal_match enforcement on the patch's added lines.\n// We can only check the diff (not the full file), so this catches\n// negative-form rules (\"Never use X\") cleanly. Positive-form rules\n// (\"must contain X\") need the full file content and are deferred to\n// edit-time hooks for v1 — we surface a note rather than skip silently.\nfunction applyLiteralMatchNegative(rules: OrgRule[], file: PrFile): Finding[] {\n if (!file.patch) return [];\n const findings: Finding[] = [];\n const lines = file.patch.split('\\n');\n let currentNewLine = 0;\n for (const line of lines) {\n if (line.startsWith('@@')) {\n const m = line.match(/\\+(\\d+)(?:,\\d+)?/);\n if (m) currentNewLine = parseInt(m[1], 10);\n continue;\n }\n if (line.startsWith('+++') || line.startsWith('---')) continue;\n if (!line.startsWith('+')) {\n // context or removed line: only NEW-file line counter advances on context\n if (!line.startsWith('-')) currentNewLine++;\n continue;\n }\n const addedContent = line.slice(1); // strip leading '+'\n for (const r of rules) {\n if (r.mode !== 'literal_match') continue;\n const spec = parseMatchSpec(r.condition);\n if (!spec || !spec.negate) continue; // only negative rules in PR scan v1\n if (!selectorMatches(spec.selector, file.filename)) continue;\n if (!addedContent.includes(spec.requires)) continue;\n findings.push({\n file: file.filename,\n line: currentNewLine,\n severity: (r.severity as Finding['severity']) ?? 'high',\n category: r.category || 'literal_match',\n description: r.text,\n fix: `Remove \\`${spec.requires}\\` from ${file.filename} (rule ${r.rule_id}).`,\n });\n }\n currentNewLine++;\n }\n return findings;\n}\n\nfunction buildPrPrompt(orgAuditRules: OrgRule[]): string {\n const orgRulesBlock = orgAuditRules.length === 0\n ? ''\n : `\\nORG-SPECIFIC RULES (these are the customer's policies — flag any violation found in the diff):\\n` +\n orgAuditRules\n .map((r, i) => ` ${i + 1}. [${r.severity}/${r.category}] ${r.text}`)\n .join('\\n') +\n '\\n';\n\n return `You are a security code reviewer analyzing a pull request diff for one file. Identify security issues + org-policy violations introduced by this diff.\n\nOutput ONLY a JSON object (no prose, no markdown fences):\n{\n \"findings\": [\n {\n \"line\": <int — line number in the NEW file, prefixed with L in the diff>,\n \"severity\": \"low\" | \"medium\" | \"high\" | \"critical\",\n \"category\": \"<snake_case>\",\n \"description\": \"<1 sentence>\",\n \"fix\": \"<concrete suggestion>\"\n }\n ],\n \"summary\": \"<one-line: 'X findings' or 'clean'>\"\n}\n\nBaseline security categories: hardcoded_secret, sql_injection, insecure_crypto, eval_exec, unsafe_deserialization, missing_validation, exposed_internal, missing_auth_check, cors_misconfig, path_traversal, command_injection, weak_random, broken_jwt.\n${orgRulesBlock}\nRules:\n- Only flag NEW issues (lines starting with +).\n- Use the L<num> line numbers I prefixed.\n- Be specific. If clean, return {\"findings\": [], \"summary\": \"clean\"}.\n\n`;\n}\n\nfunction shouldSkipFile(filename: string): boolean {\n return SKIP_FILE_PATTERNS.some((p) => p.test(filename));\n}\n\nfunction ghJson<T>(args: string[]): T {\n const out = execSync(`gh ${args.map((a) => `'${a.replace(/'/g, \"'\\\\''\")}'`).join(' ')}`, {\n encoding: 'utf-8',\n maxBuffer: 16 * 1024 * 1024,\n });\n return JSON.parse(out) as T;\n}\n\ninterface PrTarget {\n prNumber: number;\n sha: string;\n branched: boolean;\n prState: string;\n merged: boolean;\n}\n\nasync function ensureOpenPr(repo: string, prNumber: number, sha: string): Promise<PrTarget> {\n let pr: { state: string; merged: boolean; head: { ref: string; sha: string }; base: { ref: string }; title: string };\n try {\n pr = ghJson<typeof pr>(['api', `/repos/${repo}/pulls/${prNumber}`]);\n } catch {\n return { prNumber, sha, branched: false, prState: 'unknown', merged: false };\n }\n\n // Always scan the original PR's diff. Reviews work for open + merged PRs\n // (their diff is preserved). For closed-non-merged PRs, the line-level\n // review will 422 and postPrReview falls back to an issue comment.\n return { prNumber, sha: pr.head.sha, branched: false, prState: pr.state, merged: pr.merged };\n}\n\nfunction getPrFiles(repo: string, prNumber: number): PrFile[] {\n const data = ghJson<PrFile[]>([\n 'api',\n `/repos/${repo}/pulls/${prNumber}/files?per_page=250`,\n ]);\n return data;\n}\n\ninterface ScanContext {\n skip?: boolean;\n scan_all?: boolean;\n files?: string[];\n last_sha?: string;\n reason?: string;\n}\n\nfunction getLastReviewedSha(repo: string, prNumber: number): string | null {\n try {\n const reviews = ghJson<Array<{ body: string | null; commit_id: string; submitted_at: string }>>([\n 'api', `/repos/${repo}/pulls/${prNumber}/reviews?per_page=100`,\n ]);\n const synkro = reviews\n .filter((r) => r.body?.includes('Synkro Security Review'))\n .sort((a, b) => new Date(b.submitted_at).getTime() - new Date(a.submitted_at).getTime());\n return synkro.length > 0 ? synkro[0].commit_id : null;\n } catch {\n return null;\n }\n}\n\nfunction getChangedFilesSince(repo: string, baseSha: string, headSha: string): string[] | null {\n try {\n const data = ghJson<{ files?: Array<{ filename: string }> }>([\n 'api', `/repos/${repo}/compare/${baseSha}...${headSha}`,\n ]);\n return (data.files || []).map((f) => f.filename);\n } catch {\n return null;\n }\n}\n\nasync function fetchScanContext(gatewayUrl: string, apiKey: string, repo: string, prNumber: number, sha: string): Promise<ScanContext> {\n const lastSha = getLastReviewedSha(repo, prNumber);\n const changedFiles = lastSha && lastSha !== sha ? getChangedFilesSince(repo, lastSha, sha) : undefined;\n\n try {\n const url = `${gatewayUrl.replace(/\\/$/, '')}/api/pr-scans/scan-context`;\n const resp = await fetch(url, {\n method: 'POST',\n headers: { 'x-synkro-api-key': apiKey, 'Content-Type': 'application/json' },\n body: JSON.stringify({ sha, last_reviewed_sha: lastSha, changed_files: changedFiles }),\n signal: AbortSignal.timeout(15_000),\n });\n if (!resp.ok) return { scan_all: true };\n return await resp.json() as ScanContext;\n } catch {\n return { scan_all: true };\n }\n}\n\nfunction getFileDiffWithLines(file: PrFile): { hunks: string; newFileLineMap: Map<number, number> } {\n // For each hunk, build a map of \"position in patch\" → \"line number in NEW file\"\n // so claude can reference correct line numbers.\n if (!file.patch) return { hunks: '', newFileLineMap: new Map() };\n\n const lines = file.patch.split('\\n');\n const annotated: string[] = [];\n const lineMap = new Map<number, number>();\n let currentNewLine = 0;\n let patchIndex = 0;\n\n for (const line of lines) {\n patchIndex++;\n if (line.startsWith('@@')) {\n // Parse hunk header: @@ -A,B +C,D @@ ...\n const match = line.match(/\\+(\\d+)(?:,\\d+)?/);\n if (match) {\n currentNewLine = parseInt(match[1], 10);\n }\n annotated.push(line);\n continue;\n }\n if (line.startsWith('+') && !line.startsWith('+++')) {\n annotated.push(`L${currentNewLine}: ${line}`);\n lineMap.set(patchIndex, currentNewLine);\n currentNewLine++;\n } else if (line.startsWith('-') && !line.startsWith('---')) {\n annotated.push(line);\n // line was removed; don't increment new file line counter\n } else if (!line.startsWith('---') && !line.startsWith('+++')) {\n annotated.push(`L${currentNewLine}: ${line}`);\n currentNewLine++;\n } else {\n annotated.push(line);\n }\n }\n\n return { hunks: annotated.join('\\n'), newFileLineMap: lineMap };\n}\n\nfunction spawnClaudeJudge(file: PrFile, claudeToken: string, promptHeader: string): Promise<{ findings: Finding[]; latencyMs: number }> {\n const { hunks } = getFileDiffWithLines(file);\n const userMessage = `File: ${file.filename}\\n\\nDiff:\\n${hunks}`;\n const fullPrompt = promptHeader + userMessage;\n\n return new Promise((resolve) => {\n const t0 = Date.now();\n const proc = spawn(\n 'claude',\n ['--print', '--model', 'claude-sonnet-4-6', '--output-format', 'json', '--no-session-persistence'],\n {\n stdio: ['pipe', 'pipe', 'pipe'],\n env: {\n ...process.env,\n CLAUDE_CODE_OAUTH_TOKEN: claudeToken,\n },\n timeout: 120_000,\n },\n );\n proc.stdin.write(fullPrompt);\n proc.stdin.end();\n let stdout = '';\n let stderr = '';\n proc.stdout.on('data', (chunk) => { stdout += chunk.toString(); });\n proc.stderr.on('data', (chunk) => { stderr += chunk.toString(); });\n proc.on('close', (code) => {\n const latencyMs = Date.now() - t0;\n if (code !== 0) {\n console.warn(` claude exited ${code}: ${(stderr || stdout).slice(0, 500)}`);\n resolve({ findings: [], latencyMs });\n return;\n }\n try {\n const wrapper = JSON.parse(stdout);\n const responseText = (wrapper.result || wrapper.response || wrapper.text || '').trim();\n let txt = responseText;\n if (txt.startsWith('```')) {\n txt = txt.replace(/^```(?:json)?\\n?/, '').replace(/\\n?```\\s*$/, '').trim();\n }\n const verdict = JSON.parse(txt);\n const findings = (verdict.findings || []).map((f: any) => ({\n file: file.filename,\n line: f.line,\n severity: f.severity,\n category: f.category,\n description: f.description,\n fix: f.fix,\n })) as Finding[];\n resolve({ findings, latencyMs });\n } catch (parseErr) {\n console.warn(` failed to parse claude response: ${stdout.slice(0, 300)}`);\n resolve({ findings: [], latencyMs });\n }\n });\n });\n}\n\nasync function processInBatches<T, R>(items: T[], concurrency: number, fn: (item: T, index: number, total: number) => Promise<R>): Promise<R[]> {\n const results: R[] = new Array(items.length);\n let next = 0;\n async function worker() {\n while (next < items.length) {\n const idx = next++;\n results[idx] = await fn(items[idx], idx, items.length);\n }\n }\n await Promise.all(Array.from({ length: Math.min(concurrency, items.length) }, () => worker()));\n return results;\n}\n\ninterface ReviewComment {\n path: string;\n line: number;\n side: 'RIGHT';\n body: string;\n}\n\ninterface ConsolidatedReview {\n summary: string;\n comments: ReviewComment[];\n severity: 'critical' | 'high' | 'medium' | 'low';\n}\n\nfunction buildConsolidationPrompt(findings: Finding[]): string {\n return `You are a senior security reviewer writing the final PR review. You received raw findings from an automated detector. Your job:\n\n1. VERIFY — remove false positives or findings that are clearly wrong.\n2. CONSOLIDATE — if multiple findings describe the same underlying issue in the same file (e.g. 8 hardcoded secrets), merge them into ONE comment pinned to the first line, listing all affected lines. If findings are genuinely different issues (e.g. SQL injection on line 5 AND a hardcoded secret on line 12), keep them separate.\n3. WRITE — produce concise, actionable review comments. No fluff. Each comment should name the issue and say what to do. One sentence max for description, one for fix.\n\nOutput ONLY a JSON object (no prose, no markdown fences):\n{\n \"summary\": \"<2-3 sentence overview for the review body — total issues, severity, what to do>\",\n \"comments\": [\n {\n \"path\": \"<file path>\",\n \"line\": <integer, first affected line number — REQUIRED, never null>,\n \"body\": \"<markdown comment — include affected lines if consolidated, e.g. 'Lines 1-8: ...'>\"\n }\n ]\n}\n\nRules:\n- Each comment body should start with a severity emoji+label: 🔴 critical, 🟠 high, 🟡 medium, 🔵 low\n- If consolidating, mention all affected line numbers in the body\n- Keep comments short — developers read these in a PR, not a report\n- Maximum 15 comments. If more, pick the most critical and mention the rest in the summary.\n- If all findings are legitimate and distinct, keep them all (up to 15)\n- If you determine ALL findings are false positives, return {\"summary\": \"No issues found after verification.\", \"comments\": []}\n\nRaw findings from detector:\n${JSON.stringify(findings, null, 2)}\n`;\n}\n\nfunction spawnOpusConsolidator(findings: Finding[], claudeToken: string): Promise<ConsolidatedReview> {\n return new Promise((resolve) => {\n const prompt = buildConsolidationPrompt(findings);\n const proc = spawn(\n 'claude',\n ['--print', '--model', 'claude-opus-4-7', '--output-format', 'json', '--no-session-persistence'],\n {\n stdio: ['pipe', 'pipe', 'pipe'],\n env: {\n ...process.env,\n CLAUDE_CODE_OAUTH_TOKEN: claudeToken,\n },\n timeout: 120_000,\n },\n );\n proc.stdin.write(prompt);\n proc.stdin.end();\n let stdout = '';\n let stderr = '';\n proc.stdout.on('data', (chunk) => { stdout += chunk.toString(); });\n proc.stderr.on('data', (chunk) => { stderr += chunk.toString(); });\n proc.on('close', (code) => {\n if (code !== 0) {\n console.warn(` opus consolidation exited ${code}: ${(stderr || stdout).slice(0, 300)}`);\n // Fallback: convert raw findings to comments directly\n resolve(fallbackReview(findings));\n return;\n }\n try {\n const wrapper = JSON.parse(stdout);\n const responseText = (wrapper.result || wrapper.response || wrapper.text || '').trim();\n let txt = responseText;\n if (txt.startsWith('```')) {\n txt = txt.replace(/^```(?:json)?\\n?/, '').replace(/\\n?```\\s*$/, '').trim();\n }\n const review = JSON.parse(txt);\n const comments: ReviewComment[] = (review.comments || []).map((c: any) => ({\n path: c.path,\n line: c.line ?? 1,\n side: 'RIGHT' as const,\n body: c.body,\n }));\n const maxSeverity = findings.reduce((max, f) => {\n const order = ['low', 'medium', 'high', 'critical'];\n return order.indexOf(f.severity) > order.indexOf(max) ? f.severity : max;\n }, 'low' as string) as ConsolidatedReview['severity'];\n resolve({ summary: review.summary || '', comments, severity: maxSeverity });\n } catch {\n console.warn(` failed to parse opus response, using fallback`);\n resolve(fallbackReview(findings));\n }\n });\n });\n}\n\nfunction fallbackReview(findings: Finding[]): ConsolidatedReview {\n const grouped = new Map<string, Finding[]>();\n for (const f of findings) {\n const key = `${f.file}::${f.category}`;\n if (!grouped.has(key)) grouped.set(key, []);\n grouped.get(key)!.push(f);\n }\n const comments: ReviewComment[] = [];\n for (const [, group] of grouped) {\n const first = group[0];\n const lines = group.map((f) => f.line);\n const linesStr = lines.length > 1 ? `Lines ${lines.join(', ')}` : `Line ${lines[0]}`;\n const severityEmoji = first.severity === 'critical' ? '🔴' : first.severity === 'high' ? '🟠' : first.severity === 'medium' ? '🟡' : '🔵';\n comments.push({\n path: first.file,\n line: first.line,\n side: 'RIGHT',\n body: `${severityEmoji} **${first.severity}: ${first.category}**\\n\\n${linesStr}: ${first.description}\\n\\n**Fix:** ${first.fix}`,\n });\n }\n const maxSeverity = findings.reduce((max, f) => {\n const order = ['low', 'medium', 'high', 'critical'];\n return order.indexOf(f.severity) > order.indexOf(max) ? f.severity : max;\n }, 'low' as string) as ConsolidatedReview['severity'];\n return {\n summary: `${findings.length} security finding(s) detected.`,\n comments: comments.slice(0, 15),\n severity: maxSeverity,\n };\n}\n\nfunction postPrReview(\n repo: string,\n prNumber: number,\n sha: string,\n review: ConsolidatedReview,\n skipLineReview = false,\n): void {\n function postIssueComment(): void {\n try {\n const body = `## 🔒 Synkro Security Review\\n\\n${review.summary}\\n\\n` +\n review.comments.map((c) => `**${c.path}:${c.line}** — ${c.body}`).join('\\n\\n');\n execSync(`gh api -X POST /repos/${repo}/issues/${prNumber}/comments --input -`, {\n encoding: 'utf-8',\n input: JSON.stringify({ body }),\n stdio: ['pipe', 'ignore', 'pipe'],\n });\n console.log(' ✓ Posted issue comment with findings.');\n } catch (err) {\n console.warn('Failed to post issue comment:', (err as Error).message);\n }\n }\n\n // Closed-non-merged PRs: line-level review will 422 (head ref deleted /\n // diff missing). Go straight to issue comment.\n if (skipLineReview) {\n postIssueComment();\n return;\n }\n\n const preferredEvent = review.severity === 'critical' || review.severity === 'high' ? 'REQUEST_CHANGES' : 'COMMENT';\n\n function tryPost(event: string): boolean {\n const body = JSON.stringify({\n commit_id: sha,\n body: `## 🔒 Synkro Security Review\\n\\n${review.summary}`,\n event,\n comments: review.comments,\n });\n try {\n execSync(`gh api -X POST /repos/${repo}/pulls/${prNumber}/reviews --input -`, {\n encoding: 'utf-8',\n input: body,\n stdio: ['pipe', 'pipe', 'pipe'],\n });\n console.log(` ✓ Posted PR review (${event}).`);\n return true;\n } catch (err: any) {\n const stderr = err.stderr?.toString() || '';\n const stdout = err.stdout?.toString() || '';\n const combined = stderr + stdout;\n if (combined.includes('own pull request') && event === 'REQUEST_CHANGES') {\n return false;\n }\n return false;\n }\n }\n\n if (tryPost(preferredEvent)) return;\n if (preferredEvent === 'REQUEST_CHANGES' && tryPost('COMMENT')) return;\n\n postIssueComment();\n}\n\nfunction postCheckRun(repo: string, sha: string, conclusion: 'success' | 'failure', findings: Finding[]): void {\n const summary = findings.length === 0\n ? 'No security findings.'\n : `${findings.length} finding(s):\\n` + findings.slice(0, 20).map((f) => `- **${f.severity}**: ${f.file}:${f.line} — ${f.description}`).join('\\n');\n const body = JSON.stringify({\n name: 'Synkro Security Review',\n head_sha: sha,\n status: 'completed',\n conclusion,\n output: {\n title: findings.length === 0 ? 'No issues found' : `${findings.length} security finding(s)`,\n summary,\n },\n });\n try {\n execSync(`gh api -X POST /repos/${repo}/check-runs --input -`, {\n encoding: 'utf-8',\n input: body,\n stdio: ['pipe', 'ignore', 'pipe'],\n });\n } catch (err) {\n console.warn('Failed to post check run:', (err as Error).message);\n }\n}\n\nfunction shouldFail(findings: Finding[], threshold: 'critical' | 'high' | 'medium' | 'low'): boolean {\n const order = ['low', 'medium', 'high', 'critical'];\n const thresholdIdx = order.indexOf(threshold);\n return findings.some((f) => order.indexOf(f.severity) >= thresholdIdx);\n}\n\nfunction readRepoDeps(): Record<string, string> {\n const pkgPath = join(process.cwd(), 'package.json');\n if (!existsSync(pkgPath)) return {};\n try {\n const pkg = JSON.parse(readFileSync(pkgPath, 'utf-8'));\n return { ...(pkg.dependencies ?? {}), ...(pkg.devDependencies ?? {}) };\n } catch {\n return {};\n }\n}\n\nfunction getFullFileContent(filename: string): string | null {\n try {\n return execSync(`git show HEAD:${filename}`, { encoding: 'utf-8', maxBuffer: 128 * 1024 });\n } catch {\n return null;\n }\n}\n\nasync function scanCves(\n files: PrFile[],\n gatewayUrl: string,\n apiKey: string,\n): Promise<Finding[]> {\n const deps = readRepoDeps();\n if (Object.keys(deps).length === 0) return [];\n\n const findings: Finding[] = [];\n\n for (const file of files) {\n const content = getFullFileContent(file.filename);\n if (!content) continue;\n\n try {\n const resp = await fetch(`${gatewayUrl.replace(/\\/$/, '')}/api/v1/cve-scan`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'x-synkro-api-key': apiKey,\n },\n body: JSON.stringify({\n file_path: file.filename,\n content,\n dependencies: deps,\n }),\n signal: AbortSignal.timeout(8_000),\n });\n\n if (!resp.ok) continue;\n const data = await resp.json() as {\n findings: Array<{ package: string; id: string; summary: string; severity: string; fixed?: string }>;\n summary?: string;\n packages?: Array<{ package: string; count: number; ids: string[] }>;\n };\n\n if (!data.findings?.length) continue;\n\n for (const pkg of data.packages ?? []) {\n const maxSev = data.findings\n .filter((f) => f.package === pkg.package)\n .reduce((max, f) => {\n const n = parseFloat(f.severity);\n return !isNaN(n) && n > max ? n : max;\n }, 0);\n const severity: Finding['severity'] = maxSev >= 9 ? 'critical' : maxSev >= 7 ? 'high' : maxSev >= 4 ? 'medium' : 'low';\n const topIds = pkg.ids.slice(0, 3).join(', ');\n const extra = pkg.ids.length > 3 ? ` +${pkg.ids.length - 3} more` : '';\n\n findings.push({\n file: file.filename,\n line: 1,\n severity,\n category: 'vulnerable_dependency',\n description: `${pkg.package} has ${pkg.count} known CVEs (${topIds}${extra}).`,\n fix: data.findings.find((f) => f.package === pkg.package && f.fixed)\n ? `Upgrade ${pkg.package} to ${data.findings.find((f) => f.package === pkg.package && f.fixed)!.fixed}`\n : `Check https://osv.dev/list?q=${pkg.package} for fix versions.`,\n });\n }\n } catch {\n continue;\n }\n }\n\n return findings;\n}\n\nasync function postEventToBackend(opts: {\n gatewayUrl: string;\n apiKey: string;\n repo: string;\n prNumber: number;\n sha: string;\n findings: Finding[];\n filesScanned: number;\n totalLatencyMs: number;\n}): Promise<void> {\n try {\n await fetch(`${opts.gatewayUrl.replace(/\\/$/, '')}/api/v1/events/pr-scan`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'x-synkro-api-key': opts.apiKey,\n },\n body: JSON.stringify({\n repo: opts.repo,\n pr_number: opts.prNumber,\n sha: opts.sha,\n findings: opts.findings,\n summary: opts.findings.length === 0 ? 'clean' : `${opts.findings.length} findings`,\n files_scanned: opts.filesScanned,\n total_latency_ms: opts.totalLatencyMs,\n }),\n });\n } catch (err) {\n console.warn('Failed to log scan to Synkro backend:', (err as Error).message);\n }\n}\n\nexport async function scanPrCommand(): Promise<void> {\n const repo = process.env.SYNKRO_REPO || process.env.GITHUB_REPOSITORY || '';\n const prNumberStr = process.env.SYNKRO_PR_NUMBER || '';\n const sha = process.env.SYNKRO_SHA || process.env.GITHUB_SHA || '';\n const claudeToken = process.env.CLAUDE_CODE_OAUTH_TOKEN || '';\n const synkroApiKey = process.env.SYNKRO_API_KEY || '';\n const gatewayUrl = process.env.SYNKRO_GATEWAY_URL || 'https://api.synkro.sh';\n const failThreshold = (process.env.SYNKRO_FAIL_THRESHOLD || 'high') as 'critical' | 'high' | 'medium' | 'low';\n\n if (!repo || !prNumberStr || !sha || !claudeToken || !synkroApiKey) {\n console.error('Missing required env vars: SYNKRO_REPO, SYNKRO_PR_NUMBER, SYNKRO_SHA, CLAUDE_CODE_OAUTH_TOKEN, SYNKRO_API_KEY');\n process.exit(2);\n }\n const prNumber = parseInt(prNumberStr, 10);\n if (!prNumber) {\n console.error('SYNKRO_PR_NUMBER is not a valid number:', prNumberStr);\n process.exit(2);\n }\n\n console.log(`Synkro scan-pr: ${repo}#${prNumber} @ ${sha.slice(0, 7)}\\n`);\n\n // If the PR is closed/merged, branch off and open a fresh PR for findings\n const prTarget = await ensureOpenPr(repo, prNumber, sha);\n const activePrNumber = prTarget.prNumber;\n const activeSha = prTarget.sha;\n\n // Fetch the org's active runtime rules so we enforce them in PR review\n // — same rules that fire on edit-time hooks. Without this, scan-pr would\n // ignore every rule the customer added via create_guardrail / dashboard.\n const orgRules = await fetchOrgRules(gatewayUrl, synkroApiKey);\n const auditRules = orgRules.filter((r) => r.mode === 'audit');\n const literalNegativeRules = orgRules.filter((r) => {\n const spec = parseMatchSpec(r.condition);\n return r.mode === 'literal_match' && spec?.negate === true;\n });\n console.log(`Loaded ${orgRules.length} org rule(s): ${auditRules.length} audit, ${literalNegativeRules.length} literal_match (negative).`);\n const promptHeader = buildPrPrompt(auditRules);\n\n // Fetch PR file list (always from the original PR — that's where the diff lives)\n let files: PrFile[];\n try {\n files = getPrFiles(repo, activePrNumber);\n } catch (err) {\n console.error('Failed to fetch PR files:', (err as Error).message);\n process.exit(2);\n }\n\n // Ask the server which files need scanning (incremental dedup)\n const scanCtx = await fetchScanContext(gatewayUrl, synkroApiKey, repo, activePrNumber, activeSha);\n if (scanCtx.skip) {\n console.log(`Already scanned at ${activeSha.slice(0, 7)}, skipping.\\n`);\n postCheckRun(repo, activeSha, 'success', []);\n await postEventToBackend({\n gatewayUrl, apiKey: synkroApiKey, repo, prNumber: activePrNumber, sha: activeSha,\n findings: [], filesScanned: 0, totalLatencyMs: 0,\n });\n return;\n }\n\n const changedFiles = !scanCtx.scan_all && scanCtx.files ? new Set(scanCtx.files) : null;\n if (changedFiles) {\n console.log(`Incremental scan: ${changedFiles.size} file(s) changed since last scan (${scanCtx.last_sha?.slice(0, 7)}).\\n`);\n }\n\n // Filter\n const eligible = files.filter((f) => {\n if (f.status === 'removed') return false;\n if (shouldSkipFile(f.filename)) return false;\n if ((f.additions + f.deletions) > MAX_DIFF_LINES_PER_FILE) return false;\n if (!f.patch) return false;\n if (changedFiles && !changedFiles.has(f.filename)) return false;\n return true;\n });\n\n console.log(`${files.length} files in PR, ${eligible.length} eligible for scan.\\n`);\n\n if (eligible.length === 0) {\n postCheckRun(repo, activeSha, 'success', []);\n await postEventToBackend({\n gatewayUrl, apiKey: synkroApiKey, repo, prNumber: activePrNumber, sha: activeSha,\n findings: [], filesScanned: 0, totalLatencyMs: 0,\n });\n console.log('No eligible files. Exiting.');\n return;\n }\n\n // Scan in parallel batches. Each file gets:\n // 1. Deterministic literal_match-negative checks on the patch's added\n // lines (no LLM, no cost, fires on rules like \"Never use `useEffect`\n // in tsx files\"). Positive literal_match rules need full file content\n // and are deferred to edit-time hooks for v1.\n // 2. LLM-based audit pass for everything else, with the org's audit rules\n // injected into the prompt so customer policies actually surface.\n const t0 = Date.now();\n const cvePromise = scanCves(eligible, gatewayUrl, synkroApiKey).catch((err) => {\n console.warn('CVE scan failed (non-fatal):', (err as Error).message);\n return [] as Finding[];\n });\n const results = await processInBatches(eligible, MAX_PARALLEL_FILES, async (file, idx, total) => {\n process.stdout.write(`[${idx + 1}/${total}] ${file.filename}...`);\n const literalFindings = applyLiteralMatchNegative(literalNegativeRules, file);\n const llmResult = await spawnClaudeJudge(file, claudeToken, promptHeader);\n const merged = [...literalFindings, ...llmResult.findings];\n console.log(` ${merged.length === 0 ? 'clean' : `${merged.length} finding(s)`} (${(llmResult.latencyMs / 1000).toFixed(1)}s)`);\n return { findings: merged, latencyMs: llmResult.latencyMs };\n });\n const cveFindings = await cvePromise;\n if (cveFindings.length > 0) {\n console.log(`CVE scan: ${cveFindings.length} vulnerable dependency finding(s).`);\n }\n const totalLatencyMs = Date.now() - t0;\n\n const allFindings: Finding[] = [...results.flatMap((r) => r.findings), ...cveFindings];\n console.log(`\\nTotal: ${allFindings.length} finding(s) across ${eligible.length} file(s) in ${totalLatencyMs}ms\\n`);\n\n // Consolidate findings with Opus and post as a single review\n if (allFindings.length > 0) {\n console.log('Consolidating findings with Opus 4.7...');\n const review = await spawnOpusConsolidator(allFindings, claudeToken);\n console.log(` → ${review.comments.length} review comment(s), severity: ${review.severity}`);\n if (review.comments.length > 0) {\n const skipLineReview = prTarget.prState === 'closed' && !prTarget.merged;\n postPrReview(repo, activePrNumber, activeSha, review, skipLineReview);\n }\n }\n\n // Post status check\n const conclusion = shouldFail(allFindings, failThreshold) ? 'failure' : 'success';\n postCheckRun(repo, activeSha, conclusion, allFindings);\n\n // Log to backend\n await postEventToBackend({\n gatewayUrl, apiKey: synkroApiKey, repo, prNumber: activePrNumber, sha: activeSha,\n findings: allFindings, filesScanned: eligible.length, totalLatencyMs,\n });\n\n console.log(`\\n✓ Scan complete. Status: ${conclusion}.`);\n\n // Exit non-zero if we want to fail the workflow on findings\n if (conclusion === 'failure') {\n process.exit(1);\n }\n}\n","// :)\n/**\n * synkro update — re-runs install to pull the latest hook scripts and\n * prompts. The CLI binary itself is distributed via npm; users run\n * `npm install -g @synkro-sh/cli@latest` to upgrade it.\n */\nimport { installCommand } from './install.js';\n\nexport async function updateCommand(): Promise<void> {\n console.log('Refreshing Synkro hook configs and prompts...\\n');\n await installCommand();\n console.log('\\n✓ Synkro updated.');\n console.log('To upgrade the CLI itself, run: npm install -g @synkro-sh/cli@latest');\n}\n","// :)\n/**\n * synkro disconnect — remove all Synkro hook entries from agent settings.\n *\n * Preserves any other hooks the user has (Corridor, Noma, custom).\n * Optionally also removes ~/.synkro/ entirely if --purge flag set.\n *\n * Order matters: tear down LIVE runtime first (pueue task + tmux session +\n * spawned bun plugin), THEN strip config, THEN remove files. Reversing the\n * order leaves orphaned processes holding the TCP port and a tmux session\n * with claude running from a deleted cwd.\n */\nimport { existsSync, rmSync } from 'node:fs';\nimport { homedir } from 'node:os';\nimport { join } from 'node:path';\nimport { detectAgents } from '../installer/agentDetect.js';\nimport { uninstallCCHooks } from '../installer/ccHookConfig.js';\nimport { uninstallCursorHooks } from '../installer/cursorHookConfig.js';\nimport { uninstallMcpConfig } from '../installer/mcpConfig.js';\nimport { stopTask, findTask, CHANNEL_SECONDARY } from '../local-cc/pueue.js';\nimport { uninstallLocalCC } from '../local-cc/install.js';\n\nconst SYNKRO_DIR = join(homedir(), '.synkro');\n\nfunction tearDownLocalCC(): void {\n // 1. Kill tmux sessions + pueue tasks for BOTH channels (best-effort).\n let hadTask = false;\n try {\n const t1 = findTask();\n const t2 = findTask(CHANNEL_SECONDARY);\n hadTask = !!(t1 || t2);\n stopTask();\n stopTask(CHANNEL_SECONDARY);\n } catch {\n // pueue may not be installed / pueued may not be running; ignore.\n }\n console.log(`${hadTask ? '✓' : '·'} local-cc runtime: ${hadTask ? 'stopped both channels' : 'no live tasks'}`);\n\n // 2. Strip user-scope MCP entry + project-state entry from ~/.claude.json.\n uninstallLocalCC();\n console.log('✓ local-cc config: cleaned ~/.claude.json entries');\n}\n\nexport function disconnectCommand(args: string[] = []): void {\n const purge = args.includes('--purge');\n\n console.log('Synkro disconnect starting...\\n');\n\n // Tear down local-cc runtime FIRST so we don't leave orphaned processes.\n tearDownLocalCC();\n\n const agents = detectAgents();\n let sawClaudeCode = false;\n for (const agent of agents) {\n if (agent.kind === 'claude_code') {\n sawClaudeCode = true;\n const removed = uninstallCCHooks(agent.settingsPath);\n console.log(`${removed ? '✓' : '·'} ${agent.name}: ${removed ? 'removed Synkro hook entries' : 'no Synkro hooks found'}`);\n } else if (agent.kind === 'cursor') {\n const removed = uninstallCursorHooks(agent.settingsPath);\n console.log(`${removed ? '✓' : '·'} ${agent.name}: ${removed ? 'removed Synkro hook entries' : 'no Synkro hooks found'}`);\n }\n }\n\n // Also remove the Synkro guardrails MCP server entry from ~/.claude.json\n // (this is the OTHER mcp server — the rule-suggestions one — distinct from\n // the local-cc channel server cleaned up above).\n if (sawClaudeCode) {\n const mcpRemoved = uninstallMcpConfig();\n console.log(`${mcpRemoved ? '✓' : '·'} MCP guardrails server: ${mcpRemoved ? 'removed entry from ~/.claude.json' : 'no Synkro MCP entry found'}`);\n }\n\n if (purge) {\n if (existsSync(SYNKRO_DIR)) {\n rmSync(SYNKRO_DIR, { recursive: true, force: true });\n console.log(`✓ Removed ${SYNKRO_DIR}`);\n } else {\n console.log(`· ${SYNKRO_DIR} already gone, nothing to remove`);\n }\n } else if (existsSync(SYNKRO_DIR)) {\n console.log(`Config preserved at ${SYNKRO_DIR}. Run with --purge to remove.`);\n }\n\n console.log('\\nSynkro disconnected.');\n}\n","// :)\n/**\n * synkro uninstall — fully remove Synkro from this machine.\n *\n * Removes all hooks from agent settings, the MCP server entry,\n * and the ~/.synkro directory (credentials, config, hook scripts, daemon).\n */\nimport { disconnectCommand } from './disconnect.js';\n\nexport function uninstallCommand(): void {\n console.log('Uninstalling Synkro...\\n');\n disconnectCommand(['--purge']);\n console.log('\\nTo reinstall later: synkro install');\n}\n","// :)\n/**\n * synkro reinstall — clean uninstall + fresh install.\n *\n * Removes all hooks, MCP config, and ~/.synkro, then runs the full\n * install flow (auth, hook scripts, MCP server, config).\n */\nimport { disconnectCommand } from './disconnect.js';\nimport { installCommand } from './install.js';\n\nexport async function reinstallCommand(): Promise<void> {\n console.log('Reinstalling Synkro...\\n');\n disconnectCommand(['--purge']);\n console.log('');\n await installCommand({ force: true });\n console.log('\\n✓ Synkro reinstalled.');\n}\n","/**\n * `synkro local-cc <subcommand>` — manage the local pueue-backed Claude Code\n * session that powers grading and intent classification when the inference\n * provider toggle is set to local-cc.\n */\n\nimport { spawnSync } from 'node:child_process';\nimport { homedir } from 'node:os';\nimport { join } from 'node:path';\nimport { installLocalCC, SESSION_DIR, PLUGIN_PATH, RUN_SCRIPT_PATH, PLUGIN_SETTINGS_PATH, CLAUDE_JSON_PATH, TMUX_SESSION_NAME, CHANNEL_2_PORT, TMUX_SESSION_NAME_2 } from '../local-cc/install.js';\nimport { readRecentTurns, followTurns, TURN_LOG_PATH, type TurnEntry } from '../local-cc/turnLog.js';\nimport {\n assertClaudeInstalled,\n assertPueueInstalled,\n assertTmuxInstalled,\n ensureRunning,\n findTask,\n startTask,\n stopTask,\n tailLogs,\n waitForChannelReady,\n CHANNEL_PRIMARY,\n CHANNEL_SECONDARY,\n} from '../local-cc/pueue.js';\nimport { isLocalCCEnabled } from '../local-cc/settings.js';\nimport { submitToChannel, isChannelAvailable, CHANNEL_HOST, CHANNEL_PORT } from '../local-cc/client.js';\nimport { getAccessToken, ensureValidToken } from '../auth/stub.js';\nimport { existsSync, readFileSync, writeFileSync } from 'node:fs';\n\nfunction printHelp(): void {\n console.log(`synkro local-cc — manage the local Claude Code inference session\n\nOVERVIEW\n Routes Synkro's grading and intent-classification work through a long-running\n Claude Code session on this machine instead of remote Inngest+Gemini.\n\n When enabled, three call sites switch over:\n • security grading on edit/bash hooks\n • intent classification (agent input → structured intent)\n • remediate intent classification\n\n The session is hosted in a detached tmux session managed by a pueue task.\n The CLI talks to it via Claude Code's channels API: a Bun MCP plugin that\n pushes events into the session and receives Claude's responses through a\n \\`reply\\` MCP tool.\n\nUSAGE\n synkro local-cc <subcommand> [args]\n\nSUBCOMMANDS\n enable Install plugin, start pueue task, flip toggle to local-cc\n disable Flip toggle back to inngest (pueue task left running)\n status Show provider toggle, pueue task state, channel reachability\n start Idempotently bring up the pueue task + wait for the channel\n stop Kill the tmux session and remove the pueue task\n restart stop, then start\n install Regenerate ~/.synkro/cc_sessions/ files (plugin, runner, settings)\n logs [N] [--raw] [--live]\n Show the last N (default 20) channel turns: when, role,\n duration, severity, request preview.\n --raw / -r include full request/response payloads\n --live / -f tail the log; print new turns as they arrive\n (Ctrl-C to exit)\n --tmux escape hatch — print the raw pueue/tmux\n pane log instead\n attach [--readonly] Attach to the tmux session hosting claude (Ctrl-B D to detach;\n --readonly / -r to attach view-only)\n test Send a smoke-test classification through the channel\n help Show this message\n\nCONFIGURATION\n Provider toggle:\n Stored server-side in your inference settings (gradingProvider).\n Toggle via: synkro local-cc enable / disable\n Or via dashboard inference settings (set grading provider to claude-code).\n\n Claude Code session settings (scoped to ~/.synkro/cc_sessions only):\n ${PLUGIN_SETTINGS_PATH}\n Currently sets {\"fastMode\": true}. Edit to add other CC settings for this\n session without affecting your other CC projects.\n\n MCP server registration (for the channel plugin):\n ${CLAUDE_JSON_PATH}\n A 'synkro-local' entry under mcpServers, pointing at:\n bun ${PLUGIN_PATH}\n Workspace trust is also pre-accepted under projects[\\`${SESSION_DIR}\\`].\n\n Channel runtime files:\n ${RUN_SCRIPT_PATH}\n bash wrapper that pueue invokes; owns the tmux session lifecycle.\n ${PLUGIN_PATH}\n Bun MCP channel plugin (auto-generated, do not edit).\n 127.0.0.1:${CHANNEL_PORT}\n Loopback TCP endpoint the CLI POSTs to in order to submit a request.\n\nENVIRONMENT VARIABLES\n SYNKRO_CHANNEL_PORT Override the TCP port used by both the plugin and\n the CLI client (loopback only). Default: 8929\n SYNKRO_CHANNEL_TIMEOUT_MS Per-request timeout inside the channel plugin\n (default: 120000)\n\nREQUIRED TOOLS\n claude Claude Code CLI, authenticated to your subscription\n pueue pueue + pueued (https://github.com/Nukesor/pueue) running\n tmux For detached pty around claude\n bun Runtime for the MCP channel plugin\n\nTROUBLESHOOTING\n • Channel unreachable after \\`enable\\`?\n synkro local-cc logs # check pueue task output\n tmux attach -t ${TMUX_SESSION_NAME} # see what claude is showing\n • Stale state after a crash:\n synkro local-cc stop && synkro local-cc start\n • Want to inspect or interact with the live session:\n synkro local-cc attach\n`);\n}\n\nconst CONFIG_PATH = join(homedir(), '.synkro', 'config.env');\n\nfunction readGatewayUrl(): string {\n if (existsSync(CONFIG_PATH)) {\n const m = readFileSync(CONFIG_PATH, 'utf-8').match(/^SYNKRO_GATEWAY_URL='([^']*)'/m);\n if (m) return m[1];\n }\n return 'https://api.synkro.sh';\n}\n\nfunction updateLocalInferenceFlag(enabled: boolean): void {\n if (!existsSync(CONFIG_PATH)) return;\n let content = readFileSync(CONFIG_PATH, 'utf-8');\n const flag = enabled ? 'yes' : 'no';\n if (content.includes('SYNKRO_LOCAL_INFERENCE=')) {\n content = content.replace(/^SYNKRO_LOCAL_INFERENCE='[^']*'/m, `SYNKRO_LOCAL_INFERENCE='${flag}'`);\n } else {\n content = content.trimEnd() + `\\nSYNKRO_LOCAL_INFERENCE='${flag}'\\n`;\n }\n writeFileSync(CONFIG_PATH, content, 'utf-8');\n}\n\nasync function setServerGradingProvider(provider: 'claude-code' | null): Promise<void> {\n await ensureValidToken();\n const jwt = getAccessToken();\n if (!jwt) throw new Error('Not authenticated. Run `synkro install` first.');\n const gatewayUrl = readGatewayUrl();\n const body = provider\n ? { roles: { grading: { provider, model: 'default' } } }\n : { roles: { grading: { provider: null, model: null } } };\n const resp = await fetch(`${gatewayUrl}/api/settings/inference?scope=user`, {\n method: 'PUT',\n headers: { 'Authorization': `Bearer ${jwt}`, 'Content-Type': 'application/json' },\n body: JSON.stringify(body),\n });\n if (!resp.ok) {\n const text = await resp.text().catch(() => '');\n throw new Error(`Failed to update inference settings: ${resp.status} ${text.slice(0, 200)}`);\n }\n}\n\nasync function cmdStatus(): Promise<void> {\n console.log(`Local inference: ${isLocalCCEnabled() ? 'enabled' : 'disabled'}`);\n try {\n assertPueueInstalled();\n } catch (err) {\n console.log(`Pueue: NOT AVAILABLE (${(err as Error).message})`);\n return;\n }\n\n // Channel 1 (judge: bash + edit, port 8929)\n const t = findTask(CHANNEL_PRIMARY);\n if (!t) {\n console.log('Channel 1 (judge) pueue task: not present');\n } else {\n console.log(`Channel 1 (judge) pueue task: id=${t.id} status=${t.status}`);\n }\n const ch1Up = await isChannelAvailable();\n console.log(`Channel 1 ${CHANNEL_HOST}:${CHANNEL_PORT}: ${ch1Up ? 'reachable' : 'unreachable'}`);\n const tmux1 = spawnSync('tmux', ['has-session', '-t', `=${TMUX_SESSION_NAME}`], { encoding: 'utf-8' });\n console.log(`tmux '${TMUX_SESSION_NAME}': ${tmux1.status === 0 ? 'live' : 'absent'}`);\n\n // Channel 2 (CWE, port 8930)\n const t2 = findTask(CHANNEL_SECONDARY);\n if (!t2) {\n console.log('Channel 2 (CWE) pueue task: not present');\n } else {\n console.log(`Channel 2 (CWE) pueue task: id=${t2.id} status=${t2.status}`);\n }\n const ch2Up = await isChannelAvailable(CHANNEL_2_PORT);\n console.log(`Channel 2 ${CHANNEL_HOST}:${CHANNEL_2_PORT}: ${ch2Up ? 'reachable' : 'unreachable'}`);\n const tmux2 = spawnSync('tmux', ['has-session', '-t', `=${TMUX_SESSION_NAME_2}`], { encoding: 'utf-8' });\n console.log(`tmux '${TMUX_SESSION_NAME_2}': ${tmux2.status === 0 ? 'live' : 'absent'}`);\n}\n\nasync function cmdEnable(): Promise<void> {\n assertClaudeInstalled();\n assertPueueInstalled();\n assertTmuxInstalled();\n console.log('Installing local-CC channel plugin...');\n const r = installLocalCC();\n console.log(` plugin: ${r.pluginPath}`);\n console.log(` cwd: ${r.sessionDir}`);\n console.log('Starting channel 1 (judge)...');\n const t1 = ensureRunning({ channel: CHANNEL_PRIMARY });\n console.log(` task: id=${t1.id} status=${t1.status}`);\n console.log('Starting channel 2 (CWE)...');\n const t2 = ensureRunning({ channel: CHANNEL_SECONDARY });\n console.log(` task: id=${t2.id} status=${t2.status}`);\n console.log('Waiting for channels (auto-confirming any CC prompts)...');\n const [ready1, ready2] = await Promise.all([\n waitForChannelReady(CHANNEL_PORT, 60_000, CHANNEL_HOST, CHANNEL_PRIMARY.tmuxSession),\n waitForChannelReady(CHANNEL_2_PORT, 60_000, CHANNEL_HOST, CHANNEL_SECONDARY.tmuxSession),\n ]);\n if (ready1) console.log(` channel 1 ready at ${CHANNEL_HOST}:${CHANNEL_PORT}`);\n else console.warn(` ⚠ channel 1 did not come up within 60s — check \\`synkro local-cc logs\\``);\n if (ready2) console.log(` channel 2 ready at ${CHANNEL_HOST}:${CHANNEL_2_PORT}`);\n else console.warn(` ⚠ channel 2 (CWE) did not come up within 60s`);\n console.log('Updating inference settings...');\n await setServerGradingProvider('claude-code');\n updateLocalInferenceFlag(true);\n console.log('Grading provider set to claude-code (local inference enabled).');\n}\n\nasync function cmdDisable(): Promise<void> {\n console.log('Updating inference settings...');\n await setServerGradingProvider(null);\n updateLocalInferenceFlag(false);\n console.log('Grading provider cleared (remote inference restored). Pueue task left running — use `synkro local-cc stop` to terminate.');\n}\n\nasync function warmChannels(ready1: boolean, ready2: boolean): Promise<void> {\n const warmups: Promise<void>[] = [];\n if (ready1) {\n warmups.push(\n submitToChannel('grade-bash', 'Proposed command: echo hello\\nUser intent: warmup\\nRecent user messages: []\\nRecent actions: []\\nOrg rules: []\\n', { timeoutMs: 30_000 })\n .then(() => console.log(' channel 1 warm.'))\n .catch(() => console.log(' channel 1 warmup skipped (non-fatal).'))\n );\n }\n if (ready2) {\n warmups.push(\n submitToChannel('grade-cwe', 'File: /tmp/warmup.ts\\nContent (first 4000 chars):\\nconsole.log(\"hello\");\\n\\nCWE rules to check against:\\n[]\\n', { timeoutMs: 30_000, port: CHANNEL_2_PORT })\n .then(() => console.log(' channel 2 warm.'))\n .catch(() => console.log(' channel 2 warmup skipped (non-fatal).'))\n );\n }\n if (warmups.length) {\n console.log('Warming up inference...');\n await Promise.all(warmups);\n }\n}\n\nasync function cmdStart(): Promise<void> {\n assertClaudeInstalled();\n assertPueueInstalled();\n assertTmuxInstalled();\n const t1 = ensureRunning({ channel: CHANNEL_PRIMARY });\n console.log(`Channel 1 (judge): id=${t1.id} status=${t1.status}`);\n const t2 = ensureRunning({ channel: CHANNEL_SECONDARY });\n console.log(`Channel 2 (CWE): id=${t2.id} status=${t2.status}`);\n const [ready1, ready2] = await Promise.all([\n waitForChannelReady(CHANNEL_PORT, 60_000, CHANNEL_HOST, CHANNEL_PRIMARY.tmuxSession),\n waitForChannelReady(CHANNEL_2_PORT, 60_000, CHANNEL_HOST, CHANNEL_SECONDARY.tmuxSession),\n ]);\n console.log(ready1 ? `channel 1 ready (${CHANNEL_PORT}).` : '⚠ channel 1 did not come up within 60s.');\n console.log(ready2 ? `channel 2 ready (${CHANNEL_2_PORT}).` : '⚠ channel 2 (CWE) did not come up within 60s.');\n await warmChannels(ready1, ready2);\n}\n\nfunction cmdStop(): void {\n stopTask(CHANNEL_PRIMARY);\n stopTask(CHANNEL_SECONDARY);\n console.log('Both channels stopped.');\n}\n\nasync function cmdRestart(): Promise<void> {\n stopTask(CHANNEL_PRIMARY);\n stopTask(CHANNEL_SECONDARY);\n const t1 = startTask({ channel: CHANNEL_PRIMARY });\n const t2 = startTask({ channel: CHANNEL_SECONDARY });\n console.log(`Channel 1 restarted: id=${t1.id} status=${t1.status}`);\n console.log(`Channel 2 restarted: id=${t2.id} status=${t2.status}`);\n const [ready1, ready2] = await Promise.all([\n waitForChannelReady(CHANNEL_PORT, 60_000, CHANNEL_HOST, CHANNEL_PRIMARY.tmuxSession),\n waitForChannelReady(CHANNEL_2_PORT, 60_000, CHANNEL_HOST, CHANNEL_SECONDARY.tmuxSession),\n ]);\n console.log(ready1 ? `channel 1 ready (${CHANNEL_PORT}).` : '⚠ channel 1 did not come up within 60s.');\n console.log(ready2 ? `channel 2 ready (${CHANNEL_2_PORT}).` : '⚠ channel 2 (CWE) did not come up within 60s.');\n await warmChannels(ready1, ready2);\n}\n\nfunction relativeTime(iso: string): string {\n const ts = new Date(iso).getTime();\n if (!Number.isFinite(ts)) return iso;\n const sec = Math.max(0, Math.round((Date.now() - ts) / 1000));\n if (sec < 60) return `${sec}s ago`;\n if (sec < 3600) return `${Math.round(sec / 60)}m ago`;\n if (sec < 86400) return `${Math.round(sec / 3600)}h ago`;\n return `${Math.round(sec / 86400)}d ago`;\n}\n\nfunction colorize(s: string, code: number): string {\n if (!process.stdout.isTTY) return s;\n return `\u001b[${code}m${s}\u001b[0m`;\n}\n\nfunction statusGlyph(t: TurnEntry): string {\n if (t.status === 'ok') return colorize('✓', 32); // green check\n if (t.status === 'timeout') return colorize('⏱', 33); // yellow clock\n return colorize('✗', 31); // red X\n}\n\nfunction severityCell(t: TurnEntry): string {\n if (t.severity) {\n const sev = t.severity;\n if (sev === 'block' || sev === 'violations' || sev === 'unclear') return colorize(sev, 33);\n return colorize(sev, 36); // cyan for everything else\n }\n if (t.error) return colorize(t.error.slice(0, 40), 31);\n return '—';\n}\n\nfunction firstLine(s: string): string {\n return s.split('\\n').find(l => l.trim().length > 0)?.trim() ?? '';\n}\n\nfunction formatTurn(t: TurnEntry, raw: boolean): string {\n const when = relativeTime(t.ts).padEnd(8);\n const dur = (t.duration_ms < 1000\n ? `${t.duration_ms}ms`\n : `${(t.duration_ms / 1000).toFixed(1)}s`).padStart(7);\n const role = t.role.padEnd(24);\n const sev = severityCell(t).padEnd(20);\n const preview = (() => {\n const req = firstLine(t.request_preview).slice(0, 60);\n return colorize(req, 90);\n })();\n const head = `${statusGlyph(t)} ${when} ${dur} ${role} ${sev} ${preview}`;\n if (!raw) return head;\n // raw mode: include full request + response previews on subsequent lines\n const blocks: string[] = [head];\n blocks.push(colorize(' request:', 90));\n blocks.push(' ' + t.request_preview.replace(/\\n/g, '\\n '));\n if (t.response_preview) {\n blocks.push(colorize(' response:', 90));\n blocks.push(' ' + t.response_preview.replace(/\\n/g, '\\n '));\n }\n if (t.error) {\n blocks.push(colorize(' error:', 31) + ' ' + t.error);\n }\n return blocks.join('\\n');\n}\n\nfunction cmdLogs(rest: string[]): void | Promise<void> {\n let n = 20;\n let raw = false;\n let live = false;\n for (const arg of rest) {\n if (arg === '--raw' || arg === '-r') raw = true;\n else if (arg === '--live' || arg === '-f') live = true;\n else if (arg === '--tmux') {\n // Escape hatch: show the original raw pueue/tmux output for the task.\n console.log(tailLogs(80));\n return;\n } else {\n const parsed = parseInt(arg, 10);\n if (parsed > 0) n = parsed;\n }\n }\n\n const header = ' ' + colorize('status when dur role severity request', 90);\n\n // Print backlog (newest first) — same as non-live mode.\n const turns = readRecentTurns(n);\n if (turns.length === 0) {\n if (!live) {\n console.log(`No turns logged yet at ${TURN_LOG_PATH}.`);\n console.log('Run a few requests through the channel (synkro local-cc test) and try again.');\n return;\n }\n console.log(`No turns logged yet at ${TURN_LOG_PATH} — waiting for new entries… (Ctrl-C to exit)`);\n } else {\n console.log(`Last ${turns.length} channel turn(s) (newest first):`);\n console.log(header);\n for (const t of turns) console.log(' ' + formatTurn(t, raw));\n }\n\n if (!live) {\n if (!raw) console.log(' ' + colorize('(use --raw / -r to see full payloads, --live / -f to follow)', 90));\n return;\n }\n\n // Live tail. Follow the JSONL log; print each new entry as it arrives.\n // Returning a Promise keeps the process alive until SIGINT.\n return new Promise<void>(resolve => {\n console.log(' ' + colorize('— following new turns (Ctrl-C to exit) —', 90));\n const stop = followTurns(t => {\n console.log(' ' + formatTurn(t, raw));\n });\n const onSigint = () => {\n stop();\n process.removeListener('SIGINT', onSigint);\n resolve();\n };\n process.on('SIGINT', onSigint);\n });\n}\n\nfunction cmdAttach(rest: string[]): void {\n assertTmuxInstalled();\n const readonly = rest.some(a => a === '--readonly' || a === '-r');\n\n // Verify the session exists before tmux drops the user into a confusing state.\n const has = spawnSync('tmux', ['has-session', '-t', `=${TMUX_SESSION_NAME}`], { encoding: 'utf-8' });\n if (has.status !== 0) {\n console.error(`No tmux session '${TMUX_SESSION_NAME}' running. Start it with: synkro local-cc start`);\n process.exit(1);\n }\n\n if (!process.stdout.isTTY) {\n console.error('attach requires a TTY. Run this command directly in your terminal, not piped.');\n process.exit(1);\n }\n\n console.log(`Attaching to tmux session '${TMUX_SESSION_NAME}'${readonly ? ' (read-only)' : ''}.`);\n console.log('Detach with Ctrl-B then D. (Do not press Ctrl-C — that would interrupt claude.)');\n console.log();\n\n const args = readonly\n ? ['attach-session', '-r', '-t', TMUX_SESSION_NAME]\n : ['attach-session', '-t', TMUX_SESSION_NAME];\n const r = spawnSync('tmux', args, { stdio: 'inherit' });\n process.exit(r.status ?? 0);\n}\n\nasync function cmdTest(): Promise<void> {\n console.log('Sending smoke-test grading request through the channel...');\n const result = await submitToChannel(\n 'grade-bash',\n 'Command: echo \"hello world\"\\nIntent: testing channel connectivity',\n );\n console.log('Raw reply:');\n console.log(result);\n}\n\nfunction cmdInstall(): void {\n const r = installLocalCC();\n console.log(`Reinstalled plugin at ${r.pluginPath}`);\n}\n\nexport async function localCcCommand(args: string[]): Promise<void> {\n const sub = args[0] ?? '';\n try {\n switch (sub) {\n case 'enable': await cmdEnable(); break;\n case 'disable': cmdDisable(); break;\n case 'status': await cmdStatus(); break;\n case 'start': await cmdStart(); break;\n case 'stop': cmdStop(); break;\n case 'restart': await cmdRestart(); break;\n case 'install': cmdInstall(); break;\n case 'logs': await cmdLogs(args.slice(1)); break;\n case 'attach': cmdAttach(args.slice(1)); break;\n case 'test': await cmdTest(); break;\n case '':\n case 'help':\n case '--help':\n case '-h':\n printHelp(); break;\n default:\n console.error(`Unknown subcommand: ${sub}`);\n printHelp();\n process.exit(1);\n }\n } catch (err) {\n console.error((err as Error).message);\n process.exit(1);\n }\n}\n","/**\n * `synkro grade <edit|bash>` — replacement for the legacy\n * ~/.synkro/bin/grader_daemon.py. Reads a grading payload from stdin,\n * routes it through the local-CC channel, and writes the raw verdict\n * (including the <synkro-verdict>...</synkro-verdict> wrapper that the\n * shell hook scripts grep for) to stdout.\n *\n * Failures print to stderr and exit non-zero so the hook scripts fall\n * through to the cloud fast-tier path cleanly.\n */\n\nimport { submitToChannel, LocalCCError } from '../local-cc/client.js';\nimport type { LocalCCRole } from '../local-cc/prompts.js';\n\nasync function readStdin(): Promise<string> {\n return new Promise((resolve, reject) => {\n const chunks: Buffer[] = [];\n process.stdin.on('data', c => chunks.push(c));\n process.stdin.on('end', () => resolve(Buffer.concat(chunks).toString('utf-8')));\n process.stdin.on('error', reject);\n });\n}\n\nexport async function gradeCommand(args: string[]): Promise<void> {\n const mode = args[0] ?? '';\n let role: LocalCCRole;\n if (mode === 'edit') role = 'grade-edit';\n else if (mode === 'bash') role = 'grade-bash';\n else if (mode === 'plan') role = 'grade-plan';\n else if (mode === 'cwe') role = 'grade-cwe';\n else {\n console.error('Usage: synkro grade <edit|bash|plan|cwe>');\n process.exit(2);\n }\n\n const payload = await readStdin();\n if (!payload.trim()) {\n console.error('synkro grade: empty stdin');\n process.exit(2);\n }\n\n try {\n const result = await submitToChannel(role, payload, { timeoutMs: 60_000 });\n process.stdout.write(result);\n if (!result.endsWith('\\n')) process.stdout.write('\\n');\n } catch (err) {\n if (err instanceof LocalCCError) {\n console.error(`synkro grade: ${err.message}`);\n } else {\n console.error(`synkro grade: ${(err as Error).message}`);\n }\n process.exit(3);\n }\n}\n","#!/usr/bin/env node\n/**\n * Synkro CLI bootstrap.\n *\n * Loads .env, then dispatches to the right subcommand.\n */\nimport { readFileSync, existsSync } from 'node:fs';\nimport { resolve } from 'node:path';\n\n// Load env vars synchronously from a few candidate paths\nconst envCandidates = [\n resolve(process.cwd(), '.env'),\n resolve(process.env.HOME ?? '', '.synkro', 'config.env'),\n];\nfor (const envPath of envCandidates) {\n if (!existsSync(envPath)) continue;\n const envContent = readFileSync(envPath, 'utf-8');\n for (const line of envContent.split('\\n')) {\n const trimmed = line.trim();\n if (!trimmed || trimmed.startsWith('#')) continue;\n const eqIndex = trimmed.indexOf('=');\n if (eqIndex <= 0) continue;\n const key = trimmed.slice(0, eqIndex).trim();\n const value = trimmed.slice(eqIndex + 1).trim().replace(/^['\"]|['\"]$/g, '');\n if (!process.env[key] && !value.startsWith('op://')) process.env[key] = value;\n }\n}\n\nconst args = process.argv.slice(2);\nconst cmd = args[0] || '';\nconst subArgs = args.slice(1);\n\nfunction printHelp() {\n console.log(`Synkro CLI — runtime safety for AI coding agents\n\nUsage:\n synkro-cli <command> [options] (recommended — avoids name collisions)\n synkro <command> [options] (alias)\n\nCommands:\n install [--force] Install Synkro hooks for detected agents (Claude Code, etc.)\n login Authenticate with Synkro (browser sign-in)\n logout Clear local credentials\n status Show current setup state\n link Link repos to a Synkro project (local git or GitHub OAuth)\n unlink Remove repo links from Synkro projects\n config View or change settings (e.g. --inference fast|standard)\n setup-github Configure GitHub PR scanning (push secrets + workflow file)\n update Refresh hook configs and judge prompts\n disconnect [--purge] Remove Synkro hooks from agents (--purge also removes ~/.synkro)\n uninstall Fully remove Synkro from this machine\n reinstall Clean uninstall + fresh install\n local-cc <sub> Manage the local Claude Code inference session (enable/disable/status/start/stop/logs/test)\n grade <edit|bash> Internal: read prompt from stdin, return verdict (called by hook scripts)\n help Show this message\n\nQuick start:\n $ synkro-cli install # one-time setup\n $ synkro-cli setup-github # enable PR scanning (optional)\n $ claude # use Claude Code normally; Synkro judges in real time\n (\\`synkro\\` also works as an alias unless something else on your $PATH shadows it)\n`);\n}\n\nasync function main() {\n switch (cmd) {\n case 'install': {\n const { installCommand, parseArgs } = await import('./commands/install.js');\n await installCommand(parseArgs(subArgs));\n break;\n }\n case 'login':\n case 'auth': {\n const { loginCommand } = await import('./commands/login.js');\n await loginCommand(subArgs);\n break;\n }\n case 'logout': {\n const { logoutCommand } = await import('./commands/logout.js');\n logoutCommand();\n break;\n }\n case 'status': {\n const { statusCommand } = await import('./commands/status.js');\n await statusCommand();\n break;\n }\n case 'link': {\n const { linkCommand } = await import('./commands/link.js');\n await linkCommand();\n break;\n }\n case 'unlink': {\n const { unlinkCommand } = await import('./commands/unlink.js');\n await unlinkCommand();\n break;\n }\n case 'config': {\n const { configCommand } = await import('./commands/config.js');\n await configCommand(subArgs);\n break;\n }\n case 'setup-github': {\n const { setupGithubCommand } = await import('./commands/setupGithub.js');\n const ghOpts = {};\n for (const a of subArgs) {\n if (a === '--non-interactive') ghOpts.nonInteractive = true;\n else if (a === '--skip-claude-token') ghOpts.skipClaudeToken = true;\n else if (a.startsWith('--github-token=')) ghOpts.githubToken = a.slice('--github-token='.length);\n }\n await setupGithubCommand(ghOpts);\n break;\n }\n case 'scan-pr': {\n const { scanPrCommand } = await import('./commands/scanPr.js');\n await scanPrCommand();\n break;\n }\n case 'update': {\n const { updateCommand } = await import('./commands/update.js');\n await updateCommand();\n break;\n }\n case 'disconnect': {\n const { disconnectCommand } = await import('./commands/disconnect.js');\n disconnectCommand(subArgs);\n break;\n }\n case 'uninstall': {\n const { uninstallCommand } = await import('./commands/uninstall.js');\n uninstallCommand();\n break;\n }\n case 'reinstall': {\n const { reinstallCommand } = await import('./commands/reinstall.js');\n await reinstallCommand();\n break;\n }\n case 'local-cc': {\n const { localCcCommand } = await import('./commands/localCc.js');\n await localCcCommand(subArgs);\n break;\n }\n case 'grade': {\n const { gradeCommand } = await import('./commands/grade.js');\n await gradeCommand(subArgs);\n break;\n }\n case 'help':\n case '--help':\n case '-h':\n case '': {\n printHelp();\n break;\n }\n default: {\n console.error(`Unknown command: ${cmd}`);\n printHelp();\n process.exit(1);\n }\n }\n}\n\nmain().catch((err) => {\n console.error(err);\n process.exit(1);\n});\n"],"mappings":";;;;;;;;;;;;;;;;;;AAMA,SAAS,kBAAkB;AAC3B,SAAS,eAAe;AACxB,SAAS,YAAY;AACrB,SAAS,gBAAgB;AAazB,SAAS,MAAMA,MAAiC;AAC9C,MAAI;AACF,UAAM,SAAS,SAAS,SAASA,IAAG,IAAI,EAAE,UAAU,QAAQ,CAAC,EAAE,KAAK;AACpE,WAAO,UAAU;AAAA,EACnB,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEA,SAAS,WAAWA,MAAiC;AACnD,MAAI;AACF,UAAM,SAAS,SAAS,GAAGA,IAAG,mBAAmB,EAAE,UAAU,SAAS,SAAS,IAAK,CAAC,EAAE,KAAK;AAC5F,WAAO,OAAO,MAAM,IAAI,EAAE,CAAC;AAAA,EAC7B,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEO,SAAS,eAAgC;AAC9C,QAAM,SAA0B,CAAC;AACjC,QAAM,OAAO,QAAQ;AAGrB,QAAM,eAAe,MAAM,QAAQ;AACnC,QAAM,kBAAkB,KAAK,MAAM,SAAS;AAC5C,MAAI,gBAAgB,WAAW,eAAe,GAAG;AAC/C,WAAO,KAAK;AAAA,MACV,MAAM;AAAA,MACN,MAAM;AAAA,MACN,YAAY;AAAA,MACZ,WAAW;AAAA,MACX,cAAc,KAAK,iBAAiB,eAAe;AAAA,MACnD,SAAS,eAAe,WAAW,QAAQ,IAAI;AAAA,IACjD,CAAC;AAAA,EACH;AAGA,QAAM,cAAc,MAAM,OAAO;AACjC,QAAM,iBAAiB,KAAK,MAAM,QAAQ;AAC1C,MAAI,eAAe,WAAW,cAAc,GAAG;AAC7C,WAAO,KAAK;AAAA,MACV,MAAM;AAAA,MACN,MAAM;AAAA,MACN,YAAY;AAAA,MACZ,WAAW;AAAA,MACX,cAAc,KAAK,gBAAgB,aAAa;AAAA,MAChD,SAAS,cAAc,WAAW,OAAO,IAAI;AAAA,IAC/C,CAAC;AAAA,EACH;AAGA,QAAM,eAAe,MAAM,QAAQ;AACnC,QAAM,kBAAkB,KAAK,MAAM,SAAS;AAC5C,MAAI,gBAAgB,WAAW,eAAe,GAAG;AAC/C,WAAO,KAAK;AAAA,MACV,MAAM;AAAA,MACN,MAAM;AAAA,MACN,YAAY;AAAA,MACZ,WAAW;AAAA,MACX,cAAc,KAAK,iBAAiB,YAAY;AAAA,MAChD,SAAS,eAAe,WAAW,QAAQ,IAAI;AAAA,IACjD,CAAC;AAAA,EACH;AAEA,SAAO;AACT;AAvFA;AAAA;AAAA;AAAA;AAAA;;;ACOA,SAAS,cAAAC,aAAY,cAAc,eAAe,YAAY,iBAA6B;AAC3F,SAAS,eAAe;AAqCxB,SAAS,aAAa,MAA0B;AAC9C,MAAI,CAACA,YAAW,IAAI,EAAG,QAAO,CAAC;AAC/B,MAAI;AACF,UAAM,MAAM,aAAa,MAAM,OAAO;AACtC,WAAO,KAAK,MAAM,GAAG;AAAA,EACvB,SAAS,KAAK;AACZ,UAAM,IAAI,MAAM,mBAAmB,IAAI,KAAM,IAAc,OAAO,EAAE;AAAA,EACtE;AACF;AAEA,SAAS,oBAAoB,MAAc,UAA4B;AACrE,YAAU,QAAQ,IAAI,GAAG,EAAE,WAAW,KAAK,CAAC;AAC5C,QAAM,UAAU,GAAG,IAAI;AACvB,gBAAc,SAAS,KAAK,UAAU,UAAU,MAAM,CAAC,IAAI,MAAM,OAAO;AACxE,aAAW,SAAS,IAAI;AAC1B;AAEA,SAAS,cAAc,OAAqB;AAC1C,MAAI,QAAQ,aAAa,EAAG,QAAO;AACnC,QAAM,QAAQ,MAAM,QAAQ,OAAO,KAAK,IAAI,MAAM,QAAQ,CAAC;AAC3D,SAAO,MAAM;AAAA,IAAK,CAAC,MACjB,OAAO,GAAG,YAAY,YAAY,EAAE,QAAQ,SAAS,iBAAiB;AAAA,EACxE;AACF;AAEA,SAAS,oBAAoB,QAAyD,WAAyB;AAC7G,MAAI,CAAC,OAAQ;AACb,QAAM,MAAO,OAAe,SAAS;AACrC,MAAI,CAAC,MAAM,QAAQ,GAAG,EAAG;AACzB,EAAC,OAAe,SAAS,IAAI,IAAI,OAAO,CAAC,UAAe,CAAC,cAAc,KAAK,CAAC;AAC/E;AAMO,SAAS,eAAe,cAAsB,QAAgC;AACnF,QAAM,WAAW,aAAa,YAAY;AAC1C,WAAS,QAAQ,SAAS,SAAS,CAAC;AAGpC,sBAAoB,SAAS,OAAc,YAAY;AACvD,sBAAoB,SAAS,OAAc,aAAa;AACxD,sBAAoB,SAAS,OAAc,YAAY;AACvD,sBAAoB,SAAS,OAAc,cAAc;AACzD,sBAAoB,SAAS,OAAc,kBAAkB;AAE7D,sBAAoB,SAAS,OAAc,MAAM;AAEjD,WAAS,MAAM,aAAa,SAAS,MAAM,cAAc,CAAC;AAC1D,WAAS,MAAM,cAAc,SAAS,MAAM,eAAe,CAAC;AAC5D,WAAS,MAAM,aAAa,SAAS,MAAM,cAAc,CAAC;AAC1D,WAAS,MAAM,eAAe,SAAS,MAAM,gBAAgB,CAAC;AAC9D,WAAS,MAAM,mBAAoB,SAAS,MAAM,oBAA8B,CAAC;AAGjF,WAAS,MAAM,WAAW,KAAK;AAAA,IAC7B,SAAS;AAAA,IACT,OAAO;AAAA,MACL;AAAA,QACE,MAAM;AAAA,QACN,SAAS,OAAO;AAAA,QAChB,SAAS;AAAA,MACX;AAAA,IACF;AAAA,IACA,CAAC,aAAa,GAAG;AAAA,EACnB,CAAQ;AAOR,WAAS,MAAM,WAAW,KAAK;AAAA,IAC7B,SAAS;AAAA,IACT,OAAO;AAAA,MACL;AAAA,QACE,MAAM;AAAA,QACN,SAAS,OAAO;AAAA,QAChB,SAAS;AAAA,MACX;AAAA,MACA;AAAA,QACE,MAAM;AAAA,QACN,SAAS,OAAO;AAAA,QAChB,SAAS;AAAA,MACX;AAAA,MACA;AAAA,QACE,MAAM;AAAA,QACN,SAAS,OAAO;AAAA,QAChB,SAAS;AAAA,MACX;AAAA,IACF;AAAA,IACA,CAAC,aAAa,GAAG;AAAA,EACnB,CAAQ;AAGR,WAAS,MAAM,WAAW,KAAK;AAAA,IAC7B,SAAS;AAAA,IACT,OAAO;AAAA,MACL;AAAA,QACE,MAAM;AAAA,QACN,SAAS,OAAO;AAAA,QAChB,SAAS;AAAA,MACX;AAAA,IACF;AAAA,IACA,CAAC,aAAa,GAAG;AAAA,EACnB,CAAQ;AAGR,WAAS,MAAM,WAAW,KAAK;AAAA,IAC7B,SAAS;AAAA,IACT,OAAO;AAAA,MACL;AAAA,QACE,MAAM;AAAA,QACN,SAAS,OAAO;AAAA,QAChB,SAAS;AAAA,MACX;AAAA,IACF;AAAA,IACA,CAAC,aAAa,GAAG;AAAA,EACnB,CAAQ;AAOR,WAAS,MAAM,YAAY,KAAK;AAAA,IAC9B,SAAS;AAAA,IACT,OAAO;AAAA,MACL;AAAA,QACE,MAAM;AAAA,QACN,SAAS,OAAO;AAAA,MAClB;AAAA,IACF;AAAA,IACA,CAAC,aAAa,GAAG;AAAA,EACnB,CAAQ;AAMR,WAAS,MAAM,WAAW,KAAK;AAAA,IAC7B,OAAO;AAAA,MACL;AAAA,QACE,MAAM;AAAA,QACN,SAAS,OAAO;AAAA,MAClB;AAAA,IACF;AAAA,IACA,CAAC,aAAa,GAAG;AAAA,EACnB,CAAQ;AAGR,WAAS,MAAM,aAAa,KAAK;AAAA,IAC/B,OAAO;AAAA,MACL;AAAA,QACE,MAAM;AAAA,QACN,SAAS,OAAO;AAAA,MAClB;AAAA,IACF;AAAA,IACA,CAAC,aAAa,GAAG;AAAA,EACnB,CAAQ;AAIR,EAAC,SAAS,MAAM,iBAA2B,KAAK;AAAA,IAC9C,OAAO;AAAA,MACL;AAAA,QACE,MAAM;AAAA,QACN,SAAS,OAAO;AAAA,QAChB,SAAS;AAAA,MACX;AAAA,IACF;AAAA,IACA,CAAC,aAAa,GAAG;AAAA,EACnB,CAAQ;AAIR,WAAS,MAAM,OAAO,SAAS,MAAM,QAAQ,CAAC;AAC9C,sBAAoB,SAAS,OAAc,MAAM;AACjD,WAAS,MAAM,KAAK,KAAK;AAAA,IACvB,OAAO;AAAA,MACL;AAAA,QACE,MAAM;AAAA,QACN,SAAS,OAAO;AAAA,QAChB,SAAS;AAAA,MACX;AAAA,IACF;AAAA,IACA,CAAC,aAAa,GAAG;AAAA,EACnB,CAAQ;AAER,sBAAoB,cAAc,QAAQ;AAC5C;AAMO,SAAS,iBAAiB,cAA+B;AAC9D,MAAI,CAACA,YAAW,YAAY,EAAG,QAAO;AACtC,QAAM,WAAW,aAAa,YAAY;AAC1C,MAAI,CAAC,SAAS,MAAO,QAAO;AAE5B,QAAM,SAAS,CAAC,cAAc,eAAe,cAAc,gBAAgB,QAAQ,kBAAkB;AACrG,aAAW,OAAO,QAAQ;AACxB,wBAAoB,SAAS,OAAc,GAAG;AAAA,EAChD;AAGA,aAAW,OAAO,QAAQ;AACxB,QAAI,MAAM,QAAS,SAAS,MAAc,GAAG,CAAC,KAAM,SAAS,MAAc,GAAG,EAAE,WAAW,GAAG;AAC5F,aAAQ,SAAS,MAAc,GAAG;AAAA,IACpC;AAAA,EACF;AAEA,MAAI,OAAO,KAAK,SAAS,KAAK,EAAE,WAAW,GAAG;AAC5C,WAAO,SAAS;AAAA,EAClB;AAEA,sBAAoB,cAAc,QAAQ;AAC1C,SAAO;AACT;AAMO,SAAS,eAAe,cAM7B;AACA,MAAI,CAACA,YAAW,YAAY,GAAG;AAC7B,WAAO,EAAE,WAAW,OAAO,gBAAgB,OAAO,iBAAiB,OAAO,YAAY,OAAO,cAAc,MAAM;AAAA,EACnH;AACA,QAAM,WAAW,aAAa,YAAY;AAC1C,QAAM,MAAO,SAAS,OAAe,cAAc,CAAC;AACpD,QAAM,OAAQ,SAAS,OAAe,eAAe,CAAC;AACtD,QAAM,kBAAmB,SAAS,OAAe,cAAc,CAAC;AAChE,QAAM,oBAAqB,SAAS,OAAe,gBAAgB,CAAC;AACpE,QAAM,iBAAiB,IAAI,KAAK,CAAC,MAAW,IAAI,aAAa,MAAM,IAAI;AACvE,QAAM,kBAAkB,KAAK,KAAK,CAAC,MAAW,IAAI,aAAa,MAAM,IAAI;AACzE,QAAM,aAAa,gBAAgB,KAAK,CAAC,MAAW,IAAI,aAAa,MAAM,IAAI;AAC/E,QAAM,eAAe,kBAAkB,KAAK,CAAC,MAAW,IAAI,aAAa,MAAM,IAAI;AACnF,SAAO;AAAA,IACL,WAAW,kBAAkB,mBAAmB,cAAc;AAAA,IAC9D;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACF;AAzSA,IAyBM;AAzBN;AAAA;AAAA;AAyBA,IAAM,gBAAgB;AAAA;AAAA;;;ACjBtB,SAAS,gBAAAC,eAAc,iBAAAC,gBAAe,cAAAC,aAAY,aAAAC,kBAAiB;AACnE,SAAS,WAAAC,UAAS,SAAS,iBAAiB;AAC5C,SAAS,WAAAC,gBAAe;AAsBxB,SAAS,WAAW,GAAmB;AACrC,SAAO,MAAM,EAAE,QAAQ,MAAM,OAAO,IAAI;AAC1C;AAGA,SAAS,YAAY,YAA4B;AAC/C,SAAO,2CAA2C,WAAW,UAAU;AACzE;AAEA,SAAS,UAAU,YAA4B;AAC7C,SAAO,aAAa,WAAW,UAAU;AAC3C;AAOA,SAAS,kBAAkB,MAAsB;AAC/C,QAAM,WAAW,QAAQ,UAAU,IAAI,CAAC;AACxC,MAAI,CAAC,oBAAoB,KAAK,SAAO,SAAS,WAAW,MAAM,GAAG,KAAK,aAAa,GAAG,GAAG;AACxF,UAAM,IAAI,MAAM,gEAAgE,QAAQ,EAAE;AAAA,EAC5F;AACA,SAAO;AACT;AAiBA,SAAS,cAAc,SAAkC;AACvD,QAAM,WAAW,kBAAkB,OAAO;AAC1C,MAAI;AACF,UAAM,MAAML,cAAa,UAAU,OAAO;AAC1C,WAAO,KAAK,MAAM,GAAG;AAAA,EACvB,SAAS,KAAU;AACjB,QAAI,KAAK,SAAS,SAAU,QAAO,EAAE,SAAS,GAAG,OAAO,CAAC,EAAE;AAC3D,UAAM,IAAI,MAAM,mBAAmB,QAAQ,KAAM,IAAc,OAAO,EAAE;AAAA,EAC1E;AACF;AAEA,SAAS,qBAAqB,SAAiB,MAA6B;AAC1E,QAAM,WAAW,kBAAkB,OAAO;AAC1C,EAAAG,WAAUC,SAAQ,QAAQ,GAAG,EAAE,WAAW,KAAK,CAAC;AAChD,QAAM,UAAU,GAAG,QAAQ;AAC3B,EAAAH,eAAc,SAAS,KAAK,UAAU,MAAM,MAAM,CAAC,IAAI,MAAM,EAAE,UAAU,SAAS,MAAM,IAAM,CAAC;AAC/F,EAAAC,YAAW,SAAS,QAAQ;AAC9B;AAEA,SAASI,eAAc,OAAqB;AAC1C,MAAI,QAAQC,cAAa,EAAG,QAAO;AACnC,SAAO,OAAO,OAAO,YAAY,YAAY,MAAM,QAAQ,SAAS,iBAAiB;AACvF;AAQA,SAASC,qBAAoB,OAAiC,OAAqB;AACjF,MAAI,CAAC,MAAO;AACZ,QAAM,MAAM,MAAM,KAAK;AACvB,MAAI,CAAC,MAAM,QAAQ,GAAG,EAAG;AACzB,QAAM,KAAK,IAAI,IAAI,OAAO,CAAC,UAAe,CAACF,eAAc,KAAK,CAAC;AACjE;AAEA,SAAS,WACP,OACA,OACA,YACA,MACM;AACN,QAAO,KAAK,IAAI,MAAO,KAAK,KAAK,CAAC;AAClC,QAAO,KAAK,EAAG,KAAK;AAAA,IAClB,SAAS,YAAY,UAAU;AAAA,IAC/B,SAAS,KAAK;AAAA,IACd,YAAY,KAAK,cAAc;AAAA,IAC/B,GAAI,KAAK,UAAU,EAAE,SAAS,KAAK,QAAQ,IAAI,CAAC;AAAA,IAChD,CAACC,cAAa,GAAG;AAAA,EACnB,CAAC;AACH;AAEO,SAAS,mBAAmB,eAAuB,QAAgC;AACxF,QAAM,OAAO,cAAc,aAAa;AACxC,OAAK,UAAU,KAAK,WAAW;AAC/B,OAAK,QAAQ,KAAK,SAAS,CAAC;AAE5B,aAAW,OAAO,YAAY;AAC5B,IAAAC,qBAAoB,KAAK,OAAO,GAAG;AAAA,EACrC;AAEA,QAAM,IAAI,KAAK;AAEf,aAAW,GAAG,gBAAgB,OAAO,wBAAwB,EAAE,SAAS,EAAE,CAAC;AAC3E,aAAW,GAAG,cAAc,OAAO,uBAAuB,EAAE,SAAS,GAAG,CAAC;AACzE,aAAW,GAAG,sBAAsB,OAAO,4BAA4B,EAAE,SAAS,EAAE,CAAC;AACrF,aAAW,GAAG,QAAQ,OAAO,0BAA0B,EAAE,SAAS,EAAE,CAAC;AAErE,IAAE,uBAAuB,EAAE,wBAAwB,CAAC;AACpD,IAAE,qBAAqB,KAAK;AAAA,IAC1B,SAAS,UAAU,OAAO,mBAAmB;AAAA,IAC7C,SAAS;AAAA,IACT,YAAY;AAAA,IACZ,CAACD,cAAa,GAAG;AAAA,EACnB,CAAC;AAED,aAAW,GAAG,uBAAuB,OAAO,wBAAwB,EAAE,SAAS,GAAG,CAAC;AAGnF,IAAE,aAAa,EAAE,cAAc,CAAC;AAChC,IAAE,WAAW,KAAK;AAAA,IAChB,SAAS,UAAU,OAAO,mBAAmB;AAAA,IAC7C,SAAS;AAAA,IACT,YAAY;AAAA,IACZ,SAAS;AAAA,IACT,CAACA,cAAa,GAAG;AAAA,EACnB,CAAC;AAED,aAAW,GAAG,cAAc,OAAO,wBAAwB;AAAA,IACzD,SAAS;AAAA,IACT,SAAS;AAAA,EACX,CAAC;AACD,aAAW,GAAG,cAAc,OAAO,uBAAuB;AAAA,IACxD,SAAS;AAAA,IACT,SAAS;AAAA,EACX,CAAC;AACD,aAAW,GAAG,cAAc,OAAO,uBAAuB;AAAA,IACxD,SAAS;AAAA,IACT,SAAS;AAAA,EACX,CAAC;AACD,aAAW,GAAG,cAAc,OAAO,sBAAsB;AAAA,IACvD,SAAS;AAAA,IACT,SAAS;AAAA,EACX,CAAC;AACD,aAAW,GAAG,cAAc,OAAO,qBAAqB;AAAA,IACtD,SAAS;AAAA,IACT,SAAS;AAAA,EACX,CAAC;AAED,IAAE,gBAAgB,EAAE,iBAAiB,CAAC;AACtC,IAAE,cAAc,KAAK;AAAA,IACnB,SAAS,UAAU,OAAO,qBAAqB;AAAA,IAC/C,SAAS;AAAA,IACT,YAAY;AAAA,IACZ,CAACA,cAAa,GAAG;AAAA,EACnB,CAAC;AAED,aAAW,GAAG,eAAe,OAAO,wBAAwB;AAAA,IAC1D,SAAS;AAAA,IACT,SAAS;AAAA,EACX,CAAC;AAED,uBAAqB,eAAe,IAAI;AAC1C;AAEO,SAAS,qBAAqB,eAAgC;AACnE,MAAI;AACJ,MAAI;AACF,WAAO,cAAc,aAAa;AAAA,EACpC,QAAQ;AACN,WAAO;AAAA,EACT;AACA,MAAI,CAAC,KAAK,MAAO,QAAO;AAExB,aAAW,OAAO,YAAY;AAC5B,IAAAC,qBAAoB,KAAK,OAAO,GAAG;AAAA,EACrC;AAEA,aAAW,OAAO,YAAY;AAC5B,QAAI,MAAM,QAAQ,KAAK,MAAM,GAAG,CAAC,KAAK,KAAK,MAAM,GAAG,EAAE,WAAW,GAAG;AAClE,aAAO,KAAK,MAAM,GAAG;AAAA,IACvB;AAAA,EACF;AACA,MAAI,OAAO,KAAK,KAAK,KAAK,EAAE,WAAW,GAAG;AACxC,WAAO,KAAK;AAAA,EACd;AAEA,uBAAqB,eAAe,IAAI;AACxC,SAAO;AACT;AAEA,SAAS,qBAAqB,OAAsC,gBAAiC;AACnG,UAAQ,SAAS,CAAC,GAAG;AAAA,IAAK,CAAC,MACzBF,eAAc,CAAC,KAAK,OAAO,EAAE,YAAY,YAAY,EAAE,QAAQ,SAAS,cAAc;AAAA,EACxF;AACF;AAEO,SAAS,mBAAmB,eAiBjC;AACA,MAAI;AACJ,MAAI;AACF,WAAO,cAAc,aAAa;AAAA,EACpC,QAAQ;AACN,WAAO;AAAA,MACL,WAAW;AAAA,MACX,cAAc;AAAA,MAAO,YAAY;AAAA,MAAO,oBAAoB;AAAA,MAAO,MAAM;AAAA,MACzE,sBAAsB;AAAA,MAAO,qBAAqB;AAAA,MAClD,YAAY;AAAA,MAAO,gBAAgB;AAAA,MAAO,gBAAgB;AAAA,MAC1D,eAAe;AAAA,MAAO,eAAe;AAAA,MAAO,iBAAiB;AAAA,MAAO,gBAAgB;AAAA,MACpF,eAAe;AAAA,MAAO,aAAa;AAAA,IACrC;AAAA,EACF;AACA,QAAM,IAAI,KAAK,SAAS,CAAC;AACzB,QAAM,gBAAgB,EAAE,gBAAgB,CAAC,GAAG,KAAK,CAAC,MAAMA,eAAc,CAAC,CAAC;AACxE,QAAM,cAAc,EAAE,cAAc,CAAC,GAAG,KAAK,CAAC,MAAMA,eAAc,CAAC,CAAC;AACpE,QAAM,sBAAsB,EAAE,sBAAsB,CAAC,GAAG,KAAK,CAAC,MAAMA,eAAc,CAAC,CAAC;AACpF,QAAM,QAAQ,EAAE,QAAQ,CAAC,GAAG,KAAK,CAAC,MAAMA,eAAc,CAAC,CAAC;AACxD,QAAM,wBAAwB,EAAE,wBAAwB,CAAC,GAAG,KAAK,CAAC,MAAMA,eAAc,CAAC,CAAC;AACxF,QAAM,uBAAuB,EAAE,uBAAuB,CAAC,GAAG,KAAK,CAAC,MAAMA,eAAc,CAAC,CAAC;AACtF,QAAM,MAAM,EAAE,cAAc,CAAC;AAC7B,QAAM,iBAAiB,qBAAqB,KAAK,eAAe,KAAK,qBAAqB,KAAK,mBAAmB;AAClH,QAAM,iBAAiB,qBAAqB,KAAK,kBAAkB,KAAK,qBAAqB,KAAK,sBAAsB;AACxH,QAAM,gBAAgB,qBAAqB,KAAK,iBAAiB;AACjE,QAAM,gBAAgB,qBAAqB,KAAK,iBAAiB;AACjE,QAAM,kBAAkB,qBAAqB,KAAK,gBAAgB;AAClE,QAAM,iBAAiB,qBAAqB,KAAK,eAAe;AAChE,QAAM,aAAa,kBAAkB,kBAAkB,iBAAiB,iBAAiB,mBAAmB;AAC5G,QAAM,iBAAiB,EAAE,iBAAiB,CAAC,GAAG,KAAK,CAAC,MAAMA,eAAc,CAAC,CAAC;AAC1E,QAAM,eAAe,EAAE,eAAe,CAAC,GAAG,KAAK,CAAC,MAAMA,eAAc,CAAC,CAAC;AACtE,SAAO;AAAA,IACL,WAAW,gBAAgB,cAAc,sBAAsB,QAC1D,wBAAwB,uBAAuB,cAAc,iBAAiB;AAAA,IACnF;AAAA,IAAc;AAAA,IAAY;AAAA,IAAoB;AAAA,IAC9C;AAAA,IAAsB;AAAA,IACtB;AAAA,IAAY;AAAA,IAAgB;AAAA,IAAgB;AAAA,IAAe;AAAA,IAAe;AAAA,IAAiB;AAAA,IAC3F;AAAA,IAAe;AAAA,EACjB;AACF;AA/RA,IA8BMC,gBAeA,qBAoDA;AAjGN;AAAA;AAAA;AA8BA,IAAMA,iBAAgB;AAetB,IAAM,sBAAsB;AAAA,MAC1B,QAAQF,SAAQ,GAAG,SAAS;AAAA,MAC5B,QAAQA,SAAQ,GAAG,WAAW,QAAQ;AAAA,IACxC;AAiDA,IAAM,aAAa;AAAA,MACjB;AAAA,MAAgB;AAAA,MAAc;AAAA,MAAsB;AAAA,MACpD;AAAA,MAAwB;AAAA,MACxB;AAAA,MAAc;AAAA,MAAiB;AAAA,IACjC;AAAA;AAAA;;;ACzFA,SAAS,cAAAI,aAAY,gBAAAC,eAAc,iBAAAC,gBAAe,cAAAC,aAAY,aAAAC,kBAAiB;AAC/E,SAAS,WAAAC,gBAAe;AACxB,SAAS,WAAAC,UAAS,QAAAC,aAAY;AAsB9B,SAAS,iBAA6B;AACpC,MAAI,CAACP,YAAW,cAAc,EAAG,QAAO,CAAC;AACzC,MAAI;AACF,UAAM,MAAMC,cAAa,gBAAgB,OAAO;AAChD,WAAO,KAAK,MAAM,GAAG;AAAA,EACvB,SAAS,KAAK;AACZ,UAAM,IAAI,MAAM,mBAAmB,cAAc,KAAM,IAAc,OAAO,EAAE;AAAA,EAChF;AACF;AAEA,SAAS,sBAAsB,QAA0B;AACvD,EAAAG,WAAUE,SAAQ,cAAc,GAAG,EAAE,WAAW,KAAK,CAAC;AACtD,QAAM,UAAU,GAAG,cAAc;AACjC,EAAAJ,eAAc,SAAS,KAAK,UAAU,QAAQ,MAAM,CAAC,IAAI,MAAM,OAAO;AACtE,EAAAC,YAAW,SAAS,cAAc;AACpC;AAgBO,SAAS,iBAAiB,MAAwD;AACvF,QAAM,SAAS,eAAe;AAC9B,SAAO,aAAa,OAAO,cAAc,CAAC;AAI1C,aAAW,CAAC,MAAM,KAAK,KAAK,OAAO,QAAQ,OAAO,UAAU,GAAG;AAC7D,QAAI,QAAQK,cAAa,MAAM,KAAM,QAAO,OAAO,WAAW,IAAI;AAAA,EACpE;AAEA,MAAI,KAAK,OAAO;AACd,UAAMC,OAAM;AACZ,UAAM,YAAYF,MAAKF,SAAQ,GAAG,WAAW,kBAAkB;AAC/D,QAAI,aAAa;AACjB,QAAI;AAAE,mBAAaJ,cAAa,WAAW,OAAO,EAAE,KAAK;AAAA,IAAG,QAAQ;AAAA,IAAC;AACrE,WAAO,WAAW,kBAAkB,IAAI;AAAA,MACtC,MAAM;AAAA,MACN,KAAAQ;AAAA,MACA,GAAI,aAAa,EAAE,SAAS,EAAE,eAAe,UAAU,UAAU,GAAG,EAAE,IAAI,CAAC;AAAA,MAC3E,CAACD,cAAa,GAAG;AAAA,IACnB;AACA,0BAAsB,MAAM;AAC5B,WAAO,EAAE,MAAM,gBAAgB,KAAAC,KAAI;AAAA,EACrC;AAEA,QAAM,MAAM,GAAG,KAAK,WAAW,QAAQ,OAAO,EAAE,CAAC;AACjD,SAAO,WAAW,kBAAkB,IAAI;AAAA,IACtC,MAAM;AAAA,IACN;AAAA,IACA,SAAS,EAAE,eAAe,UAAU,KAAK,WAAW,GAAG;AAAA,IACvD,CAACD,cAAa,GAAG;AAAA,EACnB;AAEA,wBAAsB,MAAM;AAC5B,SAAO,EAAE,MAAM,gBAAgB,IAAI;AACrC;AAMO,SAAS,qBAA8B;AAC5C,MAAI,CAACR,YAAW,cAAc,EAAG,QAAO;AACxC,QAAM,SAAS,eAAe;AAC9B,MAAI,CAAC,OAAO,cAAc,OAAO,KAAK,OAAO,UAAU,EAAE,WAAW,EAAG,QAAO;AAE9E,MAAI,UAAU;AACd,aAAW,CAAC,MAAM,KAAK,KAAK,OAAO,QAAQ,OAAO,UAAU,GAAG;AAC7D,QAAI,QAAQQ,cAAa,MAAM,MAAM;AACnC,aAAO,OAAO,WAAW,IAAI;AAC7B,gBAAU;AAAA,IACZ;AAAA,EACF;AACA,MAAI,CAAC,QAAS,QAAO;AAGrB,MAAI,OAAO,KAAK,OAAO,UAAU,EAAE,WAAW,EAAG,QAAO,OAAO;AAE/D,wBAAsB,MAAM;AAC5B,SAAO;AACT;AAKO,SAAS,mBAId;AACA,MAAI,CAACR,YAAW,cAAc,GAAG;AAC/B,WAAO,EAAE,WAAW,OAAO,YAAY,eAAe;AAAA,EACxD;AACA,QAAM,SAAS,eAAe;AAC9B,QAAM,QAAQ,OAAO,aAAa,kBAAkB;AACpD,MAAI,CAAC,SAAS,MAAMQ,cAAa,MAAM,MAAM;AAC3C,WAAO,EAAE,WAAW,OAAO,YAAY,eAAe;AAAA,EACxD;AACA,SAAO,EAAE,WAAW,MAAM,YAAY,gBAAgB,KAAK,MAAM,IAAI;AACvE;AAlJA,IAgBMA,gBACA,oBACA;AAlBN;AAAA;AAAA;AAgBA,IAAMA,iBAAgB;AACtB,IAAM,qBAAqB;AAC3B,IAAM,iBAAiBD,MAAKF,SAAQ,GAAG,cAAc;AAAA;AAAA;;;AClBrD,IASa;AATb;AAAA;AAAA;AASO,IAAM,uBAAuB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACTpC,IASa,kBA8/BA,kBAwNA,iBAwMA,iBAuJA,eAsbA,gBAyKA,eA2KA,iBA4FA,kBAqEA,kBA0EA,oBAmJA,uBAoDA,sBA6VA;AAvlGb;AAAA;AAAA;AASO,IAAM,mBAAmB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AA8/BzB,IAAM,mBAAmB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAwNzB,IAAM,kBAAkB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAwMxB,IAAM,kBAAkB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAuJxB,IAAM,gBAAgB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAsbtB,IAAM,iBAAiB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAyKvB,IAAM,gBAAgB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AA2KtB,IAAM,kBAAkB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AA4FxB,IAAM,mBAAmB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAqEzB,IAAM,mBAAmB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AA0EzB,IAAM,qBAAqB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAmJ3B,IAAM,wBAAwB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAoD9B,IAAM,uBAAuB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AA6V7B,IAAM,yBAAyB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;AChlGtC,SAAS,oBAAqD;AAC9D,SAAS,iBAAAK,gBAAe,gBAAAC,eAAc,cAAAC,aAAY,aAAAC,YAAW,cAAAC,mBAAkB;AAC/E,SAAS,WAAAC,UAAS,gBAAgB;AAClC,SAAS,QAAAC,OAAM,WAAAC,gBAAe;AAC9B,SAAS,gBAAgB;AACzB,OAAO,SAAS;AAmKhB,SAAS,YAAY,KAAmB;AACtC,QAAM,KAAK,SAAS;AACpB,MAAI;AACJ,MAAIC;AAEJ,UAAQ,IAAI;AAAA,IACV,KAAK;AACH,YAAM;AACN,MAAAA,QAAO,CAAC,GAAG;AACX;AAAA,IACF,KAAK;AAIH,YAAM;AACN,MAAAA,QAAO,CAAC,MAAM,SAAS,IAAI,GAAG;AAC9B;AAAA,IACF;AACE,YAAM;AACN,MAAAA,QAAO,CAAC,GAAG;AAAA,EACf;AAKA,WAAS,KAAKA,OAAM,CAAC,UAAU;AAC7B,QAAI,OAAO;AACT,cAAQ,MAAM,uCAAuC;AACrD,cAAQ,IAAI,kCAAkC,GAAG,EAAE;AAAA,IACrD;AAAA,EACF,CAAC;AACH;AAKO,SAAS,gBAAgB,MAA6B;AAC3D,QAAM,MAAMD,SAAQ,SAAS;AAE7B,MAAI,CAACL,YAAW,GAAG,GAAG;AACpB,IAAAC,WAAU,KAAK,EAAE,WAAW,MAAM,MAAM,IAAM,CAAC;AAAA,EACjD;AAEA,EAAAH,eAAc,WAAW,KAAK,UAAU,MAAM,MAAM,CAAC,GAAG,EAAE,MAAM,IAAM,CAAC;AACzE;AAKO,SAAS,kBAA0C;AACxD,MAAI,CAACE,YAAW,SAAS,GAAG;AAC1B,WAAO;AAAA,EACT;AAEA,MAAI;AACF,UAAM,UAAUD,cAAa,WAAW,MAAM;AAC9C,WAAO,KAAK,MAAM,OAAO;AAAA,EAC3B,SAAS,OAAO;AACd,WAAO;AAAA,EACT;AACF;AAWA,SAAS,uBAAiD;AAMxD,QAAM,eAAe;AAAA,IACnB,+BAA+B;AAAA,IAC/B,gCAAgC;AAAA,IAChC,gCAAgC;AAAA,IAChC,QAAQ;AAAA,EACV;AAEA,SAAO,IAAI,QAAQ,CAACQ,UAAS,WAAW;AACtC,UAAM,SAAS,aAAa,CAAC,KAAsB,QAAwB;AAGzE,UAAI,IAAI,WAAW,WAAW;AAC5B,cAAM,SAAS,IAAI,QAAQ;AAC3B,YAAI,WAAW,qBAAqB;AAClC,cAAI,UAAU,KAAK,YAAY;AAAA,QACjC,OAAO;AACL,cAAI,UAAU,KAAK;AAAA,YACjB,gCAAgC;AAAA,YAChC,gCAAgC;AAAA,YAChC,QAAQ;AAAA,UACV,CAAC;AAAA,QACH;AACA,YAAI,IAAI;AACR;AAAA,MACF;AAKA,YAAM,YAAY,IAAI,QAAQ;AAC9B,UAAI,aAAa,cAAc,qBAAqB;AAClD,YAAI,UAAU,KAAK,EAAE,QAAQ,SAAS,CAAC;AACvC,YAAI,IAAI;AACR;AAAA,MACF;AAEA,UAAI,CAAC,IAAI,KAAK;AACZ,YAAI,UAAU,KAAK,YAAY;AAC/B,YAAI,IAAI;AACR;AAAA,MACF;AAEA,YAAM,MAAM,IAAI,IAAI,IAAI,KAAK,oBAAoB,IAAI,EAAE;AAEvD,UAAI,IAAI,aAAa,SAAS;AAC5B,YAAI,UAAU,KAAK,YAAY;AAC/B,YAAI,IAAI;AACR;AAAA,MACF;AAEA,UAAI,IAAI,WAAW,QAAQ;AAIzB,YAAI,UAAU,KAAK,EAAE,GAAG,cAAc,SAAS,iBAAiB,gBAAgB,YAAY,CAAC;AAC7F,YAAI,IAAI,UAAU;AAClB;AAAA,MACF;AAGA,YAAM,WAAW,KAAK;AACtB,YAAM,SAAmB,CAAC;AAC1B,UAAI,QAAQ;AACZ,UAAI,UAAU;AACd,UAAI,GAAG,QAAQ,CAAC,UAAkB;AAChC,YAAI,QAAS;AACb,iBAAS,MAAM;AACf,YAAI,QAAQ,UAAU;AACpB,oBAAU;AACV,cAAI,UAAU,KAAK,YAAY;AAC/B,cAAI,IAAI;AACR;AAAA,QACF;AACA,eAAO,KAAK,KAAK;AAAA,MACnB,CAAC;AACD,UAAI,GAAG,OAAO,MAAM;AAClB,YAAI,QAAS;AACb,YAAI;AACJ,YAAI;AACF,mBAAS,KAAK,MAAM,OAAO,OAAO,MAAM,EAAE,SAAS,MAAM,CAAC;AAAA,QAC5D,QAAQ;AACN,cAAI,UAAU,KAAK,EAAE,GAAG,cAAc,gBAAgB,mBAAmB,CAAC;AAC1E,cAAI,IAAI,KAAK,UAAU,EAAE,OAAO,eAAe,CAAC,CAAC;AACjD,qBAAW,MAAM;AACf,mBAAO,MAAM;AACb,mBAAO,IAAI,MAAM,0CAA0C,CAAC;AAAA,UAC9D,GAAG,GAAG;AACN;AAAA,QACF;AAEA,cAAM,QAA4B,QAAQ;AAC1C,YAAI,CAAC,SAAS,OAAO,UAAU,UAAU;AACvC,cAAI,UAAU,KAAK,EAAE,GAAG,cAAc,gBAAgB,mBAAmB,CAAC;AAC1E,cAAI,IAAI,KAAK,UAAU,EAAE,OAAO,gBAAgB,CAAC,CAAC;AAClD,qBAAW,MAAM;AACf,mBAAO,MAAM;AACb,mBAAO,IAAI,MAAM,sCAAsC,CAAC;AAAA,UAC1D,GAAG,GAAG;AACN;AAAA,QACF;AAEA,cAAM,WAA4B;AAAA,UAChC,cAAc;AAAA,UACd,eAAe,OAAO,OAAO,kBAAkB,WAAW,OAAO,gBAAgB;AAAA,UACjF,SAAS,OAAO,OAAO,YAAY,WAAW,OAAO,UAAU;AAAA,UAC/D,OAAO,OAAO,OAAO,UAAU,WAAW,OAAO,QAAQ;AAAA,UACzD,QAAQ,OAAO,OAAO,WAAW,WAAW,OAAO,SAAS;AAAA,UAC5D,OAAO,OAAO,OAAO,UAAU,WAAW,OAAO,QAAQ;AAAA,QAC3D;AAEA,YAAI,UAAU,KAAK,EAAE,GAAG,cAAc,gBAAgB,mBAAmB,CAAC;AAC1E,YAAI,IAAI,KAAK,UAAU,EAAE,IAAI,KAAK,CAAC,CAAC;AAEpC,mBAAW,MAAM;AACf,iBAAO,MAAM;AACb,UAAAA,SAAQ,QAAQ;AAAA,QAClB,GAAG,GAAG;AAAA,MACR,CAAC;AACD,UAAI,GAAG,SAAS,CAAC,MAAM;AACrB,YAAI,QAAS;AACb,kBAAU;AACV,YAAI;AAAE,cAAI,UAAU,KAAK,YAAY;AAAG,cAAI,IAAI;AAAA,QAAG,QAAQ;AAAA,QAAC;AAC5D,mBAAW,MAAM;AACf,iBAAO,MAAM;AACb,iBAAO,CAAC;AAAA,QACV,GAAG,GAAG;AAAA,MACR,CAAC;AAAA,IACH,CAAC;AAED,WAAO,OAAO,IAAI;AAElB,WAAO,GAAG,SAAS,CAAC,UAAiC;AACnD,UAAI,MAAM,SAAS,cAAc;AAC/B;AAAA,UACE,IAAI;AAAA,YACF,QAAQ,IAAI;AAAA,UACd;AAAA,QACF;AAAA,MACF,OAAO;AACL,eAAO,KAAK;AAAA,MACd;AAAA,IACF,CAAC;AAAA,EACH,CAAC;AACH;AAYA,eAAsB,aACpB,UACiC;AACjC,QAAM,OAAO,aAAa,MAAM;AAAA,EAAC;AAEjC,MAAI;AACF,SAAK,EAAE,OAAO,WAAW,CAAC;AAG1B,UAAM,gBAAgB,qBAAqB;AAG3C,UAAM,UAAU,GAAG,mBAAmB,kBAAkB,IAAI;AAC5D,gBAAY,OAAO;AAEnB,SAAK,EAAE,OAAO,kBAAkB,KAAK,QAAQ,CAAC;AAC9C,SAAK,EAAE,OAAO,UAAU,CAAC;AAGzB,UAAM,OAAO,MAAM;AAEnB,SAAK,EAAE,OAAO,UAAU,CAAC;AACzB,oBAAgB,IAAI;AACpB,WAAO;AAAA,EACT,SAAS,OAAO;AACd,UAAM,UAAU,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;AACrE,SAAK,EAAE,OAAO,SAAS,QAAQ,CAAC;AAChC,WAAO;AAAA,EACT;AACF;AAKO,SAAS,kBAA2B;AACzC,QAAM,QAAQ,gBAAgB;AAC9B,MAAI,CAAC,MAAO,QAAO;AAGnB,MAAI;AACF,UAAM,UAAU,IAAI,OAAO,MAAM,YAAY;AAC7C,QAAI,CAAC,SAAS,IAAK,QAAO;AAG1B,WAAO,KAAK,IAAI,IAAI,QAAQ,MAAM;AAAA,EACpC,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAsBO,SAAS,cAAwB;AACtC,QAAM,QAAQ,gBAAgB;AAC9B,MAAI,CAAC,OAAO;AACV,UAAM,IAAI,MAAM,mBAAmB;AAAA,EACrC;AAKA,MAAI,MAAM,SAAS;AACjB,WAAO;AAAA,MACL,IAAI,MAAM;AAAA,MACV,OAAO,MAAM,SAAS;AAAA,MACtB,QAAQ,MAAM;AAAA,IAChB;AAAA,EACF;AAEA,QAAM,UAAU,IAAI,OAAO,MAAM,YAAY;AAC7C,MAAI,CAAC,SAAS;AACZ,UAAM,IAAI,MAAM,eAAe;AAAA,EACjC;AAEA,SAAO;AAAA,IACL,IAAI,QAAQ;AAAA,IACZ,OAAO,QAAQ,SAAS;AAAA,IACxB,QAAQ,QAAQ;AAAA,EAClB;AACF;AAKO,SAAS,iBAAgC;AAC9C,QAAM,QAAQ,gBAAgB;AAC9B,SAAO,OAAO,gBAAgB;AAChC;AAKO,SAAS,iBAA0B;AACxC,QAAM,QAAQ,gBAAgB;AAC9B,MAAI,CAAC,MAAO,QAAO;AAEnB,MAAI;AACF,UAAM,UAAU,IAAI,OAAO,MAAM,YAAY;AAC7C,QAAI,CAAC,SAAS,IAAK,QAAO;AAG1B,UAAM,YAAY,QAAQ,MAAM;AAChC,UAAM,SAAS,IAAI,KAAK;AACxB,WAAO,KAAK,IAAI,IAAI,YAAY;AAAA,EAClC,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAKA,eAAsB,eAAiC;AACrD,QAAM,QAAQ,gBAAgB;AAC9B,MAAI,CAAC,OAAO,cAAe,QAAO;AAElC,MAAI;AACF,UAAM,WAAW,MAAM,MAAM,GAAG,cAAc,qBAAqB;AAAA,MACjE,QAAQ;AAAA,MACR,SAAS,EAAE,gBAAgB,mBAAmB;AAAA,MAC9C,MAAM,KAAK,UAAU,EAAE,eAAe,MAAM,cAAc,CAAC;AAAA,IAC7D,CAAC;AAED,QAAI,CAAC,SAAS,GAAI,QAAO;AAEzB,UAAM,OAAO,MAAM,SAAS,KAAK;AACjC,QAAI,KAAK,cAAc;AACrB,sBAAgB;AAAA,QACd,GAAG;AAAA,QACH,cAAc,KAAK;AAAA,QACnB,eAAe,KAAK,iBAAiB,MAAM;AAAA,MAC7C,CAAC;AACD,aAAO;AAAA,IACT;AACA,WAAO;AAAA,EACT,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAQA,eAAsB,mBAAqC;AACzD,MAAI,CAAC,gBAAgB,EAAG,QAAO;AAE/B,MAAI,eAAe,GAAG;AACpB,QAAI,CAAC,gBAAgB;AACnB,uBAAiB,aAAa,EAAE,QAAQ,MAAM;AAAE,yBAAiB;AAAA,MAAM,CAAC;AAAA,IAC1E;AACA,UAAM,YAAY,MAAM;AACxB,QAAI,CAAC,WAAW;AACd,uBAAiB;AACjB,aAAO;AAAA,IACT;AAAA,EACF;AAEA,SAAO;AACT;AAKO,SAAS,mBAAyB;AACvC,MAAIP,YAAW,SAAS,GAAG;AACzB,IAAAE,YAAW,SAAS;AAAA,EACtB;AACF;AAnlBA,IA8BM,MAKA,kBACA,qBAGA,WACA,aACA,gBA0FA,YAmbF;AAtjBJ;AAAA;AAAA;AA8BA,IAAM,OAAO;AAKb,IAAM,mBAAmB,QAAQ,IAAI;AACrC,IAAM,sBAAuB,oBAAoB,eAAe,KAAK,gBAAgB,IACjF,mBACA;AACJ,IAAM,YAAY,QAAQ,IAAI,oBAAoBE,MAAKD,SAAQ,GAAG,WAAW,kBAAkB;AAC/F,IAAM,cAAc,QAAQ,IAAI,mBAAmB,QAAQ,IAAI;AAC/D,IAAM,iBAAkB,eAAe,eAAe,KAAK,WAAW,IAClE,cACA;AAwFJ,IAAM,aAAa;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAmbnB,IAAI,iBAA0C;AAAA;AAAA;;;ACtjB9C;AAAA;AAAA;AAMA;AAAA;AAAA;;;ACMO,SAAS,cAAc,KAAmB;AAC/C,YAAU;AACZ;AAkDA,eAAe,QACb,QACA,UACA,MACY;AACZ,MAAI,CAAC,SAAS;AACZ,UAAM,IAAI,MAAM,qDAAqD;AAAA,EACvE;AAEA,QAAM,MAAM,GAAG,OAAO,GAAG,QAAQ;AACjC,QAAM,iBAAiB;AACvB,QAAM,cAAc,eAAe;AAEnC,QAAM,UAAkC;AAAA,IACtC,gBAAgB;AAAA,EAClB;AAEA,MAAI,aAAa;AACf,YAAQ,eAAe,IAAI,UAAU,WAAW;AAAA,EAClD;AAEA,QAAM,WAAW,MAAM,MAAM,KAAK;AAAA,IAChC;AAAA,IACA;AAAA,IACA,MAAM,OAAO,KAAK,UAAU,IAAI,IAAI;AAAA,EACtC,CAAC;AAED,MAAI,CAAC,SAAS,IAAI;AAChB,UAAM,QAAQ,MAAM,SAAS,KAAK,EAAE,MAAM,OAAO,EAAE,QAAQ,SAAS,WAAW,EAAE;AACjF,UAAM,IAAI,MAAM,MAAM,UAAU,cAAc,SAAS,MAAM,EAAE;AAAA,EACjE;AAEA,SAAO,SAAS,KAAK;AACvB;AAyBA,eAAsB,cACpB,MACA,OACgC;AAChC,QAAM,OAAgC,EAAE,KAAK;AAC7C,MAAI,SAAS,MAAM,SAAS,EAAG,MAAK,QAAQ;AAC5C,SAAO,QAA+B,QAAQ,aAAa,IAAI;AACjE;AAkBA,eAAsB,eAAmC;AACvD,SAAO,QAAmB,OAAO,WAAW;AAC9C;AAsDA,eAAsB,WACpB,WACA,QAC0B;AAC1B,SAAO,QAAyB,UAAU,aAAa,SAAS,UAAU,MAAM,EAAE;AACpF;AAhNA,IAUI;AAVJ;AAAA;AAAA;AAQA;AAEA,IAAI,UAAU;AAAA;AAAA;;;ACVd,IAaa,sBAoDA;AAjEb;AAAA;AAAA;AAaO,IAAM,uBAAuB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAoD7B,IAAM,gBAAgB;AAAA;AAAA;;;AC1D7B,SAAS,cAAAK,aAAY,aAAAC,YAAW,iBAAAC,sBAAqB;AACrD,SAAS,YAAAC,iBAAgB;AACzB,SAAS,QAAAC,aAAY;AAOrB,SAAS,YAAY,OAAe,OAAe,MAAc,MAAc,OAAqB;AAClG,EAAAD,UAAS,iBAAiB,IAAI,WAAW,KAAK,IAAI,IAAI,aAAa;AAAA,IACjE,OAAO;AAAA,IACP,KAAK,EAAE,GAAG,QAAQ,KAAK,UAAU,MAAM;AAAA,IACvC,OAAO,CAAC,QAAQ,UAAU,MAAM;AAAA,IAChC,SAAS;AAAA,EACX,CAAC;AACH;AAKA,eAAsB,oBAAoB,MAA6F;AACrI,QAAM,QAAmE,CAAC;AAC1E,MAAI,OAAO;AACX,SAAO,QAAQ,GAAG;AAChB,UAAM,MAAM,uDAAuD,IAAI;AACvE,UAAM,OAAO,MAAM,MAAM,KAAK;AAAA,MAC5B,SAAS;AAAA,QACP,eAAe,UAAU,KAAK,KAAK;AAAA,QACnC,QAAQ;AAAA,QACR,wBAAwB;AAAA,MAC1B;AAAA,IACF,CAAC;AACD,QAAI,CAAC,KAAK,IAAI;AACZ,YAAM,IAAI,MAAM,cAAc,KAAK,MAAM,gBAAgB;AAAA,IAC3D;AACA,UAAM,OAAO,MAAM,KAAK,KAAK;AAC7B,QAAI,KAAK,WAAW,EAAG;AACvB,eAAW,KAAK,MAAM;AACpB,YAAM,KAAK,EAAE,OAAO,EAAE,MAAM,OAAO,MAAM,EAAE,MAAM,WAAW,EAAE,UAAU,CAAC;AAAA,IAC3E;AACA,QAAI,KAAK,SAAS,IAAK;AACvB;AAAA,EACF;AACA,SAAO;AACT;AAEA,eAAsB,kBACpB,MACA,OACA,MACA,SACe;AACf,MAAI;AAAE,IAAAA,UAAS,gBAAgB,EAAE,OAAO,UAAU,SAAS,IAAK,CAAC;AAAA,EAAG,QAAQ;AAC1E,UAAM,IAAI,MAAM,+DAA+D;AAAA,EACjF;AACA,MAAI,QAAQ,sBAAsB;AAChC,gBAAY,KAAK,OAAO,OAAO,MAAM,2BAA2B,QAAQ,oBAAoB;AAAA,EAC9F;AACA,cAAY,KAAK,OAAO,OAAO,MAAM,kBAAkB,QAAQ,YAAY;AAC7E;AAMO,SAAS,kBAAkB,cAAqC;AACrE,QAAM,cAAcC,MAAK,cAAc,WAAW,WAAW;AAC7D,EAAAH,WAAU,aAAa,EAAE,WAAW,KAAK,CAAC;AAC1C,QAAM,eAAeG,MAAK,aAAa,YAAY;AACnD,EAAAF,eAAc,cAAc,sBAAsB,OAAO;AACzD,SAAO;AACT;AAKO,SAAS,YAAY,UAAiC;AAC3D,MAAI,MAAM;AACV,SAAO,OAAO,QAAQ,KAAK;AACzB,QAAIF,YAAWI,MAAK,KAAK,MAAM,CAAC,EAAG,QAAO;AAC1C,UAAM,SAASA,MAAK,KAAK,IAAI;AAC7B,QAAI,WAAW,IAAK;AACpB,UAAM;AAAA,EACR;AACA,SAAO;AACT;AA7FA,IA+Fa,cAKA;AApGb;AAAA;AAAA;AAUA;AAqFO,IAAM,eAAe;AAAA,MAC1B,cAAc;AAAA,MACd,gBAAgB;AAAA,IAClB;AAEO,IAAM,yBAAyB;AAAA;AAAA;;;AC5FtC,SAAS,YAAAC,iBAAgB;AACzB,SAAS,gBAAAC,qBAAoB;AAC7B,SAAS,uBAAuB;AAUhC,SAAS,gBAAgE;AACvE,MAAI;AACF,UAAM,YAAYD,UAAS,6BAA6B,EAAE,UAAU,SAAS,SAAS,IAAK,CAAC,EAAE,KAAK;AAEnG,UAAM,WAAW,UAAU,MAAM,6BAA6B;AAC9D,UAAM,YAAY,UAAU,MAAM,qCAAqC;AACvE,UAAM,QAAQ,YAAY;AAC1B,QAAI,CAAC,MAAO,QAAO;AACnB,UAAM,WAAW,MAAM,CAAC;AACxB,WAAO,EAAE,UAAU,WAAW,SAAS,MAAM,GAAG,EAAE,IAAI,KAAK,SAAS;AAAA,EACtE,QAAQ;AAAE,WAAO;AAAA,EAAM;AACzB;AAEA,SAAS,IAAI,IAAwC,UAAmC;AACtF,SAAO,IAAI,QAAQ,CAACE,aAAY,GAAG,SAAS,UAAUA,QAAO,CAAC;AAChE;AAEA,SAAS,qBAAsC;AAC7C,SAAO,IAAI,QAAQ,CAACA,UAAS,WAAW;AACtC,UAAM,SAASD,cAAa,CAAC,KAAK,QAAQ;AACxC,UAAI,IAAI,WAAW,WAAW;AAC5B,YAAI,UAAU,KAAK;AAAA,UACjB,+BAA+BE;AAAA,UAC/B,gCAAgC;AAAA,UAChC,gCAAgC;AAAA,QAClC,CAAC;AACD,YAAI,IAAI;AACR;AAAA,MACF;AAEA,UAAI,IAAI,QAAQ,WAAW,IAAI,WAAW,QAAQ;AAChD,YAAI,UAAU,GAAG;AACjB,YAAI,IAAI;AACR;AAAA,MACF;AAEA,UAAI,OAAO;AACX,UAAI,GAAG,QAAQ,CAAC,UAAU;AAAE,gBAAQ;AAAA,MAAO,CAAC;AAC5C,UAAI,GAAG,OAAO,MAAM;AAClB,YAAI;AACF,gBAAM,SAAS,KAAK,MAAM,IAAI;AAC9B,cAAI,CAAC,OAAO,cAAc;AACxB,gBAAI,UAAU,KAAK;AAAA,cACjB,gBAAgB;AAAA,cAChB,+BAA+BA;AAAA,YACjC,CAAC;AACD,gBAAI,IAAI,KAAK,UAAU,EAAE,OAAO,uBAAuB,CAAC,CAAC;AACzD;AAAA,UACF;AACA,cAAI,UAAU,KAAK;AAAA,YACjB,gBAAgB;AAAA,YAChB,+BAA+BA;AAAA,UACjC,CAAC;AACD,cAAI,IAAI,KAAK,UAAU,EAAE,IAAI,KAAK,CAAC,CAAC;AACpC,qBAAW,MAAM,OAAO,MAAM,GAAG,GAAG;AACpC,UAAAD,SAAQ,OAAO,YAAY;AAAA,QAC7B,QAAQ;AACN,cAAI,UAAU,KAAK,EAAE,gBAAgB,mBAAmB,CAAC;AACzD,cAAI,IAAI,KAAK,UAAU,EAAE,OAAO,eAAe,CAAC,CAAC;AAAA,QACnD;AAAA,MACF,CAAC;AAAA,IACH,CAAC;AAED,WAAO,GAAG,SAAS,CAAC,QAA+B;AACjD,UAAI,IAAI,SAAS,cAAc;AAC7B,eAAO,IAAI,MAAM,QAAQ,WAAW,kDAAkD,CAAC;AAAA,MACzF,OAAO;AACL,eAAO,GAAG;AAAA,MACZ;AAAA,IACF,CAAC;AAED,WAAO,OAAO,WAAW;AAAA,EAC3B,CAAC;AACH;AAEA,SAASE,aAAY,KAAmB;AACtC,QAAM,EAAE,UAAAC,UAAS,IAAI,UAAQ,eAAoB;AACjD,QAAM,OAAO,QAAQ;AACrB,QAAM,KAAK,CAAC,QAAsB;AAChC,QAAI,IAAK,SAAQ,IAAI,6BAA6B,GAAG,EAAE;AAAA,EACzD;AACA,MAAI,SAAS,SAAU,CAAAA,UAAS,QAAQ,CAAC,GAAG,GAAG,EAAE;AAAA,WACxC,SAAS,QAAS,CAAAA,UAAS,OAAO,CAAC,MAAM,SAAS,IAAI,GAAG,GAAG,EAAE;AAAA,MAClE,CAAAA,UAAS,YAAY,CAAC,GAAG,GAAG,EAAE;AACrC;AAEA,eAAe,8BAAqE;AAClF,QAAM,MAAM,GAAGF,oBAAmB,oBAAoB,WAAW;AACjE,UAAQ,IAAI,+CAA+C;AAC3D,EAAAC,aAAY,GAAG;AAEf,UAAQ,IAAI,uCAAuC;AACnD,QAAM,UAAU,MAAM,mBAAmB;AACzC,UAAQ,IAAI,6BAAwB;AAEpC,QAAM,QAAQ,MAAM,oBAAoB,EAAE,OAAO,QAAQ,CAAC;AAC1D,MAAI,MAAM,WAAW,GAAG;AACtB,YAAQ,IAAI,wCAAwC;AACpD,WAAO,CAAC;AAAA,EACV;AAEA,UAAQ,IAAI,WAAW,MAAM,MAAM;AAAA,CAAW;AAC9C,QAAM,QAAQ,CAAC,GAAQ,MAAc;AACnC,YAAQ,IAAI,OAAO,OAAO,IAAI,CAAC,EAAE,SAAS,CAAC,CAAC,KAAK,EAAE,SAAS,EAAE;AAAA,EAChE,CAAC;AACD,UAAQ,IAAI;AAEZ,QAAM,KAAK,gBAAgB,EAAE,OAAO,QAAQ,OAAO,QAAQ,QAAQ,OAAO,CAAC;AAC3E,MAAI;AACF,UAAM,YAAY,MAAM,IAAI,IAAI,wDAAwD;AACxF,UAAM,UAAU,UACb,MAAM,GAAG,EACT,IAAI,CAAC,MAAM,SAAS,EAAE,KAAK,GAAG,EAAE,IAAI,CAAC,EACrC,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC,KAAK,KAAK,KAAK,IAAI,MAAM,MAAM;AAExD,QAAI,QAAQ,WAAW,GAAG;AACxB,cAAQ,IAAI,sBAAsB;AAClC,aAAO,CAAC;AAAA,IACV;AAEA,WAAO,QAAQ,IAAI,CAAC,OAAO,EAAE,WAAW,MAAM,CAAC,EAAE,UAAU,EAAE;AAAA,EAC/D,UAAE;AACA,OAAG,MAAM;AAAA,EACX;AACF;AAEA,eAAsB,qBAAqB,MAA8C;AACvF,QAAM,YAAY,cAAc;AAEhC,MAAI,MAAM,YAAY,WAAW;AAC/B,YAAQ,IAAI,4BAA4B;AACxC,QAAI;AACF,YAAM,WAAW,MAAM,aAAa;AACpC,YAAM,gBAAgB,SAAS;AAAA,QAAK,CAAC,MACnC,EAAE,OAAO,KAAK,CAAC,MAAW,EAAE,cAAc,UAAU,QAAQ;AAAA,MAC9D;AACA,UAAI,CAAC,eAAe;AAClB,cAAM,cAAc,UAAU,WAAW,CAAC,EAAE,WAAW,UAAU,SAAS,CAAC,CAAC;AAC5E,gBAAQ,IAAI,6BAAwB,UAAU,SAAS,eAAe,UAAU,QAAQ,EAAE;AAAA,MAC5F,OAAO;AACL,gBAAQ,IAAI,YAAO,UAAU,QAAQ,yCAAyC;AAAA,MAChF;AAAA,IACF,SAAS,KAAK;AACZ,cAAQ,KAAK,iCAA6B,IAAc,OAAO,EAAE;AAAA,IACnE;AACA,YAAQ,IAAI;AACZ;AAAA,EACF;AAEA,QAAM,KAAK,gBAAgB,EAAE,OAAO,QAAQ,OAAO,QAAQ,QAAQ,OAAO,CAAC;AAE3E,MAAI;AACF,YAAQ,IAAI,4BAA4B;AACxC,UAAM,UAAoB,CAAC;AAC3B,QAAI,WAAW;AACb,cAAQ,KAAK,mBAAmB,UAAU,QAAQ,GAAG;AAAA,IACvD;AACA,YAAQ,KAAK,gCAAgC;AAC7C,YAAQ,KAAK,cAAc;AAE3B,YAAQ,QAAQ,CAAC,KAAK,MAAM;AAC1B,cAAQ,IAAI,KAAK,IAAI,CAAC,KAAK,GAAG,EAAE;AAAA,IAClC,CAAC;AACD,YAAQ,IAAI;AAEZ,UAAM,SAAS,MAAM,IAAI,IAAI,qBAAqB;AAClD,UAAM,YAAY,SAAS,OAAO,KAAK,GAAG,EAAE;AAC5C,YAAQ,IAAI;AACZ,OAAG,MAAM;AAET,UAAM,WAAW,YAAY,IAAI;AACjC,UAAM,YAAY,YAAY,IAAI;AAClC,UAAM,UAAU,YAAY,IAAI;AAEhC,QAAI,cAAc,YAAY,WAAW;AACvC,UAAI;AACF,cAAM,WAAW,MAAM,aAAa;AACpC,cAAM,gBAAgB,SAAS;AAAA,UAAK,CAAC,MACnC,EAAE,OAAO,KAAK,CAAC,MAAW,EAAE,cAAc,UAAU,QAAQ;AAAA,QAC9D;AACA,YAAI,CAAC,eAAe;AAClB,gBAAM,cAAc,UAAU,WAAW,CAAC,EAAE,WAAW,UAAU,SAAS,CAAC,CAAC;AAC5E,kBAAQ,IAAI,6BAAwB,UAAU,SAAS,eAAe,UAAU,QAAQ,EAAE;AAAA,QAC5F,OAAO;AACL,kBAAQ,IAAI,YAAO,UAAU,QAAQ,yCAAyC;AAAA,QAChF;AAAA,MACF,SAAS,KAAK;AACZ,gBAAQ,KAAK,iCAA6B,IAAc,OAAO,EAAE;AAAA,MACnE;AAAA,IACF,WAAW,cAAc,WAAW;AAClC,YAAM,gBAAgB,MAAM,4BAA4B;AACxD,UAAI,cAAc,SAAS,GAAG;AAC5B,YAAI;AACF,gBAAM,WAAW,MAAM,aAAa;AACpC,gBAAM,oBAAoB,IAAI;AAAA,YAC5B,SAAS,QAAQ,CAAC,OAAY,EAAE,SAAS,CAAC,GAAG,IAAI,CAAC,MAAW,EAAE,SAAS,CAAC;AAAA,UAC3E;AACA,gBAAM,WAAW,cAAc,OAAO,CAAC,MAAM,CAAC,kBAAkB,IAAI,EAAE,SAAS,CAAC;AAEhF,cAAI,SAAS,WAAW,GAAG;AACzB,oBAAQ,IAAI,iDAA4C;AAAA,UAC1D,OAAO;AACL,kBAAM,cAAc,SAAS,WAAW,IACpC,SAAS,CAAC,EAAE,UAAU,MAAM,GAAG,EAAE,IAAI,KAAK,YAC1C;AACJ,kBAAM,cAAc,aAAa,QAAQ;AACzC,oBAAQ,IAAI,mBAAc,SAAS,MAAM,wBAAwB,WAAW,GAAG;AAAA,UACjF;AAAA,QACF,SAAS,KAAK;AACZ,kBAAQ,KAAK,kCAA8B,IAAc,OAAO,EAAE;AAAA,QACpE;AAAA,MACF;AAAA,IACF,WAAW,cAAc,SAAS;AAChC,cAAQ,IAAI,sDAAsD;AAAA,IACpE,OAAO;AACL,cAAQ,IAAI,6CAA6C;AAAA,IAC3D;AAAA,EACF,QAAQ;AACN,OAAG,MAAM;AAAA,EACX;AACA,UAAQ,IAAI;AACd;AAjPA,IAcME,mBACAH,sBAGA;AAlBN;AAAA;AAAA;AAWA;AACA;AAEA,IAAMG,oBAAmB,QAAQ,IAAI;AACrC,IAAMH,uBAAuBG,qBAAoB,eAAe,KAAKA,iBAAgB,IACjFA,oBACA;AACJ,IAAM,cAAc;AAAA;AAAA;;;AClBpB;AAAA;AAAA;AAAA;AAAA;AAcA,SAAS,mBAAAC,wBAAuB;AAChC,SAAS,SAAS,OAAO,UAAU,cAAc;AACjD,SAAS,YAAAC,WAAU,SAAS,iBAAiB;AAC7C,SAAS,cAAAC,aAAY,gBAAAC,eAAc,cAAAC,mBAAkB;AACrD,SAAS,WAAAC,UAAS,YAAAC,iBAAgB;AAClC,SAAS,QAAAC,aAAY;AACrB,SAAS,YAAAC,iBAAgB;AAczB,SAAS,aAAqC;AAC5C,MAAI,CAACN,YAAW,WAAW,EAAG,QAAO,CAAC;AACtC,QAAM,MAA8B,CAAC;AACrC,aAAW,QAAQC,cAAa,aAAa,OAAO,EAAE,MAAM,IAAI,GAAG;AACjE,UAAM,IAAI,KAAK,KAAK;AACpB,QAAI,CAAC,KAAK,EAAE,WAAW,GAAG,EAAG;AAC7B,UAAM,KAAK,EAAE,QAAQ,GAAG;AACxB,QAAI,KAAK,EAAG,KAAI,EAAE,MAAM,GAAG,EAAE,EAAE,KAAK,CAAC,IAAI,EAAE,MAAM,KAAK,CAAC,EAAE,KAAK,EAAE,QAAQ,gBAAgB,EAAE;AAAA,EAC5F;AACA,SAAO;AACT;AAEA,eAAe,OAAO,IAAwC,GAAW,OAA6B,CAAC,GAAoB;AACzH,MAAI,KAAK,QAAQ;AACf,YAAQ,OAAO,MAAM,CAAC;AACtB,UAAM,SAAU,QAAQ,MAAc;AACtC,QAAK,QAAQ,MAAc,WAAY,CAAC,QAAQ,MAAc,WAAW,IAAI;AAC7E,WAAO,MAAM,IAAI,QAAgB,CAACM,aAAY;AAC5C,UAAI,QAAQ;AACZ,YAAM,SAAS,CAAC,SAAiB;AAC/B,cAAM,IAAI,KAAK,SAAS,OAAO;AAC/B,YAAI,MAAM,QAAQ,MAAM,QAAQ,MAAM,QAAQ;AAC5C,kBAAQ,MAAM,eAAe,QAAQ,MAAM;AAC3C,cAAK,QAAQ,MAAc,WAAY,CAAC,QAAQ,MAAc,WAAW,UAAU,KAAK;AACxF,kBAAQ,OAAO,MAAM,IAAI;AACzB,UAAAA,SAAQ,KAAK;AACb;AAAA,QACF;AACA,YAAI,MAAM,IAAK,SAAQ,KAAK,GAAG;AAC/B,YAAI,MAAM,UAAO,MAAM,MAAM;AAAE,kBAAQ,MAAM,MAAM,GAAG,EAAE;AAAG;AAAA,QAAQ;AACnE,iBAAS;AAAA,MACX;AACA,cAAQ,MAAM,GAAG,QAAQ,MAAM;AAAA,IACjC,CAAC;AAAA,EACH;AACA,SAAO,MAAM,GAAG,SAAS,CAAC;AAC5B;AAEA,SAASC,aAAY,KAAmB;AACtC,QAAM,KAAKJ,UAAS;AACpB,MAAI;AACJ,MAAIK;AACJ,UAAQ,IAAI;AAAA,IACV,KAAK;AAAU,YAAM;AAAQ,MAAAA,QAAO,CAAC,GAAG;AAAG;AAAA,IAC3C,KAAK;AAAS,YAAM;AAAO,MAAAA,QAAO,CAAC,MAAM,SAAS,IAAI,GAAG;AAAG;AAAA,IAC5D;AAAS,YAAM;AAAY,MAAAA,QAAO,CAAC,GAAG;AAAG;AAAA,EAC3C;AACA,EAAAH,UAAS,KAAKG,OAAM,MAAM;AAAA,EAAC,CAAC;AAC9B;AAEA,SAAS,MAAM,IAA2B;AACxC,SAAO,IAAI,QAAQ,OAAK,WAAW,GAAG,EAAE,CAAC;AAC3C;AAEA,SAAS,0BAA2C;AAClD,QAAM,UAAUJ,MAAK,YAAY,iBAAiB,KAAK,IAAI,CAAC,MAAM;AAClE,SAAO,IAAI,QAAQ,CAACE,UAAS,WAAW;AACtC,UAAM,OAAO,UAAU,UAAU,CAAC,MAAM,SAAS,UAAU,aAAa,GAAG;AAAA,MACzE,OAAO;AAAA,IACT,CAAC;AACD,SAAK,GAAG,SAAS,CAAC,QAAQ,OAAO,IAAI,MAAM,uCAAuC,IAAI,OAAO,EAAE,CAAC,CAAC;AACjG,SAAK,GAAG,SAAS,CAAC,SAAS;AACzB,UAAI,MAAM;AACV,UAAI;AAAE,cAAMN,cAAa,SAAS,OAAO;AAAA,MAAG,SAAS,GAAG;AACtD,eAAO,IAAI,MAAM,sCAAuC,EAAY,OAAO,EAAE,CAAC;AAC9E;AAAA,MACF;AACA,UAAI;AAAE,QAAAC,YAAW,OAAO;AAAA,MAAG,QAAQ;AAAA,MAAC;AACpC,UAAI,SAAS,GAAG;AAAE,eAAO,IAAI,MAAM,uCAAuC,IAAI,EAAE,CAAC;AAAG;AAAA,MAAQ;AAE5F,YAAM,WAAW;AACjB,UAAI,SAAS;AACb,UAAI;AACJ,cAAQ,IAAI,SAAS,KAAK,GAAG,OAAO,KAAM,WAAU,EAAE,CAAC;AACvD,YAAM,QAAQ,OAAO,QAAQ,OAAO,EAAE,EAAE,MAAM,6BAA6B;AAC3E,UAAI,CAAC,OAAO;AAAE,eAAO,IAAI,MAAM,2DAA2D,IAAI,MAAM,aAAa,OAAO,MAAM,IAAI,CAAC;AAAG;AAAA,MAAQ;AAC9I,MAAAK,SAAQ,MAAM,CAAC,CAAC;AAAA,IAClB,CAAC;AAAA,EACH,CAAC;AACH;AAEA,eAAe,QAAiB,YAAoBG,MAAa,MAAc,OAAoB,CAAC,GAAe;AACjH,QAAM,OAAO,MAAM,MAAM,GAAG,UAAU,GAAG,IAAI,IAAI;AAAA,IAC/C,GAAG;AAAA,IACH,SAAS;AAAA,MACP,iBAAiB,UAAUA,IAAG;AAAA,MAC9B,gBAAgB;AAAA,MAChB,GAAI,KAAK,WAAW,CAAC;AAAA,IACvB;AAAA,EACF,CAAC;AACD,MAAI,CAAC,KAAK,IAAI;AACZ,UAAM,OAAO,MAAM,KAAK,KAAK,EAAE,MAAM,MAAM,EAAE;AAC7C,UAAM,IAAI,MAAM,OAAO,KAAK,MAAM,KAAK,KAAK,MAAM,GAAG,GAAG,CAAC,EAAE;AAAA,EAC7D;AACA,SAAO,KAAK,KAAK;AACnB;AAMA,eAAsB,cAAc,YAAoBA,MAAa,OAA6B,CAAC,GAA2B;AAE5H,MAAI;AACF,UAAM,SAAS,MAAM;AAAA,MACnB;AAAA,MAAYA;AAAA,MAAK;AAAA,IACnB;AACA,QAAI,OAAO,aAAa,OAAO,OAAO;AACpC,UAAI,CAAC,KAAK,OAAQ,SAAQ,IAAI,+CAA0C;AACxE,aAAO,OAAO;AAAA,IAChB;AAAA,EACF,QAAQ;AAAA,EAAC;AAGT,MAAI,CAAC,KAAK,OAAQ,SAAQ,IAAI,0CAA0C;AACxE,MAAI;AACF,UAAM,WAAW,MAAM;AAAA,MACrB;AAAA,MAAYA;AAAA,MAAK;AAAA,MAAsC,EAAE,QAAQ,QAAQ,MAAM,KAAK;AAAA,IACtF;AACA,IAAAF,aAAY,SAAS,GAAG;AACxB,QAAI,CAAC,KAAK,OAAQ,SAAQ,IAAI,gCAAgC;AAAA,EAChE,SAAS,KAAK;AACZ,QAAI,CAAC,KAAK,OAAQ,SAAQ,MAAM,2CAA4C,IAAc,OAAO,EAAE;AACnG,WAAO;AAAA,EACT;AAGA,QAAM,WAAW,KAAK,IAAI,IAAI;AAC9B,SAAO,KAAK,IAAI,IAAI,UAAU;AAC5B,UAAM,MAAM,GAAI;AAChB,QAAI;AACF,YAAM,SAAS,MAAM;AAAA,QACnB;AAAA,QAAYE;AAAA,QAAK;AAAA,MACnB;AACA,UAAI,OAAO,aAAa,OAAO,OAAO;AACpC,YAAI,CAAC,KAAK,OAAQ,SAAQ,IAAI,8BAAyB;AACvD,eAAO,OAAO;AAAA,MAChB;AAAA,IACF,QAAQ;AAAA,IAAC;AACT,QAAI,CAAC,KAAK,OAAQ,SAAQ,OAAO,MAAM,GAAG;AAAA,EAC5C;AACA,MAAI,CAAC,KAAK,OAAQ,SAAQ,MAAM,iDAAiD;AACjF,SAAO;AACT;AAQA,eAAsB,mBAAmB,OAA2B,CAAC,GAAkB;AACrF,MAAI,CAAC,gBAAgB,GAAG;AACtB,YAAQ,MAAM,kDAAkD;AAChE,YAAQ,KAAK,CAAC;AAAA,EAChB;AACA,QAAM,SAAS,WAAW;AAC1B,QAAM,cAAc,OAAO,sBAAsB,QAAQ,IAAI,sBAAsB,yBAAyB,QAAQ,OAAO,EAAE;AAC7H,QAAMA,OAAM,eAAe;AAC3B,MAAI,CAACA,MAAK;AACR,YAAQ,MAAM,sFAAsF;AACpG,YAAQ,KAAK,CAAC;AAAA,EAChB;AAGA,UAAQ,IAAI,sCAAsC;AAClD,MAAI;AACJ,MAAI;AACF,UAAM,SAAS,MAAM;AAAA,MACnB;AAAA,MAAYA;AAAA,MAAK;AAAA,MAA0B,EAAE,QAAQ,QAAQ,MAAM,KAAK;AAAA,IAC1E;AACA,qBAAiB,OAAO;AACxB,YAAQ,IAAI,2BAAsB,eAAe,MAAM,GAAG,EAAE,CAAC,oBAAe,OAAO,WAAW,MAAM,GAAG,EAAE,CAAC,EAAE;AAAA,EAC9G,SAAS,KAAK;AACZ,YAAQ,MAAM,8BAA+B,IAAc,OAAO,EAAE;AACpE,YAAQ,KAAK,CAAC;AAAA,EAChB;AAGA,MAAI;AAEJ,MAAI,KAAK,aAAa;AACpB,cAAU,KAAK;AAAA,EACjB,WAAW,KAAK,gBAAgB;AAE9B,QAAI;AACF,YAAM,SAAS,MAAM;AAAA,QACnB;AAAA,QAAYA;AAAA,QAAK;AAAA,MACnB;AACA,UAAI,OAAO,aAAa,OAAO,OAAO;AACpC,kBAAU,OAAO;AAAA,MACnB,OAAO;AACL,cAAM,IAAI,MAAM,eAAe;AAAA,MACjC;AAAA,IACF,QAAQ;AACN,UAAI;AACF,kBAAUX,UAAS,iBAAiB,EAAE,UAAU,SAAS,SAAS,IAAK,CAAC,EAAE,KAAK;AAAA,MACjF,QAAQ;AACN,gBAAQ,MAAM,+EAA+E;AAC7F;AAAA,MACF;AAAA,IACF;AAAA,EACF,OAAO;AAEL,YAAQ,IAAI,2BAA2B;AACvC,UAAM,QAAQ,MAAM,cAAc,YAAYW,IAAG;AACjD,QAAI,CAAC,OAAO;AACV,cAAQ,MAAM,sCAAsC;AACpD,cAAQ,KAAK,CAAC;AAAA,IAChB;AACA,cAAU;AACV,YAAQ,IAAI;AAAA,EACd;AAGA,MAAI;AACJ,MAAI,CAAC,KAAK,iBAAiB;AACzB,YAAQ,IAAI,uCAAuC;AACnD,YAAQ,IAAI,2EAAsE;AAClF,QAAI;AACF,oBAAc,MAAM,wBAAwB;AAAA,IAC9C,SAAS,KAAK;AACZ,cAAQ,MAAM,+BAA+B,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,CAAC,EAAE;AAC/F,UAAI,KAAK,eAAgB;AACzB,cAAQ,KAAK,CAAC;AAAA,IAChB;AACA,QAAI,CAAC,YAAY,WAAW,eAAe,GAAG;AAC5C,cAAQ,MAAM,6EAA6E;AAC3F,UAAI,KAAK,eAAgB;AACzB,cAAQ,KAAK,CAAC;AAAA,IAChB;AACA,YAAQ,IAAI,uBAAuB;AACnC,QAAI;AACF,YAAM,iBAAiBX;AAAA,QACrB;AAAA,QACA,EAAE,KAAK,EAAE,GAAG,QAAQ,KAAK,yBAAyB,YAAY,GAAG,UAAU,SAAS,SAAS,KAAQ,OAAO,CAAC,UAAU,QAAQ,MAAM,EAAE;AAAA,MACzI;AACA,YAAM,SAAS,KAAK,MAAM,cAAc;AACxC,UAAI,OAAO,SAAU,OAAM,IAAI,MAAM,OAAO,UAAU,aAAa;AACnE,cAAQ,IAAI,6BAAwB;AAAA,IACtC,SAAS,KAAK;AACZ,cAAQ,MAAM,4BAA4B,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,CAAC,EAAE;AAC5F,UAAI,KAAK,eAAgB;AACzB,cAAQ,KAAK,CAAC;AAAA,IAChB;AAAA,EACF;AAGA,MAAI;AACJ,MAAI,KAAK,gBAAgB;AACvB,QAAI,kBAAiC;AACrC,QAAI;AACF,YAAM,YAAYA,UAAS,6BAA6B,EAAE,UAAU,SAAS,SAAS,IAAK,CAAC,EAAE,KAAK;AACnG,YAAM,IAAI,UAAU,MAAM,qCAAqC;AAC/D,UAAI,EAAG,mBAAkB,EAAE,CAAC;AAAA,IAC9B,QAAQ;AAAA,IAAC;AACT,QAAI,CAAC,iBAAiB;AACpB,cAAQ,KAAK,wDAAmD;AAChE;AAAA,IACF;AACA,UAAM,CAAC,OAAO,IAAI,IAAI,gBAAgB,MAAM,GAAG;AAC/C,eAAW,CAAC,EAAE,OAAO,MAAM,WAAW,gBAAgB,CAAC;AACvD,YAAQ,IAAI,yBAAyB,eAAe,EAAE;AAAA,EACxD,OAAO;AACL,YAAQ,IAAI,8BAA8B;AAC1C,UAAM,QAAQ,MAAM,oBAAoB,EAAE,OAAO,QAAS,CAAC;AAC3D,QAAI,MAAM,WAAW,GAAG;AACtB,cAAQ,MAAM,2DAA2D;AACzE,cAAQ,KAAK,CAAC;AAAA,IAChB;AACA,YAAQ,IAAI;AAAA,QAAW,MAAM,MAAM;AAAA,CAAwB;AAC3D,UAAM,MAAM,GAAG,GAAG,EAAE,QAAQ,CAAC,GAAG,MAAM;AACpC,cAAQ,IAAI,KAAK,OAAO,IAAI,CAAC,EAAE,SAAS,CAAC,CAAC,MAAM,EAAE,SAAS,EAAE;AAAA,IAC/D,CAAC;AACD,YAAQ,IAAI;AACZ,UAAM,MAAMD,iBAAgB,EAAE,OAAO,OAAO,CAAC;AAC7C,UAAM,eAAe,MAAM,OAAO,KAAK,gEAAgE;AACvG,UAAM,cAAc,aACjB,MAAM,GAAG,EACT,IAAI,CAAC,MAAM,SAAS,EAAE,KAAK,GAAG,EAAE,IAAI,CAAC,EACrC,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC,KAAK,KAAK,KAAK,IAAI,MAAM,MAAM;AACxD,QAAI,YAAY,WAAW,GAAG;AAC5B,cAAQ,MAAM,sBAAsB;AACpC,UAAI,MAAM;AACV,cAAQ,KAAK,CAAC;AAAA,IAChB;AACA,eAAW,YAAY,IAAI,CAAC,MAAM,MAAM,CAAC,CAAC;AAC1C,YAAQ,IAAI;AAAA,uBAA0B,SAAS,MAAM,WAAW;AAChE,eAAW,KAAK,SAAU,SAAQ,IAAI,YAAO,EAAE,SAAS,EAAE;AAC1D,YAAQ,IAAI;AACZ,UAAM,WAAW,MAAM,OAAO,KAAK,sBAAsB,GAAG,KAAK,EAAE,YAAY;AAC/E,QAAI,YAAY,SAAS,YAAY,KAAK;AACxC,cAAQ,IAAI,YAAY;AACxB,UAAI,MAAM;AACV,cAAQ,KAAK,CAAC;AAAA,IAChB;AACA,QAAI,MAAM;AAAA,EACZ;AAGA,UAAQ,IAAI;AACZ,aAAW,KAAK,UAAU;AACxB,YAAQ,OAAO,MAAM,sBAAsB,EAAE,SAAS,MAAM;AAC5D,QAAI;AACF,YAAM;AAAA,QACJ,EAAE,OAAO,QAAS;AAAA,QAClB,EAAE;AAAA,QACF,EAAE;AAAA,QACF;AAAA,UACE,sBAAsB;AAAA,UACtB,cAAc;AAAA,QAChB;AAAA,MACF;AACA,cAAQ,IAAI,QAAG;AAAA,IACjB,SAAS,KAAK;AACZ,cAAQ,IAAI,WAAO,IAAc,OAAO,GAAG;AAAA,IAC7C;AAAA,EACF;AAGA,UAAQ,IAAI;AACZ,QAAM,UAAU,YAAY,QAAQ,IAAI,CAAC;AACzC,MAAI,SAAS;AACX,UAAM,UAAU,kBAAkB,OAAO;AACzC,QAAI,SAAS;AACX,cAAQ,IAAI,mBAAmB,OAAO,EAAE;AACxC,cAAQ,IAAI,2CAA2C;AAAA,IACzD;AAAA,EACF,OAAO;AACL,YAAQ,IAAI,oEAAoE;AAChF,YAAQ,IAAI,WAAW,sBAAsB,EAAE;AAC/C,YAAQ,IAAI,yFAAyF;AAAA,EACvG;AAEA,UAAQ,IAAI;AACZ,UAAQ,IAAI,gCAA2B;AACvC,UAAQ,IAAI,mBAAmB,aAAa,YAAY,KAAK,aAAa,cAAc,EAAE;AAC1F,UAAQ,IAAI,mEAAmE;AACjF;AApXA,IA+BM,YACA;AAhCN;AAAA;AAAA;AAqBA;AAQA;AAEA,IAAM,aAAaO,MAAKF,SAAQ,GAAG,SAAS;AAC5C,IAAM,cAAcE,MAAK,YAAY,YAAY;AAAA;AAAA;;;ACtBjD,eAAsB,kBAAkB,MAMrC;AACD,QAAM,MAAM,GAAG,KAAK,WAAW,QAAQ,OAAO,EAAE,CAAC;AACjD,QAAM,OAAO,MAAM,MAAM,KAAK;AAAA,IAC5B,QAAQ;AAAA,IACR,SAAS;AAAA,MACP,iBAAiB,UAAU,KAAK,GAAG;AAAA,MACnC,cAAc;AAAA,IAChB;AAAA,IACA,QAAQ,YAAY,QAAQ,GAAI;AAAA,EAClC,CAAC;AAED,MAAI,CAAC,KAAK,IAAI;AACZ,WAAO,EAAE,SAAS,UAAU;AAAA,EAC9B;AAEA,QAAM,OAAO,MAAM,KAAK,KAAK;AAC7B,SAAO,EAAE,SAAS,KAAK,SAAS,WAAW,UAAU;AACvD;AAjCA;AAAA;AAAA;AAAA;AAAA;;;ACYA,SAAS,cAAAM,aAAY,gBAAAC,qBAAoB;AACzC,SAAS,WAAAC,gBAAe;AACxB,SAAS,QAAAC,aAAY;AAId,SAAS,mBAA4B;AAC1C,MAAI,CAACH,YAAWI,YAAW,EAAG,QAAO;AACrC,MAAI;AACF,UAAM,UAAUH,cAAaG,cAAa,OAAO;AACjD,UAAM,QAAQ,QAAQ,MAAM,oCAAoC;AAChE,WAAO,QAAQ,CAAC,MAAM;AAAA,EACxB,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AA3BA,IAgBMA;AAhBN;AAAA;AAAA;AAgBA,IAAMA,eAAcD,MAAKD,SAAQ,GAAG,WAAW,YAAY;AAAA;AAAA;;;AChB3D,IAuBa;AAvBb;AAAA;AAAA;AAuBO,IAAM,wBAAwB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACXrC,SAAS,cAAAG,aAAY,aAAAC,YAAW,iBAAAC,gBAAe,gBAAAC,eAAc,WAAW,cAAc,cAAAC,aAAY,cAAAC,aAAY,UAAU,WAAW,iBAAiB;AACpJ,SAAS,QAAAC,aAAY;AACrB,SAAS,WAAAC,gBAAe;AACxB,SAAS,iBAAiB;AA6L1B,SAAS,mBAAyB;AAChC,EAAAN,WAAU,aAAa,EAAE,WAAW,KAAK,CAAC;AAC1C,EAAAA,WAAU,qBAAqB,EAAE,WAAW,KAAK,CAAC;AAClD,EAAAC,eAAc,aAAa,uBAAuB,OAAO;AACzD,YAAU,aAAa,GAAK;AAC5B,EAAAA,eAAc,iBAAiB,qBAAqB,OAAO;AAC3D,EAAAA;AAAA,IACE;AAAA,IACA,KAAK,UAAU;AAAA,MACb,UAAU;AAAA,MACV,uBAAuB,CAAC,cAAc;AAAA,IACxC,GAAG,MAAM,CAAC,IAAI;AAAA,IACd;AAAA,EACF;AACA,EAAAA,eAAc,iBAAiB,mBAAmB,OAAO;AACzD,YAAU,iBAAiB,GAAK;AAGhC,EAAAD,WAAU,eAAe,EAAE,WAAW,KAAK,CAAC;AAC5C,EAAAA,WAAU,uBAAuB,EAAE,WAAW,KAAK,CAAC;AACpD,EAAAC,eAAc,eAAe,uBAAuB,OAAO;AAC3D,YAAU,eAAe,GAAK;AAC9B,EAAAA,eAAc,mBAAmB,qBAAqB,OAAO;AAC7D,EAAAA;AAAA,IACE;AAAA,IACA,KAAK,UAAU;AAAA,MACb,UAAU;AAAA,MACV,uBAAuB,CAAC,cAAc;AAAA,IACxC,GAAG,MAAM,CAAC,IAAI;AAAA,IACd;AAAA,EACF;AACA,EAAAA,eAAc,mBAAmB,qBAAqB,OAAO;AAC7D,YAAU,mBAAmB,GAAK;AACpC;AAEA,SAAS,gBAAsB;AAC7B,aAAW,OAAO,CAAC,aAAa,aAAa,GAAG;AAC9C,UAAM,IAAI,UAAU,OAAO,CAAC,WAAW,UAAU,GAAG;AAAA,MAClD,KAAK;AAAA,MACL,UAAU;AAAA,MACV,SAAS;AAAA,IACX,CAAC;AACD,QAAI,EAAE,WAAW,GAAG;AAClB,YAAM,IAAI;AAAA,QACR,yBAAyB,GAAG,KAAK,EAAE,UAAU,EAAE,UAAU,SAAS;AAAA,MACpE;AAAA,IACF;AAAA,EACF;AACF;AAoBA,SAAS,uBAAuB,SAA8C;AAC5E,MAAI,CAACF,YAAW,gBAAgB,GAAG;AAEjC;AAAA,EACF;AAGA,QAAM,eAAeG,cAAa,kBAAkB,OAAO;AAC3D,MAAI;AACJ,MAAI;AACF,aAAS,KAAK,MAAM,YAAY;AAAA,EAClC,SAAS,KAAK;AACZ,UAAM,IAAI;AAAA,MACR,gCAAgC,gBAAgB,KAAM,IAAc,OAAO;AAAA,MAE3E;AAAA,IACF;AAAA,EACF;AAEA,QAAM,eAAe,IAAI,IAAI,OAAO,KAAK,MAAM,CAAC;AAGhD,QAAM,QAAQ,QAAQ,MAAM;AAC5B,MAAI,CAAC,MAAO;AAGZ,aAAW,KAAK,cAAc;AAC5B,QAAI,EAAE,KAAK,SAAS;AAClB,YAAM,IAAI;AAAA,QACR,qBAAqB,gBAAgB,oCAAoC,CAAC;AAAA,MAE5E;AAAA,IACF;AAAA,EACF;AAEA,QAAM,UAAU,KAAK,UAAU,QAAQ,MAAM,CAAC,IAAI;AAGlD,MAAI;AACF,SAAK,MAAM,OAAO;AAAA,EACpB,SAAS,KAAK;AACZ,UAAM,IAAI;AAAA,MACR,qBAAqB,gBAAgB;AAAA,MAErC;AAAA,IACF;AAAA,EACF;AAGA,eAAa,kBAAkB,uBAAuB;AAItD,QAAM,UAAU,GAAG,gBAAgB,eAAe,QAAQ,GAAG;AAC7D,MAAI;AACF,IAAAD,eAAc,SAAS,SAAS,OAAO;AAEvC,UAAM,KAAK,SAAS,SAAS,GAAG;AAChC,QAAI;AAAE,gBAAU,EAAE;AAAA,IAAG,UAAE;AAAU,gBAAU,EAAE;AAAA,IAAG;AAChD,IAAAE,YAAW,SAAS,gBAAgB;AAAA,EACtC,SAAS,KAAK;AAEZ,QAAI;AAAE,MAAAC,YAAW,OAAO;AAAA,IAAG,QAAQ;AAAA,IAAe;AAClD,QAAI;AAAE,mBAAa,yBAAyB,gBAAgB;AAAA,IAAG,QAAQ;AAAA,IAAe;AACtF,UAAM,IAAI;AAAA,MACR,mBAAmB,gBAAgB,KAAM,IAAc,OAAO,eACjD,uBAAuB;AAAA,MACpC;AAAA,IACF;AAAA,EACF;AACF;AAQA,SAAS,sBAA4B;AACnC,QAAM,MAAM;AAAA,IACV,YAAY;AAAA,MACV,CAAC,eAAe,GAAG;AAAA,QACjB,SAAS;AAAA,QACT,MAAM,CAAC,WAAW;AAAA,MACpB;AAAA,IACF;AAAA,EACF;AACA,EAAAH,eAAc,kBAAkB,KAAK,UAAU,KAAK,MAAM,CAAC,IAAI,MAAM,OAAO;AAG5E,QAAM,OAAO;AAAA,IACX,YAAY;AAAA,MACV,CAAC,eAAe,GAAG;AAAA,QACjB,SAAS;AAAA,QACT,MAAM,CAAC,aAAa;AAAA,MACtB;AAAA,IACF;AAAA,EACF;AACA,EAAAA,eAAc,oBAAoB,KAAK,UAAU,MAAM,MAAM,CAAC,IAAI,MAAM,OAAO;AACjF;AAWA,SAAS,kBAAwB;AAC/B,yBAAuB,YAAU;AAC/B,QAAI,QAAQ;AAGZ,QAAI,OAAO,cAAc,OAAO,OAAO,eAAe,YAAY,OAAO,WAAW,eAAe,GAAG;AACpG,aAAO,OAAO,WAAW,eAAe;AACxC,cAAQ;AAAA,IACV;AAEA,QAAI,CAAC,OAAO,YAAY,OAAO,OAAO,aAAa,UAAU;AAC3D,aAAO,WAAW,CAAC;AAAA,IACrB;AACA,UAAM,WAAW,OAAO;AAExB,eAAW,OAAO,CAAC,aAAa,aAAa,GAAG;AAC9C,YAAM,WAAW,SAAS,GAAG,KAAK,OAAO,SAAS,GAAG,MAAM,WACvD,SAAS,GAAG,IACZ,CAAC;AACL,YAAM,cAAc,MAAM,KAAK,oBAAI,IAAI;AAAA,QACrC,GAAK,SAAS,yBAAkD,CAAC;AAAA,QACjE;AAAA,MACF,CAAC,CAAC;AACF,YAAM,OAAO;AAAA,QACX,GAAG;AAAA,QACH,wBAAwB;AAAA,QACxB,+BAA+B;AAAA,QAC/B,uBAAuB;AAAA,MACzB;AACA,UACE,SAAS,2BAA2B,QACpC,SAAS,kCAAkC,QAC3C,KAAK,UAAU,SAAS,yBAAyB,CAAC,CAAC,MAAM,KAAK,UAAU,WAAW,GACnF;AACA,iBAAS,GAAG,IAAI;AAChB,gBAAQ;AAAA,MACV;AAAA,IACF;AACA,WAAO;AAAA,EACT,CAAC;AACH;AAOO,SAAS,iBAA6D;AAE3E,MAAI,WAAW,UAAU,OAAO,CAAC,WAAW,GAAG,EAAE,UAAU,QAAQ,CAAC;AACpE,MAAI,SAAS,WAAW,GAAG;AACzB,QAAI,QAAQ,aAAa,UAAU;AACjC,cAAQ,IAAI,8BAA8B;AAC1C,YAAM,QAAQ,UAAU,QAAQ,CAAC,WAAW,iBAAiB,GAAG,EAAE,UAAU,SAAS,OAAO,WAAW,SAAS,KAAQ,CAAC;AACzH,UAAI,MAAM,WAAW,GAAG;AACtB,cAAM,IAAI,oBAAoB,qFAAqF;AAAA,MACrH;AACA,iBAAW,UAAU,OAAO,CAAC,WAAW,GAAG,EAAE,UAAU,QAAQ,CAAC;AAChE,UAAI,SAAS,WAAW,GAAG;AACzB,cAAM,IAAI,oBAAoB,gFAAgF;AAAA,MAChH;AAAA,IACF,OAAO;AACL,YAAM,IAAI,oBAAoB,uEAAuE;AAAA,IACvG;AAAA,EACF;AAEA,mBAAiB;AACjB,gBAAc;AACd,sBAAoB;AACpB,kBAAgB;AAEhB,SAAO,EAAE,YAAY,aAAa,YAAY,YAAY;AAC5D;AAcO,SAAS,mBAAyB;AACvC,yBAAuB,YAAU;AAC/B,QAAI,QAAQ;AACZ,QAAI,OAAO,cAAc,OAAO,WAAW,eAAe,GAAG;AAC3D,aAAO,OAAO,WAAW,eAAe;AACxC,cAAQ;AAAA,IACV;AACA,eAAW,OAAO,CAAC,aAAa,aAAa,GAAG;AAC9C,UAAI,OAAO,YAAY,OAAO,OAAO,aAAa,YAAY,OAAO,SAAS,GAAG,GAAG;AAClF,eAAO,OAAO,SAAS,GAAG;AAC1B,gBAAQ;AAAA,MACV;AAAA,IACF;AACA,WAAO;AAAA,EACT,CAAC;AACH;AAneA,IAkBa,yBAEA,aACA,aACA,iBACA,qBACA,sBACA,kBACA,kBACA,iBACA,mBAEA,eACA,eACA,mBACA,uBACA,wBACA,oBACA,mBACA,qBACA,gBAkBP,mBAoEA,qBAyDA,iBAEA,qBAcO;AArMb;AAAA;AAAA;AAgBA;AAEO,IAAM,0BAA0BI,MAAKC,SAAQ,GAAG,yBAAyB;AAEzE,IAAM,cAAcD,MAAKC,SAAQ,GAAG,WAAW,aAAa;AAC5D,IAAM,cAAcD,MAAK,aAAa,mBAAmB;AACzD,IAAM,kBAAkBA,MAAK,aAAa,cAAc;AACxD,IAAM,sBAAsBA,MAAK,aAAa,SAAS;AACvD,IAAM,uBAAuBA,MAAK,qBAAqB,eAAe;AACtE,IAAM,mBAAmBA,MAAK,aAAa,WAAW;AACtD,IAAM,mBAAmBA,MAAKC,SAAQ,GAAG,cAAc;AACvD,IAAM,kBAAkBD,MAAK,aAAa,eAAe;AACzD,IAAM,oBAAoB;AAE1B,IAAM,gBAAgBA,MAAKC,SAAQ,GAAG,WAAW,eAAe;AAChE,IAAM,gBAAgBD,MAAK,eAAe,mBAAmB;AAC7D,IAAM,oBAAoBA,MAAK,eAAe,cAAc;AAC5D,IAAM,wBAAwBA,MAAK,eAAe,SAAS;AAC3D,IAAM,yBAAyBA,MAAK,uBAAuB,eAAe;AAC1E,IAAM,qBAAqBA,MAAK,eAAe,WAAW;AAC1D,IAAM,oBAAoBA,MAAK,eAAe,eAAe;AAC7D,IAAM,sBAAsB;AAC5B,IAAM,iBAAiB;AAkB9B,IAAM,oBAAoB;AAAA;AAAA;AAAA;AAAA,UAIhB,iBAAiB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAgE3B,IAAM,sBAAsB;AAAA,oEACwC,cAAc;AAAA;AAAA;AAAA,UAGxE,mBAAmB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,yCAoBY,cAAc;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,yBAM9B,cAAc;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,+CAkBQ,cAAc;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAS7D,IAAM,kBAAkB;AAExB,IAAM,sBAAsB,KAAK;AAAA,MAC/B;AAAA,QACE,MAAM;AAAA,QACN,SAAS;AAAA,QACT,SAAS;AAAA,QACT,MAAM;AAAA,QACN,cAAc;AAAA,UACZ,6BAA6B;AAAA,QAC/B;AAAA,MACF;AAAA,MACA;AAAA,MACA;AAAA,IACF,IAAI;AAEG,IAAM,sBAAN,cAAkC,MAAM;AAAA,MAC7C,YAAY,SAAiC,OAAiB;AAC5D,cAAM,OAAO;AAD8B;AAE3C,aAAK,OAAO;AAAA,MACd;AAAA,MAH6C;AAAA,IAI/C;AAAA;AAAA;;;AClMA,SAAS,cAAc,aAAAE,YAAW,aAAa;AAC/C,SAAS,WAAAC,gBAAe;AACxB,SAAS,QAAAC,aAAY;AACrB,SAAS,eAAe;AA6BxB,SAAS,iBAAuB;AAC9B,QAAM,IAAIF,WAAU,SAAS,CAAC,WAAW,GAAG,EAAE,UAAU,QAAQ,CAAC;AACjE,MAAI,EAAE,WAAW,GAAG;AAClB,UAAM,IAAI,WAAW,mGAAmG;AAAA,EAC1H;AACF;AAEA,SAAS,aAA0B;AACjC,iBAAe;AACf,QAAM,IAAIA,WAAU,SAAS,CAAC,UAAU,QAAQ,GAAG,EAAE,UAAU,QAAQ,CAAC;AACxE,MAAI,EAAE,WAAW,GAAG;AAClB,UAAM,IAAI,WAAW,wBAAwB,EAAE,UAAU,EAAE,UAAU,eAAe,4BAAuB;AAAA,EAC7G;AACA,MAAI;AACF,WAAO,KAAK,MAAM,EAAE,MAAM;AAAA,EAC5B,SAAS,KAAK;AACZ,UAAM,IAAI,WAAW,0CAA0C,EAAE,OAAO,MAAM,GAAG,GAAG,CAAC,IAAI,GAAG;AAAA,EAC9F;AACF;AAUA,SAAS,WAAW,GAAuC;AACzD,MAAI,OAAO,MAAM,SAAU,QAAO;AAClC,MAAI,KAAK,OAAO,MAAM,UAAU;AAC9B,QAAI,aAAa,EAAG,QAAO;AAC3B,QAAI,UAAU,GAAG;AACf,YAAM,SAAU,EAA8C,MAAM;AACpE,UAAI,OAAO,WAAW,SAAU,QAAO,SAAS,MAAM;AACtD,UAAI,UAAU,OAAO,WAAW,SAAU,QAAO,SAAS,OAAO,KAAK,MAAM,EAAE,CAAC,KAAK,SAAS;AAC7F,aAAO;AAAA,IACT;AACA,WAAO,OAAO,KAAK,CAAC,EAAE,CAAC,KAAK;AAAA,EAC9B;AACA,SAAO;AACT;AAYO,SAAS,SAAS,UAA2B,iBAAkC;AACpF,QAAM,OAAO,WAAW;AACxB,aAAW,CAAC,IAAI,CAAC,KAAK,OAAO,QAAQ,KAAK,KAAK,GAAG;AAChD,QAAI,EAAE,UAAU,QAAQ,WAAW;AACjC,aAAO;AAAA,QACL,IAAI,OAAO,EAAE;AAAA,QACb,OAAO,EAAE;AAAA,QACT,QAAQ,WAAW,EAAE,MAAM;AAAA,QAC3B,SAAS,EAAE;AAAA,QACX,KAAK,EAAE;AAAA,MACT;AAAA,IACF;AAAA,EACF;AACA,SAAO;AACT;AAsBO,SAAS,UAAU,OAAqB,CAAC,GAAa;AAC3D,QAAM,KAAK,KAAK,WAAW;AAC3B,QAAM,MAAM,KAAK,OAAO,GAAG;AAE3B,MAAI,WAAW,SAAS,EAAE;AAC1B,SAAO,UAAU;AACf,QAAI,SAAS,WAAW,aAAa,SAAS,WAAW,UAAU;AACjE,MAAAA,WAAU,QAAQ,CAAC,gBAAgB,MAAM,IAAI,GAAG,WAAW,EAAE,GAAG,EAAE,UAAU,QAAQ,CAAC;AACrF,MAAAA,WAAU,SAAS,CAAC,QAAQ,OAAO,SAAS,EAAE,CAAC,GAAG,EAAE,UAAU,QAAQ,CAAC;AACvE,eAAS,IAAI,GAAG,IAAI,IAAI,KAAK;AAC3B,cAAM,QAAQ,SAAS,EAAE;AACzB,YAAI,CAAC,SAAS,MAAM,OAAO,SAAS,MAAO,MAAM,WAAW,aAAa,MAAM,WAAW,SAAW;AACrG,QAAAA,WAAU,SAAS,CAAC,KAAK,GAAG,EAAE,UAAU,QAAQ,CAAC;AAAA,MACnD;AAAA,IACF;AACA,IAAAA,WAAU,SAAS,CAAC,UAAU,OAAO,SAAS,EAAE,CAAC,GAAG,EAAE,UAAU,QAAQ,CAAC;AACzE,eAAW,SAAS,EAAE;AAAA,EACxB;AAEA,QAAM,YAAYE,MAAK,KAAK,eAAe;AAC3C,QAAMC,QAAO;AAAA,IACX;AAAA,IACA;AAAA,IAAW,GAAG;AAAA,IACd;AAAA,IAAuB;AAAA,IACvB;AAAA,IACA;AAAA,IAAQ;AAAA,EACV;AAEA,QAAM,IAAIH,WAAU,SAASG,OAAM,EAAE,UAAU,QAAQ,CAAC;AACxD,MAAI,EAAE,WAAW,GAAG;AAClB,UAAM,IAAI,WAAW,qBAAqB,EAAE,UAAU,EAAE,MAAM,EAAE;AAAA,EAClE;AACA,QAAM,UAAU,SAAS,EAAE;AAC3B,MAAI,CAAC,SAAS;AACZ,UAAM,IAAI,WAAW,8CAA8C,GAAG,SAAS,QAAQ;AAAA,EACzF;AACA,SAAO;AACT;AAIO,SAAS,SAAS,UAA2B,iBAAuB;AACzE,EAAAH,WAAU,QAAQ,CAAC,gBAAgB,MAAM,IAAI,QAAQ,WAAW,EAAE,GAAG,EAAE,UAAU,QAAQ,CAAC;AAE1F,MAAI,IAAI,SAAS,OAAO;AACxB,SAAO,GAAG;AACR,QAAI,EAAE,WAAW,aAAa,EAAE,WAAW,UAAU;AACnD,MAAAA,WAAU,SAAS,CAAC,QAAQ,OAAO,EAAE,EAAE,CAAC,GAAG,EAAE,UAAU,QAAQ,CAAC;AAEhE,eAAS,IAAI,GAAG,IAAI,IAAI,KAAK;AAC3B,cAAM,QAAQ,SAAS,OAAO;AAC9B,YAAI,CAAC,SAAS,MAAM,OAAO,EAAE,MAAO,MAAM,WAAW,aAAa,MAAM,WAAW,SAAW;AAC9F,QAAAA,WAAU,SAAS,CAAC,KAAK,GAAG,EAAE,UAAU,QAAQ,CAAC;AAAA,MACnD;AAAA,IACF;AACA,IAAAA,WAAU,SAAS,CAAC,UAAU,OAAO,EAAE,EAAE,CAAC,GAAG,EAAE,UAAU,QAAQ,CAAC;AAClE,QAAI,SAAS,OAAO;AAAA,EACtB;AACF;AAGO,SAAS,SAAS,QAAQ,IAAI,UAA2B,iBAAyB;AACvF,QAAM,IAAI,SAAS,OAAO;AAC1B,MAAI,CAAC,EAAG,QAAO,OAAO,QAAQ,SAAS;AACvC,QAAM,IAAIA,WAAU,SAAS,CAAC,OAAO,WAAW,OAAO,KAAK,GAAG,OAAO,EAAE,EAAE,CAAC,GAAG,EAAE,UAAU,QAAQ,CAAC;AACnG,SAAO,EAAE,UAAU,EAAE,UAAU;AACjC;AAGO,SAAS,cAAc,OAAqB,CAAC,GAAa;AAC/D,QAAM,KAAK,KAAK,WAAW;AAC3B,QAAM,IAAI,SAAS,EAAE;AACrB,MAAI,KAAK,EAAE,WAAW,UAAW,QAAO;AACxC,SAAO,UAAU,IAAI;AACvB;AAGA,SAAS,UAAU,MAAc,MAAc,YAAY,KAAuB;AAChF,SAAO,IAAI,QAAQ,CAAAI,aAAW;AAC5B,UAAM,OAAO,QAAQ,MAAM,IAAI;AAC/B,UAAM,OAAO,CAAC,OAAgB;AAC5B,UAAI;AAAE,aAAK,QAAQ;AAAA,MAAG,QAAQ;AAAA,MAAa;AAC3C,MAAAA,SAAQ,EAAE;AAAA,IACZ;AACA,SAAK,KAAK,WAAW,MAAM,KAAK,IAAI,CAAC;AACrC,SAAK,KAAK,SAAS,MAAM,KAAK,KAAK,CAAC;AACpC,SAAK,WAAW,WAAW,MAAM,KAAK,KAAK,CAAC;AAAA,EAC9C,CAAC;AACH;AAIA,SAAS,mBAAmB,cAAsB,cAAoB;AACpE,EAAAJ,WAAU,QAAQ,CAAC,aAAa,MAAM,aAAa,GAAG,GAAG,EAAE,UAAU,QAAQ,CAAC;AAC9E,EAAAA,WAAU,QAAQ,CAAC,aAAa,MAAM,aAAa,OAAO,GAAG,EAAE,UAAU,QAAQ,CAAC;AACpF;AASA,eAAsB,oBACpB,MACA,YAAY,KACZ,OAAO,aACP,cAAsB,cACJ;AAClB,QAAM,WAAW,KAAK,IAAI,IAAI;AAC9B,SAAO,KAAK,IAAI,IAAI,UAAU;AAC5B,QAAI,MAAM,UAAU,MAAM,IAAI,EAAG,QAAO;AACxC,uBAAmB,WAAW;AAC9B,UAAM,IAAI,QAAQ,OAAK,WAAW,GAAG,GAAI,CAAC;AAAA,EAC5C;AACA,SAAO,UAAU,MAAM,IAAI;AAC7B;AAEA,SAAS,YAAY,KAAsB;AACzC,QAAM,OAAOA,WAAU,QAAQ,CAAC,WAAW,GAAG,EAAE,UAAU,QAAQ,CAAC;AACnE,MAAI,KAAK,WAAW,EAAG,QAAO;AAC9B,UAAQ,IAAI,gBAAgB,GAAG,cAAc;AAC7C,QAAM,IAAIA,WAAU,QAAQ,CAAC,WAAW,GAAG,GAAG,EAAE,UAAU,SAAS,OAAO,WAAW,SAAS,KAAQ,CAAC;AACvG,SAAO,EAAE,WAAW;AACtB;AAGO,SAAS,uBAA6B;AAC3C,MAAI,IAAIA,WAAU,SAAS,CAAC,WAAW,GAAG,EAAE,UAAU,QAAQ,CAAC;AAC/D,MAAI,EAAE,WAAW,GAAG;AAClB,QAAI,QAAQ,aAAa,YAAY,YAAY,OAAO,GAAG;AACzD,UAAIA,WAAU,SAAS,CAAC,WAAW,GAAG,EAAE,UAAU,QAAQ,CAAC;AAC3D,UAAI,EAAE,WAAW,EAAG,OAAM,IAAI,WAAW,uDAAuD;AAAA,IAClG,OAAO;AACL,YAAM,IAAI,WAAW,6FAA6F;AAAA,IACpH;AAAA,EACF;AAEA,QAAM,SAASA,WAAU,SAAS,CAAC,UAAU,QAAQ,GAAG,EAAE,UAAU,SAAS,SAAS,IAAM,CAAC;AAC7F,MAAI,OAAO,WAAW,GAAG;AACvB,YAAQ,IAAI,6BAA6B;AACzC,UAAM,QAAQ,MAAM,UAAU,CAAC,IAAI,GAAG,EAAE,OAAO,UAAU,UAAU,KAAK,CAAC;AACzE,UAAM,MAAM;AAEZ,IAAAA,WAAU,SAAS,CAAC,GAAG,CAAC;AACxB,UAAM,QAAQA,WAAU,SAAS,CAAC,UAAU,QAAQ,GAAG,EAAE,UAAU,SAAS,SAAS,IAAM,CAAC;AAC5F,QAAI,MAAM,WAAW,GAAG;AACtB,YAAM,IAAI,WAAW,4EAA4E;AAAA,IACnG;AAAA,EACF;AACA,EAAAA,WAAU,SAAS,CAAC,YAAY,GAAG,GAAG,EAAE,UAAU,QAAQ,CAAC;AAC7D;AAGO,SAAS,wBAA8B;AAC5C,QAAM,IAAIA,WAAU,UAAU,CAAC,WAAW,GAAG,EAAE,UAAU,QAAQ,CAAC;AAClE,MAAI,EAAE,WAAW,GAAG;AAClB,UAAM,IAAI,WAAW,8FAA8F;AAAA,EACrH;AACF;AAGO,SAAS,sBAA4B;AAC1C,MAAI,IAAIA,WAAU,QAAQ,CAAC,IAAI,GAAG,EAAE,UAAU,QAAQ,CAAC;AACvD,MAAI,EAAE,WAAW,GAAG;AAClB,QAAI,QAAQ,aAAa,YAAY,YAAY,MAAM,GAAG;AACxD,UAAIA,WAAU,QAAQ,CAAC,IAAI,GAAG,EAAE,UAAU,QAAQ,CAAC;AACnD,UAAI,EAAE,WAAW,EAAG,OAAM,IAAI,WAAW,sDAAsD;AAAA,IACjG,OAAO;AACL,YAAM,IAAI,WAAW,mFAAmF;AAAA,IAC1G;AAAA,EACF;AACF;AA9SA,IAaa,YACA,cACAK,cAEA,cACA,gBACAC,gBAEA,YAoEA,iBACA;AA1Fb;AAAA;AAAA;AAaO,IAAM,aAAa;AACnB,IAAM,eAAe;AACrB,IAAMD,eAAcH,MAAKD,SAAQ,GAAG,WAAW,aAAa;AAE5D,IAAM,eAAe;AACrB,IAAM,iBAAiB;AACvB,IAAMK,iBAAgBJ,MAAKD,SAAQ,GAAG,WAAW,eAAe;AAEhE,IAAM,aAAN,cAAyB,MAAM;AAAA,MACpC,YAAY,SAAiC,OAAiB;AAC5D,cAAM,OAAO;AAD8B;AAE3C,aAAK,OAAO;AAAA,MACd;AAAA,MAH6C;AAAA,IAI/C;AA+DO,IAAM,kBAAmC,EAAE,WAAW,YAAY,aAAa,cAAc,YAAYI,aAAY;AACrH,IAAM,oBAAqC,EAAE,WAAW,cAAc,aAAa,gBAAgB,YAAYC,eAAc;AAAA;AAAA;;;ACpFpI,SAAS,gBAAAC,qBAAoB;AAC7B,SAAS,WAAAC,gBAAe;AACxB,SAAS,QAAAC,aAAY;AAarB,eAAe,eAAyC;AACtD,MAAIC,OAAM;AACV,MAAI,aAAa;AACjB,MAAI;AACF,UAAM,QAAQ,KAAK,MAAMH,cAAa,YAAY,OAAO,CAAC;AAC1D,IAAAG,OAAM,MAAM,gBAAgB;AAC5B,iBAAa,MAAM,eAAe;AAAA,EACpC,QAAQ;AACN,UAAM,IAAI,MAAM,mDAAmD;AAAA,EACrE;AACA,MAAI,CAACA,KAAK,OAAM,IAAI,MAAM,8CAA8C;AAExE,QAAM,OAAO,MAAM,MAAM,GAAG,UAAU,6BAA6B;AAAA,IACjE,SAAS,EAAE,eAAe,UAAUA,IAAG,GAAG;AAAA,IAC1C,QAAQ,YAAY,QAAQ,GAAI;AAAA,EAClC,CAAC;AACD,MAAI,CAAC,KAAK,GAAI,OAAM,IAAI,MAAM,4BAA4B,KAAK,MAAM,EAAE;AACvE,SAAO,KAAK,KAAK;AACnB;AAEA,eAAe,UAAU,MAAoC;AAC3D,QAAM,UAAU,MAAM,aAAa;AACnC,QAAM,SAAS,SAAS,eAAe,QAAQ,qBAC3C,SAAS,eAAe,QAAQ,qBAChC,SAAS,cAAc,QAAQ,oBAC/B,QAAQ;AACZ,MAAI,CAAC,QAAQ;AACX,UAAM,IAAI,MAAM,uBAAuB,IAAI,sBAAsB;AAAA,EACnE;AACA,SAAO;AACT;AAUA,eAAsB,oBAAoB,MAAmB,SAAkC;AAC7F,QAAM,SAAS,MAAM,UAAU,IAAI;AACnC,SAAO,GAAG,MAAM;AAAA;AAAA,EAEhB,0BAA0B;AAAA;AAAA;AAAA;AAAA;AAAA,EAK1B,OAAO;AACT;AAvEA,IAYM,YAyCA;AArDN;AAAA;AAAA;AAYA,IAAM,aAAaD,MAAKD,SAAQ,GAAG,WAAW,kBAAkB;AAyChE,IAAM,6BAA6B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;AC3CnC,SAAS,gBAAgB,cAAAG,aAAY,aAAAC,YAAW,YAAAC,WAAU,gBAAAC,eAAc,UAAU,aAAAC,YAAW,UAAU,WAAW,mBAAmB;AACrI,SAAS,WAAAC,UAAS,QAAAC,cAAY;AAC9B,SAAS,WAAAC,iBAAe;AA0BxB,SAAS,SAAS,GAAW,MAAM,aAAqB;AACtD,MAAI,EAAE,UAAU,IAAK,QAAO;AAC5B,SAAO,EAAE,MAAM,GAAG,GAAG,IAAI,eAAU,EAAE,SAAS,OAAO;AACvD;AAGA,SAAS,gBAAgB,QAAoC;AAE3D,QAAM,IAAI,OAAO,MAAM,oEAAoE;AAC3F,MAAI,CAAC,EAAG,QAAO;AACf,MAAI;AACF,UAAM,MAAM,KAAK,MAAM,EAAE,CAAC,CAAC;AAC3B,QAAI,IAAI,SAAU,QAAO,OAAO,IAAI,QAAQ;AAC5C,QAAI,OAAO,IAAI,OAAO,UAAW,QAAO,IAAI,KAAK,OAAO;AACxD,QAAI,IAAI,KAAM,QAAO,OAAO,IAAI,IAAI;AACpC,QAAI,IAAI,QAAS,QAAO,OAAO,IAAI,OAAO;AAAA,EAC5C,QAAQ;AAAA,EAER;AACA,SAAO;AACT;AAGO,SAAS,WAAWC,OAOlB;AACP,MAAI;AACF,IAAAP,WAAUI,SAAQ,aAAa,GAAG,EAAE,WAAW,KAAK,CAAC;AACrD,UAAM,QAAmB;AAAA,MACvB,IAAI,IAAI,KAAKG,MAAK,SAAS,EAAE,YAAY;AAAA,MACzC,MAAMA,MAAK;AAAA,MACX,aAAa,KAAK,IAAI,IAAIA,MAAK;AAAA,MAC/B,QAAQA,MAAK;AAAA,MACb,iBAAiB,SAASA,MAAK,OAAO;AAAA,MACtC,kBAAkBA,MAAK,SAAS,SAASA,MAAK,MAAM,IAAI;AAAA,MACxD,UAAUA,MAAK,SAAS,gBAAgBA,MAAK,MAAM,IAAI;AAAA,MACvD,OAAOA,MAAK;AAAA,IACd;AACA,mBAAe,eAAe,KAAK,UAAU,KAAK,IAAI,MAAM,OAAO;AAAA,EACrE,QAAQ;AAAA,EAER;AACF;AAMO,SAAS,gBAAgB,IAAI,IAAiB;AACnD,MAAI,CAACR,YAAW,aAAa,EAAG,QAAO,CAAC;AACxC,MAAI;AAGF,UAAM,OAAO,SAAS,aAAa,EAAE;AACrC,QAAI,SAAS,EAAG,QAAO,CAAC;AACxB,UAAM,OAAOG,cAAa,eAAe,OAAO;AAChD,UAAM,QAAQ,KAAK,MAAM,IAAI,EAAE,OAAO,OAAO;AAC7C,UAAM,QAAQ,MAAM,MAAM,CAAC,CAAC,EAAE,QAAQ;AACtC,WAAO,MACJ,IAAI,UAAQ;AACX,UAAI;AAAE,eAAO,KAAK,MAAM,IAAI;AAAA,MAAgB,QAAQ;AAAE,eAAO;AAAA,MAAM;AAAA,IACrE,CAAC,EACA,OAAO,CAAC,MAAsB,MAAM,IAAI;AAAA,EAC7C,QAAQ;AACN,WAAO,CAAC;AAAA,EACV;AACF;AAOO,SAAS,YAAY,SAA6C;AAGvE,MAAI;AACF,IAAAF,WAAUI,SAAQ,aAAa,GAAG,EAAE,WAAW,KAAK,CAAC;AACrD,QAAI,CAACL,YAAW,aAAa,GAAG;AAC9B,qBAAe,eAAe,IAAI,OAAO;AAAA,IAC3C;AAAA,EACF,QAAQ;AAAA,EAER;AAEA,MAAI,YAAY,MAAM;AACpB,QAAI;AAAE,aAAO,SAAS,aAAa,EAAE;AAAA,IAAM,QAAQ;AAAE,aAAO;AAAA,IAAG;AAAA,EACjE,GAAG;AACH,MAAI,iBAAiB;AAErB,QAAM,gBAAgB,CAAC,MAAc,OAAqB;AACxD,QAAI,MAAM,KAAM;AAChB,QAAI,KAAoB;AACxB,QAAI;AACF,WAAKE,UAAS,eAAe,GAAG;AAChC,YAAM,MAAM,KAAK;AACjB,YAAM,MAAM,OAAO,MAAM,GAAG;AAC5B,eAAS,IAAI,KAAK,GAAG,KAAK,IAAI;AAC9B,YAAM,OAAO,iBAAiB,IAAI,SAAS,OAAO;AAClD,YAAM,cAAc,KAAK,YAAY,IAAI;AACzC,UAAI,gBAAgB,IAAI;AACtB,yBAAiB;AACjB;AAAA,MACF;AACA,YAAM,WAAW,KAAK,MAAM,GAAG,WAAW;AAC1C,uBAAiB,KAAK,MAAM,cAAc,CAAC;AAC3C,iBAAW,QAAQ,SAAS,MAAM,IAAI,GAAG;AACvC,YAAI,CAAC,KAAM;AACX,YAAI;AACF,kBAAQ,KAAK,MAAM,IAAI,CAAc;AAAA,QACvC,QAAQ;AAAA,QAER;AAAA,MACF;AAAA,IACF,QAAQ;AAAA,IAER,UAAE;AACA,UAAI,OAAO,MAAM;AACf,YAAI;AAAE,UAAAE,WAAU,EAAE;AAAA,QAAG,QAAQ;AAAA,QAAa;AAAA,MAC5C;AAAA,IACF;AAAA,EACF;AAIA,YAAU,eAAe,EAAE,UAAU,IAAI,GAAG,CAAC,MAAM,SAAS;AAC1D,QAAI,KAAK,OAAO,UAAU;AAExB,iBAAW;AACX,uBAAiB;AAAA,IACnB;AACA,QAAI,KAAK,OAAO,UAAU;AACxB,oBAAc,UAAU,KAAK,IAAI;AACjC,iBAAW,KAAK;AAAA,IAClB;AAAA,EACF,CAAC;AAED,SAAO,MAAM,YAAY,aAAa;AACxC;AArLA,IAea,eAqBP;AApCN;AAAA;AAAA;AAeO,IAAM,gBAAgBE,OAAKC,UAAQ,GAAG,WAAW,eAAe,WAAW;AAqBlF,IAAM,cAAc;AAAA;AAAA;;;AC5BpB,SAAS,WAAW,mBAAmB;AACvC,SAAS,WAAAE,gBAAe;AA4BxB,eAAsB,gBACpB,MACA,SACA,OAAsB,CAAC,GACN;AACjB,QAAM,UAAU,MAAM,oBAAoB,MAAM,OAAO;AACvD,QAAM,OAAO,KAAK,UAAU,EAAE,MAAM,QAAQ,CAAC;AAC7C,QAAM,YAAY,KAAK,aAAa;AACpC,QAAM,OAAO,KAAK,QAAQ;AAC1B,QAAM,YAAY,KAAK,IAAI;AAE3B,MAAI;AACF,UAAM,SAAS,MAAM,IAAI,QAAgB,CAACC,UAAS,WAAW;AAC5D,YAAM,MAAM,YAAY;AAAA,QACtB,MAAM;AAAA,QACN;AAAA,QACA,QAAQ;AAAA,QACR,MAAM;AAAA,QACN,SAAS;AAAA,UACP,gBAAgB;AAAA,UAChB,kBAAkB,OAAO,WAAW,IAAI;AAAA,QAC1C;AAAA,QACA,SAAS;AAAA,MACX,GAAG,SAAO;AACR,cAAM,SAAmB,CAAC;AAC1B,YAAI,GAAG,QAAQ,OAAK,OAAO,KAAK,CAAC,CAAC;AAClC,YAAI,GAAG,OAAO,MAAM;AAClB,gBAAM,OAAO,OAAO,OAAO,MAAM,EAAE,SAAS,OAAO;AACnD,cAAI,IAAI,eAAe,KAAK;AAC1B,mBAAO,IAAI,aAAa,oBAAoB,IAAI,UAAU,KAAK,KAAK,MAAM,GAAG,GAAG,CAAC,EAAE,CAAC;AACpF;AAAA,UACF;AACA,cAAI;AACF,kBAAM,SAAS,KAAK,MAAM,IAAI;AAC9B,gBAAI,OAAO,OAAO;AAChB,qBAAO,IAAI,aAAa,OAAO,KAAK,CAAC;AACrC;AAAA,YACF;AACA,YAAAA,SAAQ,OAAO,OAAO,UAAU,EAAE,CAAC;AAAA,UACrC,SAAS,KAAK;AACZ,mBAAO,IAAI,aAAa,+BAA+B,KAAK,MAAM,GAAG,GAAG,CAAC,IAAI,GAAG,CAAC;AAAA,UACnF;AAAA,QACF,CAAC;AAAA,MACH,CAAC;AACD,UAAI,GAAG,WAAW,MAAM;AACtB,YAAI,QAAQ,IAAI,aAAa,mCAAmC,SAAS,IAAI,CAAC;AAAA,MAChF,CAAC;AACD,UAAI,GAAG,SAAS,SAAO;AACrB,cAAM,MAAO,IAA8B,SAAS,iBAChD,iCAAiC,YAAY,IAAI,YAAY,kCAC7D,2BAA4B,IAAc,OAAO;AACrD,eAAO,IAAI,aAAa,KAAK,GAAG,CAAC;AAAA,MACnC,CAAC;AACD,UAAI,MAAM,IAAI;AACd,UAAI,IAAI;AAAA,IACV,CAAC;AACD,eAAW,EAAE,WAAW,MAAM,SAAS,SAAS,QAAQ,QAAQ,KAAK,CAAC;AACtE,WAAO;AAAA,EACT,SAAS,KAAK;AACZ,UAAM,UAAW,IAAc,WAAW,OAAO,GAAG;AACpD,UAAM,SAAS,aAAa,KAAK,OAAO,IAAI,YAAY;AACxD,eAAW,EAAE,WAAW,MAAM,SAAS,SAAS,QAAQ,OAAO,QAAQ,CAAC;AACxE,UAAM;AAAA,EACR;AACF;AAGO,SAAS,mBAAmB,OAAO,cAAc,YAAY,KAAuB;AACzF,SAAO,IAAI,QAAQ,CAAAA,aAAW;AAC5B,UAAM,OAAOD,SAAQ,MAAM,YAAY;AACvC,UAAM,OAAO,CAAC,OAAgB;AAC5B,UAAI;AAAE,aAAK,QAAQ;AAAA,MAAG,QAAQ;AAAA,MAAa;AAC3C,MAAAC,SAAQ,EAAE;AAAA,IACZ;AACA,SAAK,KAAK,WAAW,MAAM,KAAK,IAAI,CAAC;AACrC,SAAK,KAAK,SAAS,MAAM,KAAK,KAAK,CAAC;AACpC,SAAK,WAAW,WAAW,MAAM,KAAK,KAAK,CAAC;AAAA,EAC9C,CAAC;AACH;AAnHA,IAaa,cACA,cAEP,oBAEO;AAlBb;AAAA;AAAA;AAUA;AACA;AAEO,IAAM,eAAe;AACrB,IAAM,eAAe,SAAS,QAAQ,IAAI,uBAAuB,QAAQ,EAAE;AAElF,IAAM,qBAAqB;AAEpB,IAAM,eAAN,cAA2B,MAAM;AAAA,MACtC,YAAY,SAAiC,OAAiB;AAC5D,cAAM,OAAO;AAD8B;AAE3C,aAAK,OAAO;AAAA,MACd;AAAA,MAH6C;AAAA,IAI/C;AAAA;AAAA;;;ACvBA;AAAA;AAAA;AAAA;AAAA;AASA,SAAS,cAAAC,cAAY,aAAAC,YAAW,iBAAAC,gBAAe,aAAAC,YAAW,gBAAAC,gBAAc,aAAa,kBAAAC,iBAAgB,cAAAC,mBAAkB;AACvH,SAAS,WAAAC,iBAAe;AACxB,SAAS,QAAAC,cAAY;AACrB,SAAS,YAAAC,WAAU,aAAAC,YAAW,SAAAC,cAAa;AAC3C,SAAS,mBAAAC,wBAAuB;AAyChC,SAAS,yBAAyB,KAA6C;AAC7E,MAAI,CAAC,IAAK,QAAO;AACjB,SAAO,eAAe,KAAK,GAAG,IAAI,MAAM;AAC1C;AAEO,SAAS,UAAU,MAAgC;AACxD,QAAM,OAAuB,CAAC;AAC9B,aAAW,KAAK,MAAM;AACpB,QAAI,EAAE,WAAW,YAAY,EAAG,MAAK,SAAS,EAAE,MAAM,aAAa,MAAM;AAAA,aAChE,EAAE,WAAW,YAAY,EAAG,MAAK,aAAa,EAAE,MAAM,aAAa,MAAM;AAAA,aACzE,MAAM,cAAe,MAAK,WAAW;AAAA,aACrC,MAAM,WAAY,MAAK,QAAQ;AAAA,aAC/B,MAAM,aAAa,MAAM,KAAM,MAAK,QAAQ;AAAA,aAC5C,MAAM,cAAe,MAAK,WAAW;AAAA,EAChD;AACA,MAAI,CAAC,KAAK,YAAY;AACpB,UAAM,UAAU,yBAAyB,QAAQ,IAAI,kBAAkB;AACvE,QAAI,QAAS,MAAK,aAAa;AAAA,EACjC;AAIA,SAAO;AACT;AAEA,eAAe,0BAA4C;AACzD,QAAM,KAAKA,iBAAgB,EAAE,OAAO,QAAQ,OAAO,QAAQ,QAAQ,OAAO,CAAC;AAC3E,SAAO,IAAI,QAAQ,CAACC,aAAY;AAC9B,OAAG;AAAA,MACD;AAAA,MAEA,CAAC,WAAW;AACV,WAAG,MAAM;AACT,cAAM,UAAU,OAAO,KAAK,EAAE,YAAY;AAC1C,QAAAA,SAAQ,YAAY,MAAM,YAAY,OAAO,YAAY,KAAK;AAAA,MAChE;AAAA,IACF;AAAA,EACF,CAAC;AACH;AAIA,SAAS,kBAAwB;AAC/B,EAAAZ,WAAUa,aAAY,EAAE,WAAW,KAAK,CAAC;AACzC,EAAAb,WAAU,WAAW,EAAE,WAAW,KAAK,CAAC;AACxC,EAAAA,WAAU,SAAS,EAAE,WAAW,KAAK,CAAC;AACtC,EAAAA,WAAU,aAAa,EAAE,WAAW,KAAK,CAAC;AAC5C;AAEA,SAAS,mBAeP;AACA,QAAM,iBAAiBO,OAAK,WAAW,kBAAkB;AACzD,QAAM,yBAAyBA,OAAK,WAAW,qBAAqB;AACpE,QAAM,yBAAyBA,OAAK,WAAW,qBAAqB;AACpE,QAAM,wBAAwBA,OAAK,WAAW,oBAAoB;AAClE,QAAM,wBAAwBA,OAAK,WAAW,oBAAoB;AAClE,QAAM,sBAAsBA,OAAK,WAAW,kBAAkB;AAC9D,QAAM,uBAAuBA,OAAK,WAAW,mBAAmB;AAChE,QAAM,wBAAwBA,OAAK,WAAW,oBAAoB;AAClE,QAAM,yBAAyBA,OAAK,WAAW,qBAAqB;AACpE,QAAM,2BAA2BA,OAAK,WAAW,uBAAuB;AACxE,QAAM,6BAA6BA,OAAK,WAAW,0BAA0B;AAC7E,QAAM,mBAAmBA,OAAK,WAAW,mBAAmB;AAC5D,QAAM,uBAAuBA,OAAK,WAAW,mBAAmB;AAChE,QAAM,sBAAsBA,OAAK,WAAW,sBAAsB;AAClE,QAAM,wBAAwBA,OAAK,WAAW,wBAAwB;AACtE,QAAM,qBAAqBA,OAAK,WAAW,qBAAqB;AAEhE,EAAAN,eAAc,gBAAgB,eAAe,OAAO;AACpD,EAAAA,eAAc,wBAAwB,kBAAkB,OAAO;AAC/D,EAAAA,eAAc,wBAAwB,kBAAkB,OAAO;AAC/D,EAAAA,eAAc,uBAAuB,iBAAiB,OAAO;AAC7D,EAAAA,eAAc,uBAAuB,iBAAiB,OAAO;AAC7D,EAAAA,eAAc,qBAAqB,eAAe,OAAO;AACzD,EAAAA,eAAc,sBAAsB,gBAAgB,OAAO;AAC3D,EAAAA,eAAc,uBAAuB,iBAAiB,OAAO;AAC7D,EAAAA,eAAc,wBAAwB,kBAAkB,OAAO;AAC/D,EAAAA,eAAc,0BAA0B,oBAAoB,OAAO;AACnE,EAAAA,eAAc,4BAA4B,uBAAuB,OAAO;AACxE,EAAAA,eAAc,kBAAkB,kBAAkB,OAAO;AACzD,EAAAA,eAAc,sBAAsB,sBAAsB,OAAO;AACjE,EAAAA,eAAc,qBAAqB,sBAAsB,OAAO;AAChE,EAAAA,eAAc,uBAAuB,wBAAwB,OAAO;AACpE,EAAAA,eAAc,oBAAoB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAA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","names":["cmd","existsSync","readFileSync","writeFileSync","renameSync","mkdirSync","dirname","homedir","isSynkroEntry","SYNKRO_MARKER","removeSynkroEntries","existsSync","readFileSync","writeFileSync","renameSync","mkdirSync","homedir","dirname","join","SYNKRO_MARKER","url","writeFileSync","readFileSync","existsSync","mkdirSync","unlinkSync","homedir","join","dirname","args","resolve","existsSync","mkdirSync","writeFileSync","execSync","join","execSync","createServer","resolve","SYNKRO_WEB_AUTH_URL","openBrowser","execFile","RAW_WEB_AUTH_URL","createInterface","execSync","existsSync","readFileSync","unlinkSync","homedir","platform","join","execFile","resolve","openBrowser","args","jwt","existsSync","readFileSync","homedir","join","CONFIG_PATH","existsSync","mkdirSync","writeFileSync","readFileSync","renameSync","unlinkSync","join","homedir","spawnSync","homedir","join","args","resolve","SESSION_DIR","SESSION_DIR_2","readFileSync","homedir","join","jwt","existsSync","mkdirSync","openSync","readFileSync","closeSync","dirname","join","homedir","args","connect","resolve","existsSync","mkdirSync","writeFileSync","chmodSync","readFileSync","appendFileSync","renameSync","homedir","join","execSync","spawnSync","spawn","createInterface","resolve","SYNKRO_DIR","CONFIG_PATH","detectGitRepo","token","profile","setupGithubCommand","init_install","args","info","existsSync","readFileSync","homedir","join","CONFIG_PATH","HOOKS_DIR","SYNKRO_DIR","createInterface","ask","resolve","readFileSync","writeFileSync","existsSync","join","homedir","readConfigEnv","CONFIG_PATH","args","config","SYNKRO_DIR","execSync","spawn","readFileSync","existsSync","join","args","resolve","prompt","init_install","existsSync","homedir","join","args","SYNKRO_DIR","init_install","spawnSync","homedir","join","existsSync","readFileSync","writeFileSync","CONFIG_PATH","updateLocalInferenceFlag","jwt","resolve","args","resolve","args","readFileSync","existsSync","resolve","printHelp","installCommand","parseArgs","loginCommand","logoutCommand","statusCommand","linkCommand","unlinkCommand","configCommand","setupGithubCommand","scanPrCommand","updateCommand","disconnectCommand","uninstallCommand","reinstallCommand","localCcCommand","gradeCommand"]}
|
|
1
|
+
{"version":3,"sources":["../cli/installer/agentDetect.ts","../cli/installer/ccHookConfig.ts","../cli/installer/cursorHookConfig.ts","../cli/installer/mcpConfig.ts","../cli/installer/hookScripts.ts","../cli/installer/hookScriptsTs.ts","../cli/auth/stub.ts","../cli/auth/index.ts","../cli/api/projects.ts","../cli/installer/workflowTemplate.ts","../cli/installer/githubSetup.ts","../cli/commands/repoConnect.ts","../cli/commands/setupGithub.ts","../cli/installer/promptFetcher.ts","../cli/local-cc/settings.ts","../cli/local-cc/channelSource.ts","../cli/local-cc/install.ts","../cli/local-cc/pueue.ts","../cli/local-cc/prompts.ts","../cli/local-cc/turnLog.ts","../cli/local-cc/client.ts","../cli/commands/install.ts","../cli/commands/login.ts","../cli/commands/logout.ts","../cli/commands/status.ts","../cli/commands/link.ts","../cli/commands/unlink.ts","../cli/commands/config.ts","../cli/commands/scanPr.ts","../cli/commands/update.ts","../cli/commands/disconnect.ts","../cli/commands/uninstall.ts","../cli/commands/reinstall.ts","../cli/commands/localCc.ts","../cli/commands/grade.ts","../cli/bootstrap.js"],"sourcesContent":["/**\n * Detect which AI coding agents are installed on the user's machine.\n *\n * Returns a list of agents with their config paths so the installer\n * knows where to write hook configs.\n */\nimport { existsSync } from 'node:fs';\nimport { homedir } from 'node:os';\nimport { join } from 'node:path';\nimport { execSync } from 'node:child_process';\n\nexport type AgentKind = 'claude_code' | 'codex' | 'cursor';\n\nexport interface DetectedAgent {\n kind: AgentKind;\n name: string;\n binaryPath?: string;\n configDir: string;\n settingsPath: string;\n version?: string;\n}\n\nfunction which(cmd: string): string | undefined {\n try {\n const result = execSync(`which ${cmd}`, { encoding: 'utf-8' }).trim();\n return result || undefined;\n } catch {\n return undefined;\n }\n}\n\nfunction getVersion(cmd: string): string | undefined {\n try {\n const result = execSync(`${cmd} --version 2>&1`, { encoding: 'utf-8', timeout: 5000 }).trim();\n return result.split('\\n')[0];\n } catch {\n return undefined;\n }\n}\n\nexport function detectAgents(): DetectedAgent[] {\n const agents: DetectedAgent[] = [];\n const home = homedir();\n\n // Claude Code\n const claudeBinary = which('claude');\n const claudeConfigDir = join(home, '.claude');\n if (claudeBinary || existsSync(claudeConfigDir)) {\n agents.push({\n kind: 'claude_code',\n name: 'Claude Code',\n binaryPath: claudeBinary,\n configDir: claudeConfigDir,\n settingsPath: join(claudeConfigDir, 'settings.json'),\n version: claudeBinary ? getVersion('claude') : undefined,\n });\n }\n\n // Codex (OpenAI's CLI)\n const codexBinary = which('codex');\n const codexConfigDir = join(home, '.codex');\n if (codexBinary || existsSync(codexConfigDir)) {\n agents.push({\n kind: 'codex',\n name: 'Codex',\n binaryPath: codexBinary,\n configDir: codexConfigDir,\n settingsPath: join(codexConfigDir, 'config.toml'),\n version: codexBinary ? getVersion('codex') : undefined,\n });\n }\n\n // Cursor\n const cursorBinary = which('cursor');\n const cursorConfigDir = join(home, '.cursor');\n if (cursorBinary || existsSync(cursorConfigDir)) {\n agents.push({\n kind: 'cursor',\n name: 'Cursor',\n binaryPath: cursorBinary,\n configDir: cursorConfigDir,\n settingsPath: join(cursorConfigDir, 'hooks.json'),\n version: cursorBinary ? getVersion('cursor') : undefined,\n });\n }\n\n return agents;\n}\n\nexport function findClaudeAuth(): { path: string; exists: boolean } {\n // CC stores OAuth token in macOS keychain (item: \"Claude Code-credentials\")\n // or in ~/.claude/auth.json on Linux. We don't extract it; we ask user to\n // run `claude setup-token` for headless use.\n const authJsonPath = join(homedir(), '.claude', 'auth.json');\n return { path: authJsonPath, exists: existsSync(authJsonPath) };\n}\n","// :)\n/**\n * Atomically merge Synkro hook entries into ~/.claude/settings.json.\n *\n * Preserves any other hooks the user has configured (Corridor, Noma, custom\n * scripts, etc.) — we only add our entries, never replace the file.\n */\nimport { existsSync, readFileSync, writeFileSync, renameSync, mkdirSync, unlinkSync } from 'node:fs';\nimport { dirname } from 'node:path';\n\nexport interface SynkroHookConfig {\n bashJudgeScriptPath: string;\n bashFollowupScriptPath: string;\n editPrecheckScriptPath: string;\n cwePrecheckScriptPath: string;\n cvePrecheckScriptPath: string;\n planJudgeScriptPath: string;\n agentJudgeScriptPath: string;\n stopSummaryScriptPath: string;\n sessionStartScriptPath: string;\n transcriptSyncScriptPath: string;\n userPromptSubmitScriptPath: string;\n skipTranscriptSync?: boolean;\n}\n\nconst SYNKRO_MARKER = '__synkro_managed__';\n\ninterface HookEntry {\n matcher?: string;\n hooks: Array<Record<string, unknown>>;\n [SYNKRO_MARKER]?: boolean;\n}\n\ninterface CCSettings {\n hooks?: {\n PreToolUse?: HookEntry[];\n PostToolUse?: HookEntry[];\n SessionEnd?: HookEntry[];\n SessionStart?: HookEntry[];\n Stop?: HookEntry[];\n [k: string]: unknown;\n };\n [k: string]: unknown;\n}\n\nfunction readSettings(path: string): CCSettings {\n if (!existsSync(path)) return {};\n try {\n const raw = readFileSync(path, 'utf-8');\n return JSON.parse(raw) as CCSettings;\n } catch (err) {\n throw new Error(`Failed to parse ${path}: ${(err as Error).message}`);\n }\n}\n\nfunction writeSettingsAtomic(path: string, settings: CCSettings): void {\n mkdirSync(dirname(path), { recursive: true });\n const tmpPath = `${path}.synkro.tmp`;\n writeFileSync(tmpPath, JSON.stringify(settings, null, 2) + '\\n', 'utf-8');\n renameSync(tmpPath, path);\n}\n\nfunction isSynkroEntry(entry: any): boolean {\n if (entry?.[SYNKRO_MARKER]) return true;\n const hooks = Array.isArray(entry?.hooks) ? entry.hooks : [];\n return hooks.some((h: any) =>\n typeof h?.command === 'string' && h.command.includes('/.synkro/hooks/'),\n );\n}\n\nfunction removeSynkroEntries(events: CCSettings['hooks'] extends infer H ? H : never, eventName: string): void {\n if (!events) return;\n const arr = (events as any)[eventName];\n if (!Array.isArray(arr)) return;\n (events as any)[eventName] = arr.filter((entry: any) => !isSynkroEntry(entry));\n}\n\n/**\n * Merge Synkro hooks into the settings file. Idempotent — replaces any\n * existing Synkro-managed entries with the new versions.\n */\nexport function installCCHooks(settingsPath: string, config: SynkroHookConfig): void {\n const settings = readSettings(settingsPath);\n settings.hooks = settings.hooks ?? {};\n\n // Remove any prior Synkro entries (to support `synkro update`)\n removeSynkroEntries(settings.hooks as any, 'PreToolUse');\n removeSynkroEntries(settings.hooks as any, 'PostToolUse');\n removeSynkroEntries(settings.hooks as any, 'SessionEnd');\n removeSynkroEntries(settings.hooks as any, 'SessionStart');\n removeSynkroEntries(settings.hooks as any, 'UserPromptSubmit');\n // Also clean up any older `Stop`-event entry from earlier v1.6 builds.\n removeSynkroEntries(settings.hooks as any, 'Stop');\n\n settings.hooks.PreToolUse = settings.hooks.PreToolUse ?? [];\n settings.hooks.PostToolUse = settings.hooks.PostToolUse ?? [];\n settings.hooks.SessionEnd = settings.hooks.SessionEnd ?? [];\n settings.hooks.SessionStart = settings.hooks.SessionStart ?? [];\n settings.hooks.UserPromptSubmit = (settings.hooks.UserPromptSubmit as any[]) ?? [];\n\n // PreToolUse Bash/Read/Grep/Glob → command hook script (Cerebras-judged)\n settings.hooks.PreToolUse.push({\n matcher: 'Bash|Read|Grep|Glob',\n hooks: [\n {\n type: 'command',\n command: config.bashJudgeScriptPath,\n timeout: 30,\n },\n ],\n [SYNKRO_MARKER]: true,\n } as any);\n\n // PreToolUse Edit/Write → three hooks in ONE entry so CC waits for all\n // before processing deny decisions. Each hook runs in parallel:\n // 1. edit-precheck: org rules grading on channel 1 (port 8929)\n // 2. cwe-precheck: CWE Top 25 grading on channel 2 (port 8930)\n // 3. cve-precheck: CVE/OSV dependency scan (curl, no LLM)\n settings.hooks.PreToolUse.push({\n matcher: 'Edit|Write|MultiEdit|NotebookEdit',\n hooks: [\n {\n type: 'command',\n command: config.editPrecheckScriptPath,\n timeout: 30,\n },\n {\n type: 'command',\n command: config.cwePrecheckScriptPath,\n timeout: 30,\n },\n {\n type: 'command',\n command: config.cvePrecheckScriptPath,\n timeout: 10,\n },\n ],\n [SYNKRO_MARKER]: true,\n } as any);\n\n // PreToolUse Agent → scan subagent prompts against org rules.\n settings.hooks.PreToolUse.push({\n matcher: 'Agent',\n hooks: [\n {\n type: 'command',\n command: config.agentJudgeScriptPath,\n timeout: 30,\n },\n ],\n [SYNKRO_MARKER]: true,\n } as any);\n\n // PreToolUse ExitPlanMode → advisory plan review against org rules.\n settings.hooks.PreToolUse.push({\n matcher: 'ExitPlanMode',\n hooks: [\n {\n type: 'command',\n command: config.planJudgeScriptPath,\n timeout: 45,\n },\n ],\n [SYNKRO_MARKER]: true,\n } as any);\n\n // PostToolUse Edit/Write removed — PreToolUse hooks handle grading now.\n\n // PostToolUse Bash → flips pending precheck_corrections row to 'allow'\n // once the bash command actually executed. Required for the bash trendline\n // (approved-vs-rejected) the dashboard reads from precheck_corrections.\n settings.hooks.PostToolUse.push({\n matcher: 'Bash',\n hooks: [\n {\n type: 'command',\n command: config.bashFollowupScriptPath,\n },\n ],\n [SYNKRO_MARKER]: true,\n } as any);\n\n // SessionEnd → end-of-session summary line (`[synkro] stop → N findings: ...`).\n // We use SessionEnd, not Stop, because Stop fires after every agent turn\n // (which would spam the user in interactive mode); SessionEnd fires once\n // when the session itself terminates.\n settings.hooks.SessionEnd.push({\n hooks: [\n {\n type: 'command',\n command: config.stopSummaryScriptPath,\n },\n ],\n [SYNKRO_MARKER]: true,\n } as any);\n\n // SessionStart → \"[synkro] session start → N open findings in this repo\" if any.\n settings.hooks.SessionStart.push({\n hooks: [\n {\n type: 'command',\n command: config.sessionStartScriptPath,\n },\n ],\n [SYNKRO_MARKER]: true,\n } as any);\n\n // UserPromptSubmit → captures explicit consent keywords from user messages.\n // Writes a file-based grant so the next blocked tool call is allowed through.\n (settings.hooks.UserPromptSubmit as any[]).push({\n hooks: [\n {\n type: 'command',\n command: config.userPromptSubmitScriptPath,\n timeout: 5,\n },\n ],\n [SYNKRO_MARKER]: true,\n } as any);\n\n // Stop → usage telemetry + optional transcript sync (fires after every agent turn).\n // Always installed: usage tracking is ungated; transcript sync is gated inside the script.\n settings.hooks.Stop = settings.hooks.Stop ?? [];\n removeSynkroEntries(settings.hooks as any, 'Stop');\n settings.hooks.Stop.push({\n hooks: [\n {\n type: 'command',\n command: config.transcriptSyncScriptPath,\n timeout: 3,\n },\n ],\n [SYNKRO_MARKER]: true,\n } as any);\n\n writeSettingsAtomic(settingsPath, settings);\n}\n\n/**\n * Remove all Synkro-managed hook entries from settings.json.\n * Used by `synkro disconnect`.\n */\nexport function uninstallCCHooks(settingsPath: string): boolean {\n if (!existsSync(settingsPath)) return false;\n const settings = readSettings(settingsPath);\n if (!settings.hooks) return false;\n\n const events = ['PreToolUse', 'PostToolUse', 'SessionEnd', 'SessionStart', 'Stop', 'UserPromptSubmit'] as const;\n for (const evt of events) {\n removeSynkroEntries(settings.hooks as any, evt);\n }\n\n // If a hook event array is now empty, delete it\n for (const evt of events) {\n if (Array.isArray((settings.hooks as any)[evt]) && (settings.hooks as any)[evt].length === 0) {\n delete (settings.hooks as any)[evt];\n }\n }\n // If hooks object is now empty, delete it\n if (Object.keys(settings.hooks).length === 0) {\n delete settings.hooks;\n }\n\n writeSettingsAtomic(settingsPath, settings);\n return true;\n}\n\n/**\n * Check whether Synkro hooks are currently installed in settings.json.\n * Used by `synkro status`.\n */\nexport function inspectCCHooks(settingsPath: string): {\n installed: boolean;\n preToolUseBash: boolean;\n postToolUseEdit: boolean;\n sessionEnd: boolean;\n sessionStart: boolean;\n} {\n if (!existsSync(settingsPath)) {\n return { installed: false, preToolUseBash: false, postToolUseEdit: false, sessionEnd: false, sessionStart: false };\n }\n const settings = readSettings(settingsPath);\n const pre = (settings.hooks as any)?.PreToolUse ?? [];\n const post = (settings.hooks as any)?.PostToolUse ?? [];\n const sessionEndHooks = (settings.hooks as any)?.SessionEnd ?? [];\n const sessionStartHooks = (settings.hooks as any)?.SessionStart ?? [];\n const preToolUseBash = pre.some((e: any) => e?.[SYNKRO_MARKER] === true);\n const postToolUseEdit = post.some((e: any) => e?.[SYNKRO_MARKER] === true);\n const sessionEnd = sessionEndHooks.some((e: any) => e?.[SYNKRO_MARKER] === true);\n const sessionStart = sessionStartHooks.some((e: any) => e?.[SYNKRO_MARKER] === true);\n return {\n installed: preToolUseBash || postToolUseEdit || sessionEnd || sessionStart,\n preToolUseBash,\n postToolUseEdit,\n sessionEnd,\n sessionStart,\n };\n}\n","// :)\n/**\n * Atomically merge Synkro hook entries into ~/.cursor/hooks.json.\n *\n * Cursor hooks reuse the same TypeScript scripts as Claude Code (cc-*.ts),\n * run with SYNKRO_HOOK_FORMAT=cursor so _synkro-common translates CC JSON output\n * to Cursor's permission JSON for preToolUse. CC install is unchanged.\n */\nimport { readFileSync, writeFileSync, renameSync, mkdirSync } from 'node:fs';\nimport { dirname, resolve, normalize } from 'node:path';\nimport { homedir } from 'node:os';\n\nexport interface CursorHookConfig {\n /** Cursor-specific bash/shell judge (preToolUse with Shell|Bash matcher) */\n bashJudgeScriptPath: string;\n /** Cursor-specific post-edit capture (afterFileEdit event shape) */\n editCaptureScriptPath: string;\n /** Shared cc-*.ts scripts (invoked with SYNKRO_HOOK_FORMAT=cursor) */\n bashFollowupScriptPath: string;\n editPrecheckScriptPath: string;\n cwePrecheckScriptPath: string;\n cvePrecheckScriptPath: string;\n planJudgeScriptPath: string;\n agentJudgeScriptPath: string;\n stopSummaryScriptPath: string;\n sessionStartScriptPath: string;\n userPromptSubmitScriptPath: string;\n transcriptSyncScriptPath: string;\n}\n\nconst SYNKRO_MARKER = '__synkro_managed__';\n\nfunction shellQuote(s: string): string {\n return \"'\" + s.replace(/'/g, \"'\\\\''\") + \"'\";\n}\n\n/** Run a shared cc-*.ts hook with Cursor output translation. */\nfunction cursorCcCmd(scriptPath: string): string {\n return 'env SYNKRO_HOOK_FORMAT=cursor bun run ' + shellQuote(scriptPath);\n}\n\nfunction bunRunCmd(scriptPath: string): string {\n return 'bun run ' + shellQuote(scriptPath);\n}\n\nconst ALLOWED_PARENT_DIRS = [\n resolve(homedir(), '.cursor'),\n resolve(homedir(), '.config', 'cursor'),\n];\n\nfunction validateHooksPath(path: string): string {\n const resolved = resolve(normalize(path));\n if (!ALLOWED_PARENT_DIRS.some(dir => resolved.startsWith(dir + '/') || resolved === dir)) {\n throw new Error(`Hooks path must be under ~/.cursor or ~/.config/cursor, got: ${resolved}`);\n }\n return resolved;\n}\n\ninterface CursorHookEntry {\n command: string;\n timeout?: number;\n failClosed?: boolean;\n matcher?: string;\n [SYNKRO_MARKER]?: boolean;\n}\n\ninterface CursorHooksFile {\n version?: number;\n hooks?: {\n [event: string]: CursorHookEntry[];\n };\n}\n\nfunction readHooksFile(rawPath: string): CursorHooksFile {\n const safePath = validateHooksPath(rawPath);\n try {\n const raw = readFileSync(safePath, 'utf-8');\n return JSON.parse(raw) as CursorHooksFile;\n } catch (err: any) {\n if (err?.code === 'ENOENT') return { version: 1, hooks: {} };\n throw new Error(`Failed to parse ${safePath}: ${(err as Error).message}`);\n }\n}\n\nfunction writeHooksFileAtomic(rawPath: string, data: CursorHooksFile): void {\n const safePath = validateHooksPath(rawPath);\n mkdirSync(dirname(safePath), { recursive: true });\n const tmpPath = `${safePath}.synkro.tmp`;\n writeFileSync(tmpPath, JSON.stringify(data, null, 2) + '\\n', { encoding: 'utf-8', mode: 0o600 });\n renameSync(tmpPath, safePath);\n}\n\nfunction isSynkroEntry(entry: any): boolean {\n if (entry?.[SYNKRO_MARKER]) return true;\n return typeof entry?.command === 'string' && entry.command.includes('/.synkro/hooks/');\n}\n\nconst ALL_EVENTS = [\n 'sessionStart', 'sessionEnd', 'beforeSubmitPrompt', 'stop',\n 'beforeShellExecution', 'afterShellExecution',\n 'preToolUse', 'afterFileEdit', 'postToolUse',\n];\n\nfunction removeSynkroEntries(hooks: CursorHooksFile['hooks'], event: string): void {\n if (!hooks) return;\n const arr = hooks[event];\n if (!Array.isArray(arr)) return;\n hooks[event] = arr.filter((entry: any) => !isSynkroEntry(entry));\n}\n\nfunction pushCcHook(\n hooks: CursorHooksFile['hooks'],\n event: string,\n scriptPath: string,\n opts: { timeout: number; matcher?: string; failClosed?: boolean },\n): void {\n hooks![event] = hooks![event] ?? [];\n hooks![event]!.push({\n command: cursorCcCmd(scriptPath),\n timeout: opts.timeout,\n failClosed: opts.failClosed ?? false,\n ...(opts.matcher ? { matcher: opts.matcher } : {}),\n [SYNKRO_MARKER]: true,\n });\n}\n\nexport function installCursorHooks(hooksJsonPath: string, config: CursorHookConfig): void {\n const file = readHooksFile(hooksJsonPath);\n file.version = file.version ?? 1;\n file.hooks = file.hooks ?? {};\n\n for (const evt of ALL_EVENTS) {\n removeSynkroEntries(file.hooks, evt);\n }\n\n const h = file.hooks;\n\n pushCcHook(h, 'sessionStart', config.sessionStartScriptPath, { timeout: 5 });\n pushCcHook(h, 'sessionEnd', config.stopSummaryScriptPath, { timeout: 10 });\n pushCcHook(h, 'beforeSubmitPrompt', config.userPromptSubmitScriptPath, { timeout: 5 });\n pushCcHook(h, 'stop', config.transcriptSyncScriptPath, { timeout: 3 });\n\n h.beforeShellExecution = h.beforeShellExecution ?? [];\n h.beforeShellExecution.push({\n command: bunRunCmd(config.bashJudgeScriptPath),\n timeout: 15,\n failClosed: false,\n [SYNKRO_MARKER]: true,\n });\n\n pushCcHook(h, 'afterShellExecution', config.bashFollowupScriptPath, { timeout: 10 });\n\n // preToolUse covers non-shell tools (Read, Edit, etc.) + dedup prevents double-grading shell commands\n h.preToolUse = h.preToolUse ?? [];\n h.preToolUse.push({\n command: bunRunCmd(config.bashJudgeScriptPath),\n timeout: 15,\n failClosed: false,\n matcher: 'Shell|Bash|Read|ReadFile|Grep|Glob|terminal|run_terminal_cmd|execute_command|read_file|grep_search|file_search|list_dir|codebase_search|delete_file',\n [SYNKRO_MARKER]: true,\n });\n\n pushCcHook(h, 'preToolUse', config.editPrecheckScriptPath, {\n timeout: 15,\n matcher: 'Write|Edit|StrReplace|MultiEdit|NotebookEdit|edit_file|reapply|edit_notebook',\n });\n pushCcHook(h, 'preToolUse', config.cwePrecheckScriptPath, {\n timeout: 15,\n matcher: 'Write|Edit|StrReplace|MultiEdit|NotebookEdit|edit_file|reapply|edit_notebook',\n });\n pushCcHook(h, 'preToolUse', config.cvePrecheckScriptPath, {\n timeout: 10,\n matcher: 'Write|Edit|StrReplace|MultiEdit|NotebookEdit|edit_file|reapply|edit_notebook',\n });\n pushCcHook(h, 'preToolUse', config.agentJudgeScriptPath, {\n timeout: 15,\n matcher: 'Agent|Task',\n });\n pushCcHook(h, 'preToolUse', config.planJudgeScriptPath, {\n timeout: 20,\n matcher: 'ExitPlanMode|SwitchMode|CreatePlan',\n });\n\n h.afterFileEdit = h.afterFileEdit ?? [];\n h.afterFileEdit.push({\n command: bunRunCmd(config.editCaptureScriptPath),\n timeout: 15,\n failClosed: false,\n [SYNKRO_MARKER]: true,\n });\n\n pushCcHook(h, 'postToolUse', config.bashFollowupScriptPath, {\n timeout: 10,\n matcher: 'Shell|Bash|terminal|run_terminal_cmd|execute_command|delete_file',\n });\n\n writeHooksFileAtomic(hooksJsonPath, file);\n}\n\nexport function uninstallCursorHooks(hooksJsonPath: string): boolean {\n let file: CursorHooksFile;\n try {\n file = readHooksFile(hooksJsonPath);\n } catch {\n return false;\n }\n if (!file.hooks) return false;\n\n for (const evt of ALL_EVENTS) {\n removeSynkroEntries(file.hooks, evt);\n }\n\n for (const evt of ALL_EVENTS) {\n if (Array.isArray(file.hooks[evt]) && file.hooks[evt].length === 0) {\n delete file.hooks[evt];\n }\n }\n if (Object.keys(file.hooks).length === 0) {\n delete file.hooks;\n }\n\n writeHooksFileAtomic(hooksJsonPath, file);\n return true;\n}\n\nfunction preToolUseUsesScript(hooks: CursorHookEntry[] | undefined, scriptBasename: string): boolean {\n return (hooks ?? []).some((e) =>\n isSynkroEntry(e) && typeof e.command === 'string' && e.command.includes(scriptBasename),\n );\n}\n\nexport function inspectCursorHooks(hooksJsonPath: string): {\n installed: boolean;\n sessionStart: boolean;\n sessionEnd: boolean;\n beforeSubmitPrompt: boolean;\n stop: boolean;\n beforeShellExecution: boolean;\n afterShellExecution: boolean;\n preToolUse: boolean;\n preToolUseBash: boolean;\n preToolUseEdit: boolean;\n preToolUseCwe: boolean;\n preToolUseCve: boolean;\n preToolUseAgent: boolean;\n preToolUsePlan: boolean;\n afterFileEdit: boolean;\n postToolUse: boolean;\n} {\n let file: CursorHooksFile;\n try {\n file = readHooksFile(hooksJsonPath);\n } catch {\n return {\n installed: false,\n sessionStart: false, sessionEnd: false, beforeSubmitPrompt: false, stop: false,\n beforeShellExecution: false, afterShellExecution: false,\n preToolUse: false, preToolUseBash: false, preToolUseEdit: false,\n preToolUseCwe: false, preToolUseCve: false, preToolUseAgent: false, preToolUsePlan: false,\n afterFileEdit: false, postToolUse: false,\n };\n }\n const h = file.hooks ?? {};\n const sessionStart = (h.sessionStart ?? []).some((e) => isSynkroEntry(e));\n const sessionEnd = (h.sessionEnd ?? []).some((e) => isSynkroEntry(e));\n const beforeSubmitPrompt = (h.beforeSubmitPrompt ?? []).some((e) => isSynkroEntry(e));\n const stop = (h.stop ?? []).some((e) => isSynkroEntry(e));\n const beforeShellExecution = (h.beforeShellExecution ?? []).some((e) => isSynkroEntry(e));\n const afterShellExecution = (h.afterShellExecution ?? []).some((e) => isSynkroEntry(e));\n const pre = h.preToolUse ?? [];\n const preToolUseBash = preToolUseUsesScript(pre, 'cc-bash-judge') || preToolUseUsesScript(pre, 'cursor-bash-judge');\n const preToolUseEdit = preToolUseUsesScript(pre, 'cc-edit-precheck') || preToolUseUsesScript(pre, 'cursor-edit-precheck');\n const preToolUseCwe = preToolUseUsesScript(pre, 'cc-cwe-precheck');\n const preToolUseCve = preToolUseUsesScript(pre, 'cc-cve-precheck');\n const preToolUseAgent = preToolUseUsesScript(pre, 'cc-agent-judge');\n const preToolUsePlan = preToolUseUsesScript(pre, 'cc-plan-judge');\n const preToolUse = preToolUseBash || preToolUseEdit || preToolUseCwe || preToolUseCve || preToolUseAgent || preToolUsePlan;\n const afterFileEdit = (h.afterFileEdit ?? []).some((e) => isSynkroEntry(e));\n const postToolUse = (h.postToolUse ?? []).some((e) => isSynkroEntry(e));\n return {\n installed: sessionStart || sessionEnd || beforeSubmitPrompt || stop\n || beforeShellExecution || afterShellExecution || preToolUse || afterFileEdit || postToolUse,\n sessionStart, sessionEnd, beforeSubmitPrompt, stop,\n beforeShellExecution, afterShellExecution,\n preToolUse, preToolUseBash, preToolUseEdit, preToolUseCwe, preToolUseCve, preToolUseAgent, preToolUsePlan,\n afterFileEdit, postToolUse,\n };\n}\n","/**\n * Atomically merge the Synkro guardrails MCP server entry into ~/.claude.json.\n *\n * CC's MCP config lives in ~/.claude.json (NOT ~/.claude/settings.json — those\n * are different files). It accepts an HTTP-transport server with a static\n * Authorization header set at config-write time. We register one entry,\n * `synkro-guardrails`, marked with `__synkro_managed__: true` so we can safely\n * remove it on `synkro disconnect` without touching the user's other servers.\n *\n * Mirrors the pattern in ccHookConfig.ts — read-modify-write atomically via\n * tmpfile + rename; preserve any other top-level keys; never replace the file.\n */\nimport { existsSync, readFileSync, writeFileSync, renameSync, mkdirSync } from 'node:fs';\nimport { homedir } from 'node:os';\nimport { dirname, join } from 'node:path';\n\nconst SYNKRO_MARKER = '__synkro_managed__';\nconst SYNKRO_SERVER_NAME = 'synkro-guardrails';\nconst CC_CONFIG_PATH = join(homedir(), '.claude.json');\n\ninterface ClaudeJson {\n mcpServers?: Record<string, McpServerEntry>;\n [k: string]: unknown;\n}\n\ninterface McpServerEntry {\n type?: 'http' | 'stdio' | 'sse';\n url?: string;\n command?: string;\n args?: string[];\n headers?: Record<string, string>;\n env?: Record<string, string>;\n [SYNKRO_MARKER]?: boolean;\n [k: string]: unknown;\n}\n\nfunction readClaudeJson(): ClaudeJson {\n if (!existsSync(CC_CONFIG_PATH)) return {};\n try {\n const raw = readFileSync(CC_CONFIG_PATH, 'utf-8');\n return JSON.parse(raw) as ClaudeJson;\n } catch (err) {\n throw new Error(`Failed to parse ${CC_CONFIG_PATH}: ${(err as Error).message}`);\n }\n}\n\nfunction writeClaudeJsonAtomic(config: ClaudeJson): void {\n mkdirSync(dirname(CC_CONFIG_PATH), { recursive: true });\n const tmpPath = `${CC_CONFIG_PATH}.synkro.tmp`;\n writeFileSync(tmpPath, JSON.stringify(config, null, 2) + '\\n', 'utf-8');\n renameSync(tmpPath, CC_CONFIG_PATH);\n}\n\nexport interface InstallMcpOptions {\n gatewayUrl: string; // e.g. http://localhost:8788\n // Long-lived (1y) Synkro-signed JWT scoped to mcp:guardrails. Minted by\n // POST /api/v1/cli/mcp-token during install. We deliberately do NOT write\n // the WorkOS access token here anymore — that one expires in 5 min and\n // silently breaks the CC MCP connection.\n bearerToken: string;\n local?: boolean;\n}\n\n/**\n * Register the Synkro guardrails MCP server in ~/.claude.json.\n * Idempotent — replaces any prior Synkro-managed entry with the new one.\n */\nexport function installMcpConfig(opts: InstallMcpOptions): { path: string; url: string } {\n const config = readClaudeJson();\n config.mcpServers = config.mcpServers ?? {};\n\n // Remove any prior Synkro-managed entry (so re-running install picks up\n // a refreshed JWT). Leave non-Synkro entries alone.\n for (const [name, entry] of Object.entries(config.mcpServers)) {\n if (entry?.[SYNKRO_MARKER] === true) delete config.mcpServers[name];\n }\n\n if (opts.local) {\n const url = 'http://127.0.0.1:8931/';\n const tokenPath = join(homedir(), '.synkro', '.mcp-local-token');\n let localToken = '';\n try { localToken = readFileSync(tokenPath, 'utf-8').trim(); } catch {}\n config.mcpServers[SYNKRO_SERVER_NAME] = {\n type: 'http',\n url,\n ...(localToken ? { headers: { Authorization: `Bearer ${localToken}` } } : {}),\n [SYNKRO_MARKER]: true,\n };\n writeClaudeJsonAtomic(config);\n return { path: CC_CONFIG_PATH, url };\n }\n\n const url = `${opts.gatewayUrl.replace(/\\/$/, '')}/api/v1/mcp/guardrails`;\n config.mcpServers[SYNKRO_SERVER_NAME] = {\n type: 'http',\n url,\n headers: { Authorization: `Bearer ${opts.bearerToken}` },\n [SYNKRO_MARKER]: true,\n };\n\n writeClaudeJsonAtomic(config);\n return { path: CC_CONFIG_PATH, url };\n}\n\n/**\n * Remove all Synkro-managed MCP server entries from ~/.claude.json.\n * Returns true if anything was removed.\n */\nexport function uninstallMcpConfig(): boolean {\n if (!existsSync(CC_CONFIG_PATH)) return false;\n const config = readClaudeJson();\n if (!config.mcpServers || Object.keys(config.mcpServers).length === 0) return false;\n\n let removed = false;\n for (const [name, entry] of Object.entries(config.mcpServers)) {\n if (entry?.[SYNKRO_MARKER] === true) {\n delete config.mcpServers[name];\n removed = true;\n }\n }\n if (!removed) return false;\n\n // If the mcpServers object is now empty, drop it to keep the file tidy.\n if (Object.keys(config.mcpServers).length === 0) delete config.mcpServers;\n\n writeClaudeJsonAtomic(config);\n return true;\n}\n\n/**\n * Inspect whether the Synkro MCP server entry is currently registered.\n */\nexport function inspectMcpConfig(): {\n installed: boolean;\n configPath: string;\n url?: string;\n} {\n if (!existsSync(CC_CONFIG_PATH)) {\n return { installed: false, configPath: CC_CONFIG_PATH };\n }\n const config = readClaudeJson();\n const entry = config.mcpServers?.[SYNKRO_SERVER_NAME];\n if (!entry || entry[SYNKRO_MARKER] !== true) {\n return { installed: false, configPath: CC_CONFIG_PATH };\n }\n return { installed: true, configPath: CC_CONFIG_PATH, url: entry.url };\n}\n\n// ─── Cursor MCP Config ───\n\nconst CURSOR_MCP_PATH = join(homedir(), '.cursor', 'mcp.json');\n\ninterface CursorMcpJson {\n mcpServers?: Record<string, McpServerEntry>;\n [k: string]: unknown;\n}\n\nfunction readCursorMcpJson(): CursorMcpJson {\n if (!existsSync(CURSOR_MCP_PATH)) return {};\n try {\n const raw = readFileSync(CURSOR_MCP_PATH, 'utf-8');\n return JSON.parse(raw) as CursorMcpJson;\n } catch (err) {\n throw new Error(`Failed to parse ${CURSOR_MCP_PATH}: ${(err as Error).message}`);\n }\n}\n\nfunction writeCursorMcpJsonAtomic(config: CursorMcpJson): void {\n mkdirSync(dirname(CURSOR_MCP_PATH), { recursive: true });\n const tmpPath = `${CURSOR_MCP_PATH}.synkro.tmp`;\n writeFileSync(tmpPath, JSON.stringify(config, null, 2) + '\\n', 'utf-8');\n renameSync(tmpPath, CURSOR_MCP_PATH);\n}\n\n/**\n * Register the Synkro guardrails MCP server in ~/.cursor/mcp.json.\n * Idempotent — replaces any prior Synkro-managed entry with the new one.\n */\nexport function installCursorMcpConfig(opts: InstallMcpOptions): { path: string; url: string } {\n const config = readCursorMcpJson();\n config.mcpServers = config.mcpServers ?? {};\n\n for (const [name, entry] of Object.entries(config.mcpServers)) {\n if (entry?.[SYNKRO_MARKER] === true) delete config.mcpServers[name];\n }\n\n if (opts.local) {\n const url = 'http://127.0.0.1:8931/';\n const tokenPath = join(homedir(), '.synkro', '.mcp-local-token');\n let localToken = '';\n try { localToken = readFileSync(tokenPath, 'utf-8').trim(); } catch {}\n config.mcpServers[SYNKRO_SERVER_NAME] = {\n url,\n ...(localToken ? { headers: { Authorization: `Bearer ${localToken}` } } : {}),\n [SYNKRO_MARKER]: true,\n };\n writeCursorMcpJsonAtomic(config);\n return { path: CURSOR_MCP_PATH, url };\n }\n\n const url = `${opts.gatewayUrl.replace(/\\/$/, '')}/api/v1/mcp/guardrails`;\n config.mcpServers[SYNKRO_SERVER_NAME] = {\n url,\n headers: { Authorization: `Bearer ${opts.bearerToken}` },\n [SYNKRO_MARKER]: true,\n };\n\n writeCursorMcpJsonAtomic(config);\n return { path: CURSOR_MCP_PATH, url };\n}\n\n/**\n * Remove all Synkro-managed MCP server entries from ~/.cursor/mcp.json.\n */\nexport function uninstallCursorMcpConfig(): boolean {\n if (!existsSync(CURSOR_MCP_PATH)) return false;\n const config = readCursorMcpJson();\n if (!config.mcpServers || Object.keys(config.mcpServers).length === 0) return false;\n\n let removed = false;\n for (const [name, entry] of Object.entries(config.mcpServers)) {\n if (entry?.[SYNKRO_MARKER] === true) {\n delete config.mcpServers[name];\n removed = true;\n }\n }\n if (!removed) return false;\n\n if (Object.keys(config.mcpServers).length === 0) delete config.mcpServers;\n\n writeCursorMcpJsonAtomic(config);\n return true;\n}\n","// :)\n/**\n * Bash hook scripts for Cursor IDE adapter and shared common utilities.\n *\n * CC hooks have been moved to hookScriptsTs.ts (TypeScript + Bun runtime).\n * This file retains the bash common script (sourced by Cursor hooks) and\n * the Cursor-specific adapter scripts.\n */\n\nexport const SYNKRO_COMMON_SCRIPT = `#!/bin/bash\n# Shared Synkro hook utilities — sourced by all hook scripts.\n\nsynkro_log() { echo \"[synkro] $1\" >&2; }\n\n# Load config\n_SYNKRO_CONFIG=\"$HOME/.synkro/config.env\"\nif [ -f \"$_SYNKRO_CONFIG\" ]; then\n set -a; . \"$_SYNKRO_CONFIG\"; set +a\nfi\n\nGATEWAY_URL=\"\\${SYNKRO_GATEWAY_URL:-https://api.synkro.sh}\"\nCREDS_PATH=\"\\${SYNKRO_CREDENTIALS_PATH:-$HOME/.synkro/credentials.json}\"\n\nsynkro_load_jwt() {\n if [ ! -f \"$CREDS_PATH\" ]; then echo \"\"; return 1; fi\n jq -r '.access_token // empty' \"$CREDS_PATH\" 2>/dev/null\n}\n\nsynkro_refresh_jwt() {\n # Lock via mkdir (atomic on all Unix including macOS — no flock needed)\n local lockdir=\"\\${CREDS_PATH}.lockdir\"\n if ! mkdir \"$lockdir\" 2>/dev/null; then\n # Another hook is refreshing — wait and re-read\n local _w=0\n while [ -d \"$lockdir\" ] && [ $_w -lt 5 ]; do sleep 0.5; _w=$((_w+1)); done\n JWT=$(jq -r '.access_token // empty' \"$CREDS_PATH\" 2>/dev/null)\n return 0\n fi\n trap \"rmdir \\\\\"$lockdir\\\\\" 2>/dev/null\" RETURN\n\n # Re-check expiry — another hook may have just refreshed\n local p2 exp2 now2\n p2=$(printf '%s' \"$JWT\" | cut -d. -f2)\n case $((\\${#p2} % 4)) in 2) p2=\"\\${p2}==\";; 3) p2=\"\\${p2}=\";; esac\n exp2=$(printf '%s' \"$p2\" | tr '_-' '/+' | base64 -D 2>/dev/null | jq -r '.exp // 0' 2>/dev/null)\n now2=$(date -u +%s)\n if [ $((exp2 - now2)) -ge 60 ]; then return 0; fi\n\n local rt\n rt=$(jq -r '.refresh_token // empty' \"$CREDS_PATH\" 2>/dev/null)\n if [ -z \"$rt\" ]; then return 1; fi\n local resp\n resp=$(curl -sS -X POST \"\\${GATEWAY_URL}/api/auth/refresh\" \\\\\n -H \"Content-Type: application/json\" \\\\\n -d \"$(jq -n --arg rt \"$rt\" '{refresh_token:$rt}')\" \\\\\n --max-time 4 2>/dev/null)\n local new_at\n new_at=$(echo \"$resp\" | jq -r '.access_token // empty' 2>/dev/null)\n if [ -z \"$new_at\" ]; then return 1; fi\n local new_rt\n new_rt=$(echo \"$resp\" | jq -r '.refresh_token // empty' 2>/dev/null)\n [ -z \"$new_rt\" ] && new_rt=\"$rt\"\n local tmp=\"\\${CREDS_PATH}.synkro.tmp\"\n local existing\n existing=$(cat \"$CREDS_PATH\" 2>/dev/null)\n if [ -z \"$existing\" ] || ! echo \"$existing\" | jq -e '.' >/dev/null 2>&1; then\n existing='{}'\n fi\n echo \"$existing\" | jq --arg at \"$new_at\" --arg rt \"$new_rt\" '. + {access_token:$at,refresh_token:$rt}' > \"$tmp\" 2>/dev/null && mv \"$tmp\" \"$CREDS_PATH\"\n JWT=\"$new_at\"\n}\n\nsynkro_ensure_fresh_jwt() {\n [ -z \"$JWT\" ] && return 1\n local p exp now\n p=$(printf '%s' \"$JWT\" | cut -d. -f2)\n case $((\\${#p} % 4)) in 2) p=\"\\${p}==\";; 3) p=\"\\${p}=\";; esac\n exp=$(printf '%s' \"$p\" | tr '_-' '/+' | base64 -D 2>/dev/null | jq -r '.exp // 0' 2>/dev/null)\n now=$(date -u +%s)\n [ $((exp - now)) -lt 60 ] && synkro_refresh_jwt\n}\n\nsynkro_detect_repo() {\n local cwd=\"\\${1:-.}\"\n if command -v git >/dev/null 2>&1; then\n local r\n r=$(git -C \"$cwd\" remote get-url origin 2>/dev/null || true)\n [ -n \"$r\" ] && echo \"$r\" | sed -E 's|^git@[^:]+:||; s|^https?://[^/]+/||; s|\\\\.git$||' && return\n fi\n echo \"\"\n}\n\nsynkro_channel_up() {\n (exec 3<>/dev/tcp/127.0.0.1/\\${SYNKRO_CHANNEL_PORT:-8929}) 2>/dev/null && exec 3<&- 3>&-\n}\n\n# Fetch hook config. Sets SYNKRO_CAPTURE_DEPTH, SYNKRO_TIER, SYNKRO_RULES, SYNKRO_SILENT, SYNKRO_POLICY_NAME.\n_SYNKRO_RULES_FILE=\"$HOME/.synkro/rules.json\"\n_SYNKRO_TELEMETRY_FILE=\"$HOME/.synkro/telemetry.jsonl\"\n\nsynkro_load_config() {\n # Local-first: read from ~/.synkro/rules.json if it exists (zero latency, no network)\n if [ -f \"$_SYNKRO_RULES_FILE\" ]; then\n local rdata\n rdata=$(cat \"$_SYNKRO_RULES_FILE\" 2>/dev/null)\n if [ -n \"$rdata\" ]; then\n SYNKRO_CAPTURE_DEPTH=\"local_only\"\n SYNKRO_TIER=\"standard\"\n SYNKRO_SILENT=$(echo \"$rdata\" | jq -r '.config.silent // false' 2>/dev/null)\n local active_id\n active_id=$(echo \"$rdata\" | jq -r '.config.activePolicyId // empty' 2>/dev/null)\n if [ -n \"$active_id\" ]; then\n SYNKRO_POLICY_NAME=$(echo \"$rdata\" | jq -r --arg id \"$active_id\" '.policies[]? | select(.id == $id) | .name // empty' 2>/dev/null)\n SYNKRO_RULES=$(echo \"$rdata\" | jq -c --arg id \"$active_id\" '[.policies[]? | select(.id == $id) | .rules[]? | select(.hook_stage == \"pre\" or .hook_stage == \"both\" or .hook_stage == null) | {rule_id,text,severity,category,mode}]' 2>/dev/null || echo \"[]\")\n else\n SYNKRO_POLICY_NAME=$(echo \"$rdata\" | jq -r '.policies[0]?.name // empty' 2>/dev/null)\n SYNKRO_RULES=$(echo \"$rdata\" | jq -c '[.policies[0]?.rules[]? | select(.hook_stage == \"pre\" or .hook_stage == \"both\" or .hook_stage == null) | {rule_id,text,severity,category,mode}]' 2>/dev/null || echo \"[]\")\n fi\n return\n fi\n fi\n\n # Fallback: fetch from cloud API\n local resp\n resp=$(curl -sS \"\\${GATEWAY_URL}/api/v1/hook/config\\${1:+?$1}\" -H \"Authorization: Bearer $JWT\" --max-time 4 2>/dev/null || echo \"\")\n if [ -z \"$resp\" ]; then return; fi\n SYNKRO_CAPTURE_DEPTH=$(echo \"$resp\" | jq -r '.capture_depth // \"local_only\"' 2>/dev/null)\n SYNKRO_TIER=$(echo \"$resp\" | jq -r '.tier // \"standard\"' 2>/dev/null)\n SYNKRO_SILENT=$(echo \"$resp\" | jq -r '.silent_mode // false' 2>/dev/null)\n SYNKRO_POLICY_NAME=$(echo \"$resp\" | jq -r '.active_policy_name // empty' 2>/dev/null)\n SYNKRO_RULES=$(echo \"$resp\" | jq -c '[.rules[]? | select(.hook_stage == \"pre\" or .hook_stage == \"both\" or .hook_stage == null) | {rule_id,text,severity,category,mode}]' 2>/dev/null || echo \"[]\")\n}\n\nsynkro_local_capture() {\n local event_json=\"$1\"\n local ts\n ts=$(date -u +\"%Y-%m-%dT%H:%M:%S.000Z\")\n local line\n line=$(echo \"$event_json\" | jq -c --arg ts \"$ts\" '. + {_ts: $ts}' 2>/dev/null)\n [ -n \"$line\" ] && printf '%s\\\\n' \"$line\" >> \"$_SYNKRO_TELEMETRY_FILE\" 2>/dev/null\n}\n\nsynkro_tag() {\n if [ \"$SYNKRO_SILENT\" = \"true\" ]; then echo \"[synkro:silent]\"; return; fi\n local route=\"\\${1:-\\$(synkro_route)}\"\n local rs=\"\\${SYNKRO_POLICY_NAME:-all}\"\n echo \"[synkro:\\${route}:\\${rs}]\"\n}\n\nsynkro_route() {\n [ \"$SYNKRO_CAPTURE_DEPTH\" = \"local_only\" ] && echo \"local\" && return\n synkro_channel_up && echo \"local\" && return\n echo \"cloud\"\n}\n\nSYNKRO_CONSENT_FILE=\"$HOME/.synkro/.local-consent\"\n\n_TAB=\\$(printf '\\\\t')\n\nsynkro_consent_grant() {\n local sid=\"\\$1\" hash=\"\\$2\"\n printf '%s\\\\t%s\\\\tactive\\\\n' \"$sid\" \"$hash\" >> \"$SYNKRO_CONSENT_FILE\" 2>/dev/null || true\n}\n\nsynkro_consent_has_active() {\n local sid=\"\\$1\" hash=\"\\$2\"\n grep -q \"^\\${sid}\\${_TAB}\\${hash}\\${_TAB}active\\$\" \"$SYNKRO_CONSENT_FILE\" 2>/dev/null\n}\n\nsynkro_consent_consume() {\n local sid=\"\\$1\" hash=\"\\$2\"\n [ ! -f \"$SYNKRO_CONSENT_FILE\" ] && return\n local tmp=\"\\${SYNKRO_CONSENT_FILE}.tmp\"\n local pat=\"\\${sid}\\${_TAB}\\${hash}\\${_TAB}active\"\n local rep=\"\\${sid}\\${_TAB}\\${hash}\\${_TAB}consumed\"\n awk -v p=\"$pat\" -v r=\"$rep\" '{if(\\$0==p)print r;else print}' \"$SYNKRO_CONSENT_FILE\" > \"$tmp\" 2>/dev/null && mv \"$tmp\" \"$SYNKRO_CONSENT_FILE\" 2>/dev/null || true\n}\n\nsynkro_post_with_retry() {\n local url=\"$1\" body=\"$2\" timeout=\"\\${3:-8}\"\n local resp\n resp=$(curl -sS -X POST \"$url\" \\\\\n -H \"Content-Type: application/json\" \\\\\n -H \"Authorization: Bearer $JWT\" \\\\\n -d \"$body\" --max-time \"$timeout\" 2>/dev/null || echo \"\")\n if echo \"$resp\" | grep -qE '\"detail\":\"Token has expired|\"detail\":\"Invalid or expired token'; then\n if synkro_refresh_jwt; then\n resp=$(curl -sS -X POST \"$url\" \\\\\n -H \"Content-Type: application/json\" \\\\\n -H \"Authorization: Bearer $JWT\" \\\\\n -d \"$body\" --max-time \"$timeout\" 2>/dev/null || echo \"\")\n fi\n fi\n echo \"$resp\"\n}\n`;\n\n\n// ─── Cursor IDE adapter scripts (legacy bash — install writes TypeScript from hookScriptsTs.ts) ───\n\nexport const CURSOR_BASH_JUDGE_SCRIPT = `#!/bin/bash\nSCRIPT_DIR=\"$(cd \"$(dirname \"\\${BASH_SOURCE[0]}\")\" && pwd)\"\n. \"$SCRIPT_DIR/_synkro-common.sh\"\n\nJWT=$(synkro_load_jwt)\nif [ -z \"$JWT\" ]; then echo '{}'; exit 0; fi\nsynkro_ensure_fresh_jwt\n\nPAYLOAD=$(cat)\nif [ -z \"$PAYLOAD\" ]; then echo '{}'; exit 0; fi\n\nCOMMAND=$(echo \"$PAYLOAD\" | jq -r '.command // empty' 2>/dev/null)\nif [ -z \"$COMMAND\" ]; then echo '{}'; exit 0; fi\n\nCWD=$(echo \"$PAYLOAD\" | jq -r '.cwd // empty' 2>/dev/null)\nSESSION_ID=$(echo \"$PAYLOAD\" | jq -r '.conversation_id // empty' 2>/dev/null)\nGIT_REPO=$(synkro_detect_repo \"\\${CWD:-.}\")\n\nCMD_SHORT=$(printf '%s' \"$COMMAND\" | head -c 80)\nsynkro_log \"bashGuard checking: $CMD_SHORT\"\n\nsynkro_load_config\nif [ \"$SYNKRO_SILENT\" = \"true\" ]; then\n echo '{}'; exit 0\nfi\n\nBODY=$(jq -n \\\\\n --arg cmd \"$COMMAND\" \\\\\n --arg session_id \"$SESSION_ID\" \\\\\n --arg cwd \"$CWD\" \\\\\n --arg repo \"$GIT_REPO\" \\\\\n '{\n hook_event: \"PreToolUse\",\n tool_name: \"Bash\",\n tool_input: {command: $cmd},\n response_format: \"cursor\",\n session_id: (if ($session_id | length) > 0 then $session_id else null end),\n cwd: (if ($cwd | length) > 0 then $cwd else null end),\n repo: (if ($repo | length) > 0 then $repo else null end)\n }')\n\nRESP=$(synkro_post_with_retry \"\\${GATEWAY_URL}/api/v1/hook/judge\" \"$BODY\" 6)\n\nif [ -z \"$RESP\" ]; then\n synkro_log \"bashGuard $CMD_SHORT → error (timeout)\"\n echo '{}'; exit 0\nfi\n\n# Server returns cursor-format directly in hook_response\nif echo \"$RESP\" | jq -e '.hook_response' >/dev/null 2>&1; then\n echo \"$RESP\" | jq -c '.hook_response'\nelse\n echo '{}'\nfi\nexit 0\n`;\n\nexport const CURSOR_EDIT_PRECHECK_SCRIPT = `#!/bin/bash\nSCRIPT_DIR=\"$(cd \"$(dirname \"\\${BASH_SOURCE[0]}\")\" && pwd)\"\n. \"$SCRIPT_DIR/_synkro-common.sh\"\n\nJWT=$(synkro_load_jwt)\nif [ -z \"$JWT\" ]; then echo '{}'; exit 0; fi\nsynkro_ensure_fresh_jwt\n\nPAYLOAD=$(cat)\nif [ -z \"$PAYLOAD\" ]; then echo '{}'; exit 0; fi\n\nTOOL_NAME=$(echo \"$PAYLOAD\" | jq -r '.tool_name // empty' 2>/dev/null)\nCWD=$(echo \"$PAYLOAD\" | jq -r '.cwd // empty' 2>/dev/null)\nSESSION_ID=$(echo \"$PAYLOAD\" | jq -r '.conversation_id // empty' 2>/dev/null)\nGIT_REPO=$(synkro_detect_repo \"\\${CWD:-.}\")\n\nFILE_PATH=$(echo \"$PAYLOAD\" | jq -r '.tool_input.file_path // .tool_input.path // .tool_input.target_file // empty' 2>/dev/null)\nCONTENT=$(echo \"$PAYLOAD\" | jq -r '.tool_input.content // .tool_input.new_string // .tool_input.code_edit // empty' 2>/dev/null)\nif [ -z \"$FILE_PATH\" ]; then echo '{}'; exit 0; fi\n\nBASENAME=$(basename \"$FILE_PATH\" 2>/dev/null || echo \"$FILE_PATH\")\nsynkro_log \"editGuard checking: $BASENAME\"\n\nsynkro_load_config\nif [ \"$SYNKRO_SILENT\" = \"true\" ]; then\n echo '{}'; exit 0\nfi\n\nBODY=$(jq -n \\\\\n --arg file_path \"$FILE_PATH\" \\\\\n --arg content \"$CONTENT\" \\\\\n --arg session_id \"$SESSION_ID\" \\\\\n --arg cwd \"$CWD\" \\\\\n --arg repo \"$GIT_REPO\" \\\\\n '{\n hook_event: \"PreToolUse\",\n tool_name: \"Edit\",\n tool_input: {file_path: $file_path, content: $content},\n file_path: $file_path,\n content: $content,\n response_format: \"cursor\",\n session_id: (if ($session_id | length) > 0 then $session_id else null end),\n cwd: (if ($cwd | length) > 0 then $cwd else null end),\n repo: (if ($repo | length) > 0 then $repo else null end)\n }')\n\nRESP=$(synkro_post_with_retry \"\\${GATEWAY_URL}/api/v1/hook/judge\" \"$BODY\" 8)\n\nif [ -z \"$RESP\" ]; then\n synkro_log \"editGuard $BASENAME → error (timeout)\"\n echo '{}'; exit 0\nfi\n\nif echo \"$RESP\" | jq -e '.hook_response' >/dev/null 2>&1; then\n echo \"$RESP\" | jq -c '.hook_response'\nelse\n echo '{}'\nfi\nexit 0\n`;\n\nexport const CURSOR_EDIT_CAPTURE_SCRIPT = `#!/bin/bash\nSCRIPT_DIR=\"$(cd \"$(dirname \"\\${BASH_SOURCE[0]}\")\" && pwd)\"\n. \"$SCRIPT_DIR/_synkro-common.sh\"\n\nJWT=$(synkro_load_jwt)\nif [ -z \"$JWT\" ]; then echo '{}'; exit 0; fi\n\nPAYLOAD=$(cat)\nif [ -z \"$PAYLOAD\" ]; then echo '{}'; exit 0; fi\n\nFILE_PATH=$(echo \"$PAYLOAD\" | jq -r '.file_path // empty' 2>/dev/null)\nif [ -z \"$FILE_PATH\" ]; then echo '{}'; exit 0; fi\n\nCWD=$(echo \"$PAYLOAD\" | jq -r '.cwd // .workspace_roots[0] // empty' 2>/dev/null)\nSESSION_ID=$(echo \"$PAYLOAD\" | jq -r '.conversation_id // empty' 2>/dev/null)\nGIT_REPO=$(synkro_detect_repo \"\\${CWD:-.}\")\nBASENAME=$(basename \"$FILE_PATH\" 2>/dev/null || echo \"$FILE_PATH\")\n\nFULL_PATH=\"$FILE_PATH\"\n[ -n \"$CWD\" ] && FULL_PATH=\"$CWD/$FILE_PATH\"\nFULL_CONTENT=\"\"\n[ -f \"$FULL_PATH\" ] && FULL_CONTENT=$(head -c 50000 \"$FULL_PATH\" 2>/dev/null || true)\n\nDEPS_JSON=\"{}\"\n_PKG_DIR=\"\\${CWD:-.}\"\nwhile [ \"$_PKG_DIR\" != \"/\" ]; do\n if [ -f \"$_PKG_DIR/package.json\" ]; then\n DEPS_JSON=$(jq -c '(.dependencies // {}) + (.devDependencies // {})' \"$_PKG_DIR/package.json\" 2>/dev/null || echo \"{}\")\n break\n fi\n _PKG_DIR=$(dirname \"$_PKG_DIR\")\ndone\n\nsynkro_log \"editScan $BASENAME\"\n\n(\n BODY=$(jq -n \\\\\n --arg file_path \"$FILE_PATH\" --arg content \"$FULL_CONTENT\" \\\\\n --arg session_id \"$SESSION_ID\" --arg cwd \"$CWD\" --arg repo \"$GIT_REPO\" \\\\\n --argjson deps \"$DEPS_JSON\" \\\\\n '{capture_type:\"edit_scan\",tool_input:{file_path:$file_path,content:$content},edit_verdict:{ok:true},dependencies:$deps}\n + (if ($session_id | length) > 0 then {session_id:$session_id} else {} end)\n + (if ($cwd | length) > 0 then {cwd:$cwd} else {} end)\n + (if ($repo | length) > 0 then {repo:$repo} else {} end)')\n curl -sS -X POST \"\\${GATEWAY_URL}/api/v1/hook/capture\" \\\\\n -H \"Content-Type: application/json\" -H \"Authorization: Bearer $JWT\" \\\\\n -d \"$BODY\" --max-time 10 >/dev/null 2>&1 || true\n) &\ndisown 2>/dev/null || true\n\necho '{}'\nexit 0\n`;\n\nexport const CURSOR_BASH_FOLLOWUP_SCRIPT = `#!/bin/bash\nSCRIPT_DIR=\"$(cd \"$(dirname \"\\${BASH_SOURCE[0]}\")\" && pwd)\"\n. \"$SCRIPT_DIR/_synkro-common.sh\"\n\nJWT=$(synkro_load_jwt)\nif [ -z \"$JWT\" ]; then echo '{}'; exit 0; fi\n\nPAYLOAD=$(cat)\nTOOL_NAME=$(echo \"$PAYLOAD\" | jq -r '.tool_name // empty' 2>/dev/null)\ncase \"$TOOL_NAME\" in Shell|Bash|terminal|run_terminal_cmd|execute_command) ;; *) echo '{}'; exit 0 ;; esac\n\nSESSION_ID=$(echo \"$PAYLOAD\" | jq -r '.conversation_id // empty' 2>/dev/null)\nTOOL_USE_ID=$(echo \"$PAYLOAD\" | jq -r '.tool_use_id // empty' 2>/dev/null)\n\nIS_ERROR=$(echo \"$PAYLOAD\" | jq -r '.tool_result.is_error // false' 2>/dev/null)\nCMD=$(echo \"$PAYLOAD\" | jq -r '.tool_input.command // empty' 2>/dev/null)\nCMD_HASH=\"\"\nif [ -n \"$CMD\" ]; then\n CMD_HASH=$(printf '%s' \"$CMD\" | shasum -a 256 | cut -c1-16)\nfi\n\nif [ -n \"$CMD_HASH\" ] && [ -n \"$SESSION_ID\" ]; then\n if [ \"$IS_ERROR\" = \"false\" ]; then\n synkro_consent_consume \"$SESSION_ID\" \"$CMD_HASH\"\n else\n if ! synkro_consent_has_active \"$SESSION_ID\" \"$CMD_HASH\"; then\n synkro_consent_grant \"$SESSION_ID\" \"$CMD_HASH\"\n fi\n fi\nfi\n\nif [ -n \"$SESSION_ID\" ] && [ -n \"$TOOL_USE_ID\" ]; then\n (\n BODY=$(jq -n --arg sid \"$SESSION_ID\" --arg tid \"$TOOL_USE_ID\" \\\\\n --argjson err \"$IS_ERROR\" --arg ch \"$CMD_HASH\" \\\\\n '{capture_type:\"bash_followup\",session_id:$sid,tool_use_id:$tid,is_error:$err,command_hash:$ch}')\n curl -sS -X POST \"\\${GATEWAY_URL}/api/v1/hook/capture\" \\\\\n -H \"Content-Type: application/json\" -H \"Authorization: Bearer $JWT\" \\\\\n -d \"$BODY\" --max-time 3 >/dev/null 2>&1 || true\n ) &\n disown 2>/dev/null || true\nfi\n\necho '{}'\nexit 0\n`;\n","// :)\n/**\n * TypeScript hook scripts for Bun runtime — written to ~/.synkro/hooks/ during `synkro install`.\n *\n * Each export is the full source code of a TypeScript file that runs via `#!/usr/bin/env bun`.\n * All grading, classification, CVE scanning, and persistence lives server-side.\n * Local mode: grade via local-cc channel with prompts from /v1/hook/config, send anonymized metadata to /v1/hook/capture.\n */\n\nexport const SYNKRO_COMMON_TS = `\n// Shared Synkro hook utilities — imported by all hook scripts.\nimport { readFileSync, writeFileSync, appendFileSync, mkdirSync, existsSync, renameSync, openSync, closeSync, unlinkSync } from 'node:fs';\nimport { join, dirname, basename, extname, resolve as resolvePath } from 'node:path';\nimport { homedir } from 'node:os';\nimport { execSync } from 'node:child_process';\nimport { constants as FS_CONSTANTS } from 'node:fs';\n\n// ─── Config ───\n\nconst HOME = homedir();\nconst CONFIG_PATH = join(HOME, '.synkro', 'config.env');\n\n// Load config.env into process.env\nif (existsSync(CONFIG_PATH)) {\n try {\n const lines = readFileSync(CONFIG_PATH, 'utf-8').split('\\\\n');\n for (const line of lines) {\n const trimmed = line.trim();\n if (!trimmed || trimmed.startsWith('#')) continue;\n const eqIdx = trimmed.indexOf('=');\n if (eqIdx < 1) continue;\n const key = trimmed.slice(0, eqIdx).trim();\n let val = trimmed.slice(eqIdx + 1).trim();\n // Strip surrounding quotes\n if ((val.startsWith('\"') && val.endsWith('\"')) || (val.startsWith(\"'\") && val.endsWith(\"'\"))) {\n val = val.slice(1, -1);\n }\n process.env[key] = val;\n }\n } catch {}\n}\n\nconst ALLOWED_GATEWAY_HOSTS = new Set(['api.synkro.sh', 'localhost', '127.0.0.1']);\nfunction validateGatewayUrl(raw: string): string {\n try {\n const u = new URL(raw);\n if (!ALLOWED_GATEWAY_HOSTS.has(u.hostname)) return 'https://api.synkro.sh';\n return raw.replace(/\\\\/+$/, '');\n } catch {\n return 'https://api.synkro.sh';\n }\n}\nexport const GATEWAY_URL = validateGatewayUrl(process.env.SYNKRO_GATEWAY_URL || 'https://api.synkro.sh');\nexport const CREDS_PATH = process.env.SYNKRO_CREDENTIALS_PATH || join(HOME, '.synkro', 'credentials.json');\nconst LAST_PROMPT_FILE = join(HOME, '.synkro', '.last-prompt');\n\n// ─── Path Validation ───\n\nexport function isPathUnder(filePath: string, cwd: string): boolean {\n if (!filePath || !cwd) return false;\n const resolved = resolvePath(filePath);\n const base = resolvePath(cwd);\n return resolved.startsWith(base + '/') || resolved === base;\n}\n\n// ─── Logging ───\n\nexport function log(msg: string): void {\n process.stderr.write('[synkro] ' + msg + '\\\\n');\n}\n\n// ─── JWT Management ───\n\nexport function loadJwt(): string | null {\n try {\n if (!existsSync(CREDS_PATH)) return null;\n const creds = JSON.parse(readFileSync(CREDS_PATH, 'utf-8'));\n return creds.access_token || null;\n } catch {\n return null;\n }\n}\n\nfunction decodeJwtExp(jwt: string): number {\n try {\n const parts = jwt.split('.');\n if (parts.length < 2) return 0;\n let payload = parts[1].replace(/-/g, '+').replace(/_/g, '/');\n while (payload.length % 4) payload += '=';\n const decoded = Buffer.from(payload, 'base64').toString('utf-8');\n const obj = JSON.parse(decoded);\n return obj.exp || 0;\n } catch {\n return 0;\n }\n}\n\nexport async function refreshJwt(jwt: string): Promise<string> {\n const creds = JSON.parse(readFileSync(CREDS_PATH, 'utf-8'));\n const rt = creds.refresh_token;\n if (!rt) return jwt;\n\n const resp = await fetch(GATEWAY_URL + '/api/auth/refresh', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ refresh_token: rt }),\n signal: AbortSignal.timeout(4000),\n });\n\n if (!resp.ok) {\n log('refresh failed: HTTP ' + resp.status);\n return jwt;\n }\n\n const data = await resp.json() as any;\n const newAt = data.access_token;\n if (!newAt) return jwt;\n\n const newRt = data.refresh_token || rt;\n const existing = (() => {\n try { return JSON.parse(readFileSync(CREDS_PATH, 'utf-8')); } catch { return {}; }\n })();\n const updated = { ...existing, access_token: newAt, refresh_token: newRt };\n const tmp = CREDS_PATH + '.synkro.tmp';\n writeFileSync(tmp, JSON.stringify(updated, null, 2));\n renameSync(tmp, CREDS_PATH);\n return newAt;\n}\n\nfunction jwtIsExpired(jwt: string): boolean {\n const exp = decodeJwtExp(jwt);\n return exp - Math.floor(Date.now() / 1000) < 60;\n}\n\nfunction lockIsStale(lockfile: string, maxAgeMs = 8000): boolean {\n try {\n const stat = require('node:fs').statSync(lockfile);\n return Date.now() - stat.mtimeMs > maxAgeMs;\n } catch {\n return false;\n }\n}\n\nexport async function ensureFreshJwt(jwt: string): Promise<string> {\n if (!jwt) return jwt;\n if (!jwtIsExpired(jwt)) return jwt;\n\n const lockfile = CREDS_PATH + '.lock';\n\n // Clean stale lock left by a killed hook\n if (existsSync(lockfile) && lockIsStale(lockfile)) {\n try { unlinkSync(lockfile); } catch {}\n }\n\n let fd = -1;\n try {\n fd = openSync(lockfile, FS_CONSTANTS.O_WRONLY | FS_CONSTANTS.O_CREAT | FS_CONSTANTS.O_EXCL, 0o644);\n } catch {\n // Another process is refreshing — wait for it\n for (let i = 0; i < 8; i++) {\n await new Promise(r => setTimeout(r, 500));\n if (!existsSync(lockfile)) break;\n }\n // Stale lock after full wait — force-remove\n if (existsSync(lockfile) && lockIsStale(lockfile)) {\n try { unlinkSync(lockfile); } catch {}\n }\n // Re-read — the winner should have written a fresh JWT\n const fresh = loadJwt();\n if (fresh && !jwtIsExpired(fresh)) return fresh;\n // Winner's refresh failed — try to acquire lock ourselves\n try {\n fd = openSync(lockfile, FS_CONSTANTS.O_WRONLY | FS_CONSTANTS.O_CREAT | FS_CONSTANTS.O_EXCL, 0o644);\n } catch {\n return fresh || jwt;\n }\n }\n\n try {\n // Re-check — another hook may have written fresh creds while we waited for lock\n const freshJwt = loadJwt();\n if (freshJwt && !jwtIsExpired(freshJwt)) return freshJwt;\n return await refreshJwt(jwt);\n } catch {\n return jwt;\n } finally {\n try { closeSync(fd); } catch {}\n try { unlinkSync(lockfile); } catch {}\n }\n}\n\n// ─── Repo Detection ───\n\nexport function detectRepo(cwd: string): string {\n try {\n const url = execSync('git remote get-url origin 2>/dev/null', { cwd, timeout: 3000, encoding: 'utf-8', stdio: ['pipe', 'pipe', 'pipe'] }).trim();\n if (!url) return '';\n return url\n .replace(/^git@[^:]+:/, '')\n .replace(/^https?:\\\\/\\\\/[^/]+\\\\//, '')\n .replace(/\\\\.git$/, '');\n } catch {\n return '';\n }\n}\n\n// ─── Channel Health ───\n\nexport async function channelUp(port = 8929): Promise<boolean> {\n return new Promise(resolve => {\n const sock = require('node:net').connect(port, '127.0.0.1');\n const done = (ok: boolean) => { try { sock.destroy(); } catch {} resolve(ok); };\n sock.once('connect', () => done(true));\n sock.once('error', () => done(false));\n sock.setTimeout(500, () => done(false));\n });\n}\n\nexport async function cweChannelUp(): Promise<boolean> {\n return channelUp(8930);\n}\n\n// ─── Config Loading ───\n\nexport interface Rule {\n rule_id: string;\n text: string;\n severity: string;\n category: string;\n mode: string;\n}\n\nexport interface HookConfig {\n captureDepth: string;\n tier: string;\n silent: boolean;\n policyName: string;\n rules: Rule[];\n scanExemptions: Array<{ path: string; cwe_id: string }>;\n}\n\nexport async function loadConfig(jwt: string, query?: string): Promise<HookConfig> {\n const config: HookConfig = {\n captureDepth: 'local_only',\n tier: 'standard',\n silent: false,\n policyName: '',\n rules: [],\n scanExemptions: [],\n };\n\n // Local-first: read from ~/.synkro/rules.json if it exists (zero latency, no network)\n const localRulesPath = join(HOME, '.synkro', 'rules.json');\n try {\n if (existsSync(localRulesPath)) {\n const raw = JSON.parse(readFileSync(localRulesPath, 'utf-8'));\n const activePolicyId = raw.config?.activePolicyId || 'local-policy';\n const policy = (raw.policies || []).find((p: any) => p.id === activePolicyId) || raw.policies?.[0];\n if (policy) {\n config.policyName = policy.name || '';\n config.rules = (policy.rules || [])\n .filter((r: any) => r.hook_stage === 'pre' || r.hook_stage === 'both' || r.hook_stage == null)\n .map((r: any) => ({\n rule_id: r.rule_id || '',\n text: r.text || '',\n severity: r.severity || '',\n category: r.category || '',\n mode: r.mode || 'blocking',\n }));\n }\n config.silent = raw.config?.silent === true;\n if (Array.isArray(raw.scanExemptions)) {\n config.scanExemptions = raw.scanExemptions\n .filter((e: any) => e && typeof e.path === 'string')\n .map((e: any) => ({ path: e.path, cwe_id: e.cwe_id || '' }));\n }\n return config;\n }\n } catch {}\n\n // Fallback: fetch from cloud API\n try {\n const url = GATEWAY_URL + '/api/v1/hook/config' + (query ? '?' + query : '');\n const resp = await fetch(url, {\n headers: { Authorization: 'Bearer ' + jwt },\n signal: AbortSignal.timeout(4000),\n });\n const data = await resp.json() as any;\n config.captureDepth = data.capture_depth || 'local_only';\n config.tier = data.tier || 'standard';\n config.silent = data.silent_mode === true || data.silent_mode === 'true';\n config.policyName = data.active_policy_name || '';\n if (Array.isArray(data.scan_exemptions)) {\n config.scanExemptions = data.scan_exemptions\n .filter((e: any) => e && typeof e.path === 'string')\n .map((e: any) => ({ path: e.path, cwe_id: e.cwe_id || '' }));\n }\n if (Array.isArray(data.rules)) {\n config.rules = data.rules\n .filter((r: any) => r.hook_stage === 'pre' || r.hook_stage === 'both' || r.hook_stage == null)\n .map((r: any) => ({\n rule_id: r.rule_id || '',\n text: r.text || '',\n severity: r.severity || '',\n category: r.category || '',\n mode: r.mode || 'blocking',\n }));\n }\n } catch {}\n return config;\n}\n\n// ─── Routing ───\n\nexport async function route(config: HookConfig): Promise<'local' | 'cloud'> {\n if (config.captureDepth === 'local_only') return 'local';\n if (await channelUp()) return 'local';\n return 'cloud';\n}\n\nexport async function cweRoute(config: HookConfig): Promise<'local' | 'cloud'> {\n if (config.captureDepth === 'local_only') return 'local';\n if (await cweChannelUp()) return 'local';\n return 'cloud';\n}\n\n// ─── Tag Building ───\n\nexport function tag(rt: string, config: HookConfig): string {\n if (config.silent) return '[synkro:silent]';\n const rs = config.policyName || 'all';\n return '[synkro:' + rt + ':' + rs + ']';\n}\n\n// ─── Local Grading (direct channel call) ───\n\ntype GradeRole = 'grade-edit' | 'grade-bash' | 'grade-plan' | 'grade-cwe';\n\nconst ROLE_MAP: Record<string, GradeRole> = {\n edit: 'grade-edit', bash: 'grade-bash', plan: 'grade-plan', cwe: 'grade-cwe',\n};\n\nconst PRIMER_KEY: Record<GradeRole, string> = {\n 'grade-edit': 'grader_primer_edit',\n 'grade-bash': 'grader_primer_bash',\n 'grade-plan': 'grader_primer_plan',\n 'grade-cwe': 'grader_primer_cwe',\n};\n\nlet primerCache: { data: Record<string, string>; ts: number } | null = null;\n\nasync function fetchPrimer(role: GradeRole, jwt: string): Promise<string> {\n if (primerCache && Date.now() - primerCache.ts < 300_000) {\n const cached = primerCache.data[PRIMER_KEY[role]];\n if (cached) return cached;\n }\n const resp = await fetch(GATEWAY_URL + '/api/v1/cli/judge-prompts', {\n headers: { Authorization: 'Bearer ' + jwt },\n signal: AbortSignal.timeout(4000),\n });\n if (!resp.ok) throw new Error('primer fetch failed: ' + resp.status);\n const data = await resp.json() as Record<string, string>;\n primerCache = { data, ts: Date.now() };\n return data[PRIMER_KEY[role]] || '';\n}\n\nconst CHANNEL_REPLY_INSTRUCTIONS = \\`\nDELIVERY METHOD — MANDATORY, OVERRIDES ALL OTHER OUTPUT RULES:\nYou are running inside a Synkro MCP channel. Do NOT output your verdict as text.\nInstead, after generating your verdict, call the reply tool EXACTLY ONCE with:\n - req_id: the req_id from this channel event's meta\n - result: your complete verdict block as a string (the <synkro-verdict>…</synkro-verdict> XML)\nAny text output is silently discarded. Only the reply tool call is captured.\\`;\n\nasync function channelGrade(role: GradeRole, prompt: string, jwt: string, port: number, timeoutMs = 20000): Promise<string> {\n const primer = await fetchPrimer(role, jwt);\n const content = primer + '\\\\n\\\\n' + CHANNEL_REPLY_INSTRUCTIONS + '\\\\n\\\\n---\\\\nPAYLOAD (the input to evaluate):\\\\n\\\\n' + prompt;\n const body = JSON.stringify({ role, content });\n\n const resp = await fetch('http://127.0.0.1:' + port + '/submit', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body,\n signal: AbortSignal.timeout(timeoutMs),\n });\n\n if (!resp.ok) {\n const text = await resp.text().catch(() => '');\n throw new Error('channel ' + resp.status + ': ' + text.slice(0, 200));\n }\n\n const data = await resp.json() as { result?: string; error?: string };\n if (data.error) throw new Error(data.error);\n return String(data.result || '');\n}\n\nexport async function localGrade(surface: string, prompt: string, timeoutMs = 20000): Promise<string> {\n if (!(await channelUp())) throw new Error('SYNKRO_CHANNEL_DOWN');\n const jwt = loadJwt();\n if (!jwt) throw new Error('NO_JWT');\n return channelGrade(ROLE_MAP[surface] || 'grade-edit', prompt, jwt, 8929, timeoutMs);\n}\n\nexport async function localGradeCwe(prompt: string): Promise<string> {\n const jwt = loadJwt();\n if (!jwt) throw new Error('NO_JWT');\n return channelGrade('grade-cwe', prompt, jwt, 8930, 45000);\n}\n\n// ─── Verdict Parsing ───\n\nexport interface Verdict {\n ok: boolean;\n reason: string;\n suggestedFix: string;\n ruleId: string;\n ruleMode: string;\n severity: string;\n category: string;\n}\n\nexport function parseVerdict(resp: string): Verdict {\n const verdict: Verdict = {\n ok: true,\n reason: '',\n suggestedFix: '',\n ruleId: '',\n ruleMode: '',\n severity: 'low',\n category: 'clean',\n };\n\n // Flatten newlines for easier regex\n const flat = resp.replace(/\\\\n/g, ' ');\n const outerMatch = flat.match(/<synkro-verdict>(.*)<\\\\/synkro-verdict>/);\n if (!outerMatch) return verdict;\n const inner = outerMatch[1];\n\n const okMatch = inner.match(/<ok>(.*?)<\\\\/ok>/);\n if (okMatch) verdict.ok = okMatch[1].trim() !== 'false';\n\n const reasonMatch = inner.match(/<reason>(.*?)<\\\\/reason>/) || inner.match(/<reasoning>(.*?)<\\\\/reasoning>/);\n if (reasonMatch) verdict.reason = reasonMatch[1].trim();\n\n const fixMatch = inner.match(/<suggested_fix>(.*?)<\\\\/suggested_fix>/);\n if (fixMatch) verdict.suggestedFix = fixMatch[1].trim();\n\n if (!verdict.ok) {\n const ruleIdMatch = inner.match(/<rule_id>(.*?)<\\\\/rule_id>/);\n const ruleModeMatch = inner.match(/<rule_mode>(.*?)<\\\\/rule_mode>/);\n const sevMatch = inner.match(/<risk_level>(.*?)<\\\\/risk_level>/);\n\n if (ruleIdMatch) {\n verdict.ruleId = ruleIdMatch[1].trim();\n } else {\n // Try to find inside a <violation> block\n const violationMatch = inner.match(/<violation>(.*?)<\\\\/violation>/);\n if (violationMatch) {\n const vBlock = violationMatch[1];\n const vRuleId = vBlock.match(/<rule_id>(.*?)<\\\\/rule_id>/);\n if (vRuleId) verdict.ruleId = vRuleId[1].trim();\n if (!verdict.reason) {\n const vReason = vBlock.match(/<reason>(.*?)<\\\\/reason>/);\n if (vReason) verdict.reason = vReason[1].trim();\n }\n if (!verdict.suggestedFix) {\n const vFix = vBlock.match(/<suggested_fix>(.*?)<\\\\/suggested_fix>/);\n if (vFix) verdict.suggestedFix = vFix[1].trim();\n }\n if (!sevMatch) {\n const vSev = vBlock.match(/<severity>(.*?)<\\\\/severity>/);\n if (vSev) verdict.severity = vSev[1].trim();\n }\n }\n }\n\n if (ruleModeMatch) verdict.ruleMode = ruleModeMatch[1].trim();\n if (sevMatch) verdict.severity = sevMatch[1].trim();\n verdict.severity = verdict.severity || 'high';\n\n const catMatch = inner.match(/<category>(.*?)<\\\\/category>/);\n verdict.category = catMatch ? catMatch[1].trim() : 'uncategorized';\n\n // Fallback: extract rule ID from reason text\n if (!verdict.ruleId && verdict.reason) {\n const rMatch = verdict.reason.match(/[Rr]\\\\d{3}/);\n if (rMatch) verdict.ruleId = rMatch[0];\n }\n }\n\n return verdict;\n}\n\n// ─── Telemetry Dispatch ───\n\nexport function dispatchCapture(\n jwt: string,\n hookType: string,\n verdictStr: string,\n severity: string,\n category: string,\n toolName: string,\n repo: string,\n sessionId: string,\n captureDepth: string,\n opts?: {\n command?: string;\n reasoning?: string;\n rulesChecked?: Rule[] | string;\n violatedRules?: string[];\n recentUserMessages?: string[];\n ccModel?: string;\n },\n): void {\n // Fire-and-forget\n const eventId = 'evt_' + Date.now() + '_' + process.pid;\n const model = opts?.ccModel || 'unknown';\n const sendFull =\n captureDepth === 'full' ||\n (captureDepth === 'evidence_on_violation' && ['block', 'warning', 'deny'].includes(verdictStr));\n\n const body: Record<string, any> = {\n capture_type: 'local_verdict',\n event_id: eventId,\n hook_type: hookType,\n verdict: verdictStr,\n severity,\n category,\n cc_model: model,\n model,\n tool_name: toolName,\n };\n if (repo) body.repo = repo;\n if (sessionId) body.session_id = sessionId;\n\n // Local telemetry always gets full content — data never leaves the machine\n const localBody = { ...body };\n if (opts) {\n if (opts.command) localBody.command = opts.command;\n if (opts.reasoning) localBody.reasoning = opts.reasoning;\n if (opts.rulesChecked) localBody.rules_checked = opts.rulesChecked;\n if (opts.violatedRules) localBody.violated_rules = opts.violatedRules;\n if (opts.recentUserMessages) localBody.recent_user_messages = opts.recentUserMessages;\n }\n appendLocalTelemetry(localBody);\n\n // local_only: no data leaves the machine\n if (captureDepth === 'local_only') return;\n\n if (sendFull && opts) {\n body.capture_depth = captureDepth;\n if (opts.command) body.command = opts.command;\n if (opts.reasoning) body.reasoning = opts.reasoning;\n if (opts.rulesChecked) body.rules_checked = opts.rulesChecked;\n if (opts.violatedRules) body.violated_rules = opts.violatedRules;\n if (opts.recentUserMessages) body.recent_user_messages = opts.recentUserMessages;\n }\n\n fetch(GATEWAY_URL + '/api/v1/hook/capture', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json', Authorization: 'Bearer ' + jwt },\n body: JSON.stringify(body),\n signal: AbortSignal.timeout(3000),\n }).catch(() => {});\n}\n\nexport function appendLocalTelemetry(body: Record<string, any>): void {\n try {\n const telPath = join(HOME, '.synkro', 'telemetry.jsonl');\n appendFileSync(telPath, JSON.stringify({ ...body, _ts: new Date().toISOString() }) + '\\\\n', 'utf-8');\n } catch {}\n}\n\n// ─── Rule Mode Lookup ───\n\nexport function ruleMode(ruleId: string, rules: Rule[]): 'blocking' | 'audit' {\n if (!ruleId || !rules.length) return 'blocking';\n const matched = rules.filter(r => r.rule_id === ruleId);\n if (matched.some(r => r.mode === 'blocking')) return 'blocking';\n return (matched[0]?.mode as 'blocking' | 'audit') || 'blocking';\n}\n\n// ─── Content Reconstruction ───\n\nexport function reconstructContent(toolName: string, toolInput: any, filePath: string, cwd?: string): string {\n const canRead = filePath && cwd && isPathUnder(filePath, cwd);\n switch (toolName) {\n case 'Write':\n return toolInput.content || '';\n case 'Edit': {\n let content = '';\n try {\n if (canRead && existsSync(filePath)) {\n content = readFileSync(filePath, 'utf-8').slice(0, 65536);\n }\n } catch {}\n const oldStr = toolInput.old_string || '';\n const newStr = toolInput.new_string || '';\n if (oldStr && content.includes(oldStr)) {\n return content.replace(oldStr, newStr);\n }\n return content || newStr;\n }\n case 'MultiEdit': {\n let content = '';\n try {\n if (canRead && existsSync(filePath)) {\n content = readFileSync(filePath, 'utf-8').slice(0, 65536);\n }\n } catch {}\n const edits = Array.isArray(toolInput.edits) ? toolInput.edits : [];\n for (const edit of edits) {\n if (!edit || typeof edit !== 'object') continue;\n const old = edit.old_string || '';\n const nw = edit.new_string || '';\n if (old && content.includes(old)) {\n content = content.replace(old, nw);\n }\n }\n return content;\n }\n case 'NotebookEdit':\n return toolInput.new_source || '';\n case 'StrReplace':\n return toolInput.new_string || toolInput.content || toolInput.code_edit || '';\n default:\n return toolInput.content || toolInput.new_string || toolInput.code_edit || '';\n }\n}\n\n// ─── HTTP with Retry ───\n\nexport async function postWithRetry(url: string, body: any, jwt: string, timeout = 8000): Promise<any> {\n let currentJwt = jwt;\n let resp: Response;\n try {\n resp = await fetch(url, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json', Authorization: 'Bearer ' + currentJwt },\n body: JSON.stringify(body),\n signal: AbortSignal.timeout(timeout),\n });\n } catch {\n return null;\n }\n\n let data: any;\n try { data = await resp.json(); } catch { return null; }\n\n // Retry on token expiry\n if (data?.detail && (data.detail.includes('Token has expired') || data.detail.includes('Invalid or expired token'))) {\n try {\n currentJwt = await refreshJwt(currentJwt);\n const resp2 = await fetch(url, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json', Authorization: 'Bearer ' + currentJwt },\n body: JSON.stringify(body),\n signal: AbortSignal.timeout(timeout),\n });\n data = await resp2.json();\n } catch {\n return null;\n }\n }\n\n return data;\n}\n\n// ─── Read Stdin ───\n\nexport async function readStdin(): Promise<string> {\n const chunks: Buffer[] = [];\n for await (const chunk of process.stdin) {\n chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));\n }\n return Buffer.concat(chunks).toString('utf-8');\n}\n\n// ─── Transcript Extraction ───\n\nexport interface TranscriptContext {\n userIntent: string;\n recentUserMessages: string[];\n recentMessages: Array<{ type: string; text: string }>;\n recentActions: Array<{ tool: string; input: string }>;\n sessionSummary: string;\n ccModel: string;\n ccUsage: Record<string, any>;\n}\n\nexport function extractTranscript(transcriptPath: string | undefined): TranscriptContext {\n const ctx: TranscriptContext = {\n userIntent: '',\n recentUserMessages: [],\n recentMessages: [],\n recentActions: [],\n sessionSummary: '',\n ccModel: '',\n ccUsage: {},\n };\n\n if (!transcriptPath || !existsSync(transcriptPath)) return ctx;\n\n try {\n const raw = readFileSync(transcriptPath, 'utf-8');\n const lines = raw.split('\\\\n').filter(l => l.trim());\n // Take the last 400 lines\n const tail = lines.slice(-400);\n\n const parsed: any[] = [];\n for (const line of tail) {\n try { parsed.push(JSON.parse(line)); } catch {}\n }\n\n // Recent user messages (last 5)\n const userMsgs: string[] = [];\n for (const entry of parsed) {\n if (entry.type !== 'user') continue;\n const content = entry.message?.content;\n let text = '';\n if (typeof content === 'string') text = content;\n else if (Array.isArray(content)) text = content.map((c: any) => c.text || '').join(' ');\n if (text) userMsgs.push(text);\n }\n ctx.recentUserMessages = userMsgs.slice(-5);\n ctx.userIntent = ctx.recentUserMessages[ctx.recentUserMessages.length - 1] || '';\n\n // Recent messages (last 10, user + assistant)\n const msgs: Array<{ type: string; text: string }> = [];\n for (const entry of parsed) {\n if (entry.type !== 'user' && entry.type !== 'assistant') continue;\n const content = entry.message?.content;\n let text = '';\n if (typeof content === 'string') text = content.slice(0, 500);\n else if (Array.isArray(content)) text = content.map((c: any) => (c.text || '').slice(0, 300)).join(' ');\n msgs.push({ type: entry.type, text });\n }\n ctx.recentMessages = msgs.slice(-10);\n\n // Recent tool calls (last 5)\n const actions: Array<{ tool: string; input: string }> = [];\n for (const entry of parsed) {\n if (entry.type !== 'assistant') continue;\n const content = entry.message?.content;\n if (!Array.isArray(content)) continue;\n for (const block of content) {\n if (block.type !== 'tool_use') continue;\n actions.push({\n tool: block.name || '',\n input: JSON.stringify(block.input || {}).slice(0, 200),\n });\n }\n }\n ctx.recentActions = actions.slice(-5);\n\n // Session summary\n for (const entry of parsed) {\n if (entry.type === 'summary' && entry.summary) {\n ctx.sessionSummary = entry.summary;\n }\n }\n\n // CC model\n const assistantEntries = parsed.filter(e => e.type === 'assistant');\n if (assistantEntries.length > 0) {\n const last = assistantEntries[assistantEntries.length - 1];\n ctx.ccModel = last.message?.model || '';\n const usage = last.message?.usage;\n if (usage) {\n ctx.ccUsage = {\n input_tokens: usage.input_tokens,\n output_tokens: usage.output_tokens,\n cache_creation_input_tokens: usage.cache_creation_input_tokens,\n cache_read_input_tokens: usage.cache_read_input_tokens,\n };\n }\n }\n } catch {}\n\n return ctx;\n}\n\n// ─── Last Prompt ───\n\nexport function readLastPrompt(): string {\n try {\n if (!existsSync(LAST_PROMPT_FILE)) return '';\n return readFileSync(LAST_PROMPT_FILE, 'utf-8').trim();\n } catch {\n return '';\n }\n}\n\n// ─── Find Nearest Package Dependencies ───\n\nexport function findNearestDeps(filePath: string): Record<string, string> {\n let dir = dirname(filePath);\n while (dir !== '/' && dir !== '.') {\n const pkg = join(dir, 'package.json');\n if (existsSync(pkg)) {\n try {\n const data = JSON.parse(readFileSync(pkg, 'utf-8'));\n return { ...(data.dependencies || {}), ...(data.devDependencies || {}) };\n } catch {}\n }\n const parent = dirname(dir);\n if (parent === dir) break;\n dir = parent;\n }\n return {};\n}\n\n// ─── Consent Tracking ───\n\nconst CONSENT_FILE = join(HOME, '.synkro', '.local-consent');\n\nexport function consentGrant(sessionId: string, hash: string): void {\n try {\n const dir = dirname(CONSENT_FILE);\n if (!existsSync(dir)) mkdirSync(dir, { recursive: true });\n const line = sessionId + '\\\\t' + hash + '\\\\tactive\\\\n';\n const { appendFileSync } = require('node:fs');\n appendFileSync(CONSENT_FILE, line, 'utf-8');\n } catch {}\n}\n\nexport function consentHasActive(sessionId: string, hash: string): boolean {\n try {\n if (!existsSync(CONSENT_FILE)) return false;\n const content = readFileSync(CONSENT_FILE, 'utf-8');\n return content.includes(sessionId + '\\\\t' + hash + '\\\\tactive');\n } catch {\n return false;\n }\n}\n\nexport function consentConsume(sessionId: string, hash: string): void {\n try {\n if (!existsSync(CONSENT_FILE)) return;\n const content = readFileSync(CONSENT_FILE, 'utf-8');\n const target = sessionId + '\\\\t' + hash + '\\\\tactive';\n const replacement = sessionId + '\\\\t' + hash + '\\\\tconsumed';\n const updated = content.split('\\\\n').map(l => l === target ? replacement : l).join('\\\\n');\n writeFileSync(CONSENT_FILE, updated, 'utf-8');\n } catch {}\n}\n\n// ─── Crypto Hash ───\n\nexport function hashCommand(cmd: string): string {\n const { createHash } = require('node:crypto');\n return createHash('sha256').update(cmd).digest('hex').slice(0, 16);\n}\n\n// ─── Transcript Usage Aggregation ───\n\nexport function aggregateUsage(transcriptPath: string): { model: string; totals: Record<string, number> } {\n const result = { model: '', totals: { in: 0, out: 0, cw: 0, cr: 0 } };\n if (!transcriptPath || !existsSync(transcriptPath)) return result;\n try {\n const raw = readFileSync(transcriptPath, 'utf-8');\n const lines = raw.split('\\\\n').filter(l => l.trim());\n for (const line of lines) {\n try {\n const entry = JSON.parse(line);\n if (entry.type !== 'assistant') continue;\n result.model = entry.message?.model || result.model;\n const u = entry.message?.usage;\n if (u) {\n result.totals.in += u.input_tokens || 0;\n result.totals.out += u.output_tokens || 0;\n result.totals.cw += u.cache_creation_input_tokens || 0;\n result.totals.cr += u.cache_read_input_tokens || 0;\n }\n } catch {}\n }\n } catch {}\n return result;\n}\n\n// ─── Scan Finding Dispatch ───\n\nexport function dispatchFinding(\n jwt: string,\n finding: {\n session_id: string;\n file_path: string;\n finding_type: 'cwe' | 'cve';\n finding_id: string;\n severity?: string;\n status: 'open' | 'resolved' | 'exempted';\n detail?: string;\n description?: string;\n package_name?: string;\n package_version?: string;\n fixed_version?: string;\n aliases?: string[];\n references?: Array<{ type: string; url: string }>;\n cwe_name?: string;\n },\n captureDepth: string,\n): void {\n const localEntry: Record<string, any> = {\n capture_type: 'scan_finding',\n ...finding,\n };\n appendLocalTelemetry(localEntry);\n\n if (captureDepth === 'local_only') return;\n\n const cloudBody: Record<string, any> = {\n finding_type: finding.finding_type,\n finding_id: finding.finding_id,\n severity: finding.severity,\n status: finding.status,\n session_id: finding.session_id,\n };\n\n if (captureDepth === 'evidence_on_violation' || captureDepth === 'full') {\n cloudBody.file_path = finding.file_path;\n cloudBody.package_name = finding.package_name;\n cloudBody.package_version = finding.package_version;\n cloudBody.fixed_version = finding.fixed_version;\n cloudBody.aliases = finding.aliases;\n cloudBody.references = finding.references;\n cloudBody.cwe_name = finding.cwe_name;\n }\n if (captureDepth === 'full') {\n cloudBody.detail = finding.detail;\n cloudBody.description = finding.description;\n }\n\n fetch(GATEWAY_URL + '/api/v1/hook/finding', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json', Authorization: 'Bearer ' + jwt },\n body: JSON.stringify(cloudBody),\n signal: AbortSignal.timeout(3000),\n }).catch(() => {});\n}\n\n// ─── Hook tool-name sets (CC + Cursor) ───\n\nexport const EDIT_TOOL_NAMES = new Set([\n 'Edit', 'Write', 'MultiEdit', 'NotebookEdit', 'StrReplace',\n]);\nexport const SHELL_TOOL_NAMES = new Set([\n 'Bash', 'Shell', 'Read', 'Grep', 'Glob', 'terminal', 'run_terminal_cmd', 'execute_command',\n]);\nexport const AGENT_TOOL_NAMES = new Set(['Agent', 'Task']);\nexport const PLAN_TOOL_NAMES = new Set(['ExitPlanMode', 'SwitchMode', 'CreatePlan']);\n\nexport function isEditTool(toolName: string): boolean {\n return EDIT_TOOL_NAMES.has(toolName);\n}\nexport function isShellTool(toolName: string): boolean {\n return SHELL_TOOL_NAMES.has(toolName);\n}\nexport function isAgentTool(toolName: string): boolean {\n return AGENT_TOOL_NAMES.has(toolName);\n}\nexport function isPlanTool(toolName: string): boolean {\n return PLAN_TOOL_NAMES.has(toolName);\n}\n\nexport function hookSessionId(payload: Record<string, unknown>): string {\n return String(payload.session_id ?? payload.conversation_id ?? '');\n}\n\nexport function isCursorHookFormat(): boolean {\n return process.env.SYNKRO_HOOK_FORMAT === 'cursor';\n}\n\nlet cursorHookExited = false;\n\nexport function setupCursorHookSignals(): void {\n if (!isCursorHookFormat()) return;\n process.on('SIGTERM', () => outputEmpty());\n}\n\nfunction cursorHookExit(): never {\n cursorHookExited = true;\n process.exit(0);\n}\n\n// ─── Output Helpers ───\n\nexport function outputJson(obj: any): void {\n if (isCursorHookFormat()) {\n if (obj?.permission === 'allow') {\n const u = typeof obj.user_message === 'string' ? obj.user_message : '';\n const a = typeof obj.agent_message === 'string' ? obj.agent_message : u;\n if (u || a) {\n if (!cursorHookExited) {\n cursorHookExited = true;\n process.stdout.write(JSON.stringify({ permission: 'allow' }) + '\\\\n');\n }\n cursorHookExit();\n }\n }\n const hso = obj?.hookSpecificOutput;\n const sys = typeof obj?.systemMessage === 'string' ? obj.systemMessage : '';\n if (hso?.permissionDecision === 'deny') {\n const reason = hso.permissionDecisionReason || hso.additionalContext || sys;\n if (!cursorHookExited) {\n cursorHookExited = true;\n process.stdout.write(JSON.stringify({\n permission: 'deny',\n user_message: sys || reason,\n agent_message: hso.additionalContext || reason,\n }) + '\\\\n');\n }\n cursorHookExit();\n }\n const addCtx = typeof hso?.additionalContext === 'string' ? hso.additionalContext : '';\n const ctx = sys || addCtx;\n if (ctx) {\n if (!cursorHookExited) {\n cursorHookExited = true;\n process.stdout.write(JSON.stringify({ permission: 'allow' }) + '\\\\n');\n }\n cursorHookExit();\n }\n outputEmpty();\n return;\n }\n console.log(JSON.stringify(obj));\n}\n\nexport function outputEmpty(): void {\n if (isCursorHookFormat()) {\n if (!cursorHookExited) {\n cursorHookExited = true;\n try { process.stdout.write('{}\\\\n'); } catch {}\n }\n cursorHookExit();\n }\n console.log('{}');\n}\n`;\n\n\n// ─── CC PreToolUse Edit/Write/MultiEdit/NotebookEdit pre-check (TypeScript) ───\n\nexport const EDIT_PRECHECK_TS = `#!/usr/bin/env bun\nimport {\n loadJwt, ensureFreshJwt, detectRepo, loadConfig, route, tag, localGrade,\n parseVerdict, dispatchCapture, ruleMode, reconstructContent, isPathUnder, postWithRetry,\n readStdin, extractTranscript, readLastPrompt, findNearestDeps, log,\n outputJson, outputEmpty, setupCursorHookSignals, isEditTool, hookSessionId, GATEWAY_URL,\n type HookConfig, type Rule,\n} from './_synkro-common.ts';\nimport { existsSync, readFileSync } from 'node:fs';\nimport { basename, dirname, join } from 'node:path';\n\nasync function main() {\n setupCursorHookSignals();\n try {\n const input = await readStdin();\n if (!input.trim()) { outputEmpty(); return; }\n\n const payload = JSON.parse(input);\n const toolName = payload.tool_name || '';\n if (!isEditTool(toolName)) {\n outputEmpty();\n return;\n }\n\n const toolInput = payload.tool_input || {};\n const sessionId = hookSessionId(payload);\n const toolUseId = payload.tool_use_id || '';\n const cwd = payload.cwd || '';\n const permissionMode = payload.permission_mode || '';\n const transcriptPath = payload.transcript_path || '';\n\n const filePath = toolInput.file_path || toolInput.notebook_path || toolInput.path || '';\n if (!filePath) { outputEmpty(); return; }\n\n if (filePath.includes('/.synkro/hooks/')) { outputEmpty(); return; }\n\n const fileShort = basename(filePath);\n log('editGuard checking: ' + fileShort);\n\n const gitRepo = detectRepo(cwd || '.');\n\n let jwt = loadJwt();\n if (!jwt) { outputEmpty(); return; }\n jwt = await ensureFreshJwt(jwt);\n\n // Reconstruct proposed content\n const proposed = reconstructContent(toolName, toolInput, filePath, cwd);\n if (!proposed) { outputEmpty(); return; }\n\n // Build diff field\n let diffField: any = null;\n if (toolInput.old_string != null || toolInput.new_string != null || toolInput.edits != null) {\n diffField = {};\n if (toolInput.old_string != null) diffField.old_string = toolInput.old_string;\n if (toolInput.new_string != null) diffField.new_string = toolInput.new_string;\n if (toolInput.edits != null) diffField.edits = toolInput.edits;\n }\n\n // Read file before edit for cloud payload\n let fileBefore = '';\n if (toolName !== 'Write' && filePath && isPathUnder(filePath, cwd || '.') && existsSync(filePath)) {\n try { fileBefore = readFileSync(filePath, 'utf-8').slice(0, 65536); } catch {}\n }\n\n // Extract transcript context\n const transcript = extractTranscript(transcriptPath);\n const lastPrompt = readLastPrompt();\n\n // Load config and decide route\n const config = await loadConfig(jwt);\n const rt = await route(config);\n const tagStr = tag(rt, config);\n\n if (config.silent) {\n outputJson({ systemMessage: tagStr + ' editGuard \\\\u2192 skipped (silent mode)' });\n return;\n }\n\n if (rt === 'local') {\n // ─── Local grading: org rules ONLY (channel 1, port 8929) ───\n const proposedShort = proposed.slice(0, 4000);\n const graderPrompt = [\n 'Working directory: ' + (cwd || '.'),\n 'Repo: ' + (gitRepo || 'unknown'),\n 'File: ' + filePath,\n 'Proposed content (first 4000 chars):',\n proposedShort,\n 'User intent (last human message): ' + (transcript.userIntent || 'none stated'),\n 'Last user prompt: ' + (lastPrompt || 'none'),\n 'Org rules: ' + JSON.stringify(config.rules),\n ].join('\\\\n');\n\n let gradeResp: string;\n try {\n gradeResp = await localGrade('edit', graderPrompt);\n } catch {\n outputJson({ systemMessage: tagStr + ' editGuard ' + fileShort + ' \\\\u2192 local grader unavailable, skipped' });\n return;\n }\n\n const verdict = parseVerdict(gradeResp);\n const editContent = 'file=' + filePath + ' content=' + proposed.slice(0, 2000);\n const violatedRules = verdict.ruleId ? [verdict.ruleId] : [];\n\n if (!verdict.ok) {\n const mode = verdict.ruleMode || ruleMode(verdict.ruleId, config.rules);\n const guardReason = (verdict.ruleId ? '(' + verdict.ruleId + ') ' : '') + (verdict.reason || 'policy violation');\n\n if (mode !== 'audit') {\n const denyReason = 'Guard: ' + guardReason + '\\\\nFix all issues before retrying. Do NOT ask the user to make the edit manually — resolve the violation in code yourself.';\n dispatchCapture(jwt, 'edit', 'block', verdict.severity || 'critical', verdict.category || 'security',\n toolName, gitRepo, sessionId, config.captureDepth, {\n command: editContent, reasoning: guardReason,\n rulesChecked: config.rules, violatedRules,\n ccModel: transcript.ccModel,\n });\n outputJson({\n systemMessage: tagStr + ' editGuard ' + fileShort + ' \\\\u2192 blocked: ' + guardReason,\n hookSpecificOutput: { hookEventName: 'PreToolUse', permissionDecision: 'deny', permissionDecisionReason: denyReason, additionalContext: denyReason },\n });\n return;\n }\n\n // Audit mode — warn but allow\n dispatchCapture(jwt, 'edit', 'warning', verdict.severity || 'medium', verdict.category || 'security',\n toolName, gitRepo, sessionId, config.captureDepth, {\n command: editContent, reasoning: guardReason,\n rulesChecked: config.rules, violatedRules,\n ccModel: transcript.ccModel,\n });\n outputJson({ systemMessage: tagStr + ' editGuard ' + fileShort + ' \\\\u2192 warning: ' + guardReason, hookSpecificOutput: { hookEventName: 'PreToolUse', additionalContext: 'Synkro local edit judge (audit). ' + guardReason } });\n return;\n }\n\n // Clean\n dispatchCapture(jwt, 'edit', 'pass', 'audit', verdict.category || 'trivial_edit',\n toolName, gitRepo, sessionId, config.captureDepth, {\n command: editContent, reasoning: verdict.reason || 'no policy violations detected',\n rulesChecked: config.rules, violatedRules: [],\n ccModel: transcript.ccModel,\n });\n const passLine = tagStr + ' editGuard ' + fileShort + ' \\\\u2192 pass: ' + (verdict.reason || 'no policy violations detected');\n outputJson({ systemMessage: passLine, hookSpecificOutput: { hookEventName: 'PreToolUse', additionalContext: 'Synkro local edit judge. ' + (verdict.reason || 'no policy violations detected') } });\n return;\n }\n\n // ─── Cloud grading ───\n const deps = findNearestDeps(filePath);\n const isHeadless = ['acceptEdits', 'bypassPermissions', 'plan', 'auto'].includes(permissionMode)\n || process.env.SYNKRO_HEADLESS === '1';\n\n const body = {\n hook_event: 'PreToolUse',\n tool_name: toolName,\n tool_input: toolInput,\n file_path: filePath,\n content: proposed,\n file_before: fileBefore || null,\n diff: diffField,\n dependencies: deps,\n user_intent: transcript.userIntent || null,\n last_user_message: lastPrompt || null,\n recent_user_messages: transcript.recentUserMessages,\n recent_messages: transcript.recentMessages,\n recent_actions: transcript.recentActions,\n session_id: sessionId || null,\n tool_use_id: toolUseId || null,\n cwd: cwd || null,\n repo: gitRepo || null,\n permission_mode: permissionMode || null,\n headless: isHeadless,\n };\n\n const resp = await postWithRetry(GATEWAY_URL + '/api/v1/hook/judge', body, jwt, 8000);\n\n if (!resp) {\n log('editGuard ' + fileShort + ' \\\\u2192 error (timeout)');\n outputEmpty();\n return;\n }\n\n if (!resp.hook_response || typeof resp.hook_response !== 'object') {\n log('editGuard ' + fileShort + ' \\\\u2192 pass (no hook_response)');\n outputEmpty();\n return;\n }\n\n const hookResp = resp.hook_response;\n const decision = hookResp?.hookSpecificOutput?.permissionDecision;\n\n if (decision === 'deny' || decision === 'ask') {\n log('editGuard ' + fileShort + ' \\\\u2192 BLOCKED');\n // Strip permissionDecision — we use systemMessage only\n const cleaned = { ...hookResp };\n if (cleaned.hookSpecificOutput) {\n cleaned.hookSpecificOutput = { ...cleaned.hookSpecificOutput };\n delete cleaned.hookSpecificOutput.permissionDecision;\n delete cleaned.hookSpecificOutput.permissionDecisionReason;\n }\n outputJson(cleaned);\n } else {\n const reason = hookResp.reason || '';\n log('editGuard ' + fileShort + ' \\\\u2192 pass' + (reason ? ': ' + reason : ''));\n outputJson(hookResp);\n }\n } catch (err) {\n process.stderr.write('[synkro] editGuard error: ' + String(err) + '\\\\n');\n outputEmpty();\n }\n}\n\nmain();\n`;\n\n\n// ─── CC PreToolUse CWE scan (standalone, channel 2) (TypeScript) ───\n\nexport const CWE_PRECHECK_TS = `#!/usr/bin/env bun\nimport {\n loadJwt, ensureFreshJwt, detectRepo, loadConfig, cweRoute, tag,\n localGradeCwe, parseVerdict, reconstructContent, readStdin, log,\n outputJson, outputEmpty, setupCursorHookSignals, isEditTool, hookSessionId, dispatchFinding, GATEWAY_URL,\n} from './_synkro-common.ts';\nimport { basename, extname } from 'node:path';\n\nasync function main() {\n setupCursorHookSignals();\n try {\n const input = await readStdin();\n if (!input.trim()) { outputEmpty(); return; }\n\n const payload = JSON.parse(input);\n const toolName = payload.tool_name || '';\n if (!isEditTool(toolName)) {\n outputEmpty();\n return;\n }\n\n const toolInput = payload.tool_input || {};\n const sessionId = hookSessionId(payload);\n const cwd = payload.cwd || '';\n const gitRepo = detectRepo(cwd || '.');\n\n const filePath = toolInput.file_path || toolInput.notebook_path || toolInput.path || '';\n if (!filePath) { outputEmpty(); return; }\n\n if (filePath.includes('/.synkro/hooks/')) { outputEmpty(); return; }\n\n const fileShort = basename(filePath);\n const fileExt = extname(filePath); // e.g. \".ts\"\n\n let jwt = loadJwt();\n if (!jwt) { outputEmpty(); return; }\n jwt = await ensureFreshJwt(jwt);\n\n // Reconstruct proposed content\n const proposed = reconstructContent(toolName, toolInput, filePath, cwd);\n if (!proposed) { outputEmpty(); return; }\n\n // Change-anchored window: for Edit/MultiEdit send context around the diff,\n // for Write send first 4000 chars (new files have patterns at the top).\n let cweContent: string;\n if (toolName === 'Edit' || toolName === 'MultiEdit') {\n const newStr = toolName === 'Edit'\n ? (toolInput.new_string || '')\n : (Array.isArray(toolInput.edits) ? toolInput.edits.map((e: any) => e?.new_string || '').join('\\\\n') : '');\n const changeIdx = proposed.indexOf(newStr);\n if (changeIdx >= 0 && proposed.length > 6000) {\n const start = Math.max(0, changeIdx - 2000);\n const end = Math.min(proposed.length, changeIdx + newStr.length + 2000);\n cweContent = proposed.slice(start, end);\n } else {\n cweContent = proposed.slice(0, 6000);\n }\n } else {\n cweContent = proposed.slice(0, 4000);\n }\n\n const config = await loadConfig(jwt);\n const rt = await cweRoute(config);\n\n // Build set of exempted CWE IDs for this file path\n const exemptedCwes = new Set<string>();\n for (const ex of config.scanExemptions) {\n if (ex.cwe_id && filePath.includes(ex.path)) {\n exemptedCwes.add(ex.cwe_id.toUpperCase());\n }\n }\n if (config.silent) {\n outputJson({ systemMessage: '[synkro:' + rt + ':cweScan] ' + fileShort + ' \\\\u2192 skipped (silent mode)' });\n return;\n }\n\n const cweTag = '[synkro:' + rt + ':cweScan]';\n\n if (rt === 'local') {\n // ─── Local CWE grading on channel 2 (port 8930) ───\n let cweRules: any[] = [];\n try {\n const resp = await fetch(GATEWAY_URL + '/api/v1/cwe-rules?ext=' + encodeURIComponent(fileExt), {\n headers: { Authorization: 'Bearer ' + jwt },\n signal: AbortSignal.timeout(4000),\n });\n const data = await resp.json() as any;\n cweRules = data.rules || [];\n } catch {}\n\n if (cweRules.length === 0) {\n outputJson({ systemMessage: cweTag + ' ' + fileShort + ' \\\\u2192 clean (no CWE rules for ' + fileExt + ')' });\n return;\n }\n\n const graderPrompt = [\n 'File: ' + filePath,\n 'Content:',\n cweContent,\n '',\n 'CWE rules to check against:',\n JSON.stringify(cweRules),\n ].join('\\\\n');\n\n let gradeResp: string;\n try {\n gradeResp = await localGradeCwe(graderPrompt);\n } catch (gradeErr: any) {\n const reason = gradeErr?.message || String(gradeErr);\n outputJson({ systemMessage: cweTag + ' ' + fileShort + ' \\\\u2192 grader unavailable (' + reason + '), skipped' });\n return;\n }\n\n const verdict = parseVerdict(gradeResp);\n\n if (!verdict.ok) {\n const ruleIdMatches = gradeResp.match(/<rule_id>([^<]+)<\\\\/rule_id>/g) || [];\n const cweIds: string[] = [];\n for (const match of ruleIdMatches.slice(0, 5)) {\n const id = match.replace(/<\\\\/?rule_id>/g, '').trim().replace(/^cwe-/, 'CWE-');\n if (id && !cweIds.includes(id)) cweIds.push(id);\n }\n\n const fixMatches = gradeResp.match(/<suggested_fix>([^<]+)<\\\\/suggested_fix>/g) || [];\n const fixes: Record<string, string> = {};\n for (let i = 0; i < Math.min(cweIds.length, fixMatches.length); i++) {\n fixes[cweIds[i]] = fixMatches[i].replace(/<\\\\/?suggested_fix>/g, '').trim();\n }\n\n // Filter out exempted CWEs for this file\n const activeCweIds = cweIds.filter(id => !exemptedCwes.has(id.toUpperCase()));\n\n if (activeCweIds.length === 0) {\n outputJson({ systemMessage: cweTag + ' ' + fileShort + ' \\\\u2192 clean (exempted: ' + cweIds.join(', ') + ')' });\n return;\n }\n\n const cweNameMap = new Map<string, string>();\n for (const r of cweRules) {\n if (r.cwe && r.name) cweNameMap.set(r.cwe.toUpperCase(), r.name);\n }\n\n const displayIds = activeCweIds.slice(0, 3).join(', ');\n const count = activeCweIds.length;\n const label = count === 1 ? 'match' : 'matches';\n const cweMsg = cweTag + ' ' + fileShort + ' \\\\u2192 ' + count + ' CWE ' + label + ' (' + displayIds + ')';\n const denyDetail = '[' + displayIds + '] ' + (verdict.reason || 'code weakness detected');\n const fixLines = activeCweIds\n .filter(id => fixes[id])\n .map(id => '[' + id + '] Fix: ' + fixes[id]);\n const fixHint = fixLines.length > 0 ? '\\\\n' + fixLines.join('\\\\n') : '';\n const ctx = 'CWE: ' + denyDetail + fixHint + '\\\\nFix all issues before retrying. Do NOT ask the user to make the edit manually — resolve the weakness in code yourself.';\n\n for (const cweId of activeCweIds) {\n dispatchFinding(jwt, {\n session_id: sessionId,\n file_path: filePath,\n finding_type: 'cwe',\n finding_id: cweId,\n severity: verdict.severity || 'high',\n status: 'open',\n detail: verdict.reason || 'code weakness detected',\n cwe_name: cweNameMap.get(cweId.toUpperCase()) || undefined,\n }, config.captureDepth);\n }\n\n dispatchCapture(jwt, 'cwe', 'block', verdict.severity || 'high', verdict.category || 'security',\n toolName, gitRepo, sessionId, config.captureDepth, {\n command: 'edit ' + filePath,\n reasoning: denyDetail,\n violatedRules: activeCweIds,\n });\n\n outputJson({\n systemMessage: cweMsg,\n hookSpecificOutput: { hookEventName: 'PreToolUse', permissionDecision: 'deny', permissionDecisionReason: ctx, additionalContext: ctx },\n });\n return;\n }\n\n dispatchFinding(jwt, {\n session_id: sessionId,\n file_path: filePath,\n finding_type: 'cwe',\n finding_id: 'pass',\n status: 'resolved',\n }, config.captureDepth);\n\n dispatchCapture(jwt, 'cwe', 'pass', 'audit', 'clean',\n toolName, gitRepo, sessionId, config.captureDepth, {\n command: 'edit ' + filePath,\n reasoning: verdict.reason || 'no CWE weaknesses detected',\n });\n\n const cleanMsg = cweTag + ' ' + fileShort + ' \\\\u2192 clean' + (verdict.reason ? ' (' + verdict.reason + ')' : '');\n outputJson({ systemMessage: cleanMsg, hookSpecificOutput: { hookEventName: 'PreToolUse', additionalContext: cleanMsg } });\n return;\n }\n\n // ─── Cloud CWE grading (handled by server) ───\n // Cloud edit precheck already includes CWE — this hook is a no-op for cloud.\n outputEmpty();\n } catch (err) {\n process.stderr.write('[synkro] cweGuard error: ' + String(err) + '\\\\n');\n outputEmpty();\n }\n}\n\nmain();\n`;\n\n\n// ─── CC PreToolUse CVE scan (standalone, curl only) (TypeScript) ───\n\nexport const CVE_PRECHECK_TS = `#!/usr/bin/env bun\nimport {\n loadJwt, ensureFreshJwt, detectRepo, loadConfig, route, tag,\n reconstructContent, readStdin, findNearestDeps, log,\n outputJson, outputEmpty, setupCursorHookSignals, isEditTool, hookSessionId, dispatchFinding, dispatchCapture, GATEWAY_URL,\n} from './_synkro-common.ts';\nimport { basename } from 'node:path';\n\nconst MANIFEST_NAMES = new Set([\n 'package.json', 'requirements.txt', 'requirements-dev.txt', 'requirements-test.txt',\n 'Pipfile', 'go.mod', 'go.sum', 'Gemfile', 'pom.xml', 'Cargo.toml', 'composer.json', 'pyproject.toml',\n]);\n\nfunction isManifest(filename: string): boolean {\n if (MANIFEST_NAMES.has(filename)) return true;\n if (filename.startsWith('requirements') && filename.endsWith('.txt')) return true;\n if (filename.startsWith('build.gradle')) return true;\n if (filename.endsWith('.cabal')) return true;\n return false;\n}\n\nasync function main() {\n setupCursorHookSignals();\n try {\n const input = await readStdin();\n if (!input.trim()) { outputEmpty(); return; }\n\n const payload = JSON.parse(input);\n const toolName = payload.tool_name || '';\n if (!isEditTool(toolName)) {\n outputEmpty();\n return;\n }\n\n const toolInput = payload.tool_input || {};\n const sessionId = hookSessionId(payload);\n const cwd = payload.cwd || '';\n const gitRepo = detectRepo(cwd || '.');\n\n const filePath = toolInput.file_path || toolInput.notebook_path || toolInput.path || '';\n if (!filePath) { outputEmpty(); return; }\n\n if (filePath.includes('/.synkro/hooks/')) { outputEmpty(); return; }\n\n const fileShort = basename(filePath);\n\n let jwt = loadJwt();\n if (!jwt) { outputEmpty(); return; }\n jwt = await ensureFreshJwt(jwt);\n\n const config = await loadConfig(jwt);\n const rt = await route(config);\n\n if (config.silent) {\n outputJson({ systemMessage: '[synkro:' + rt + ':cveScan] ' + fileShort + ' \\\\u2192 skipped (silent mode)' });\n return;\n }\n\n const cveTag = '[synkro:' + rt + ':cveScan]';\n\n // Reconstruct proposed content\n const proposed = reconstructContent(toolName, toolInput, filePath, cwd);\n if (!proposed) {\n outputJson({ systemMessage: cveTag + ' ' + fileShort + ' \\\\u2192 skip (no content)' });\n return;\n }\n\n const proposedShort = proposed.slice(0, 4000);\n\n // For code files, find nearest package.json and extract deps\n let deps: Record<string, string> = {};\n if (!isManifest(fileShort)) {\n deps = findNearestDeps(filePath);\n }\n\n // CVE scan via OSV API\n const cveBody = {\n file_path: filePath,\n content: proposedShort,\n dependencies: deps,\n };\n\n let cveResp: any;\n try {\n const resp = await fetch(GATEWAY_URL + '/api/v1/cve-scan', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json', Authorization: 'Bearer ' + jwt },\n body: JSON.stringify(cveBody),\n signal: AbortSignal.timeout(8000),\n });\n cveResp = await resp.json();\n } catch {\n outputJson({ systemMessage: cveTag + ' ' + fileShort + ' \\\\u2192 error (timeout)' });\n return;\n }\n\n const findings = Array.isArray(cveResp?.findings) ? cveResp.findings : [];\n if (findings.length > 0) {\n for (const f of findings.slice(0, 10)) {\n const cveId = (f.aliases || []).find((a: string) => a.startsWith('CVE-')) || f.id || 'unknown';\n dispatchFinding(jwt, {\n session_id: sessionId,\n file_path: filePath,\n finding_type: 'cve',\n finding_id: cveId,\n severity: f.severity || 'high',\n status: 'open',\n detail: f.summary || f.title || 'vulnerable dependency',\n description: f.details || undefined,\n package_name: f.package || undefined,\n package_version: f.version || undefined,\n fixed_version: f.fixed || undefined,\n aliases: f.aliases || undefined,\n references: f.references || undefined,\n }, config.captureDepth);\n }\n\n const formatFinding = (f: any): string => {\n const id = (f.aliases || []).find((a: string) => a.startsWith('CVE-')) || f.id || '?';\n const pkg = f.package || '?';\n const ver = f.version || '?';\n const title = f.title || f.summary || 'vulnerable';\n const fix = f.fixed ? ' (fix: >=' + f.fixed + ')' : ' (no safe version)';\n return '[' + id + '] ' + pkg + '@' + ver + ': ' + title + fix;\n };\n\n const top3 = findings.slice(0, 3).map(formatFinding).join('; ');\n const count = findings.length;\n const label = count === 1 ? 'advisory' : 'advisories';\n const cveMsg = cveTag + ' ' + fileShort + ' \\\\u2192 ' + count + ' ' + label;\n const ctx = 'CVE: ' + top3 + '\\\\nFix all issues before retrying. Do NOT ask the user to make the edit manually — upgrade the vulnerable dependencies yourself.';\n\n const cveIds = findings.slice(0, 10).map((f: any) =>\n (f.aliases || []).find((a: string) => a.startsWith('CVE-')) || f.id || 'unknown'\n );\n dispatchCapture(jwt, 'cve', 'block', 'critical', 'security',\n toolName, gitRepo, sessionId, config.captureDepth, {\n command: 'edit ' + filePath,\n reasoning: top3,\n violatedRules: cveIds,\n });\n\n outputJson({\n systemMessage: cveMsg,\n hookSpecificOutput: { hookEventName: 'PreToolUse', permissionDecision: 'deny', permissionDecisionReason: ctx, additionalContext: ctx },\n });\n return;\n }\n\n outputJson({ systemMessage: cveTag + ' ' + fileShort + ' \\\\u2192 clean' });\n } catch (err) {\n process.stderr.write('[synkro] cveGuard error: ' + String(err) + '\\\\n');\n outputEmpty();\n }\n}\n\nmain();\n`;\n\n\n// ─── CC PreToolUse Bash/Read/Grep/Glob judge (TypeScript) ───\n\nexport const BASH_JUDGE_TS = `#!/usr/bin/env bun\nimport {\n loadJwt, ensureFreshJwt, detectRepo, loadConfig, route, tag, localGrade,\n parseVerdict, dispatchCapture, dispatchFinding, ruleMode, postWithRetry, readStdin,\n extractTranscript, readLastPrompt, log,\n outputJson, outputEmpty, setupCursorHookSignals, isShellTool, hookSessionId, GATEWAY_URL,\n type HookConfig, type Rule,\n} from './_synkro-common.ts';\n\nconst TOP_NPM_PKGS = new Set([\n 'express','react','lodash','chalk','commander','debug','dotenv','webpack',\n 'typescript','moment','uuid','cors','body-parser','mongoose','jsonwebtoken','bcrypt',\n 'nodemon','eslint','prettier','jest','mocha','chai','sinon','supertest','request',\n 'async','bluebird','underscore','ramda','rxjs','socket.io','redis','pg','mysql',\n 'sequelize','knex','prisma','next','nuxt','vue','svelte','angular','ember',\n 'react-dom','react-router','react-redux','redux','mobx','formik','yup','zod',\n 'ajv','joi','helmet','morgan','passport','cookie-parser','express-session',\n 'multer','sharp','jimp','puppeteer','playwright','cheerio','got','node-fetch',\n 'superagent','inquirer','ora','yargs','minimist','glob','rimraf','mkdirp',\n 'fs-extra','chokidar','ws','graphql','apollo-server','fastify','koa','hapi',\n 'nest','drizzle-orm','typeorm','mikro-orm','bull','bullmq','ioredis','kafkajs',\n 'amqplib','nodemailer','handlebars','ejs','pug','marked','highlight.js',\n 'dayjs','date-fns','luxon','nanoid','cuid','short-uuid','colors','picocolors',\n 'winston','pino','bunyan','semver','tar','archiver','unzipper','crypto-js',\n 'bcryptjs','argon2','jose','openai','anthropic','langchain','tensorflow',\n 'onnxruntime-node','sharp','canvas','three','d3','chart.js','echarts',\n 'tailwindcss','postcss','autoprefixer','sass','less','styled-components',\n 'emotion','framer-motion','gsap','lottie-web','swiper','i18next',\n]);\n\nconst TOP_PYPI_PKGS = new Set([\n 'requests','flask','django','numpy','pandas','scipy','matplotlib','scikit-learn',\n 'tensorflow','torch','pytorch','keras','fastapi','uvicorn','gunicorn','celery',\n 'redis','sqlalchemy','alembic','pydantic','httpx','aiohttp','beautifulsoup4',\n 'scrapy','selenium','playwright','pillow','opencv-python','boto3','awscli',\n 'google-cloud-storage','azure-storage-blob','psycopg2','pymongo','motor',\n 'pytest','unittest2','mock','coverage','tox','black','flake8','mypy','ruff',\n 'isort','pylint','bandit','cryptography','paramiko','fabric','click','typer',\n 'rich','colorama','tqdm','loguru','python-dotenv','pyyaml','toml','orjson',\n 'ujson','marshmallow','attrs','dataclasses-json','jinja2','mako','arrow',\n 'pendulum','dateutil','pytz','regex','chardet','charset-normalizer',\n 'langchain','openai','anthropic','transformers','huggingface-hub','tokenizers',\n 'gradio','streamlit','dash','plotly','seaborn','bokeh','altair',\n]);\n\nfunction levenshtein(a: string, b: string): number {\n const m = a.length, n = b.length;\n if (Math.abs(m - n) > 2) return 3;\n const dp: number[][] = Array.from({ length: m + 1 }, (_, i) => {\n const row = new Array(n + 1).fill(0);\n row[0] = i;\n return row;\n });\n for (let j = 1; j <= n; j++) dp[0][j] = j;\n for (let i = 1; i <= m; i++) {\n for (let j = 1; j <= n; j++) {\n dp[i][j] = a[i - 1] === b[j - 1]\n ? dp[i - 1][j - 1]\n : 1 + Math.min(dp[i - 1][j], dp[i][j - 1], dp[i - 1][j - 1]);\n }\n }\n return dp[m][n];\n}\n\nfunction checkTyposquat(pkg: string, isPip: boolean): string | null {\n const topPkgs = isPip ? TOP_PYPI_PKGS : TOP_NPM_PKGS;\n if (topPkgs.has(pkg)) return null;\n const pkgLower = pkg.toLowerCase();\n for (const known of topPkgs) {\n const dist = levenshtein(pkgLower, known);\n if (dist > 0 && dist <= 2) return known;\n }\n return null;\n}\n\ninterface PkgMeta {\n deprecated?: string;\n weeklyDownloads?: number;\n}\n\nasync function main() {\n setupCursorHookSignals();\n try {\n const input = await readStdin();\n if (!input.trim()) { outputEmpty(); return; }\n\n const payload = JSON.parse(input);\n const toolName = payload.tool_name || '';\n if (!isShellTool(toolName)) {\n outputEmpty();\n return;\n }\n\n const toolInput = payload.tool_input || {};\n const sessionId = hookSessionId(payload);\n const toolUseId = payload.tool_use_id || '';\n const cwd = payload.cwd || '';\n const permissionMode = payload.permission_mode || '';\n const transcriptPath = payload.transcript_path || '';\n const gitRepo = detectRepo(cwd || '.');\n const transcript = extractTranscript(transcriptPath);\n\n let command = '';\n switch (toolName) {\n case 'Bash':\n case 'Shell':\n case 'terminal':\n case 'run_terminal_cmd':\n case 'execute_command':\n command = toolInput.command || ''; break;\n case 'Read': command = 'cat ' + (toolInput.file_path || ''); break;\n case 'Grep': command = \"grep -r '\" + (toolInput.pattern || '') + \"' \" + (toolInput.path || '.'); break;\n case 'Glob': command = \"find . -name '\" + (toolInput.pattern || '') + \"'\"; break;\n }\n if (!command) { outputEmpty(); return; }\n\n const cmdShort = command.slice(0, 80);\n log('bashGuard checking: ' + cmdShort);\n\n let jwt = loadJwt();\n if (!jwt) { outputEmpty(); return; }\n jwt = await ensureFreshJwt(jwt);\n\n // ─── Install protection: CVE + typosquat + deprecated + popularity ───\n let installScanMsg = '';\n if (toolName === 'Bash') {\n const pkgInstallMatch = command.match(\n /(?:npm\\\\s+(?:install|i|add)|pnpm\\\\s+(?:add|install|i)|yarn\\\\s+add|bun\\\\s+(?:add|install|i)|(?:uv\\\\s+)?pip3?\\\\s+install|go\\\\s+get|cargo\\\\s+add|gem\\\\s+install|composer\\\\s+require)\\\\s+([^|;&><]+)/\n );\n const isPip = /(?:uv\\\\s+)?pip3?\\\\s+install/.test(command);\n const isGo = command.match(/^go\\\\s+get/);\n const isCargo = command.match(/^cargo\\\\s+add/);\n const isGem = command.match(/^gem\\\\s+install/);\n const isComposer = command.match(/^composer\\\\s+require/);\n if (pkgInstallMatch) {\n const rawArgs = pkgInstallMatch[1];\n const deps: Record<string, string> = {};\n const tokens = rawArgs.split(/\\\\s+/);\n let skipNext = false;\n for (const token of tokens) {\n if (skipNext) { skipNext = false; continue; }\n if (!token || !/^[@a-zA-Z]/.test(token)) continue;\n if (token.startsWith('-')) {\n if (/^--(python|target|prefix|root|constraint|requirement|index-url|extra-index-url|find-links|build|src|cache-dir|filter|workspace)$/.test(token)) skipNext = true;\n continue;\n }\n if (isPip) {\n const pipMatch = token.match(/^([a-zA-Z0-9_.-]+)(?:[=~!<>]=?(.+))?$/);\n if (pipMatch) {\n deps[pipMatch[1]] = pipMatch[2]?.replace(/^=/, '') || '*';\n continue;\n }\n }\n const atIdx = token.lastIndexOf('@');\n if (atIdx > 0) {\n deps[token.slice(0, atIdx)] = token.slice(atIdx + 1);\n } else {\n deps[token] = '*';\n }\n }\n\n if (Object.keys(deps).length > 0) {\n const warnings: string[] = [];\n const pkgMeta: Record<string, PkgMeta> = {};\n\n const metaLookups = Object.keys(deps).map(async (pkg) => {\n try {\n if (isPip) {\n const r = await fetch('https://pypi.org/pypi/' + encodeURIComponent(pkg) + '/json', { signal: AbortSignal.timeout(4000) });\n if (r.ok) {\n const d = await r.json() as any;\n if (deps[pkg] === '*' && d?.info?.version) deps[pkg] = d.info.version;\n const classifiers: string[] = d?.info?.classifiers || [];\n const isInactive = classifiers.some((c: string) => /Development Status :: [67]/.test(c));\n if (isInactive) pkgMeta[pkg] = { ...pkgMeta[pkg], deprecated: 'package marked as inactive/obsolete' };\n if (d?.info?.yanked) pkgMeta[pkg] = { ...pkgMeta[pkg], deprecated: d.info.yanked_reason || 'yanked from PyPI' };\n } else if (r.status === 404) {\n warnings.push('\\\\u26a0 ' + pkg + ': package not found on PyPI \\\\u2014 may not exist');\n }\n } else {\n const verSlug = deps[pkg] !== '*' ? deps[pkg] : 'latest';\n const [metaResp, dlResp] = await Promise.all([\n fetch('https://registry.npmjs.org/' + encodeURIComponent(pkg) + '/' + verSlug, { signal: AbortSignal.timeout(4000) }),\n fetch('https://api.npmjs.org/downloads/point/last-week/' + encodeURIComponent(pkg), { signal: AbortSignal.timeout(4000) }),\n ]);\n if (metaResp.ok) {\n const d = await metaResp.json() as any;\n if (deps[pkg] === '*' && d?.version) deps[pkg] = d.version;\n if (d?.deprecated) pkgMeta[pkg] = { ...pkgMeta[pkg], deprecated: d.deprecated };\n } else if (metaResp.status === 404) {\n warnings.push('\\\\u26a0 ' + pkg + ': package not found on npm \\\\u2014 may not exist');\n }\n if (dlResp.ok) {\n const d = await dlResp.json() as any;\n if (typeof d?.downloads === 'number') pkgMeta[pkg] = { ...pkgMeta[pkg], weeklyDownloads: d.downloads };\n }\n }\n } catch {}\n });\n await Promise.all(metaLookups);\n\n for (const pkg of Object.keys(deps)) {\n const similar = checkTyposquat(pkg, isPip);\n if (similar) {\n const dl = pkgMeta[pkg]?.weeklyDownloads;\n if (dl === undefined || dl < 1000) {\n warnings.push('\\\\u26a0 ' + pkg + ': possible typosquat of \"' + similar + '\"' + (dl !== undefined ? ' (' + dl + ' weekly downloads)' : '') + ' \\\\u2014 verify package name');\n }\n }\n }\n\n for (const [pkg, meta] of Object.entries(pkgMeta)) {\n if (meta.deprecated) {\n warnings.push('\\\\u26a0 ' + pkg + ': deprecated \\\\u2014 ' + meta.deprecated);\n }\n }\n\n if (!isPip) {\n for (const [pkg, meta] of Object.entries(pkgMeta)) {\n if (meta.weeklyDownloads !== undefined && meta.weeklyDownloads < 50 && !warnings.some(w => w.includes(pkg))) {\n warnings.push('\\\\u26a0 ' + pkg + ': very low adoption (' + meta.weeklyDownloads + ' weekly downloads) \\\\u2014 consider a more established alternative');\n }\n }\n }\n\n const manifestFile = isPip ? 'requirements.txt'\n : isGo ? 'go.mod'\n : isCargo ? 'Cargo.toml'\n : isGem ? 'Gemfile'\n : isComposer ? 'composer.json'\n : 'package.json';\n const manifestContent = isPip\n ? Object.entries(deps).map(([k, v]) => v === '*' ? k : k + '==' + v).join('\\\\n')\n : JSON.stringify({ dependencies: deps });\n\n try {\n const cveBody = { file_path: manifestFile, content: manifestContent, dependencies: deps };\n const cveResp = await fetch(GATEWAY_URL + '/api/v1/cve-scan', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json', Authorization: 'Bearer ' + jwt },\n body: JSON.stringify(cveBody),\n signal: AbortSignal.timeout(8000),\n }).then(r => r.json()) as any;\n\n const findings = Array.isArray(cveResp?.findings) ? cveResp.findings : [];\n const scannedPkgs = Object.entries(deps).map(([k, v]) => k + '@' + v).join(', ');\n\n if (findings.length > 0) {\n const top3 = findings.slice(0, 3).map((f: any) => {\n const id = f.cve || f.id || '?';\n const pkg = f.package || '?';\n const ver = f.version || '?';\n const title = f.title || f.summary || 'vulnerable';\n return '[' + id + '] ' + pkg + '@' + ver + ': ' + title;\n }).join('; ');\n const count = findings.length;\n const label = count === 1 ? 'advisory' : 'advisories';\n const cveMsg = '[synkro:installScan] ' + cmdShort + ' \\\\u2192 ' + count + ' ' + label;\n const ctx = 'CVE: ' + top3 + '\\\\nDo NOT install packages with known vulnerabilities. Use a patched version or a different package.'\n + (warnings.length > 0 ? '\\\\n' + warnings.join('\\\\n') : '');\n\n const config = await loadConfig(jwt);\n for (const f of findings) {\n dispatchFinding(jwt, {\n session_id: sessionId,\n file_path: command,\n finding_type: 'cve',\n finding_id: f.cve || f.id || f.package,\n severity: typeof f.severity === 'number' ? (f.severity >= 9 ? 'critical' : f.severity >= 7 ? 'high' : f.severity >= 4 ? 'medium' : 'low') : (f.severity || 'medium'),\n status: 'open',\n detail: f.details || f.summary || null,\n description: f.summary || null,\n package_name: f.package || null,\n package_version: f.version || null,\n fixed_version: f.fixed || null,\n aliases: f.aliases || [],\n references: f.references || [],\n }, config.captureDepth);\n }\n\n const cveIds = findings.map((f: any) => f.cve || f.id || f.package);\n dispatchCapture(jwt, 'cve', 'block', 'critical', 'security',\n 'Bash', gitRepo, sessionId, config.captureDepth, {\n command,\n reasoning: top3,\n violatedRules: cveIds,\n ccModel: transcript.ccModel,\n });\n\n outputJson({\n systemMessage: cveMsg,\n hookSpecificOutput: { hookEventName: 'PreToolUse', permissionDecision: 'deny', permissionDecisionReason: ctx, additionalContext: ctx },\n });\n return;\n }\n\n const parts: string[] = ['[synkro:installScan] ' + scannedPkgs + ' \\\\u2192 clean, no known vulnerabilities'];\n if (warnings.length > 0) parts.push(...warnings);\n installScanMsg = parts.join('\\\\n');\n } catch (e) {\n log('bashGuard install scan failed: ' + String(e));\n if (warnings.length > 0) {\n installScanMsg = '[synkro:installScan] ' + warnings.join('\\\\n');\n }\n }\n }\n }\n }\n\n const lastPrompt = readLastPrompt();\n\n const config = await loadConfig(jwt);\n const rt = await route(config);\n const tagStr = tag(rt, config);\n\n if (config.silent) {\n const msg = (installScanMsg ? installScanMsg + '\\\\n' : '') + tagStr + ' bashGuard \\\\u2192 skipped (silent mode)';\n outputJson({ systemMessage: msg, hookSpecificOutput: { hookEventName: 'PreToolUse', additionalContext: msg } });\n return;\n }\n\n if (rt === 'local') {\n const graderPrompt = [\n 'Working directory: ' + (cwd || '.'),\n 'Repo: ' + (gitRepo || 'unknown'),\n 'Command: ' + command,\n 'User intent (last human message): ' + (transcript.userIntent || 'none stated'),\n 'Last user prompt: ' + (lastPrompt || 'none'),\n 'Org rules: ' + JSON.stringify(config.rules),\n ].join('\\\\n');\n\n let gradeResp: string;\n try {\n gradeResp = await localGrade('bash', graderPrompt);\n } catch {\n outputJson({ systemMessage: tagStr + ' bashGuard \\\\u2192 local grader unavailable, skipped' });\n return;\n }\n\n const verdict = parseVerdict(gradeResp);\n const violatedRules = verdict.ruleId ? [verdict.ruleId] : [];\n\n if (!verdict.ok) {\n const mode = verdict.ruleMode || ruleMode(verdict.ruleId, config.rules);\n const guardReason = (verdict.ruleId ? '(' + verdict.ruleId + ') ' : '') + (verdict.reason || 'policy violation');\n\n if (mode === 'audit') {\n const reason = tagStr + ' bashGuard \\\\u2192 warning' + (verdict.ruleId ? ' (' + verdict.ruleId + ')' : '') + ': ' + (verdict.reason || 'policy violation');\n const combined = (installScanMsg ? installScanMsg + '\\\\n' : '') + reason;\n outputJson({ systemMessage: combined, hookSpecificOutput: { hookEventName: 'PreToolUse', additionalContext: combined } });\n dispatchCapture(jwt, 'bash', 'warning', verdict.severity || 'medium', verdict.category || 'security',\n toolName, gitRepo, sessionId, config.captureDepth, {\n command, reasoning: guardReason, rulesChecked: config.rules, violatedRules,\n recentUserMessages: transcript.recentUserMessages, ccModel: transcript.ccModel,\n });\n } else {\n const reason = tagStr + ' bashGuard \\\\u2192 blocked' + (verdict.ruleId ? ' (' + verdict.ruleId + ')' : '') + ': ' + (verdict.reason || 'policy violation') + '. Ask the user for explicit consent before retrying.';\n const combined = (installScanMsg ? installScanMsg + '\\\\n' : '') + reason;\n outputJson({\n systemMessage: combined,\n hookSpecificOutput: { hookEventName: 'PreToolUse', permissionDecision: 'deny', permissionDecisionReason: reason, additionalContext: combined },\n });\n dispatchCapture(jwt, 'bash', 'block', verdict.severity || 'critical', verdict.category || 'security',\n toolName, gitRepo, sessionId, config.captureDepth, {\n command, reasoning: guardReason, rulesChecked: config.rules, violatedRules,\n recentUserMessages: transcript.recentUserMessages, ccModel: transcript.ccModel,\n });\n }\n } else {\n const reason = tagStr + ' bashGuard \\\\u2192 pass: ' + (verdict.reason || 'no policy violations detected');\n const combined = (installScanMsg ? installScanMsg + '\\\\n' : '') + reason;\n outputJson({ systemMessage: combined, hookSpecificOutput: { hookEventName: 'PreToolUse', additionalContext: combined } });\n dispatchCapture(jwt, 'bash', 'pass', 'audit', verdict.category || 'trivial_utility',\n toolName, gitRepo, sessionId, config.captureDepth, {\n command, reasoning: verdict.reason || 'no policy violations detected',\n rulesChecked: config.rules, violatedRules: [],\n recentUserMessages: transcript.recentUserMessages, ccModel: transcript.ccModel,\n });\n }\n return;\n }\n\n // ─── Cloud grading ───\n const isHeadless = ['acceptEdits', 'bypassPermissions', 'plan', 'auto'].includes(permissionMode)\n || process.env.SYNKRO_HEADLESS === '1';\n\n const body: Record<string, any> = {\n hook_event: 'PreToolUse',\n tool_name: toolName,\n tool_input: toolInput,\n user_intent: transcript.userIntent || null,\n last_user_message: lastPrompt || null,\n recent_user_messages: transcript.recentUserMessages,\n recent_messages: transcript.recentMessages,\n recent_actions: transcript.recentActions,\n session_id: sessionId || null,\n tool_use_id: toolUseId || null,\n cwd: cwd || null,\n repo: gitRepo || null,\n permission_mode: permissionMode || null,\n headless: isHeadless,\n cc_model: transcript.ccModel || null,\n cc_usage: transcript.ccUsage || {},\n session_summary: transcript.sessionSummary || null,\n };\n\n const resp = await postWithRetry(GATEWAY_URL + '/api/v1/hook/judge', body, jwt, 8000);\n\n if (!resp) {\n log('bashGuard ' + cmdShort + ' \\\\u2192 error (timeout)');\n if (installScanMsg) {\n outputJson({ systemMessage: installScanMsg, hookSpecificOutput: { hookEventName: 'PreToolUse', additionalContext: installScanMsg } });\n } else { outputEmpty(); }\n return;\n }\n\n if (!resp.hook_response || typeof resp.hook_response !== 'object') {\n log('bashGuard ' + cmdShort + ' \\\\u2192 pass (no hook_response)');\n if (installScanMsg) {\n outputJson({ systemMessage: installScanMsg, hookSpecificOutput: { hookEventName: 'PreToolUse', additionalContext: installScanMsg } });\n } else { outputEmpty(); }\n return;\n }\n\n if (installScanMsg) {\n const existing = resp.hook_response.systemMessage || '';\n resp.hook_response.systemMessage = installScanMsg + (existing ? '\\\\n' + existing : '');\n if (resp.hook_response.hookSpecificOutput) {\n const existingCtx = resp.hook_response.hookSpecificOutput.additionalContext || '';\n resp.hook_response.hookSpecificOutput.additionalContext = installScanMsg + (existingCtx ? '\\\\n' + existingCtx : '');\n } else {\n resp.hook_response.hookSpecificOutput = { hookEventName: 'PreToolUse', additionalContext: resp.hook_response.systemMessage };\n }\n }\n outputJson(resp.hook_response);\n } catch (err) {\n process.stderr.write('[synkro] bashGuard error: ' + String(err) + '\\\\n');\n outputEmpty();\n }\n}\n\nmain();\n`;\n\n\n// ─── CC PreToolUse Agent subagent judge (TypeScript) ───\n\nexport const AGENT_JUDGE_TS = `#!/usr/bin/env bun\nimport {\n loadJwt, ensureFreshJwt, detectRepo, loadConfig, route, tag, localGrade,\n parseVerdict, dispatchCapture, ruleMode, postWithRetry, readStdin,\n extractTranscript, readLastPrompt, log,\n outputJson, outputEmpty, setupCursorHookSignals, isAgentTool, hookSessionId, GATEWAY_URL,\n type HookConfig, type Rule,\n} from './_synkro-common.ts';\n\nasync function main() {\n setupCursorHookSignals();\n try {\n const input = await readStdin();\n if (!input.trim()) { outputEmpty(); return; }\n\n const payload = JSON.parse(input);\n const toolName = payload.tool_name || '';\n if (!isAgentTool(toolName)) {\n outputEmpty();\n return;\n }\n\n const toolInput = payload.tool_input || {};\n const sessionId = hookSessionId(payload);\n const toolUseId = payload.tool_use_id || '';\n const cwd = payload.cwd || '';\n const permissionMode = payload.permission_mode || '';\n const transcriptPath = payload.transcript_path || '';\n const gitRepo = detectRepo(cwd || '.');\n\n const prompt = toolInput.prompt || '';\n const description = toolInput.description || '';\n const subagentType = toolInput.subagent_type || 'general-purpose';\n if (!prompt) { outputEmpty(); return; }\n\n const promptShort = prompt.slice(0, 80);\n log('agentGuard checking: ' + description + ' (' + subagentType + ')');\n\n let jwt = loadJwt();\n if (!jwt) { outputEmpty(); return; }\n jwt = await ensureFreshJwt(jwt);\n\n const transcript = extractTranscript(transcriptPath);\n const lastPrompt = readLastPrompt();\n\n const config = await loadConfig(jwt);\n const rt = await route(config);\n const tagStr = tag(rt, config);\n\n if (config.silent) {\n const msg = tagStr + ' agentGuard \\\\u2192 skipped (silent mode)';\n outputJson({ systemMessage: msg, hookSpecificOutput: { hookEventName: 'PreToolUse', additionalContext: msg } });\n return;\n }\n\n if (rt === 'local') {\n const graderPrompt = [\n 'Working directory: ' + (cwd || '.'),\n 'Repo: ' + (gitRepo || 'unknown'),\n 'Tool: Agent (subagent spawn)',\n 'Subagent type: ' + subagentType,\n 'Description: ' + description,\n 'Subagent prompt (first 4000 chars):',\n prompt.slice(0, 4000),\n 'User intent (last human message): ' + (transcript.userIntent || 'none stated'),\n 'Last user prompt: ' + (lastPrompt || 'none'),\n 'Org rules: ' + JSON.stringify(config.rules),\n ].join('\\\\n');\n\n let gradeResp: string;\n try {\n gradeResp = await localGrade('bash', graderPrompt);\n } catch {\n outputJson({ systemMessage: tagStr + ' agentGuard \\\\u2192 local grader unavailable, skipped' });\n return;\n }\n\n const verdict = parseVerdict(gradeResp);\n const agentContent = 'agent=' + subagentType + ' desc=' + description + ' prompt=' + prompt.slice(0, 2000);\n const violatedRules = verdict.ruleId ? [verdict.ruleId] : [];\n\n if (!verdict.ok) {\n const mode = verdict.ruleMode || ruleMode(verdict.ruleId, config.rules);\n const guardReason = (verdict.ruleId ? '(' + verdict.ruleId + ') ' : '') + (verdict.reason || 'policy violation');\n\n if (mode === 'audit') {\n const reason = tagStr + ' agentGuard \\\\u2192 warning' + (verdict.ruleId ? ' (' + verdict.ruleId + ')' : '') + ': ' + (verdict.reason || 'policy violation');\n outputJson({ systemMessage: reason, hookSpecificOutput: { hookEventName: 'PreToolUse', additionalContext: reason } });\n dispatchCapture(jwt, 'agent', 'warning', verdict.severity || 'medium', verdict.category || 'security',\n toolName, gitRepo, sessionId, config.captureDepth, {\n command: agentContent, reasoning: guardReason, rulesChecked: config.rules, violatedRules,\n recentUserMessages: transcript.recentUserMessages, ccModel: transcript.ccModel,\n });\n } else {\n const reason = tagStr + ' agentGuard \\\\u2192 blocked' + (verdict.ruleId ? ' (' + verdict.ruleId + ')' : '') + ': ' + (verdict.reason || 'policy violation') + '. Ask the user for explicit consent before retrying.';\n outputJson({\n systemMessage: reason,\n hookSpecificOutput: { hookEventName: 'PreToolUse', permissionDecision: 'deny', permissionDecisionReason: reason, additionalContext: reason },\n });\n dispatchCapture(jwt, 'agent', 'block', verdict.severity || 'critical', verdict.category || 'security',\n toolName, gitRepo, sessionId, config.captureDepth, {\n command: agentContent, reasoning: guardReason, rulesChecked: config.rules, violatedRules,\n recentUserMessages: transcript.recentUserMessages, ccModel: transcript.ccModel,\n });\n }\n } else {\n const reason = tagStr + ' agentGuard \\\\u2192 pass: ' + (verdict.reason || 'no policy violations detected');\n outputJson({ systemMessage: reason, hookSpecificOutput: { hookEventName: 'PreToolUse', additionalContext: reason } });\n dispatchCapture(jwt, 'agent', 'pass', 'audit', verdict.category || 'subagent_spawn',\n toolName, gitRepo, sessionId, config.captureDepth, {\n command: agentContent, reasoning: verdict.reason || 'no policy violations detected',\n rulesChecked: config.rules, violatedRules: [],\n recentUserMessages: transcript.recentUserMessages, ccModel: transcript.ccModel,\n });\n }\n return;\n }\n\n // ─── Cloud grading ───\n const isHeadless = ['acceptEdits', 'bypassPermissions', 'plan', 'auto'].includes(permissionMode)\n || process.env.SYNKRO_HEADLESS === '1';\n\n const body: Record<string, any> = {\n hook_event: 'PreToolUse',\n tool_name: toolName,\n tool_input: toolInput,\n user_intent: transcript.userIntent || null,\n last_user_message: lastPrompt || null,\n recent_user_messages: transcript.recentUserMessages,\n recent_messages: transcript.recentMessages,\n recent_actions: transcript.recentActions,\n session_id: sessionId || null,\n tool_use_id: toolUseId || null,\n cwd: cwd || null,\n repo: gitRepo || null,\n permission_mode: permissionMode || null,\n headless: isHeadless,\n cc_model: transcript.ccModel || null,\n cc_usage: transcript.ccUsage || {},\n session_summary: transcript.sessionSummary || null,\n };\n\n const resp = await postWithRetry(GATEWAY_URL + '/api/v1/hook/judge', body, jwt, 8000);\n\n if (!resp) {\n log('agentGuard ' + promptShort + ' \\\\u2192 error (timeout)');\n outputEmpty();\n return;\n }\n\n if (!resp.hook_response || typeof resp.hook_response !== 'object') {\n log('agentGuard ' + promptShort + ' \\\\u2192 pass (no hook_response)');\n outputEmpty();\n return;\n }\n\n outputJson(resp.hook_response);\n } catch (err) {\n process.stderr.write('[synkro] agentGuard error: ' + String(err) + '\\\\n');\n outputEmpty();\n }\n}\n\nmain();\n`;\n\n\n// ─── CC PreToolUse ExitPlanMode plan review (TypeScript) ───\n\nexport const PLAN_JUDGE_TS = `#!/usr/bin/env bun\nimport {\n loadJwt, ensureFreshJwt, detectRepo, loadConfig, route, tag, localGrade,\n parseVerdict, dispatchCapture, postWithRetry, readStdin, log,\n outputJson, outputEmpty, setupCursorHookSignals, isPlanTool, hookSessionId, GATEWAY_URL,\n} from './_synkro-common.ts';\nimport { existsSync, readFileSync, writeFileSync, readdirSync, statSync } from 'node:fs';\nimport { join } from 'node:path';\nimport { homedir } from 'node:os';\n\nfunction findLatestPlanInDir(plansDir: string): string | null {\n if (!existsSync(plansDir)) return null;\n try {\n const files = readdirSync(plansDir)\n .filter(f => f.endsWith('.md'))\n .map(f => ({ name: f, mtime: statSync(join(plansDir, f)).mtimeMs }))\n .sort((a, b) => b.mtime - a.mtime);\n return files.length > 0 ? join(plansDir, files[0].name) : null;\n } catch {\n return null;\n }\n}\n\nfunction findLatestPlan(): string | null {\n const dirs = [\n join(homedir(), '.claude', 'plans'),\n join(homedir(), '.cursor', 'plans'),\n ];\n let best: { path: string; mtime: number } | null = null;\n for (const dir of dirs) {\n const p = findLatestPlanInDir(dir);\n if (!p) continue;\n try {\n const mtime = statSync(p).mtimeMs;\n if (!best || mtime > best.mtime) best = { path: p, mtime };\n } catch {}\n }\n return best?.path ?? null;\n}\n\nfunction appendReviewToPlan(planFile: string, verdict: string): void {\n try {\n let content = readFileSync(planFile, 'utf-8');\n content = content.replace(/<!-- synkro-plan-review -->[\\\\s\\\\S]*?<!-- \\\\/synkro-plan-review -->/g, '').trimEnd();\n const now = new Date().toISOString().replace('T', ' ').slice(0, 16);\n content += '\\\\n\\\\n<!-- synkro-plan-review -->\\\\n\\\\n---\\\\n\\\\n**Synkro Plan Review** \\\\u2014 ' + now + '\\\\n\\\\n' + verdict + '\\\\n\\\\n<!-- /synkro-plan-review -->\\\\n';\n writeFileSync(planFile, content, 'utf-8');\n } catch {}\n}\n\nasync function main() {\n setupCursorHookSignals();\n try {\n const input = await readStdin();\n if (!input.trim()) { outputEmpty(); return; }\n\n const payload = JSON.parse(input);\n const toolName = payload.tool_name || '';\n if (!isPlanTool(toolName)) { outputEmpty(); return; }\n\n const planFile = findLatestPlan();\n if (!planFile) { outputEmpty(); return; }\n const plan = readFileSync(planFile, 'utf-8');\n if (plan.length < 20) { outputEmpty(); return; }\n\n const sessionId = hookSessionId(payload);\n const cwd = payload.cwd || '';\n const gitRepo = detectRepo(cwd || '.');\n\n const planShort = plan.slice(0, 80);\n log('planReview checking: ' + planShort + '...');\n\n let jwt = loadJwt();\n if (!jwt) { outputEmpty(); return; }\n jwt = await ensureFreshJwt(jwt);\n\n const config = await loadConfig(jwt);\n const rt = await route(config);\n const tagStr = tag(rt, config);\n\n if (config.silent) {\n outputJson({ systemMessage: tagStr + ' planReview \\\\u2192 skipped (silent mode)' });\n return;\n }\n\n if (rt === 'local') {\n const graderPrompt = [\n 'Working directory: ' + (cwd || '.'),\n 'Repo: ' + (gitRepo || 'unknown'),\n 'Plan:',\n plan.slice(0, 8000),\n 'Org rules: ' + JSON.stringify(config.rules),\n ].join('\\\\n');\n\n let gradeResp: string;\n try {\n gradeResp = await localGrade('plan', graderPrompt);\n } catch {\n outputJson({ systemMessage: tagStr + ' planReview \\\\u2192 local grader unavailable, skipped' });\n return;\n }\n\n const verdict = parseVerdict(gradeResp);\n const planContent = plan.slice(0, 2000);\n const violatedRules = verdict.ruleId ? [verdict.ruleId] : [];\n\n if (!verdict.ok) {\n const reviewMsg = (verdict.ruleId ? '(first: ' + verdict.ruleId + ') ' : '') + (verdict.reason || 'check org rules during implementation');\n appendReviewToPlan(planFile, '\\\\u26a0\\\\ufe0f Advisory \\\\u2014 ' + reviewMsg);\n const advLine = tagStr + ' planReview \\\\u2192 ' + reviewMsg;\n outputJson({ systemMessage: advLine, hookSpecificOutput: { hookEventName: 'PreToolUse', additionalContext: 'Synkro local plan judge (advisory). ' + reviewMsg } });\n dispatchCapture(jwt, 'plan_review', 'advisory', verdict.severity || 'medium', verdict.category || 'general',\n 'ExitPlanMode', gitRepo, sessionId, config.captureDepth, {\n command: planContent, reasoning: verdict.reason || 'check org rules',\n rulesChecked: config.rules, violatedRules,\n });\n } else {\n const reviewMsg = verdict.reason || 'no relevant org rules for this plan';\n appendReviewToPlan(planFile, '\\\\u2705 Clean \\\\u2014 ' + reviewMsg);\n const cleanLine = tagStr + ' planReview \\\\u2192 clean: ' + reviewMsg;\n outputJson({ systemMessage: cleanLine, hookSpecificOutput: { hookEventName: 'PreToolUse', additionalContext: 'Synkro local plan judge. ' + reviewMsg } });\n dispatchCapture(jwt, 'plan_review', 'clean', 'audit', verdict.category || 'general',\n 'ExitPlanMode', gitRepo, sessionId, config.captureDepth, {\n command: planContent, reasoning: reviewMsg,\n rulesChecked: config.rules, violatedRules: [],\n });\n }\n return;\n }\n\n // ─── Cloud grading ───\n const body = {\n hook_event: 'PreToolUse',\n tool_name: 'ExitPlanMode',\n tool_input: { plan: plan.slice(0, 16000) },\n session_id: sessionId || null,\n cwd: cwd || null,\n repo: gitRepo || null,\n };\n\n const resp = await postWithRetry(GATEWAY_URL + '/api/v1/hook/judge', body, jwt, 12000);\n\n if (!resp) {\n log('planReview \\\\u2192 error (timeout)');\n outputEmpty();\n return;\n }\n\n const hookResp = resp?.hook_response;\n if (!hookResp) { outputEmpty(); return; }\n\n const decision = hookResp?.hookSpecificOutput?.permissionDecision;\n if (decision) {\n const reason = hookResp?.hookSpecificOutput?.permissionDecisionReason || 'check org rules';\n appendReviewToPlan(planFile, '\\\\u26a0\\\\ufe0f Advisory \\\\u2014 ' + reason);\n outputJson({ systemMessage: tagStr + ' planReview \\\\u2192 advisory: ' + reason });\n } else {\n const cloudMsg = hookResp.systemMessage || '';\n if (cloudMsg) appendReviewToPlan(planFile, '\\\\u2705 ' + cloudMsg);\n outputJson(hookResp);\n }\n } catch (err) {\n process.stderr.write('[synkro] planReview error: ' + String(err) + '\\\\n');\n outputEmpty();\n }\n}\n\nmain();\n`;\n\n\n// ─── CC SessionEnd stop summary (TypeScript) ───\n\nexport const STOP_SUMMARY_TS = `#!/usr/bin/env bun\nimport {\n loadJwt, detectRepo, loadConfig, tag, readStdin, aggregateUsage,\n outputJson, outputEmpty, appendLocalTelemetry, setupCursorHookSignals, hookSessionId, GATEWAY_URL,\n} from './_synkro-common.ts';\n\nasync function main() {\n setupCursorHookSignals();\n try {\n const input = await readStdin();\n if (!input.trim()) { outputEmpty(); return; }\n\n const payload = JSON.parse(input);\n const sessionId = hookSessionId(payload);\n if (!sessionId) { outputEmpty(); return; }\n\n const cwd = payload.cwd || '';\n const transcriptPath = payload.transcript_path || '';\n const gitRepo = detectRepo(cwd || '.');\n\n let jwt = loadJwt();\n if (!jwt) { outputEmpty(); return; }\n\n if (transcriptPath) {\n const usage = aggregateUsage(transcriptPath);\n if (usage.totals.in + usage.totals.out > 0) {\n const usageBody = {\n capture_type: 'usage_tick',\n event_id: 'usage_' + Date.now() + '_' + process.pid,\n hook_type: 'stop',\n verdict: 'allow',\n severity: 'none',\n model: usage.model || 'unknown',\n cc_model: usage.model || '',\n cc_usage: {\n input_tokens: usage.totals.in,\n output_tokens: usage.totals.out,\n cache_creation_input_tokens: usage.totals.cw,\n cache_read_input_tokens: usage.totals.cr,\n },\n ...(gitRepo ? { repo: gitRepo } : {}),\n ...(sessionId ? { session_id: sessionId } : {}),\n };\n appendLocalTelemetry(usageBody);\n fetch(GATEWAY_URL + '/api/v1/hook/capture', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json', Authorization: 'Bearer ' + jwt },\n body: JSON.stringify(usageBody),\n signal: AbortSignal.timeout(3000),\n }).catch(() => {});\n }\n }\n\n let resp: any;\n try {\n const r = await fetch(GATEWAY_URL + '/api/v1/cli/session-summary?session_id=' + encodeURIComponent(sessionId), {\n headers: { Authorization: 'Bearer ' + jwt },\n signal: AbortSignal.timeout(3000),\n });\n resp = await r.json();\n } catch {\n outputEmpty();\n return;\n }\n\n const edits = resp?.edits_scanned || 0;\n const findings = resp?.findings || 0;\n const autoFixed = resp?.auto_fixed || 0;\n const open = resp?.open || 0;\n\n if (!edits) { outputEmpty(); return; }\n\n const config = await loadConfig(jwt);\n const tagStr = tag('local', config);\n\n if (!findings) {\n outputJson({ systemMessage: tagStr + ' stop \\\\u2192 0 issues across ' + edits + ' edit(s), session complete' });\n } else {\n outputJson({ systemMessage: tagStr + ' stop \\\\u2192 ' + findings + ' finding(s): ' + autoFixed + ' auto-fixed, ' + open + ' open' });\n }\n } catch (err) {\n process.stderr.write('[synkro] stopSummary error: ' + String(err) + '\\\\n');\n outputEmpty();\n }\n}\n\nmain();\n`;\n\n\n// ─── CC SessionStart (TypeScript) ───\n\nexport const SESSION_START_TS = `#!/usr/bin/env bun\nimport {\n loadJwt, detectRepo, channelUp, tag, readStdin,\n outputJson, outputEmpty, setupCursorHookSignals, hookSessionId, GATEWAY_URL,\n type HookConfig,\n} from './_synkro-common.ts';\n\nasync function main() {\n setupCursorHookSignals();\n try {\n const input = await readStdin();\n if (!input.trim()) { outputEmpty(); return; }\n\n const payload = JSON.parse(input);\n const cwd = payload.cwd || '';\n const sessionId = hookSessionId(payload);\n const gitRepo = detectRepo(cwd || '.');\n\n let jwt = loadJwt();\n\n const isChannelUp = await channelUp();\n const rt = isChannelUp ? 'local' : 'cloud';\n\n let policyName = '';\n let silent = false;\n let openFindings = 0;\n\n if (jwt) {\n try {\n const url = GATEWAY_URL + '/api/v1/hook/config?session_id=' + encodeURIComponent(sessionId || '') + '&repo=' + encodeURIComponent(gitRepo || '');\n const r = await fetch(url, {\n headers: { Authorization: 'Bearer ' + jwt },\n signal: AbortSignal.timeout(3000),\n });\n const data = await r.json() as any;\n silent = data.silent_mode === true || data.silent_mode === 'true';\n policyName = data.active_policy_name || '';\n openFindings = data.session_context?.open_findings || 0;\n } catch {}\n }\n\n const fakeConfig: HookConfig = { captureDepth: 'local_only', tier: 'standard', silent, policyName, rules: [] };\n const tagStr = tag(rt, fakeConfig);\n const routeLine = tagStr + ' inference: ' + (isChannelUp ? 'local-cc (channel reachable on 127.0.0.1:8929)' : 'cloud (local-cc channel not reachable)');\n\n if (!jwt) {\n outputJson({ systemMessage: routeLine });\n return;\n }\n\n if (!openFindings) {\n outputJson({ systemMessage: routeLine });\n } else if (openFindings === 1) {\n outputJson({ systemMessage: routeLine + '\\\\n' + tagStr + ' session start \\\\u2192 1 open finding in this repo from a prior session.' });\n } else {\n outputJson({ systemMessage: routeLine + '\\\\n' + tagStr + ' session start \\\\u2192 ' + openFindings + ' open findings in this repo from prior sessions.' });\n }\n } catch (err) {\n process.stderr.write('[synkro] sessionStart error: ' + String(err) + '\\\\n');\n outputEmpty();\n }\n}\n\nmain();\n`;\n\n\n// ─── CC PostToolUse Bash followup (TypeScript) ───\n\nexport const BASH_FOLLOWUP_TS = `#!/usr/bin/env bun\nimport {\n loadJwt, loadConfig, readStdin, hashCommand, consentGrant, consentHasActive, consentConsume,\n outputEmpty, appendLocalTelemetry, setupCursorHookSignals, isShellTool, hookSessionId, GATEWAY_URL,\n} from './_synkro-common.ts';\n\nasync function main() {\n setupCursorHookSignals();\n try {\n const input = await readStdin();\n if (!input.trim()) { outputEmpty(); return; }\n\n const payload = JSON.parse(input);\n const toolName = payload.tool_name || '';\n const shellCmd = typeof payload.command === 'string' ? payload.command : (payload.tool_input?.command || '');\n if (!isShellTool(toolName) && !shellCmd) { outputEmpty(); return; }\n\n const jwt = loadJwt();\n if (!jwt) { outputEmpty(); return; }\n\n const sessionId = hookSessionId(payload);\n const toolUseId = payload.tool_use_id || payload.tool_call_id || 'cursor-shell';\n if (!sessionId) { outputEmpty(); return; }\n\n let isError = payload.tool_result?.is_error === true;\n try {\n const out = JSON.parse(payload.tool_output || '{}');\n if (out.exitCode !== 0 || out.is_error === true) isError = true;\n } catch {}\n const cmd = shellCmd;\n const cmdHash = cmd ? hashCommand(cmd) : '';\n\n if (cmdHash && sessionId) {\n if (!isError) {\n consentConsume(sessionId, cmdHash);\n } else {\n if (!consentHasActive(sessionId, cmdHash)) {\n consentGrant(sessionId, cmdHash);\n }\n }\n }\n\n const body = {\n capture_type: 'bash_followup',\n session_id: sessionId,\n tool_use_id: toolUseId,\n is_error: isError,\n command_hash: cmdHash,\n };\n\n appendLocalTelemetry(body);\n\n const config = await loadConfig(jwt);\n if (config.captureDepth !== 'local_only') {\n fetch(GATEWAY_URL + '/api/v1/hook/capture', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json', Authorization: 'Bearer ' + jwt },\n body: JSON.stringify(body),\n signal: AbortSignal.timeout(3000),\n }).catch(() => {});\n }\n\n outputEmpty();\n } catch {\n outputEmpty();\n }\n}\n\nmain();\n`;\n\n\n// ─── CC Stop transcript sync (TypeScript) ───\n\nexport const TRANSCRIPT_SYNC_TS = `#!/usr/bin/env bun\nimport {\n loadJwt, detectRepo, readStdin, aggregateUsage, appendLocalTelemetry,\n outputEmpty, setupCursorHookSignals, hookSessionId, GATEWAY_URL,\n} from './_synkro-common.ts';\nimport { existsSync, readFileSync, writeFileSync, mkdirSync } from 'node:fs';\nimport { join, dirname } from 'node:path';\nimport { homedir } from 'node:os';\n\nasync function main() {\n setupCursorHookSignals();\n try {\n const input = await readStdin();\n if (!input.trim()) { outputEmpty(); return; }\n\n const payload = JSON.parse(input);\n const sessionId = hookSessionId(payload);\n const transcriptPath = payload.transcript_path || '';\n const cwd = payload.cwd || '';\n\n if (!sessionId || !transcriptPath || !existsSync(transcriptPath)) {\n outputEmpty();\n return;\n }\n\n const jwt = loadJwt();\n if (!jwt) { outputEmpty(); return; }\n\n const usage = aggregateUsage(transcriptPath);\n if (usage.totals.in + usage.totals.out > 0) {\n const usageBody = {\n capture_type: 'usage_tick',\n event_id: 'usage_' + Date.now() + '_' + process.pid,\n hook_type: 'stop',\n verdict: 'allow',\n severity: 'none',\n model: usage.model || 'unknown',\n cc_model: usage.model || '',\n cc_usage: {\n input_tokens: usage.totals.in,\n output_tokens: usage.totals.out,\n cache_creation_input_tokens: usage.totals.cw,\n cache_read_input_tokens: usage.totals.cr,\n },\n session_id: sessionId,\n };\n appendLocalTelemetry(usageBody);\n fetch(GATEWAY_URL + '/api/v1/hook/capture', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json', Authorization: 'Bearer ' + jwt },\n body: JSON.stringify(usageBody),\n signal: AbortSignal.timeout(3000),\n }).catch(() => {});\n }\n\n if (process.env.SYNKRO_TRANSCRIPT_CONSENT === 'no') { outputEmpty(); return; }\n\n const gitRepo = detectRepo(cwd || '.');\n if (!gitRepo) { outputEmpty(); return; }\n\n let captureDepth = 'local_only';\n try {\n const r = await fetch(GATEWAY_URL + '/api/v1/hook/config', {\n headers: { Authorization: 'Bearer ' + jwt },\n signal: AbortSignal.timeout(3000),\n });\n const data = await r.json() as any;\n captureDepth = data.capture_depth || 'local_only';\n } catch {}\n\n if (captureDepth === 'local_only') { outputEmpty(); return; }\n\n const offsetDir = join(homedir(), '.synkro', '.transcript-offsets');\n mkdirSync(offsetDir, { recursive: true });\n const offsetFile = join(offsetDir, sessionId);\n let offset = 0;\n if (existsSync(offsetFile)) {\n try { offset = parseInt(readFileSync(offsetFile, 'utf-8').trim(), 10) || 0; } catch {}\n }\n\n const raw = readFileSync(transcriptPath, 'utf-8');\n const allLines = raw.split('\\\\n').filter(l => l.trim());\n const totalLines = allLines.length;\n\n if (totalLines <= offset) { outputEmpty(); return; }\n\n let startIdx = offset;\n const delta = totalLines - offset;\n if (delta > 200) startIdx = totalLines - 200;\n\n const messages: any[] = [];\n for (let i = startIdx; i < totalLines; i++) {\n try {\n const entry = JSON.parse(allLines[i]);\n if (entry.type !== 'user' && entry.type !== 'assistant') continue;\n const content = entry.message?.content;\n let text = '';\n if (typeof content === 'string') text = content.slice(0, 8000);\n else if (Array.isArray(content)) {\n text = content.map((c: any) => {\n if (typeof c === 'string') return c;\n if (c?.type === 'text') return c.text || '';\n return '';\n }).join(' ').slice(0, 8000);\n }\n\n const msg: any = { message_index: i, type: entry.type, content: text };\n if (entry.type === 'assistant') {\n const toolCalls = (Array.isArray(content) ? content : [])\n .filter((c: any) => c?.type === 'tool_use')\n .map((c: any) => ({ name: c.name, input: JSON.stringify(c.input || {}).slice(0, 500), id: c.id }));\n if (toolCalls.length > 0) msg.tool_calls = toolCalls;\n msg.model = entry.message?.model || null;\n const u = entry.message?.usage;\n if (u) msg.usage = { input_tokens: u.input_tokens, output_tokens: u.output_tokens, cache_creation_input_tokens: u.cache_creation_input_tokens, cache_read_input_tokens: u.cache_read_input_tokens };\n }\n messages.push(msg);\n } catch {}\n }\n\n writeFileSync(offsetFile, String(totalLines), 'utf-8');\n\n if (messages.length === 0) { outputEmpty(); return; }\n\n const syncBody = {\n repo: gitRepo,\n sessions: [{ cc_session_id: sessionId, messages }],\n };\n fetch(GATEWAY_URL + '/api/v1/cli/sync-transcripts', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json', Authorization: 'Bearer ' + jwt },\n body: JSON.stringify(syncBody),\n signal: AbortSignal.timeout(10000),\n }).catch(() => {});\n\n outputEmpty();\n } catch {\n outputEmpty();\n }\n}\n\nmain();\n`;\n\n\n// ─── CC UserPromptSubmit (TypeScript) ───\n\nexport const USER_PROMPT_SUBMIT_TS = `#!/usr/bin/env bun\nimport { readStdin, appendLocalTelemetry, aggregateUsage, outputEmpty, setupCursorHookSignals, hookSessionId } from './_synkro-common.ts';\nimport { writeFileSync, mkdirSync } from 'node:fs';\nimport { join, dirname } from 'node:path';\nimport { homedir } from 'node:os';\n\nasync function main() {\n setupCursorHookSignals();\n try {\n const input = await readStdin();\n if (!input.trim()) { outputEmpty(); return; }\n const payload = JSON.parse(input);\n const msg = payload.message || payload.prompt || payload.content || '';\n if (msg) {\n const promptFile = join(homedir(), '.synkro', '.last-prompt');\n mkdirSync(dirname(promptFile), { recursive: true });\n writeFileSync(promptFile, msg, 'utf-8');\n }\n\n const sessionId = hookSessionId(payload);\n const transcriptPath = payload.transcript_path || '';\n if (sessionId && transcriptPath) {\n const usage = aggregateUsage(transcriptPath);\n if (usage.totals.in + usage.totals.out > 0) {\n appendLocalTelemetry({\n capture_type: 'usage_tick',\n event_id: 'usage_' + Date.now() + '_' + process.pid,\n hook_type: 'prompt_submit',\n session_id: sessionId,\n model: usage.model || 'unknown',\n cc_model: usage.model || '',\n cc_usage: {\n input_tokens: usage.totals.in,\n output_tokens: usage.totals.out,\n cache_creation_input_tokens: usage.totals.cw,\n cache_read_input_tokens: usage.totals.cr,\n },\n });\n }\n }\n outputEmpty();\n } catch {\n outputEmpty();\n }\n}\n\nmain();\n`;\n\n\n// ─── Cursor IDE TypeScript adapter scripts ───\n\nexport const CURSOR_BASH_JUDGE_TS = `#!/usr/bin/env bun\nimport {\n loadJwt, ensureFreshJwt, detectRepo, loadConfig, route, tag, localGrade,\n parseVerdict, dispatchCapture, ruleMode, postWithRetry, readStdin,\n extractTranscript, readLastPrompt, log, GATEWAY_URL,\n type Rule,\n} from './_synkro-common.ts';\nimport { createHash } from 'node:crypto';\nimport { existsSync, statSync, writeFileSync, mkdirSync } from 'node:fs';\n\nconst DEDUP_DIR = process.env.HOME + '/.synkro/.dedup';\nconst DEDUP_TTL_MS = 3000;\n\nfunction isDuplicate(command: string, sessionId: string): boolean {\n const hash = createHash('md5').update(sessionId + ':' + command).digest('hex').slice(0, 12);\n const marker = DEDUP_DIR + '/' + hash;\n try {\n if (existsSync(marker)) {\n const age = Date.now() - statSync(marker).mtimeMs;\n if (age < DEDUP_TTL_MS) return true;\n }\n } catch {}\n try {\n mkdirSync(DEDUP_DIR, { recursive: true });\n writeFileSync(marker, '', { flag: 'w' });\n } catch {}\n return false;\n}\n\n// Cursor beforeShellExecution timeout is 15s; stay under it (JWT refresh + grade).\nconst CURSOR_GRADE_TIMEOUT_MS = 7500;\nconst CURSOR_CLOUD_TIMEOUT_MS = 6000;\n\nlet hookDone = false;\n\nfunction finishAllow(): never {\n if (!hookDone) {\n hookDone = true;\n try { process.stdout.write('{}\\\\n'); } catch {}\n }\n process.exit(0);\n}\n\nfunction finishWith(payload: Record<string, unknown>): never {\n hookDone = true;\n process.stdout.write(JSON.stringify(payload) + '\\\\n');\n process.exit(0);\n}\n\nprocess.on('SIGTERM', () => finishAllow());\n\nconst SHELL_TOOL_NAMES = new Set(['Bash', 'Shell', 'terminal', 'run_terminal_cmd', 'execute_command']);\nconst READ_TOOL_NAMES = new Set(['Read', 'ReadFile', 'read_file']);\nconst SEARCH_TOOL_NAMES = new Set(['Grep', 'grep_search', 'codebase_search', 'file_search']);\nconst DIR_TOOL_NAMES = new Set(['Glob', 'list_dir']);\nconst DELETE_TOOL_NAMES = new Set(['delete_file']);\nconst BASH_PRE_TOOL_NAMES = new Set([...SHELL_TOOL_NAMES, ...READ_TOOL_NAMES, ...SEARCH_TOOL_NAMES, ...DIR_TOOL_NAMES, ...DELETE_TOOL_NAMES]);\n\nfunction extractCommand(payload: Record<string, unknown>): { command: string; toolName: string } {\n const direct = typeof payload.command === 'string' ? payload.command : '';\n if (direct) return { command: direct, toolName: 'Bash' };\n\n const toolName = typeof payload.tool_name === 'string' ? payload.tool_name : '';\n if (!BASH_PRE_TOOL_NAMES.has(toolName)) return { command: '', toolName };\n\n const toolInput = (payload.tool_input && typeof payload.tool_input === 'object')\n ? payload.tool_input as Record<string, unknown>\n : {};\n\n let command = '';\n if (SHELL_TOOL_NAMES.has(toolName)) {\n command = String(toolInput.command ?? '');\n } else if (READ_TOOL_NAMES.has(toolName)) {\n command = 'cat ' + String(toolInput.file_path ?? toolInput.path ?? '');\n } else if (SEARCH_TOOL_NAMES.has(toolName)) {\n command = \"grep -r '\" + String(toolInput.pattern ?? toolInput.query ?? '') + \"' \" + String(toolInput.path ?? '.');\n } else if (DIR_TOOL_NAMES.has(toolName)) {\n command = \"find . -name '\" + String(toolInput.pattern ?? toolInput.relative_workspace_path ?? '') + \"'\";\n } else if (DELETE_TOOL_NAMES.has(toolName)) {\n command = 'rm ' + String(toolInput.target_file ?? toolInput.file_path ?? toolInput.path ?? '');\n }\n return { command, toolName: toolName || 'Bash' };\n}\n\nasync function main() {\n try {\n const input = await readStdin();\n if (!input.trim()) finishAllow();\n\n const payload = JSON.parse(input) as Record<string, unknown>;\n const { command, toolName } = extractCommand(payload);\n if (!command) finishAllow();\n\n const cwd = typeof payload.cwd === 'string' ? payload.cwd : '';\n const sessionId = String(payload.conversation_id ?? payload.session_id ?? '');\n\n if (isDuplicate(command, sessionId)) {\n log('bashGuard skip (dedup): ' + command.slice(0, 80));\n finishAllow();\n }\n\n const transcriptPath = typeof payload.transcript_path === 'string' ? payload.transcript_path : '';\n const rawModel = String(payload.model ?? payload.model_id ?? '');\n const KNOWN_MODELS = new Set(['gpt-4', 'gpt-4o', 'gpt-4.1', 'gpt-5', 'o1', 'o3', 'o4-mini', 'claude-sonnet-4-5', 'claude-opus-4-5', 'sonnet-4', 'sonnet-4-thinking', 'gemini-2.5-pro', 'gemini-2.5-flash']);\n const model = rawModel ? (KNOWN_MODELS.has(rawModel) || rawModel.startsWith('claude-') || rawModel.startsWith('gpt-') || rawModel.startsWith('gemini-') || rawModel.startsWith('o1') || rawModel.startsWith('o3') ? rawModel : 'cursor/' + rawModel) : 'cursor';\n const repo = detectRepo(cwd || '.');\n\n const cmdShort = command.slice(0, 80);\n log('bashGuard checking: ' + cmdShort);\n\n let jwt = loadJwt();\n if (!jwt) finishAllow();\n jwt = await ensureFreshJwt(jwt);\n\n const transcript = extractTranscript(transcriptPath);\n const lastPrompt = readLastPrompt();\n\n const config = await loadConfig(jwt);\n if (config.silent) finishAllow();\n\n const rt = await route(config);\n const tagStr = tag(rt, config);\n\n if (rt === 'local') {\n const rulesBlock = config.rules.map((r: Rule, i: number) =>\n (i + 1) + '. [' + r.rule_id + '] (' + r.severity + '/' + r.mode + ') ' + r.text\n ).join('\\\\n');\n\n const graderPrompt = [\n 'RULES:',\n rulesBlock || '(none)',\n '',\n 'COMMAND TO EVALUATE:',\n command,\n '',\n 'User intent (last human message): ' + (transcript.userIntent || lastPrompt || 'none stated'),\n 'Last user prompt: ' + (lastPrompt || 'none'),\n ].join('\\\\n');\n\n let gradeResp: string;\n try {\n gradeResp = await localGrade('bash', graderPrompt, CURSOR_GRADE_TIMEOUT_MS);\n } catch (e) {\n log('bashGuard ' + cmdShort + ' → pass (grade unavailable): ' + String(e));\n finishWith({ permission: 'allow' });\n }\n\n const verdict = parseVerdict(gradeResp);\n\n if (!verdict.ok) {\n const mode = verdict.ruleMode || ruleMode(verdict.ruleId, config.rules);\n const guardReason = (verdict.ruleId ? '(' + verdict.ruleId + ') ' : '') + (verdict.reason || 'policy violation');\n\n if (mode !== 'audit') {\n dispatchCapture(jwt, 'bash', 'block', verdict.severity || 'critical', verdict.category || 'security',\n 'Bash', gitRepo, sessionId, config.captureDepth, {\n command, reasoning: guardReason,\n rulesChecked: config.rules, violatedRules: verdict.ruleId ? [verdict.ruleId] : [],\n ccModel: model,\n });\n finishWith({\n permission: 'deny',\n user_message: tagStr + ' bashGuard → block: ' + guardReason,\n agent_message: 'Synkro safety judge. Reasoning: ' + (verdict.reason || guardReason),\n });\n }\n\n dispatchCapture(jwt, 'bash', 'warning', verdict.severity || 'medium', verdict.category || 'security',\n 'Bash', gitRepo, sessionId, config.captureDepth, {\n command, reasoning: guardReason,\n rulesChecked: config.rules, violatedRules: verdict.ruleId ? [verdict.ruleId] : [],\n ccModel: model,\n });\n log('bashGuard ' + cmdShort + ' → audit warning');\n finishWith({ permission: 'allow' });\n } else {\n dispatchCapture(jwt, 'bash', 'pass', 'audit', verdict.category || 'clean',\n 'Bash', gitRepo, sessionId, config.captureDepth, {\n command, reasoning: verdict.reason || 'no policy violations detected',\n rulesChecked: config.rules, violatedRules: [],\n ccModel: model,\n });\n }\n\n const passReason = verdict.reason || 'no policy violations detected';\n log('bashGuard ' + cmdShort + ' → pass: ' + passReason);\n finishWith({ permission: 'allow' });\n }\n\n const body: Record<string, any> = {\n hook_event: 'PreToolUse',\n tool_name: toolName || 'Bash',\n tool_input: { command },\n response_format: 'cursor',\n user_intent: transcript.userIntent || null,\n last_user_message: lastPrompt || null,\n recent_user_messages: transcript.recentUserMessages,\n recent_messages: transcript.recentMessages,\n session_id: sessionId || null,\n cwd: cwd || null,\n repo: repo || null,\n };\n\n const resp = await postWithRetry(GATEWAY_URL + '/api/v1/hook/judge', body, jwt, CURSOR_CLOUD_TIMEOUT_MS);\n\n if (!resp) {\n log('bashGuard ' + cmdShort + ' → pass (cloud timeout)');\n finishAllow();\n }\n\n if (resp.hook_response) {\n const hr = resp.hook_response as Record<string, unknown>;\n if (hr.permission === 'allow') {\n const um = String(hr.user_message || '');\n const am = String(hr.agent_message || um);\n if (um || am) {\n finishWith({ permission: 'allow' });\n }\n }\n finishWith(hr);\n }\n log('bashGuard ' + cmdShort + ' → pass (no hook_response)');\n finishAllow();\n } catch (e) {\n log('bashGuard error: ' + String(e));\n finishAllow();\n }\n}\n\nmain().catch((e) => {\n log('bashGuard fatal: ' + String(e));\n finishAllow();\n});`;\n\nexport const CURSOR_EDIT_PRECHECK_TS = `#!/usr/bin/env bun\nimport {\n loadJwt, ensureFreshJwt, detectRepo, loadConfig, route, tag, localGrade,\n parseVerdict, dispatchCapture, ruleMode, postWithRetry, readStdin,\n log, GATEWAY_URL,\n type Rule,\n} from './_synkro-common.ts';\nimport { basename } from 'node:path';\n\nconst CURSOR_GRADE_TIMEOUT_MS = 7500;\nconst CURSOR_CLOUD_TIMEOUT_MS = 8000;\n\nlet hookDone = false;\n\nfunction finishAllow(): never {\n if (!hookDone) {\n hookDone = true;\n try { process.stdout.write('{}\\\\n'); } catch {}\n }\n process.exit(0);\n}\n\nfunction finishWith(payload: Record<string, unknown>): never {\n hookDone = true;\n process.stdout.write(JSON.stringify(payload) + '\\\\n');\n process.exit(0);\n}\n\nprocess.on('SIGTERM', () => finishAllow());\n\nasync function main() {\n try {\n const input = await readStdin();\n if (!input.trim()) finishAllow();\n\n const payload = JSON.parse(input);\n const toolName = payload.tool_name || '';\n const toolInput = payload.tool_input || {};\n const cwd = payload.cwd || '';\n const sessionId = payload.conversation_id || '';\n\n const filePath = toolInput.file_path || toolInput.path || toolInput.target_file || '';\n const content = toolInput.content || toolInput.new_string || toolInput.code_edit || '';\n if (!filePath) finishAllow();\n\n const fileShort = basename(filePath);\n log('editGuard checking: ' + fileShort);\n\n const repo = detectRepo(cwd || '.');\n\n let jwt = loadJwt();\n if (!jwt) finishAllow();\n jwt = await ensureFreshJwt(jwt);\n\n const config = await loadConfig(jwt);\n if (config.silent) finishAllow();\n\n const rt = await route(config);\n const tagStr = tag(rt, config);\n\n if (rt === 'local') {\n const contentShort = content.slice(0, 4000);\n const rulesBlock = config.rules.map((r: Rule, i: number) =>\n (i + 1) + '. [' + r.rule_id + '] (' + r.severity + '/' + r.mode + ') ' + r.text\n ).join('\\\\n');\n\n const graderPrompt = [\n 'RULES:',\n rulesBlock || '(none)',\n '',\n 'FILE: ' + filePath,\n '',\n 'CONTENT TO EVALUATE (first 4000 chars):',\n contentShort,\n ].join('\\\\n');\n\n let gradeResp: string;\n try {\n gradeResp = await localGrade('edit', graderPrompt, CURSOR_GRADE_TIMEOUT_MS);\n } catch (e) {\n log('editGuard ' + fileShort + ' → pass (grade unavailable): ' + String(e));\n finishWith({ permission: 'allow' });\n }\n\n const verdict = parseVerdict(gradeResp);\n const editContent = 'file=' + filePath + ' content=' + content.slice(0, 2000);\n\n if (!verdict.ok) {\n const mode = verdict.ruleMode || ruleMode(verdict.ruleId, config.rules);\n const guardReason = (verdict.ruleId ? '(' + verdict.ruleId + ') ' : '') + (verdict.reason || 'policy violation');\n\n if (mode !== 'audit') {\n dispatchCapture(jwt, 'edit', 'block', verdict.severity || 'critical', verdict.category || 'security',\n toolName || 'Edit', repo, sessionId, config.captureDepth, {\n command: editContent, reasoning: guardReason,\n rulesChecked: config.rules, violatedRules: verdict.ruleId ? [verdict.ruleId] : [],\n });\n finishWith({\n permission: 'deny',\n user_message: tagStr + ' editGuard ' + fileShort + ' → block: ' + guardReason,\n agent_message: 'Synkro safety judge. Reasoning: ' + (verdict.reason || guardReason),\n });\n }\n\n dispatchCapture(jwt, 'edit', 'warning', verdict.severity || 'medium', verdict.category || 'security',\n toolName || 'Edit', repo, sessionId, config.captureDepth, {\n command: editContent, reasoning: guardReason,\n rulesChecked: config.rules, violatedRules: verdict.ruleId ? [verdict.ruleId] : [],\n });\n const warnReason = verdict.reason || guardReason;\n log('editGuard ' + fileShort + ' \\\\u2192 audit warning: ' + warnReason);\n finishWith({ permission: 'allow' });\n }\n\n dispatchCapture(jwt, 'edit', 'pass', 'audit', verdict.category || 'trivial_edit',\n toolName || 'Edit', repo, sessionId, config.captureDepth, {\n command: editContent, reasoning: verdict.reason || 'no policy violations detected',\n rulesChecked: config.rules, violatedRules: [],\n });\n const passReason = verdict.reason || 'no policy violations detected';\n log('editGuard ' + fileShort + ' \\\\u2192 pass: ' + passReason);\n finishWith({ permission: 'allow' });\n }\n\n const body = {\n hook_event: 'PreToolUse',\n tool_name: toolName || 'Edit',\n tool_input: { file_path: filePath, content },\n file_path: filePath,\n content,\n response_format: 'cursor',\n session_id: sessionId || null,\n cwd: cwd || null,\n repo: repo || null,\n };\n\n const resp = await postWithRetry(GATEWAY_URL + '/api/v1/hook/judge', body, jwt, CURSOR_CLOUD_TIMEOUT_MS);\n\n if (!resp) {\n log('editGuard ' + fileShort + ' → pass (cloud timeout)');\n finishAllow();\n }\n\n if (resp.hook_response) {\n const hr = resp.hook_response as Record<string, unknown>;\n if (hr.permission === 'allow') {\n const um = String(hr.user_message || '');\n const am = String(hr.agent_message || um);\n if (um || am) {\n finishWith({ permission: 'allow' });\n }\n }\n finishWith(hr);\n }\n log('editGuard ' + fileShort + ' → pass (no hook_response)');\n finishAllow();\n } catch (e) {\n log('editGuard error: ' + String(e));\n finishAllow();\n }\n}\n\nmain().catch((e) => {\n log('editGuard fatal: ' + String(e));\n finishAllow();\n});`;\n\nexport const CURSOR_EDIT_CAPTURE_TS = `#!/usr/bin/env bun\nimport {\n loadJwt, ensureFreshJwt, detectRepo, readStdin,\n appendLocalTelemetry, log, GATEWAY_URL,\n} from './_synkro-common.ts';\nimport { existsSync, readFileSync } from 'node:fs';\nimport { basename, dirname, join } from 'node:path';\nimport { homedir } from 'node:os';\n\nlet hookDone = false;\n\nfunction finish(): never {\n if (!hookDone) {\n hookDone = true;\n try { process.stdout.write('{}\\\\n'); } catch {}\n }\n process.exit(0);\n}\n\nprocess.on('SIGTERM', () => finish());\n\nasync function main() {\n try {\n const input = await readStdin();\n if (!input.trim()) finish();\n\n const payload = JSON.parse(input);\n const filePath = payload.file_path || '';\n if (!filePath) finish();\n\n const cwd = payload.cwd || payload.workspace_roots?.[0] || '';\n const sessionId = payload.conversation_id || '';\n const repo = detectRepo(cwd || '.');\n\n log('editScan ' + basename(filePath));\n\n let jwt = loadJwt();\n if (!jwt) finish();\n jwt = await ensureFreshJwt(jwt);\n\n let fileContent = '';\n const fullPath = filePath.startsWith('/') ? filePath : (cwd ? join(cwd, filePath) : filePath);\n try {\n if (existsSync(fullPath)) {\n const buf = readFileSync(fullPath);\n fileContent = buf.slice(0, 50000).toString('utf-8');\n }\n } catch {}\n\n let dependencies: Record<string, string> = {};\n let pkgDir = cwd || dirname(fullPath);\n while (pkgDir !== '/' && pkgDir !== '.') {\n const pkgPath = join(pkgDir, 'package.json');\n if (existsSync(pkgPath)) {\n try {\n const pkg = JSON.parse(readFileSync(pkgPath, 'utf-8'));\n dependencies = { ...(pkg.dependencies || {}), ...(pkg.devDependencies || {}) };\n } catch {}\n break;\n }\n const parent = dirname(pkgDir);\n if (parent === pkgDir) break;\n pkgDir = parent;\n }\n\n const captureBody: Record<string, any> = {\n capture_type: 'edit_scan',\n tool_input: { file_path: filePath, content: fileContent },\n edit_verdict: { ok: true },\n dependencies,\n };\n if (sessionId) captureBody.session_id = sessionId;\n if (cwd) captureBody.cwd = cwd;\n if (repo) captureBody.repo = repo;\n\n const rulesPath = join(homedir(), '.synkro', 'rules.json');\n if (existsSync(rulesPath)) {\n appendLocalTelemetry(captureBody);\n } else {\n fetch(GATEWAY_URL + '/api/v1/hook/capture', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json', Authorization: 'Bearer ' + jwt },\n body: JSON.stringify(captureBody),\n signal: AbortSignal.timeout(10000),\n }).catch(() => {});\n appendLocalTelemetry(captureBody);\n }\n\n finish();\n } catch (e) {\n log('editScan error: ' + String(e));\n finish();\n }\n}\n\nmain().catch((e) => {\n log('editScan fatal: ' + String(e));\n finish();\n});`;\n\nexport const CURSOR_BASH_FOLLOWUP_TS = `#!/usr/bin/env bun\nimport {\n loadJwt, readStdin, appendLocalTelemetry, log, GATEWAY_URL,\n} from './_synkro-common.ts';\nimport { existsSync, readFileSync, writeFileSync, appendFileSync, mkdirSync } from 'node:fs';\nimport { join, dirname } from 'node:path';\nimport { createHash } from 'node:crypto';\nimport { homedir } from 'node:os';\n\nconst CONSENT_FILE = join(homedir(), '.synkro', '.local-consent');\n\nlet hookDone = false;\n\nfunction finish(): never {\n if (!hookDone) {\n hookDone = true;\n try { process.stdout.write('{}\\\\n'); } catch {}\n }\n process.exit(0);\n}\n\nprocess.on('SIGTERM', () => finish());\n\nfunction hashCmd(cmd: string): string {\n return createHash('sha256').update(cmd).digest('hex').slice(0, 16);\n}\n\nfunction consentGrant(sid: string, hash: string): void {\n try {\n const dir = dirname(CONSENT_FILE);\n if (!existsSync(dir)) mkdirSync(dir, { recursive: true });\n appendFileSync(CONSENT_FILE, sid + '\\\\t' + hash + '\\\\tactive\\\\n', 'utf-8');\n } catch {}\n}\n\nfunction consentHasActive(sid: string, hash: string): boolean {\n try {\n if (!existsSync(CONSENT_FILE)) return false;\n const content = readFileSync(CONSENT_FILE, 'utf-8');\n return content.includes(sid + '\\\\t' + hash + '\\\\tactive');\n } catch {\n return false;\n }\n}\n\nfunction consentConsume(sid: string, hash: string): void {\n try {\n if (!existsSync(CONSENT_FILE)) return;\n const content = readFileSync(CONSENT_FILE, 'utf-8');\n const target = sid + '\\\\t' + hash + '\\\\tactive';\n const replacement = sid + '\\\\t' + hash + '\\\\tconsumed';\n const updated = content.split('\\\\n').map((l: string) => l === target ? replacement : l).join('\\\\n');\n writeFileSync(CONSENT_FILE, updated, 'utf-8');\n } catch {}\n}\n\nasync function main() {\n try {\n const input = await readStdin();\n if (!input.trim()) finish();\n\n const payload = JSON.parse(input);\n const toolName = payload.tool_name || '';\n\n const shellTools = ['Shell', 'Bash', 'terminal', 'run_terminal_cmd', 'execute_command'];\n if (!shellTools.includes(toolName)) finish();\n\n const sessionId = payload.conversation_id || '';\n const toolUseId = payload.tool_use_id || '';\n const command = payload.tool_input?.command || '';\n\n let isError = false;\n try {\n const output = JSON.parse(payload.tool_output || '{}');\n isError = output.exitCode !== 0 || output.is_error === true;\n } catch { isError = false; }\n\n const cmdHash = command ? hashCmd(command) : '';\n\n if (cmdHash && sessionId) {\n if (!isError) {\n consentConsume(sessionId, cmdHash);\n } else {\n if (!consentHasActive(sessionId, cmdHash)) {\n consentGrant(sessionId, cmdHash);\n }\n }\n }\n\n const captureBody: Record<string, any> = {\n capture_type: 'bash_followup',\n session_id: sessionId || null,\n tool_use_id: toolUseId || null,\n is_error: isError,\n command_hash: cmdHash,\n };\n\n const rulesPath = join(homedir(), '.synkro', 'rules.json');\n if (existsSync(rulesPath)) {\n appendLocalTelemetry(captureBody);\n } else {\n const jwt = loadJwt();\n if (jwt && sessionId && toolUseId) {\n fetch(GATEWAY_URL + '/api/v1/hook/capture', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json', Authorization: 'Bearer ' + jwt },\n body: JSON.stringify(captureBody),\n signal: AbortSignal.timeout(3000),\n }).catch(() => {});\n }\n appendLocalTelemetry(captureBody);\n }\n\n finish();\n } catch (e) {\n log('bashFollowup error: ' + String(e));\n finish();\n }\n}\n\nmain().catch((e) => {\n log('bashFollowup fatal: ' + String(e));\n finish();\n});`;\n\nexport const CURSOR_SESSION_START_TS = `#!/usr/bin/env bun\nimport {\n loadJwt, loadConfig, readStdin, log,\n type HookConfig,\n} from './_synkro-common.ts';\n\nlet hookDone = false;\n\nfunction finishAllow(): never {\n if (!hookDone) {\n hookDone = true;\n try { process.stdout.write('{}\\\\n'); } catch {}\n }\n process.exit(0);\n}\n\nfunction finishWith(payload: Record<string, unknown>): never {\n hookDone = true;\n process.stdout.write(JSON.stringify(payload) + '\\\\n');\n process.exit(0);\n}\n\nprocess.on('SIGTERM', () => finishAllow());\n\nasync function main() {\n try {\n const input = await readStdin();\n\n let jwt = loadJwt();\n const config: HookConfig = jwt ? await loadConfig(jwt) : {\n captureDepth: 'local_only', tier: 'standard', silent: false,\n policyName: '', rules: [], scanExemptions: [],\n };\n\n const policyName = config.policyName || 'default';\n const ruleCount = config.rules.length;\n const mode = config.silent ? 'silent' : 'active';\n\n const context = [\n 'This session is monitored by Synkro (' + mode + ' mode, policy: \"' + policyName + '\", ' + ruleCount + ' rules).',\n 'Synkro enforces security and compliance rules on tool calls (shell commands, file edits).',\n 'If a tool call is blocked, Synkro will explain which rule was violated and why.',\n 'Do not suggest workarounds to bypass Synkro hooks — fix the underlying issue instead.',\n ].join(' ');\n\n finishWith({ additional_context: context });\n } catch (e) {\n log('sessionStart error: ' + String(e));\n finishAllow();\n }\n}\n\nmain().catch((e) => {\n log('sessionStart fatal: ' + String(e));\n finishAllow();\n});`;\n\n","/**\n * Synkro CLI Authentication\n *\n * OAuth-style authentication flow for CLI integration with Synkro web platform.\n * Mirrors the Node.js reference implementation.\n */\n\nimport { createServer, IncomingMessage, ServerResponse } from \"node:http\";\nimport { writeFileSync, readFileSync, existsSync, mkdirSync, unlinkSync } from \"node:fs\";\nimport { homedir, platform } from \"node:os\";\nimport { join, dirname } from \"node:path\";\nimport { execFile } from \"node:child_process\";\nimport jwt from \"jsonwebtoken\";\n\n// Types\ninterface WorkOSJwtPayload {\n iss: string; // \"https://api.workos.com/\"\n sub: string; // user ID\n aud?: string; // client ID\n exp: number;\n iat: number;\n email?: string;\n org_id?: string;\n role?: string;\n permissions?: string[];\n sid?: string; // session ID\n}\n\n// Configuration — matches the desktop app pattern (packages/desktop/src-tauri/src/auth.rs).\n// Dev dashboard runs on :4322; CLI listens on 8100 for the OAuth callback.\nconst PORT = 8100;\n// Same poisoning concern as the gateway URL: a developer's local .env or\n// op:// expansion can land in SYNKRO_WEB_AUTH_URL. Only honor http(s) values;\n// fall through to the prod dashboard otherwise so the OAuth callback always\n// has a real origin to open the browser at.\nconst RAW_WEB_AUTH_URL = process.env.SYNKRO_WEB_AUTH_URL;\nconst SYNKRO_WEB_AUTH_URL = (RAW_WEB_AUTH_URL && /^https?:\\/\\//.test(RAW_WEB_AUTH_URL))\n ? RAW_WEB_AUTH_URL\n : \"https://app.synkro.sh\";\nconst AUTH_FILE = process.env.SYNKRO_AUTH_FILE || join(homedir(), \".synkro\", \"credentials.json\");\nconst RAW_API_URL = process.env.SYNKRO_CRUD_URL || process.env.SYNKRO_API_URL;\nconst SYNKRO_API_URL = (RAW_API_URL && /^https?:\\/\\//.test(RAW_API_URL))\n ? RAW_API_URL\n : \"https://api.synkro.sh\";\n\n// Types — matches the AuthCredentials shape returned by the dashboard's\n// /api/auth/cli-callback (see packages/app/src/pages/api/auth/cli-callback.ts).\nexport interface AuthCredentials {\n access_token: string;\n refresh_token: string;\n user_id?: string;\n email?: string;\n org_id?: string;\n state?: string;\n}\n\nexport interface UserInfo {\n id: string;\n email: string;\n org_id?: string;\n}\n\n// HTML responses\nconst SUCCESS_HTML = `\n<!DOCTYPE html>\n<html>\n<head>\n <title>Authentication Successful - Synkro CLI</title>\n <style>\n body {\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;\n display: flex;\n justify-content: center;\n align-items: center;\n height: 100vh;\n margin: 0;\n background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);\n }\n .container {\n background: white;\n padding: 3rem;\n border-radius: 1rem;\n box-shadow: 0 20px 60px rgba(0,0,0,0.3);\n text-align: center;\n max-width: 400px;\n }\n .checkmark {\n width: 80px;\n height: 80px;\n border-radius: 50%;\n background: #10b981;\n margin: 0 auto 1.5rem;\n display: flex;\n align-items: center;\n justify-content: center;\n }\n .checkmark svg {\n width: 50px;\n height: 50px;\n stroke: white;\n }\n h1 {\n color: #1f2937;\n margin: 0 0 0.5rem;\n }\n p {\n color: #6b7280;\n margin: 0;\n }\n .close-note {\n margin-top: 1.5rem;\n font-size: 0.875rem;\n color: #9ca3af;\n }\n </style>\n</head>\n<body>\n <div class=\"container\">\n <div class=\"checkmark\">\n <svg fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"3\" d=\"M5 13l4 4L19 7\"></path>\n </svg>\n </div>\n <h1>Authentication Successful!</h1>\n <p>Your Synkro CLI has been authenticated.</p>\n <p class=\"close-note\">You can close this window and return to your terminal.</p>\n </div>\n</body>\n</html>\n`;\n\nconst ERROR_HTML = `\n<!DOCTYPE html>\n<html>\n<head>\n <title>Authentication Failed - Synkro CLI</title>\n <style>\n body {\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;\n display: flex;\n justify-content: center;\n align-items: center;\n height: 100vh;\n margin: 0;\n background: linear-gradient(135deg, #f87171 0%, #dc2626 100%);\n }\n .container {\n background: white;\n padding: 3rem;\n border-radius: 1rem;\n box-shadow: 0 20px 60px rgba(0,0,0,0.3);\n text-align: center;\n max-width: 400px;\n }\n h1 {\n color: #1f2937;\n margin: 0 0 0.5rem;\n }\n p {\n color: #6b7280;\n }\n </style>\n</head>\n<body>\n <div class=\"container\">\n <h1>Authentication Failed</h1>\n <p>Please try again from your terminal.</p>\n </div>\n</body>\n</html>\n`;\n\n/**\n * Open URL in default browser\n */\nfunction openBrowser(url: string): void {\n const os = platform();\n let bin: string;\n let args: string[];\n\n switch (os) {\n case \"darwin\":\n bin = \"open\";\n args = [url];\n break;\n case \"win32\":\n // `start` is a cmd built-in, so we host it via cmd /c, but pass the URL\n // as a literal arg through execFile (no shell parsing) to keep shell\n // metacharacters from being interpreted.\n bin = \"cmd\";\n args = [\"/c\", \"start\", \"\", url];\n break;\n default:\n bin = \"xdg-open\";\n args = [url];\n }\n\n // execFile (vs exec) does NOT spawn a shell, so url is treated as a single\n // argv entry regardless of contents. Removes shell-injection risk if url\n // ever ends up containing $/`/;/&/etc.\n execFile(bin, args, (error) => {\n if (error) {\n console.error(\"Failed to open browser automatically.\");\n console.log(`Please open this URL manually: ${url}`);\n }\n });\n}\n\n/**\n * Save authentication credentials to file\n */\nexport function saveCredentials(data: AuthCredentials): void {\n const dir = dirname(AUTH_FILE);\n\n if (!existsSync(dir)) {\n mkdirSync(dir, { recursive: true, mode: 0o700 });\n }\n\n writeFileSync(AUTH_FILE, JSON.stringify(data, null, 2), { mode: 0o600 });\n}\n\n/**\n * Load saved authentication credentials\n */\nexport function loadCredentials(): AuthCredentials | null {\n if (!existsSync(AUTH_FILE)) {\n return null;\n }\n\n try {\n const content = readFileSync(AUTH_FILE, \"utf8\");\n return JSON.parse(content);\n } catch (error) {\n return null;\n }\n}\n\n/**\n * Create HTTP server to receive OAuth callback.\n *\n * Mirrors packages/desktop/src-tauri/src/auth.rs:\n * - Listens on PORT\n * - Handles OPTIONS preflight (CORS)\n * - Catches /auth?token=...&refresh_token=...&user_id=...&email=...&org_id=...&state=...\n * - Returns success HTML on token receipt\n */\nfunction createCallbackServer(): Promise<AuthCredentials> {\n // Tokens land via POST body (JSON), never query params, so they don't get\n // logged into req.url, browser DevTools URL bars, server access logs, or\n // tooling that snapshots URLs. CORS origin is pinned to the configured\n // dashboard so a random page on a different origin can't post forged\n // credentials at the local listener.\n const CORS_HEADERS = {\n \"Access-Control-Allow-Origin\": SYNKRO_WEB_AUTH_URL,\n \"Access-Control-Allow-Methods\": \"POST, OPTIONS\",\n \"Access-Control-Allow-Headers\": \"Content-Type\",\n \"Vary\": \"Origin\",\n };\n\n return new Promise((resolve, reject) => {\n const server = createServer((req: IncomingMessage, res: ServerResponse) => {\n // CORS preflight — only echo the pinned origin if the request actually\n // comes from there; otherwise omit ACAO so the browser blocks the call.\n if (req.method === \"OPTIONS\") {\n const origin = req.headers.origin;\n if (origin === SYNKRO_WEB_AUTH_URL) {\n res.writeHead(204, CORS_HEADERS);\n } else {\n res.writeHead(204, {\n \"Access-Control-Allow-Methods\": \"POST, OPTIONS\",\n \"Access-Control-Allow-Headers\": \"Content-Type\",\n \"Vary\": \"Origin\",\n });\n }\n res.end();\n return;\n }\n\n // Reject requests whose Origin header isn't the dashboard. Browsers\n // send Origin on cross-origin POSTs, so this stops a hostile page from\n // forging credentials at our local listener even if it bypasses CORS.\n const reqOrigin = req.headers.origin;\n if (reqOrigin && reqOrigin !== SYNKRO_WEB_AUTH_URL) {\n res.writeHead(403, { \"Vary\": \"Origin\" });\n res.end();\n return;\n }\n\n if (!req.url) {\n res.writeHead(404, CORS_HEADERS);\n res.end();\n return;\n }\n\n const url = new URL(req.url, `http://localhost:${PORT}`);\n\n if (url.pathname !== \"/auth\") {\n res.writeHead(404, CORS_HEADERS);\n res.end();\n return;\n }\n\n if (req.method !== \"POST\") {\n // Reject GET so a stale/older dashboard build can't deliver tokens\n // via query-string. Forces upgrade. CLI 1.0.4+ requires the v1.6+\n // dashboard build.\n res.writeHead(405, { ...CORS_HEADERS, \"Allow\": \"POST, OPTIONS\", \"Content-Type\": \"text/html\" });\n res.end(ERROR_HTML);\n return;\n }\n\n // Cap body at 16 KB — JWT pairs are ~4–8 KB; anything bigger is junk.\n const MAX_BODY = 16 * 1024;\n const chunks: Buffer[] = [];\n let total = 0;\n let aborted = false;\n req.on(\"data\", (chunk: Buffer) => {\n if (aborted) return;\n total += chunk.length;\n if (total > MAX_BODY) {\n aborted = true;\n res.writeHead(413, CORS_HEADERS);\n res.end();\n return;\n }\n chunks.push(chunk);\n });\n req.on(\"end\", () => {\n if (aborted) return;\n let parsed: any;\n try {\n parsed = JSON.parse(Buffer.concat(chunks).toString(\"utf8\"));\n } catch {\n res.writeHead(400, { ...CORS_HEADERS, \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ error: \"invalid_json\" }));\n setTimeout(() => {\n server.close();\n reject(new Error(\"Authentication failed: invalid JSON body\"));\n }, 200);\n return;\n }\n\n const token: string | undefined = parsed?.token;\n if (!token || typeof token !== \"string\") {\n res.writeHead(400, { ...CORS_HEADERS, \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ error: \"missing_token\" }));\n setTimeout(() => {\n server.close();\n reject(new Error(\"Authentication failed: missing token\"));\n }, 200);\n return;\n }\n\n const authData: AuthCredentials = {\n access_token: token,\n refresh_token: typeof parsed.refresh_token === \"string\" ? parsed.refresh_token : \"\",\n user_id: typeof parsed.user_id === \"string\" ? parsed.user_id : undefined,\n email: typeof parsed.email === \"string\" ? parsed.email : undefined,\n org_id: typeof parsed.org_id === \"string\" ? parsed.org_id : undefined,\n state: typeof parsed.state === \"string\" ? parsed.state : undefined,\n };\n\n res.writeHead(200, { ...CORS_HEADERS, \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ ok: true }));\n\n setTimeout(() => {\n server.close();\n resolve(authData);\n }, 200);\n });\n req.on(\"error\", (e) => {\n if (aborted) return;\n aborted = true;\n try { res.writeHead(500, CORS_HEADERS); res.end(); } catch {}\n setTimeout(() => {\n server.close();\n reject(e);\n }, 200);\n });\n });\n\n server.listen(PORT);\n\n server.on(\"error\", (error: NodeJS.ErrnoException) => {\n if (error.code === \"EADDRINUSE\") {\n reject(\n new Error(\n `Port ${PORT} is already in use. Close any other Synkro CLI instance and retry.`,\n ),\n );\n } else {\n reject(error);\n }\n });\n });\n}\n\nexport type AuthStatus =\n | { phase: 'starting' }\n | { phase: 'browser-opened'; url: string }\n | { phase: 'waiting' }\n | { phase: 'success' }\n | { phase: 'error'; message: string };\n\n/**\n * Initiate the OAuth-style authentication flow\n */\nexport async function authenticate(\n onStatus?: (status: AuthStatus) => void,\n): Promise<AuthCredentials | null> {\n const emit = onStatus || (() => {});\n\n try {\n emit({ phase: 'starting' });\n\n // Start local server to receive the callback\n const serverPromise = createCallbackServer();\n\n // Open browser to the CLI auth page\n const authUrl = `${SYNKRO_WEB_AUTH_URL}/cli-auth?port=${PORT}`;\n openBrowser(authUrl);\n\n emit({ phase: 'browser-opened', url: authUrl });\n emit({ phase: 'waiting' });\n\n // Wait for authentication callback\n const data = await serverPromise;\n\n emit({ phase: 'success' });\n saveCredentials(data);\n return data;\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n emit({ phase: 'error', message });\n return null;\n }\n}\n\n/**\n * Check if user is authenticated (credentials exist and token not expired)\n */\nexport function isAuthenticated(): boolean {\n const creds = loadCredentials();\n if (!creds) return false;\n\n // Also check token expiry\n try {\n const decoded = jwt.decode(creds.access_token) as { exp?: number } | null;\n if (!decoded?.exp) return true; // Can't decode, assume valid (refresh will handle it)\n\n // Consider expired if past expiration (no buffer here — ensureValidToken handles refresh buffer)\n return Date.now() < decoded.exp * 1000;\n } catch {\n return true; // Decode failed, let ensureValidToken handle it\n }\n}\n\n/**\n * Get current user ID from JWT token\n */\nexport function getCurrentUserId(): string {\n const creds = loadCredentials();\n if (!creds) {\n throw new Error(\"Not authenticated\");\n }\n\n const decoded = jwt.decode(creds.access_token) as WorkOSJwtPayload | null;\n if (!decoded?.sub) {\n throw new Error(\"Invalid token\");\n }\n\n return decoded.sub;\n}\n\n/**\n * Get user info from JWT\n */\nexport function getUserInfo(): UserInfo {\n const creds = loadCredentials();\n if (!creds) {\n throw new Error(\"Not authenticated\");\n }\n\n // Prefer the explicit user_id/email/org_id stashed during the OAuth callback\n // (the dashboard returns them as query params). Fall back to JWT decode if\n // we somehow received older creds without those fields.\n if (creds.user_id) {\n return {\n id: creds.user_id,\n email: creds.email ?? '',\n org_id: creds.org_id,\n };\n }\n\n const decoded = jwt.decode(creds.access_token) as WorkOSJwtPayload | null;\n if (!decoded) {\n throw new Error(\"Invalid token\");\n }\n\n return {\n id: decoded.sub,\n email: decoded.email ?? '',\n org_id: decoded.org_id,\n };\n}\n\n/**\n * Get access token for API calls\n */\nexport function getAccessToken(): string | null {\n const creds = loadCredentials();\n return creds?.access_token || null;\n}\n\n/**\n * Check if token is expired (with 5 min buffer)\n */\nexport function isTokenExpired(): boolean {\n const creds = loadCredentials();\n if (!creds) return true;\n\n try {\n const decoded = jwt.decode(creds.access_token) as { exp?: number } | null;\n if (!decoded?.exp) return true;\n\n // Expired if less than 5 minutes remaining\n const expiresAt = decoded.exp * 1000;\n const buffer = 5 * 60 * 1000; // 5 minutes\n return Date.now() > expiresAt - buffer;\n } catch {\n return true;\n }\n}\n\n/**\n * Refresh the access token using refresh_token\n */\nexport async function refreshToken(): Promise<boolean> {\n const creds = loadCredentials();\n if (!creds?.refresh_token) return false;\n\n try {\n const response = await fetch(`${SYNKRO_API_URL}/api/auth/refresh`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ refresh_token: creds.refresh_token }),\n });\n\n if (!response.ok) return false;\n\n const data = await response.json();\n if (data.access_token) {\n saveCredentials({\n ...creds,\n access_token: data.access_token,\n refresh_token: data.refresh_token || creds.refresh_token,\n });\n return true;\n }\n return false;\n } catch {\n return false;\n }\n}\n\n// Mutex: prevent concurrent token refresh races\nlet refreshPromise: Promise<boolean> | null = null;\n\n/**\n * Ensure we have a valid token, refreshing if needed\n */\nexport async function ensureValidToken(): Promise<boolean> {\n if (!isAuthenticated()) return false;\n\n if (isTokenExpired()) {\n if (!refreshPromise) {\n refreshPromise = refreshToken().finally(() => { refreshPromise = null; });\n }\n const refreshed = await refreshPromise;\n if (!refreshed) {\n clearCredentials();\n return false;\n }\n }\n\n return true;\n}\n\n/**\n * Clear saved credentials (logout)\n */\nexport function clearCredentials(): void {\n if (existsSync(AUTH_FILE)) {\n unlinkSync(AUTH_FILE);\n }\n}\n\n/**\n * Get secrets for a user's integrations\n * In production, this would fetch from Infisical vault using the access token\n *\n * These are secrets the USER provides for THEIR integrations:\n * - AWS credentials (for their S3 buckets)\n * - HuggingFace token (for their datasets)\n * - Langsmith API key (for their projects)\n */\nexport async function getSecrets(\n userId: string,\n integrationId: string,\n): Promise<Record<string, string>> {\n // TODO: In production, use access token to fetch from Infisical\n // For now, return from environment (dev mode)\n return {\n AWS_ACCESS_KEY_ID: process.env.USER_AWS_KEY || \"\",\n AWS_SECRET_ACCESS_KEY: process.env.USER_AWS_SECRET || \"\",\n AWS_REGION: process.env.USER_AWS_REGION || \"us-east-1\",\n HF_TOKEN: process.env.USER_HF_TOKEN || \"\",\n LANGSMITH_API_KEY: process.env.USER_LANGSMITH_KEY || \"\",\n };\n}\n","/**\n * Synkro CLI Authentication Module\n *\n * Exports authentication functions for use throughout the CLI.\n */\n\nexport {\n authenticate,\n isAuthenticated,\n getCurrentUserId,\n getUserInfo,\n getAccessToken,\n loadCredentials,\n saveCredentials,\n clearCredentials,\n getSecrets,\n isTokenExpired,\n refreshToken,\n ensureValidToken,\n} from './stub.js';\n\nexport type { AuthCredentials, UserInfo, AuthStatus } from './stub.js';\n","// :)\n/**\n * CLI API client for project and violation endpoints.\n *\n * Calls the Synkro API (Hono/Cloudflare Workers) for project management, violations, and policy upsert.\n * SYNKRO_CRUD_URL points to the API server (defaults to http://localhost:8788/api).\n */\n\nimport { getAccessToken, ensureValidToken } from '../auth/index.js';\n\nlet API_URL = 'https://api.synkro.sh/api';\n\nexport function setApiBaseUrl(url: string): void {\n API_URL = url;\n}\n\n// Types\n\nexport interface Project {\n id: string;\n slug: string;\n name: string;\n provider: string | null;\n is_active: boolean;\n api_key_count: number;\n created_at: string | null;\n repos?: Array<{ id: string; github_repo_id: number | null; full_name: string }>;\n}\n\nexport interface Violation {\n id: string | null;\n run_id: string | null;\n score: number | null;\n severity: string | null;\n issues: string[];\n rules_violated: string[];\n messages: Array<{ role: string; content: string }>;\n model: string | null;\n comment: string | null;\n created_at: string | null;\n}\n\nexport interface ViolationsResponse {\n violations: Violation[];\n total: number;\n project_id: string;\n project_slug: string | null;\n policy_text: string | null;\n rules: Array<Record<string, unknown>>;\n}\n\nexport interface GetViolationsOptions {\n limit?: number;\n offset?: number;\n severity?: string;\n}\n\nexport interface PolicyUpsertResponse {\n ok: boolean;\n policy_id: string;\n}\n\n// API helper\n\nasync function callApi<T>(\n method: string,\n endpoint: string,\n body?: Record<string, unknown>\n): Promise<T> {\n if (!API_URL) {\n throw new Error('API URL not configured. Run `synkro install` first.');\n }\n\n const url = `${API_URL}${endpoint}`;\n await ensureValidToken();\n const accessToken = getAccessToken();\n\n const headers: Record<string, string> = {\n 'Content-Type': 'application/json',\n };\n\n if (accessToken) {\n headers['Authorization'] = `Bearer ${accessToken}`;\n }\n\n const response = await fetch(url, {\n method,\n headers,\n body: body ? JSON.stringify(body) : undefined,\n });\n\n if (!response.ok) {\n const error = await response.json().catch(() => ({ detail: response.statusText }));\n throw new Error(error.detail || `API error: ${response.status}`);\n }\n\n return response.json();\n}\n\nexport interface CreateProjectResponse {\n id: string;\n slug: string;\n name: string;\n provider: string | null;\n is_active: boolean;\n created_at: string | null;\n}\n\nexport interface CreateApiKeyResponse {\n id: string;\n key: string;\n key_prefix: string;\n name: string;\n project_id: string;\n created_at: string | null;\n}\n\n// Project API functions\n\n/**\n * Create a new project.\n */\nexport async function createProject(\n name: string,\n repos?: Array<{ github_repo_id?: number; full_name: string; default_branch?: string; private?: boolean }>,\n): Promise<CreateProjectResponse> {\n const body: Record<string, unknown> = { name };\n if (repos && repos.length > 0) body.repos = repos;\n return callApi<CreateProjectResponse>('POST', '/projects', body);\n}\n\n/**\n * Create an API key for a project. Returns plaintext key (shown once).\n */\nexport async function createApiKey(\n projectId: string,\n name?: string\n): Promise<CreateApiKeyResponse> {\n return callApi<CreateApiKeyResponse>('POST', '/api-keys', {\n project_id: projectId,\n name: name || 'CLI Key',\n });\n}\n\n/**\n * List all projects for the authenticated user.\n */\nexport async function listProjects(): Promise<Project[]> {\n return callApi<Project[]>('GET', '/projects');\n}\n\n/**\n * Get violations for a project with optional filtering.\n * Also returns the active policy text and rules.\n */\nexport async function getViolations(\n projectId: string,\n options: GetViolationsOptions = {}\n): Promise<ViolationsResponse> {\n const params = new URLSearchParams();\n if (options.limit) params.set('limit', String(options.limit));\n if (options.offset) params.set('offset', String(options.offset));\n if (options.severity) params.set('severity', options.severity);\n\n const qs = params.toString();\n const endpoint = `/projects/${projectId}/violations${qs ? `?${qs}` : ''}`;\n return callApi<ViolationsResponse>('GET', endpoint);\n}\n\n/**\n * Upsert a policy for a project (deploy rules to gateway).\n */\nexport async function upsertPolicy(\n projectId: string,\n policyText: string,\n rules: Array<Record<string, unknown>>,\n ruleCount: number\n): Promise<PolicyUpsertResponse> {\n return callApi<PolicyUpsertResponse>('POST', `/projects/${projectId}/policies`, {\n policy_text: policyText,\n rules,\n rule_count: ruleCount,\n });\n}\n\n/**\n * Update a project's configuration.\n */\nexport async function updateProject(\n projectId: string,\n updates: {\n name?: string;\n provider?: string;\n langsmith_project?: string;\n is_active?: boolean;\n }\n): Promise<{ ok: boolean }> {\n return callApi<{ ok: boolean }>('PATCH', `/projects/${projectId}`, updates as Record<string, unknown>);\n}\n\n/**\n * Unlink a repo from a project.\n */\nexport async function unlinkRepo(\n projectId: string,\n repoId: string,\n): Promise<{ ok: boolean }> {\n return callApi<{ ok: boolean }>('DELETE', `/projects/${projectId}/repos/${repoId}`);\n}\n\n/**\n * Resolve a project by name or slug (fuzzy match against user's projects).\n * Returns the first matching project or null.\n */\nexport async function resolveProject(nameOrSlug: string): Promise<Project | null> {\n const projects = await listProjects();\n const query = nameOrSlug.toLowerCase();\n\n // Exact slug match\n const exactSlug = projects.find(p => p.slug === nameOrSlug);\n if (exactSlug) return exactSlug;\n\n // Exact name match (case-insensitive)\n const exactName = projects.find(p => p.name.toLowerCase() === query);\n if (exactName) return exactName;\n\n // Partial name match\n const partial = projects.find(p => p.name.toLowerCase().includes(query));\n if (partial) return partial;\n\n // Partial slug match\n const partialSlug = projects.find(p => p.slug.includes(query));\n if (partialSlug) return partialSlug;\n\n return null;\n}\n","// :)\n/**\n * GitHub Actions workflow YAML template for Synkro PR scanning.\n *\n * Customer commits this to .github/workflows/synkro.yml. Triggers on\n * pull_request open/synchronize/reopened. Runs `synkro scan-pr` which\n * spawns claude --print with their CLAUDE_CODE_OAUTH_TOKEN per file.\n *\n * Both CLIs install via npm (npm registry attestations + signed packages\n * are verified by npm itself). No curl-pipe-to-bash; we publish to npm\n * specifically so CI can install us through a trusted package manager.\n */\n\nexport const SYNKRO_WORKFLOW_YAML = `name: Synkro Security Review\non:\n pull_request:\n types: [opened, synchronize, reopened]\n workflow_dispatch:\n inputs:\n pr_number:\n description: PR number to scan\n required: true\n sha:\n description: Commit SHA to scan\n required: true\n\njobs:\n scan:\n runs-on: ubuntu-latest\n permissions:\n contents: write\n pull-requests: write\n checks: write\n steps:\n - uses: actions/checkout@v4\n with:\n fetch-depth: 0\n ref: \\${{ inputs.sha || github.event.pull_request.head.sha }}\n\n - name: Cache npm globals\n id: cache-npm-global\n uses: actions/cache@v4\n with:\n path: ~/.npm-global\n key: synkro-cli-\\${{ runner.os }}-v1\n\n - name: Install Synkro CLI + Claude Code CLI\n run: |\n npm config set prefix ~/.npm-global\n npm install -g @synkro-sh/cli @anthropic-ai/claude-code\n echo \"$HOME/.npm-global/bin\" >> $GITHUB_PATH\n\n - name: Run Synkro PR scan\n run: synkro-cli scan-pr\n env:\n CLAUDE_CODE_OAUTH_TOKEN: \\${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}\n SYNKRO_API_KEY: \\${{ secrets.SYNKRO_API_KEY }}\n GH_TOKEN: \\${{ secrets.GITHUB_TOKEN }}\n SYNKRO_PR_NUMBER: \\${{ inputs.pr_number || github.event.pull_request.number }}\n SYNKRO_REPO: \\${{ github.repository }}\n SYNKRO_SHA: \\${{ inputs.sha || github.event.pull_request.head.sha }}\n SYNKRO_GATEWAY_URL: \\${{ vars.SYNKRO_GATEWAY_URL || 'https://api.synkro.sh' }}\n`;\n\nexport const WORKFLOW_FILENAME = 'synkro.yml';\nexport const WORKFLOW_PATH = '.github/workflows/synkro.yml';\n","/**\n * GitHub repo setup for PR scanning.\n *\n * Uses `gh secret set` to push CLAUDE_CODE_OAUTH_TOKEN + SYNKRO_API_KEY\n * as repo secrets, then writes .github/workflows/synkro.yml to the local\n * clone if present.\n */\nimport { existsSync, mkdirSync, writeFileSync } from 'node:fs';\nimport { execSync } from 'node:child_process';\nimport { join } from 'node:path';\nimport { SYNKRO_WORKFLOW_YAML, WORKFLOW_PATH } from './workflowTemplate.js';\n\nexport interface GitHubAuthOptions {\n token: string;\n}\n\nfunction ghSecretSet(token: string, owner: string, repo: string, name: string, value: string): void {\n execSync(`gh secret set ${name} --repo ${owner}/${repo} --body -`, {\n input: value,\n env: { ...process.env, GH_TOKEN: token },\n stdio: ['pipe', 'ignore', 'pipe'],\n timeout: 30_000,\n });\n}\n\n/**\n * List repos the token has access to.\n */\nexport async function listAccessibleRepos(opts: GitHubAuthOptions): Promise<Array<{ owner: string; repo: string; full_name: string }>> {\n const repos: Array<{ owner: string; repo: string; full_name: string }> = [];\n let page = 1;\n while (page <= 5) { // cap pagination\n const url = `https://api.github.com/user/repos?per_page=100&page=${page}&affiliation=owner,collaborator`;\n const resp = await fetch(url, {\n headers: {\n Authorization: `Bearer ${opts.token}`,\n Accept: 'application/vnd.github+json',\n 'X-GitHub-Api-Version': '2022-11-28',\n },\n });\n if (!resp.ok) {\n throw new Error(`GitHub API ${resp.status} listing repos`);\n }\n const data = await resp.json() as Array<{ full_name: string; owner: { login: string }; name: string }>;\n if (data.length === 0) break;\n for (const r of data) {\n repos.push({ owner: r.owner.login, repo: r.name, full_name: r.full_name });\n }\n if (data.length < 100) break;\n page++;\n }\n return repos;\n}\n\nexport async function pushSecretsToRepo(\n opts: GitHubAuthOptions,\n owner: string,\n repo: string,\n secrets: { claudeCodeOauthToken?: string; synkroApiKey: string },\n): Promise<void> {\n try { execSync('gh --version', { stdio: 'ignore', timeout: 5000 }); } catch {\n throw new Error('GitHub CLI (gh) not found. Install it: https://cli.github.com');\n }\n if (secrets.claudeCodeOauthToken) {\n ghSecretSet(opts.token, owner, repo, 'CLAUDE_CODE_OAUTH_TOKEN', secrets.claudeCodeOauthToken);\n }\n ghSecretSet(opts.token, owner, repo, 'SYNKRO_API_KEY', secrets.synkroApiKey);\n}\n\n/**\n * Write the workflow YAML to a local repo clone (if the user is in one).\n * Returns the absolute path written, or null if cwd isn't a git repo.\n */\nexport function writeWorkflowFile(repoRootPath: string): string | null {\n const workflowDir = join(repoRootPath, '.github', 'workflows');\n mkdirSync(workflowDir, { recursive: true });\n const workflowFile = join(workflowDir, 'synkro.yml');\n writeFileSync(workflowFile, SYNKRO_WORKFLOW_YAML, 'utf-8');\n return workflowFile;\n}\n\n/**\n * Find the git repo root for a given cwd. Returns null if not in a git repo.\n */\nexport function findGitRoot(startCwd: string): string | null {\n let cur = startCwd;\n while (cur && cur !== '/') {\n if (existsSync(join(cur, '.git'))) return cur;\n const parent = join(cur, '..');\n if (parent === cur) break;\n cur = parent;\n }\n return null;\n}\n\nexport const SECRET_NAMES = {\n CLAUDE_OAUTH: 'CLAUDE_CODE_OAUTH_TOKEN',\n SYNKRO_API_KEY: 'SYNKRO_API_KEY',\n} as const;\n\nexport const WORKFLOW_RELATIVE_PATH = WORKFLOW_PATH;\n","// :)\n/**\n * Shared helpers for repo connection during install and `synkro link`.\n *\n * Two paths:\n * 1. Local git repo — detect from `git remote get-url origin`, any provider\n * 2. GitHub OAuth — browser flow, list repos, interactive picker\n */\nimport { execSync } from 'node:child_process';\nimport { createServer } from 'node:http';\nimport { createInterface } from 'node:readline';\nimport { createProject, listProjects } from '../api/projects.js';\nimport { listAccessibleRepos } from '../installer/githubSetup.js';\n\nconst RAW_WEB_AUTH_URL = process.env.SYNKRO_WEB_AUTH_URL;\nconst SYNKRO_WEB_AUTH_URL = (RAW_WEB_AUTH_URL && /^https?:\\/\\//.test(RAW_WEB_AUTH_URL))\n ? RAW_WEB_AUTH_URL\n : 'https://app.synkro.sh';\nconst GITHUB_PORT = 8101;\n\nfunction detectGitRepo(): { fullName: string; shortName: string } | null {\n try {\n const remoteUrl = execSync('git remote get-url origin', { encoding: 'utf-8', timeout: 5000 }).trim();\n // Match any git host — github, gitlab, bitbucket, self-hosted, etc.\n const sshMatch = remoteUrl.match(/^git@[^:]+:(.+?)(?:\\.git)?$/);\n const httpMatch = remoteUrl.match(/^https?:\\/\\/[^/]+\\/(.+?)(?:\\.git)?$/);\n const match = sshMatch || httpMatch;\n if (!match) return null;\n const fullName = match[1];\n return { fullName, shortName: fullName.split('/').pop() || fullName };\n } catch { return null; }\n}\n\nfunction ask(rl: ReturnType<typeof createInterface>, question: string): Promise<string> {\n return new Promise((resolve) => rl.question(question, resolve));\n}\n\nfunction waitForGithubToken(): Promise<string> {\n return new Promise((resolve, reject) => {\n const server = createServer((req, res) => {\n if (req.method === 'OPTIONS') {\n res.writeHead(204, {\n 'Access-Control-Allow-Origin': SYNKRO_WEB_AUTH_URL,\n 'Access-Control-Allow-Methods': 'POST, OPTIONS',\n 'Access-Control-Allow-Headers': 'Content-Type',\n });\n res.end();\n return;\n }\n\n if (req.url !== '/auth' || req.method !== 'POST') {\n res.writeHead(404);\n res.end();\n return;\n }\n\n let body = '';\n req.on('data', (chunk) => { body += chunk; });\n req.on('end', () => {\n try {\n const parsed = JSON.parse(body);\n if (!parsed.github_token) {\n res.writeHead(400, {\n 'Content-Type': 'application/json',\n 'Access-Control-Allow-Origin': SYNKRO_WEB_AUTH_URL,\n });\n res.end(JSON.stringify({ error: 'missing github_token' }));\n return;\n }\n res.writeHead(200, {\n 'Content-Type': 'application/json',\n 'Access-Control-Allow-Origin': SYNKRO_WEB_AUTH_URL,\n });\n res.end(JSON.stringify({ ok: true }));\n setTimeout(() => server.close(), 200);\n resolve(parsed.github_token);\n } catch {\n res.writeHead(400, { 'Content-Type': 'application/json' });\n res.end(JSON.stringify({ error: 'invalid json' }));\n }\n });\n });\n\n server.on('error', (err: NodeJS.ErrnoException) => {\n if (err.code === 'EADDRINUSE') {\n reject(new Error(`Port ${GITHUB_PORT} is in use. Close other processes and try again.`));\n } else {\n reject(err);\n }\n });\n\n server.listen(GITHUB_PORT);\n });\n}\n\nfunction openBrowser(url: string): void {\n const { execFile } = require('node:child_process');\n const plat = process.platform;\n const cb = (err: Error | null) => {\n if (err) console.log(` Open this URL manually: ${url}`);\n };\n if (plat === 'darwin') execFile('open', [url], cb);\n else if (plat === 'win32') execFile('cmd', ['/c', 'start', '', url], cb);\n else execFile('xdg-open', [url], cb);\n}\n\nasync function connectGithubAndSelectRepos(): Promise<Array<{ full_name: string }>> {\n const url = `${SYNKRO_WEB_AUTH_URL}/cli-github?port=${GITHUB_PORT}`;\n console.log(' Opening browser for GitHub authorization...');\n openBrowser(url);\n\n console.log(' Waiting for GitHub authorization...');\n const ghToken = await waitForGithubToken();\n console.log(' ✓ GitHub connected\\n');\n\n const repos = await listAccessibleRepos({ token: ghToken });\n if (repos.length === 0) {\n console.log(' No accessible repos found on GitHub.');\n return [];\n }\n\n console.log(` Found ${repos.length} repos:\\n`);\n repos.forEach((r: any, i: number) => {\n console.log(` ${String(i + 1).padStart(3)}. ${r.full_name}`);\n });\n console.log();\n\n const rl = createInterface({ input: process.stdin, output: process.stdout });\n try {\n const selection = await ask(rl, ' Select repos (comma-separated numbers, e.g. 1,3,5): ');\n const indices = selection\n .split(',')\n .map((s) => parseInt(s.trim(), 10) - 1)\n .filter((n) => !isNaN(n) && n >= 0 && n < repos.length);\n\n if (indices.length === 0) {\n console.log(' No repos selected.');\n return [];\n }\n\n return indices.map((i) => ({ full_name: repos[i].full_name }));\n } finally {\n rl.close();\n }\n}\n\nexport async function promptRepoConnection(opts?: { linkRepo?: boolean }): Promise<void> {\n const localRepo = detectGitRepo();\n\n if (opts?.linkRepo && localRepo) {\n console.log('Connect repos to Synkro:\\n');\n try {\n const existing = await listProjects();\n const alreadyLinked = existing.some((p: any) =>\n p.repos?.some((r: any) => r.full_name === localRepo.fullName),\n );\n if (!alreadyLinked) {\n await createProject(localRepo.shortName, [{ full_name: localRepo.fullName }]);\n console.log(` ✓ Created project \"${localRepo.shortName}\" linked to ${localRepo.fullName}`);\n } else {\n console.log(` ✓ ${localRepo.fullName} is already linked to a Synkro project.`);\n }\n } catch (err) {\n console.warn(` ⚠ Could not link repo: ${(err as Error).message}`);\n }\n console.log();\n return;\n }\n\n const rl = createInterface({ input: process.stdin, output: process.stdout });\n\n try {\n console.log('Connect repos to Synkro:\\n');\n const options: string[] = [];\n if (localRepo) {\n options.push(`Link this repo (${localRepo.fullName})`);\n }\n options.push('Connect GitHub to select repos');\n options.push('Skip for now');\n\n options.forEach((opt, i) => {\n console.log(` ${i + 1}. ${opt}`);\n });\n console.log();\n\n const choice = await ask(rl, ' Choose (number): ');\n const choiceNum = parseInt(choice.trim(), 10);\n console.log();\n rl.close();\n\n const localIdx = localRepo ? 1 : -1;\n const githubIdx = localRepo ? 2 : 1;\n const skipIdx = localRepo ? 3 : 2;\n\n if (choiceNum === localIdx && localRepo) {\n try {\n const existing = await listProjects();\n const alreadyLinked = existing.some((p: any) =>\n p.repos?.some((r: any) => r.full_name === localRepo.fullName),\n );\n if (!alreadyLinked) {\n await createProject(localRepo.shortName, [{ full_name: localRepo.fullName }]);\n console.log(` ✓ Created project \"${localRepo.shortName}\" linked to ${localRepo.fullName}`);\n } else {\n console.log(` ✓ ${localRepo.fullName} is already linked to a Synkro project.`);\n }\n } catch (err) {\n console.warn(` ⚠ Could not link repo: ${(err as Error).message}`);\n }\n } else if (choiceNum === githubIdx) {\n const selectedRepos = await connectGithubAndSelectRepos();\n if (selectedRepos.length > 0) {\n try {\n const existing = await listProjects();\n const existingFullNames = new Set(\n existing.flatMap((p: any) => (p.repos || []).map((r: any) => r.full_name)),\n );\n const newRepos = selectedRepos.filter((r) => !existingFullNames.has(r.full_name));\n\n if (newRepos.length === 0) {\n console.log(' ✓ All selected repos are already linked.');\n } else {\n const projectName = newRepos.length === 1\n ? newRepos[0].full_name.split('/').pop() || 'Project'\n : 'Multi-Repo Project';\n await createProject(projectName, newRepos);\n console.log(` ✓ Linked ${newRepos.length} repo(s) to project \"${projectName}\"`);\n }\n } catch (err) {\n console.warn(` ⚠ Could not link repos: ${(err as Error).message}`);\n }\n }\n } else if (choiceNum === skipIdx) {\n console.log(' Skipped. Run `synkro link` later to connect repos.');\n } else {\n console.log(' Invalid choice. Skipping repo connection.');\n }\n } catch {\n rl.close();\n }\n console.log();\n}\n","/**\n * synkro setup-github — interactive setup for PR scanning.\n *\n * Flow:\n * 1. Ensure user is logged in (has JWT + user_id + org_id in ~/.synkro/credentials.json).\n * 2. Check if GitHub is connected via WorkOS Pipes.\n * 3. If not, get Pipes OAuth URL and open browser for the user to authorize.\n * 4. Poll until GitHub connection is established.\n * 5. Run `claude setup-token` to get Claude Code OAuth token.\n * 6. List accessible repos via GitHub API (using Pipes token).\n * 7. Interactive multi-select.\n * 8. Push secrets (SYNKRO_API_KEY + CLAUDE_CODE_OAUTH_TOKEN) to each repo.\n * 9. Write .github/workflows/synkro.yml to the local repo if cwd is one.\n */\nimport { createInterface } from 'node:readline/promises';\nimport { stdin as input, stdout as output } from 'node:process';\nimport { execSync, spawn as nodeSpawn } from 'node:child_process';\nimport { existsSync, readFileSync, unlinkSync } from 'node:fs';\nimport { homedir, platform } from 'node:os';\nimport { join } from 'node:path';\nimport { execFile } from 'node:child_process';\nimport {\n listAccessibleRepos,\n pushSecretsToRepo,\n writeWorkflowFile,\n findGitRoot,\n SECRET_NAMES,\n WORKFLOW_RELATIVE_PATH,\n} from '../installer/githubSetup.js';\nimport { isAuthenticated, getAccessToken, getUserInfo } from '../auth/stub.js';\n\nconst SYNKRO_DIR = join(homedir(), '.synkro');\nconst CONFIG_PATH = join(SYNKRO_DIR, 'config.env');\n\nfunction readConfig(): Record<string, string> {\n if (!existsSync(CONFIG_PATH)) return {};\n const out: Record<string, string> = {};\n for (const line of readFileSync(CONFIG_PATH, 'utf-8').split('\\n')) {\n const t = line.trim();\n if (!t || t.startsWith('#')) continue;\n const eq = t.indexOf('=');\n if (eq > 0) out[t.slice(0, eq).trim()] = t.slice(eq + 1).trim().replace(/^['\"]|['\"]$/g, '');\n }\n return out;\n}\n\nasync function prompt(rl: ReturnType<typeof createInterface>, q: string, opts: { silent?: boolean } = {}): Promise<string> {\n if (opts.silent) {\n process.stdout.write(q);\n const wasRaw = (process.stdin as any).isRaw;\n if ((process.stdin as any).setRawMode) (process.stdin as any).setRawMode(true);\n return await new Promise<string>((resolve) => {\n let chunk = '';\n const onData = (data: Buffer) => {\n const s = data.toString('utf-8');\n if (s === '\\r' || s === '\\n' || s === '\\r\\n') {\n process.stdin.removeListener('data', onData);\n if ((process.stdin as any).setRawMode) (process.stdin as any).setRawMode(wasRaw ?? false);\n process.stdout.write('\\n');\n resolve(chunk);\n return;\n }\n if (s === '\u0003') process.exit(130);\n if (s === '' || s === '\\b') { chunk = chunk.slice(0, -1); return; }\n chunk += s;\n };\n process.stdin.on('data', onData);\n });\n }\n return await rl.question(q);\n}\n\nfunction openBrowser(url: string): void {\n const os = platform();\n let bin: string;\n let args: string[];\n switch (os) {\n case 'darwin': bin = 'open'; args = [url]; break;\n case 'win32': bin = 'cmd'; args = ['/c', 'start', '', url]; break;\n default: bin = 'xdg-open'; args = [url]; break;\n }\n execFile(bin, args, () => {});\n}\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise(r => setTimeout(r, ms));\n}\n\nfunction captureClaudeSetupToken(): Promise<string> {\n const tmpFile = join(SYNKRO_DIR, `token-capture-${Date.now()}.raw`);\n return new Promise((resolve, reject) => {\n const proc = nodeSpawn('script', ['-q', tmpFile, 'claude', 'setup-token'], {\n stdio: 'inherit',\n });\n proc.on('error', (err) => reject(new Error(`Failed to spawn claude setup-token: ${err.message}`)));\n proc.on('close', (code) => {\n let raw = '';\n try { raw = readFileSync(tmpFile, 'utf-8'); } catch (e) {\n reject(new Error(`Could not read script output file: ${(e as Error).message}`));\n return;\n }\n try { unlinkSync(tmpFile); } catch {}\n if (code !== 0) { reject(new Error(`claude setup-token exited with code ${code}`)); return; }\n // Grab yellow-colored text segments (token is rendered in RGB 255,193,7)\n const yellowRe = /\\x1B\\[38;2;255;193;7m([^\\x1B]*)/g;\n let yellow = '';\n let m: RegExpExecArray | null;\n while ((m = yellowRe.exec(raw)) !== null) yellow += m[1];\n const token = yellow.replace(/\\s/g, '').match(/sk-ant-oat01-[A-Za-z0-9_-]+/);\n if (!token) { reject(new Error(`Could not find token in claude setup-token output (file=${raw.length}b, yellow=${yellow.length}b)`)); return; }\n resolve(token[0]);\n });\n });\n}\n\nasync function apiCall<T = any>(gatewayUrl: string, jwt: string, path: string, opts: RequestInit = {}): Promise<T> {\n const resp = await fetch(`${gatewayUrl}${path}`, {\n ...opts,\n headers: {\n 'Authorization': `Bearer ${jwt}`,\n 'Content-Type': 'application/json',\n ...(opts.headers || {}),\n },\n });\n if (!resp.ok) {\n const text = await resp.text().catch(() => '');\n throw new Error(`API ${resp.status}: ${text.slice(0, 200)}`);\n }\n return resp.json() as Promise<T>;\n}\n\n/**\n * Connect GitHub via WorkOS Pipes OAuth. Returns the token if connected, null if user\n * declines or times out. Exported so `install` can embed this in its flow.\n */\nexport async function connectGitHub(gatewayUrl: string, jwt: string, opts: { silent?: boolean } = {}): Promise<string | null> {\n // Check if already connected\n try {\n const result = await apiCall<{ connected: boolean; token?: string }>(\n gatewayUrl, jwt, '/api/v1/cli/github-token',\n );\n if (result.connected && result.token) {\n if (!opts.silent) console.log(' ✓ GitHub already connected via Synkro.');\n return result.token;\n }\n } catch {}\n\n // Get Pipes OAuth URL and open browser\n if (!opts.silent) console.log(' Opening browser to authorize GitHub...');\n try {\n const authResp = await apiCall<{ url: string }>(\n gatewayUrl, jwt, '/api/pipes-widget/authorize/github', { method: 'POST', body: '{}' },\n );\n openBrowser(authResp.url);\n if (!opts.silent) console.log(' Waiting for authorization...');\n } catch (err) {\n if (!opts.silent) console.error(` Failed to start GitHub authorization: ${(err as Error).message}`);\n return null;\n }\n\n // Poll until connected (max 2 minutes)\n const deadline = Date.now() + 120_000;\n while (Date.now() < deadline) {\n await sleep(2000);\n try {\n const result = await apiCall<{ connected: boolean; token?: string }>(\n gatewayUrl, jwt, '/api/v1/cli/github-token',\n );\n if (result.connected && result.token) {\n if (!opts.silent) console.log('\\n ✓ GitHub connected!');\n return result.token;\n }\n } catch {}\n if (!opts.silent) process.stdout.write('.');\n }\n if (!opts.silent) console.error('\\n Timed out waiting for GitHub authorization.');\n return null;\n}\n\nexport interface SetupGithubOptions {\n nonInteractive?: boolean;\n githubToken?: string;\n skipClaudeToken?: boolean;\n}\n\nexport async function setupGithubCommand(opts: SetupGithubOptions = {}): Promise<void> {\n if (!isAuthenticated()) {\n console.error('Not authenticated. Run `synkro-cli login` first.');\n process.exit(1);\n }\n const config = readConfig();\n const gatewayUrl = (config.SYNKRO_GATEWAY_URL || process.env.SYNKRO_GATEWAY_URL || 'https://api.synkro.sh').replace(/\\/$/, '');\n const jwt = getAccessToken();\n if (!jwt) {\n console.error('Could not load access token from ~/.synkro/credentials.json. Run `synkro-cli login`.');\n process.exit(1);\n }\n\n // ── 1. Mint CI API key ──────────────────────────────────────────────\n console.log('Requesting CI API key from Synkro...');\n let synkroCiApiKey: string;\n try {\n const minted = await apiCall<{ api_key: string; expires_at: string }>(\n gatewayUrl, jwt, '/api/v1/cli/ci-api-key', { method: 'POST', body: '{}' },\n );\n synkroCiApiKey = minted.api_key;\n console.log(` ✓ Issued CI key (${synkroCiApiKey.slice(0, 18)}…), expires ${minted.expires_at.slice(0, 10)}`);\n } catch (err) {\n console.error(`Failed to mint CI API key: ${(err as Error).message}`);\n process.exit(1);\n }\n\n // ── 2. Get GitHub token via WorkOS Pipes ────────────────────────────\n let ghToken: string;\n\n if (opts.githubToken) {\n ghToken = opts.githubToken;\n } else if (opts.nonInteractive) {\n // In non-interactive mode (CI), try Pipes first, fall back to gh CLI\n try {\n const result = await apiCall<{ connected: boolean; token?: string }>(\n gatewayUrl, jwt, '/api/v1/cli/github-token',\n );\n if (result.connected && result.token) {\n ghToken = result.token;\n } else {\n throw new Error('not connected');\n }\n } catch {\n try {\n ghToken = execSync('gh auth token', { encoding: 'utf-8', timeout: 5000 }).trim();\n } catch {\n console.error('GitHub not connected. Run `synkro-cli setup-github` interactively to connect.');\n return;\n }\n }\n } else {\n // Interactive mode — use Pipes OAuth\n console.log('\\nConnecting to GitHub...');\n const token = await connectGitHub(gatewayUrl, jwt);\n if (!token) {\n console.error('GitHub connection failed. Try again.');\n process.exit(1);\n }\n ghToken = token;\n console.log();\n }\n\n // ── 3. Claude Code OAuth token ──────────────────────────────────────\n let claudeToken: string | undefined;\n if (!opts.skipClaudeToken) {\n console.log('Generating Claude Code OAuth token...');\n console.log(' A browser window will open — authorize with your Claude account.\\n');\n try {\n claudeToken = await captureClaudeSetupToken();\n } catch (err) {\n console.error(`Failed to get Claude token: ${err instanceof Error ? err.message : String(err)}`);\n if (opts.nonInteractive) return;\n process.exit(1);\n }\n if (!claudeToken.startsWith('sk-ant-oat01-')) {\n console.error('Invalid token received from `claude setup-token`. Expected sk-ant-oat01-...');\n if (opts.nonInteractive) return;\n process.exit(1);\n }\n console.log(' Validating token...');\n try {\n const validateResult = execSync(\n 'claude --print --output-format json \"say ok\"',\n { env: { ...process.env, CLAUDE_CODE_OAUTH_TOKEN: claudeToken }, encoding: 'utf-8', timeout: 30_000, stdio: ['ignore', 'pipe', 'pipe'] },\n );\n const result = JSON.parse(validateResult);\n if (result.is_error) throw new Error(result.result || 'auth failed');\n console.log(' ✓ Token validated.\\n');\n } catch (err) {\n console.error(`Token validation failed: ${err instanceof Error ? err.message : String(err)}`);\n if (opts.nonInteractive) return;\n process.exit(1);\n }\n }\n\n // ── 4. Select repos ─────────────────────────────────────────────────\n let selected: Array<{ owner: string; repo: string; full_name: string }>;\n if (opts.nonInteractive) {\n let currentFullName: string | null = null;\n try {\n const remoteUrl = execSync('git remote get-url origin', { encoding: 'utf-8', timeout: 5000 }).trim();\n const m = remoteUrl.match(/(?:github\\.com)[:/](.+?)(?:\\.git)?$/);\n if (m) currentFullName = m[1];\n } catch {}\n if (!currentFullName) {\n console.warn(' ⚠ Not in a GitHub repo. Skipping PR scan setup.');\n return;\n }\n const [owner, repo] = currentFullName.split('/');\n selected = [{ owner, repo, full_name: currentFullName }];\n console.log(` Auto-selected repo: ${currentFullName}`);\n } else {\n console.log('Fetching accessible repos...');\n const repos = await listAccessibleRepos({ token: ghToken! });\n if (repos.length === 0) {\n console.error('No accessible repos found. Check your GitHub permissions.');\n process.exit(1);\n }\n console.log(`\\nFound ${repos.length} accessible repo(s):\\n`);\n repos.slice(0, 100).forEach((r, i) => {\n console.log(` ${String(i + 1).padStart(3)}. ${r.full_name}`);\n });\n console.log();\n const rl2 = createInterface({ input, output });\n const selectionRaw = await prompt(rl2, 'Select repos to enable (comma-separated numbers, e.g. 1,3,5): ');\n const selectedIdx = selectionRaw\n .split(',')\n .map((s) => parseInt(s.trim(), 10) - 1)\n .filter((n) => !isNaN(n) && n >= 0 && n < repos.length);\n if (selectedIdx.length === 0) {\n console.error('No valid selections.');\n rl2.close();\n process.exit(1);\n }\n selected = selectedIdx.map((i) => repos[i]);\n console.log(`\\nWill push secrets to ${selected.length} repo(s):`);\n for (const r of selected) console.log(` • ${r.full_name}`);\n console.log();\n const confirm = (await prompt(rl2, 'Continue? (yes/no): ')).trim().toLowerCase();\n if (confirm !== 'yes' && confirm !== 'y') {\n console.log('Cancelled.');\n rl2.close();\n process.exit(0);\n }\n rl2.close();\n }\n\n // ── 5. Push secrets ─────────────────────────────────────────────────\n console.log();\n for (const r of selected) {\n process.stdout.write(`Pushing secrets to ${r.full_name}... `);\n try {\n await pushSecretsToRepo(\n { token: ghToken! },\n r.owner,\n r.repo,\n {\n claudeCodeOauthToken: claudeToken,\n synkroApiKey: synkroCiApiKey,\n },\n );\n console.log('✓');\n } catch (err) {\n console.log(`✗ (${(err as Error).message})`);\n }\n }\n\n // ── 6. Write workflow file ──────────────────────────────────────────\n console.log();\n const gitRoot = findGitRoot(process.cwd());\n if (gitRoot) {\n const written = writeWorkflowFile(gitRoot);\n if (written) {\n console.log(`Wrote workflow: ${written}`);\n console.log('Commit and push it to enable PR scanning.');\n }\n } else {\n console.log('Not in a git repo. To enable scanning, add this file to your repo:');\n console.log(` Path: ${WORKFLOW_RELATIVE_PATH}`);\n console.log(` Content: run \\`synkro-cli setup-github\\` from inside a repo to write it automatically`);\n }\n\n console.log();\n console.log('✓ PR scan setup complete.');\n console.log(`Secrets pushed: ${SECRET_NAMES.CLAUDE_OAUTH}, ${SECRET_NAMES.SYNKRO_API_KEY}`);\n console.log('Open a PR on any selected repo to trigger your first Synkro scan.');\n}\n","/**\n * Fetch judge prompt metadata from the Synkro API.\n * Prompts are fetched live on every grade call — never cached to disk.\n * This module only fetches the version string for display during install.\n */\n\ninterface PromptsMetaResponse {\n version: string;\n}\n\nexport async function fetchJudgePrompts(opts: {\n gatewayUrl: string;\n jwt: string;\n forceRefresh?: boolean;\n}): Promise<{\n version: string;\n}> {\n const url = `${opts.gatewayUrl.replace(/\\/$/, '')}/api/v1/hook/config`;\n const resp = await fetch(url, {\n method: 'GET',\n headers: {\n 'Authorization': `Bearer ${opts.jwt}`,\n 'User-Agent': 'synkro-cli/1.0',\n },\n signal: AbortSignal.timeout(5000),\n });\n\n if (!resp.ok) {\n return { version: 'unknown' };\n }\n\n const data = await resp.json() as { prompts?: { version?: string } };\n return { version: data.prompts?.version ?? 'unknown' };\n}\n","/**\n * Inference provider detection.\n *\n * The source of truth is the user's inference_settings in TimescaleDB.\n * At install time, `/api/v1/cli/me` returns `local_inference: true` when\n * `gradingProvider = 'claude-code'`. The install flow writes\n * `SYNKRO_LOCAL_INFERENCE='yes'` into ~/.synkro/config.env.\n *\n * At runtime the hook scripts use a TCP probe (synkro_channel_up) to decide\n * routing — this module is only used by CLI commands and intent routers.\n */\n\nimport { existsSync, readFileSync } from 'node:fs';\nimport { homedir } from 'node:os';\nimport { join } from 'node:path';\n\nconst CONFIG_PATH = join(homedir(), '.synkro', 'config.env');\n\nexport function isLocalCCEnabled(): boolean {\n if (!existsSync(CONFIG_PATH)) return false;\n try {\n const content = readFileSync(CONFIG_PATH, 'utf-8');\n const match = content.match(/^SYNKRO_LOCAL_INFERENCE='([^']*)'/m);\n return match?.[1] === 'yes';\n } catch {\n return false;\n }\n}\n","/**\n * Synkro local-CC channel plugin source.\n *\n * Embedded as a string so `synkro install` can drop it on disk at\n * `~/.synkro/cc_sessions/synkro-channel.ts` without bundling extra files.\n *\n * The plugin:\n * - Registers as an MCP channel (`claude/channel` capability) so events\n * pushed via `mcp.notification()` arrive in Claude's context as\n * `<channel source=\"synkro-local\" req_id=\"...\" role=\"...\">...</channel>` tags.\n * - Listens on TCP at 127.0.0.1:SYNKRO_CHANNEL_PORT (default 8929) — pinned\n * to loopback so nothing leaves the host.\n * - Exposes a `reply` MCP tool. When Claude calls it with a `req_id` and\n * `result`, the plugin matches the open request and returns the result\n * to the HTTP caller.\n *\n * Run by Claude Code as a subprocess (stdio MCP transport). Started indirectly\n * by `claude --dangerously-load-development-channels server:synkro-local`,\n * which the pueue task wraps.\n */\n\nexport const SYNKRO_CHANNEL_DEFAULT_PORT = 8929;\n\nexport const CHANNEL_PLUGIN_SOURCE = `#!/usr/bin/env bun\n/**\n * Synkro local-CC channel plugin (auto-generated by \\`synkro install\\`).\n * DO NOT EDIT — your changes will be overwritten on next install.\n */\nimport { Server } from '@modelcontextprotocol/sdk/server/index.js';\nimport { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';\nimport { ListToolsRequestSchema, CallToolRequestSchema } from '@modelcontextprotocol/sdk/types.js';\n\nconst PORT = parseInt(process.env.SYNKRO_CHANNEL_PORT || '8929', 10);\nconst HOSTNAME = '127.0.0.1';\n\nconst REQUEST_TIMEOUT_MS = parseInt(process.env.SYNKRO_CHANNEL_TIMEOUT_MS || '120000', 10);\n\ninterface PendingRequest {\n resolve: (result: string) => void;\n reject: (err: Error) => void;\n timer: ReturnType<typeof setTimeout>;\n}\n\nconst pending = new Map<string, PendingRequest>();\nlet nextRequestId = 1;\n\nconst mcp = new Server(\n { name: 'synkro-local', version: '0.1.0' },\n {\n capabilities: {\n experimental: { 'claude/channel': {} },\n tools: {},\n },\n instructions: [\n 'Synkro local inference channel.',\n 'Each <channel source=\"synkro-local\" req_id=\"...\" role=\"...\"> event contains a',\n 'self-contained instruction block followed by the payload to evaluate. Treat it',\n 'as a fresh isolated request — IGNORE any prior conversation turns or context.',\n 'Do not call Read, Edit, Write, Bash, or any other tool. Do exactly one thing:',\n 'parse the request, produce the structured response described inside it, then',\n 'call the \\\\\\`reply\\\\\\` tool exactly once with the same req_id and the response',\n 'wrapped as the \\\\\\`result\\\\\\` argument (a string). Output no other text.',\n ].join(' '),\n },\n);\n\nmcp.setRequestHandler(ListToolsRequestSchema, async () => ({\n tools: [{\n name: 'reply',\n description: 'Return the response for a Synkro local-inference request',\n inputSchema: {\n type: 'object',\n properties: {\n req_id: { type: 'string', description: 'The req_id from the channel event being answered' },\n result: { type: 'string', description: 'The response text. For grading/classification roles, the JSON-tagged verdict the role requested.' },\n },\n required: ['req_id', 'result'],\n },\n }],\n}));\n\nmcp.setRequestHandler(CallToolRequestSchema, async req => {\n if (req.params.name !== 'reply') {\n throw new Error('unknown tool: ' + req.params.name);\n }\n const args = req.params.arguments as { req_id?: string; result?: string };\n const reqId = String(args.req_id ?? '');\n const result = String(args.result ?? '');\n const p = pending.get(reqId);\n if (p) {\n clearTimeout(p.timer);\n pending.delete(reqId);\n p.resolve(result);\n return { content: [{ type: 'text', text: 'ok' }] };\n }\n return { content: [{ type: 'text', text: 'unknown req_id (likely already timed out)' }] };\n});\n\n// Bind the listener BEFORE awaiting mcp.connect — Bun.serve is synchronous\n// and must run on the script's first tick, otherwise the stdio transport's\n// read loop can starve the serve setup.\nBun.serve({\n port: PORT,\n hostname: HOSTNAME,\n idleTimeout: 0,\n async fetch(req) {\n const url = new URL(req.url);\n if (url.pathname === '/healthz') {\n return new Response(JSON.stringify({ ok: true, pending: pending.size }), {\n headers: { 'Content-Type': 'application/json' },\n });\n }\n if (req.method !== 'POST' || url.pathname !== '/submit') {\n return new Response('not found', { status: 404 });\n }\n let body: { role?: string; content?: string };\n try {\n body = await req.json() as typeof body;\n } catch {\n return new Response('invalid json', { status: 400 });\n }\n const role = String(body.role ?? '');\n const content = String(body.content ?? '');\n if (!role || !content) {\n return new Response('missing role/content', { status: 400 });\n }\n const reqId = 'r' + (nextRequestId++) + Date.now().toString(36);\n const result = await new Promise<string>((resolve, reject) => {\n const timer = setTimeout(() => {\n pending.delete(reqId);\n reject(new Error('timeout waiting for reply (' + REQUEST_TIMEOUT_MS + 'ms)'));\n }, REQUEST_TIMEOUT_MS);\n pending.set(reqId, { resolve, reject, timer });\n mcp.notification({\n method: 'notifications/claude/channel',\n params: {\n content,\n meta: { req_id: reqId, role },\n },\n }).catch(err => {\n clearTimeout(timer);\n pending.delete(reqId);\n reject(err instanceof Error ? err : new Error(String(err)));\n });\n }).catch(err => {\n return JSON.stringify({ error: err instanceof Error ? err.message : String(err) });\n });\n if (typeof result === 'string' && result.startsWith('{\"error\":')) {\n return new Response(result, { status: 504, headers: { 'Content-Type': 'application/json' } });\n }\n return new Response(JSON.stringify({ result }), {\n headers: { 'Content-Type': 'application/json' },\n });\n },\n});\n\nprocess.on('SIGTERM', () => process.exit(0));\nprocess.on('SIGINT', () => process.exit(0));\n\n// MCP stdio handshake last. The transport's read loop keeps the process\n// alive; the TCP listener is already bound at this point so the CLI can\n// hit it as soon as Claude finishes its end of the handshake.\nawait mcp.connect(new StdioServerTransport());\n`;\n","/**\n * Filesystem setup for the local-CC channel plugin.\n *\n * Idempotent: safe to call multiple times. Writes:\n * ~/.synkro/cc_sessions/synkro-channel.ts (the Bun MCP plugin)\n * ~/.synkro/cc_sessions/package.json (deps for the plugin)\n * ~/.synkro/cc_sessions/.claude/settings.json (fastMode:true scoped to this cwd)\n *\n * Then patches ~/.claude.json to register the plugin under mcpServers, and runs\n * `bun install` in the session dir so @modelcontextprotocol/sdk is on disk.\n */\n\nimport { existsSync, mkdirSync, writeFileSync, readFileSync, chmodSync, copyFileSync, renameSync, unlinkSync, openSync, fsyncSync, closeSync } from 'node:fs';\nimport { join } from 'node:path';\nimport { homedir } from 'node:os';\nimport { spawnSync } from 'node:child_process';\nimport { CHANNEL_PLUGIN_SOURCE } from './channelSource.js';\n\nexport const CLAUDE_JSON_BACKUP_PATH = join(homedir(), '.claude.json.synkro-bak');\n\nexport const SESSION_DIR = join(homedir(), '.synkro', 'cc_sessions');\nexport const PLUGIN_PATH = join(SESSION_DIR, 'synkro-channel.ts');\nexport const PLUGIN_PKG_PATH = join(SESSION_DIR, 'package.json');\nexport const PLUGIN_SETTINGS_DIR = join(SESSION_DIR, '.claude');\nexport const PLUGIN_SETTINGS_PATH = join(PLUGIN_SETTINGS_DIR, 'settings.json');\nexport const PROJECT_MCP_PATH = join(SESSION_DIR, '.mcp.json');\nexport const CLAUDE_JSON_PATH = join(homedir(), '.claude.json');\nexport const RUN_SCRIPT_PATH = join(SESSION_DIR, 'run-claude.sh');\nexport const TMUX_SESSION_NAME = 'synkro-local-cc';\n\nexport const SESSION_DIR_2 = join(homedir(), '.synkro', 'cc_sessions_2');\nexport const PLUGIN_PATH_2 = join(SESSION_DIR_2, 'synkro-channel.ts');\nexport const PLUGIN_PKG_PATH_2 = join(SESSION_DIR_2, 'package.json');\nexport const PLUGIN_SETTINGS_DIR_2 = join(SESSION_DIR_2, '.claude');\nexport const PLUGIN_SETTINGS_PATH_2 = join(PLUGIN_SETTINGS_DIR_2, 'settings.json');\nexport const PROJECT_MCP_PATH_2 = join(SESSION_DIR_2, '.mcp.json');\nexport const RUN_SCRIPT_PATH_2 = join(SESSION_DIR_2, 'run-claude.sh');\nexport const TMUX_SESSION_NAME_2 = 'synkro-local-cc-2';\nexport const CHANNEL_2_PORT = 8930;\n\n/**\n * Bash wrapper that owns the tmux session hosting `claude`. Pueue runs this\n * script as its task. The script:\n * 1. Kills any prior tmux session by the same name (idempotent restart).\n * 2. Starts a detached tmux session running claude. tmux gives claude a\n * real pty, so it stays in interactive mode (vs. dropping into --print).\n * 3. Blocks on tmux's lifetime so pueue's task duration mirrors claude's.\n * `synkro local-cc stop` kills the tmux session, which unblocks the\n * poll and lets pueue mark the task done.\n *\n * Dismissal of startup dialogs (workspace trust / dev-channels /\n * MCP consent) is owned by the TS-side waitForChannelReady, which polls\n * the channel TCP port and injects \\`tmux send-keys ... Enter\\` each tick\n * the probe fails. That's adaptive (no fixed window) and self-terminating\n * (stops the moment the port binds).\n */\nconst RUN_SCRIPT_SOURCE = `#!/usr/bin/env bash\n# Auto-generated by \\`synkro install\\`. Do not edit.\nset -uo pipefail\n\nSESSION=${TMUX_SESSION_NAME}\nLOG=\"$HOME/.synkro/cc_sessions/run-claude.log\"\n\nlog() { echo \"[$(date '+%H:%M:%S')] $*\" >> \"$LOG\"; echo \"$*\"; }\n\n# Pre-flight checks\nif ! command -v claude >/dev/null 2>&1; then\n log \"ERROR: claude CLI not found on PATH. Install Claude Code first.\"\n exit 1\nfi\n\nif ! command -v tmux >/dev/null 2>&1; then\n log \"ERROR: tmux not found on PATH.\"\n exit 1\nfi\n\n# Check claude is authenticated\nif ! claude --version >/dev/null 2>&1; then\n log \"ERROR: claude --version failed. Is Claude Code installed correctly?\"\n exit 1\nfi\n\nlog \"Starting local-CC session...\"\nlog \"claude version: $(claude --version 2>&1 | head -1)\"\n\n# Kill any previous session so restarts come up clean.\ntmux kill-session -t \"=$SESSION\" 2>/dev/null || true\n\n# Start claude inside a detached tmux session so it has a real pty.\n# Redirect stderr to the log so we can see why it dies.\ntmux new-session -d -s \"$SESSION\" \\\\\n \"claude --dangerously-load-development-channels server:synkro-local --dangerously-skip-permissions --setting-sources project,local --model claude-sonnet-4-6 2>>$LOG; echo 'claude exited with code '\\$'?' >> $LOG\"\n\n# Claude's --dangerously-load-development-channels shows a confirmation\n# prompt: option 1 = \"I am using this for local development\" (accept),\n# option 2 = \"Exit\". Auto-accept by sending '1' + Enter.\nsleep 3\nif tmux has-session -t \"=$SESSION\" 2>/dev/null; then\n tmux send-keys -t \"$SESSION\" '1' 2>/dev/null || true\n sleep 1\n tmux send-keys -t \"$SESSION\" Enter 2>/dev/null || true\n sleep 1\n # Additional Enter for any follow-up prompts (workspace trust, MCP consent)\n tmux send-keys -t \"$SESSION\" Enter 2>/dev/null || true\n log \"Sent auto-accept keys to claude session.\"\nfi\n\nsleep 2\nif ! tmux has-session -t \"$SESSION\" 2>/dev/null; then\n log \"ERROR: tmux session died immediately. Check $LOG for details.\"\n log \"Try running claude manually to verify auth: claude --print 'say ok'\"\n exit 1\nfi\n\nlog \"tmux session started successfully.\"\n\n# Block on the tmux session so pueue's task lifetime tracks claude's.\nwhile tmux has-session -t \"=$SESSION\" 2>/dev/null; do\n sleep 5\ndone\n\nlog \"tmux session ended.\"\n`;\n\nconst RUN_SCRIPT_SOURCE_2 = `#!/usr/bin/env bash\n# Auto-generated by \\`synkro install\\`. Channel 2 (CWE scan, port ${CHANNEL_2_PORT}).\nset -uo pipefail\n\nSESSION=${TMUX_SESSION_NAME_2}\nLOG=\"$HOME/.synkro/cc_sessions_2/run-claude.log\"\n\nlog() { echo \"[$(date '+%H:%M:%S')] $*\" >> \"$LOG\"; echo \"$*\"; }\n\nif ! command -v claude >/dev/null 2>&1; then\n log \"ERROR: claude CLI not found on PATH.\"\n exit 1\nfi\n\nif ! command -v tmux >/dev/null 2>&1; then\n log \"ERROR: tmux not found on PATH.\"\n exit 1\nfi\n\nif ! claude --version >/dev/null 2>&1; then\n log \"ERROR: claude --version failed.\"\n exit 1\nfi\n\nlog \"Starting local-CC channel 2 (port ${CHANNEL_2_PORT})...\"\nlog \"claude version: $(claude --version 2>&1 | head -1)\"\n\ntmux kill-session -t \"=$SESSION\" 2>/dev/null || true\n\ntmux new-session -d -s \"$SESSION\" \\\\\n \"SYNKRO_CHANNEL_PORT=${CHANNEL_2_PORT} claude --dangerously-load-development-channels server:synkro-local --dangerously-skip-permissions --setting-sources project,local --model claude-sonnet-4-6 2>>$LOG; echo 'claude exited with code '\\$'?' >> $LOG\"\n\nsleep 3\nif tmux has-session -t \"=$SESSION\" 2>/dev/null; then\n tmux send-keys -t \"$SESSION\" '1' 2>/dev/null || true\n sleep 1\n tmux send-keys -t \"$SESSION\" Enter 2>/dev/null || true\n sleep 1\n tmux send-keys -t \"$SESSION\" Enter 2>/dev/null || true\n log \"Sent auto-accept keys to channel 2 session.\"\nfi\n\nsleep 2\nif ! tmux has-session -t \"$SESSION\" 2>/dev/null; then\n log \"ERROR: tmux session died immediately. Check $LOG for details.\"\n exit 1\nfi\n\nlog \"tmux session started successfully (port ${CHANNEL_2_PORT}).\"\n\nwhile tmux has-session -t \"=$SESSION\" 2>/dev/null; do\n sleep 5\ndone\n\nlog \"tmux session ended.\"\n`;\n\nconst MCP_SERVER_NAME = 'synkro-local';\n\nconst PLUGIN_PACKAGE_JSON = JSON.stringify(\n {\n name: 'synkro-local-channel',\n private: true,\n version: '0.1.0',\n type: 'module',\n dependencies: {\n '@modelcontextprotocol/sdk': '^1.0.0',\n },\n },\n null,\n 2,\n) + '\\n';\n\nexport class LocalCCInstallError extends Error {\n constructor(message: string, public readonly cause?: unknown) {\n super(message);\n this.name = 'LocalCCInstallError';\n }\n}\n\nfunction writePluginFiles(): void {\n mkdirSync(SESSION_DIR, { recursive: true });\n mkdirSync(PLUGIN_SETTINGS_DIR, { recursive: true });\n writeFileSync(PLUGIN_PATH, CHANNEL_PLUGIN_SOURCE, 'utf-8');\n chmodSync(PLUGIN_PATH, 0o755);\n writeFileSync(PLUGIN_PKG_PATH, PLUGIN_PACKAGE_JSON, 'utf-8');\n writeFileSync(\n PLUGIN_SETTINGS_PATH,\n JSON.stringify({\n fastMode: true,\n enabledMcpjsonServers: ['synkro-local'],\n }, null, 2) + '\\n',\n 'utf-8',\n );\n writeFileSync(RUN_SCRIPT_PATH, RUN_SCRIPT_SOURCE, 'utf-8');\n chmodSync(RUN_SCRIPT_PATH, 0o755);\n\n // Channel 2 — identical plugin, different port + tmux session\n mkdirSync(SESSION_DIR_2, { recursive: true });\n mkdirSync(PLUGIN_SETTINGS_DIR_2, { recursive: true });\n writeFileSync(PLUGIN_PATH_2, CHANNEL_PLUGIN_SOURCE, 'utf-8');\n chmodSync(PLUGIN_PATH_2, 0o755);\n writeFileSync(PLUGIN_PKG_PATH_2, PLUGIN_PACKAGE_JSON, 'utf-8');\n writeFileSync(\n PLUGIN_SETTINGS_PATH_2,\n JSON.stringify({\n fastMode: true,\n enabledMcpjsonServers: ['synkro-local'],\n }, null, 2) + '\\n',\n 'utf-8',\n );\n writeFileSync(RUN_SCRIPT_PATH_2, RUN_SCRIPT_SOURCE_2, 'utf-8');\n chmodSync(RUN_SCRIPT_PATH_2, 0o755);\n}\n\nfunction runBunInstall(): void {\n for (const dir of [SESSION_DIR, SESSION_DIR_2]) {\n const r = spawnSync('bun', ['install', '--silent'], {\n cwd: dir,\n encoding: 'utf-8',\n timeout: 120_000,\n });\n if (r.status !== 0) {\n throw new LocalCCInstallError(\n `bun install failed in ${dir}: ${r.stderr || r.stdout || 'unknown'}`,\n );\n }\n }\n}\n\ninterface ClaudeJson {\n mcpServers?: Record<string, { command: string; args?: string[]; env?: Record<string, string> }>;\n projects?: Record<string, Record<string, unknown>>;\n [k: string]: unknown;\n}\n\n/**\n * Safely apply `mutator` to ~/.claude.json with five layers of protection:\n * 1. Refuse to operate on malformed input — never try to \"fix\" a corrupted file.\n * 2. Take a rolling backup at ~/.claude.json.synkro-bak before any write.\n * 3. Validate the post-mutation result is still well-formed JSON and didn't\n * lose any top-level keys that existed in the original.\n * 4. Atomic write via tmp + fsync + rename(2) (POSIX-atomic).\n * 5. Restore from backup on any error in the write phase.\n *\n * If the file doesn't exist (no claude install on this machine), the mutation\n * is silently skipped — we don't create the file, since claude owns it.\n */\nfunction safelyMutateClaudeJson(mutator: (json: ClaudeJson) => boolean): void {\n if (!existsSync(CLAUDE_JSON_PATH)) {\n // claude.json doesn't exist; nothing to mutate. We don't create it.\n return;\n }\n\n // (1) Read + parse, refuse to proceed on malformed input.\n const originalText = readFileSync(CLAUDE_JSON_PATH, 'utf-8');\n let parsed: ClaudeJson;\n try {\n parsed = JSON.parse(originalText) as ClaudeJson;\n } catch (err) {\n throw new LocalCCInstallError(\n `refusing to modify malformed ${CLAUDE_JSON_PATH}: ${(err as Error).message}. ` +\n `Please fix the JSON manually before retrying.`,\n err,\n );\n }\n\n const originalKeys = new Set(Object.keys(parsed));\n\n // (Mutator returns true if it changed anything; false means no-op.)\n const dirty = mutator(parsed);\n if (!dirty) return;\n\n // (3a) Validate the in-memory mutation didn't drop a top-level key.\n for (const k of originalKeys) {\n if (!(k in parsed)) {\n throw new LocalCCInstallError(\n `refusing to write ${CLAUDE_JSON_PATH}: mutator dropped top-level key \"${k}\". ` +\n `This is a bug — please report.`,\n );\n }\n }\n\n const newText = JSON.stringify(parsed, null, 2) + '\\n';\n\n // (3b) Round-trip parse to guarantee well-formed output before any write.\n try {\n JSON.parse(newText);\n } catch (err) {\n throw new LocalCCInstallError(\n `refusing to write ${CLAUDE_JSON_PATH}: serialized result is not valid JSON. ` +\n `This is a bug — please report.`,\n err,\n );\n }\n\n // (2) Take a rolling backup of the *original* (pre-mutation) bytes.\n copyFileSync(CLAUDE_JSON_PATH, CLAUDE_JSON_BACKUP_PATH);\n\n // (4) Atomic write: tmp + fsync + rename. This guarantees a reader\n // never sees a half-written file — they see either the old or new.\n const tmpPath = `${CLAUDE_JSON_PATH}.synkro-tmp.${process.pid}`;\n try {\n writeFileSync(tmpPath, newText, 'utf-8');\n // fsync the tmp file so the bytes are durably on disk before rename.\n const fd = openSync(tmpPath, 'r');\n try { fsyncSync(fd); } finally { closeSync(fd); }\n renameSync(tmpPath, CLAUDE_JSON_PATH);\n } catch (err) {\n // (5) Best-effort recovery: restore the original from backup.\n try { unlinkSync(tmpPath); } catch { /* ignore */ }\n try { copyFileSync(CLAUDE_JSON_BACKUP_PATH, CLAUDE_JSON_PATH); } catch { /* ignore */ }\n throw new LocalCCInstallError(\n `failed to write ${CLAUDE_JSON_PATH}: ${(err as Error).message}. ` +\n `Backup at ${CLAUDE_JSON_BACKUP_PATH} preserves the prior state.`,\n err,\n );\n }\n}\n\n/**\n * Register the synkro-local MCP server at PROJECT scope (.mcp.json inside\n * ~/.synkro/cc_sessions) — NOT at user scope in ~/.claude.json. User scope\n * would make every claude session on the machine spawn the plugin and race\n * to bind the UDS, which corrupts request routing.\n */\nfunction writeProjectMcpJson(): void {\n const mcp = {\n mcpServers: {\n [MCP_SERVER_NAME]: {\n command: 'bun',\n args: [PLUGIN_PATH],\n },\n },\n };\n writeFileSync(PROJECT_MCP_PATH, JSON.stringify(mcp, null, 2) + '\\n', 'utf-8');\n\n // Channel 2 uses the same plugin source but on a different port\n const mcp2 = {\n mcpServers: {\n [MCP_SERVER_NAME]: {\n command: 'bun',\n args: [PLUGIN_PATH_2],\n },\n },\n };\n writeFileSync(PROJECT_MCP_PATH_2, JSON.stringify(mcp2, null, 2) + '\\n', 'utf-8');\n}\n\n/**\n * Pre-accept the workspace trust dialog and approve the project-scoped MCP\n * server in ~/.claude.json so claude doesn't block on those prompts under\n * tmux. Also strips any stale top-level mcpServers[\"synkro-local\"] left over\n * from previous (incorrect) installs.\n *\n * All mutations go through safelyMutateClaudeJson — see that function for\n * the integrity guarantees.\n */\nfunction patchClaudeJson(): void {\n safelyMutateClaudeJson(parsed => {\n let dirty = false;\n\n // Remove any stale user-scope registration from previous installs.\n if (parsed.mcpServers && typeof parsed.mcpServers === 'object' && parsed.mcpServers[MCP_SERVER_NAME]) {\n delete parsed.mcpServers[MCP_SERVER_NAME];\n dirty = true;\n }\n\n if (!parsed.projects || typeof parsed.projects !== 'object') {\n parsed.projects = {};\n }\n const projects = parsed.projects as Record<string, Record<string, unknown>>;\n\n for (const dir of [SESSION_DIR, SESSION_DIR_2]) {\n const existing = projects[dir] && typeof projects[dir] === 'object'\n ? projects[dir]\n : {};\n const wantEnabled = Array.from(new Set([\n ...((existing.enabledMcpjsonServers as string[] | undefined) ?? []),\n MCP_SERVER_NAME,\n ]));\n const next = {\n ...existing,\n hasTrustDialogAccepted: true,\n hasCompletedProjectOnboarding: true,\n enabledMcpjsonServers: wantEnabled,\n };\n if (\n existing.hasTrustDialogAccepted !== true ||\n existing.hasCompletedProjectOnboarding !== true ||\n JSON.stringify(existing.enabledMcpjsonServers ?? []) !== JSON.stringify(wantEnabled)\n ) {\n projects[dir] = next;\n dirty = true;\n }\n }\n return dirty;\n });\n}\n\n/**\n * Run the full local-CC install. Throws LocalCCInstallError on any failure\n * the caller is expected to surface. Does NOT start the pueue task — call\n * `pueue.ensureRunning()` after for that.\n */\nexport function installLocalCC(): { sessionDir: string; pluginPath: string } {\n // bun is required for the plugin runtime — auto-install if missing.\n let bunCheck = spawnSync('bun', ['--version'], { encoding: 'utf-8' });\n if (bunCheck.status !== 0) {\n if (process.platform === 'darwin') {\n console.log(' Installing bun via brew...');\n const brewR = spawnSync('brew', ['install', 'oven-sh/bun/bun'], { encoding: 'utf-8', stdio: 'inherit', timeout: 120_000 });\n if (brewR.status !== 0) {\n throw new LocalCCInstallError('bun auto-install failed. Install manually: curl -fsSL https://bun.sh/install | bash');\n }\n bunCheck = spawnSync('bun', ['--version'], { encoding: 'utf-8' });\n if (bunCheck.status !== 0) {\n throw new LocalCCInstallError('bun installed but not found on PATH. Restart your terminal and re-run install.');\n }\n } else {\n throw new LocalCCInstallError('bun is required. Install it: curl -fsSL https://bun.sh/install | bash');\n }\n }\n\n writePluginFiles();\n runBunInstall();\n writeProjectMcpJson();\n patchClaudeJson();\n\n return { sessionDir: SESSION_DIR, pluginPath: PLUGIN_PATH };\n}\n\n/**\n * Strip every trace of local-cc from ~/.claude.json:\n * - user-scope mcpServers[\"synkro-local\"] (legacy from older installs)\n * - projects[SESSION_DIR] entry (workspace trust + enabledMcpjsonServers we added)\n *\n * Goes through safelyMutateClaudeJson for the same integrity guarantees as\n * patchClaudeJson — atomic write, backup, post-write validation. Errors are\n * surfaced so the caller sees them, not silently swallowed.\n *\n * Plugin files in ~/.synkro/cc_sessions/ are removed by the caller via\n * `rm -rf ~/.synkro` when uninstalling with --purge.\n */\nexport function uninstallLocalCC(): void {\n safelyMutateClaudeJson(parsed => {\n let dirty = false;\n if (parsed.mcpServers && parsed.mcpServers[MCP_SERVER_NAME]) {\n delete parsed.mcpServers[MCP_SERVER_NAME];\n dirty = true;\n }\n for (const dir of [SESSION_DIR, SESSION_DIR_2]) {\n if (parsed.projects && typeof parsed.projects === 'object' && parsed.projects[dir]) {\n delete parsed.projects[dir];\n dirty = true;\n }\n }\n return dirty;\n });\n}\n","/**\n * Pueue lifecycle for the local-CC `claude` process.\n *\n * One labelled pueue task per user, started at install (or on demand) and\n * managed via the `pueue` CLI. We assume `pueue` and `pueued` are installed\n * and the daemon is running — surface a friendly error otherwise.\n */\n\nimport { execFileSync, spawnSync, spawn } from 'node:child_process';\nimport { homedir } from 'node:os';\nimport { join } from 'node:path';\nimport { connect } from 'node:net';\n\nexport const TASK_LABEL = 'synkro-local-cc';\nexport const TMUX_SESSION = 'synkro-local-cc';\nexport const SESSION_DIR = join(homedir(), '.synkro', 'cc_sessions');\n\nexport const TASK_LABEL_2 = 'synkro-local-cc-2';\nexport const TMUX_SESSION_2 = 'synkro-local-cc-2';\nexport const SESSION_DIR_2 = join(homedir(), '.synkro', 'cc_sessions_2');\n\nexport class PueueError extends Error {\n constructor(message: string, public readonly cause?: unknown) {\n super(message);\n this.name = 'PueueError';\n }\n}\n\ninterface PueueStatusEntry {\n id: number;\n label?: string;\n status: string | { Running?: unknown; Done?: { result?: string } };\n command: string;\n path: string;\n}\n\ninterface PueueStatus {\n tasks: Record<string, PueueStatusEntry>;\n}\n\nfunction pueueAvailable(): void {\n const r = spawnSync('pueue', ['--version'], { encoding: 'utf-8' });\n if (r.status !== 0) {\n throw new PueueError('pueue CLI not found on PATH. Install pueue (https://github.com/Nukesor/pueue) and start `pueued`.');\n }\n}\n\nfunction statusJson(): PueueStatus {\n pueueAvailable();\n const r = spawnSync('pueue', ['status', '--json'], { encoding: 'utf-8' });\n if (r.status !== 0) {\n throw new PueueError(`pueue status failed: ${r.stderr || r.stdout || 'unknown error'} — is pueued running?`);\n }\n try {\n return JSON.parse(r.stdout) as PueueStatus;\n } catch (err) {\n throw new PueueError(`pueue status returned non-JSON output: ${r.stdout.slice(0, 200)}`, err);\n }\n}\n\nexport interface TaskInfo {\n id: number;\n label: string;\n status: string;\n command: string;\n cwd: string;\n}\n\nfunction statusName(s: PueueStatusEntry['status']): string {\n if (typeof s === 'string') return s;\n if (s && typeof s === 'object') {\n if ('Running' in s) return 'Running';\n if ('Done' in s) {\n const result = (s as { Done?: { result?: string | object } }).Done?.result;\n if (typeof result === 'string') return `Done (${result})`;\n if (result && typeof result === 'object') return `Done (${Object.keys(result)[0] ?? 'unknown'})`;\n return 'Done';\n }\n return Object.keys(s)[0] ?? 'unknown';\n }\n return 'unknown';\n}\n\nexport interface ChannelIdentity {\n taskLabel: string;\n tmuxSession: string;\n sessionDir: string;\n}\n\nexport const CHANNEL_PRIMARY: ChannelIdentity = { taskLabel: TASK_LABEL, tmuxSession: TMUX_SESSION, sessionDir: SESSION_DIR };\nexport const CHANNEL_SECONDARY: ChannelIdentity = { taskLabel: TASK_LABEL_2, tmuxSession: TMUX_SESSION_2, sessionDir: SESSION_DIR_2 };\n\n/** Find a synkro local-cc task by label. Returns null if absent. */\nexport function findTask(channel: ChannelIdentity = CHANNEL_PRIMARY): TaskInfo | null {\n const data = statusJson();\n for (const [id, t] of Object.entries(data.tasks)) {\n if (t.label === channel.taskLabel) {\n return {\n id: Number(id),\n label: t.label,\n status: statusName(t.status),\n command: t.command,\n cwd: t.path,\n };\n }\n }\n return null;\n}\n\n/** True if a synkro local-cc task is currently Running. */\nexport function isRunning(channel: ChannelIdentity = CHANNEL_PRIMARY): boolean {\n const t = findTask(channel);\n return t?.status === 'Running';\n}\n\nexport interface StartOptions {\n /** Override the channel plugin script path. */\n pluginPath?: string;\n /** Working directory for the claude process. */\n cwd?: string;\n /** Which channel to start (default: primary). */\n channel?: ChannelIdentity;\n}\n\n/**\n * Add a fresh pueue task for the local-cc claude session. Removes any prior\n * task with the same label first (Done or otherwise) so subsequent calls\n * always replace cleanly.\n */\nexport function startTask(opts: StartOptions = {}): TaskInfo {\n const ch = opts.channel ?? CHANNEL_PRIMARY;\n const cwd = opts.cwd ?? ch.sessionDir;\n // Remove ALL tasks with this label (not just the first) to clear stale duplicates\n let existing = findTask(ch);\n while (existing) {\n if (existing.status === 'Running' || existing.status === 'Queued') {\n spawnSync('tmux', ['kill-session', '-t', `=${ch.tmuxSession}`], { encoding: 'utf-8' });\n spawnSync('pueue', ['kill', String(existing.id)], { encoding: 'utf-8' });\n for (let i = 0; i < 10; i++) {\n const check = findTask(ch);\n if (!check || check.id !== existing.id || (check.status !== 'Running' && check.status !== 'Queued')) break;\n spawnSync('sleep', ['0.5'], { encoding: 'utf-8' });\n }\n }\n spawnSync('pueue', ['remove', String(existing.id)], { encoding: 'utf-8' });\n existing = findTask(ch);\n }\n\n const runScript = join(cwd, 'run-claude.sh');\n const args = [\n 'add',\n '--label', ch.taskLabel,\n '--working-directory', cwd,\n '--',\n 'bash', runScript,\n ];\n\n const r = spawnSync('pueue', args, { encoding: 'utf-8' });\n if (r.status !== 0) {\n throw new PueueError(`pueue add failed: ${r.stderr || r.stdout}`);\n }\n const created = findTask(ch);\n if (!created) {\n throw new PueueError(`pueue add succeeded but no task with label ${ch.taskLabel} found`);\n }\n return created;\n}\n\n/** Stop and remove the synkro local-cc task. Also kills the tmux session\n * that owns the underlying claude process. No-op if neither is present. */\nexport function stopTask(channel: ChannelIdentity = CHANNEL_PRIMARY): void {\n spawnSync('tmux', ['kill-session', '-t', `=${channel.tmuxSession}`], { encoding: 'utf-8' });\n // Remove ALL tasks with this label, waiting for each kill to land\n let t = findTask(channel);\n while (t) {\n if (t.status === 'Running' || t.status === 'Queued') {\n spawnSync('pueue', ['kill', String(t.id)], { encoding: 'utf-8' });\n // Wait for the task to leave Running state before removing\n for (let i = 0; i < 10; i++) {\n const check = findTask(channel);\n if (!check || check.id !== t.id || (check.status !== 'Running' && check.status !== 'Queued')) break;\n spawnSync('sleep', ['0.5'], { encoding: 'utf-8' });\n }\n }\n spawnSync('pueue', ['remove', String(t.id)], { encoding: 'utf-8' });\n t = findTask(channel);\n }\n}\n\n/** Print the last N log lines for the task. Returns the raw text. */\nexport function tailLogs(lines = 80, channel: ChannelIdentity = CHANNEL_PRIMARY): string {\n const t = findTask(channel);\n if (!t) return `(no ${channel.taskLabel} task)`;\n const r = spawnSync('pueue', ['log', '--lines', String(lines), String(t.id)], { encoding: 'utf-8' });\n return r.stdout || r.stderr || '(no output)';\n}\n\n/** Ensure the task is running; start it if not. Returns the task info. */\nexport function ensureRunning(opts: StartOptions = {}): TaskInfo {\n const ch = opts.channel ?? CHANNEL_PRIMARY;\n const t = findTask(ch);\n if (t && t.status === 'Running') return t;\n return startTask(opts);\n}\n\n/** Probe a TCP port — true if something accepts a connection within timeoutMs. */\nfunction probePort(host: string, port: number, timeoutMs = 500): Promise<boolean> {\n return new Promise(resolve => {\n const sock = connect(port, host);\n const done = (ok: boolean) => {\n try { sock.destroy(); } catch { /* noop */ }\n resolve(ok);\n };\n sock.once('connect', () => done(true));\n sock.once('error', () => done(false));\n sock.setTimeout(timeoutMs, () => done(false));\n });\n}\n\n/** Dismiss startup prompts in the tmux session. Best-effort.\n * Sends '1' (dev-channels accept) then Enter (other prompts). */\nfunction tmuxDismissPrompts(tmuxSession: string = TMUX_SESSION): void {\n spawnSync('tmux', ['send-keys', '-t', tmuxSession, '1'], { encoding: 'utf-8' });\n spawnSync('tmux', ['send-keys', '-t', tmuxSession, 'Enter'], { encoding: 'utf-8' });\n}\n\n/**\n * Wait up to `timeoutMs` for the channel TCP listener to come up. Each tick\n * the probe fails, send an Enter into the tmux session — that walks claude\n * past any startup confirmation dialogs (workspace trust, dev-channels,\n * MCP consent). The loop self-terminates the moment the port binds, so\n * we don't keep spamming Enters once claude is at the live `❯` prompt.\n */\nexport async function waitForChannelReady(\n port: number,\n timeoutMs = 60_000,\n host = '127.0.0.1',\n tmuxSession: string = TMUX_SESSION,\n): Promise<boolean> {\n const deadline = Date.now() + timeoutMs;\n while (Date.now() < deadline) {\n if (await probePort(host, port)) return true;\n tmuxDismissPrompts(tmuxSession);\n await new Promise(r => setTimeout(r, 1000));\n }\n return probePort(host, port);\n}\n\nfunction brewInstall(pkg: string): boolean {\n const brew = spawnSync('brew', ['--version'], { encoding: 'utf-8' });\n if (brew.status !== 0) return false;\n console.log(` Installing ${pkg} via brew...`);\n const r = spawnSync('brew', ['install', pkg], { encoding: 'utf-8', stdio: 'inherit', timeout: 120_000 });\n return r.status === 0;\n}\n\n/** Ensure pueue is installed and pueued daemon is running. Auto-installs on macOS. */\nexport function assertPueueInstalled(): void {\n let r = spawnSync('pueue', ['--version'], { encoding: 'utf-8' });\n if (r.status !== 0) {\n if (process.platform === 'darwin' && brewInstall('pueue')) {\n r = spawnSync('pueue', ['--version'], { encoding: 'utf-8' });\n if (r.status !== 0) throw new PueueError('pueue install succeeded but binary not found on PATH.');\n } else {\n throw new PueueError('pueue not found. Install it: brew install pueue (macOS) or https://github.com/Nukesor/pueue');\n }\n }\n // Ensure pueued daemon is running\n const status = spawnSync('pueue', ['status', '--json'], { encoding: 'utf-8', timeout: 5_000 });\n if (status.status !== 0) {\n console.log(' Starting pueued daemon...');\n const child = spawn('pueued', ['-d'], { stdio: 'ignore', detached: true });\n child.unref();\n // Give the daemon a moment to bind its socket\n spawnSync('sleep', ['1']);\n const retry = spawnSync('pueue', ['status', '--json'], { encoding: 'utf-8', timeout: 5_000 });\n if (retry.status !== 0) {\n throw new PueueError('pueue daemon not reachable after starting pueued. Check `pueued` manually.');\n }\n }\n spawnSync('pueue', ['parallel', '2'], { encoding: 'utf-8' });\n}\n\n/** Ensure claude CLI is installed. */\nexport function assertClaudeInstalled(): void {\n const r = spawnSync('claude', ['--version'], { encoding: 'utf-8' });\n if (r.status !== 0) {\n throw new PueueError('claude CLI not found on PATH. Install Claude Code first: https://docs.claude.com/claude-code');\n }\n}\n\n/** Ensure tmux is installed. Auto-installs on macOS. */\nexport function assertTmuxInstalled(): void {\n let r = spawnSync('tmux', ['-V'], { encoding: 'utf-8' });\n if (r.status !== 0) {\n if (process.platform === 'darwin' && brewInstall('tmux')) {\n r = spawnSync('tmux', ['-V'], { encoding: 'utf-8' });\n if (r.status !== 0) throw new PueueError('tmux install succeeded but binary not found on PATH.');\n } else {\n throw new PueueError('tmux not found. Install it: brew install tmux (macOS) or apt install tmux (Linux)');\n }\n }\n}\n\n/** No-throw helper used by the install command for status messages. */\nexport function readVersion(bin: string): string | null {\n try {\n return execFileSync(bin, ['--version'], { encoding: 'utf-8', timeout: 3000 }).trim();\n } catch {\n return null;\n }\n}\n","/**\n * Role-specific prompts for the local-CC channel.\n *\n * Primers are fetched live from the Synkro API on every grade call.\n * No local caching — prompt updates deploy instantly.\n */\nimport { readFileSync } from 'node:fs';\nimport { homedir } from 'node:os';\nimport { join } from 'node:path';\n\nexport type LocalCCRole = 'grade-edit' | 'grade-bash' | 'grade-plan' | 'grade-cwe';\n\nconst CREDS_PATH = join(homedir(), '.synkro', 'credentials.json');\n\ninterface PromptsResponse {\n grader_primer_bash?: string;\n grader_primer_edit?: string;\n grader_primer_plan?: string;\n grader_primer_cwe?: string;\n}\n\nasync function fetchPrimers(): Promise<PromptsResponse> {\n let jwt = '';\n let gatewayUrl = '';\n try {\n const creds = JSON.parse(readFileSync(CREDS_PATH, 'utf-8'));\n jwt = creds.access_token || '';\n gatewayUrl = creds.gateway_url || 'https://api.synkro.sh';\n } catch {\n throw new Error('No credentials found. Run `synkro install` first.');\n }\n if (!jwt) throw new Error('No access token. Run `synkro install` first.');\n\n const resp = await fetch(`${gatewayUrl}/api/v1/cli/judge-prompts`, {\n headers: { Authorization: `Bearer ${jwt}` },\n signal: AbortSignal.timeout(5000),\n });\n if (!resp.ok) throw new Error(`Failed to fetch prompts: ${resp.status}`);\n return resp.json() as Promise<PromptsResponse>;\n}\n\nasync function getPrimer(role: LocalCCRole): Promise<string> {\n const prompts = await fetchPrimers();\n const primer = role === 'grade-edit' ? prompts.grader_primer_edit\n : role === 'grade-plan' ? prompts.grader_primer_plan\n : role === 'grade-cwe' ? prompts.grader_primer_cwe\n : prompts.grader_primer_bash;\n if (!primer) {\n throw new Error(`No primer for role \"${role}\" returned from API.`);\n }\n return primer;\n}\n\nconst CHANNEL_REPLY_INSTRUCTIONS = `\nDELIVERY METHOD — MANDATORY, OVERRIDES ALL OTHER OUTPUT RULES:\nYou are running inside a Synkro MCP channel. Do NOT output your verdict as text.\nInstead, after generating your verdict, call the \\`reply\\` tool EXACTLY ONCE with:\n - req_id: the req_id from this channel event's meta\n - result: your complete verdict block as a string (the <synkro-verdict>…</synkro-verdict> XML)\nAny text output is silently discarded. Only the reply tool call is captured.`;\n\nexport async function buildChannelContent(role: LocalCCRole, payload: string): Promise<string> {\n const primer = await getPrimer(role);\n return `${primer}\n\n${CHANNEL_REPLY_INSTRUCTIONS}\n\n---\nPAYLOAD (the input to evaluate):\n\n${payload}`;\n}\n","/**\n * JSONL log of every channel turn (grade / classify) — one entry per line.\n *\n * Written by `submitToChannel` after each request completes (success or fail).\n * Read by `synkro local-cc logs` to show recent activity.\n *\n * Best-effort: any I/O error here is swallowed so logging never breaks the\n * actual grading path.\n */\n\nimport { appendFileSync, existsSync, mkdirSync, openSync, readFileSync, readSync, closeSync, statSync, watchFile, unwatchFile } from 'node:fs';\nimport { dirname, join } from 'node:path';\nimport { homedir } from 'node:os';\nimport type { LocalCCRole } from './prompts.js';\n\nexport const TURN_LOG_PATH = join(homedir(), '.synkro', 'cc_sessions', 'turns.log');\n\nexport type TurnStatus = 'ok' | 'timeout' | 'error';\n\nexport interface TurnEntry {\n /** ISO timestamp of when the request started. */\n ts: string;\n role: LocalCCRole;\n /** Wall-clock ms from submit to result/error. */\n duration_ms: number;\n status: TurnStatus;\n /** Truncated request payload (first ~400 chars of caller-supplied text). */\n request_preview: string;\n /** Truncated response result (first ~400 chars). Empty on non-ok. */\n response_preview: string;\n /** Parsed severity if we can extract one from the verdict. Optional. */\n severity?: string;\n /** Error message if status is timeout/error. */\n error?: string;\n}\n\nconst PREVIEW_MAX = 400;\n\nfunction truncate(s: string, max = PREVIEW_MAX): string {\n if (s.length <= max) return s;\n return s.slice(0, max) + '… [+' + (s.length - max) + ' chars]';\n}\n\n/** Best-effort extraction of severity from the result text. */\nfunction extractSeverity(result: string): string | undefined {\n // <synkro-verdict>{\"severity\":\"audit\",…}</synkro-verdict>\n const m = result.match(/<synkro-(?:verdict|intent)>([\\s\\S]*?)<\\/synkro-(?:verdict|intent)>/);\n if (!m) return undefined;\n try {\n const obj = JSON.parse(m[1]) as { severity?: string; verdict?: string; type?: string; ok?: boolean };\n if (obj.severity) return String(obj.severity);\n if (typeof obj.ok === 'boolean') return obj.ok ? 'ok' : 'violations';\n if (obj.type) return String(obj.type);\n if (obj.verdict) return String(obj.verdict);\n } catch {\n // not parseable JSON inside the tag — ignore\n }\n return undefined;\n}\n\n/** Append one turn to the JSONL log. Best-effort — never throws. */\nexport function appendTurn(args: {\n startedAt: number;\n role: LocalCCRole;\n request: string;\n result?: string;\n status: TurnStatus;\n error?: string;\n}): void {\n try {\n mkdirSync(dirname(TURN_LOG_PATH), { recursive: true });\n const entry: TurnEntry = {\n ts: new Date(args.startedAt).toISOString(),\n role: args.role,\n duration_ms: Date.now() - args.startedAt,\n status: args.status,\n request_preview: truncate(args.request),\n response_preview: args.result ? truncate(args.result) : '',\n severity: args.result ? extractSeverity(args.result) : undefined,\n error: args.error,\n };\n appendFileSync(TURN_LOG_PATH, JSON.stringify(entry) + '\\n', 'utf-8');\n } catch {\n // never let logging fail the call site\n }\n}\n\n/**\n * Read the most recent N turn entries (newest first). Returns an empty array\n * on any error.\n */\nexport function readRecentTurns(n = 20): TurnEntry[] {\n if (!existsSync(TURN_LOG_PATH)) return [];\n try {\n // For typical sizes this is fine; if the file grows huge we'd switch to\n // tail-style reverse reading. Cap by stat size for safety.\n const size = statSync(TURN_LOG_PATH).size;\n if (size === 0) return [];\n const text = readFileSync(TURN_LOG_PATH, 'utf-8');\n const lines = text.split('\\n').filter(Boolean);\n const lastN = lines.slice(-n).reverse();\n return lastN\n .map(line => {\n try { return JSON.parse(line) as TurnEntry; } catch { return null; }\n })\n .filter((x): x is TurnEntry => x !== null);\n } catch {\n return [];\n }\n}\n\n/**\n * Tail the turn log: invoke `onEntry` for each new entry appended after the\n * call. Handles file truncation/rotation by re-syncing offset to 0 when size\n * shrinks. Returns a `stop()` to detach the watcher.\n */\nexport function followTurns(onEntry: (t: TurnEntry) => void): () => void {\n // Make sure the file exists so watchFile has something to poll. Creating\n // an empty file is harmless if it already exists.\n try {\n mkdirSync(dirname(TURN_LOG_PATH), { recursive: true });\n if (!existsSync(TURN_LOG_PATH)) {\n appendFileSync(TURN_LOG_PATH, '', 'utf-8');\n }\n } catch {\n // best-effort\n }\n\n let lastSize = (() => {\n try { return statSync(TURN_LOG_PATH).size; } catch { return 0; }\n })();\n let pendingPartial = ''; // bytes after the last \\n if a write split on a line boundary\n\n const drainNewBytes = (from: number, to: number): void => {\n if (to <= from) return;\n let fd: number | null = null;\n try {\n fd = openSync(TURN_LOG_PATH, 'r');\n const len = to - from;\n const buf = Buffer.alloc(len);\n readSync(fd, buf, 0, len, from);\n const text = pendingPartial + buf.toString('utf-8');\n const lastNewline = text.lastIndexOf('\\n');\n if (lastNewline === -1) {\n pendingPartial = text;\n return;\n }\n const complete = text.slice(0, lastNewline);\n pendingPartial = text.slice(lastNewline + 1);\n for (const line of complete.split('\\n')) {\n if (!line) continue;\n try {\n onEntry(JSON.parse(line) as TurnEntry);\n } catch {\n // skip malformed lines\n }\n }\n } catch {\n // best-effort\n } finally {\n if (fd !== null) {\n try { closeSync(fd); } catch { /* noop */ }\n }\n }\n };\n\n // Poll-based watcher (cross-platform reliable). 250ms is responsive\n // enough for tail-f UX without burning CPU.\n watchFile(TURN_LOG_PATH, { interval: 250 }, (curr, prev) => {\n if (curr.size < lastSize) {\n // file truncated/rotated — reset.\n lastSize = 0;\n pendingPartial = '';\n }\n if (curr.size > lastSize) {\n drainNewBytes(lastSize, curr.size);\n lastSize = curr.size;\n }\n });\n\n return () => unwatchFile(TURN_LOG_PATH);\n}\n","/**\n * Client for the local-CC channel plugin.\n *\n * POSTs to a TCP port on 127.0.0.1 exposed by the channel plugin running\n * inside the pueue-managed `claude` process. Returns the raw text Claude\n * wrote into the `reply` tool's `result` argument.\n */\n\nimport { request as httpRequest } from 'node:http';\nimport { connect } from 'node:net';\nimport { buildChannelContent, type LocalCCRole } from './prompts.js';\nimport { appendTurn } from './turnLog.js';\n\nexport const CHANNEL_HOST = '127.0.0.1';\nexport const CHANNEL_PORT = parseInt(process.env.SYNKRO_CHANNEL_PORT || '8929', 10);\n\nconst DEFAULT_TIMEOUT_MS = 90_000;\n\nexport class LocalCCError extends Error {\n constructor(message: string, public readonly cause?: unknown) {\n super(message);\n this.name = 'LocalCCError';\n }\n}\n\nexport interface SubmitOptions {\n /** Timeout for the round-trip in ms (default 90s). */\n timeoutMs?: number;\n /** Override the channel port (default: CHANNEL_PORT / 8929). */\n port?: number;\n}\n\n/**\n * Send a request through the channel and wait for Claude's `reply` tool output.\n * Throws LocalCCError if the channel isn't reachable, the channel times out, or\n * the plugin returns a non-2xx status.\n */\nexport async function submitToChannel(\n role: LocalCCRole,\n payload: string,\n opts: SubmitOptions = {},\n): Promise<string> {\n const content = await buildChannelContent(role, payload);\n const body = JSON.stringify({ role, content });\n const timeoutMs = opts.timeoutMs ?? DEFAULT_TIMEOUT_MS;\n const port = opts.port ?? CHANNEL_PORT;\n const startedAt = Date.now();\n\n try {\n const result = await new Promise<string>((resolve, reject) => {\n const req = httpRequest({\n host: CHANNEL_HOST,\n port,\n method: 'POST',\n path: '/submit',\n headers: {\n 'Content-Type': 'application/json',\n 'Content-Length': Buffer.byteLength(body),\n },\n timeout: timeoutMs,\n }, res => {\n const chunks: Buffer[] = [];\n res.on('data', c => chunks.push(c));\n res.on('end', () => {\n const text = Buffer.concat(chunks).toString('utf-8');\n if (res.statusCode !== 200) {\n reject(new LocalCCError(`channel returned ${res.statusCode}: ${text.slice(0, 500)}`));\n return;\n }\n try {\n const parsed = JSON.parse(text) as { result?: string; error?: string };\n if (parsed.error) {\n reject(new LocalCCError(parsed.error));\n return;\n }\n resolve(String(parsed.result ?? ''));\n } catch (err) {\n reject(new LocalCCError(`malformed channel response: ${text.slice(0, 200)}`, err));\n }\n });\n });\n req.on('timeout', () => {\n req.destroy(new LocalCCError(`channel request timed out after ${timeoutMs}ms`));\n });\n req.on('error', err => {\n const msg = (err as NodeJS.ErrnoException).code === 'ECONNREFUSED'\n ? `channel connection refused at ${CHANNEL_HOST}:${CHANNEL_PORT} (is the pueue task running?)`\n : `channel request failed: ${(err as Error).message}`;\n reject(new LocalCCError(msg, err));\n });\n req.write(body);\n req.end();\n });\n appendTurn({ startedAt, role, request: payload, result, status: 'ok' });\n return result;\n } catch (err) {\n const message = (err as Error).message ?? String(err);\n const status = /timed out/i.test(message) ? 'timeout' : 'error';\n appendTurn({ startedAt, role, request: payload, status, error: message });\n throw err;\n }\n}\n\n/** Quick TCP probe — true if anything is listening on the given port (default: CHANNEL_PORT). */\nexport function isChannelAvailable(port = CHANNEL_PORT, timeoutMs = 500): Promise<boolean> {\n return new Promise(resolve => {\n const sock = connect(port, CHANNEL_HOST);\n const done = (ok: boolean) => {\n try { sock.destroy(); } catch { /* noop */ }\n resolve(ok);\n };\n sock.once('connect', () => done(true));\n sock.once('error', () => done(false));\n sock.setTimeout(timeoutMs, () => done(false));\n });\n}\n","// :)\n/**\n * synkro install — first-time setup on customer's machine.\n *\n * Detects installed AI agents (CC, Codex), drops hook scripts in\n * ~/.synkro/hooks/, writes settings.json hook entries, fetches the latest\n * Edit/Write prompt from the gateway and inlines it into settings, writes\n * config.env. Triggers `synkro login` if not already authed.\n */\nimport { existsSync, mkdirSync, writeFileSync, chmodSync, readFileSync, readdirSync, appendFileSync, renameSync } from 'node:fs';\nimport { homedir } from 'node:os';\nimport { join } from 'node:path';\nimport { execSync, spawnSync, spawn } from 'node:child_process';\nimport { createInterface } from 'node:readline';\nimport { detectAgents } from '../installer/agentDetect.js';\nimport { installCCHooks } from '../installer/ccHookConfig.js';\nimport { installCursorHooks } from '../installer/cursorHookConfig.js';\nimport { installMcpConfig, installCursorMcpConfig } from '../installer/mcpConfig.js';\nimport { SYNKRO_COMMON_SCRIPT } from '../installer/hookScripts.js';\nimport { SYNKRO_COMMON_TS, EDIT_PRECHECK_TS, CWE_PRECHECK_TS, CVE_PRECHECK_TS, BASH_JUDGE_TS, AGENT_JUDGE_TS, PLAN_JUDGE_TS, STOP_SUMMARY_TS, SESSION_START_TS, BASH_FOLLOWUP_TS, TRANSCRIPT_SYNC_TS, USER_PROMPT_SUBMIT_TS, CURSOR_BASH_JUDGE_TS, CURSOR_EDIT_CAPTURE_TS } from '../installer/hookScriptsTs.js';\nimport { isAuthenticated, getAccessToken, authenticate, getUserInfo, ensureValidToken } from '../auth/stub.js';\nimport { promptRepoConnection } from './repoConnect.js';\nimport { setApiBaseUrl, listProjects } from '../api/projects.js';\nimport { connectGitHub } from './setupGithub.js';\nimport { fetchJudgePrompts } from '../installer/promptFetcher.js';\nimport { isLocalCCEnabled } from '../local-cc/settings.js';\nimport { installLocalCC, CHANNEL_2_PORT } from '../local-cc/install.js';\nimport { ensureRunning as ensurePueueRunning, startTask as startPueueTask, stopTask as stopPueueTask, assertClaudeInstalled, assertPueueInstalled, assertTmuxInstalled, waitForChannelReady, findTask, tailLogs, CHANNEL_SECONDARY } from '../local-cc/pueue.js';\nimport { CHANNEL_HOST, CHANNEL_PORT, submitToChannel } from '../local-cc/client.js';\n\n// Replaced by tsup `define` at build time.\ndeclare const __SYNKRO_CLI_VERSION__: string;\ndeclare const __MCP_LOCAL_SERVER_SRC__: string;\n\nconst SYNKRO_DIR = join(homedir(), '.synkro');\nconst HOOKS_DIR = join(SYNKRO_DIR, 'hooks');\nconst BIN_DIR = join(SYNKRO_DIR, 'bin');\nconst CONFIG_PATH = join(SYNKRO_DIR, 'config.env');\n\ninterface InstallOptions {\n gatewayUrl?: string; // override default\n apiKey?: string; // skip prompting if provided (for tests/CI)\n skipAuth?: boolean;\n noMcp?: boolean; // skip registering the guardrails MCP server\n force?: boolean; // bypass the \"already installed\" short-circuit\n linkRepo?: boolean; // auto-link current repo (non-interactive)\n}\n\n// Accept a gateway URL only if it's a plain http(s) URL. The published CLI is\n// invoked from arbitrary cwds where a developer's monorepo .env / op://\n// reference / direnv export may have poisoned SYNKRO_GATEWAY_URL with a value\n// the CLI can't actually call (e.g. \"op://dev/synkro/gateway-url\"). Silently\n// ignoring those falls through to the prod default so customers never have to\n// wrestle with shell hygiene before `synkro install` works.\nfunction sanitizeGatewayCandidate(raw: string | undefined): string | undefined {\n if (!raw) return undefined;\n return /^https?:\\/\\//.test(raw) ? raw : undefined;\n}\n\nexport function parseArgs(argv: string[]): InstallOptions {\n const opts: InstallOptions = {};\n for (const a of argv) {\n if (a.startsWith('--api-key=')) opts.apiKey = a.slice('--api-key='.length);\n else if (a.startsWith('--gateway=')) opts.gatewayUrl = a.slice('--gateway='.length);\n else if (a === '--skip-auth') opts.skipAuth = true;\n else if (a === '--no-mcp') opts.noMcp = true;\n else if (a === '--force' || a === '-f') opts.force = true;\n else if (a === '--link-repo') opts.linkRepo = true;\n }\n if (!opts.gatewayUrl) {\n const fromEnv = sanitizeGatewayCandidate(process.env.SYNKRO_GATEWAY_URL);\n if (fromEnv) opts.gatewayUrl = fromEnv;\n }\n // NOTE: we deliberately do NOT pick up SYNKRO_API_KEY from process.env. The\n // root .env / global env may contain a stale or unrelated SYNKRO_API_KEY.\n // The legitimate sources are: --api-key flag, or fresh OAuth via authenticate().\n return opts;\n}\n\nasync function promptTranscriptConsent(): Promise<boolean> {\n const rl = createInterface({ input: process.stdin, output: process.stdout });\n return new Promise((resolve) => {\n rl.question(\n 'Would you like Synkro to use Claude Code session transcripts\\n' +\n 'to generate guardrail rules and policies for your team? (Y/n) ',\n (answer) => {\n rl.close();\n const trimmed = answer.trim().toLowerCase();\n resolve(trimmed === '' || trimmed === 'y' || trimmed === 'yes');\n },\n );\n });\n}\n\nconst OFFSETS_DIR = join(SYNKRO_DIR, '.transcript-offsets');\n\nfunction ensureSynkroDir(): void {\n mkdirSync(SYNKRO_DIR, { recursive: true });\n mkdirSync(HOOKS_DIR, { recursive: true });\n mkdirSync(BIN_DIR, { recursive: true });\n mkdirSync(OFFSETS_DIR, { recursive: true });\n}\n\nfunction writeHookScripts(): {\n bashScript: string;\n bashFollowupScript: string;\n editPrecheckScript: string;\n cwePrecheckScript: string;\n cvePrecheckScript: string;\n planJudgeScript: string;\n agentJudgeScript: string;\n stopSummaryScript: string;\n sessionStartScript: string;\n transcriptSyncScript: string;\n userPromptSubmitScript: string;\n cursorBashJudgeScript: string;\n cursorEditCaptureScript: string;\n mcpLocalServerScript: string;\n} {\n const bashScriptPath = join(HOOKS_DIR, 'cc-bash-judge.ts');\n const bashFollowupScriptPath = join(HOOKS_DIR, 'cc-bash-followup.ts');\n const editPrecheckScriptPath = join(HOOKS_DIR, 'cc-edit-precheck.ts');\n const cwePrecheckScriptPath = join(HOOKS_DIR, 'cc-cwe-precheck.ts');\n const cvePrecheckScriptPath = join(HOOKS_DIR, 'cc-cve-precheck.ts');\n const planJudgeScriptPath = join(HOOKS_DIR, 'cc-plan-judge.ts');\n const agentJudgeScriptPath = join(HOOKS_DIR, 'cc-agent-judge.ts');\n const stopSummaryScriptPath = join(HOOKS_DIR, 'cc-stop-summary.ts');\n const sessionStartScriptPath = join(HOOKS_DIR, 'cc-session-start.ts');\n const transcriptSyncScriptPath = join(HOOKS_DIR, 'cc-transcript-sync.ts');\n const userPromptSubmitScriptPath = join(HOOKS_DIR, 'cc-user-prompt-submit.ts');\n const commonScriptPath = join(HOOKS_DIR, '_synkro-common.ts');\n const commonBashScriptPath = join(HOOKS_DIR, '_synkro-common.sh');\n const cursorBashJudgePath = join(HOOKS_DIR, 'cursor-bash-judge.ts');\n const cursorEditCapturePath = join(HOOKS_DIR, 'cursor-edit-capture.ts');\n const mcpLocalServerPath = join(HOOKS_DIR, 'mcp-local-server.ts');\n\n writeFileSync(bashScriptPath, BASH_JUDGE_TS, 'utf-8');\n writeFileSync(bashFollowupScriptPath, BASH_FOLLOWUP_TS, 'utf-8');\n writeFileSync(editPrecheckScriptPath, EDIT_PRECHECK_TS, 'utf-8');\n writeFileSync(cwePrecheckScriptPath, CWE_PRECHECK_TS, 'utf-8');\n writeFileSync(cvePrecheckScriptPath, CVE_PRECHECK_TS, 'utf-8');\n writeFileSync(planJudgeScriptPath, PLAN_JUDGE_TS, 'utf-8');\n writeFileSync(agentJudgeScriptPath, AGENT_JUDGE_TS, 'utf-8');\n writeFileSync(stopSummaryScriptPath, STOP_SUMMARY_TS, 'utf-8');\n writeFileSync(sessionStartScriptPath, SESSION_START_TS, 'utf-8');\n writeFileSync(transcriptSyncScriptPath, TRANSCRIPT_SYNC_TS, 'utf-8');\n writeFileSync(userPromptSubmitScriptPath, USER_PROMPT_SUBMIT_TS, 'utf-8');\n writeFileSync(commonScriptPath, SYNKRO_COMMON_TS, 'utf-8');\n writeFileSync(commonBashScriptPath, SYNKRO_COMMON_SCRIPT, 'utf-8');\n writeFileSync(cursorBashJudgePath, CURSOR_BASH_JUDGE_TS, 'utf-8');\n writeFileSync(cursorEditCapturePath, CURSOR_EDIT_CAPTURE_TS, 'utf-8');\n writeFileSync(mcpLocalServerPath, __MCP_LOCAL_SERVER_SRC__, 'utf-8');\n\n chmodSync(bashScriptPath, 0o755);\n chmodSync(bashFollowupScriptPath, 0o755);\n chmodSync(editPrecheckScriptPath, 0o755);\n chmodSync(cwePrecheckScriptPath, 0o755);\n chmodSync(cvePrecheckScriptPath, 0o755);\n chmodSync(planJudgeScriptPath, 0o755);\n chmodSync(agentJudgeScriptPath, 0o755);\n chmodSync(stopSummaryScriptPath, 0o755);\n chmodSync(sessionStartScriptPath, 0o755);\n chmodSync(transcriptSyncScriptPath, 0o755);\n chmodSync(userPromptSubmitScriptPath, 0o755);\n chmodSync(commonScriptPath, 0o755);\n chmodSync(commonBashScriptPath, 0o755);\n chmodSync(cursorBashJudgePath, 0o755);\n chmodSync(cursorEditCapturePath, 0o755);\n chmodSync(mcpLocalServerPath, 0o755);\n\n return {\n bashScript: bashScriptPath,\n bashFollowupScript: bashFollowupScriptPath,\n editPrecheckScript: editPrecheckScriptPath,\n cwePrecheckScript: cwePrecheckScriptPath,\n cvePrecheckScript: cvePrecheckScriptPath,\n planJudgeScript: planJudgeScriptPath,\n agentJudgeScript: agentJudgeScriptPath,\n stopSummaryScript: stopSummaryScriptPath,\n sessionStartScript: sessionStartScriptPath,\n transcriptSyncScript: transcriptSyncScriptPath,\n userPromptSubmitScript: userPromptSubmitScriptPath,\n cursorBashJudgeScript: cursorBashJudgePath,\n cursorEditCaptureScript: cursorEditCapturePath,\n mcpLocalServerScript: mcpLocalServerPath,\n };\n}\n\n// Sanitize values before writing into config.env — the file is sourced as a\n// shell script, so any unquoted shell metacharacter in a value is dangerous\n// (newlines smuggle new assignments; (){}#`$ etc. let an attacker run code\n// on `source config.env`). Two defenses:\n// 1. Strip non-printable ASCII (drops newlines, tabs, control chars)\n// 2. Wrap the result in single-quotes — single-quoted shell strings are\n// entirely literal, so anything inside is safe regardless of contents.\n// Internal single-quotes are escaped using the standard '\\'' trick.\nfunction sanitizeConfigValue(raw: string | undefined, maxLen = 256): string {\n if (!raw) return '';\n return raw\n .replace(/[^\\x20-\\x7E]/g, '') // drop non-printable (newlines/tabs/control)\n .slice(0, maxLen);\n}\n\nfunction shellQuoteSingle(value: string): string {\n // Escape any single quote in `value` for inclusion inside a single-quoted\n // shell string: 'foo'\"'\"'bar' renders as foo'bar.\n return `'${value.replace(/'/g, \"'\\\\''\")}'`;\n}\n\n// Resolve the absolute path to the synkro bundle (dist/bootstrap.js) so hook\n// scripts can invoke `node \"$SYNKRO_CLI_BIN\" grade ...` without relying on\n// the user's shell PATH. Returning a single path (vs a command string) keeps\n// the hook invocation a single quoted arg — no shell-injection surface.\n//\n// process.argv[1] is the script the user executed: with the pnpm/npm shim\n// it's the absolute path to dist/bootstrap.js. With `just synkro install`\n// it's also the absolute bundle path (the just recipe runs `node bootstrap.js`).\nfunction resolveSynkroBundle(): string | null {\n const scriptPath = process.argv[1];\n if (scriptPath && existsSync(scriptPath)) return scriptPath;\n return null;\n}\n\nfunction writeConfigEnv(opts: { gatewayUrl: string; userId?: string; orgId?: string; email?: string; tier?: string; inference?: string; synkroBin?: string | null; transcriptConsent?: boolean; localInference?: boolean }): void {\n const credsPath = join(SYNKRO_DIR, 'credentials.json');\n const safeGateway = sanitizeConfigValue(opts.gatewayUrl);\n const safeUserId = sanitizeConfigValue(opts.userId);\n const safeOrgId = sanitizeConfigValue(opts.orgId);\n const safeEmail = sanitizeConfigValue(opts.email);\n const safeTier = sanitizeConfigValue(opts.tier ?? 'pro', 32);\n const safeInference = sanitizeConfigValue(opts.inference ?? 'fast', 16);\n // Allow up to 1KB for the bin command since it may be `node /long/path/to/bootstrap.js`.\n const safeSynkroBin = sanitizeConfigValue(opts.synkroBin ?? '', 1024);\n const lines = [\n '# Synkro CLI config (managed by synkro install)',\n '# JWT auth — the hook scripts read SYNKRO_CREDENTIALS_PATH at runtime',\n '# and send Authorization: Bearer <access_token> on every gateway call.',\n `SYNKRO_GATEWAY_URL=${shellQuoteSingle(safeGateway)}`,\n `SYNKRO_CREDENTIALS_PATH=${shellQuoteSingle(credsPath)}`,\n `SYNKRO_TIER=${shellQuoteSingle(safeTier)}`,\n `SYNKRO_INFERENCE=${shellQuoteSingle(safeInference)}`,\n `SYNKRO_VERSION=${shellQuoteSingle(__SYNKRO_CLI_VERSION__)}`,\n ];\n if (safeSynkroBin) lines.push(`SYNKRO_CLI_BIN=${shellQuoteSingle(safeSynkroBin)}`);\n if (safeUserId) lines.push(`SYNKRO_USER_ID=${shellQuoteSingle(safeUserId)}`);\n if (safeOrgId) lines.push(`SYNKRO_ORG_ID=${shellQuoteSingle(safeOrgId)}`);\n if (safeEmail) lines.push(`SYNKRO_EMAIL=${shellQuoteSingle(safeEmail)}`);\n if (opts.transcriptConsent !== undefined) {\n lines.push(`SYNKRO_TRANSCRIPT_CONSENT=${shellQuoteSingle(opts.transcriptConsent ? 'yes' : 'no')}`);\n }\n lines.push(`SYNKRO_LOCAL_INFERENCE=${shellQuoteSingle(opts.localInference ? 'yes' : 'no')}`);\n lines.push('');\n writeFileSync(CONFIG_PATH, lines.join('\\n'), 'utf-8');\n chmodSync(CONFIG_PATH, 0o600);\n}\n\nfunction updateLocalInferenceFlag(enabled: boolean): void {\n if (!existsSync(CONFIG_PATH)) return;\n let content = readFileSync(CONFIG_PATH, 'utf-8');\n const flag = enabled ? 'yes' : 'no';\n if (content.includes('SYNKRO_LOCAL_INFERENCE=')) {\n content = content.replace(/^SYNKRO_LOCAL_INFERENCE='[^']*'/m, `SYNKRO_LOCAL_INFERENCE='${flag}'`);\n } else {\n content = content.trimEnd() + `\\nSYNKRO_LOCAL_INFERENCE='${flag}'\\n`;\n }\n writeFileSync(CONFIG_PATH, content, 'utf-8');\n}\n\nfunction collectLocalMetadata(): Record<string, unknown> {\n const meta: Record<string, unknown> = { platform: process.platform };\n try {\n meta.display_name = execSync('git config user.name', { encoding: 'utf-8', timeout: 3000 }).trim();\n } catch {}\n try {\n const remote = execSync('git remote get-url origin', { encoding: 'utf-8', timeout: 3000 }).trim();\n const sshMatch = remote.match(/^git@[^:]+:(.+?)(?:\\.git)?$/);\n const httpMatch = remote.match(/^https?:\\/\\/[^/]+\\/(.+?)(?:\\.git)?$/);\n const m = sshMatch || httpMatch;\n if (m) meta.active_repo = m[1];\n } catch {}\n try {\n meta.cc_version = execSync('claude --version', { encoding: 'utf-8', timeout: 5000 }).trim().split('\\n')[0];\n } catch {}\n\n const claudeDir = join(homedir(), '.claude');\n\n try {\n const settings = JSON.parse(readFileSync(join(claudeDir, 'settings.json'), 'utf-8'));\n const plugins = Object.keys(settings.enabledPlugins ?? {}).filter(k => settings.enabledPlugins[k]);\n if (plugins.length) meta.enabled_plugins = plugins;\n if (settings.permissions?.defaultMode) meta.permissions_mode = settings.permissions.defaultMode;\n } catch {}\n\n try {\n const mcpCache = JSON.parse(readFileSync(join(claudeDir, 'mcp-needs-auth-cache.json'), 'utf-8'));\n const mcpNames = Object.keys(mcpCache);\n if (mcpNames.length) meta.mcp_servers = mcpNames;\n } catch {}\n\n try {\n const mcpList = execSync('claude mcp list 2>/dev/null', { encoding: 'utf-8', timeout: 10000 });\n const connected = mcpList.split('\\n')\n .filter(l => l.includes('Connected'))\n .map(l => l.split(':')[0].trim())\n .filter(Boolean);\n if (connected.length) meta.mcp_servers_connected = connected;\n } catch {}\n\n try {\n const sessionsDir = join(claudeDir, 'sessions');\n const files = readdirSync(sessionsDir).filter(f => f.endsWith('.json')).slice(-5);\n for (const f of files) {\n const s = JSON.parse(readFileSync(join(sessionsDir, f), 'utf-8'));\n if (s.version) { meta.cc_version = meta.cc_version || s.version; break; }\n }\n } catch {}\n\n return meta;\n}\n\nasync function fetchUserProfile(gatewayUrl: string, token: string): Promise<{ tier: string; inference: string; localInference: boolean; captureDepth: string }> {\n try {\n const resp = await fetch(`${gatewayUrl}/api/v1/cli/me`, {\n headers: { 'Authorization': `Bearer ${token}` },\n });\n if (!resp.ok) return { tier: 'pro', inference: 'fast', localInference: false, captureDepth: 'full' };\n const data = await resp.json() as { fast_inference?: boolean; plan_tier?: string; local_inference?: boolean; capture_depth?: string };\n\n const meta = collectLocalMetadata();\n fetch(`${gatewayUrl}/api/v1/cli/me`, {\n method: 'PATCH',\n headers: { 'Authorization': `Bearer ${token}`, 'Content-Type': 'application/json' },\n body: JSON.stringify(meta),\n }).catch(() => {});\n\n return {\n tier: data.plan_tier ?? 'pro',\n inference: data.fast_inference ? 'fast' : 'standard',\n localInference: !!data.local_inference,\n captureDepth: data.capture_depth ?? 'full',\n };\n } catch {\n return { tier: 'pro', inference: 'fast', localInference: false, captureDepth: 'full' };\n }\n}\n\n// JWT-only auth — no API key minting. The hook scripts read the JWT from\n// ~/.synkro/credentials.json and send it as Authorization: Bearer <jwt>.\n// Auth middleware on the gateway resolves user/org from JWT claims via JWKS.\n\n// The CLI ships the user's WorkOS Bearer JWT to the gateway URL. If a\n// hostile actor sets --gateway=http://evil.example.com on a user's machine,\n// the JWT lands at the attacker's server. Allow only:\n// - https://*.synkro.sh and synkro.sh apex\n// - http(s)://localhost or 127.0.0.1 (dev)\n// Anything else is rejected before any fetch is attempted.\nfunction assertGatewayAllowed(gatewayUrl: string): void {\n let parsed: URL;\n try { parsed = new URL(gatewayUrl); }\n catch { throw new Error(`Invalid gateway URL: ${gatewayUrl}`); }\n const proto = parsed.protocol;\n const host = parsed.hostname;\n if (proto !== 'http:' && proto !== 'https:') {\n throw new Error(`Gateway URL must be http(s); got ${proto}`);\n }\n const isLocalhost = host === 'localhost' || host === '127.0.0.1' || host === '::1';\n const isSynkro = host === 'synkro.sh' || host.endsWith('.synkro.sh');\n if (proto === 'http:' && !isLocalhost) {\n throw new Error(`Gateway URL must be HTTPS for non-localhost hosts; got ${gatewayUrl}`);\n }\n if (!isLocalhost && !isSynkro) {\n throw new Error(`Gateway host not in allowlist (synkro.sh or *.synkro.sh): ${host}`);\n }\n}\n\n// Detect a complete prior install: every hook script present, config.env\n// written, and CC settings.json carries our `__synkro_managed__` markers.\n// MCP registration is opt-out (--no-mcp) so we don't gate on it. Returns\n// true only when every required surface is present so we never short-\n// circuit a partial / broken install.\nfunction isAlreadyInstalled(): boolean {\n const requiredScripts = [\n join(HOOKS_DIR, 'cc-bash-judge.ts'),\n join(HOOKS_DIR, 'cc-bash-followup.ts'),\n join(HOOKS_DIR, 'cc-edit-precheck.ts'),\n join(HOOKS_DIR, 'cc-cwe-precheck.ts'),\n join(HOOKS_DIR, 'cc-cve-precheck.ts'),\n join(HOOKS_DIR, 'cc-plan-judge.ts'),\n join(HOOKS_DIR, 'cc-stop-summary.ts'),\n join(HOOKS_DIR, 'cc-session-start.ts'),\n ];\n if (!requiredScripts.every((p) => existsSync(p))) return false;\n if (!existsSync(CONFIG_PATH)) return false;\n\n const settingsPath = join(homedir(), '.claude', 'settings.json');\n if (!existsSync(settingsPath)) return false;\n try {\n const settings = JSON.parse(readFileSync(settingsPath, 'utf-8'));\n const hooks = settings?.hooks;\n if (!hooks || typeof hooks !== 'object') return false;\n const hasManaged = (kind: string) =>\n Array.isArray(hooks[kind]) &&\n hooks[kind].some((entry: Record<string, unknown>) => entry?.__synkro_managed__ === true);\n if (!hasManaged('PreToolUse')) return false;\n if (!hasManaged('PostToolUse')) return false;\n if (!hasManaged('SessionEnd')) return false;\n if (!hasManaged('SessionStart')) return false;\n } catch {\n return false;\n }\n return true;\n}\n\nfunction printChannelDiagnostics(): void {\n try {\n const tmuxCheck = spawnSync('tmux', ['has-session', '-t', 'synkro-local-cc'], { encoding: 'utf-8' });\n console.warn(` tmux session: ${tmuxCheck.status === 0 ? 'running' : 'not running'}`);\n const pueueTask = findTask();\n console.warn(` pueue task: ${pueueTask ? `id=${pueueTask.id} status=${pueueTask.status}` : 'not found'}`);\n const bunCheck = spawnSync('bun', ['--version'], { encoding: 'utf-8' });\n console.warn(` bun: ${bunCheck.status === 0 ? bunCheck.stdout.trim() : 'not found'}`);\n const claudeCheck = spawnSync('claude', ['--version'], { encoding: 'utf-8' });\n console.warn(` claude: ${claudeCheck.status === 0 ? claudeCheck.stdout.trim().split('\\n')[0] : 'not found'}`);\n if (pueueTask) {\n const logs = tailLogs(15);\n if (logs && logs !== '(no output)') {\n console.warn(` pueue logs (last 15 lines):`);\n for (const line of logs.split('\\n').slice(0, 15)) {\n console.warn(` ${line}`);\n }\n }\n }\n const logPath = join(homedir(), '.synkro', 'cc_sessions', 'run-claude.log');\n if (existsSync(logPath)) {\n const logContent = readFileSync(logPath, 'utf-8').trim().split('\\n').slice(-10);\n console.warn(` run-claude.log:`);\n for (const line of logContent) console.warn(` ${line}`);\n }\n } catch {}\n console.warn(` Run \\`synkro local-cc status\\` and \\`synkro local-cc logs --tmux\\` to debug.`);\n}\n\n\nconst RULES_PATH = join(SYNKRO_DIR, 'rules.json');\nconst MCP_LOCAL_PORT = 8931;\n\nasync function backfillLocalRules(gatewayUrl: string, token: string): Promise<void> {\n if (existsSync(RULES_PATH)) {\n console.log(' Local rules already exist — skipping cloud backfill.');\n return;\n }\n try {\n const resp = await fetch(`${gatewayUrl}/api/v1/hook/config`, {\n headers: { 'Authorization': `Bearer ${token}` },\n signal: AbortSignal.timeout(8000),\n });\n if (!resp.ok) {\n console.log(' No cloud rules to backfill.');\n return;\n }\n const data = await resp.json() as any;\n const rules: any[] = (data.rules || []).map((r: any) => ({\n rule_id: r.rule_id || `r_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`,\n text: r.text || '',\n category: r.category || 'custom',\n severity: r.severity || 'medium',\n mode: r.mode || 'blocking',\n hook_stage: r.hook_stage || 'both',\n scope: r.scope || 'user',\n }));\n const policyName = data.active_policy_name || 'My Rules';\n const policyId = data.active_policy_id || 'local-policy';\n const scanExemptions = (data.scan_exemptions || [])\n .filter((e: any) => e && typeof e.path === 'string')\n .map((e: any) => ({ path: e.path, cwe_id: e.cwe_id || '', reason: e.reason }));\n const silent = data.silent_mode === true || data.silent_mode === 'true';\n\n const rulesFile = {\n policies: [{\n id: policyId,\n name: policyName,\n rules,\n ruleCount: rules.length,\n scopeOwner: 'user',\n isActive: true,\n }],\n config: { silent, activePolicyId: policyId },\n scanExemptions,\n };\n\n const tmp = RULES_PATH + '.tmp';\n writeFileSync(tmp, JSON.stringify(rulesFile, null, 2) + '\\n', 'utf-8');\n renameSync(tmp, RULES_PATH);\n\n const telemetryPath = join(SYNKRO_DIR, 'telemetry.jsonl');\n const event = {\n capture_type: 'rule_sync',\n policy_id: policyId,\n policy_name: policyName,\n rules,\n rule_count: rules.length,\n scan_exemptions: scanExemptions,\n silent,\n _ts: new Date().toISOString(),\n };\n try { appendFileSync(telemetryPath, JSON.stringify(event) + '\\n', 'utf-8'); } catch {}\n\n console.log(` Backfilled ${rules.length} rules from cloud to ~/.synkro/rules.json`);\n } catch (err) {\n console.warn(` ⚠ Cloud backfill failed: ${(err as Error).message}`);\n }\n}\n\nasync function startLocalMcpServer(): Promise<void> {\n const serverScript = join(HOOKS_DIR, 'mcp-local-server.ts');\n if (!existsSync(serverScript)) {\n console.warn(' ⚠ Local MCP server script not found — skipping.');\n return;\n }\n\n // Check if already running\n try {\n const probe = await fetch(`http://127.0.0.1:${MCP_LOCAL_PORT}/`, { signal: AbortSignal.timeout(1000) });\n if (probe.ok) {\n console.log(` Local MCP server already running on port ${MCP_LOCAL_PORT}`);\n return;\n }\n } catch {}\n\n const proc = spawn('bun', ['run', serverScript], {\n stdio: 'ignore',\n detached: true,\n });\n proc.unref();\n\n // Wait up to 5s for the server to come up\n for (let i = 0; i < 25; i++) {\n await new Promise(r => setTimeout(r, 200));\n try {\n const probe = await fetch(`http://127.0.0.1:${MCP_LOCAL_PORT}/`, { signal: AbortSignal.timeout(500) });\n if (probe.ok) {\n console.log(` Local MCP server started on port ${MCP_LOCAL_PORT}`);\n return;\n }\n } catch {}\n }\n console.warn(` ⚠ Local MCP server did not start within 5s — it may need to be started manually.`);\n}\n\nexport async function installCommand(opts: InstallOptions = {}): Promise<void> {\n const gatewayUrl = opts.gatewayUrl\n || sanitizeGatewayCandidate(process.env.SYNKRO_GATEWAY_URL)\n || 'https://api.synkro.sh';\n\n // Reject hostile gateway overrides before we leak the JWT.\n try {\n assertGatewayAllowed(gatewayUrl);\n } catch (err) {\n console.error((err as Error).message);\n process.exit(1);\n }\n\n // Idempotent short-circuit. If creds are still valid AND every required\n // surface is in place, skip the heavy steps (auth, hooks, daemon) but\n // still check whether the current repo needs linking — the user may be\n // running install from a second repo folder.\n if (!opts.force && isAuthenticated() && isAlreadyInstalled()) {\n setApiBaseUrl(`${gatewayUrl}/api`);\n await ensureValidToken();\n const currentRepo = detectGitRepo();\n if (currentRepo) {\n try {\n const projects = await listProjects();\n const alreadyLinked = projects.some((p: any) =>\n p.repos?.some((r: any) => r.full_name === currentRepo),\n );\n if (!alreadyLinked) {\n console.log(`Synkro is installed. This repo (${currentRepo}) is not linked yet.\\n`);\n await promptRepoConnection();\n return;\n }\n } catch {\n // API unreachable — fall through to normal \"already installed\" message\n }\n }\n // Even on short-circuit, check if local-cc should be set up.\n const token = getAccessToken();\n if (token) {\n const profile = await fetchUserProfile(gatewayUrl, token);\n if (profile.localInference && !isLocalCCEnabled()) {\n console.log('Local inference enabled — setting up local-CC channels...');\n try {\n assertClaudeInstalled();\n assertPueueInstalled();\n assertTmuxInstalled();\n stopPueueTask();\n stopPueueTask(CHANNEL_SECONDARY);\n installLocalCC();\n const t1 = startPueueTask();\n const t2 = startPueueTask({ channel: CHANNEL_SECONDARY });\n console.log(` channel 1: pueue id=${t1.id} channel 2: pueue id=${t2.id}`);\n console.log(' Waiting for both channels...');\n const [ready1, ready2] = await Promise.all([\n waitForChannelReady(CHANNEL_PORT, 60_000, CHANNEL_HOST),\n waitForChannelReady(CHANNEL_2_PORT, 60_000, CHANNEL_HOST, CHANNEL_SECONDARY.tmuxSession),\n ]);\n if (ready1) console.log(` channel 1 ready at ${CHANNEL_HOST}:${CHANNEL_PORT}`);\n else console.warn(' ⚠ channel 1 did not come up within 60s — check `synkro local-cc logs`');\n if (ready2) console.log(` channel 2 ready at ${CHANNEL_HOST}:${CHANNEL_2_PORT}`);\n else console.warn(' ⚠ channel 2 did not come up within 60s');\n updateLocalInferenceFlag(true);\n } catch (err) {\n console.warn(` ⚠ Local-CC setup skipped: ${(err as Error).message}`);\n console.warn(' Install pueue, tmux, and claude, then re-run install.');\n }\n }\n }\n console.log('✓ Synkro is already installed and configured.');\n console.log(' Run `synkro-cli update` to refresh hook scripts and judge prompts.');\n console.log(' Run `synkro-cli install --force` to reinstall from scratch.');\n return;\n }\n\n console.log('Synkro install starting...\\n');\n\n // 1. Auth via browser OAuth (WorkOS). Persists JWT + refresh_token to\n // ~/.synkro/credentials.json. The hook scripts authenticate against\n // the gateway with `Authorization: Bearer <access_token>` — auth\n // middleware resolves user/org from the JWT claims directly.\n if (!isAuthenticated()) {\n console.log('Opening browser for Synkro auth...');\n const result = await authenticate((status) => {\n switch (status.phase) {\n case 'starting': console.log(' Starting local callback server...'); break;\n case 'browser-opened': console.log(` Browser opened: ${status.url}`); break;\n case 'waiting': console.log(' Waiting for browser auth to complete...'); break;\n case 'success': console.log(' ✓ Authenticated'); break;\n case 'error': console.error(` ✗ ${status.message}`); break;\n }\n });\n if (!result) {\n console.error('Authentication failed. If you are running a self-hosted dashboard, set SYNKRO_WEB_AUTH_URL to its origin.');\n process.exit(1);\n }\n }\n const token = getAccessToken();\n if (!token) {\n console.error('No access token available after auth.');\n process.exit(1);\n }\n\n // 1b. Connect GitHub via WorkOS Pipes (optional — skip if user declines or it fails)\n console.log('\\nConnecting to GitHub (optional — for PR scanning)...');\n let ghToken: string | null = null;\n try {\n ghToken = await connectGitHub(gatewayUrl, token);\n } catch {}\n if (ghToken) {\n console.log();\n } else {\n console.log(' Skipped. Run `synkro setup-github` later to enable PR scanning.\\n');\n }\n\n // 1c. Connect repos (local git or GitHub OAuth)\n setApiBaseUrl(`${gatewayUrl}/api`);\n await promptRepoConnection({ linkRepo: opts.linkRepo });\n\n // 2. Detect installed agents\n const agents = detectAgents();\n if (agents.length === 0) {\n console.error('No AI coding agents detected. Install Claude Code first: https://docs.claude.com/claude-code');\n process.exit(1);\n }\n console.log('Detected agents:');\n for (const a of agents) {\n console.log(` ✓ ${a.name}${a.version ? ` (${a.version})` : ''}`);\n }\n console.log();\n\n // 3. Set up Synkro directory + hook scripts + grader daemon\n ensureSynkroDir();\n const scripts = writeHookScripts();\n console.log('Wrote hook scripts:');\n console.log(` ${scripts.bashScript}`);\n console.log(` ${scripts.bashFollowupScript}`);\n console.log(` ${scripts.editPrecheckScript}`);\n console.log(` ${scripts.planJudgeScript}`);\n console.log(` ${scripts.agentJudgeScript}`);\n console.log(` ${scripts.stopSummaryScript}`);\n console.log(` ${scripts.sessionStartScript}`);\n console.log(` ${scripts.transcriptSyncScript}\\n`);\n\n // Kill any stale legacy Python grader daemons so they don't keep handling\n // requests after we've switched to the local-CC channel path.\n for (const mode of ['edit', 'bash']) {\n const pidFile = join(SYNKRO_DIR, 'daemon', mode, 'daemon.pid');\n try {\n const pid = parseInt(readFileSync(pidFile, 'utf-8').trim(), 10);\n if (pid > 0) {\n process.kill(pid, 'SIGTERM');\n console.log(`Stopped stale ${mode} grader daemon (pid ${pid})`);\n }\n } catch {}\n }\n\n\n // 3b. Transcript consent — ask whether the user wants session transcripts\n // used for generating guardrail rules. If declined, we skip transcript\n // ingestion (Steps 7/7b) and don't install the incremental sync hook.\n let transcriptConsent = true;\n if (process.stdin.isTTY) {\n transcriptConsent = await promptTranscriptConsent();\n if (transcriptConsent) {\n console.log(' ✓ Transcript collection enabled\\n');\n } else {\n console.log(' ✗ Transcript collection disabled — skipping transcript sync\\n');\n }\n }\n\n // 4. Configure CC hooks (atomic merge into settings.json).\n // Edit/Write/MultiEdit/NotebookEdit fires a thin command shim that POSTs\n // proposed content to /api/v1/precheck-edit. Server cosines the content\n // against the org's active agent_runtime rules and returns the deterministic\n // CC-hook JSON (deny + retry guidance, or empty allow). No LLM in the hook\n // path — agent retries with a safer version on its own inference when it\n // reads the denial reason.\n let hasClaudeCode = false;\n let hasCursor = false;\n for (const agent of agents) {\n if (agent.kind === 'claude_code') {\n hasClaudeCode = true;\n installCCHooks(agent.settingsPath, {\n bashJudgeScriptPath: scripts.bashScript,\n bashFollowupScriptPath: scripts.bashFollowupScript,\n editPrecheckScriptPath: scripts.editPrecheckScript,\n cwePrecheckScriptPath: scripts.cwePrecheckScript,\n cvePrecheckScriptPath: scripts.cvePrecheckScript,\n planJudgeScriptPath: scripts.planJudgeScript,\n agentJudgeScriptPath: scripts.agentJudgeScript,\n stopSummaryScriptPath: scripts.stopSummaryScript,\n sessionStartScriptPath: scripts.sessionStartScript,\n transcriptSyncScriptPath: scripts.transcriptSyncScript,\n userPromptSubmitScriptPath: scripts.userPromptSubmitScript,\n skipTranscriptSync: !transcriptConsent,\n });\n console.log(`Configured ${agent.name} hooks at ${agent.settingsPath}`);\n } else if (agent.kind === 'cursor') {\n hasCursor = true;\n installCursorHooks(agent.settingsPath, {\n bashJudgeScriptPath: scripts.cursorBashJudgeScript,\n editCaptureScriptPath: scripts.cursorEditCaptureScript,\n bashFollowupScriptPath: scripts.bashFollowupScript,\n editPrecheckScriptPath: scripts.editPrecheckScript,\n cwePrecheckScriptPath: scripts.cwePrecheckScript,\n cvePrecheckScriptPath: scripts.cvePrecheckScript,\n planJudgeScriptPath: scripts.planJudgeScript,\n agentJudgeScriptPath: scripts.agentJudgeScript,\n stopSummaryScriptPath: scripts.stopSummaryScript,\n sessionStartScriptPath: scripts.sessionStartScript,\n userPromptSubmitScriptPath: scripts.userPromptSubmitScript,\n transcriptSyncScriptPath: scripts.transcriptSyncScript,\n });\n console.log(`Configured ${agent.name} hooks at ${agent.settingsPath}`);\n }\n }\n console.log();\n\n // 5b. Fetch user profile early — we need captureDepth to decide local vs cloud MCP.\n let userId: string | undefined;\n let orgId: string | undefined;\n let email: string | undefined;\n try {\n const info = getUserInfo();\n userId = info.id;\n orgId = info.org_id;\n email = info.email;\n } catch {\n // unreachable — we just authenticated above\n }\n const profile = await fetchUserProfile(gatewayUrl, token);\n const useLocalMcp = profile.captureDepth === 'local_only' || profile.localInference;\n\n // 5c. Register the Synkro Guardrails MCP server in ~/.claude.json.\n // Local mode: point to localhost:8931, start local server, backfill rules.\n // Cloud mode: mint long-lived JWT, point to cloud endpoint.\n if (hasClaudeCode && !opts.noMcp) {\n if (useLocalMcp) {\n try {\n const mcp = installMcpConfig({ gatewayUrl, bearerToken: '', local: true });\n console.log(`Registered local MCP guardrails server in ${mcp.path}`);\n console.log(` url: ${mcp.url}`);\n console.log(' (rules stored in ~/.synkro/rules.json)');\n console.log();\n\n await backfillLocalRules(gatewayUrl, token);\n await startLocalMcpServer();\n } catch (err) {\n console.warn(` ⚠ Local MCP setup failed: ${(err as Error).message}`);\n console.warn(' Hooks are still installed. Re-run `synkro-cli install` to retry.');\n console.log();\n }\n } else {\n try {\n const mintResp = await fetch(`${gatewayUrl}/api/v1/cli/mcp-token`, {\n method: 'POST',\n headers: {\n 'Authorization': `Bearer ${token}`,\n 'Content-Type': 'application/json',\n },\n body: '{}',\n });\n if (!mintResp.ok) {\n const errText = await mintResp.text().catch(() => '');\n throw new Error(`mcp-token mint failed (${mintResp.status}): ${errText.slice(0, 200)}`);\n }\n const minted = await mintResp.json() as { token: string; expires_at: string };\n const mcp = installMcpConfig({ gatewayUrl, bearerToken: minted.token });\n console.log(`Registered Synkro guardrails MCP server in ${mcp.path}`);\n console.log(` url: ${mcp.url}`);\n console.log(` expires: ${minted.expires_at} (~1 year)`);\n console.log(' (restart any running Claude Code session for it to load)');\n console.log();\n } catch (err) {\n console.warn(` ⚠ MCP registration failed: ${(err as Error).message}`);\n console.warn(' Hooks are still installed. Re-run `synkro-cli install` to retry MCP setup.');\n console.log();\n }\n }\n }\n\n // 5d. Register the Synkro Guardrails MCP server in ~/.cursor/mcp.json.\n if (hasCursor && !opts.noMcp) {\n try {\n if (useLocalMcp) {\n const mcp = installCursorMcpConfig({ gatewayUrl, bearerToken: '', local: true });\n console.log(`Registered local MCP guardrails server in ${mcp.path}`);\n console.log(` url: ${mcp.url}`);\n } else {\n const mintResp = await fetch(`${gatewayUrl}/api/v1/cli/mcp-token`, {\n method: 'POST',\n headers: {\n 'Authorization': `Bearer ${token}`,\n 'Content-Type': 'application/json',\n },\n body: '{}',\n });\n if (!mintResp.ok) {\n const errText = await mintResp.text().catch(() => '');\n throw new Error(`mcp-token mint failed (${mintResp.status}): ${errText.slice(0, 200)}`);\n }\n const minted = await mintResp.json() as { token: string; expires_at: string };\n const mcp = installCursorMcpConfig({ gatewayUrl, bearerToken: minted.token });\n console.log(`Registered Synkro guardrails MCP server in ${mcp.path}`);\n console.log(` url: ${mcp.url}`);\n }\n console.log();\n } catch (err) {\n console.warn(` ⚠ Cursor MCP registration failed: ${(err as Error).message}`);\n console.log();\n }\n }\n\n // 6. Write config.env — hook scripts read SYNKRO_GATEWAY_URL and the\n // credentials path. They auth via Bearer JWT (resolved from the\n // credentials file at runtime so refreshes Just Work).\n const priorLocalFlag = (() => {\n try {\n const content = readFileSync(CONFIG_PATH, 'utf-8');\n return content.includes(\"SYNKRO_LOCAL_INFERENCE='yes'\");\n } catch { return false; }\n })();\n const synkroBundle = resolveSynkroBundle();\n writeConfigEnv({ gatewayUrl, userId, orgId, email, tier: profile.tier, inference: profile.inference, synkroBin: synkroBundle, transcriptConsent, localInference: profile.localInference });\n console.log(`Wrote config to ${CONFIG_PATH}`);\n console.log(` inference: ${profile.inference} (server-side grading)`);\n if (profile.localInference) console.log(` local inference: enabled (gradingProvider=claude-code)`);\n if (synkroBundle) console.log(` SYNKRO_CLI_BIN=${synkroBundle}`);\n else console.warn(' ⚠ Could not resolve synkro bundle path; hooks will fall back to PATH lookup of `synkro`.');\n\n try {\n const prompts = await fetchJudgePrompts({ gatewayUrl, jwt: token });\n console.log(` prompts: ${prompts.version} (live)`);\n } catch (err) {\n console.warn(` ⚠ Could not cache judge prompts: ${(err as Error).message}`);\n }\n console.log();\n\n // Always install local-cc dependencies (tmux, pueue, bun) so `synkro local-cc enable`\n // works without a second install. On macOS these auto-install via brew.\n console.log('Setting up local-CC dependencies...');\n const localCcDeps: string[] = [];\n try {\n assertClaudeInstalled();\n localCcDeps.push('claude');\n } catch (err) {\n console.warn(` ⚠ claude: ${(err as Error).message}`);\n }\n try {\n assertPueueInstalled();\n localCcDeps.push('pueue');\n } catch (err) {\n console.warn(` ⚠ pueue: ${(err as Error).message}`);\n }\n try {\n assertTmuxInstalled();\n localCcDeps.push('tmux');\n } catch (err) {\n console.warn(` ⚠ tmux: ${(err as Error).message}`);\n }\n if (localCcDeps.length === 3) {\n console.log(` ✓ All local-CC dependencies ready (${localCcDeps.join(', ')})`);\n } else {\n console.warn(` ⚠ Some dependencies missing — \\`synkro local-cc enable\\` may not work until they're installed.`);\n }\n console.log();\n\n if (profile.localInference && localCcDeps.length === 3) {\n try {\n // Stop any running channels BEFORE overwriting plugin files — bun install\n // in the session dir corrupts node_modules under a live process.\n stopPueueTask();\n stopPueueTask(CHANNEL_SECONDARY);\n\n const r = installLocalCC();\n console.log(`Installed local-CC channel plugin at ${r.pluginPath}`);\n\n // Force-restart both channels — installLocalCC() just overwrote plugin\n // files, so any previously running channel is stale.\n const t1 = startPueueTask();\n const t2 = startPueueTask({ channel: CHANNEL_SECONDARY });\n console.log(`Channel 1 (org rules): pueue id=${t1.id} status=${t1.status}`);\n console.log(`Channel 2 (CWE scan): pueue id=${t2.id} status=${t2.status}`);\n\n // Wait for both channels in parallel\n console.log('Waiting for both channels (up to 60s)...');\n const [ready1, ready2] = await Promise.all([\n waitForChannelReady(CHANNEL_PORT, 60_000, CHANNEL_HOST),\n waitForChannelReady(CHANNEL_2_PORT, 60_000, CHANNEL_HOST, CHANNEL_SECONDARY.tmuxSession),\n ]);\n\n if (ready1) {\n console.log(` channel 1 ready at ${CHANNEL_HOST}:${CHANNEL_PORT}`);\n } else {\n console.warn(` ⚠ Channel 1 did not come up within 60s.`);\n printChannelDiagnostics();\n }\n\n if (ready2) {\n console.log(` channel 2 ready at ${CHANNEL_HOST}:${CHANNEL_2_PORT}`);\n } else {\n console.warn(` ⚠ Channel 2 did not come up within 60s.`);\n console.warn(` Run \\`synkro local-cc status\\` to debug.`);\n }\n\n // Warm both channels in parallel\n if (ready1 || ready2) {\n console.log('Warming up inference...');\n const warmups: Promise<void>[] = [];\n if (ready1) {\n warmups.push(\n submitToChannel('grade-bash', 'Proposed command: echo hello\\nUser intent: warmup\\nRecent user messages: []\\nRecent actions: []\\nOrg rules: []\\n', { timeoutMs: 30_000 })\n .then(() => { console.log(' channel 1 warm'); })\n .catch(() => { console.log(' channel 1 warmup skipped (non-fatal)'); }),\n );\n }\n if (ready2) {\n warmups.push(\n submitToChannel('grade-cwe', 'File: /tmp/warmup.ts\\nContent (first 4000 chars):\\nconsole.log(\"hello\");\\n\\nCWE rules to check against:\\n[]\\n', { timeoutMs: 30_000, port: CHANNEL_2_PORT })\n .then(() => { console.log(' channel 2 warm'); })\n .catch(() => { console.log(' channel 2 warmup skipped (non-fatal)'); }),\n );\n }\n await Promise.all(warmups);\n console.log();\n }\n } catch (err) {\n console.warn(` ⚠ Local-CC setup failed: ${(err as Error).message}\\n`);\n }\n }\n\n // 7. Ingest CC session transcripts (only if user consented).\n if (transcriptConsent) {\n try {\n const repo = detectGitRepo();\n if (repo) {\n const ingested = await ingestSessionTranscripts(gatewayUrl, token, repo);\n if (ingested > 0) {\n console.log(`Indexed ${ingested} session insights from Claude Code history for ${repo}.`);\n console.log(' This helps the safety judge understand your workflow.\\n');\n }\n }\n } catch (err) {\n console.warn(` ⚠ Session indexing skipped: ${(err as Error).message}\\n`);\n }\n\n // 7b. Bulk sync CC session transcripts into agent_sessions/agent_messages.\n try {\n const repo = detectGitRepo();\n if (repo) {\n const result = await syncTranscriptsBulk(gatewayUrl, token, repo);\n if (result.messages > 0) {\n console.log(`Synced ${result.sessions} sessions (${result.messages} messages) from Claude Code history.`);\n console.log(' This data will be used to suggest guardrail rules.\\n');\n }\n }\n } catch (err) {\n console.warn(` ⚠ Transcript sync skipped: ${(err as Error).message}\\n`);\n }\n }\n\n // 8. PR scan setup (secrets + workflow) — only if GitHub was connected in step 1b\n if (ghToken) {\n const { setupGithubCommand } = await import('./setupGithub.js');\n await setupGithubCommand({ nonInteractive: true, githubToken: ghToken });\n }\n\n // 9. Done\n console.log('✓ Synkro installed.');\n}\n\nfunction detectGitRepo(): string | null {\n try {\n const remoteUrl = execSync('git remote get-url origin', { encoding: 'utf-8', timeout: 5000 }).trim();\n const sshMatch = remoteUrl.match(/^git@[^:]+:(.+?)(?:\\.git)?$/);\n const httpMatch = remoteUrl.match(/^https?:\\/\\/[^/]+\\/(.+?)(?:\\.git)?$/);\n const match = sshMatch || httpMatch;\n return match ? match[1] : null;\n } catch {\n return null;\n }\n}\n\nfunction getClaudeProjectsFolder(): string | null {\n const cwd = process.cwd();\n // CC stores transcripts in ~/.claude/projects/{sanitized-path}/\n // where sanitized-path replaces / with -\n const sanitized = '-' + cwd.replace(/\\//g, '-');\n const projectsDir = join(homedir(), '.claude', 'projects', sanitized);\n return existsSync(projectsDir) ? projectsDir : null;\n}\n\ninterface SessionInsight {\n session_id: string;\n insight_type: 'summary' | 'user_message';\n content: string;\n metadata?: Record<string, unknown>;\n}\n\nfunction extractSessionInsights(projectsDir: string): SessionInsight[] {\n const insights: SessionInsight[] = [];\n const files = readdirSync(projectsDir).filter(f => f.endsWith('.jsonl'));\n\n for (const file of files) {\n const sessionId = file.replace('.jsonl', '');\n const filePath = join(projectsDir, file);\n\n try {\n const content = readFileSync(filePath, 'utf-8');\n const lines = content.split('\\n').filter(Boolean);\n\n // Extract compaction summaries\n for (let i = 0; i < lines.length; i++) {\n try {\n const entry = JSON.parse(lines[i]);\n if (entry.type === 'user' &&\n typeof entry.message?.content === 'string' &&\n entry.message.content.startsWith('This session is being continued')) {\n insights.push({\n session_id: sessionId,\n insight_type: 'summary',\n content: entry.message.content.slice(0, 4000),\n metadata: { source: 'compaction_summary' },\n });\n }\n } catch {}\n }\n\n // Extract user messages (last 20 per session — recent preferences)\n const userMessages: string[] = [];\n for (let i = lines.length - 1; i >= 0 && userMessages.length < 20; i--) {\n try {\n const entry = JSON.parse(lines[i]);\n if (entry.type === 'user') {\n const text = typeof entry.message?.content === 'string'\n ? entry.message.content\n : Array.isArray(entry.message?.content)\n ? entry.message.content.map((b: any) => b.text ?? b).filter((t: any) => typeof t === 'string').join(' ')\n : null;\n if (text && text.length > 10 && text.length < 2000 && !text.startsWith('This session is being continued')) {\n userMessages.push(text);\n }\n }\n } catch {}\n }\n for (const msg of userMessages.reverse()) {\n insights.push({\n session_id: sessionId,\n insight_type: 'user_message',\n content: msg.slice(0, 2000),\n });\n }\n } catch {}\n }\n\n return insights;\n}\n\nasync function ingestSessionTranscripts(gatewayUrl: string, token: string, repo: string): Promise<number> {\n const projectsDir = getClaudeProjectsFolder();\n if (!projectsDir) return 0;\n\n const insights = extractSessionInsights(projectsDir);\n if (insights.length === 0) return 0;\n\n console.log(`Found ${insights.length} session insights from Claude Code history...`);\n\n // Send in batches of 100\n let total = 0;\n for (let i = 0; i < insights.length; i += 100) {\n const batch = insights.slice(i, i + 100);\n try {\n const resp = await fetch(`${gatewayUrl}/api/v1/cli/ingest-sessions`, {\n method: 'POST',\n headers: {\n 'Authorization': `Bearer ${token}`,\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({ repo, sessions: batch }),\n });\n if (resp.ok) {\n const result = await resp.json() as { accepted: number };\n total += result.accepted;\n }\n } catch {}\n }\n\n return total;\n}\n\ninterface TranscriptMessage {\n message_index: number;\n type: 'user' | 'assistant';\n content: string;\n tool_calls?: Array<{ name: string; input: string; id: string }>;\n model?: string;\n usage?: {\n input_tokens?: number;\n output_tokens?: number;\n cache_creation_input_tokens?: number;\n cache_read_input_tokens?: number;\n };\n}\n\nfunction extractTextContent(content: any): string {\n if (typeof content === 'string') return content.slice(0, 8000);\n if (Array.isArray(content)) {\n return content\n .filter((b: any) => typeof b === 'string' || (b?.type === 'text'))\n .map((b: any) => typeof b === 'string' ? b : (b?.text || ''))\n .join(' ')\n .slice(0, 8000);\n }\n return '';\n}\n\nfunction parseTranscriptFile(filePath: string): TranscriptMessage[] {\n const content = readFileSync(filePath, 'utf-8');\n const lines = content.split('\\n').filter(Boolean);\n const messages: TranscriptMessage[] = [];\n\n for (let i = 0; i < lines.length; i++) {\n try {\n const entry = JSON.parse(lines[i]);\n if (entry.type !== 'user' && entry.type !== 'assistant') continue;\n\n const msg: TranscriptMessage = {\n message_index: i,\n type: entry.type,\n content: extractTextContent(entry.message?.content),\n };\n\n if (entry.type === 'assistant') {\n if (Array.isArray(entry.message?.content)) {\n const toolCalls = entry.message.content\n .filter((b: any) => b?.type === 'tool_use')\n .map((b: any) => ({\n name: b.name || '',\n input: JSON.stringify(b.input || {}).slice(0, 500),\n id: b.id || '',\n }));\n if (toolCalls.length > 0) msg.tool_calls = toolCalls;\n }\n if (entry.message?.model) msg.model = entry.message.model;\n if (entry.message?.usage) {\n msg.usage = {\n input_tokens: entry.message.usage.input_tokens,\n output_tokens: entry.message.usage.output_tokens,\n cache_creation_input_tokens: entry.message.usage.cache_creation_input_tokens,\n cache_read_input_tokens: entry.message.usage.cache_read_input_tokens,\n };\n }\n }\n\n if (msg.content.length > 0) messages.push(msg);\n } catch {}\n }\n\n return messages;\n}\n\nasync function syncTranscriptsBulk(gatewayUrl: string, token: string, repo: string): Promise<{ sessions: number; messages: number }> {\n const projectsDir = getClaudeProjectsFolder();\n if (!projectsDir) return { sessions: 0, messages: 0 };\n\n const files = readdirSync(projectsDir).filter(f => f.endsWith('.jsonl'));\n if (files.length === 0) return { sessions: 0, messages: 0 };\n\n console.log(`Found ${files.length} CC session transcripts, syncing...`);\n\n const maxMessagesPerSession = 500;\n let totalSessions = 0;\n let totalMessages = 0;\n\n // Batch sessions into groups of 5\n for (let i = 0; i < files.length; i += 5) {\n const batch = files.slice(i, i + 5);\n const sessions: Array<{ cc_session_id: string; messages: TranscriptMessage[] }> = [];\n\n for (const file of batch) {\n const sessionId = file.replace('.jsonl', '');\n const filePath = join(projectsDir, file);\n\n try {\n const allMessages = parseTranscriptFile(filePath);\n const messages = allMessages.length > maxMessagesPerSession\n ? allMessages.slice(-maxMessagesPerSession)\n : allMessages;\n\n if (messages.length > 0) {\n sessions.push({ cc_session_id: sessionId, messages });\n }\n } catch {}\n }\n\n if (sessions.length === 0) continue;\n\n try {\n const resp = await fetch(`${gatewayUrl}/api/v1/cli/sync-transcripts`, {\n method: 'POST',\n headers: {\n 'Authorization': `Bearer ${token}`,\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({ repo, sessions }),\n });\n if (resp.ok) {\n const result = await resp.json() as { accepted: number; sessions: number };\n totalMessages += result.accepted;\n totalSessions += result.sessions;\n }\n } catch {}\n\n // Write offset files so the Stop hook doesn't re-send\n for (const file of batch) {\n const sessionId = file.replace('.jsonl', '');\n const filePath = join(projectsDir, file);\n try {\n const content = readFileSync(filePath, 'utf-8');\n const lineCount = content.split('\\n').filter(Boolean).length;\n writeFileSync(join(OFFSETS_DIR, sessionId), String(lineCount), 'utf-8');\n } catch {}\n }\n }\n\n return { sessions: totalSessions, messages: totalMessages };\n}\n","/**\n * synkro login — browser OAuth via WorkOS (reuses existing auth flow).\n */\nimport { authenticate, isAuthenticated, getUserInfo } from '../auth/stub.js';\n\nexport async function loginCommand(args: string[] = []): Promise<void> {\n const force = args.includes('--force') || args.includes('-f');\n if (isAuthenticated() && !force) {\n const info = getUserInfo();\n console.log(`Already authenticated as ${info?.email ?? 'unknown'}.`);\n console.log('Use --force to re-authenticate.');\n return;\n }\n console.log('Opening browser for Synkro login...');\n const result = await authenticate((status) => {\n switch (status.phase) {\n case 'starting': console.log(' Starting local callback server...'); break;\n case 'browser-opened': console.log(` Browser opened: ${status.url}`); break;\n case 'waiting': console.log(' Waiting for browser auth to complete...'); break;\n case 'success': console.log(' ✓ Authenticated'); break;\n case 'error': console.error(` ✗ ${status.message}`); break;\n }\n });\n if (!result) {\n console.error('Login failed. Make sure the Synkro web app is running.');\n process.exit(1);\n }\n const info = getUserInfo();\n console.log(`✓ Logged in as ${info?.email ?? 'unknown'}.`);\n}\n","/**\n * synkro logout — clear local credentials.\n */\nimport { isAuthenticated, clearCredentials } from '../auth/stub.js';\n\nexport function logoutCommand(): void {\n if (!isAuthenticated()) {\n console.log('Not authenticated.');\n return;\n }\n clearCredentials();\n console.log('Logged out.');\n}\n","// :)\n/**\n * synkro status — show current setup state.\n */\nimport { existsSync, readFileSync } from 'node:fs';\nimport { homedir } from 'node:os';\nimport { join } from 'node:path';\nimport { isAuthenticated, getUserInfo, getAccessToken, ensureValidToken } from '../auth/stub.js';\nimport { detectAgents } from '../installer/agentDetect.js';\nimport { inspectCCHooks } from '../installer/ccHookConfig.js';\nimport { inspectCursorHooks } from '../installer/cursorHookConfig.js';\nimport { inspectMcpConfig } from '../installer/mcpConfig.js';\nimport { findTask, CHANNEL_PRIMARY, CHANNEL_SECONDARY } from '../local-cc/pueue.js';\n\nconst SYNKRO_DIR = join(homedir(), '.synkro');\nconst CONFIG_PATH = join(SYNKRO_DIR, 'config.env');\n\nfunction readConfigEnv(): Record<string, string> {\n if (!existsSync(CONFIG_PATH)) return {};\n const out: Record<string, string> = {};\n const raw = readFileSync(CONFIG_PATH, 'utf-8');\n for (const line of raw.split('\\n')) {\n const trimmed = line.trim();\n if (!trimmed || trimmed.startsWith('#')) continue;\n const eq = trimmed.indexOf('=');\n if (eq > 0) {\n const k = trimmed.slice(0, eq).trim();\n const v = trimmed.slice(eq + 1).trim();\n out[k] = v;\n }\n }\n return out;\n}\n\nexport async function statusCommand(): Promise<void> {\n console.log('Synkro CLI status\\n');\n\n // Auth\n if (isAuthenticated()) {\n const info = getUserInfo();\n console.log(`Authentication: ✓ logged in as ${info?.email ?? 'unknown'}`);\n if (info?.org_id) console.log(` org_id: ${info.org_id}`);\n if (info?.id) console.log(` user_id: ${info.id}`);\n } else {\n console.log('Authentication: ✗ not logged in (run: synkro-cli login)');\n }\n console.log();\n\n // Config — fetch live profile from server\n const config = readConfigEnv();\n const gatewayUrl = (config.SYNKRO_GATEWAY_URL || 'https://api.synkro.sh').replace(/^['\"]|['\"]$/g, '');\n let serverTier = config.SYNKRO_TIER || '(unset)';\n let serverInference = config.SYNKRO_INFERENCE || '(unset)';\n let localInference = false;\n await ensureValidToken();\n const token = getAccessToken();\n if (token) {\n try {\n const resp = await fetch(`${gatewayUrl}/api/v1/cli/me`, {\n headers: { 'Authorization': `Bearer ${token}` },\n signal: AbortSignal.timeout(5000),\n });\n if (resp.ok) {\n const data = await resp.json() as { fast_inference?: boolean; local_inference?: boolean; plan_tier?: string; tier?: string };\n serverInference = data.fast_inference ? 'fast' : 'standard';\n localInference = !!data.local_inference;\n serverTier = data.plan_tier ?? data.tier ?? serverTier;\n }\n } catch {}\n }\n\n console.log('Config:');\n console.log(` gateway: ${gatewayUrl}`);\n console.log(` credentials: ${config.SYNKRO_CREDENTIALS_PATH ?? '(unset)'}`);\n console.log(` tier: ${serverTier}`);\n const inferenceLabel = localInference\n ? \"'local' (grading via Claude Code channels)\"\n : \"'server' (grading via provider keys)\";\n console.log(` inference: ${inferenceLabel}`);\n console.log(` version: ${config.SYNKRO_VERSION ?? '(unset)'}`);\n console.log();\n\n // Agents\n const agents = detectAgents();\n console.log('Detected agents:');\n if (agents.length === 0) {\n console.log(' (none — install Claude Code first)');\n } else {\n for (const a of agents) {\n console.log(` ✓ ${a.name}${a.version ? ` (${a.version})` : ''}`);\n console.log(` settings: ${a.settingsPath}`);\n if (a.kind === 'claude_code') {\n const hooks = inspectCCHooks(a.settingsPath);\n console.log(` hooks installed: ${hooks.installed ? '✓' : '✗'}`);\n if (hooks.installed) {\n console.log(` • PreToolUse Bash: ${hooks.preToolUseBash ? '✓' : '✗'}`);\n console.log(` • PreToolUse Edit: ${hooks.postToolUseEdit ? '✓' : '✗'}`);\n console.log(` • SessionEnd summary: ${hooks.sessionEnd ? '✓' : '✗'}`);\n console.log(` • SessionStart: ${hooks.sessionStart ? '✓' : '✗'}`);\n }\n } else if (a.kind === 'cursor') {\n const hooks = inspectCursorHooks(a.settingsPath);\n console.log(` hooks installed: ${hooks.installed ? '✓' : '✗'}`);\n if (hooks.installed) {\n console.log(` • sessionStart: ${hooks.sessionStart ? '✓' : '✗'}`);\n console.log(` • sessionEnd: ${hooks.sessionEnd ? '✓' : '✗'}`);\n console.log(` • beforeSubmitPrompt: ${hooks.beforeSubmitPrompt ? '✓' : '✗'}`);\n console.log(` • beforeShellExecution: ${hooks.beforeShellExecution ? '✓' : '✗'}`);\n console.log(` • afterShellExecution: ${hooks.afterShellExecution ? '✓' : '✗'}`);\n console.log(` • PreToolUse Bash: ${hooks.preToolUseBash ? '✓' : '✗'}`);\n console.log(` • PreToolUse Edit: ${hooks.preToolUseEdit ? '✓' : '✗'}`);\n console.log(` • PreToolUse CWE: ${hooks.preToolUseCwe ? '✓' : '✗'}`);\n console.log(` • PreToolUse CVE: ${hooks.preToolUseCve ? '✓' : '✗'}`);\n console.log(` • PreToolUse Agent: ${hooks.preToolUseAgent ? '✓' : '✗'}`);\n console.log(` • PreToolUse Plan: ${hooks.preToolUsePlan ? '✓' : '✗'}`);\n console.log(` • afterFileEdit: ${hooks.afterFileEdit ? '✓' : '✗'}`);\n console.log(` • postToolUse: ${hooks.postToolUse ? '✓' : '✗'}`);\n console.log(` • stop (transcript): ${hooks.stop ? '✓' : '✗'}`);\n }\n }\n }\n }\n console.log();\n\n // Hook scripts — CC and Cursor hooks are TypeScript (bun); _synkro-common.sh is legacy CC fallback only\n const HOOKS_DIR = join(SYNKRO_DIR, 'hooks');\n const ccHooks = [\n 'cc-bash-judge.ts',\n 'cc-bash-followup.ts',\n 'cc-edit-precheck.ts',\n 'cc-cwe-precheck.ts',\n 'cc-cve-precheck.ts',\n 'cc-plan-judge.ts',\n 'cc-agent-judge.ts',\n 'cc-stop-summary.ts',\n 'cc-session-start.ts',\n 'cc-transcript-sync.ts',\n 'cc-user-prompt-submit.ts',\n '_synkro-common.ts',\n ];\n const cursorHooks = [\n 'cursor-bash-judge.ts',\n 'cursor-edit-capture.ts',\n 'cc-edit-precheck.ts',\n 'cc-cwe-precheck.ts',\n 'cc-cve-precheck.ts',\n 'cc-agent-judge.ts',\n 'cc-plan-judge.ts',\n 'cc-session-start.ts',\n 'cc-stop-summary.ts',\n 'cc-bash-followup.ts',\n 'cc-user-prompt-submit.ts',\n 'cc-transcript-sync.ts',\n '_synkro-common.ts',\n ];\n console.log('Hook scripts (Claude Code):');\n for (const f of ccHooks) {\n const p = join(HOOKS_DIR, f);\n console.log(` ${existsSync(p) ? '✓' : '✗'} ${p}`);\n }\n console.log('Hook scripts (Cursor):');\n for (const f of cursorHooks) {\n const p = join(HOOKS_DIR, f);\n console.log(` ${existsSync(p) ? '✓' : '✗'} ${p}`);\n }\n console.log();\n\n // Local-CC channels\n if (localInference) {\n console.log('Local-CC channels:');\n try {\n const t1 = findTask(CHANNEL_PRIMARY);\n if (t1) {\n console.log(` Channel 1 (org rules): pueue id=${t1.id} status=${t1.status}`);\n } else {\n console.log(' Channel 1 (org rules): not running');\n }\n const t2 = findTask(CHANNEL_SECONDARY);\n if (t2) {\n console.log(` Channel 2 (CWE scan): pueue id=${t2.id} status=${t2.status}`);\n } else {\n console.log(' Channel 2 (CWE scan): not running');\n }\n } catch {\n console.log(' (pueue not available)');\n }\n console.log();\n }\n\n // MCP guardrails server registration (CC's ~/.claude.json)\n const mcp = inspectMcpConfig();\n console.log('Guardrails MCP server (Claude Code):');\n if (mcp.installed) {\n console.log(` ✓ registered in ${mcp.configPath}`);\n console.log(` url: ${mcp.url}`);\n } else {\n console.log(` ✗ not registered (run: synkro-cli install)`);\n console.log(` expected at ${mcp.configPath} → mcpServers.synkro-guardrails`);\n }\n}\n","// :)\n/**\n * synkro link — connect repos to a Synkro project.\n *\n * Same two-path UX as install: link the local git repo or connect GitHub\n * to select from your accessible repos.\n */\nimport { isAuthenticated, ensureValidToken } from '../auth/stub.js';\nimport { promptRepoConnection } from './repoConnect.js';\n\nexport async function linkCommand(): Promise<void> {\n if (!isAuthenticated()) {\n console.error('Not authenticated. Run `synkro install` or `synkro login` first.');\n process.exit(1);\n }\n await ensureValidToken();\n await promptRepoConnection();\n}\n","// :)\n/**\n * synkro unlink — remove repo links from Synkro projects.\n *\n * Lists all linked repos across projects, lets the user pick which to remove.\n */\nimport { createInterface } from 'node:readline';\nimport { isAuthenticated, ensureValidToken } from '../auth/stub.js';\nimport { listProjects, unlinkRepo } from '../api/projects.js';\n\nfunction ask(rl: ReturnType<typeof createInterface>, question: string): Promise<string> {\n return new Promise((resolve) => rl.question(question, resolve));\n}\n\nexport async function unlinkCommand(): Promise<void> {\n if (!isAuthenticated()) {\n console.error('Not authenticated. Run `synkro install` or `synkro login` first.');\n process.exit(1);\n }\n await ensureValidToken();\n\n const projects = await listProjects();\n const linked: Array<{ projectId: string; projectName: string; repoId: string; fullName: string }> = [];\n\n for (const p of projects) {\n for (const r of (p as any).repos || []) {\n if (r.full_name && r.id) {\n linked.push({ projectId: p.id, projectName: p.name, repoId: r.id, fullName: r.full_name });\n }\n }\n }\n\n if (linked.length === 0) {\n console.log('No linked repos found.');\n return;\n }\n\n console.log('\\nLinked repos:\\n');\n linked.forEach((r, i) => {\n console.log(` ${i + 1}. ${r.fullName} (${r.projectName})`);\n });\n console.log();\n\n const rl = createInterface({ input: process.stdin, output: process.stdout });\n try {\n const selection = await ask(rl, ' Select repos to unlink (comma-separated numbers): ');\n const indices = selection\n .split(',')\n .map((s) => parseInt(s.trim(), 10) - 1)\n .filter((n) => !isNaN(n) && n >= 0 && n < linked.length);\n\n if (indices.length === 0) {\n console.log(' No repos selected.');\n return;\n }\n\n for (const idx of indices) {\n const r = linked[idx];\n await unlinkRepo(r.projectId, r.repoId);\n console.log(` ✓ Unlinked ${r.fullName} from ${r.projectName}`);\n }\n } finally {\n rl.close();\n }\n console.log();\n}\n","import { readFileSync, writeFileSync, existsSync } from 'node:fs';\nimport { join } from 'node:path';\nimport { homedir } from 'node:os';\nimport { isAuthenticated, getAccessToken } from '../auth/stub.js';\n\nconst SYNKRO_DIR = join(homedir(), '.synkro');\nconst CONFIG_PATH = join(SYNKRO_DIR, 'config.env');\n\nfunction readConfigEnv(): Record<string, string> {\n if (!existsSync(CONFIG_PATH)) return {};\n const out: Record<string, string> = {};\n for (const line of readFileSync(CONFIG_PATH, 'utf-8').split('\\n')) {\n const t = line.trim();\n if (!t || t.startsWith('#')) continue;\n const eq = t.indexOf('=');\n if (eq > 0) out[t.slice(0, eq).trim()] = t.slice(eq + 1).trim().replace(/^['\"]|['\"]$/g, '');\n }\n return out;\n}\n\nfunction updateConfigValue(key: string, value: string): void {\n if (!existsSync(CONFIG_PATH)) {\n console.error('No config found. Run `synkro install` first.');\n process.exit(1);\n }\n const lines = readFileSync(CONFIG_PATH, 'utf-8').split('\\n');\n const pattern = new RegExp(`^${key}=`);\n let found = false;\n const updated = lines.map((line) => {\n if (pattern.test(line.trim())) {\n found = true;\n return `${key}='${value}'`;\n }\n return line;\n });\n if (!found) updated.splice(updated.length - 1, 0, `${key}='${value}'`);\n writeFileSync(CONFIG_PATH, updated.join('\\n'), 'utf-8');\n}\n\nexport async function configCommand(args: string[]): Promise<void> {\n if (args.length === 0) {\n const config = readConfigEnv();\n console.log('Synkro config:\\n');\n console.log(` inference: ${config.SYNKRO_INFERENCE || 'fast'}`);\n console.log(` tier: ${config.SYNKRO_TIER || 'pro'}`);\n console.log(` gateway: ${config.SYNKRO_GATEWAY_URL || 'https://api.synkro.sh'}`);\n console.log(` version: ${config.SYNKRO_VERSION || '?'}`);\n console.log(`\\nTo change: synkro config --inference fast|standard`);\n return;\n }\n\n let inferenceValue: string | undefined;\n for (const a of args) {\n if (a.startsWith('--inference=')) inferenceValue = a.slice('--inference='.length);\n else if (a === '--inference' && args.indexOf(a) + 1 < args.length) inferenceValue = args[args.indexOf(a) + 1];\n }\n\n if (!inferenceValue || !['fast', 'standard'].includes(inferenceValue)) {\n console.error('Usage: synkro config --inference fast|standard');\n process.exit(1);\n }\n\n if (!isAuthenticated()) {\n console.error('Not authenticated. Run `synkro login` first.');\n process.exit(1);\n }\n\n const token = getAccessToken();\n const config = readConfigEnv();\n const gatewayUrl = (config.SYNKRO_GATEWAY_URL || 'https://api.synkro.sh').replace(/\\/$/, '');\n\n try {\n const resp = await fetch(`${gatewayUrl}/api/v1/cli/me`, {\n method: 'PATCH',\n headers: {\n 'Authorization': `Bearer ${token}`,\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({ fast_inference: inferenceValue === 'fast' }),\n });\n if (!resp.ok) {\n const errText = await resp.text().catch(() => '');\n console.error(`Failed to update: ${resp.status} ${errText.slice(0, 200)}`);\n process.exit(1);\n }\n } catch (err) {\n console.error(`Failed to reach server: ${(err as Error).message}`);\n process.exit(1);\n }\n\n updateConfigValue('SYNKRO_INFERENCE', inferenceValue);\n console.log(`✓ Inference set to '${inferenceValue}'.`);\n}\n","// :)\n/**\n * synkro scan-pr — runs inside a GitHub Actions runner.\n *\n * Reads PR context from env (set by the workflow YAML), fetches the PR diff\n * via `gh` CLI, spawns `claude --print` per changed file with the customer's\n * CLAUDE_CODE_OAUTH_TOKEN, aggregates findings, posts inline review comments,\n * sets a status check, and POSTs aggregate to /v1/events/pr-scan for logging.\n *\n * Auth chain (set by workflow YAML):\n * CLAUDE_CODE_OAUTH_TOKEN — long-lived OAuth token from `claude setup-token`\n * SYNKRO_API_KEY — auth for our backend\n * GH_TOKEN — GitHub-injected, for posting comments + checks\n * SYNKRO_PR_NUMBER, SYNKRO_REPO, SYNKRO_SHA — PR context\n */\nimport { execSync, spawn } from 'node:child_process';\nimport { readFileSync, existsSync } from 'node:fs';\nimport { dirname, join } from 'node:path';\n\nconst SKIP_FILE_PATTERNS = [\n /\\.lock$/i,\n /\\.min\\./i,\n /\\.map$/i,\n /^dist\\//,\n /^build\\//,\n /^vendor\\//,\n /^node_modules\\//,\n /^\\.next\\//,\n /package-lock\\.json$/,\n /yarn\\.lock$/,\n /pnpm-lock\\.yaml$/,\n /Cargo\\.lock$/,\n /go\\.sum$/,\n];\n\nconst MAX_DIFF_LINES_PER_FILE = 1000;\nconst MAX_PARALLEL_FILES = 10;\n\ninterface PrFile {\n filename: string;\n status: string; // 'added' | 'modified' | 'removed' | 'renamed'\n additions: number;\n deletions: number;\n patch?: string;\n}\n\ninterface Finding {\n file: string;\n line: number;\n severity: 'low' | 'medium' | 'high' | 'critical';\n category: string;\n description: string;\n fix: string;\n}\n\ninterface OrgRule {\n rule_id: string;\n text: string;\n category: string;\n severity: string;\n mode: 'audit' | 'literal_match';\n condition: string;\n}\n\ninterface LiteralMatchSpec {\n selector: string;\n requires: string;\n position: 'start' | 'anywhere';\n negate?: boolean;\n}\n\nfunction parseMatchSpec(condition: string): LiteralMatchSpec | null {\n if (!condition.startsWith('match_spec:')) return null;\n try {\n const parsed = JSON.parse(condition.slice('match_spec:'.length)) as Partial<LiteralMatchSpec>;\n if (\n typeof parsed?.selector !== 'string' ||\n typeof parsed?.requires !== 'string' ||\n (parsed.position !== 'start' && parsed.position !== 'anywhere')\n ) return null;\n return {\n selector: parsed.selector,\n requires: parsed.requires,\n position: parsed.position,\n negate: parsed.negate === true,\n };\n } catch {\n return null;\n }\n}\n\nfunction selectorMatches(selector: string, filePath: string): boolean {\n const m = selector.match(/^\\*\\*\\/\\*\\.([a-z0-9]+)$/i);\n if (!m) return false;\n return filePath.toLowerCase().endsWith('.' + m[1].toLowerCase());\n}\n\nasync function fetchOrgRules(gatewayUrl: string, apiKey: string): Promise<OrgRule[]> {\n try {\n const resp = await fetch(`${gatewayUrl.replace(/\\/$/, '')}/api/v1/cli/pr-rules`, {\n headers: { 'x-synkro-api-key': apiKey },\n });\n if (!resp.ok) {\n console.warn(`[scan-pr] failed to fetch org rules: HTTP ${resp.status}`);\n return [];\n }\n const data = await resp.json() as { rules?: OrgRule[] };\n return Array.isArray(data?.rules) ? data.rules : [];\n } catch (err) {\n console.warn(`[scan-pr] could not fetch org rules: ${(err as Error).message}`);\n return [];\n }\n}\n\n// Deterministic literal_match enforcement on the patch's added lines.\n// We can only check the diff (not the full file), so this catches\n// negative-form rules (\"Never use X\") cleanly. Positive-form rules\n// (\"must contain X\") need the full file content and are deferred to\n// edit-time hooks for v1 — we surface a note rather than skip silently.\nfunction applyLiteralMatchNegative(rules: OrgRule[], file: PrFile): Finding[] {\n if (!file.patch) return [];\n const findings: Finding[] = [];\n const lines = file.patch.split('\\n');\n let currentNewLine = 0;\n for (const line of lines) {\n if (line.startsWith('@@')) {\n const m = line.match(/\\+(\\d+)(?:,\\d+)?/);\n if (m) currentNewLine = parseInt(m[1], 10);\n continue;\n }\n if (line.startsWith('+++') || line.startsWith('---')) continue;\n if (!line.startsWith('+')) {\n // context or removed line: only NEW-file line counter advances on context\n if (!line.startsWith('-')) currentNewLine++;\n continue;\n }\n const addedContent = line.slice(1); // strip leading '+'\n for (const r of rules) {\n if (r.mode !== 'literal_match') continue;\n const spec = parseMatchSpec(r.condition);\n if (!spec || !spec.negate) continue; // only negative rules in PR scan v1\n if (!selectorMatches(spec.selector, file.filename)) continue;\n if (!addedContent.includes(spec.requires)) continue;\n findings.push({\n file: file.filename,\n line: currentNewLine,\n severity: (r.severity as Finding['severity']) ?? 'high',\n category: r.category || 'literal_match',\n description: r.text,\n fix: `Remove \\`${spec.requires}\\` from ${file.filename} (rule ${r.rule_id}).`,\n });\n }\n currentNewLine++;\n }\n return findings;\n}\n\nfunction buildPrPrompt(orgAuditRules: OrgRule[]): string {\n const orgRulesBlock = orgAuditRules.length === 0\n ? ''\n : `\\nORG-SPECIFIC RULES (these are the customer's policies — flag any violation found in the diff):\\n` +\n orgAuditRules\n .map((r, i) => ` ${i + 1}. [${r.severity}/${r.category}] ${r.text}`)\n .join('\\n') +\n '\\n';\n\n return `You are a security code reviewer analyzing a pull request diff for one file. Identify security issues + org-policy violations introduced by this diff.\n\nOutput ONLY a JSON object (no prose, no markdown fences):\n{\n \"findings\": [\n {\n \"line\": <int — line number in the NEW file, prefixed with L in the diff>,\n \"severity\": \"low\" | \"medium\" | \"high\" | \"critical\",\n \"category\": \"<snake_case>\",\n \"description\": \"<1 sentence>\",\n \"fix\": \"<concrete suggestion>\"\n }\n ],\n \"summary\": \"<one-line: 'X findings' or 'clean'>\"\n}\n\nBaseline security categories: hardcoded_secret, sql_injection, insecure_crypto, eval_exec, unsafe_deserialization, missing_validation, exposed_internal, missing_auth_check, cors_misconfig, path_traversal, command_injection, weak_random, broken_jwt.\n${orgRulesBlock}\nRules:\n- Only flag NEW issues (lines starting with +).\n- Use the L<num> line numbers I prefixed.\n- Be specific. If clean, return {\"findings\": [], \"summary\": \"clean\"}.\n\n`;\n}\n\nfunction shouldSkipFile(filename: string): boolean {\n return SKIP_FILE_PATTERNS.some((p) => p.test(filename));\n}\n\nfunction ghJson<T>(args: string[]): T {\n const out = execSync(`gh ${args.map((a) => `'${a.replace(/'/g, \"'\\\\''\")}'`).join(' ')}`, {\n encoding: 'utf-8',\n maxBuffer: 16 * 1024 * 1024,\n });\n return JSON.parse(out) as T;\n}\n\ninterface PrTarget {\n prNumber: number;\n sha: string;\n branched: boolean;\n prState: string;\n merged: boolean;\n}\n\nasync function ensureOpenPr(repo: string, prNumber: number, sha: string): Promise<PrTarget> {\n let pr: { state: string; merged: boolean; head: { ref: string; sha: string }; base: { ref: string }; title: string };\n try {\n pr = ghJson<typeof pr>(['api', `/repos/${repo}/pulls/${prNumber}`]);\n } catch {\n return { prNumber, sha, branched: false, prState: 'unknown', merged: false };\n }\n\n // Always scan the original PR's diff. Reviews work for open + merged PRs\n // (their diff is preserved). For closed-non-merged PRs, the line-level\n // review will 422 and postPrReview falls back to an issue comment.\n return { prNumber, sha: pr.head.sha, branched: false, prState: pr.state, merged: pr.merged };\n}\n\nfunction getPrFiles(repo: string, prNumber: number): PrFile[] {\n const data = ghJson<PrFile[]>([\n 'api',\n `/repos/${repo}/pulls/${prNumber}/files?per_page=250`,\n ]);\n return data;\n}\n\ninterface ScanContext {\n skip?: boolean;\n scan_all?: boolean;\n files?: string[];\n last_sha?: string;\n reason?: string;\n}\n\nfunction getLastReviewedSha(repo: string, prNumber: number): string | null {\n try {\n const reviews = ghJson<Array<{ body: string | null; commit_id: string; submitted_at: string }>>([\n 'api', `/repos/${repo}/pulls/${prNumber}/reviews?per_page=100`,\n ]);\n const synkro = reviews\n .filter((r) => r.body?.includes('Synkro Security Review'))\n .sort((a, b) => new Date(b.submitted_at).getTime() - new Date(a.submitted_at).getTime());\n return synkro.length > 0 ? synkro[0].commit_id : null;\n } catch {\n return null;\n }\n}\n\nfunction getChangedFilesSince(repo: string, baseSha: string, headSha: string): string[] | null {\n try {\n const data = ghJson<{ files?: Array<{ filename: string }> }>([\n 'api', `/repos/${repo}/compare/${baseSha}...${headSha}`,\n ]);\n return (data.files || []).map((f) => f.filename);\n } catch {\n return null;\n }\n}\n\nasync function fetchScanContext(gatewayUrl: string, apiKey: string, repo: string, prNumber: number, sha: string): Promise<ScanContext> {\n const lastSha = getLastReviewedSha(repo, prNumber);\n const changedFiles = lastSha && lastSha !== sha ? getChangedFilesSince(repo, lastSha, sha) : undefined;\n\n try {\n const url = `${gatewayUrl.replace(/\\/$/, '')}/api/pr-scans/scan-context`;\n const resp = await fetch(url, {\n method: 'POST',\n headers: { 'x-synkro-api-key': apiKey, 'Content-Type': 'application/json' },\n body: JSON.stringify({ sha, last_reviewed_sha: lastSha, changed_files: changedFiles }),\n signal: AbortSignal.timeout(15_000),\n });\n if (!resp.ok) return { scan_all: true };\n return await resp.json() as ScanContext;\n } catch {\n return { scan_all: true };\n }\n}\n\nfunction getFileDiffWithLines(file: PrFile): { hunks: string; newFileLineMap: Map<number, number> } {\n // For each hunk, build a map of \"position in patch\" → \"line number in NEW file\"\n // so claude can reference correct line numbers.\n if (!file.patch) return { hunks: '', newFileLineMap: new Map() };\n\n const lines = file.patch.split('\\n');\n const annotated: string[] = [];\n const lineMap = new Map<number, number>();\n let currentNewLine = 0;\n let patchIndex = 0;\n\n for (const line of lines) {\n patchIndex++;\n if (line.startsWith('@@')) {\n // Parse hunk header: @@ -A,B +C,D @@ ...\n const match = line.match(/\\+(\\d+)(?:,\\d+)?/);\n if (match) {\n currentNewLine = parseInt(match[1], 10);\n }\n annotated.push(line);\n continue;\n }\n if (line.startsWith('+') && !line.startsWith('+++')) {\n annotated.push(`L${currentNewLine}: ${line}`);\n lineMap.set(patchIndex, currentNewLine);\n currentNewLine++;\n } else if (line.startsWith('-') && !line.startsWith('---')) {\n annotated.push(line);\n // line was removed; don't increment new file line counter\n } else if (!line.startsWith('---') && !line.startsWith('+++')) {\n annotated.push(`L${currentNewLine}: ${line}`);\n currentNewLine++;\n } else {\n annotated.push(line);\n }\n }\n\n return { hunks: annotated.join('\\n'), newFileLineMap: lineMap };\n}\n\nfunction spawnClaudeJudge(file: PrFile, claudeToken: string, promptHeader: string): Promise<{ findings: Finding[]; latencyMs: number }> {\n const { hunks } = getFileDiffWithLines(file);\n const userMessage = `File: ${file.filename}\\n\\nDiff:\\n${hunks}`;\n const fullPrompt = promptHeader + userMessage;\n\n return new Promise((resolve) => {\n const t0 = Date.now();\n const proc = spawn(\n 'claude',\n ['--print', '--model', 'claude-sonnet-4-6', '--output-format', 'json', '--no-session-persistence'],\n {\n stdio: ['pipe', 'pipe', 'pipe'],\n env: {\n ...process.env,\n CLAUDE_CODE_OAUTH_TOKEN: claudeToken,\n },\n timeout: 120_000,\n },\n );\n proc.stdin.write(fullPrompt);\n proc.stdin.end();\n let stdout = '';\n let stderr = '';\n proc.stdout.on('data', (chunk) => { stdout += chunk.toString(); });\n proc.stderr.on('data', (chunk) => { stderr += chunk.toString(); });\n proc.on('close', (code) => {\n const latencyMs = Date.now() - t0;\n if (code !== 0) {\n console.warn(` claude exited ${code}: ${(stderr || stdout).slice(0, 500)}`);\n resolve({ findings: [], latencyMs });\n return;\n }\n try {\n const wrapper = JSON.parse(stdout);\n const responseText = (wrapper.result || wrapper.response || wrapper.text || '').trim();\n let txt = responseText;\n if (txt.startsWith('```')) {\n txt = txt.replace(/^```(?:json)?\\n?/, '').replace(/\\n?```\\s*$/, '').trim();\n }\n const verdict = JSON.parse(txt);\n const findings = (verdict.findings || []).map((f: any) => ({\n file: file.filename,\n line: f.line,\n severity: f.severity,\n category: f.category,\n description: f.description,\n fix: f.fix,\n })) as Finding[];\n resolve({ findings, latencyMs });\n } catch (parseErr) {\n console.warn(` failed to parse claude response: ${stdout.slice(0, 300)}`);\n resolve({ findings: [], latencyMs });\n }\n });\n });\n}\n\nasync function processInBatches<T, R>(items: T[], concurrency: number, fn: (item: T, index: number, total: number) => Promise<R>): Promise<R[]> {\n const results: R[] = new Array(items.length);\n let next = 0;\n async function worker() {\n while (next < items.length) {\n const idx = next++;\n results[idx] = await fn(items[idx], idx, items.length);\n }\n }\n await Promise.all(Array.from({ length: Math.min(concurrency, items.length) }, () => worker()));\n return results;\n}\n\ninterface ReviewComment {\n path: string;\n line: number;\n side: 'RIGHT';\n body: string;\n}\n\ninterface ConsolidatedReview {\n summary: string;\n comments: ReviewComment[];\n severity: 'critical' | 'high' | 'medium' | 'low';\n}\n\nfunction buildConsolidationPrompt(findings: Finding[]): string {\n return `You are a senior security reviewer writing the final PR review. You received raw findings from an automated detector. Your job:\n\n1. VERIFY — remove false positives or findings that are clearly wrong.\n2. CONSOLIDATE — if multiple findings describe the same underlying issue in the same file (e.g. 8 hardcoded secrets), merge them into ONE comment pinned to the first line, listing all affected lines. If findings are genuinely different issues (e.g. SQL injection on line 5 AND a hardcoded secret on line 12), keep them separate.\n3. WRITE — produce concise, actionable review comments. No fluff. Each comment should name the issue and say what to do. One sentence max for description, one for fix.\n\nOutput ONLY a JSON object (no prose, no markdown fences):\n{\n \"summary\": \"<2-3 sentence overview for the review body — total issues, severity, what to do>\",\n \"comments\": [\n {\n \"path\": \"<file path>\",\n \"line\": <integer, first affected line number — REQUIRED, never null>,\n \"body\": \"<markdown comment — include affected lines if consolidated, e.g. 'Lines 1-8: ...'>\"\n }\n ]\n}\n\nRules:\n- Each comment body should start with a severity emoji+label: 🔴 critical, 🟠 high, 🟡 medium, 🔵 low\n- If consolidating, mention all affected line numbers in the body\n- Keep comments short — developers read these in a PR, not a report\n- Maximum 15 comments. If more, pick the most critical and mention the rest in the summary.\n- If all findings are legitimate and distinct, keep them all (up to 15)\n- If you determine ALL findings are false positives, return {\"summary\": \"No issues found after verification.\", \"comments\": []}\n\nRaw findings from detector:\n${JSON.stringify(findings, null, 2)}\n`;\n}\n\nfunction spawnOpusConsolidator(findings: Finding[], claudeToken: string): Promise<ConsolidatedReview> {\n return new Promise((resolve) => {\n const prompt = buildConsolidationPrompt(findings);\n const proc = spawn(\n 'claude',\n ['--print', '--model', 'claude-opus-4-7', '--output-format', 'json', '--no-session-persistence'],\n {\n stdio: ['pipe', 'pipe', 'pipe'],\n env: {\n ...process.env,\n CLAUDE_CODE_OAUTH_TOKEN: claudeToken,\n },\n timeout: 120_000,\n },\n );\n proc.stdin.write(prompt);\n proc.stdin.end();\n let stdout = '';\n let stderr = '';\n proc.stdout.on('data', (chunk) => { stdout += chunk.toString(); });\n proc.stderr.on('data', (chunk) => { stderr += chunk.toString(); });\n proc.on('close', (code) => {\n if (code !== 0) {\n console.warn(` opus consolidation exited ${code}: ${(stderr || stdout).slice(0, 300)}`);\n // Fallback: convert raw findings to comments directly\n resolve(fallbackReview(findings));\n return;\n }\n try {\n const wrapper = JSON.parse(stdout);\n const responseText = (wrapper.result || wrapper.response || wrapper.text || '').trim();\n let txt = responseText;\n if (txt.startsWith('```')) {\n txt = txt.replace(/^```(?:json)?\\n?/, '').replace(/\\n?```\\s*$/, '').trim();\n }\n const review = JSON.parse(txt);\n const comments: ReviewComment[] = (review.comments || []).map((c: any) => ({\n path: c.path,\n line: c.line ?? 1,\n side: 'RIGHT' as const,\n body: c.body,\n }));\n const maxSeverity = findings.reduce((max, f) => {\n const order = ['low', 'medium', 'high', 'critical'];\n return order.indexOf(f.severity) > order.indexOf(max) ? f.severity : max;\n }, 'low' as string) as ConsolidatedReview['severity'];\n resolve({ summary: review.summary || '', comments, severity: maxSeverity });\n } catch {\n console.warn(` failed to parse opus response, using fallback`);\n resolve(fallbackReview(findings));\n }\n });\n });\n}\n\nfunction fallbackReview(findings: Finding[]): ConsolidatedReview {\n const grouped = new Map<string, Finding[]>();\n for (const f of findings) {\n const key = `${f.file}::${f.category}`;\n if (!grouped.has(key)) grouped.set(key, []);\n grouped.get(key)!.push(f);\n }\n const comments: ReviewComment[] = [];\n for (const [, group] of grouped) {\n const first = group[0];\n const lines = group.map((f) => f.line);\n const linesStr = lines.length > 1 ? `Lines ${lines.join(', ')}` : `Line ${lines[0]}`;\n const severityEmoji = first.severity === 'critical' ? '🔴' : first.severity === 'high' ? '🟠' : first.severity === 'medium' ? '🟡' : '🔵';\n comments.push({\n path: first.file,\n line: first.line,\n side: 'RIGHT',\n body: `${severityEmoji} **${first.severity}: ${first.category}**\\n\\n${linesStr}: ${first.description}\\n\\n**Fix:** ${first.fix}`,\n });\n }\n const maxSeverity = findings.reduce((max, f) => {\n const order = ['low', 'medium', 'high', 'critical'];\n return order.indexOf(f.severity) > order.indexOf(max) ? f.severity : max;\n }, 'low' as string) as ConsolidatedReview['severity'];\n return {\n summary: `${findings.length} security finding(s) detected.`,\n comments: comments.slice(0, 15),\n severity: maxSeverity,\n };\n}\n\nfunction postPrReview(\n repo: string,\n prNumber: number,\n sha: string,\n review: ConsolidatedReview,\n skipLineReview = false,\n): void {\n function postIssueComment(): void {\n try {\n const body = `## 🔒 Synkro Security Review\\n\\n${review.summary}\\n\\n` +\n review.comments.map((c) => `**${c.path}:${c.line}** — ${c.body}`).join('\\n\\n');\n execSync(`gh api -X POST /repos/${repo}/issues/${prNumber}/comments --input -`, {\n encoding: 'utf-8',\n input: JSON.stringify({ body }),\n stdio: ['pipe', 'ignore', 'pipe'],\n });\n console.log(' ✓ Posted issue comment with findings.');\n } catch (err) {\n console.warn('Failed to post issue comment:', (err as Error).message);\n }\n }\n\n // Closed-non-merged PRs: line-level review will 422 (head ref deleted /\n // diff missing). Go straight to issue comment.\n if (skipLineReview) {\n postIssueComment();\n return;\n }\n\n const preferredEvent = review.severity === 'critical' || review.severity === 'high' ? 'REQUEST_CHANGES' : 'COMMENT';\n\n function tryPost(event: string): boolean {\n const body = JSON.stringify({\n commit_id: sha,\n body: `## 🔒 Synkro Security Review\\n\\n${review.summary}`,\n event,\n comments: review.comments,\n });\n try {\n execSync(`gh api -X POST /repos/${repo}/pulls/${prNumber}/reviews --input -`, {\n encoding: 'utf-8',\n input: body,\n stdio: ['pipe', 'pipe', 'pipe'],\n });\n console.log(` ✓ Posted PR review (${event}).`);\n return true;\n } catch (err: any) {\n const stderr = err.stderr?.toString() || '';\n const stdout = err.stdout?.toString() || '';\n const combined = stderr + stdout;\n if (combined.includes('own pull request') && event === 'REQUEST_CHANGES') {\n return false;\n }\n return false;\n }\n }\n\n if (tryPost(preferredEvent)) return;\n if (preferredEvent === 'REQUEST_CHANGES' && tryPost('COMMENT')) return;\n\n postIssueComment();\n}\n\nfunction postCheckRun(repo: string, sha: string, conclusion: 'success' | 'failure', findings: Finding[]): void {\n const summary = findings.length === 0\n ? 'No security findings.'\n : `${findings.length} finding(s):\\n` + findings.slice(0, 20).map((f) => `- **${f.severity}**: ${f.file}:${f.line} — ${f.description}`).join('\\n');\n const body = JSON.stringify({\n name: 'Synkro Security Review',\n head_sha: sha,\n status: 'completed',\n conclusion,\n output: {\n title: findings.length === 0 ? 'No issues found' : `${findings.length} security finding(s)`,\n summary,\n },\n });\n try {\n execSync(`gh api -X POST /repos/${repo}/check-runs --input -`, {\n encoding: 'utf-8',\n input: body,\n stdio: ['pipe', 'ignore', 'pipe'],\n });\n } catch (err) {\n console.warn('Failed to post check run:', (err as Error).message);\n }\n}\n\nfunction shouldFail(findings: Finding[], threshold: 'critical' | 'high' | 'medium' | 'low'): boolean {\n const order = ['low', 'medium', 'high', 'critical'];\n const thresholdIdx = order.indexOf(threshold);\n return findings.some((f) => order.indexOf(f.severity) >= thresholdIdx);\n}\n\nfunction readRepoDeps(): Record<string, string> {\n const pkgPath = join(process.cwd(), 'package.json');\n if (!existsSync(pkgPath)) return {};\n try {\n const pkg = JSON.parse(readFileSync(pkgPath, 'utf-8'));\n return { ...(pkg.dependencies ?? {}), ...(pkg.devDependencies ?? {}) };\n } catch {\n return {};\n }\n}\n\nfunction getFullFileContent(filename: string): string | null {\n try {\n return execSync(`git show HEAD:${filename}`, { encoding: 'utf-8', maxBuffer: 128 * 1024 });\n } catch {\n return null;\n }\n}\n\nasync function scanCves(\n files: PrFile[],\n gatewayUrl: string,\n apiKey: string,\n): Promise<Finding[]> {\n const deps = readRepoDeps();\n if (Object.keys(deps).length === 0) return [];\n\n const findings: Finding[] = [];\n\n for (const file of files) {\n const content = getFullFileContent(file.filename);\n if (!content) continue;\n\n try {\n const resp = await fetch(`${gatewayUrl.replace(/\\/$/, '')}/api/v1/cve-scan`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'x-synkro-api-key': apiKey,\n },\n body: JSON.stringify({\n file_path: file.filename,\n content,\n dependencies: deps,\n }),\n signal: AbortSignal.timeout(8_000),\n });\n\n if (!resp.ok) continue;\n const data = await resp.json() as {\n findings: Array<{ package: string; id: string; summary: string; severity: string; fixed?: string }>;\n summary?: string;\n packages?: Array<{ package: string; count: number; ids: string[] }>;\n };\n\n if (!data.findings?.length) continue;\n\n for (const pkg of data.packages ?? []) {\n const maxSev = data.findings\n .filter((f) => f.package === pkg.package)\n .reduce((max, f) => {\n const n = parseFloat(f.severity);\n return !isNaN(n) && n > max ? n : max;\n }, 0);\n const severity: Finding['severity'] = maxSev >= 9 ? 'critical' : maxSev >= 7 ? 'high' : maxSev >= 4 ? 'medium' : 'low';\n const topIds = pkg.ids.slice(0, 3).join(', ');\n const extra = pkg.ids.length > 3 ? ` +${pkg.ids.length - 3} more` : '';\n\n findings.push({\n file: file.filename,\n line: 1,\n severity,\n category: 'vulnerable_dependency',\n description: `${pkg.package} has ${pkg.count} known CVEs (${topIds}${extra}).`,\n fix: data.findings.find((f) => f.package === pkg.package && f.fixed)\n ? `Upgrade ${pkg.package} to ${data.findings.find((f) => f.package === pkg.package && f.fixed)!.fixed}`\n : `Check https://osv.dev/list?q=${pkg.package} for fix versions.`,\n });\n }\n } catch {\n continue;\n }\n }\n\n return findings;\n}\n\nasync function postEventToBackend(opts: {\n gatewayUrl: string;\n apiKey: string;\n repo: string;\n prNumber: number;\n sha: string;\n findings: Finding[];\n filesScanned: number;\n totalLatencyMs: number;\n}): Promise<void> {\n try {\n await fetch(`${opts.gatewayUrl.replace(/\\/$/, '')}/api/v1/events/pr-scan`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'x-synkro-api-key': opts.apiKey,\n },\n body: JSON.stringify({\n repo: opts.repo,\n pr_number: opts.prNumber,\n sha: opts.sha,\n findings: opts.findings,\n summary: opts.findings.length === 0 ? 'clean' : `${opts.findings.length} findings`,\n files_scanned: opts.filesScanned,\n total_latency_ms: opts.totalLatencyMs,\n }),\n });\n } catch (err) {\n console.warn('Failed to log scan to Synkro backend:', (err as Error).message);\n }\n}\n\nexport async function scanPrCommand(): Promise<void> {\n const repo = process.env.SYNKRO_REPO || process.env.GITHUB_REPOSITORY || '';\n const prNumberStr = process.env.SYNKRO_PR_NUMBER || '';\n const sha = process.env.SYNKRO_SHA || process.env.GITHUB_SHA || '';\n const claudeToken = process.env.CLAUDE_CODE_OAUTH_TOKEN || '';\n const synkroApiKey = process.env.SYNKRO_API_KEY || '';\n const gatewayUrl = process.env.SYNKRO_GATEWAY_URL || 'https://api.synkro.sh';\n const failThreshold = (process.env.SYNKRO_FAIL_THRESHOLD || 'high') as 'critical' | 'high' | 'medium' | 'low';\n\n if (!repo || !prNumberStr || !sha || !claudeToken || !synkroApiKey) {\n console.error('Missing required env vars: SYNKRO_REPO, SYNKRO_PR_NUMBER, SYNKRO_SHA, CLAUDE_CODE_OAUTH_TOKEN, SYNKRO_API_KEY');\n process.exit(2);\n }\n const prNumber = parseInt(prNumberStr, 10);\n if (!prNumber) {\n console.error('SYNKRO_PR_NUMBER is not a valid number:', prNumberStr);\n process.exit(2);\n }\n\n console.log(`Synkro scan-pr: ${repo}#${prNumber} @ ${sha.slice(0, 7)}\\n`);\n\n // If the PR is closed/merged, branch off and open a fresh PR for findings\n const prTarget = await ensureOpenPr(repo, prNumber, sha);\n const activePrNumber = prTarget.prNumber;\n const activeSha = prTarget.sha;\n\n // Fetch the org's active runtime rules so we enforce them in PR review\n // — same rules that fire on edit-time hooks. Without this, scan-pr would\n // ignore every rule the customer added via create_guardrail / dashboard.\n const orgRules = await fetchOrgRules(gatewayUrl, synkroApiKey);\n const auditRules = orgRules.filter((r) => r.mode === 'audit');\n const literalNegativeRules = orgRules.filter((r) => {\n const spec = parseMatchSpec(r.condition);\n return r.mode === 'literal_match' && spec?.negate === true;\n });\n console.log(`Loaded ${orgRules.length} org rule(s): ${auditRules.length} audit, ${literalNegativeRules.length} literal_match (negative).`);\n const promptHeader = buildPrPrompt(auditRules);\n\n // Fetch PR file list (always from the original PR — that's where the diff lives)\n let files: PrFile[];\n try {\n files = getPrFiles(repo, activePrNumber);\n } catch (err) {\n console.error('Failed to fetch PR files:', (err as Error).message);\n process.exit(2);\n }\n\n // Ask the server which files need scanning (incremental dedup)\n const scanCtx = await fetchScanContext(gatewayUrl, synkroApiKey, repo, activePrNumber, activeSha);\n if (scanCtx.skip) {\n console.log(`Already scanned at ${activeSha.slice(0, 7)}, skipping.\\n`);\n postCheckRun(repo, activeSha, 'success', []);\n await postEventToBackend({\n gatewayUrl, apiKey: synkroApiKey, repo, prNumber: activePrNumber, sha: activeSha,\n findings: [], filesScanned: 0, totalLatencyMs: 0,\n });\n return;\n }\n\n const changedFiles = !scanCtx.scan_all && scanCtx.files ? new Set(scanCtx.files) : null;\n if (changedFiles) {\n console.log(`Incremental scan: ${changedFiles.size} file(s) changed since last scan (${scanCtx.last_sha?.slice(0, 7)}).\\n`);\n }\n\n // Filter\n const eligible = files.filter((f) => {\n if (f.status === 'removed') return false;\n if (shouldSkipFile(f.filename)) return false;\n if ((f.additions + f.deletions) > MAX_DIFF_LINES_PER_FILE) return false;\n if (!f.patch) return false;\n if (changedFiles && !changedFiles.has(f.filename)) return false;\n return true;\n });\n\n console.log(`${files.length} files in PR, ${eligible.length} eligible for scan.\\n`);\n\n if (eligible.length === 0) {\n postCheckRun(repo, activeSha, 'success', []);\n await postEventToBackend({\n gatewayUrl, apiKey: synkroApiKey, repo, prNumber: activePrNumber, sha: activeSha,\n findings: [], filesScanned: 0, totalLatencyMs: 0,\n });\n console.log('No eligible files. Exiting.');\n return;\n }\n\n // Scan in parallel batches. Each file gets:\n // 1. Deterministic literal_match-negative checks on the patch's added\n // lines (no LLM, no cost, fires on rules like \"Never use `useEffect`\n // in tsx files\"). Positive literal_match rules need full file content\n // and are deferred to edit-time hooks for v1.\n // 2. LLM-based audit pass for everything else, with the org's audit rules\n // injected into the prompt so customer policies actually surface.\n const t0 = Date.now();\n const cvePromise = scanCves(eligible, gatewayUrl, synkroApiKey).catch((err) => {\n console.warn('CVE scan failed (non-fatal):', (err as Error).message);\n return [] as Finding[];\n });\n const results = await processInBatches(eligible, MAX_PARALLEL_FILES, async (file, idx, total) => {\n process.stdout.write(`[${idx + 1}/${total}] ${file.filename}...`);\n const literalFindings = applyLiteralMatchNegative(literalNegativeRules, file);\n const llmResult = await spawnClaudeJudge(file, claudeToken, promptHeader);\n const merged = [...literalFindings, ...llmResult.findings];\n console.log(` ${merged.length === 0 ? 'clean' : `${merged.length} finding(s)`} (${(llmResult.latencyMs / 1000).toFixed(1)}s)`);\n return { findings: merged, latencyMs: llmResult.latencyMs };\n });\n const cveFindings = await cvePromise;\n if (cveFindings.length > 0) {\n console.log(`CVE scan: ${cveFindings.length} vulnerable dependency finding(s).`);\n }\n const totalLatencyMs = Date.now() - t0;\n\n const allFindings: Finding[] = [...results.flatMap((r) => r.findings), ...cveFindings];\n console.log(`\\nTotal: ${allFindings.length} finding(s) across ${eligible.length} file(s) in ${totalLatencyMs}ms\\n`);\n\n // Consolidate findings with Opus and post as a single review\n if (allFindings.length > 0) {\n console.log('Consolidating findings with Opus 4.7...');\n const review = await spawnOpusConsolidator(allFindings, claudeToken);\n console.log(` → ${review.comments.length} review comment(s), severity: ${review.severity}`);\n if (review.comments.length > 0) {\n const skipLineReview = prTarget.prState === 'closed' && !prTarget.merged;\n postPrReview(repo, activePrNumber, activeSha, review, skipLineReview);\n }\n }\n\n // Post status check\n const conclusion = shouldFail(allFindings, failThreshold) ? 'failure' : 'success';\n postCheckRun(repo, activeSha, conclusion, allFindings);\n\n // Log to backend\n await postEventToBackend({\n gatewayUrl, apiKey: synkroApiKey, repo, prNumber: activePrNumber, sha: activeSha,\n findings: allFindings, filesScanned: eligible.length, totalLatencyMs,\n });\n\n console.log(`\\n✓ Scan complete. Status: ${conclusion}.`);\n\n // Exit non-zero if we want to fail the workflow on findings\n if (conclusion === 'failure') {\n process.exit(1);\n }\n}\n","// :)\n/**\n * synkro update — re-runs install to pull the latest hook scripts and\n * prompts. The CLI binary itself is distributed via npm; users run\n * `npm install -g @synkro-sh/cli@latest` to upgrade it.\n */\nimport { installCommand } from './install.js';\n\nexport async function updateCommand(): Promise<void> {\n console.log('Refreshing Synkro hook configs and prompts...\\n');\n await installCommand();\n console.log('\\n✓ Synkro updated.');\n console.log('To upgrade the CLI itself, run: npm install -g @synkro-sh/cli@latest');\n}\n","// :)\n/**\n * synkro disconnect — remove all Synkro hook entries from agent settings.\n *\n * Preserves any other hooks the user has (Corridor, Noma, custom).\n * Optionally also removes ~/.synkro/ entirely if --purge flag set.\n *\n * Order matters: tear down LIVE runtime first (pueue task + tmux session +\n * spawned bun plugin), THEN strip config, THEN remove files. Reversing the\n * order leaves orphaned processes holding the TCP port and a tmux session\n * with claude running from a deleted cwd.\n */\nimport { existsSync, rmSync } from 'node:fs';\nimport { homedir } from 'node:os';\nimport { join } from 'node:path';\nimport { detectAgents } from '../installer/agentDetect.js';\nimport { uninstallCCHooks } from '../installer/ccHookConfig.js';\nimport { uninstallCursorHooks } from '../installer/cursorHookConfig.js';\nimport { uninstallMcpConfig, uninstallCursorMcpConfig } from '../installer/mcpConfig.js';\nimport { stopTask, findTask, CHANNEL_SECONDARY } from '../local-cc/pueue.js';\nimport { uninstallLocalCC } from '../local-cc/install.js';\n\nconst SYNKRO_DIR = join(homedir(), '.synkro');\n\nfunction tearDownLocalCC(): void {\n // 1. Kill tmux sessions + pueue tasks for BOTH channels (best-effort).\n let hadTask = false;\n try {\n const t1 = findTask();\n const t2 = findTask(CHANNEL_SECONDARY);\n hadTask = !!(t1 || t2);\n stopTask();\n stopTask(CHANNEL_SECONDARY);\n } catch {\n // pueue may not be installed / pueued may not be running; ignore.\n }\n console.log(`${hadTask ? '✓' : '·'} local-cc runtime: ${hadTask ? 'stopped both channels' : 'no live tasks'}`);\n\n // 2. Strip user-scope MCP entry + project-state entry from ~/.claude.json.\n uninstallLocalCC();\n console.log('✓ local-cc config: cleaned ~/.claude.json entries');\n}\n\nexport function disconnectCommand(args: string[] = []): void {\n const purge = args.includes('--purge');\n\n console.log('Synkro disconnect starting...\\n');\n\n // Tear down local-cc runtime FIRST so we don't leave orphaned processes.\n tearDownLocalCC();\n\n const agents = detectAgents();\n let sawClaudeCode = false;\n for (const agent of agents) {\n if (agent.kind === 'claude_code') {\n sawClaudeCode = true;\n const removed = uninstallCCHooks(agent.settingsPath);\n console.log(`${removed ? '✓' : '·'} ${agent.name}: ${removed ? 'removed Synkro hook entries' : 'no Synkro hooks found'}`);\n } else if (agent.kind === 'cursor') {\n const removed = uninstallCursorHooks(agent.settingsPath);\n console.log(`${removed ? '✓' : '·'} ${agent.name}: ${removed ? 'removed Synkro hook entries' : 'no Synkro hooks found'}`);\n }\n }\n\n // Also remove the Synkro guardrails MCP server entries from config files\n // (this is the OTHER mcp server — the rule-suggestions one — distinct from\n // the local-cc channel server cleaned up above).\n if (sawClaudeCode) {\n const mcpRemoved = uninstallMcpConfig();\n console.log(`${mcpRemoved ? '✓' : '·'} MCP guardrails (CC): ${mcpRemoved ? 'removed from ~/.claude.json' : 'no entry found'}`);\n }\n {\n const cursorMcpRemoved = uninstallCursorMcpConfig();\n console.log(`${cursorMcpRemoved ? '✓' : '·'} MCP guardrails (Cursor): ${cursorMcpRemoved ? 'removed from ~/.cursor/mcp.json' : 'no entry found'}`);\n }\n\n if (purge) {\n if (existsSync(SYNKRO_DIR)) {\n rmSync(SYNKRO_DIR, { recursive: true, force: true });\n console.log(`✓ Removed ${SYNKRO_DIR}`);\n } else {\n console.log(`· ${SYNKRO_DIR} already gone, nothing to remove`);\n }\n } else if (existsSync(SYNKRO_DIR)) {\n console.log(`Config preserved at ${SYNKRO_DIR}. Run with --purge to remove.`);\n }\n\n console.log('\\nSynkro disconnected.');\n}\n","// :)\n/**\n * synkro uninstall — fully remove Synkro from this machine.\n *\n * Removes all hooks from agent settings, the MCP server entry,\n * and the ~/.synkro directory (credentials, config, hook scripts, daemon).\n */\nimport { disconnectCommand } from './disconnect.js';\n\nexport function uninstallCommand(): void {\n console.log('Uninstalling Synkro...\\n');\n disconnectCommand(['--purge']);\n console.log('\\nTo reinstall later: synkro install');\n}\n","// :)\n/**\n * synkro reinstall — clean uninstall + fresh install.\n *\n * Removes all hooks, MCP config, and ~/.synkro, then runs the full\n * install flow (auth, hook scripts, MCP server, config).\n */\nimport { disconnectCommand } from './disconnect.js';\nimport { installCommand } from './install.js';\n\nexport async function reinstallCommand(): Promise<void> {\n console.log('Reinstalling Synkro...\\n');\n disconnectCommand(['--purge']);\n console.log('');\n await installCommand({ force: true });\n console.log('\\n✓ Synkro reinstalled.');\n}\n","/**\n * `synkro local-cc <subcommand>` — manage the local pueue-backed Claude Code\n * session that powers grading and intent classification when the inference\n * provider toggle is set to local-cc.\n */\n\nimport { spawnSync } from 'node:child_process';\nimport { homedir } from 'node:os';\nimport { join } from 'node:path';\nimport { installLocalCC, SESSION_DIR, PLUGIN_PATH, RUN_SCRIPT_PATH, PLUGIN_SETTINGS_PATH, CLAUDE_JSON_PATH, TMUX_SESSION_NAME, CHANNEL_2_PORT, TMUX_SESSION_NAME_2 } from '../local-cc/install.js';\nimport { readRecentTurns, followTurns, TURN_LOG_PATH, type TurnEntry } from '../local-cc/turnLog.js';\nimport {\n assertClaudeInstalled,\n assertPueueInstalled,\n assertTmuxInstalled,\n ensureRunning,\n findTask,\n startTask,\n stopTask,\n tailLogs,\n waitForChannelReady,\n CHANNEL_PRIMARY,\n CHANNEL_SECONDARY,\n} from '../local-cc/pueue.js';\nimport { isLocalCCEnabled } from '../local-cc/settings.js';\nimport { submitToChannel, isChannelAvailable, CHANNEL_HOST, CHANNEL_PORT } from '../local-cc/client.js';\nimport { getAccessToken, ensureValidToken } from '../auth/stub.js';\nimport { existsSync, readFileSync, writeFileSync } from 'node:fs';\n\nfunction printHelp(): void {\n console.log(`synkro local-cc — manage the local Claude Code inference session\n\nOVERVIEW\n Routes Synkro's grading and intent-classification work through a long-running\n Claude Code session on this machine instead of remote Inngest+Gemini.\n\n When enabled, three call sites switch over:\n • security grading on edit/bash hooks\n • intent classification (agent input → structured intent)\n • remediate intent classification\n\n The session is hosted in a detached tmux session managed by a pueue task.\n The CLI talks to it via Claude Code's channels API: a Bun MCP plugin that\n pushes events into the session and receives Claude's responses through a\n \\`reply\\` MCP tool.\n\nUSAGE\n synkro local-cc <subcommand> [args]\n\nSUBCOMMANDS\n enable Install plugin, start pueue task, flip toggle to local-cc\n disable Flip toggle back to inngest (pueue task left running)\n status Show provider toggle, pueue task state, channel reachability\n start Idempotently bring up the pueue task + wait for the channel\n stop Kill the tmux session and remove the pueue task\n restart stop, then start\n install Regenerate ~/.synkro/cc_sessions/ files (plugin, runner, settings)\n logs [N] [--raw] [--live]\n Show the last N (default 20) channel turns: when, role,\n duration, severity, request preview.\n --raw / -r include full request/response payloads\n --live / -f tail the log; print new turns as they arrive\n (Ctrl-C to exit)\n --tmux escape hatch — print the raw pueue/tmux\n pane log instead\n attach [--readonly] Attach to the tmux session hosting claude (Ctrl-B D to detach;\n --readonly / -r to attach view-only)\n test Send a smoke-test classification through the channel\n help Show this message\n\nCONFIGURATION\n Provider toggle:\n Stored server-side in your inference settings (gradingProvider).\n Toggle via: synkro local-cc enable / disable\n Or via dashboard inference settings (set grading provider to claude-code).\n\n Claude Code session settings (scoped to ~/.synkro/cc_sessions only):\n ${PLUGIN_SETTINGS_PATH}\n Currently sets {\"fastMode\": true}. Edit to add other CC settings for this\n session without affecting your other CC projects.\n\n MCP server registration (for the channel plugin):\n ${CLAUDE_JSON_PATH}\n A 'synkro-local' entry under mcpServers, pointing at:\n bun ${PLUGIN_PATH}\n Workspace trust is also pre-accepted under projects[\\`${SESSION_DIR}\\`].\n\n Channel runtime files:\n ${RUN_SCRIPT_PATH}\n bash wrapper that pueue invokes; owns the tmux session lifecycle.\n ${PLUGIN_PATH}\n Bun MCP channel plugin (auto-generated, do not edit).\n 127.0.0.1:${CHANNEL_PORT}\n Loopback TCP endpoint the CLI POSTs to in order to submit a request.\n\nENVIRONMENT VARIABLES\n SYNKRO_CHANNEL_PORT Override the TCP port used by both the plugin and\n the CLI client (loopback only). Default: 8929\n SYNKRO_CHANNEL_TIMEOUT_MS Per-request timeout inside the channel plugin\n (default: 120000)\n\nREQUIRED TOOLS\n claude Claude Code CLI, authenticated to your subscription\n pueue pueue + pueued (https://github.com/Nukesor/pueue) running\n tmux For detached pty around claude\n bun Runtime for the MCP channel plugin\n\nTROUBLESHOOTING\n • Channel unreachable after \\`enable\\`?\n synkro local-cc logs # check pueue task output\n tmux attach -t ${TMUX_SESSION_NAME} # see what claude is showing\n • Stale state after a crash:\n synkro local-cc stop && synkro local-cc start\n • Want to inspect or interact with the live session:\n synkro local-cc attach\n`);\n}\n\nconst CONFIG_PATH = join(homedir(), '.synkro', 'config.env');\n\nfunction readGatewayUrl(): string {\n if (existsSync(CONFIG_PATH)) {\n const m = readFileSync(CONFIG_PATH, 'utf-8').match(/^SYNKRO_GATEWAY_URL='([^']*)'/m);\n if (m) return m[1];\n }\n return 'https://api.synkro.sh';\n}\n\nfunction updateLocalInferenceFlag(enabled: boolean): void {\n if (!existsSync(CONFIG_PATH)) return;\n let content = readFileSync(CONFIG_PATH, 'utf-8');\n const flag = enabled ? 'yes' : 'no';\n if (content.includes('SYNKRO_LOCAL_INFERENCE=')) {\n content = content.replace(/^SYNKRO_LOCAL_INFERENCE='[^']*'/m, `SYNKRO_LOCAL_INFERENCE='${flag}'`);\n } else {\n content = content.trimEnd() + `\\nSYNKRO_LOCAL_INFERENCE='${flag}'\\n`;\n }\n writeFileSync(CONFIG_PATH, content, 'utf-8');\n}\n\nasync function setServerGradingProvider(provider: 'claude-code' | null): Promise<void> {\n await ensureValidToken();\n const jwt = getAccessToken();\n if (!jwt) throw new Error('Not authenticated. Run `synkro install` first.');\n const gatewayUrl = readGatewayUrl();\n const body = provider\n ? { roles: { grading: { provider, model: 'default' } } }\n : { roles: { grading: { provider: null, model: null } } };\n const resp = await fetch(`${gatewayUrl}/api/settings/inference?scope=user`, {\n method: 'PUT',\n headers: { 'Authorization': `Bearer ${jwt}`, 'Content-Type': 'application/json' },\n body: JSON.stringify(body),\n });\n if (!resp.ok) {\n const text = await resp.text().catch(() => '');\n throw new Error(`Failed to update inference settings: ${resp.status} ${text.slice(0, 200)}`);\n }\n}\n\nasync function cmdStatus(): Promise<void> {\n console.log(`Local inference: ${isLocalCCEnabled() ? 'enabled' : 'disabled'}`);\n try {\n assertPueueInstalled();\n } catch (err) {\n console.log(`Pueue: NOT AVAILABLE (${(err as Error).message})`);\n return;\n }\n\n // Channel 1 (judge: bash + edit, port 8929)\n const t = findTask(CHANNEL_PRIMARY);\n if (!t) {\n console.log('Channel 1 (judge) pueue task: not present');\n } else {\n console.log(`Channel 1 (judge) pueue task: id=${t.id} status=${t.status}`);\n }\n const ch1Up = await isChannelAvailable();\n console.log(`Channel 1 ${CHANNEL_HOST}:${CHANNEL_PORT}: ${ch1Up ? 'reachable' : 'unreachable'}`);\n const tmux1 = spawnSync('tmux', ['has-session', '-t', `=${TMUX_SESSION_NAME}`], { encoding: 'utf-8' });\n console.log(`tmux '${TMUX_SESSION_NAME}': ${tmux1.status === 0 ? 'live' : 'absent'}`);\n\n // Channel 2 (CWE, port 8930)\n const t2 = findTask(CHANNEL_SECONDARY);\n if (!t2) {\n console.log('Channel 2 (CWE) pueue task: not present');\n } else {\n console.log(`Channel 2 (CWE) pueue task: id=${t2.id} status=${t2.status}`);\n }\n const ch2Up = await isChannelAvailable(CHANNEL_2_PORT);\n console.log(`Channel 2 ${CHANNEL_HOST}:${CHANNEL_2_PORT}: ${ch2Up ? 'reachable' : 'unreachable'}`);\n const tmux2 = spawnSync('tmux', ['has-session', '-t', `=${TMUX_SESSION_NAME_2}`], { encoding: 'utf-8' });\n console.log(`tmux '${TMUX_SESSION_NAME_2}': ${tmux2.status === 0 ? 'live' : 'absent'}`);\n}\n\nasync function cmdEnable(): Promise<void> {\n assertClaudeInstalled();\n assertPueueInstalled();\n assertTmuxInstalled();\n console.log('Installing local-CC channel plugin...');\n const r = installLocalCC();\n console.log(` plugin: ${r.pluginPath}`);\n console.log(` cwd: ${r.sessionDir}`);\n console.log('Starting channel 1 (judge)...');\n const t1 = ensureRunning({ channel: CHANNEL_PRIMARY });\n console.log(` task: id=${t1.id} status=${t1.status}`);\n console.log('Starting channel 2 (CWE)...');\n const t2 = ensureRunning({ channel: CHANNEL_SECONDARY });\n console.log(` task: id=${t2.id} status=${t2.status}`);\n console.log('Waiting for channels (auto-confirming any CC prompts)...');\n const [ready1, ready2] = await Promise.all([\n waitForChannelReady(CHANNEL_PORT, 60_000, CHANNEL_HOST, CHANNEL_PRIMARY.tmuxSession),\n waitForChannelReady(CHANNEL_2_PORT, 60_000, CHANNEL_HOST, CHANNEL_SECONDARY.tmuxSession),\n ]);\n if (ready1) console.log(` channel 1 ready at ${CHANNEL_HOST}:${CHANNEL_PORT}`);\n else console.warn(` ⚠ channel 1 did not come up within 60s — check \\`synkro local-cc logs\\``);\n if (ready2) console.log(` channel 2 ready at ${CHANNEL_HOST}:${CHANNEL_2_PORT}`);\n else console.warn(` ⚠ channel 2 (CWE) did not come up within 60s`);\n console.log('Updating inference settings...');\n await setServerGradingProvider('claude-code');\n updateLocalInferenceFlag(true);\n console.log('Grading provider set to claude-code (local inference enabled).');\n}\n\nasync function cmdDisable(): Promise<void> {\n console.log('Updating inference settings...');\n await setServerGradingProvider(null);\n updateLocalInferenceFlag(false);\n console.log('Grading provider cleared (remote inference restored). Pueue task left running — use `synkro local-cc stop` to terminate.');\n}\n\nasync function warmChannels(ready1: boolean, ready2: boolean): Promise<void> {\n const warmups: Promise<void>[] = [];\n if (ready1) {\n warmups.push(\n submitToChannel('grade-bash', 'Proposed command: echo hello\\nUser intent: warmup\\nRecent user messages: []\\nRecent actions: []\\nOrg rules: []\\n', { timeoutMs: 30_000 })\n .then(() => console.log(' channel 1 warm.'))\n .catch(() => console.log(' channel 1 warmup skipped (non-fatal).'))\n );\n }\n if (ready2) {\n warmups.push(\n submitToChannel('grade-cwe', 'File: /tmp/warmup.ts\\nContent (first 4000 chars):\\nconsole.log(\"hello\");\\n\\nCWE rules to check against:\\n[]\\n', { timeoutMs: 30_000, port: CHANNEL_2_PORT })\n .then(() => console.log(' channel 2 warm.'))\n .catch(() => console.log(' channel 2 warmup skipped (non-fatal).'))\n );\n }\n if (warmups.length) {\n console.log('Warming up inference...');\n await Promise.all(warmups);\n }\n}\n\nasync function cmdStart(): Promise<void> {\n assertClaudeInstalled();\n assertPueueInstalled();\n assertTmuxInstalled();\n const t1 = ensureRunning({ channel: CHANNEL_PRIMARY });\n console.log(`Channel 1 (judge): id=${t1.id} status=${t1.status}`);\n const t2 = ensureRunning({ channel: CHANNEL_SECONDARY });\n console.log(`Channel 2 (CWE): id=${t2.id} status=${t2.status}`);\n const [ready1, ready2] = await Promise.all([\n waitForChannelReady(CHANNEL_PORT, 60_000, CHANNEL_HOST, CHANNEL_PRIMARY.tmuxSession),\n waitForChannelReady(CHANNEL_2_PORT, 60_000, CHANNEL_HOST, CHANNEL_SECONDARY.tmuxSession),\n ]);\n console.log(ready1 ? `channel 1 ready (${CHANNEL_PORT}).` : '⚠ channel 1 did not come up within 60s.');\n console.log(ready2 ? `channel 2 ready (${CHANNEL_2_PORT}).` : '⚠ channel 2 (CWE) did not come up within 60s.');\n await warmChannels(ready1, ready2);\n}\n\nfunction cmdStop(): void {\n stopTask(CHANNEL_PRIMARY);\n stopTask(CHANNEL_SECONDARY);\n console.log('Both channels stopped.');\n}\n\nasync function cmdRestart(): Promise<void> {\n stopTask(CHANNEL_PRIMARY);\n stopTask(CHANNEL_SECONDARY);\n const t1 = startTask({ channel: CHANNEL_PRIMARY });\n const t2 = startTask({ channel: CHANNEL_SECONDARY });\n console.log(`Channel 1 restarted: id=${t1.id} status=${t1.status}`);\n console.log(`Channel 2 restarted: id=${t2.id} status=${t2.status}`);\n const [ready1, ready2] = await Promise.all([\n waitForChannelReady(CHANNEL_PORT, 60_000, CHANNEL_HOST, CHANNEL_PRIMARY.tmuxSession),\n waitForChannelReady(CHANNEL_2_PORT, 60_000, CHANNEL_HOST, CHANNEL_SECONDARY.tmuxSession),\n ]);\n console.log(ready1 ? `channel 1 ready (${CHANNEL_PORT}).` : '⚠ channel 1 did not come up within 60s.');\n console.log(ready2 ? `channel 2 ready (${CHANNEL_2_PORT}).` : '⚠ channel 2 (CWE) did not come up within 60s.');\n await warmChannels(ready1, ready2);\n}\n\nfunction relativeTime(iso: string): string {\n const ts = new Date(iso).getTime();\n if (!Number.isFinite(ts)) return iso;\n const sec = Math.max(0, Math.round((Date.now() - ts) / 1000));\n if (sec < 60) return `${sec}s ago`;\n if (sec < 3600) return `${Math.round(sec / 60)}m ago`;\n if (sec < 86400) return `${Math.round(sec / 3600)}h ago`;\n return `${Math.round(sec / 86400)}d ago`;\n}\n\nfunction colorize(s: string, code: number): string {\n if (!process.stdout.isTTY) return s;\n return `\u001b[${code}m${s}\u001b[0m`;\n}\n\nfunction statusGlyph(t: TurnEntry): string {\n if (t.status === 'ok') return colorize('✓', 32); // green check\n if (t.status === 'timeout') return colorize('⏱', 33); // yellow clock\n return colorize('✗', 31); // red X\n}\n\nfunction severityCell(t: TurnEntry): string {\n if (t.severity) {\n const sev = t.severity;\n if (sev === 'block' || sev === 'violations' || sev === 'unclear') return colorize(sev, 33);\n return colorize(sev, 36); // cyan for everything else\n }\n if (t.error) return colorize(t.error.slice(0, 40), 31);\n return '—';\n}\n\nfunction firstLine(s: string): string {\n return s.split('\\n').find(l => l.trim().length > 0)?.trim() ?? '';\n}\n\nfunction formatTurn(t: TurnEntry, raw: boolean): string {\n const when = relativeTime(t.ts).padEnd(8);\n const dur = (t.duration_ms < 1000\n ? `${t.duration_ms}ms`\n : `${(t.duration_ms / 1000).toFixed(1)}s`).padStart(7);\n const role = t.role.padEnd(24);\n const sev = severityCell(t).padEnd(20);\n const preview = (() => {\n const req = firstLine(t.request_preview).slice(0, 60);\n return colorize(req, 90);\n })();\n const head = `${statusGlyph(t)} ${when} ${dur} ${role} ${sev} ${preview}`;\n if (!raw) return head;\n // raw mode: include full request + response previews on subsequent lines\n const blocks: string[] = [head];\n blocks.push(colorize(' request:', 90));\n blocks.push(' ' + t.request_preview.replace(/\\n/g, '\\n '));\n if (t.response_preview) {\n blocks.push(colorize(' response:', 90));\n blocks.push(' ' + t.response_preview.replace(/\\n/g, '\\n '));\n }\n if (t.error) {\n blocks.push(colorize(' error:', 31) + ' ' + t.error);\n }\n return blocks.join('\\n');\n}\n\nfunction cmdLogs(rest: string[]): void | Promise<void> {\n let n = 20;\n let raw = false;\n let live = false;\n for (const arg of rest) {\n if (arg === '--raw' || arg === '-r') raw = true;\n else if (arg === '--live' || arg === '-f') live = true;\n else if (arg === '--tmux') {\n // Escape hatch: show the original raw pueue/tmux output for the task.\n console.log(tailLogs(80));\n return;\n } else {\n const parsed = parseInt(arg, 10);\n if (parsed > 0) n = parsed;\n }\n }\n\n const header = ' ' + colorize('status when dur role severity request', 90);\n\n // Print backlog (newest first) — same as non-live mode.\n const turns = readRecentTurns(n);\n if (turns.length === 0) {\n if (!live) {\n console.log(`No turns logged yet at ${TURN_LOG_PATH}.`);\n console.log('Run a few requests through the channel (synkro local-cc test) and try again.');\n return;\n }\n console.log(`No turns logged yet at ${TURN_LOG_PATH} — waiting for new entries… (Ctrl-C to exit)`);\n } else {\n console.log(`Last ${turns.length} channel turn(s) (newest first):`);\n console.log(header);\n for (const t of turns) console.log(' ' + formatTurn(t, raw));\n }\n\n if (!live) {\n if (!raw) console.log(' ' + colorize('(use --raw / -r to see full payloads, --live / -f to follow)', 90));\n return;\n }\n\n // Live tail. Follow the JSONL log; print each new entry as it arrives.\n // Returning a Promise keeps the process alive until SIGINT.\n return new Promise<void>(resolve => {\n console.log(' ' + colorize('— following new turns (Ctrl-C to exit) —', 90));\n const stop = followTurns(t => {\n console.log(' ' + formatTurn(t, raw));\n });\n const onSigint = () => {\n stop();\n process.removeListener('SIGINT', onSigint);\n resolve();\n };\n process.on('SIGINT', onSigint);\n });\n}\n\nfunction cmdAttach(rest: string[]): void {\n assertTmuxInstalled();\n const readonly = rest.some(a => a === '--readonly' || a === '-r');\n\n // Verify the session exists before tmux drops the user into a confusing state.\n const has = spawnSync('tmux', ['has-session', '-t', `=${TMUX_SESSION_NAME}`], { encoding: 'utf-8' });\n if (has.status !== 0) {\n console.error(`No tmux session '${TMUX_SESSION_NAME}' running. Start it with: synkro local-cc start`);\n process.exit(1);\n }\n\n if (!process.stdout.isTTY) {\n console.error('attach requires a TTY. Run this command directly in your terminal, not piped.');\n process.exit(1);\n }\n\n console.log(`Attaching to tmux session '${TMUX_SESSION_NAME}'${readonly ? ' (read-only)' : ''}.`);\n console.log('Detach with Ctrl-B then D. (Do not press Ctrl-C — that would interrupt claude.)');\n console.log();\n\n const args = readonly\n ? ['attach-session', '-r', '-t', TMUX_SESSION_NAME]\n : ['attach-session', '-t', TMUX_SESSION_NAME];\n const r = spawnSync('tmux', args, { stdio: 'inherit' });\n process.exit(r.status ?? 0);\n}\n\nasync function cmdTest(): Promise<void> {\n console.log('Sending smoke-test grading request through the channel...');\n const result = await submitToChannel(\n 'grade-bash',\n 'Command: echo \"hello world\"\\nIntent: testing channel connectivity',\n );\n console.log('Raw reply:');\n console.log(result);\n}\n\nfunction cmdInstall(): void {\n const r = installLocalCC();\n console.log(`Reinstalled plugin at ${r.pluginPath}`);\n}\n\nexport async function localCcCommand(args: string[]): Promise<void> {\n const sub = args[0] ?? '';\n try {\n switch (sub) {\n case 'enable': await cmdEnable(); break;\n case 'disable': cmdDisable(); break;\n case 'status': await cmdStatus(); break;\n case 'start': await cmdStart(); break;\n case 'stop': cmdStop(); break;\n case 'restart': await cmdRestart(); break;\n case 'install': cmdInstall(); break;\n case 'logs': await cmdLogs(args.slice(1)); break;\n case 'attach': cmdAttach(args.slice(1)); break;\n case 'test': await cmdTest(); break;\n case '':\n case 'help':\n case '--help':\n case '-h':\n printHelp(); break;\n default:\n console.error(`Unknown subcommand: ${sub}`);\n printHelp();\n process.exit(1);\n }\n } catch (err) {\n console.error((err as Error).message);\n process.exit(1);\n }\n}\n","/**\n * `synkro grade <edit|bash>` — replacement for the legacy\n * ~/.synkro/bin/grader_daemon.py. Reads a grading payload from stdin,\n * routes it through the local-CC channel, and writes the raw verdict\n * (including the <synkro-verdict>...</synkro-verdict> wrapper that the\n * shell hook scripts grep for) to stdout.\n *\n * Failures print to stderr and exit non-zero so the hook scripts fall\n * through to the cloud fast-tier path cleanly.\n */\n\nimport { submitToChannel, LocalCCError } from '../local-cc/client.js';\nimport type { LocalCCRole } from '../local-cc/prompts.js';\n\nasync function readStdin(): Promise<string> {\n return new Promise((resolve, reject) => {\n const chunks: Buffer[] = [];\n process.stdin.on('data', c => chunks.push(c));\n process.stdin.on('end', () => resolve(Buffer.concat(chunks).toString('utf-8')));\n process.stdin.on('error', reject);\n });\n}\n\nexport async function gradeCommand(args: string[]): Promise<void> {\n const mode = args[0] ?? '';\n let role: LocalCCRole;\n if (mode === 'edit') role = 'grade-edit';\n else if (mode === 'bash') role = 'grade-bash';\n else if (mode === 'plan') role = 'grade-plan';\n else if (mode === 'cwe') role = 'grade-cwe';\n else {\n console.error('Usage: synkro grade <edit|bash|plan|cwe>');\n process.exit(2);\n }\n\n const payload = await readStdin();\n if (!payload.trim()) {\n console.error('synkro grade: empty stdin');\n process.exit(2);\n }\n\n try {\n const result = await submitToChannel(role, payload, { timeoutMs: 60_000 });\n process.stdout.write(result);\n if (!result.endsWith('\\n')) process.stdout.write('\\n');\n } catch (err) {\n if (err instanceof LocalCCError) {\n console.error(`synkro grade: ${err.message}`);\n } else {\n console.error(`synkro grade: ${(err as Error).message}`);\n }\n process.exit(3);\n }\n}\n","#!/usr/bin/env node\n/**\n * Synkro CLI bootstrap.\n *\n * Loads .env, then dispatches to the right subcommand.\n */\nimport { readFileSync, existsSync } from 'node:fs';\nimport { resolve } from 'node:path';\n\n// Load env vars synchronously from a few candidate paths\nconst envCandidates = [\n resolve(process.cwd(), '.env'),\n resolve(process.env.HOME ?? '', '.synkro', 'config.env'),\n];\nfor (const envPath of envCandidates) {\n if (!existsSync(envPath)) continue;\n const envContent = readFileSync(envPath, 'utf-8');\n for (const line of envContent.split('\\n')) {\n const trimmed = line.trim();\n if (!trimmed || trimmed.startsWith('#')) continue;\n const eqIndex = trimmed.indexOf('=');\n if (eqIndex <= 0) continue;\n const key = trimmed.slice(0, eqIndex).trim();\n const value = trimmed.slice(eqIndex + 1).trim().replace(/^['\"]|['\"]$/g, '');\n if (!process.env[key] && !value.startsWith('op://')) process.env[key] = value;\n }\n}\n\nconst args = process.argv.slice(2);\nconst cmd = args[0] || '';\nconst subArgs = args.slice(1);\n\nfunction printHelp() {\n console.log(`Synkro CLI — runtime safety for AI coding agents\n\nUsage:\n synkro-cli <command> [options] (recommended — avoids name collisions)\n synkro <command> [options] (alias)\n\nCommands:\n install [--force] Install Synkro hooks for detected agents (Claude Code, etc.)\n login Authenticate with Synkro (browser sign-in)\n logout Clear local credentials\n status Show current setup state\n link Link repos to a Synkro project (local git or GitHub OAuth)\n unlink Remove repo links from Synkro projects\n config View or change settings (e.g. --inference fast|standard)\n setup-github Configure GitHub PR scanning (push secrets + workflow file)\n update Refresh hook configs and judge prompts\n disconnect [--purge] Remove Synkro hooks from agents (--purge also removes ~/.synkro)\n uninstall Fully remove Synkro from this machine\n reinstall Clean uninstall + fresh install\n local-cc <sub> Manage the local Claude Code inference session (enable/disable/status/start/stop/logs/test)\n grade <edit|bash> Internal: read prompt from stdin, return verdict (called by hook scripts)\n help Show this message\n\nQuick start:\n $ synkro-cli install # one-time setup\n $ synkro-cli setup-github # enable PR scanning (optional)\n $ claude # use Claude Code normally; Synkro judges in real time\n (\\`synkro\\` also works as an alias unless something else on your $PATH shadows it)\n`);\n}\n\nasync function main() {\n switch (cmd) {\n case 'install': {\n const { installCommand, parseArgs } = await import('./commands/install.js');\n await installCommand(parseArgs(subArgs));\n break;\n }\n case 'login':\n case 'auth': {\n const { loginCommand } = await import('./commands/login.js');\n await loginCommand(subArgs);\n break;\n }\n case 'logout': {\n const { logoutCommand } = await import('./commands/logout.js');\n logoutCommand();\n break;\n }\n case 'status': {\n const { statusCommand } = await import('./commands/status.js');\n await statusCommand();\n break;\n }\n case 'link': {\n const { linkCommand } = await import('./commands/link.js');\n await linkCommand();\n break;\n }\n case 'unlink': {\n const { unlinkCommand } = await import('./commands/unlink.js');\n await unlinkCommand();\n break;\n }\n case 'config': {\n const { configCommand } = await import('./commands/config.js');\n await configCommand(subArgs);\n break;\n }\n case 'setup-github': {\n const { setupGithubCommand } = await import('./commands/setupGithub.js');\n const ghOpts = {};\n for (const a of subArgs) {\n if (a === '--non-interactive') ghOpts.nonInteractive = true;\n else if (a === '--skip-claude-token') ghOpts.skipClaudeToken = true;\n else if (a.startsWith('--github-token=')) ghOpts.githubToken = a.slice('--github-token='.length);\n }\n await setupGithubCommand(ghOpts);\n break;\n }\n case 'scan-pr': {\n const { scanPrCommand } = await import('./commands/scanPr.js');\n await scanPrCommand();\n break;\n }\n case 'update': {\n const { updateCommand } = await import('./commands/update.js');\n await updateCommand();\n break;\n }\n case 'disconnect': {\n const { disconnectCommand } = await import('./commands/disconnect.js');\n disconnectCommand(subArgs);\n break;\n }\n case 'uninstall': {\n const { uninstallCommand } = await import('./commands/uninstall.js');\n uninstallCommand();\n break;\n }\n case 'reinstall': {\n const { reinstallCommand } = await import('./commands/reinstall.js');\n await reinstallCommand();\n break;\n }\n case 'local-cc': {\n const { localCcCommand } = await import('./commands/localCc.js');\n await localCcCommand(subArgs);\n break;\n }\n case 'grade': {\n const { gradeCommand } = await import('./commands/grade.js');\n await gradeCommand(subArgs);\n break;\n }\n case 'help':\n case '--help':\n case '-h':\n case '': {\n printHelp();\n break;\n }\n default: {\n console.error(`Unknown command: ${cmd}`);\n printHelp();\n process.exit(1);\n }\n }\n}\n\nmain().catch((err) => {\n console.error(err);\n process.exit(1);\n});\n"],"mappings":"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AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AA0gCzB,IAAM,mBAAmB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAyNzB,IAAM,kBAAkB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAsNxB,IAAM,kBAAkB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAkKxB,IAAM,gBAAgB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AA+btB,IAAM,iBAAiB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAyKvB,IAAM,gBAAgB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AA6KtB,IAAM,kBAAkB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AA4FxB,IAAM,mBAAmB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAqEzB,IAAM,mBAAmB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AA0EzB,IAAM,qBAAqB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAmJ3B,IAAM,wBAAwB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAoD9B,IAAM,uBAAuB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;A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A;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAA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","names":["cmd","existsSync","readFileSync","writeFileSync","renameSync","mkdirSync","dirname","homedir","isSynkroEntry","SYNKRO_MARKER","removeSynkroEntries","existsSync","readFileSync","writeFileSync","renameSync","mkdirSync","homedir","dirname","join","SYNKRO_MARKER","url","writeFileSync","readFileSync","existsSync","mkdirSync","unlinkSync","homedir","join","dirname","args","resolve","existsSync","mkdirSync","writeFileSync","execSync","join","execSync","createServer","resolve","SYNKRO_WEB_AUTH_URL","openBrowser","execFile","RAW_WEB_AUTH_URL","createInterface","execSync","existsSync","readFileSync","unlinkSync","homedir","platform","join","execFile","resolve","openBrowser","args","jwt","existsSync","readFileSync","homedir","join","CONFIG_PATH","existsSync","mkdirSync","writeFileSync","readFileSync","renameSync","unlinkSync","join","homedir","spawnSync","homedir","join","args","resolve","SESSION_DIR","SESSION_DIR_2","readFileSync","homedir","join","jwt","existsSync","mkdirSync","openSync","readFileSync","closeSync","dirname","join","homedir","args","connect","resolve","existsSync","mkdirSync","writeFileSync","chmodSync","readFileSync","appendFileSync","renameSync","homedir","join","execSync","spawnSync","spawn","createInterface","resolve","SYNKRO_DIR","CONFIG_PATH","detectGitRepo","token","profile","setupGithubCommand","init_install","args","info","existsSync","readFileSync","homedir","join","CONFIG_PATH","HOOKS_DIR","SYNKRO_DIR","createInterface","ask","resolve","readFileSync","writeFileSync","existsSync","join","homedir","readConfigEnv","CONFIG_PATH","args","config","SYNKRO_DIR","execSync","spawn","readFileSync","existsSync","join","args","resolve","prompt","init_install","existsSync","homedir","join","args","SYNKRO_DIR","init_install","spawnSync","homedir","join","existsSync","readFileSync","writeFileSync","CONFIG_PATH","updateLocalInferenceFlag","jwt","resolve","args","resolve","args","readFileSync","existsSync","resolve","printHelp","installCommand","parseArgs","loginCommand","logoutCommand","statusCommand","linkCommand","unlinkCommand","configCommand","setupGithubCommand","scanPrCommand","updateCommand","disconnectCommand","uninstallCommand","reinstallCommand","localCcCommand","gradeCommand"]}
|