oh-my-githubcopilot 2.0.0-alpha.6-alpha.332256c → 2.0.0

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.
@@ -70,11 +70,20 @@ import { appendFileSync, mkdirSync as mkdirSync2 } from "fs";
70
70
  import { homedir as homedir2 } from "os";
71
71
  import { join as join2 } from "path";
72
72
  async function readStdin() {
73
- const chunks = [];
74
- for await (const chunk of process.stdin) {
75
- chunks.push(String(chunk));
76
- }
77
- return chunks.join("");
73
+ const readStdinActual = async () => {
74
+ const chunks = [];
75
+ for await (const chunk of process.stdin) {
76
+ chunks.push(String(chunk));
77
+ }
78
+ return chunks.join("");
79
+ };
80
+ const stdinTimeout = new Promise(
81
+ (resolve) => setTimeout(
82
+ () => resolve(""),
83
+ parseInt(process.env.OMP_HOOK_STDIN_TIMEOUT_MS ?? "500") || 500
84
+ )
85
+ );
86
+ return Promise.race([readStdinActual(), stdinTimeout]);
78
87
  }
79
88
  function logHookFailure(hook, reason) {
80
89
  try {
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/hooks/token-tracker.mts", "../../src/spending/tracker.mts", "../../src/hooks/runner.mts"],
4
- "sourcesContent": ["/**\n * token-tracker hook\n * Trigger: post-message (PostToolUse equivalent)\n * Priority: 70\n *\n * Estimates token usage from character counts (1 token \u2248 4 chars).\n * Accumulates in session state. Warns at 60%, 80%, 90% thresholds.\n */\n\nimport { readFileSync, writeFileSync, mkdirSync } from \"fs\";\nimport { homedir } from \"os\";\nimport { join } from \"path\";\nimport { incrementSpending } from \"../spending/tracker.mjs\";\n\nexport interface HookInput {\n hook_type: \"PostToolUse\";\n tool_name?: string;\n tool_input?: unknown;\n tool_output?: unknown;\n session_id?: string;\n}\n\nexport interface HookOutput {\n modifiedResult?: unknown;\n status: \"ok\" | \"skip\" | \"error\";\n latencyMs: number;\n mutations: Array<{ type: \"set_token_budget\"; budget: number } | { type: \"emit_hud\"; hudEmit: unknown } | { type: \"log\"; level: \"info\" | \"warn\" | \"error\"; message: string }>;\n log: string[];\n}\n\ninterface SessionState {\n tokens_estimated: number;\n token_budget: number;\n context_pct: number;\n warnings_issued: Set<string>;\n}\n\n// Model context windows in tokens (for future model-specific budget lookup)\n// Exported for potential external use\nexport const MODEL_CONTEXTS = {\n \"claude-sonnet-4.5\": 200_000,\n \"claude-sonnet-4\": 200_000,\n \"claude-sonnet-4.6\": 200_000,\n \"claude-opus-4.6\": 200_000,\n \"gpt-5\": 128_000,\n \"gpt-5.4-mini\": 128_000,\n \"gemini-3-pro\": 128_000,\n default: 200_000,\n};\n\nconst WARNING_THRESHOLDS = [60, 80, 90];\n\nexport function estimateTokens(input: unknown): number {\n if (!input) return 0;\n try {\n const str = typeof input === \"string\" ? input : JSON.stringify(input);\n return Math.ceil(str.length / 4);\n } catch {\n return 0;\n }\n}\n\nfunction getStatePath(sessionId?: string): string {\n const base = join(homedir(), \".omp\", \"state\");\n if (sessionId) {\n return join(base, \"sessions\", sessionId, \"session.json\");\n }\n return join(base, \"session.json\");\n}\n\nfunction ensureDir(path: string): void {\n mkdirSync(path.substring(0, path.lastIndexOf(\"/\")), { recursive: true });\n}\n\nexport function processHook(input: HookInput): HookOutput {\n const start = Date.now();\n const log: string[] = [];\n\n try {\n if (input.hook_type !== \"PostToolUse\") {\n return {\n status: \"skip\",\n latencyMs: Date.now() - start,\n mutations: [],\n log: [],\n };\n }\n\n const statePath = getStatePath(input.session_id);\n let state: SessionState;\n\n try {\n const raw = JSON.parse(readFileSync(statePath, \"utf-8\")) as Record<string, unknown>;\n // warnings_issued is persisted as an array (a Set JSON-serializes to {}).\n // Rehydrate to a Set; tolerate legacy state files where it is {} or missing.\n state = {\n ...raw,\n warnings_issued: new Set(\n Array.isArray(raw.warnings_issued) ? (raw.warnings_issued as string[]) : []\n ),\n } as unknown as SessionState;\n } catch {\n // Initialize state if not found \u2014 budget derived from model when available\n const fallbackModel = (input as { model?: string }).model ?? \"default\";\n state = {\n tokens_estimated: 0,\n token_budget: (MODEL_CONTEXTS as Record<string, number>)[fallbackModel] ?? MODEL_CONTEXTS[\"default\"] ?? 200_000,\n context_pct: 0,\n warnings_issued: new Set(),\n };\n }\n\n const inputTokens = estimateTokens(input.tool_input);\n const outputTokens = estimateTokens(input.tool_output);\n const delta = inputTokens + outputTokens;\n\n state.tokens_estimated += delta;\n state.context_pct = Math.min(100, Math.round((state.tokens_estimated / state.token_budget) * 100));\n\n const mutations: HookOutput[\"mutations\"] = [\n { type: \"set_token_budget\", budget: state.token_budget },\n ];\n\n // Check warning thresholds\n for (const threshold of WARNING_THRESHOLDS) {\n const key = `warn_${threshold}`;\n if (state.context_pct >= threshold && !state.warnings_issued.has(key)) {\n state.warnings_issued.add(key);\n const message =\n threshold >= 90\n ? `CRITICAL: Context at ${state.context_pct}%. Tokens near budget limit.`\n : threshold >= 80\n ? `WARNING: Context at ${state.context_pct}%. Consider enabling ecomode.`\n : `INFO: Context at ${state.context_pct}%.`;\n mutations.push({ type: \"log\", level: threshold >= 80 ? \"warn\" : \"info\", message });\n log.push(message);\n }\n }\n\n // Write state back \u2014 persist warnings_issued as an array since a Set\n // JSON-serializes to {} and would break .has() on the next invocation.\n try {\n ensureDir(statePath);\n writeFileSync(\n statePath,\n JSON.stringify({ ...state, warnings_issued: Array.from(state.warnings_issued) }),\n \"utf-8\"\n );\n } catch (e) {\n log.push(`Failed to write state: ${e}`);\n }\n\n // Track premium request spending\n const sessionId = input.session_id ?? `omp-${Date.now()}`;\n try { incrementSpending(sessionId); } catch { /* non-blocking */ }\n\n return {\n status: \"ok\",\n latencyMs: Date.now() - start,\n mutations,\n log,\n };\n } catch (err) {\n return {\n status: \"error\",\n latencyMs: Date.now() - start,\n mutations: [],\n log: [`Error: ${err}`],\n };\n }\n}\n\n// Main entry point \u2014 only runs when executed directly (not imported).\n// Fail-open: any stdin/parse/processing failure still emits valid JSON and exits 0.\nimport { fileURLToPath } from \"url\";\nimport { runHookMain } from \"./runner.mts\";\n\nif (process.argv[1] === fileURLToPath(import.meta.url)) {\n await runHookMain(processHook, { hookName: \"token-tracker\" });\n}\n", "/**\n * Spending tracker for OMP.\n * Tracks premium API requests per session and per calendar month.\n * Persists to ~/.omp/state/spending-monthly.json\n *\n * // v1.1 known limitation: no /omp:spending reset command. To reset monthly counter manually: rm ~/.omp/state/spending-monthly.json\n */\n\nimport { readFileSync, writeFileSync, mkdirSync } from \"fs\";\nimport { homedir } from \"os\";\nimport { join, dirname } from \"path\";\nimport type { SpendingState } from \"./types.mjs\";\n\nconst SPENDING_PATH = join(homedir(), \".omp\", \"state\", \"spending-monthly.json\");\n\nfunction currentMonth(): string {\n const now = new Date();\n return `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, \"0\")}`;\n}\n\nexport function loadSpending(sessionId: string): SpendingState {\n let raw: SpendingState;\n try {\n raw = JSON.parse(readFileSync(SPENDING_PATH, \"utf-8\")) as SpendingState;\n } catch {\n // Missing or malformed file \u2014 start fresh\n return {\n version: 1,\n sessionId,\n sessionPremiumRequests: 0,\n month: currentMonth(),\n monthlyPremiumRequests: 0,\n };\n }\n\n const month = currentMonth();\n\n // Reset monthly counter when month rolls over\n if (raw.month !== month) {\n return {\n version: 1,\n sessionId,\n sessionPremiumRequests: 0,\n month,\n monthlyPremiumRequests: 0,\n };\n }\n\n // Reset session counter when session changes\n if (raw.sessionId !== sessionId) {\n return {\n version: 1,\n sessionId,\n sessionPremiumRequests: 0,\n month,\n monthlyPremiumRequests: raw.monthlyPremiumRequests,\n };\n }\n\n return { ...raw, version: 1 };\n}\n\nexport function saveSpending(state: SpendingState): void {\n try {\n mkdirSync(dirname(SPENDING_PATH), { recursive: true });\n writeFileSync(SPENDING_PATH, JSON.stringify(state, null, 2), \"utf-8\");\n } catch (e) {\n console.warn(`[OMP] spending: failed to save state: ${e}`);\n }\n}\n\nexport function incrementSpending(sessionId: string): SpendingState {\n const state = loadSpending(sessionId);\n state.sessionPremiumRequests += 1;\n state.monthlyPremiumRequests += 1;\n saveSpending(state);\n return state;\n}\n", "/**\n * Shared hook entry-point runner.\n *\n * Hooks must be FAIL-OPEN: any failure (empty stdin, malformed JSON,\n * unexpected processing error) must still emit a valid HookOutput-shaped\n * JSON object on stdout and exit 0. A non-zero exit or non-JSON stdout\n * causes the Copilot CLI to treat the hook as errored, which denies the\n * tool call for PreToolUse hooks.\n *\n * Fail-open events are persisted (best-effort) as JSONL records to\n * ~/.omp/logs/hook-failures.jsonl and mirrored on stderr so failures\n * remain observable without ever touching the stdout JSON contract.\n */\n\nimport { appendFileSync, mkdirSync } from \"fs\";\nimport { homedir } from \"os\";\nimport { join } from \"path\";\n\nexport interface FailOpenOutput {\n decision?: \"allow\";\n status: \"error\";\n latencyMs: number;\n mutations: never[];\n log: string[];\n}\n\nexport interface RunHookOptions {\n /**\n * When true (hooks whose HookOutput supports a decision field), the\n * fail-open output includes `\"decision\": \"allow\"` so the tool call is\n * explicitly allowed.\n */\n failOpenDecision?: boolean;\n /** Hook id recorded in fail-open log entries (stderr + JSONL). */\n hookName?: string;\n}\n\nexport async function readStdin(): Promise<string> {\n const chunks: string[] = [];\n for await (const chunk of process.stdin) {\n chunks.push(String(chunk));\n }\n return chunks.join(\"\");\n}\n\n/**\n * Best-effort persistence of a fail-open event. Wrapped in its own\n * try/catch: logging must NEVER break fail-open or the one-JSON-object\n * stdout contract. Writes to stderr and the JSONL log only \u2014 never stdout.\n */\nfunction logHookFailure(hook: string, reason: string): void {\n try {\n process.stderr.write(`[omp hook fail-open] ${hook}: ${reason}\\n`);\n } catch {\n // stderr unavailable \u2014 ignore\n }\n try {\n const logsDir = join(homedir(), \".omp\", \"logs\");\n mkdirSync(logsDir, { recursive: true });\n const record = JSON.stringify({ ts: new Date().toISOString(), hook, reason });\n appendFileSync(join(logsDir, \"hook-failures.jsonl\"), record + \"\\n\", \"utf-8\");\n } catch {\n // best-effort only \u2014 never let logging break fail-open\n }\n}\n\n/**\n * Reads HookInput JSON from stdin, runs the hook, and prints the\n * HookOutput JSON to stdout. Never throws, never exits non-zero,\n * never emits non-JSON to stdout.\n */\nexport async function runHookMain<TInput>(\n processHook: (input: TInput) => unknown,\n options: RunHookOptions = {}\n): Promise<void> {\n let outputJson: string;\n try {\n const input = JSON.parse(await readStdin()) as TInput;\n const serialized = JSON.stringify(processHook(input));\n if (typeof serialized !== \"string\") {\n throw new Error(\"hook produced no serializable output\");\n }\n outputJson = serialized;\n } catch (err) {\n const reason = err instanceof Error ? err.message : String(err);\n logHookFailure(options.hookName ?? \"unknown\", reason);\n const failOpen: FailOpenOutput = {\n ...(options.failOpenDecision ? { decision: \"allow\" as const } : {}),\n status: \"error\",\n latencyMs: 0,\n mutations: [],\n log: [`fail-open: ${reason}`],\n };\n outputJson = JSON.stringify(failOpen);\n }\n console.log(outputJson);\n process.exitCode = 0;\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["/**\n * token-tracker hook\n * Trigger: post-message (PostToolUse equivalent)\n * Priority: 70\n *\n * Estimates token usage from character counts (1 token \u2248 4 chars).\n * Accumulates in session state. Warns at 60%, 80%, 90% thresholds.\n */\n\nimport { readFileSync, writeFileSync, mkdirSync } from \"fs\";\nimport { homedir } from \"os\";\nimport { join } from \"path\";\nimport { incrementSpending } from \"../spending/tracker.mjs\";\n\nexport interface HookInput {\n hook_type: \"PostToolUse\";\n tool_name?: string;\n tool_input?: unknown;\n tool_output?: unknown;\n session_id?: string;\n}\n\nexport interface HookOutput {\n modifiedResult?: unknown;\n status: \"ok\" | \"skip\" | \"error\";\n latencyMs: number;\n mutations: Array<{ type: \"set_token_budget\"; budget: number } | { type: \"emit_hud\"; hudEmit: unknown } | { type: \"log\"; level: \"info\" | \"warn\" | \"error\"; message: string }>;\n log: string[];\n}\n\ninterface SessionState {\n tokens_estimated: number;\n token_budget: number;\n context_pct: number;\n warnings_issued: Set<string>;\n}\n\n// Model context windows in tokens (for future model-specific budget lookup)\n// Exported for potential external use\nexport const MODEL_CONTEXTS = {\n \"claude-sonnet-4.5\": 200_000,\n \"claude-sonnet-4\": 200_000,\n \"claude-sonnet-4.6\": 200_000,\n \"claude-opus-4.6\": 200_000,\n \"gpt-5\": 128_000,\n \"gpt-5.4-mini\": 128_000,\n \"gemini-3-pro\": 128_000,\n default: 200_000,\n};\n\nconst WARNING_THRESHOLDS = [60, 80, 90];\n\nexport function estimateTokens(input: unknown): number {\n if (!input) return 0;\n try {\n const str = typeof input === \"string\" ? input : JSON.stringify(input);\n return Math.ceil(str.length / 4);\n } catch {\n return 0;\n }\n}\n\nfunction getStatePath(sessionId?: string): string {\n const base = join(homedir(), \".omp\", \"state\");\n if (sessionId) {\n return join(base, \"sessions\", sessionId, \"session.json\");\n }\n return join(base, \"session.json\");\n}\n\nfunction ensureDir(path: string): void {\n mkdirSync(path.substring(0, path.lastIndexOf(\"/\")), { recursive: true });\n}\n\nexport function processHook(input: HookInput): HookOutput {\n const start = Date.now();\n const log: string[] = [];\n\n try {\n if (input.hook_type !== \"PostToolUse\") {\n return {\n status: \"skip\",\n latencyMs: Date.now() - start,\n mutations: [],\n log: [],\n };\n }\n\n const statePath = getStatePath(input.session_id);\n let state: SessionState;\n\n try {\n const raw = JSON.parse(readFileSync(statePath, \"utf-8\")) as Record<string, unknown>;\n // warnings_issued is persisted as an array (a Set JSON-serializes to {}).\n // Rehydrate to a Set; tolerate legacy state files where it is {} or missing.\n state = {\n ...raw,\n warnings_issued: new Set(\n Array.isArray(raw.warnings_issued) ? (raw.warnings_issued as string[]) : []\n ),\n } as unknown as SessionState;\n } catch {\n // Initialize state if not found \u2014 budget derived from model when available\n const fallbackModel = (input as { model?: string }).model ?? \"default\";\n state = {\n tokens_estimated: 0,\n token_budget: (MODEL_CONTEXTS as Record<string, number>)[fallbackModel] ?? MODEL_CONTEXTS[\"default\"] ?? 200_000,\n context_pct: 0,\n warnings_issued: new Set(),\n };\n }\n\n const inputTokens = estimateTokens(input.tool_input);\n const outputTokens = estimateTokens(input.tool_output);\n const delta = inputTokens + outputTokens;\n\n state.tokens_estimated += delta;\n state.context_pct = Math.min(100, Math.round((state.tokens_estimated / state.token_budget) * 100));\n\n const mutations: HookOutput[\"mutations\"] = [\n { type: \"set_token_budget\", budget: state.token_budget },\n ];\n\n // Check warning thresholds\n for (const threshold of WARNING_THRESHOLDS) {\n const key = `warn_${threshold}`;\n if (state.context_pct >= threshold && !state.warnings_issued.has(key)) {\n state.warnings_issued.add(key);\n const message =\n threshold >= 90\n ? `CRITICAL: Context at ${state.context_pct}%. Tokens near budget limit.`\n : threshold >= 80\n ? `WARNING: Context at ${state.context_pct}%. Consider enabling ecomode.`\n : `INFO: Context at ${state.context_pct}%.`;\n mutations.push({ type: \"log\", level: threshold >= 80 ? \"warn\" : \"info\", message });\n log.push(message);\n }\n }\n\n // Write state back \u2014 persist warnings_issued as an array since a Set\n // JSON-serializes to {} and would break .has() on the next invocation.\n try {\n ensureDir(statePath);\n writeFileSync(\n statePath,\n JSON.stringify({ ...state, warnings_issued: Array.from(state.warnings_issued) }),\n \"utf-8\"\n );\n } catch (e) {\n log.push(`Failed to write state: ${e}`);\n }\n\n // Track premium request spending\n const sessionId = input.session_id ?? `omp-${Date.now()}`;\n try { incrementSpending(sessionId); } catch { /* non-blocking */ }\n\n return {\n status: \"ok\",\n latencyMs: Date.now() - start,\n mutations,\n log,\n };\n } catch (err) {\n return {\n status: \"error\",\n latencyMs: Date.now() - start,\n mutations: [],\n log: [`Error: ${err}`],\n };\n }\n}\n\n// Main entry point \u2014 only runs when executed directly (not imported).\n// Fail-open: any stdin/parse/processing failure still emits valid JSON and exits 0.\nimport { fileURLToPath } from \"url\";\nimport { runHookMain } from \"./runner.mts\";\n\nif (process.argv[1] === fileURLToPath(import.meta.url)) {\n await runHookMain(processHook, { hookName: \"token-tracker\" });\n}\n", "/**\n * Spending tracker for OMP.\n * Tracks premium API requests per session and per calendar month.\n * Persists to ~/.omp/state/spending-monthly.json\n *\n * // v1.1 known limitation: no /omp:spending reset command. To reset monthly counter manually: rm ~/.omp/state/spending-monthly.json\n */\n\nimport { readFileSync, writeFileSync, mkdirSync } from \"fs\";\nimport { homedir } from \"os\";\nimport { join, dirname } from \"path\";\nimport type { SpendingState } from \"./types.mjs\";\n\nconst SPENDING_PATH = join(homedir(), \".omp\", \"state\", \"spending-monthly.json\");\n\nfunction currentMonth(): string {\n const now = new Date();\n return `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, \"0\")}`;\n}\n\nexport function loadSpending(sessionId: string): SpendingState {\n let raw: SpendingState;\n try {\n raw = JSON.parse(readFileSync(SPENDING_PATH, \"utf-8\")) as SpendingState;\n } catch {\n // Missing or malformed file \u2014 start fresh\n return {\n version: 1,\n sessionId,\n sessionPremiumRequests: 0,\n month: currentMonth(),\n monthlyPremiumRequests: 0,\n };\n }\n\n const month = currentMonth();\n\n // Reset monthly counter when month rolls over\n if (raw.month !== month) {\n return {\n version: 1,\n sessionId,\n sessionPremiumRequests: 0,\n month,\n monthlyPremiumRequests: 0,\n };\n }\n\n // Reset session counter when session changes\n if (raw.sessionId !== sessionId) {\n return {\n version: 1,\n sessionId,\n sessionPremiumRequests: 0,\n month,\n monthlyPremiumRequests: raw.monthlyPremiumRequests,\n };\n }\n\n return { ...raw, version: 1 };\n}\n\nexport function saveSpending(state: SpendingState): void {\n try {\n mkdirSync(dirname(SPENDING_PATH), { recursive: true });\n writeFileSync(SPENDING_PATH, JSON.stringify(state, null, 2), \"utf-8\");\n } catch (e) {\n console.warn(`[OMP] spending: failed to save state: ${e}`);\n }\n}\n\nexport function incrementSpending(sessionId: string): SpendingState {\n const state = loadSpending(sessionId);\n state.sessionPremiumRequests += 1;\n state.monthlyPremiumRequests += 1;\n saveSpending(state);\n return state;\n}\n", "/**\n * Shared hook entry-point runner.\n *\n * Hooks must be FAIL-OPEN: any failure (empty stdin, malformed JSON,\n * unexpected processing error) must still emit a valid HookOutput-shaped\n * JSON object on stdout and exit 0. A non-zero exit or non-JSON stdout\n * causes the Copilot CLI to treat the hook as errored, which denies the\n * tool call for PreToolUse hooks.\n *\n * Fail-open events are persisted (best-effort) as JSONL records to\n * ~/.omp/logs/hook-failures.jsonl and mirrored on stderr so failures\n * remain observable without ever touching the stdout JSON contract.\n */\n\nimport { appendFileSync, mkdirSync } from \"fs\";\nimport { homedir } from \"os\";\nimport { join } from \"path\";\n\nexport interface FailOpenOutput {\n decision?: \"allow\";\n status: \"error\";\n latencyMs: number;\n mutations: never[];\n log: string[];\n}\n\nexport interface RunHookOptions {\n /**\n * When true (hooks whose HookOutput supports a decision field), the\n * fail-open output includes `\"decision\": \"allow\"` so the tool call is\n * explicitly allowed.\n */\n failOpenDecision?: boolean;\n /** Hook id recorded in fail-open log entries (stderr + JSONL). */\n hookName?: string;\n}\n\nexport async function readStdin(): Promise<string> {\n const readStdinActual = async (): Promise<string> => {\n const chunks: string[] = [];\n for await (const chunk of process.stdin) {\n chunks.push(String(chunk));\n }\n return chunks.join(\"\");\n };\n\n const stdinTimeout = new Promise<string>((resolve) =>\n setTimeout(\n () => resolve(\"\"),\n (parseInt(process.env.OMP_HOOK_STDIN_TIMEOUT_MS ?? \"500\") || 500)\n )\n );\n\n return Promise.race([readStdinActual(), stdinTimeout]);\n}\n\n/**\n * Best-effort persistence of a fail-open event. Wrapped in its own\n * try/catch: logging must NEVER break fail-open or the one-JSON-object\n * stdout contract. Writes to stderr and the JSONL log only \u2014 never stdout.\n */\nfunction logHookFailure(hook: string, reason: string): void {\n try {\n process.stderr.write(`[omp hook fail-open] ${hook}: ${reason}\\n`);\n } catch {\n // stderr unavailable \u2014 ignore\n }\n try {\n const logsDir = join(homedir(), \".omp\", \"logs\");\n mkdirSync(logsDir, { recursive: true });\n const record = JSON.stringify({ ts: new Date().toISOString(), hook, reason });\n appendFileSync(join(logsDir, \"hook-failures.jsonl\"), record + \"\\n\", \"utf-8\");\n } catch {\n // best-effort only \u2014 never let logging break fail-open\n }\n}\n\n/**\n * Reads HookInput JSON from stdin, runs the hook, and prints the\n * HookOutput JSON to stdout. Never throws, never exits non-zero,\n * never emits non-JSON to stdout.\n */\nexport async function runHookMain<TInput>(\n processHook: (input: TInput) => unknown,\n options: RunHookOptions = {}\n): Promise<void> {\n let outputJson: string;\n try {\n const input = JSON.parse(await readStdin()) as TInput;\n const serialized = JSON.stringify(processHook(input));\n if (typeof serialized !== \"string\") {\n throw new Error(\"hook produced no serializable output\");\n }\n outputJson = serialized;\n } catch (err) {\n const reason = err instanceof Error ? err.message : String(err);\n logHookFailure(options.hookName ?? \"unknown\", reason);\n const failOpen: FailOpenOutput = {\n ...(options.failOpenDecision ? { decision: \"allow\" as const } : {}),\n status: \"error\",\n latencyMs: 0,\n mutations: [],\n log: [`fail-open: ${reason}`],\n };\n outputJson = JSON.stringify(failOpen);\n }\n console.log(outputJson);\n process.exitCode = 0;\n}\n"],
5
+ "mappings": "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",
6
6
  "names": ["readFileSync", "writeFileSync", "mkdirSync", "homedir", "join", "mkdirSync", "homedir", "join", "processHook", "join", "homedir", "mkdirSync", "readFileSync", "writeFileSync"]
7
7
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "oh-my-githubcopilot",
3
- "version": "2.0.0-alpha.6-alpha.332256c",
3
+ "version": "2.0.0",
4
4
  "description": "Multi-agent orchestration for GitHub Copilot CLI. 19 agents, 59 skills, parallel execution, HUD, PSM, SWE-bench.",
5
5
  "author": {
6
6
  "name": "r3dlex"
package/plugin.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "schemaVersion": "1.0",
3
3
  "name": "oh-my-githubcopilot",
4
- "version": "2.0.0-alpha.6",
4
+ "version": "2.0.0",
5
5
  "description": "Multi-agent orchestration for GitHub Copilot CLI. 19 agents, 59 skills, parallel execution, HUD, PSM, SWE-bench.",
6
6
  "author": {
7
7
  "name": "r3dlex"