issue-flow 0.12.0 → 0.14.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.
- package/README.md +79 -274
- package/dist/agent-OX3KPHE4.js +30 -0
- package/dist/{analyze-OEET7KGE.js → analyze-WIND2SEB.js} +31 -16
- package/dist/analyze-WIND2SEB.js.map +1 -0
- package/dist/{apply-ZIA3C52S.js → apply-JNOI2LR4.js} +18 -7
- package/dist/{apply-ZIA3C52S.js.map → apply-JNOI2LR4.js.map} +1 -1
- package/dist/bench-B4VZON7E.js +910 -0
- package/dist/bench-B4VZON7E.js.map +1 -0
- package/dist/{chunk-FADTCQGM.js → chunk-2IQ6M4ZE.js} +94 -32
- package/dist/chunk-2IQ6M4ZE.js.map +1 -0
- package/dist/chunk-3BWZO4YI.js +141 -0
- package/dist/chunk-3BWZO4YI.js.map +1 -0
- package/dist/chunk-3WFZETIM.js +163 -0
- package/dist/chunk-3WFZETIM.js.map +1 -0
- package/dist/chunk-67YIU2VB.js +362 -0
- package/dist/chunk-67YIU2VB.js.map +1 -0
- package/dist/{chunk-QCPGHBSZ.js → chunk-6BENHR2I.js} +63 -21
- package/dist/chunk-6BENHR2I.js.map +1 -0
- package/dist/{chunk-BYZDPR7N.js → chunk-6QYMSNVA.js} +194 -235
- package/dist/chunk-6QYMSNVA.js.map +1 -0
- package/dist/{chunk-Y5ZDJ4PJ.js → chunk-76MMPJ53.js} +20 -10
- package/dist/chunk-76MMPJ53.js.map +1 -0
- package/dist/chunk-7Z4Y3VKM.js +26 -0
- package/dist/chunk-7Z4Y3VKM.js.map +1 -0
- package/dist/{chunk-3QHK4YQA.js → chunk-B72RQZBV.js} +346 -119
- package/dist/chunk-B72RQZBV.js.map +1 -0
- package/dist/chunk-BEZ5BA5U.js +625 -0
- package/dist/chunk-BEZ5BA5U.js.map +1 -0
- package/dist/chunk-BRCNQXYQ.js +74 -0
- package/dist/chunk-BRCNQXYQ.js.map +1 -0
- package/dist/chunk-CGKLE6WR.js +1203 -0
- package/dist/chunk-CGKLE6WR.js.map +1 -0
- package/dist/chunk-CRGIQFUQ.js +83 -0
- package/dist/chunk-CRGIQFUQ.js.map +1 -0
- package/dist/{chunk-NS6R37II.js → chunk-F6TOYSYY.js} +141 -12
- package/dist/chunk-F6TOYSYY.js.map +1 -0
- package/dist/chunk-G5EK2XRN.js +25 -0
- package/dist/chunk-G5EK2XRN.js.map +1 -0
- package/dist/chunk-HAKDLR6N.js +134 -0
- package/dist/chunk-HAKDLR6N.js.map +1 -0
- package/dist/chunk-J3SOTUVI.js +298 -0
- package/dist/chunk-J3SOTUVI.js.map +1 -0
- package/dist/chunk-JN6D3MY6.js +30 -0
- package/dist/chunk-JN6D3MY6.js.map +1 -0
- package/dist/{chunk-LAFNLLQP.js → chunk-KMRFRXIT.js} +5 -5
- package/dist/chunk-KT7JKOXB.js +72 -0
- package/dist/chunk-KT7JKOXB.js.map +1 -0
- package/dist/chunk-L6HSKTQE.js +77 -0
- package/dist/chunk-L6HSKTQE.js.map +1 -0
- package/dist/chunk-LBGHI4S2.js +1264 -0
- package/dist/chunk-LBGHI4S2.js.map +1 -0
- package/dist/chunk-LEW4QM74.js +180 -0
- package/dist/chunk-LEW4QM74.js.map +1 -0
- package/dist/{chunk-FJP3UQGD.js → chunk-LFEQIZLB.js} +115 -20
- package/dist/chunk-LFEQIZLB.js.map +1 -0
- package/dist/chunk-OOVG2KBY.js +77 -0
- package/dist/chunk-OOVG2KBY.js.map +1 -0
- package/dist/{chunk-56KS3TFQ.js → chunk-OR43364P.js} +115 -160
- package/dist/chunk-OR43364P.js.map +1 -0
- package/dist/chunk-P3LRUNC6.js +16 -0
- package/dist/chunk-P3LRUNC6.js.map +1 -0
- package/dist/{chunk-TSOLWK7Y.js → chunk-PBTA3QQX.js} +8 -7
- package/dist/{chunk-TSOLWK7Y.js.map → chunk-PBTA3QQX.js.map} +1 -1
- package/dist/{chunk-MJULIKQU.js → chunk-PHW2YV6N.js} +23 -47
- package/dist/chunk-PHW2YV6N.js.map +1 -0
- package/dist/{chunk-73ATPIYX.js → chunk-PZTWU277.js} +36 -71
- package/dist/chunk-PZTWU277.js.map +1 -0
- package/dist/{chunk-M5UCXTIT.js → chunk-QD4VOILZ.js} +22 -18
- package/dist/chunk-QD4VOILZ.js.map +1 -0
- package/dist/chunk-QJ5UZAH5.js +2718 -0
- package/dist/chunk-QJ5UZAH5.js.map +1 -0
- package/dist/chunk-QK7WUO7A.js +283 -0
- package/dist/chunk-QK7WUO7A.js.map +1 -0
- package/dist/{chunk-A4SLHOW7.js → chunk-QZVKRDJY.js} +27 -19
- package/dist/chunk-QZVKRDJY.js.map +1 -0
- package/dist/chunk-REO3PXXO.js +71 -0
- package/dist/chunk-REO3PXXO.js.map +1 -0
- package/dist/{chunk-EZATO3DW.js → chunk-RVO7L4FX.js} +594 -17
- package/dist/chunk-RVO7L4FX.js.map +1 -0
- package/dist/{chunk-XQUSIEKA.js → chunk-SZMHCEYV.js} +63 -14
- package/dist/chunk-SZMHCEYV.js.map +1 -0
- package/dist/chunk-TYRNTSPJ.js +243 -0
- package/dist/chunk-TYRNTSPJ.js.map +1 -0
- package/dist/chunk-UJ3L2MS7.js +238 -0
- package/dist/chunk-UJ3L2MS7.js.map +1 -0
- package/dist/chunk-V7MO5EDS.js +120 -0
- package/dist/chunk-V7MO5EDS.js.map +1 -0
- package/dist/chunk-WB2X37MO.js +415 -0
- package/dist/chunk-WB2X37MO.js.map +1 -0
- package/dist/{chunk-NTMMF5UD.js → chunk-X44FKNKP.js} +525 -252
- package/dist/chunk-X44FKNKP.js.map +1 -0
- package/dist/chunk-XEYH6ZZI.js +289 -0
- package/dist/chunk-XEYH6ZZI.js.map +1 -0
- package/dist/cli.js +267 -44
- package/dist/cli.js.map +1 -1
- package/dist/{config-PRJGWW7J.js → config-HMFMCY2M.js} +31 -5
- package/dist/contract-JH2WBIO6.js +8 -0
- package/dist/conventions-NJF2OPVQ.js +146 -0
- package/dist/conventions-NJF2OPVQ.js.map +1 -0
- package/dist/diagnostics-THH42ZMP.js +23 -0
- package/dist/execute-KFNNZZHI.js +35 -0
- package/dist/{generate-EGB4KV2B.js → generate-FW5C6NP7.js} +37 -20
- package/dist/{generate-EGB4KV2B.js.map → generate-FW5C6NP7.js.map} +1 -1
- package/dist/git-W72QQ6GT.js +33 -0
- package/dist/init-HTN4V67Q.js +24 -0
- package/dist/{operations-BYT626KQ.js → operations-BCFOBH42.js} +27 -15
- package/dist/{operations-BYT626KQ.js.map → operations-BCFOBH42.js.map} +1 -1
- package/dist/permissions-4AKNQZHK.js +18 -0
- package/dist/plan-SIZLGA4V.js +34 -0
- package/dist/{policy-6S4GFC76.js → policy-DT6Q6ZVM.js} +20 -6
- package/dist/policy-DT6Q6ZVM.js.map +1 -0
- package/dist/pr-OMWCGUOW.js +39 -0
- package/dist/pr-review-N24CZPKR.js +32 -0
- package/dist/pr-review-N24CZPKR.js.map +1 -0
- package/dist/prd-5OFTZAHJ.js +33 -0
- package/dist/prd-5OFTZAHJ.js.map +1 -0
- package/dist/ps-RFORTCUD.js +93 -0
- package/dist/ps-RFORTCUD.js.map +1 -0
- package/dist/recorder-KCCPMKQG.js +32 -0
- package/dist/recorder-KCCPMKQG.js.map +1 -0
- package/dist/registry-GLPXOMFG.js +15 -0
- package/dist/registry-GLPXOMFG.js.map +1 -0
- package/dist/{resume-PPCNQVVV.js → resume-M5RLKP4I.js} +55 -34
- package/dist/{resume-PPCNQVVV.js.map → resume-M5RLKP4I.js.map} +1 -1
- package/dist/review-YM4JAABY.js +35 -0
- package/dist/review-YM4JAABY.js.map +1 -0
- package/dist/routing-NNT5WKGX.js +94 -0
- package/dist/routing-NNT5WKGX.js.map +1 -0
- package/dist/run-PZUKHPQK.js +54 -0
- package/dist/run-PZUKHPQK.js.map +1 -0
- package/dist/session-publisher-T7EI3CIP.js +11 -0
- package/dist/session-publisher-T7EI3CIP.js.map +1 -0
- package/dist/{state-manager-2EDVDBAR.js → state-manager-GQH32C44.js} +4 -2
- package/dist/state-manager-GQH32C44.js.map +1 -0
- package/dist/usage-V2CHE5PY.js +177 -0
- package/dist/usage-V2CHE5PY.js.map +1 -0
- package/dist/{web-EF5WIIGG.js → web-EA3KGNFN.js} +45 -30
- package/dist/web-EA3KGNFN.js.map +1 -0
- package/package.json +6 -2
- package/prompts/execute.md +7 -8
- package/prompts/plan.md +4 -5
- package/prompts/pr.md +2 -2
- package/web/AGENTS.md +152 -13
- package/web/public/app.css +380 -109
- package/web/public/app.js +525 -25
- package/web/public/index.html +68 -1
- package/dist/analyze-OEET7KGE.js.map +0 -1
- package/dist/chunk-3QHK4YQA.js.map +0 -1
- package/dist/chunk-42PJFMAR.js +0 -605
- package/dist/chunk-42PJFMAR.js.map +0 -1
- package/dist/chunk-4JSOYE4G.js +0 -368
- package/dist/chunk-4JSOYE4G.js.map +0 -1
- package/dist/chunk-56KS3TFQ.js.map +0 -1
- package/dist/chunk-73ATPIYX.js.map +0 -1
- package/dist/chunk-A4SLHOW7.js.map +0 -1
- package/dist/chunk-BAUPQXPE.js +0 -173
- package/dist/chunk-BAUPQXPE.js.map +0 -1
- package/dist/chunk-BYZDPR7N.js.map +0 -1
- package/dist/chunk-CD5ULRVC.js +0 -615
- package/dist/chunk-CD5ULRVC.js.map +0 -1
- package/dist/chunk-DIDUVL6O.js +0 -1365
- package/dist/chunk-DIDUVL6O.js.map +0 -1
- package/dist/chunk-EZATO3DW.js.map +0 -1
- package/dist/chunk-FADTCQGM.js.map +0 -1
- package/dist/chunk-FJP3UQGD.js.map +0 -1
- package/dist/chunk-LWRSMTTZ.js +0 -18
- package/dist/chunk-LWRSMTTZ.js.map +0 -1
- package/dist/chunk-M5UCXTIT.js.map +0 -1
- package/dist/chunk-M66SJHQQ.js +0 -206
- package/dist/chunk-M66SJHQQ.js.map +0 -1
- package/dist/chunk-MJULIKQU.js.map +0 -1
- package/dist/chunk-NS6R37II.js.map +0 -1
- package/dist/chunk-NTMMF5UD.js.map +0 -1
- package/dist/chunk-QCPGHBSZ.js.map +0 -1
- package/dist/chunk-XQUSIEKA.js.map +0 -1
- package/dist/chunk-Y5ZDJ4PJ.js.map +0 -1
- package/dist/execute-XBNVL753.js +0 -18
- package/dist/init-AJHCXJBP.js +0 -10
- package/dist/plan-QOZ3RIO6.js +0 -21
- package/dist/policy-6S4GFC76.js.map +0 -1
- package/dist/pr-EBBHD2ZX.js +0 -26
- package/dist/pr-review-VVMW5PO4.js +0 -20
- package/dist/prd-JQSZF7PR.js +0 -21
- package/dist/review-3F6MARWH.js +0 -19
- package/dist/run-RXE5GDQO.js +0 -36
- package/dist/web-EF5WIIGG.js.map +0 -1
- /package/dist/{config-PRJGWW7J.js.map → agent-OX3KPHE4.js.map} +0 -0
- /package/dist/{chunk-LAFNLLQP.js.map → chunk-KMRFRXIT.js.map} +0 -0
- /package/dist/{execute-XBNVL753.js.map → config-HMFMCY2M.js.map} +0 -0
- /package/dist/{init-AJHCXJBP.js.map → contract-JH2WBIO6.js.map} +0 -0
- /package/dist/{plan-QOZ3RIO6.js.map → diagnostics-THH42ZMP.js.map} +0 -0
- /package/dist/{pr-EBBHD2ZX.js.map → execute-KFNNZZHI.js.map} +0 -0
- /package/dist/{pr-review-VVMW5PO4.js.map → git-W72QQ6GT.js.map} +0 -0
- /package/dist/{prd-JQSZF7PR.js.map → init-HTN4V67Q.js.map} +0 -0
- /package/dist/{review-3F6MARWH.js.map → permissions-4AKNQZHK.js.map} +0 -0
- /package/dist/{run-RXE5GDQO.js.map → plan-SIZLGA4V.js.map} +0 -0
- /package/dist/{state-manager-2EDVDBAR.js.map → pr-OMWCGUOW.js.map} +0 -0
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/resilience/errors.ts","../src/resilience/policy.ts","../src/utils/shell.ts","../src/resilience/retry.ts","../src/utils/git.ts","../src/storage/paths.ts","../src/storage/schemas.ts","../src/storage/resolve.ts","../src/storage/compat.ts"],"sourcesContent":["/**\n * Failure taxonomy.\n *\n * Every failure the pipeline reacts to is classified here, from a structured\n * `FailureSignal`, into one `FailureKind`. The point is that the decision to\n * retry stops depending on `String.includes` over an error message: text is the\n * last resort of `classify()`, not its first move.\n *\n * The invariants of this layer — above all, that `task_execution` is never\n * retried here — are in `src/resilience/AGENTS.md`.\n */\n\n/** What went wrong, in the terms the retry policy is written in. */\nexport type FailureKind =\n | 'network'\n | 'timeout'\n | 'stalled'\n | 'rate_limit'\n | 'provider_down'\n | 'provider_crash'\n | 'authentication'\n | 'configuration'\n | 'repository_state'\n | 'task_execution'\n | 'internal'\n | 'unknown';\n\n/** Which subsystem produced the failure. */\nexport type FailureSource = 'agent' | 'github' | 'git' | 'filesystem' | 'internal';\n\n/**\n * The raw evidence of a failure, as the caller observed it.\n *\n * Everything is optional but `source`: a caller hands over what it actually\n * has. `execa` supplies `exitCode`, `signal`, `timedOut` and — on the thrown\n * error — `errno`; an HTTP client supplies `httpStatus` and `retryAfter`.\n */\nexport interface FailureSignal {\n source: FailureSource;\n exitCode?: number | null;\n /** POSIX signal name that terminated the process (`SIGTERM`, `SIGKILL`). */\n signal?: string | null;\n /** `execa`'s own verdict that it killed the child on the configured limit. */\n timedOut?: boolean;\n stdout?: string;\n stderr?: string;\n /** `ENOTFOUND`, `ECONNRESET`, `ETIMEDOUT`, `EAI_AGAIN`… */\n errno?: string;\n httpStatus?: number;\n /** A server's `Retry-After` header: delay in seconds, or an HTTP date. */\n retryAfter?: string | number;\n /** Set by the watchdog when a child produced no output for too long. */\n stalled?: boolean;\n}\n\n/** The verdict. `retryable` is advice; the attempt budget belongs to the policy. */\nexport interface ClassifiedFailure {\n kind: FailureKind;\n message: string;\n retryable: boolean;\n source: FailureSource;\n /** Present only when the server told us how long to wait. */\n retryAfterMs?: number;\n}\n\n/**\n * Kinds worth another attempt: the cause is outside the work being done and is\n * expected to pass on its own.\n */\nconst RETRYABLE_KINDS: ReadonlySet<FailureKind> = new Set<FailureKind>([\n 'network',\n 'timeout',\n 'stalled',\n 'rate_limit',\n 'provider_down',\n 'provider_crash',\n]);\n\n/**\n * Kinds that no configuration may retry. Waiting cannot fix any of them: they\n * need a human, or they belong to a different loop entirely (`task_execution`\n * is the pipeline's own correction cycle — see `AGENTS.md` here).\n */\nconst HUMAN_ACTION_KINDS: ReadonlySet<FailureKind> = new Set<FailureKind>([\n 'authentication',\n 'configuration',\n 'repository_state',\n 'task_execution',\n]);\n\n/** Whether this layer considers `kind` worth retrying at all. */\nexport function isRetryableKind(kind: FailureKind): boolean {\n return RETRYABLE_KINDS.has(kind);\n}\n\n/** Whether `kind` escalates to a human instead of being retried, ever. */\nexport function requiresHumanAction(kind: FailureKind): boolean {\n return HUMAN_ACTION_KINDS.has(kind);\n}\n\n/* ── carrying a verdict through a throw ─────────────────────────────────── */\n\n/**\n * An `Error` that carries its own classification.\n *\n * A `catch` normally flattens a failure back into a string, which is exactly\n * the loss this layer exists to prevent: the caller that has to decide between\n * \"retry\" and \"escalate to a human\" then has nothing but a message again. Any\n * boundary that must throw — a provider, a phase — throws this instead.\n *\n * `action` is what a human has to do, and it is only ever set for a kind that\n * needs one (`gh auth login` for `authentication`). It is written at the throw\n * site because only that site knows which CLI is involved.\n */\nexport class ClassifiedError extends Error {\n readonly failure: ClassifiedFailure;\n readonly action: string | undefined;\n\n constructor(message: string, failure: ClassifiedFailure, action?: string) {\n super(message);\n this.name = 'ClassifiedError';\n this.failure = failure;\n this.action = action;\n }\n}\n\n/** The verdict an error carries, or `null` when it carries none. */\nexport function failureOf(error: unknown): ClassifiedFailure | null {\n return error instanceof ClassifiedError ? error.failure : null;\n}\n\n/** The human action an error asks for, or `null`. */\nexport function actionOf(error: unknown): string | null {\n return error instanceof ClassifiedError ? (error.action ?? null) : null;\n}\n\n/* ── step 1: errno ──────────────────────────────────────────────────────── */\n\nconst ERRNO_KINDS: Readonly<Record<string, FailureKind>> = {\n ENOTFOUND: 'network',\n EAI_AGAIN: 'network',\n ECONNRESET: 'network',\n ECONNREFUSED: 'network',\n ECONNABORTED: 'network',\n EHOSTUNREACH: 'network',\n ENETUNREACH: 'network',\n ENETDOWN: 'network',\n EPIPE: 'network',\n ETIMEDOUT: 'timeout',\n ENOENT: 'configuration',\n EACCES: 'configuration',\n EPERM: 'configuration',\n};\n\nfunction kindFromErrno(errno: string | undefined): FailureKind | undefined {\n if (!errno) return undefined;\n return ERRNO_KINDS[errno.toUpperCase()];\n}\n\n/* ── step 2: HTTP status ────────────────────────────────────────────────── */\n\n/**\n * GitHub reports a secondary rate limit as `403`, not `429`, so the text is\n * consulted before calling a `403` an authentication failure. Everywhere else\n * the status alone decides.\n */\nfunction kindFromHttpStatus(status: number | undefined, lowered: string): FailureKind | undefined {\n if (status === undefined || status < 400) return undefined;\n if (status === 429) return 'rate_limit';\n if (status === 401) return 'authentication';\n if (status === 403)\n return matchesAny(lowered, RATE_LIMIT_PATTERNS) ? 'rate_limit' : 'authentication';\n if (status === 408) return 'timeout';\n if (status >= 500) return 'provider_down';\n return 'configuration';\n}\n\n/* ── step 3: how the process ended ──────────────────────────────────────── */\n\nconst CRASH_SIGNALS: ReadonlySet<string> = new Set(['SIGKILL', 'SIGABRT', 'SIGSEGV', 'SIGBUS']);\n\nfunction kindFromTermination(signal: FailureSignal): FailureKind | undefined {\n if (signal.stalled === true) return 'stalled';\n if (signal.timedOut === true) return 'timeout';\n const name = signal.signal?.toUpperCase();\n if (!name) return undefined;\n if (name === 'SIGTERM') return 'timeout';\n if (CRASH_SIGNALS.has(name)) return 'provider_crash';\n return undefined;\n}\n\n/* ── step 4: exit codes we know the meaning of ──────────────────────────── */\n\n/**\n * Only codes whose meaning is unambiguous. `143` (SIGTERM) is deliberately\n * absent: the Claude CLI handles the signal itself and exits `143` for a\n * user's Ctrl+C just as it does for our timeout, so it decides nothing on its\n * own — `timedOut` or `signal` is what tells the two apart.\n */\nconst EXIT_CODE_KINDS: Readonly<Record<number, FailureKind>> = {\n 75: 'provider_down', // EX_TEMPFAIL\n 126: 'configuration', // found, not executable\n 127: 'configuration', // command not found\n 137: 'provider_crash', // 128 + SIGKILL, typically the OOM killer\n 139: 'provider_crash', // 128 + SIGSEGV\n};\n\nfunction kindFromExitCode(exitCode: number | null | undefined): FailureKind | undefined {\n if (exitCode === null || exitCode === undefined) return undefined;\n return EXIT_CODE_KINDS[exitCode];\n}\n\n/* ── step 5: text, and only as a last resort ────────────────────────────── */\n\nconst RATE_LIMIT_PATTERNS = [\n 'rate limit',\n 'rate-limit',\n 'ratelimit',\n 'too many requests',\n 'http 429',\n] as const;\n\n/**\n * Ordered on purpose. A message is scanned rule by rule and the first hit\n * wins, so the specific causes (rate limit, credentials) are consulted before\n * the generic ones, and `task_execution` comes last: an infrastructure failure\n * surfacing inside a test run is still an infrastructure failure.\n */\ntype TextPattern = string | RegExp;\n\nconst TEXT_RULES: readonly { kind: FailureKind; patterns: readonly TextPattern[] }[] = [\n { kind: 'rate_limit', patterns: RATE_LIMIT_PATTERNS },\n {\n kind: 'authentication',\n patterns: [\n 'authentication failed',\n 'authentication_error',\n 'gh auth login',\n 'not logged into',\n 'bad credentials',\n 'invalid api key',\n 'invalid_api_key',\n 'token expired',\n 'expired token',\n 'unauthorized',\n 'http 401',\n 'http 403',\n ],\n },\n {\n kind: 'network',\n patterns: [\n 'connection reset',\n 'connection refused',\n 'connection aborted',\n 'network error',\n 'network unavailable',\n 'temporary failure',\n 'econnreset',\n 'econnrefused',\n 'enotfound',\n 'eai_again',\n 'socket hang up',\n // What Go prints, and therefore what `gh` prints: its network errors\n // never mention \"connection reset\", they mention the resolver.\n 'no such host',\n 'dial tcp',\n 'i/o timeout',\n 'tls handshake timeout',\n 'server misbehaving',\n 'error connecting to',\n ],\n },\n // Before the timeout rule: a stall *is* a kind of timeout, and the specific\n // reading is the useful one — it has its own budget and its own escalation.\n { kind: 'stalled', patterns: ['produced no output for', '(stalled)'] },\n { kind: 'timeout', patterns: ['timed out', 'timeout', 'etimedout'] },\n {\n kind: 'provider_down',\n patterns: [\n 'service unavailable',\n 'temporarily unavailable',\n 'overloaded',\n 'bad gateway',\n 'gateway timeout',\n 'internal server error',\n 'http 500',\n 'http 502',\n 'http 503',\n 'http 504',\n ],\n },\n {\n kind: 'provider_crash',\n patterns: ['segmentation fault', 'core dumped', 'out of memory', 'killed by signal'],\n },\n {\n kind: 'repository_state',\n patterns: [\n 'unmerged paths',\n 'needs merge',\n 'automatic merge failed',\n 'rebase in progress',\n 'you are in the middle of',\n 'cherry-pick is in progress',\n 'not a git repository',\n ],\n },\n {\n kind: 'configuration',\n patterns: ['command not found', 'unknown flag', 'unknown option', 'no such file or directory'],\n },\n {\n kind: 'task_execution',\n patterns: [\n 'test failed',\n 'tests failed',\n 'failed tests',\n 'failing test',\n 'assertion failed',\n 'assertionerror',\n 'expect(received)',\n 'typecheck failed',\n 'npm err! test failed',\n 'lint failed',\n 'build failed',\n 'compilation failed',\n 'syntaxerror',\n 'typeerror',\n 'referenceerror',\n // What a runner actually prints: \"Tests 3 failed | 41 passed\",\n // \"Test Files 1 failed (12)\", \"Tests: 2 failed, 8 total\".\n /\\b\\d+\\s+failed\\b/,\n ],\n },\n];\n\nfunction matchesAny(lowered: string, patterns: readonly TextPattern[]): boolean {\n return patterns.some((pattern) =>\n typeof pattern === 'string' ? lowered.includes(pattern) : pattern.test(lowered),\n );\n}\n\nfunction kindFromText(lowered: string): FailureKind | undefined {\n if (!lowered) return undefined;\n for (const rule of TEXT_RULES) {\n if (matchesAny(lowered, rule.patterns)) return rule.kind;\n }\n return undefined;\n}\n\n/* ── Retry-After ────────────────────────────────────────────────────────── */\n\nconst RETRY_AFTER_IN_TEXT = /retry[\\s-]?after:?\\s*(\\d+)/i;\n\n/**\n * `Retry-After` is either a delay in seconds or an HTTP date. Both are\n * accepted; anything else, and a date already in the past, yields nothing\n * rather than a bogus zero.\n */\nfunction parseRetryAfter(value: string | number | undefined, now: number): number | undefined {\n if (value === undefined) return undefined;\n if (typeof value === 'number')\n return Number.isFinite(value) && value >= 0 ? value * 1000 : undefined;\n\n const trimmed = value.trim();\n if (trimmed === '') return undefined;\n if (/^\\d+$/.test(trimmed)) return Number(trimmed) * 1000;\n\n const at = Date.parse(trimmed);\n if (Number.isNaN(at)) return undefined;\n return at > now ? at - now : undefined;\n}\n\nfunction resolveRetryAfterMs(signal: FailureSignal, text: string, now: number): number | undefined {\n const explicit = parseRetryAfter(signal.retryAfter, now);\n if (explicit !== undefined) return explicit;\n\n const inText = RETRY_AFTER_IN_TEXT.exec(text);\n return inText ? Number(inText[1]) * 1000 : undefined;\n}\n\n/* ── message ────────────────────────────────────────────────────────────── */\n\nfunction combineOutput(signal: FailureSignal): string {\n return [signal.stderr, signal.stdout].filter((part) => part && part.trim() !== '').join('\\n');\n}\n\n/** The output verbatim when there is any, and a synthesised line when there is not. */\nfunction describe(signal: FailureSignal, text: string): string {\n const trimmed = text.trim();\n if (trimmed !== '') return trimmed;\n if (signal.errno) return `${signal.source} failed with ${signal.errno}`;\n if (signal.httpStatus !== undefined)\n return `${signal.source} responded HTTP ${signal.httpStatus}`;\n if (signal.timedOut === true) return `${signal.source} timed out`;\n if (signal.signal) return `${signal.source} was terminated by ${signal.signal}`;\n if (signal.exitCode !== null && signal.exitCode !== undefined) {\n return `${signal.source} exited with code ${signal.exitCode}`;\n }\n return `${signal.source} failed for an unreported reason`;\n}\n\n/* ── the classifier ─────────────────────────────────────────────────────── */\n\nexport interface ClassifyOptions {\n /** Injectable clock, so an HTTP-date `Retry-After` is testable. */\n now?: () => number;\n}\n\n/**\n * Classify a failure by precedence: errno and HTTP status first (a machine\n * told us), then how the process ended, then an exit code we know the meaning\n * of, and only then the text — which is a heuristic and is treated as one.\n */\nexport function classify(signal: FailureSignal, options: ClassifyOptions = {}): ClassifiedFailure {\n const now = options.now?.() ?? Date.now();\n const text = combineOutput(signal);\n const lowered = text.toLowerCase();\n\n const kind =\n kindFromErrno(signal.errno) ??\n kindFromHttpStatus(signal.httpStatus, lowered) ??\n kindFromTermination(signal) ??\n kindFromExitCode(signal.exitCode) ??\n kindFromText(lowered) ??\n 'unknown';\n\n const retryAfterMs = resolveRetryAfterMs(signal, text, now);\n\n return {\n kind,\n message: describe(signal, text),\n retryable: isRetryableKind(kind),\n source: signal.source,\n ...(retryAfterMs === undefined ? {} : { retryAfterMs }),\n };\n}\n","/**\n * Retry policy: how long to wait, how many times, and what to do when the\n * budget runs out.\n *\n * `errors.ts` answers *what went wrong*; this module answers *what to do about\n * it*. It is pure — no I/O, no clock of its own, no knowledge of phases — so\n * every decision here is reproducible from its arguments. `retry.ts` is the\n * only executor that acts on what this module returns.\n *\n * The run-level state machine lives here too, for the same reason: whether a\n * run may go from `blocked` to `running` is a policy question, and there must\n * be exactly one answer to it in the codebase.\n *\n * The invariants of this layer are in `src/resilience/AGENTS.md`.\n */\n\nimport { type FailureKind, requiresHumanAction } from './errors.js';\n\n/* ── the policy ─────────────────────────────────────────────────────────── */\n\n/** What to do with the provider once a kind keeps failing. */\nexport type FailoverMode = 'never' | 'after_attempts' | 'immediate';\n\n/** What a failure becomes once the attempt budget is spent. */\nexport type ExhaustedAction = 'fail' | 'block';\n\nexport interface RetryPolicy {\n /** `0` means the kind is never retried by this layer. */\n maxAttempts: number;\n initialDelayMs: number;\n /** The ceiling of the backoff — honoured even under `retryForever`. */\n maxDelayMs: number;\n backoffFactor: number;\n jitter: 'none' | 'full';\n retryForever: boolean;\n failover: FailoverMode;\n /** Attempts to burn before `failover: 'after_attempts'` kicks in. */\n failoverAfterAttempts: number;\n onExhausted: ExhaustedAction;\n}\n\n/** A partial policy, as a configuration layer supplies it. */\nexport type RetryPolicyOverride = Partial<RetryPolicy>;\n\n/* ── configuration ──────────────────────────────────────────────────────── */\n\nexport type ResilienceProfile = 'default' | 'continuous';\n\n/**\n * The configuration keys of `resilience.retry`, one per `FailureKind` in the\n * camelCase spelling the JSON uses.\n */\nexport type RetryConfigKey =\n | 'network'\n | 'timeout'\n | 'stalled'\n | 'rateLimit'\n | 'providerDown'\n | 'providerCrash'\n | 'authentication'\n | 'configuration'\n | 'repositoryState'\n | 'taskExecution'\n | 'internal'\n | 'unknown';\n\nconst CONFIG_KEYS: Readonly<Record<FailureKind, RetryConfigKey>> = {\n network: 'network',\n timeout: 'timeout',\n stalled: 'stalled',\n rate_limit: 'rateLimit',\n provider_down: 'providerDown',\n provider_crash: 'providerCrash',\n authentication: 'authentication',\n configuration: 'configuration',\n repository_state: 'repositoryState',\n task_execution: 'taskExecution',\n internal: 'internal',\n unknown: 'unknown',\n};\n\n/** The configuration key `resilience.retry` uses for `kind`. */\nexport function retryConfigKey(kind: FailureKind): RetryConfigKey {\n return CONFIG_KEYS[kind];\n}\n\n/**\n * Every key `resilience.retry` accepts, for the layers that have to iterate\n * them (the schema in `storage/schemas.ts`, the merge in `config.ts`). Derived\n * from `CONFIG_KEYS` rather than written a second time, so a new `FailureKind`\n * cannot reach the configuration surface half-declared.\n */\nexport const RETRY_CONFIG_KEYS: readonly RetryConfigKey[] = Object.values(CONFIG_KEYS);\n\n/**\n * The slice of the `resilience` configuration this module reads. The full key\n * (providers, queue, watchdog, journal, decompose) is a superset of this, and\n * satisfies it structurally.\n */\nexport interface PolicyConfig {\n profile?: ResilienceProfile;\n /**\n * Whether a credential failure may migrate to another provider. Explicit on\n * purpose, never a default: switching provider because the main one lost its\n * credential hides exactly what the user needs to be told.\n */\n failoverOnAuth?: boolean;\n retry?: Partial<Record<RetryConfigKey, RetryPolicyOverride>>;\n}\n\n/* ── the defaults ───────────────────────────────────────────────────────── */\n\nconst SECOND = 1000;\n\nfunction policy(overrides: RetryPolicyOverride): RetryPolicy {\n return {\n maxAttempts: 0,\n initialDelayMs: 0,\n maxDelayMs: 0,\n backoffFactor: 2,\n jitter: 'full',\n retryForever: false,\n failover: 'never',\n failoverAfterAttempts: 2,\n onExhausted: 'fail',\n ...overrides,\n };\n}\n\n/**\n * The table of the PRD, verbatim. Changing a number here changes documented\n * behaviour: `policy.test.ts` asserts this table cell by cell on purpose.\n */\nconst BASE_POLICIES: Readonly<Record<FailureKind, RetryPolicy>> = {\n network: policy({ maxAttempts: 8, initialDelayMs: 2 * SECOND, maxDelayMs: 120 * SECOND }),\n timeout: policy({\n maxAttempts: 2,\n initialDelayMs: 30 * SECOND,\n maxDelayMs: 120 * SECOND,\n failover: 'after_attempts',\n }),\n stalled: policy({\n maxAttempts: 2,\n initialDelayMs: 15 * SECOND,\n maxDelayMs: 15 * SECOND,\n failover: 'after_attempts',\n }),\n rate_limit: policy({\n maxAttempts: 6,\n initialDelayMs: 60 * SECOND,\n maxDelayMs: 900 * SECOND,\n failover: 'after_attempts',\n }),\n provider_down: policy({\n maxAttempts: 4,\n initialDelayMs: 10 * SECOND,\n maxDelayMs: 300 * SECOND,\n failover: 'after_attempts',\n }),\n provider_crash: policy({\n maxAttempts: 3,\n initialDelayMs: 5 * SECOND,\n maxDelayMs: 60 * SECOND,\n failover: 'after_attempts',\n }),\n authentication: policy({ maxAttempts: 0, onExhausted: 'block' }),\n configuration: policy({ maxAttempts: 0 }),\n repository_state: policy({ maxAttempts: 0, onExhausted: 'block' }),\n task_execution: policy({ maxAttempts: 0 }),\n internal: policy({ maxAttempts: 2, initialDelayMs: 5 * SECOND, maxDelayMs: 5 * SECOND }),\n unknown: policy({ maxAttempts: 2, initialDelayMs: 5 * SECOND, maxDelayMs: 5 * SECOND }),\n};\n\n/**\n * The `continuous` profile — autonomous, long-running execution. It only ever\n * *widens* a budget: what is not retryable under `default` is not retryable\n * here either.\n */\nconst CONTINUOUS_OVERRIDES: Readonly<Partial<Record<FailureKind, RetryPolicyOverride>>> = {\n network: { retryForever: true },\n timeout: { maxAttempts: 3 },\n stalled: { maxAttempts: 3 },\n rate_limit: { retryForever: true },\n provider_down: { retryForever: true, failover: 'after_attempts' },\n provider_crash: { maxAttempts: 5 },\n};\n\n/**\n * Resolve the policy of a `FailureKind`: base table → profile → user\n * configuration, in that order.\n *\n * The kinds that need a human (`authentication`, `configuration`,\n * `repository_state`, `task_execution`) are clamped **after** the user layer:\n * no configuration, no profile and no `retryForever` can buy them an attempt.\n * That clamp is the golden rule of `AGENTS.md` made unbypassable.\n */\nexport function resolvePolicy(kind: FailureKind, config: PolicyConfig = {}): RetryPolicy {\n const base = BASE_POLICIES[kind];\n const profile = config.profile === 'continuous' ? CONTINUOUS_OVERRIDES[kind] : undefined;\n const user = config.retry?.[retryConfigKey(kind)];\n\n const resolved: RetryPolicy = { ...base, ...profile, ...user };\n\n if (!requiresHumanAction(kind)) return sanitize(resolved);\n\n return sanitize({\n ...resolved,\n maxAttempts: 0,\n retryForever: false,\n failover:\n kind === 'authentication' && config.failoverOnAuth === true ? resolved.failover : 'never',\n });\n}\n\n/** Guard against a configuration layer supplying a number that breaks the math. */\nfunction sanitize(value: RetryPolicy): RetryPolicy {\n return {\n ...value,\n maxAttempts: Math.max(0, Math.trunc(value.maxAttempts)),\n initialDelayMs: Math.max(0, value.initialDelayMs),\n maxDelayMs: Math.max(0, value.maxDelayMs),\n backoffFactor: value.backoffFactor >= 1 ? value.backoffFactor : 1,\n failoverAfterAttempts: Math.max(1, Math.trunc(value.failoverAfterAttempts)),\n };\n}\n\n/* ── the decisions ──────────────────────────────────────────────────────── */\n\n/**\n * Whether another attempt is allowed after `attemptsMade` attempts have\n * already failed.\n */\nexport function shouldRetry(policy: RetryPolicy, attemptsMade: number): boolean {\n if (policy.maxAttempts <= 0) return false;\n if (policy.retryForever) return true;\n return attemptsMade < policy.maxAttempts;\n}\n\n/** Whether the next attempt should be handed to a different provider. */\nexport function shouldFailover(policy: RetryPolicy, attemptsMade: number): boolean {\n if (policy.failover === 'never') return false;\n if (policy.failover === 'immediate') return true;\n return attemptsMade >= policy.failoverAfterAttempts;\n}\n\nexport interface DelayOptions {\n /** Injectable RNG, so the jitter is deterministic under test. */\n random?: () => number;\n /** What the server asked for, when it asked (`Retry-After`). */\n retryAfterMs?: number;\n}\n\n/**\n * The delay before attempt number `attemptsMade + 1`.\n *\n * `ceiling = min(maxDelayMs, initialDelayMs * backoffFactor^(attemptsMade-1))`,\n * and full jitter — the default — draws uniformly from `[0, ceiling)`. The\n * ceiling applies under `retryForever` exactly as it does otherwise: waiting\n * forever is allowed, hammering is not.\n *\n * A server-supplied `Retry-After` wins outright, un-jittered and uncapped: the\n * point of honouring it is not to come back before it says, and capping it by\n * `maxDelayMs` would do precisely that.\n */\nexport function computeDelayMs(\n policy: RetryPolicy,\n attemptsMade: number,\n options: DelayOptions = {},\n): number {\n if (options.retryAfterMs !== undefined && options.retryAfterMs >= 0) {\n return Math.round(options.retryAfterMs);\n }\n\n const exponent = Math.max(0, attemptsMade - 1);\n const growth = policy.initialDelayMs * policy.backoffFactor ** exponent;\n const ceiling = Math.min(policy.maxDelayMs, growth);\n if (ceiling <= 0) return 0;\n\n if (policy.jitter === 'none') return Math.round(ceiling);\n\n const random = options.random ?? Math.random;\n return Math.round(random() * ceiling);\n}\n\n/* ── the abortable delay ────────────────────────────────────────────────── */\n\n/**\n * Wait `ms`, unless `signal` fires first.\n *\n * Resolves `true` when the full delay elapsed and `false` when it was cut\n * short — a caller that waited fifteen minutes and a caller that was\n * interrupted must not take the same next step. It never throws: an abort is\n * an expected outcome here, not an error.\n */\nexport function abortableDelay(\n ms: number,\n options: { signal?: AbortSignal } = {},\n): Promise<boolean> {\n const { signal } = options;\n if (signal?.aborted === true) return Promise.resolve(false);\n if (ms <= 0) return Promise.resolve(true);\n\n return new Promise((resolve) => {\n const onAbort = () => {\n clearTimeout(timer);\n resolve(false);\n };\n\n const timer = setTimeout(() => {\n signal?.removeEventListener('abort', onAbort);\n resolve(true);\n }, ms);\n\n signal?.addEventListener('abort', onAbort, { once: true });\n });\n}\n\n/* ── the run state machine ──────────────────────────────────────────────── */\n\n/**\n * The state of a run — a queued issue in `execution-plan.json`, or the single\n * issue of a `tasks.json`.\n */\nexport type RunStatus =\n | 'queued'\n | 'running'\n | 'waiting'\n | 'retrying'\n | 'paused'\n | 'blocked'\n | 'failed'\n | 'completed'\n | 'cancelled';\n\n/**\n * Every legal transition, in one place.\n *\n * `completed` and `cancelled` are terminal: nothing leaves them. `blocked` has\n * exits, but none the pipeline may take on its own — see `canTransition`.\n */\nexport const RUN_TRANSITIONS: Readonly<Record<RunStatus, readonly RunStatus[]>> = {\n queued: ['running', 'blocked', 'cancelled'],\n running: ['waiting', 'retrying', 'paused', 'blocked', 'failed', 'completed', 'cancelled'],\n waiting: ['running', 'retrying', 'paused', 'blocked', 'failed', 'cancelled'],\n retrying: ['running', 'waiting', 'paused', 'blocked', 'failed', 'cancelled'],\n paused: ['running', 'blocked', 'cancelled'],\n blocked: ['running', 'cancelled'],\n failed: ['running', 'cancelled'],\n completed: [],\n cancelled: [],\n};\n\n/** Nothing leaves these, by anyone. */\nconst TERMINAL_STATUSES: ReadonlySet<RunStatus> = new Set<RunStatus>(['completed', 'cancelled']);\n\n/** Whether `status` is terminal. */\nexport function isTerminalRunStatus(status: RunStatus): boolean {\n return TERMINAL_STATUSES.has(status);\n}\n\n/** Who is attempting the transition. The pipeline is the default. */\nexport type TransitionActor = 'pipeline' | 'human';\n\n/**\n * Whether `from → to` is legal for `actor`.\n *\n * `blocked` exists precisely to stop the machine until someone looks at it, so\n * the pipeline may enter it but never leave it: only `actor: 'human'` — a\n * `resume`, a `cancel` — can. A pipeline able to unblock itself would spin on\n * the same missing credential forever.\n */\nexport function canTransition(\n from: RunStatus,\n to: RunStatus,\n options: { actor?: TransitionActor } = {},\n): boolean {\n if (!RUN_TRANSITIONS[from].includes(to)) return false;\n if (from === 'blocked' && (options.actor ?? 'pipeline') !== 'human') return false;\n return true;\n}\n","import { basename } from 'node:path';\nimport { type Options as ExecaOptions, execa } from 'execa';\nimport {\n type ClassifiedFailure,\n classify,\n type FailureSignal,\n type FailureSource,\n} from '../resilience/errors.js';\nimport type { RetryPolicy } from '../resilience/policy.js';\nimport { type RetryPolicyFor, withRetry } from '../resilience/retry.js';\n\nexport interface ExecResult {\n stdout: string;\n stderr: string;\n exitCode: number;\n /**\n * The classified failure of the last attempt. Only ever set on the retry\n * path, and only when that attempt failed — a caller that passes no `retry`\n * gets exactly the three fields it has always got.\n */\n failure?: ClassifiedFailure;\n /** How many times the command actually ran. Only set on the retry path. */\n attempts?: number;\n}\n\n/** What `onRetryAttempt` is told once an attempt has produced a result. */\nexport interface RunRetryAttempt {\n /** 1-based number of the attempt that just finished. */\n attempt: number;\n result: ExecResult;\n /** `null` when the attempt succeeded. */\n failure: ClassifiedFailure | null;\n /** Whether another attempt follows this one. */\n willRetry: boolean;\n /** The wait before that next attempt; `0` when there is none. */\n delayMs: number;\n}\n\nexport interface RunOptions extends ExecaOptions {\n /**\n * Opt in to retrying this invocation.\n *\n * Absent — the default, and what every call site of this project did before\n * this option existed — means one `execa` call with `reject: false` and no\n * extra attempt, byte for byte.\n *\n * Only pass a policy for a command whose non-zero exit means *it failed*.\n * A command whose non-zero exit is a legitimate answer (`git rev-parse\n * --verify --quiet`, `gh pr view` on a branch with no PR) is safe anyway —\n * an unclassifiable exit lands on `unknown`, which is not a retryable kind —\n * but the intent is worth being explicit about.\n *\n * A function instead of a policy resolves it from the failure that actually\n * happened, which is how one `gh` call gets the `network` budget for a DNS\n * blip and the `rate_limit` budget for a rate limit.\n */\n retry?: RetryPolicy | RetryPolicyFor;\n /** Overrides the subsystem inferred from `command` (`git`, `gh`). */\n source?: FailureSource;\n /**\n * Cuts a pending backoff short. Named apart from execa's own `cancelSignal`\n * because it governs the wait *between* attempts, not the child process.\n */\n retrySignal?: AbortSignal;\n /** Observe each attempt: log it, publish an event, persist the error. */\n onRetryAttempt?: (info: RunRetryAttempt) => void | Promise<void>;\n}\n\n/* ── which subsystem produced the failure ───────────────────────────────── */\n\nconst SOURCE_BY_COMMAND: Readonly<Record<string, FailureSource>> = {\n git: 'git',\n gh: 'github',\n};\n\nfunction inferSource(command: string): FailureSource {\n return SOURCE_BY_COMMAND[basename(command)] ?? 'internal';\n}\n\n/* ── git invocations that are never repeated ────────────────────────────── */\n\n/**\n * `git` global options that swallow the token after them, so the subcommand of\n * `git -C /repo push --force` is `push` and not `/repo`.\n */\nconst GIT_GLOBAL_OPTIONS_WITH_VALUE: ReadonlySet<string> = new Set([\n '-C',\n '-c',\n '--git-dir',\n '--work-tree',\n '--namespace',\n '--exec-path',\n '--super-prefix',\n]);\n\n/**\n * git invocations no retry policy may repeat, whatever the caller asked for.\n *\n * `true` means the subcommand is never retried; a list means it is destructive\n * only when one of those tokens is present. This is the second of the Epic's\n * three limits — *no destructive operation is run automatically to fix state* —\n * enforced at the one chokepoint every `git` call of the project goes through.\n *\n * It errs on the side of not retrying: a sequencer operation left half-applied\n * (`rebase`, `cherry-pick`, `merge`, `am`, `revert`) is exactly the state a\n * blind second attempt turns into a lost commit, so those are absolute even\n * though a transient failure of one of them is conceivable.\n */\nconst DESTRUCTIVE_GIT_COMMANDS: Readonly<Record<string, true | readonly string[]>> = {\n rebase: true,\n 'cherry-pick': true,\n revert: true,\n merge: true,\n am: true,\n restore: true,\n 'filter-branch': true,\n gc: true,\n prune: true,\n 'update-ref': true,\n push: ['-f', '--force', '--force-with-lease', '--force-if-includes', '--delete', '--mirror'],\n reset: ['--hard', '--merge', '--keep'],\n clean: ['-f', '--force'],\n checkout: ['-f', '--force', '-B'],\n switch: ['-f', '--force', '--discard-changes'],\n branch: ['-d', '-D', '--delete', '-M'],\n tag: ['-d', '--delete', '-f', '--force'],\n stash: ['drop', 'clear', 'pop'],\n worktree: ['remove', 'prune'],\n submodule: ['deinit'],\n};\n\nfunction gitSubcommand(args: readonly string[]): { name: string; rest: readonly string[] } | null {\n for (let index = 0; index < args.length; index++) {\n const arg = args[index];\n if (arg === undefined) continue;\n if (GIT_GLOBAL_OPTIONS_WITH_VALUE.has(arg)) {\n index++;\n continue;\n }\n if (arg.startsWith('-')) continue;\n return { name: arg, rest: args.slice(index + 1) };\n }\n return null;\n}\n\n/**\n * Whether `rest` carries `token`. Long options also match their `=value` form,\n * and a short option also matches bundled into another (`git clean -fd` is\n * `-f -d`), which is how `-f` is actually written in practice.\n */\nfunction hasToken(rest: readonly string[], token: string): boolean {\n if (token.startsWith('--')) {\n return rest.some((arg) => arg === token || arg.startsWith(`${token}=`));\n }\n if (token.startsWith('-')) {\n const letter = token.slice(1);\n return rest.some((arg) => /^-[A-Za-z]+$/.test(arg) && arg.slice(1).includes(letter));\n }\n return rest.includes(token);\n}\n\n/** Whether this invocation may be retried at all, whatever the policy says. */\nexport function isRetryableInvocation(command: string, args: readonly string[]): boolean {\n if (basename(command) !== 'git') return true;\n\n const subcommand = gitSubcommand(args);\n if (subcommand === null) return true;\n\n const rule = DESTRUCTIVE_GIT_COMMANDS[subcommand.name];\n if (rule === undefined) return true;\n if (rule === true) return false;\n\n return !rule.some((token) => hasToken(subcommand.rest, token));\n}\n\n/* ── the failure signal ─────────────────────────────────────────────────── */\n\n/**\n * The fields execa puts on a failed result when `reject: false` is set. They\n * are not on its success type, hence the narrowing: `code` is where the spawn\n * errno lands (`ENOENT`, `ETIMEDOUT`, `ECONNRESET`, `EAI_AGAIN`), and\n * `originalMessage` is the only place the reason shows up when the child never\n * started and therefore wrote nothing to stderr.\n */\ninterface ExecaFailureFields {\n code?: unknown;\n signal?: unknown;\n timedOut?: unknown;\n originalMessage?: unknown;\n shortMessage?: unknown;\n}\n\nfunction asString(value: unknown): string | undefined {\n return typeof value === 'string' && value !== '' ? value : undefined;\n}\n\nfunction toFailureSignal(raw: unknown, result: ExecResult, source: FailureSource): FailureSignal {\n const fields = (raw ?? {}) as ExecaFailureFields;\n const errno = asString(fields.code);\n const signal = asString(fields.signal);\n // A command that never started has an empty stderr; execa's own message is\n // then the only evidence there is, so it stands in for the missing output.\n const stderr =\n result.stderr.trim() !== ''\n ? result.stderr\n : (asString(fields.originalMessage) ?? asString(fields.shortMessage) ?? '');\n\n return {\n source,\n exitCode: result.exitCode,\n stdout: result.stdout,\n stderr,\n ...(errno === undefined ? {} : { errno }),\n ...(signal === undefined ? {} : { signal }),\n ...(fields.timedOut === true ? { timedOut: true } : {}),\n };\n}\n\n/* ── the executor ───────────────────────────────────────────────────────── */\n\nasync function execOnce(\n command: string,\n args: string[],\n options: ExecaOptions,\n): Promise<{ result: ExecResult; raw: unknown }> {\n const raw = await execa(command, args, {\n reject: false,\n ...options,\n });\n\n return {\n raw,\n result: {\n stdout: raw.stdout?.toString() ?? '',\n stderr: raw.stderr?.toString() ?? '',\n exitCode: raw.exitCode ?? 1,\n },\n };\n}\n\n/**\n * Execute a command with arguments and capture its output.\n * Uses execa which calls execFile internally (no shell injection risk).\n * Does not throw on non-zero exit codes.\n *\n * With `options.retry` it becomes the retrying chokepoint every `gh` and `git`\n * call of the project already passes through: the failure is classified from\n * structured evidence (errno, signal, `timedOut`, exit code — text last) and\n * handed to `withRetry`, the single retry executor. Without it nothing changes.\n */\nexport async function run(\n command: string,\n args: string[] = [],\n options?: RunOptions,\n): Promise<ExecResult> {\n const { retry, source, retrySignal, onRetryAttempt, ...execaOptions } = options ?? {};\n\n if (retry === undefined || !isRetryableInvocation(command, args)) {\n return (await execOnce(command, args, execaOptions)).result;\n }\n\n const failureSource = source ?? inferSource(command);\n\n const outcome = await withRetry(\n async () => {\n const { result, raw } = await execOnce(command, args, execaOptions);\n const failure =\n result.exitCode === 0 ? null : classify(toFailureSignal(raw, result, failureSource));\n return { result, failure };\n },\n {\n policy: retry,\n evaluate: (attempt) => attempt.failure,\n ...(retrySignal === undefined ? {} : { signal: retrySignal }),\n ...(onRetryAttempt === undefined\n ? {}\n : {\n onAttempt: (info) =>\n onRetryAttempt({\n attempt: info.attempt,\n result: info.value.result,\n failure: info.value.failure,\n willRetry: info.willRetry,\n delayMs: info.delayMs,\n }),\n }),\n },\n );\n\n return {\n ...outcome.value.result,\n attempts: outcome.attempts,\n ...(outcome.failure === null ? {} : { failure: outcome.failure }),\n };\n}\n","/**\n * The single retry executor of the project.\n *\n * `errors.ts` says *what went wrong*, `policy.ts` says *what to do about it*,\n * and this module is the only place that actually does it: one loop, one\n * backoff, one veto. Before it existed the codebase had two independent retry\n * loops — `core/phase-runner.ts` for single-shot phases and the `execute` loop\n * of `core/engine.ts` — with their own counters, their own delays and no way\n * to keep them in step. Both now delegate here.\n *\n * The loop performs no I/O and knows nothing about phases, sessions or\n * publishers: everything observable is handed back through `onAttempt`, so the\n * caller keeps ownership of its own logging, events and persistence.\n *\n * The invariants of this layer are in `src/resilience/AGENTS.md`.\n */\n\nimport { type ClassifiedFailure, requiresHumanAction } from './errors.js';\nimport { abortableDelay, computeDelayMs, type RetryPolicy, shouldRetry } from './policy.js';\n\n/** What `onAttempt` is told once an attempt has produced a value. */\nexport interface RetryAttemptInfo<T> {\n /** 1-based number of the attempt that just finished. */\n attempt: number;\n /** Whatever `fn` returned, failure or not. */\n value: T;\n /** `null` when the attempt succeeded. */\n failure: ClassifiedFailure | null;\n /** Whether another attempt follows this one. */\n willRetry: boolean;\n /** The wait before that next attempt; `0` when there is none. */\n delayMs: number;\n}\n\n/**\n * A policy chosen from the failure that just happened.\n *\n * The kind is only known *after* an attempt, so a call site that spans several\n * kinds — every `gh` invocation is a network failure, a rate limit or an\n * authentication failure depending on the day — hands over the resolver rather\n * than one frozen policy. `resolvePolicy(failure.kind, config)` is what a\n * caller normally passes.\n */\nexport type RetryPolicyFor = (failure: ClassifiedFailure) => RetryPolicy;\n\nexport interface WithRetryOptions<T> {\n /** One policy, or one chosen per classified failure. */\n policy: RetryPolicy | RetryPolicyFor;\n /**\n * The verdict on one attempt's value: `null` for success, a classified\n * failure otherwise.\n *\n * It belongs to the caller because only the caller knows what a failure\n * looks like in its own return type — an exit code, an `ok: false`, a\n * rejected HTTP response. Callers holding structured evidence should build\n * the failure with `classify()` rather than re-deriving it from text.\n */\n evaluate: (value: T, attempt: number) => ClassifiedFailure | null;\n /** Cuts a pending backoff short; see `abortableDelay`. */\n signal?: AbortSignal;\n /**\n * Called once per attempt, before the backoff, with the decision already\n * taken. This is where a caller publishes its `retry` event, prints, or\n * persists an error — awaited, so those writes finish before the wait.\n */\n onAttempt?: (info: RetryAttemptInfo<T>) => void | Promise<void>;\n /** Injectable RNG for the jitter, so a test can assert the exact curve. */\n random?: () => number;\n /** Injectable wait, so a test never sleeps for real. */\n delay?: (ms: number, options: { signal?: AbortSignal }) => Promise<boolean>;\n}\n\nexport interface RetryOutcome<T> {\n /** The value of the last attempt — the successful one, or the final failure. */\n value: T;\n /** How many times `fn` ran. */\n attempts: number;\n /** `null` when the last attempt succeeded. */\n failure: ClassifiedFailure | null;\n /** The failure was retryable and the budget ran out. */\n exhausted: boolean;\n /** `signal` fired during a backoff, so the next attempt never happened. */\n aborted: boolean;\n}\n\n/**\n * Run `fn` until it succeeds, until the policy's budget is spent, or until\n * `signal` fires.\n *\n * Three separate conditions have to hold for another attempt, and all three\n * are checked here rather than by the caller:\n *\n * 1. the failure is retryable at all (`ClassifiedFailure.retryable`);\n * 2. the kind is not one that needs a human — `task_execution`,\n * `authentication`, `configuration` and `repository_state` are vetoed\n * outright, whatever the policy says and even under `retryForever`. This is\n * the golden rule of `AGENTS.md`, enforced a second time here: a caller can\n * hand-build a `RetryPolicy` without going through `resolvePolicy()`, so\n * the clamp there is not on its own enough;\n * 3. the policy still has budget (`shouldRetry`).\n */\nexport async function withRetry<T>(\n fn: (attempt: number) => Promise<T>,\n options: WithRetryOptions<T>,\n): Promise<RetryOutcome<T>> {\n const { policy, evaluate, signal, onAttempt, random } = options;\n const delay = options.delay ?? abortableDelay;\n const policyFor: RetryPolicyFor = typeof policy === 'function' ? policy : () => policy;\n\n for (let attempt = 1; ; attempt++) {\n const value = await fn(attempt);\n const failure = evaluate(value, attempt);\n\n if (failure === null) {\n await onAttempt?.({ attempt, value, failure, willRetry: false, delayMs: 0 });\n return { value, attempts: attempt, failure: null, exhausted: false, aborted: false };\n }\n\n const active = policyFor(failure);\n const eligible = failure.retryable && !requiresHumanAction(failure.kind);\n const willRetry = eligible && shouldRetry(active, attempt);\n const delayMs = willRetry\n ? computeDelayMs(active, attempt, { random, retryAfterMs: failure.retryAfterMs })\n : 0;\n\n await onAttempt?.({ attempt, value, failure, willRetry, delayMs });\n\n if (!willRetry) {\n return { value, attempts: attempt, failure, exhausted: eligible, aborted: false };\n }\n\n if (!(await delay(delayMs, { signal }))) {\n return { value, attempts: attempt, failure, exhausted: false, aborted: true };\n }\n }\n}\n\n/**\n * A fixed policy in the shape the two legacy call sites express their settings\n * in: a number of attempts and a base/max delay in seconds.\n *\n * Deliberately un-jittered. Both call sites have always used a bare\n * `base * 2^(n-1)` and both publish that number in a `retry` event and print\n * it; adding jitter here would change today's behaviour with no configuration\n * asking for it. Jitter arrives with the `resilience` key that configures it.\n */\nexport function fixedBackoffPolicy(\n maxAttempts: number,\n baseSeconds: number,\n maxSeconds: number,\n overrides: Partial<RetryPolicy> = {},\n): RetryPolicy {\n return {\n maxAttempts,\n initialDelayMs: baseSeconds * 1000,\n maxDelayMs: maxSeconds * 1000,\n backoffFactor: 2,\n jitter: 'none',\n retryForever: false,\n failover: 'never',\n failoverAfterAttempts: 2,\n onExhausted: 'fail',\n ...overrides,\n };\n}\n","import { type ExecResult, run } from './shell.js';\n\n/**\n * Get the root directory of the current git repository.\n * Throws if not inside a git repository.\n */\nexport async function getProjectRoot(): Promise<string> {\n const result = await run('git', ['rev-parse', '--show-toplevel']);\n\n if (result.exitCode !== 0) {\n throw new Error(\n 'Not inside a git repository. Please run issue-flow from within a git project.',\n );\n }\n\n return result.stdout.trim();\n}\n\n/**\n * Get the current git branch name.\n * Returns an empty string if in detached HEAD state.\n */\nexport async function getCurrentBranch(): Promise<string> {\n const result = await run('git', ['branch', '--show-current']);\n\n if (result.exitCode !== 0) {\n throw new Error(\n 'Failed to detect git branch. Ensure git is installed and you are inside a repository.',\n );\n }\n\n return result.stdout.trim();\n}\n\nexport interface CommitInfo {\n hash: string;\n subject: string;\n}\n\n/**\n * Get the base branch of the repository: the remote HEAD (origin/HEAD) when\n * available, otherwise the first of main/master that exists locally.\n * Never throws; defaults to 'main'.\n */\nexport async function getBaseBranch(): Promise<string> {\n const remoteHead = await run('git', ['symbolic-ref', '--short', 'refs/remotes/origin/HEAD']);\n if (remoteHead.exitCode === 0) {\n const name = remoteHead.stdout.trim().replace(/^origin\\//, '');\n if (name) return name;\n }\n\n for (const candidate of ['main', 'master']) {\n const check = await run('git', ['rev-parse', '--verify', '--quiet', `refs/heads/${candidate}`]);\n if (check.exitCode === 0) return candidate;\n }\n\n return 'main';\n}\n\n/**\n * Get the URL of the `origin` remote.\n * Never throws: a missing remote, a non-zero exit code, an empty stdout or a\n * git binary that cannot even be spawned all resolve to `null` — the same\n * discipline as {@link getBaseBranch}, so callers can treat \"no remote\" as an\n * ordinary state instead of an error path.\n *\n * `cwd` selects which repository is queried; omitting it falls back to\n * `process.cwd()`, which is almost never what a caller resolving a specific\n * `projectRoot` wants — pass it explicitly whenever one is available.\n */\nexport async function getRemoteUrl(cwd?: string): Promise<string | null> {\n let result: ExecResult;\n try {\n result = await run('git', ['remote', 'get-url', 'origin'], { cwd });\n } catch {\n return null;\n }\n\n if (result.exitCode !== 0) return null;\n\n const url = result.stdout.trim();\n return url === '' ? null : url;\n}\n\n/**\n * Get the abbreviated hash of the current HEAD commit.\n * Never throws, same discipline as {@link getRemoteUrl}: a repository with no\n * commits yet, a non-zero exit code, an empty stdout or a git binary that\n * cannot be spawned all resolve to `null`, so callers can treat \"no HEAD\" as\n * an ordinary state instead of an error path.\n *\n * `cwd` selects which repository is queried; omitting it falls back to\n * `process.cwd()`.\n */\nexport async function getHeadCommit(cwd?: string): Promise<string | null> {\n let result: ExecResult;\n try {\n result = await run('git', ['rev-parse', '--short', 'HEAD'], { cwd });\n } catch {\n return null;\n }\n\n if (result.exitCode !== 0) return null;\n\n const hash = result.stdout.trim();\n return hash === '' ? null : hash;\n}\n\n/**\n * Reduce a git remote URL to a canonical `host/path` identity, so that every\n * way of addressing the same repository collapses to the same string:\n *\n * ```\n * https://github.com/org/repo.git -> github.com/org/repo\n * git@github.com:org/repo.git -> github.com/org/repo\n * ssh://git@github.com:22/org/repo.git -> github.com/org/repo\n * https://user:token@github.com/org/repo -> github.com/org/repo\n * https://github.com/Org/Repo/ -> github.com/org/repo\n * ```\n *\n * Pure function: it never shells out, so it is testable without a git\n * repository. Returns `null` when no host and path can be extracted.\n *\n * Known limitation: both host and path are lowercased. Hostnames are\n * case-insensitive by definition, but repository paths are case-sensitive on\n * some self-hosted servers — so `host/org/Repo` and `host/org/repo` normalize\n * to the same identity even if that server considers them distinct. This is a\n * deliberate trade: matching the same repository across clones (where users\n * routinely type the wrong case) matters more than telling apart two repos\n * whose paths differ only by case.\n */\nexport function normalizeRemoteUrl(url: string | null | undefined): string | null {\n if (typeof url !== 'string') return null;\n\n const trimmed = url.trim();\n if (trimmed === '') return null;\n\n let authority: string;\n let path: string;\n\n const scheme = /^[a-zA-Z][a-zA-Z0-9+.-]*:\\/\\//.exec(trimmed);\n if (scheme) {\n const rest = trimmed.slice(scheme[0].length);\n const slash = rest.indexOf('/');\n if (slash === -1) return null;\n authority = rest.slice(0, slash);\n path = rest.slice(slash + 1);\n } else {\n // scp-like syntax: [user@]host:path — everything after the first colon is\n // the path, which is how git itself reads it (no port is possible here).\n const colon = trimmed.indexOf(':');\n if (colon === -1) return null;\n authority = trimmed.slice(0, colon);\n path = trimmed.slice(colon + 1);\n }\n\n // Drop embedded credentials (user, user:token) and the ssh user.\n const at = authority.lastIndexOf('@');\n if (at !== -1) authority = authority.slice(at + 1);\n\n const host = authority.replace(/:\\d+$/, '').toLowerCase();\n if (host === '') return null;\n\n const normalizedPath = path\n .replace(/[?#].*$/, '')\n .replace(/\\/+/g, '/')\n .replace(/^\\/+|\\/+$/g, '')\n .replace(/\\.git$/i, '')\n .replace(/\\/+$/, '')\n .toLowerCase();\n if (normalizedPath === '') return null;\n\n return `${host}/${normalizedPath}`;\n}\n\n/**\n * Remove Basic-Auth-style credentials (`user:token@`, `user@`) embedded in an\n * `http(s)` remote URL, without touching anything else about it — case, port,\n * the `.git` suffix, query string and trailing slashes are all preserved.\n *\n * Deliberately scoped to `http`/`https`: that is the only place a git remote\n * can carry a real secret (a PAT is routinely embedded as\n * `https://x-access-token:TOKEN@host/...` by CI and automation), and the URL\n * stays a valid, clonable remote once the userinfo is gone. SSH has no\n * equivalent syntax — both `ssh://user@host/path` and the scp-like shorthand\n * `user@host:path` require that user segment to connect at all (it is almost\n * always the fixed, non-secret `git` service account), so this function\n * leaves any other scheme, the scp-like form and local paths untouched.\n *\n * Unlike {@link normalizeRemoteUrl}, which collapses a URL to a lossy\n * `host/path` identity, this keeps the URL \"as configured\" — it exists for\n * surfaces (like the web monitor's `repository.remoteUrl`) that want to show\n * the real remote, minus whatever secret an HTTPS token commonly embeds in\n * it.\n *\n * Pure function, never throws. Returns `null` for a nullish or empty input.\n */\nexport function stripRemoteUrlCredentials(url: string | null | undefined): string | null {\n if (typeof url !== 'string') return null;\n\n const trimmed = url.trim();\n if (trimmed === '') return null;\n\n const httpScheme = /^https?:\\/\\//i.exec(trimmed);\n if (!httpScheme) return trimmed;\n\n const rest = trimmed.slice(httpScheme[0].length);\n const slash = rest.indexOf('/');\n const authority = slash === -1 ? rest : rest.slice(0, slash);\n const remainder = slash === -1 ? '' : rest.slice(slash);\n\n const at = authority.lastIndexOf('@');\n if (at === -1) return trimmed;\n return `${httpScheme[0]}${authority.slice(at + 1)}${remainder}`;\n}\n\n/**\n * List commits on HEAD that are not on the given base branch (base..HEAD),\n * most recent first. Never throws; returns [] when the range cannot be\n * resolved (e.g. unknown base branch or shallow clone).\n */\nexport async function getCommitsSince(base: string): Promise<CommitInfo[]> {\n const result = await run('git', ['log', '--pretty=format:%h%x09%s', `${base}..HEAD`]);\n if (result.exitCode !== 0) return [];\n\n return result.stdout\n .split('\\n')\n .filter((line) => line.trim() !== '')\n .map((line) => {\n const tab = line.indexOf('\\t');\n return tab === -1\n ? { hash: line.trim(), subject: '' }\n : { hash: line.slice(0, tab), subject: line.slice(tab + 1) };\n });\n}\n\n/* ── repository preflight ───────────────────────────────────────────────── */\n\n/**\n * A repository state that stops the pipeline.\n *\n * The whole point of this type is that it is *reported*, never acted on: the\n * second explicit limit of the resilience Epic is that **no destructive\n * operation is ever run automatically to fix state**. A repository mid-rebase\n * is a person's unfinished work, and an `--abort` issued by a tool at 3am is\n * indistinguishable from data loss.\n */\nexport type PreflightBlockKind =\n | 'rebase_in_progress'\n | 'merge_in_progress'\n | 'cherry_pick_in_progress'\n | 'revert_in_progress'\n | 'unmerged_paths'\n | 'detached_head'\n | 'branch_mismatch'\n | 'dirty_tree';\n\nexport interface PreflightBlock {\n kind: PreflightBlockKind;\n /** What is wrong, in one line. */\n message: string;\n /** The command a human would run. Printed, never executed. */\n suggestion: string;\n}\n\nexport interface PreflightResult {\n ok: boolean;\n blocks: PreflightBlock[];\n /** Current branch, or `null` on a detached HEAD. */\n branch: string | null;\n /** Whether the working tree has uncommitted changes. */\n dirty: boolean;\n}\n\n/** What the caller is about to do, which decides how a dirty tree is read. */\nexport type PreflightIntent =\n | /** Continuing the phase that produced the changes. */ 'resume-same-phase'\n | /** Starting a different phase, or a different issue. */ 'new-phase';\n\nexport interface PreflightOptions {\n /** Branch the plan says this work belongs to. `null` skips the check. */\n expectedBranch?: string | null;\n /** Default `new-phase`, the strict reading. */\n intent?: PreflightIntent;\n cwd?: string;\n}\n\n/** `git rev-parse --verify --quiet <ref>` — exit 0 means the ref exists. */\nasync function refExists(ref: string, cwd?: string): Promise<boolean> {\n const result = await run('git', ['rev-parse', '--verify', '--quiet', ref], {\n ...(cwd === undefined ? {} : { cwd }),\n });\n return result.exitCode === 0;\n}\n\n/** The in-progress sequencer operations, by the ref each one leaves behind. */\nconst SEQUENCER_REFS: readonly {\n ref: string;\n kind: PreflightBlockKind;\n name: string;\n suggestion: string;\n}[] = [\n {\n ref: 'REBASE_HEAD',\n kind: 'rebase_in_progress',\n name: 'a rebase',\n suggestion: 'git rebase --continue (or git rebase --abort)',\n },\n {\n ref: 'MERGE_HEAD',\n kind: 'merge_in_progress',\n name: 'a merge',\n suggestion: 'git merge --continue (or git merge --abort)',\n },\n {\n ref: 'CHERRY_PICK_HEAD',\n kind: 'cherry_pick_in_progress',\n name: 'a cherry-pick',\n suggestion: 'git cherry-pick --continue (or git cherry-pick --abort)',\n },\n {\n ref: 'REVERT_HEAD',\n kind: 'revert_in_progress',\n name: 'a revert',\n suggestion: 'git revert --continue (or git revert --abort)',\n },\n];\n\n/**\n * Describe the repository and decide whether it is safe to write to it.\n *\n * Every check is a *read*: `rev-parse`, `symbolic-ref`, `diff --name-only`,\n * `status --porcelain`, `branch --show-current`. Nothing here checks anything\n * out, resets anything, aborts anything or stashes anything — not on a resume,\n * not under a continuous profile, not ever. A repository in an ambiguous state\n * escalates to a human, which is the only correct answer for a tool that did\n * not put it there.\n *\n * A **dirty tree is not always a problem**: resuming the phase that produced\n * those changes is exactly the case where uncommitted work is expected. It\n * blocks only when the run is about to move on to something else, where the\n * changes would be carried into work they do not belong to.\n */\nexport async function preflightRepository(\n options: PreflightOptions = {},\n): Promise<PreflightResult> {\n const { cwd, expectedBranch, intent = 'new-phase' } = options;\n const at = cwd === undefined ? {} : { cwd };\n const blocks: PreflightBlock[] = [];\n\n for (const sequencer of SEQUENCER_REFS) {\n if (await refExists(sequencer.ref, cwd)) {\n blocks.push({\n kind: sequencer.kind,\n message: `The repository is in the middle of ${sequencer.name} (${sequencer.ref} is present).`,\n suggestion: sequencer.suggestion,\n });\n }\n }\n\n const unmerged = await run('git', ['diff', '--name-only', '--diff-filter=U'], at);\n const conflicted = unmerged.stdout\n .split('\\n')\n .map((line) => line.trim())\n .filter((line) => line !== '');\n if (conflicted.length > 0) {\n blocks.push({\n kind: 'unmerged_paths',\n message: `${conflicted.length} file(s) still have unresolved conflicts: ${conflicted.slice(0, 5).join(', ')}${conflicted.length > 5 ? ', …' : ''}.`,\n suggestion: 'Resolve them, then git add the files',\n });\n }\n\n // `symbolic-ref -q HEAD` fails on a detached HEAD, which is the whole test.\n const symbolic = await run('git', ['symbolic-ref', '-q', 'HEAD'], at);\n const detached = symbolic.exitCode !== 0;\n const branch = detached ? null : symbolic.stdout.trim().replace(/^refs\\/heads\\//, '');\n\n if (detached) {\n blocks.push({\n kind: 'detached_head',\n message: 'HEAD is detached, so a commit would belong to no branch.',\n suggestion: `git switch ${expectedBranch ?? '<branch>'}`,\n });\n } else if (\n expectedBranch !== undefined &&\n expectedBranch !== null &&\n expectedBranch !== '' &&\n branch !== expectedBranch\n ) {\n blocks.push({\n kind: 'branch_mismatch',\n message: `The plan works on '${expectedBranch}' but the repository is on '${branch}'.`,\n suggestion: `git switch ${expectedBranch}`,\n });\n }\n\n const status = await run('git', ['status', '--porcelain'], at);\n const dirty = status.stdout.trim() !== '';\n if (dirty && intent !== 'resume-same-phase') {\n blocks.push({\n kind: 'dirty_tree',\n message: 'The working tree has uncommitted changes that do not belong to this phase.',\n suggestion: 'Commit or stash them yourself, then run again',\n });\n }\n\n return { ok: blocks.length === 0, blocks, branch, dirty };\n}\n\n/** The report a caller prints when the preflight blocks. Never a fix. */\nexport function describePreflight(result: PreflightResult): string[] {\n return result.blocks.map((block) => `${block.message} Suggested: ${block.suggestion}`);\n}\n\n/* ── story-level idempotence ────────────────────────────────────────────── */\n\n/** A story id as it appears in a commit subject: `feat(issue-63): US-012 - …`. */\nconst STORY_ID_IN_SUBJECT = /\\bUS-\\d+\\b/g;\n\n/**\n * The story ids already committed on this branch.\n *\n * The execute prompt commits one story per commit, with the id in the subject\n * (`<type>(scope): US-001 - Title`), so the branch's own history is a record of\n * what is done — and it is the *only* record that survives a crash between the\n * commit and the agent writing `passes: true` into the plan.\n *\n * Read-only, and deliberately so: this answers a question, it never fixes\n * anything. Never throws — an unreadable history means \"nothing known\", which\n * leaves the engine doing exactly what it did before.\n */\nexport async function committedStoryIds(baseBranch: string, cwd?: string): Promise<Set<string>> {\n const at = cwd === undefined ? {} : { cwd };\n const range = baseBranch === '' ? 'HEAD' : `${baseBranch}..HEAD`;\n const result = await run('git', ['log', range, '--format=%s'], at);\n if (result.exitCode !== 0) return new Set();\n\n const ids = new Set<string>();\n for (const subject of result.stdout.split('\\n')) {\n for (const match of subject.matchAll(STORY_ID_IN_SUBJECT)) {\n ids.add(match[0]);\n }\n }\n return ids;\n}\n\n/** Whether the working tree has no uncommitted change. Never throws. */\nexport async function isWorkingTreeClean(cwd?: string): Promise<boolean> {\n const at = cwd === undefined ? {} : { cwd };\n const result = await run('git', ['status', '--porcelain'], at);\n if (result.exitCode !== 0) return false;\n return result.stdout.trim() === '';\n}\n","import { createHash } from 'node:crypto';\nimport { homedir } from 'node:os';\nimport { basename, join, resolve } from 'node:path';\nimport { getRemoteUrl, normalizeRemoteUrl } from '../utils/git.js';\n\n/** Directory created under the user's home when no override is provided. */\nexport const GLOBAL_DIR_NAME = '.issue-flow';\n\n/** Environment variable that relocates the whole global storage tree. */\nexport const GLOBAL_ROOT_ENV = 'ISSUE_FLOW_HOME';\n\n/** Directory under the global root holding one folder per project. */\nexport const PROJECTS_DIR_NAME = 'projects';\n\n/** Directory under a project holding one folder per issue. */\nexport const ISSUES_DIR_NAME = 'issues';\n\n/** Directory under a project holding one folder per multi-issue execution queue. */\nexport const QUEUES_DIR_NAME = 'queues';\n\n/** Run-ownership lock, a sibling of `issues/` inside the project directory. */\nexport const RUN_LOCK_FILENAME = 'run.lock';\n\nexport interface GetGlobalRootOptions {\n /** Environment source. Defaults to process.env. */\n env?: NodeJS.ProcessEnv;\n}\n\n/**\n * Resolve the root directory of the global storage tree (`~/.issue-flow`).\n *\n * Every path under the global storage MUST be derived from this function: it is\n * the single seam where `ISSUE_FLOW_HOME` takes effect, which is what lets\n * tests, CI and sandboxes point the whole tree at a temporary directory instead\n * of touching the real `$HOME`. A call site that joins `homedir()` by hand\n * silently opts out of that isolation.\n *\n * Pure and synchronous: it never creates directories nor touches the\n * filesystem — callers decide when (and whether) the directory should exist.\n */\nexport function getGlobalRoot(options: GetGlobalRootOptions = {}): string {\n const env = options.env ?? process.env;\n\n const override = env[GLOBAL_ROOT_ENV]?.trim();\n if (override) {\n // A relative override is resolved against the CWD so that every consumer\n // gets an absolute path, exactly as in the homedir() branch below.\n return resolve(override);\n }\n\n // The only failure mode: an environment where the home directory cannot be\n // determined (some containers and CI images). The override is the escape\n // hatch, so the error names it.\n let home: string;\n try {\n home = homedir();\n } catch {\n home = '';\n }\n if (home.trim() === '') {\n throw new Error(\n `Unable to resolve the home directory for the global storage. Set ${GLOBAL_ROOT_ENV} to an explicit path.`,\n );\n }\n\n return join(home, GLOBAL_DIR_NAME);\n}\n\n/** Maximum length of the human-readable half of a project id. */\nconst SLUG_MAX_LENGTH = 32;\n\n/** Number of hex characters kept from the sha256 of the seed. */\nconst HASH_LENGTH = 12;\n\n/** Used when the repository name has no character that survives sanitization. */\nconst FALLBACK_SLUG = 'project';\n\n/**\n * Reduce an arbitrary repository name to a path-safe slug: lowercase, only\n * `[a-z0-9-]`, runs of separators collapsed, truncated to {@link SLUG_MAX_LENGTH}.\n */\nfunction slugify(name: string): string {\n const slug = name\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, '-')\n .replace(/^-+|-+$/g, '')\n .slice(0, SLUG_MAX_LENGTH)\n .replace(/-+$/g, '');\n\n return slug === '' ? FALLBACK_SLUG : slug;\n}\n\n/**\n * Derive a stable identifier for a project, in the form `<slug>-<hash12>`.\n *\n * The seed is canonical rather than local, so the same repository yields the\n * same id on any machine: `remote:<normalizedRemote>` whenever an `origin`\n * remote exists, falling back to `path:<absoluteProjectRoot>` when it does not.\n * That is what makes moving or renaming the local folder harmless for a cloned\n * repository — the history stays attached to the remote identity.\n *\n * The `remote:` / `path:` prefix is part of the hashed seed, so a project\n * identified by path can never collide with one identified by remote even if\n * the two strings were to coincide. The slug is only cosmetic (it makes the\n * directory recognizable); the hash carries the identity.\n *\n * Known limitation of the path fallback: for a repository with no `origin`\n * remote, the absolute path *is* the identity. Moving or renaming that folder\n * produces a different id, and the previous history is left behind under the\n * old one. Configuring a remote before adopting the global storage avoids it.\n */\nexport async function getProjectId(projectRoot: string): Promise<string> {\n const remote = normalizeRemoteUrl(await getRemoteUrl(projectRoot));\n return projectIdFromRemote(remote, projectRoot);\n}\n\n/**\n * Pure half of {@link getProjectId}: derive the id once the normalized remote\n * (or `null`) is already known.\n *\n * Exported so a caller that already resolved the remote for another reason\n * (e.g. {@link resolveStorageMode} in `compat.ts`, which also persists it into\n * `metadata.json`) can compute the id without shelling out to git a second\n * time.\n */\nexport function projectIdFromRemote(remote: string | null, projectRoot: string): string {\n let seed: string;\n let name: string;\n if (remote) {\n // `host/org/repo` -> the repository name is the last path segment.\n seed = `remote:${remote}`;\n name = remote.split('/').pop() ?? '';\n } else {\n const absolute = resolve(projectRoot);\n seed = `path:${absolute}`;\n name = basename(absolute);\n }\n\n const hash = createHash('sha256').update(seed).digest('hex').slice(0, HASH_LENGTH);\n\n return `${slugify(name)}-${hash}`;\n}\n\n/**\n * Absolute directory of a project inside the global storage tree.\n *\n * `projectId` is expected to come from {@link getProjectId}, which already\n * guarantees a single safe path segment.\n */\nexport function getProjectDir(projectId: string, options: GetGlobalRootOptions = {}): string {\n return join(getGlobalRoot(options), PROJECTS_DIR_NAME, projectId);\n}\n\n/**\n * Every artifact of one issue, resolved from a single place.\n *\n * Note that `prdFile` is the requirements document (`prd.md`) and `tasksFile`\n * is the task plan (`tasks.json`) — unlike `ResolvedPaths.prdFile` in\n * `types.ts`, which historically points at `tasks.json`.\n */\nexport interface IssuePaths {\n issueDir: string;\n issueFile: string;\n metadataFile: string;\n prdFile: string;\n tasksFile: string;\n progressFile: string;\n analysisFile: string;\n sessionFile: string;\n eventsFile: string;\n rotatedEventsFile: string;\n decompositionFile: string;\n lastBranchFile: string;\n archiveDir: string;\n prReviewDir: string;\n}\n\n/**\n * Artifacts of one multi-issue execution queue.\n *\n * A queue coordinates several issues that share a branch; each issue keeps its\n * own directory under {@link ISSUES_DIR_NAME} exactly as before, and only the\n * coordination state (order, per-issue status, shared branch, consolidated Pull\n * Request) lives here. That is what keeps a single-issue run byte-identical: it\n * never creates a queue at all.\n */\nexport interface QueuePaths {\n queueDir: string;\n planFile: string;\n}\n\n/**\n * Resolve the paths of one execution queue under the global storage.\n *\n * The queue id is the identifier of the primary issue — the first one the user\n * asked for — so re-running the same command finds the same queue and resumes\n * it instead of starting a parallel one.\n *\n * Pure and synchronous like every other helper here: nothing is created.\n */\nexport function getQueuePaths(\n projectId: string,\n queueId: string | number,\n options: GetGlobalRootOptions = {},\n): QueuePaths {\n const queueDir = join(\n getProjectDir(projectId, options),\n QUEUES_DIR_NAME,\n normalizeIssueNumber(queueId),\n );\n\n return { queueDir, planFile: join(queueDir, 'execution-plan.json') };\n}\n\n/**\n * Issue identifiers become path segments, so anything that could escape the\n * issues directory is rejected before it reaches `join` — the same discipline\n * as `normalizeId()` in `issues/providers/local.ts`.\n *\n * Non-numeric identifiers (`auth-refactor`, `pr-184`) are first-class here:\n * the local provider already supports them, so the global storage must too.\n */\nfunction normalizeIssueNumber(issueNumber: string | number): string {\n const normalized = String(issueNumber).trim().replace(/^#/, '');\n\n if (normalized.length === 0) {\n throw new Error('Issue identifier cannot be empty');\n }\n if (/[/\\\\]/.test(normalized) || normalized === '.' || normalized === '..') {\n throw new Error(`Invalid issue identifier: '${issueNumber}'`);\n }\n\n return normalized;\n}\n\n/**\n * Resolve every path of a single issue under the global storage.\n *\n * This is the only place allowed to know the layout of an issue directory:\n * call sites ask for the artifact they need instead of joining names by hand,\n * so renaming or adding an artifact stays a one-file change.\n *\n * Pure and synchronous: nothing here creates directories or touches the\n * filesystem.\n */\nexport function getIssuePaths(\n projectId: string,\n issueNumber: string | number,\n options: GetGlobalRootOptions = {},\n): IssuePaths {\n const issueDir = join(\n getProjectDir(projectId, options),\n ISSUES_DIR_NAME,\n normalizeIssueNumber(issueNumber),\n );\n\n return {\n issueDir,\n issueFile: join(issueDir, 'issue.md'),\n metadataFile: join(issueDir, 'metadata.json'),\n prdFile: join(issueDir, 'prd.md'),\n tasksFile: join(issueDir, 'tasks.json'),\n progressFile: join(issueDir, 'progress.txt'),\n analysisFile: join(issueDir, 'analysis.md'),\n sessionFile: join(issueDir, 'session.json'),\n // The journal, and the single generation kept when it rotates. The\n // snapshot above is the projection; this pair is the history.\n eventsFile: join(issueDir, 'events.jsonl'),\n rotatedEventsFile: join(issueDir, 'events.1.jsonl'),\n // \"This issue looks larger than one run\": written only when the signals\n // agree, and read by a person rather than by the pipeline.\n decompositionFile: join(issueDir, 'decomposition.md'),\n lastBranchFile: join(issueDir, '.last-branch'),\n archiveDir: join(issueDir, 'archive'),\n prReviewDir: join(issueDir, 'pr-review'),\n };\n}\n","import { z } from 'zod';\nimport type { ExecutionPlan } from '../execution/types.js';\nimport type {\n ExhaustedAction,\n FailoverMode,\n PolicyConfig as ResiliencePolicyConfig,\n ResilienceProfile,\n RetryConfigKey,\n RetryPolicy,\n} from '../resilience/policy.js';\nimport { pullRequestRefSchema, webConfigSchema } from '../schemas.js';\n\n/**\n * Zod schemas for the files written under the global storage tree\n * (`~/.issue-flow`): the per-project `metadata.json` and the global\n * `config.json`.\n *\n * They live here rather than in `src/schemas.ts` so that file stays focused on\n * the pipeline domain (task plans, Issue metadata, session snapshots) and the\n * storage layer keeps owning its own formats.\n */\n\n/** Version stamped on files written by this release of the storage layer. */\nexport const STORAGE_SCHEMA_VERSION = 1;\n\n/**\n * Where a `plan` run's `US-NNN` numbering came from (issue #36):\n *\n * - `history`: recovered from the highest number already used anywhere in the\n * project's `tasks.json` files, automatically or via `--continue`.\n * - `start-us`: forced by the user via `--start-us <n>`, ignoring history.\n * - `none`: no history found (the project's first `plan` run) — starts at\n * `US-001`.\n */\nexport const userStoryNumberingSourceSchema = z.enum(['history', 'start-us', 'none']);\n\n/**\n * A single numbering decision, persisted for audit under\n * `metadata.json`'s `userStoryNumbering` (see below) — never used to resolve\n * the *next* decision, which always re-scans `tasks.json` from scratch.\n */\nexport const userStoryNumberingDecisionSchema = z.object({\n /** The `US-NNN` number the `plan` prompt was told to continue from. */\n nextNumber: z.number().int().positive(),\n source: userStoryNumberingSourceSchema,\n /** Issue the decision was made for. */\n issueNumber: z.string().min(1),\n decidedAt: z.string().min(1),\n /** Human-readable origin, e.g. the previous story id and issue it came from. */\n detail: z.string().optional(),\n});\n\n/**\n * `~/.issue-flow/projects/<project-id>/metadata.json`.\n *\n * Deliberately **not** `.strict()`: a newer release may add fields (dashboard\n * history, counters) and an older one must still be able to read the file\n * instead of rejecting it. Unknown keys are dropped on parse, never fatal.\n *\n * `root` is the last known local checkout — informative only. Identity lives in\n * `projectId` (see `getProjectId()` in `paths.ts`), so moving the folder of a\n * project that has a remote updates `root` without changing the id.\n */\nexport const projectMetadataSchema = z.object({\n schemaVersion: z.number().int().positive(),\n projectId: z.string().min(1),\n root: z.string().min(1),\n /** Normalized remote (`host/org/repo`), or null when the project has none. */\n remoteUrl: z.string().nullable(),\n createdAt: z.string().min(1),\n updatedAt: z.string().min(1),\n /** Null until the project is used by a pipeline run. */\n lastAttemptAt: z.string().nullable(),\n /**\n * Most recent User Story numbering decision made by `plan` (issue #36).\n * Absent on a project whose `plan` never ran through the numbering\n * resolver, or on a `metadata.json` written before the feature existed.\n */\n userStoryNumbering: userStoryNumberingDecisionSchema.optional(),\n});\n\n/**\n * Global counterpart of the `web` key of `.issue-flow.json`, restricted to the\n * settings that make sense machine-wide: `enabled` and `includeLogs` stay a\n * per-project decision.\n *\n * The validation of each field is reused from `webConfigSchema` through\n * `.unwrap()`, which strips the `.default()` and keeps the constraints. Picking\n * and calling `.partial()` would not be enough: in zod 4 an optional field\n * still materializes its default when the key is absent\n * (`z.object({ port: z.number().default(3737) }).partial().parse({})` yields\n * `{ port: 3737 }`), and a materialized default in this layer would override\n * the project layer during the merge.\n */\nconst webShape = webConfigSchema.shape;\nexport const globalWebConfigSchema = z\n .object({\n port: webShape.port.unwrap(),\n host: webShape.host.unwrap(),\n refreshSeconds: webShape.refreshSeconds.unwrap(),\n logLimit: webShape.logLimit.unwrap(),\n })\n .partial();\n\n/** Global retry preferences, mirroring `DEFAULTS` in `config.ts`. */\nexport const globalRetryConfigSchema = z\n .object({\n retryLimit: z.number().int().nonnegative(),\n retryForever: z.boolean(),\n backoffBaseSeconds: z.number().nonnegative(),\n backoffMaxSeconds: z.number().nonnegative(),\n })\n .partial();\n\n/* ── the `resilience` key ───────────────────────────────────────────────── */\n\n/**\n * The file format of the `resilience` key, shared by `.issue-flow.json` and\n * `~/.issue-flow/config.json` — the same object is accepted in both, because\n * they are two rungs of one ladder rather than two formats.\n *\n * **Every field is optional and none carries a `.default()`**, for the reason\n * stated on `globalConfigSchema` below and in `AGENTS.md`: both files are\n * intermediate precedence layers, and a default materialized in one of them is\n * indistinguishable from a value the user wrote.\n *\n * The enums are pinned to the types of `resilience/policy.ts` with `satisfies`,\n * so a value the resolver understands and the file rejects (or the reverse) is\n * a compile error rather than a silent drop at parse time.\n */\nconst resilienceProfileSchema = z.enum([\n 'default',\n 'continuous',\n]) satisfies z.ZodType<ResilienceProfile>;\nconst jitterSchema = z.enum(['none', 'full']) satisfies z.ZodType<RetryPolicy['jitter']>;\nconst failoverModeSchema = z.enum([\n 'never',\n 'after_attempts',\n 'immediate',\n]) satisfies z.ZodType<FailoverMode>;\nconst exhaustedActionSchema = z.enum(['fail', 'block']) satisfies z.ZodType<ExhaustedAction>;\n\n/** One `resilience.retry.<kind>` entry — a partial `RetryPolicy`. */\nexport const retryPolicyOverrideSchema = z\n .object({\n maxAttempts: z.number().int().nonnegative(),\n initialDelayMs: z.number().nonnegative(),\n maxDelayMs: z.number().nonnegative(),\n backoffFactor: z.number().min(1),\n jitter: jitterSchema,\n retryForever: z.boolean(),\n failover: failoverModeSchema,\n failoverAfterAttempts: z.number().int().positive(),\n onExhausted: exhaustedActionSchema,\n })\n .partial();\n\n/**\n * `resilience.retry`, one optional entry per `FailureKind`.\n *\n * Built from a `Record<RetryConfigKey, …>` on purpose: adding a `FailureKind`\n * without giving it a configuration key stops compiling here.\n */\nconst retryOverridesByKind: Record<RetryConfigKey, typeof retryPolicyOverrideSchema> = {\n network: retryPolicyOverrideSchema,\n timeout: retryPolicyOverrideSchema,\n stalled: retryPolicyOverrideSchema,\n rateLimit: retryPolicyOverrideSchema,\n providerDown: retryPolicyOverrideSchema,\n providerCrash: retryPolicyOverrideSchema,\n authentication: retryPolicyOverrideSchema,\n configuration: retryPolicyOverrideSchema,\n repositoryState: retryPolicyOverrideSchema,\n taskExecution: retryPolicyOverrideSchema,\n internal: retryPolicyOverrideSchema,\n unknown: retryPolicyOverrideSchema,\n};\n\nexport const resilienceRetryConfigSchema = z.object(retryOverridesByKind).partial();\n\n/** `resilience.providers` — agent health and failover (US-024, US-025). */\nexport const resilienceProvidersConfigSchema = z\n .object({\n failover: z.boolean(),\n chain: z.array(z.string().min(1)),\n cooldownMs: z.number().nonnegative(),\n })\n .partial();\n\n/** `resilience.queue` — what a failing issue does to the rest of them (US-027). */\nexport const resilienceQueueConfigSchema = z\n .object({\n onIssueFailure: z.enum(['stop', 'skip', 'block']),\n maxIssueAttempts: z.number().int().positive(),\n })\n .partial();\n\n/** `resilience.watchdog` — inactivity detection over the event stream (US-026). */\nexport const resilienceWatchdogConfigSchema = z\n .object({\n inactivityTimeoutMs: z.number().int().nonnegative(),\n })\n .partial();\n\n/** `resilience.journal` — the append-only `events.jsonl` (US-015). */\nexport const resilienceJournalConfigSchema = z\n .object({\n enabled: z.boolean(),\n maxFileBytes: z.number().int().positive(),\n })\n .partial();\n\n/** `resilience.decompose` — the \"this issue is too large\" report (US-030). */\nexport const resilienceDecomposeConfigSchema = z\n .object({\n auto: z.boolean(),\n })\n .partial();\n\nexport const resilienceConfigSchema = z\n .object({\n profile: resilienceProfileSchema,\n /** Whether a credential failure may migrate to another provider. */\n failoverOnAuth: z.boolean(),\n retry: resilienceRetryConfigSchema,\n providers: resilienceProvidersConfigSchema,\n queue: resilienceQueueConfigSchema,\n watchdog: resilienceWatchdogConfigSchema,\n journal: resilienceJournalConfigSchema,\n decompose: resilienceDecomposeConfigSchema,\n })\n .partial();\n\n/** Global commit preferences. Consumed by later issues, not by this release. */\nexport const globalCommitConfigSchema = z\n .object({\n signoff: z.boolean(),\n conventional: z.boolean(),\n })\n .partial();\n\n/**\n * `~/.issue-flow/config.json`.\n *\n * **Every key is optional and no key carries a `.default()`.** This is a design\n * constraint, not an omission: the global file is an intermediate precedence\n * layer (CLI > env > `.issue-flow.json` > `config.json` > defaults), so a\n * default materialized here would be indistinguishable from a value the user\n * actually wrote and would silently override the project layer during the\n * merge. Same reason `readWebConfigFile()` parses with\n * `webConfigSchema.partial()`. Defaults belong to the final layer only.\n */\nexport const globalConfigSchema = z\n .object({\n schemaVersion: z.number().int().positive(),\n /** Override for the directory holding `projects/`. */\n storageDir: z.string().min(1),\n web: globalWebConfigSchema,\n retry: globalRetryConfigSchema,\n commit: globalCommitConfigSchema,\n resilience: resilienceConfigSchema,\n })\n .partial();\n\n/**\n * `~/.issue-flow/web.lock`.\n *\n * Marks the single web monitoring server active on this machine: `pid` and\n * `port` let a new invocation tell a live instance from a stale one before\n * attempting to bind its own (see `web/lock.ts`). Not `.strict()` for the same\n * reason as the schemas above — a newer release may add fields.\n */\nexport const webLockSchema = z.object({\n pid: z.number().int().positive(),\n port: z.number().int().positive(),\n host: z.string().min(1),\n startedAt: z.string().min(1),\n});\n\n/**\n * `~/.issue-flow/projects/<project-id>/run.lock`.\n *\n * Ownership of a run, so a second invocation in the same project refuses\n * instead of fighting the first one over `tasks.json` and the branch. Same\n * shape of guard as `web.lock` — a pid, a host and a timestamp — plus the\n * heartbeat that tells a dead owner from a slow one.\n *\n * No field defaults: a lock is written whole by its owner, and a half-read one\n * degrades to \"no lock\" at the reader, exactly like `readWebLock`.\n */\nexport const runLockSchema = z.object({\n pid: z.number().int().positive(),\n /** `os.hostname()`. A pid only means something on the host that wrote it. */\n host: z.string().min(1),\n /** The issue (or queue) identifier the owner is running. */\n target: z.string().min(1),\n startedAt: z.string().min(1),\n lastHeartbeatAt: z.string().min(1),\n});\n\n/**\n * `~/.issue-flow/projects/<project-id>/queues/<queue-id>/execution-plan.json`.\n *\n * The coordination state of a multi-issue run: which Issues, in which order, on\n * which shared branch, and how far the queue got. Each Issue keeps its own\n * `tasks.json` — nothing of the task plan is duplicated here.\n *\n * Written only when a queue really has more than one Issue: a single-issue run\n * creates no queue directory at all, which is what keeps the storage layout of\n * every existing user unchanged.\n *\n * Not `.strict()`, like every other file schema here, and `satisfies` keeps it\n * in lockstep with the `ExecutionPlan` interface in `src/execution/types.ts`.\n */\nexport const executionPlanIssueSchema = z.object({\n id: z.string().min(1),\n number: z.number().int().positive().nullable(),\n title: z.string(),\n url: z.string().nullable(),\n source: z.string().min(1),\n position: z.number().int().positive(),\n // `blocked` and `skipped` are additive: no plan written before them can\n // carry one, and a reader that does not know them was never given one.\n status: z.enum(['pending', 'in_progress', 'completed', 'failed', 'blocked', 'skipped']),\n origin: z.enum(['requested', 'discovered']),\n dependsOn: z.array(z.string()).default([]),\n parent: z.string().nullable().default(null),\n priority: z.enum(['high', 'medium', 'low']).nullable().default(null),\n heuristic: z.boolean().default(false),\n failedPhase: z.string().nullable().default(null),\n lastError: z\n .object({ category: z.string(), message: z.string(), at: z.string() })\n .nullable()\n .default(null),\n startedAt: z.string().nullable().default(null),\n completedAt: z.string().nullable().default(null),\n // Both default, so an execution-plan.json written by an earlier release\n // parses unchanged and reads as \"never attempted, not blocked\".\n attempts: z.number().int().min(0).default(0),\n blockedReason: z.string().nullable().default(null),\n});\n\nexport const executionPlanSchema = z.object({\n schemaVersion: z.literal(1),\n id: z.string().min(1),\n project: z.string().min(1),\n requested: z.array(z.string().min(1)),\n branchName: z.string().nullable().default(null),\n noBranch: z.boolean().default(false),\n prReview: z.boolean().default(false),\n status: z.enum(['pending', 'in_progress', 'completed', 'failed']),\n createdAt: z.string().min(1),\n updatedAt: z.string().min(1),\n truncated: z.boolean().default(false),\n issues: z.array(executionPlanIssueSchema),\n excluded: z\n .array(\n z.object({\n id: z.string().min(1),\n number: z.number().int().positive().nullable(),\n title: z.string(),\n url: z.string().nullable(),\n reason: z.string(),\n }),\n )\n .default([]),\n pullRequest: pullRequestRefSchema.optional(),\n}) satisfies z.ZodType<ExecutionPlan>;\n\nexport type UserStoryNumberingSource = z.infer<typeof userStoryNumberingSourceSchema>;\nexport type UserStoryNumberingDecision = z.infer<typeof userStoryNumberingDecisionSchema>;\nexport type ProjectMetadata = z.infer<typeof projectMetadataSchema>;\nexport type ValidatedExecutionPlan = z.infer<typeof executionPlanSchema>;\nexport type GlobalWebConfig = z.infer<typeof globalWebConfigSchema>;\nexport type GlobalRetryConfig = z.infer<typeof globalRetryConfigSchema>;\nexport type GlobalCommitConfig = z.infer<typeof globalCommitConfigSchema>;\nexport type ResilienceRetryConfig = z.infer<typeof resilienceRetryConfigSchema>;\nexport type ResilienceProvidersConfig = z.infer<typeof resilienceProvidersConfigSchema>;\nexport type ResilienceQueueConfig = z.infer<typeof resilienceQueueConfigSchema>;\nexport type ResilienceWatchdogConfig = z.infer<typeof resilienceWatchdogConfigSchema>;\nexport type ResilienceJournalConfig = z.infer<typeof resilienceJournalConfigSchema>;\nexport type ResilienceDecomposeConfig = z.infer<typeof resilienceDecomposeConfigSchema>;\nexport type ResilienceConfig = z.infer<typeof resilienceConfigSchema>;\nexport type GlobalConfig = z.infer<typeof globalConfigSchema>;\n\n/**\n * The file format is a *superset* of what `resolvePolicy()` reads: it also\n * carries `providers`, `queue`, `watchdog`, `journal` and `decompose`, which\n * belong to later layers. This alias is the compile-time proof of the \"superset\"\n * half — a drift between the two shapes fails to build here rather than at the\n * first call site that passes one to the other.\n */\ntype Assert<T extends true> = T;\nexport type ResilienceConfigIsPolicyConfig = Assert<\n ResilienceConfig extends ResiliencePolicyConfig ? true : false\n>;\nexport type WebLock = z.infer<typeof webLockSchema>;\nexport type RunLock = z.infer<typeof runLockSchema>;\n","import { stat } from 'node:fs/promises';\nimport { basename, join, resolve } from 'node:path';\nimport { printInfo } from '../ui/logger.js';\nimport { getProjectRoot } from '../utils/git.js';\nimport { type MigrationResult, migrateLegacyStorage, resolveStorageMode } from './compat.js';\nimport {\n type GetGlobalRootOptions,\n getGlobalRoot,\n getIssuePaths,\n getProjectDir,\n getQueuePaths,\n ISSUES_DIR_NAME,\n type IssuePaths,\n type QueuePaths,\n RUN_LOCK_FILENAME,\n} from './paths.js';\n\n/**\n * The single entry point every pipeline command uses to learn where an issue's\n * artifacts live.\n *\n * `paths.ts` and `compat.ts` stay pure and explicit — they take a `projectId` or\n * a `projectRoot` and never decide anything on their own. This module is the\n * opposite end: it knows the current repository, resolves the storage mode,\n * triggers the legacy migration when one is due, and caches all of that for the\n * lifetime of the process, so a call site is reduced to one line.\n *\n * It deliberately does **not** create the issue directory. Callers that write\n * keep doing their own `mkdir(..., { recursive: true })`, which preserves the\n * rule in `storage/CLAUDE.md` that path resolution never touches the\n * filesystem's shape.\n */\n\nexport interface ResolveIssuePathsOptions extends GetGlobalRootOptions {\n /**\n * Repository root. Defaults to {@link getProjectRoot}, i.e. the git toplevel\n * of the current working directory — which is what makes the result identical\n * from the repo root and from any subdirectory.\n */\n projectRoot?: string;\n}\n\n/** Everything the process needs to know about a project, resolved once. */\ninterface ProjectResolution {\n projectId: string;\n legacyDir: string;\n}\n\n/**\n * Cached project resolutions, keyed by `<globalRoot>::<projectRoot>`.\n *\n * `projectRoot` is the identity of the project, but the global root takes part\n * in the key as well: `ISSUE_FLOW_HOME` decides *where* the resolution lands,\n * so two different roots must never share an entry (that would silently hand a\n * test the previous test's tree).\n *\n * The cached value is the in-flight promise rather than its result, so two\n * commands resolving concurrently share a single git call instead of racing\n * into two migrations.\n */\nconst projectCache = new Map<string, Promise<ProjectResolution>>();\n\n/**\n * Issues whose global directory has already been checked in this process, so\n * the per-issue fallback below stats each issue at most once.\n */\nconst checkedIssues = new Set<string>();\n\n/**\n * Drop every cached resolution.\n *\n * Exported for tests: the cache is keyed by project and global root, so a suite\n * that moves `ISSUE_FLOW_HOME` between cases must reset it in `beforeEach`.\n */\nexport function resetStorageResolutionCache(): void {\n projectCache.clear();\n checkedIssues.clear();\n}\n\nasync function directoryExists(path: string): Promise<boolean> {\n try {\n return (await stat(path)).isDirectory();\n } catch {\n return false;\n }\n}\n\n/**\n * Tell the user that their artifacts moved, and that nothing was taken away.\n *\n * A migration that copied nothing stays silent: `migrateLegacyStorage` is\n * called speculatively (once per project, then once per issue seen for the\n * first time), and every one of those runs after the first copies zero files.\n * Printing on those would turn a one-time notice into noise on every command.\n *\n * The \"one notice per process\" guarantee falls out of the US-001 cache: the\n * project-level migration runs once, and the per-issue fallback only reaches a\n * second migration when an issue really is still legacy-only — in which case a\n * notice is the correct output, not a duplicate.\n */\nfunction announceMigration(result: MigrationResult): void {\n if (result.copied.length === 0) return;\n\n const count = result.copied.length;\n const target = join(result.globalDir, ISSUES_DIR_NAME);\n\n printInfo(\n `Migrated ${count} file${count === 1 ? '' : 's'} from ${result.legacyDir} to ${target}`,\n );\n printInfo(\n `The legacy directory was not modified or removed — ${result.legacyDir} is kept as-is, read-only, for compatibility.`,\n );\n}\n\n/**\n * Resolve the project id, migrating the legacy tree when it is the only thing\n * that exists yet.\n *\n * A migration failure is not swallowed: `migrateLegacyStorage` already names the\n * issue and file that failed and states that the legacy directory was left\n * untouched, which is exactly what the user needs to fix it and retry.\n */\nasync function resolveProject(\n projectRoot: string,\n options: GetGlobalRootOptions,\n): Promise<ProjectResolution> {\n // resolveStorageMode returns the project id *and* the remote in a single git\n // call — calling getProjectId() afterwards would shell out to\n // `git remote get-url origin` a second time for the same answer.\n const status = await resolveStorageMode(projectRoot, options);\n\n if (status.mode === 'needs-migration') {\n announceMigration(await migrateLegacyStorage(projectRoot, options));\n }\n\n return {\n projectId: status.projectId,\n legacyDir: status.legacyDir,\n };\n}\n\n/** Cached half of {@link resolveIssuePaths}: one git call per project, per process. */\nfunction getProjectResolution(\n projectRoot: string,\n options: GetGlobalRootOptions,\n): Promise<ProjectResolution> {\n const key = `${getGlobalRoot(options)}::${projectRoot}`;\n\n const cached = projectCache.get(key);\n if (cached) return cached;\n\n // A rejected resolution is evicted so the next command retries instead of\n // inheriting a permanently failed promise.\n const pending = resolveProject(projectRoot, options).catch((err) => {\n projectCache.delete(key);\n throw err;\n });\n\n projectCache.set(key, pending);\n return pending;\n}\n\n/** Project-level directories of the current repository inside the global tree. */\nexport interface ProjectStoragePaths {\n /** Deterministic id derived from the repository's remote (or its path). */\n projectId: string;\n /** `<globalRoot>/projects/<projectId>`. */\n projectDir: string;\n /** `<projectDir>/issues` — the parent of every issue directory. */\n issuesDir: string;\n /**\n * `<projectDir>/run.lock` — who is running in this project right now.\n *\n * Project-level rather than per issue on purpose: two runs in one repository\n * share a working tree and a branch, so \"a different issue\" is not a\n * different lock.\n */\n runLockFile: string;\n}\n\n/**\n * Resolve the project-level directories, without naming an issue.\n *\n * The issue-agnostic half of {@link resolveIssuePaths}, for the two questions\n * that are about the project rather than about one issue: \"can I write here?\"\n * (`LocalFileIssueProvider.isAvailable`) and \"which identifiers are taken?\"\n * (`highestLocalNumber`, which walks `issuesDir`). It shares the same cache, so\n * asking here and then resolving an issue still costs a single git call.\n *\n * Like the rest of this module it creates nothing — a caller that needs the\n * directory to exist does its own `mkdir(..., { recursive: true })`.\n */\nexport async function resolveProjectPaths(\n options: ResolveIssuePathsOptions = {},\n): Promise<ProjectStoragePaths> {\n const { projectRoot: projectRootOption, ...rootOptions } = options;\n const projectRoot = resolve(projectRootOption ?? (await getProjectRoot()));\n\n const project = await getProjectResolution(projectRoot, rootOptions);\n const projectDir = getProjectDir(project.projectId, rootOptions);\n\n return {\n projectId: project.projectId,\n projectDir,\n issuesDir: join(projectDir, ISSUES_DIR_NAME),\n runLockFile: join(projectDir, RUN_LOCK_FILENAME),\n };\n}\n\n/**\n * Resolve the paths of a multi-issue execution queue in the current project.\n *\n * Shares the project cache with {@link resolveIssuePaths}, so a run that\n * resolves both still costs a single git call. There is no legacy tree for\n * queues — the concept did not exist before — so no migration is attempted.\n */\nexport async function resolveQueuePaths(\n queueId: string | number,\n options: ResolveIssuePathsOptions = {},\n): Promise<QueuePaths> {\n const { projectRoot: projectRootOption, ...rootOptions } = options;\n const projectRoot = resolve(projectRootOption ?? (await getProjectRoot()));\n\n const project = await getProjectResolution(projectRoot, rootOptions);\n return getQueuePaths(project.projectId, queueId, rootOptions);\n}\n\n/**\n * Resolve every path of `issueNumber` under the global storage, migrating the\n * legacy `<projectRoot>/issues/` tree first when needed.\n *\n * The migration is triggered in two situations:\n *\n * 1. project level — the global project directory does not exist at all and the\n * legacy one does (`mode === 'needs-migration'`);\n * 2. issue level — the project directory exists (so the mode is already\n * `global`) but *this* issue is only present in the legacy tree. That\n * happens whenever a project was migrated before an older issue was ever\n * read, or when a collaborator on a previous version added an issue to the\n * legacy directory afterwards. Re-running the migration is cheap and safe:\n * it skips every destination that already exists.\n */\nexport async function resolveIssuePaths(\n issueNumber: string | number,\n options: ResolveIssuePathsOptions = {},\n): Promise<IssuePaths> {\n const { projectRoot: projectRootOption, ...rootOptions } = options;\n const projectRoot = resolve(projectRootOption ?? (await getProjectRoot()));\n\n const project = await getProjectResolution(projectRoot, rootOptions);\n const paths = getIssuePaths(project.projectId, issueNumber, rootOptions);\n\n if (!checkedIssues.has(paths.issueDir)) {\n // `getIssuePaths` already validated and normalized the identifier, so the\n // last segment of issueDir is the safe form to look for under the legacy\n // tree.\n const legacyIssueDir = join(project.legacyDir, basename(paths.issueDir));\n\n if (!(await directoryExists(paths.issueDir)) && (await directoryExists(legacyIssueDir))) {\n announceMigration(await migrateLegacyStorage(projectRoot, rootOptions));\n }\n\n // Marked only once the check completed: a failed migration must be retried\n // by the next command, not remembered as done.\n checkedIssues.add(paths.issueDir);\n }\n\n return paths;\n}\n","import type { Dirent } from 'node:fs';\nimport { copyFile, mkdir, readdir, readFile, stat } from 'node:fs/promises';\nimport { join, resolve, sep } from 'node:path';\nimport { isoNow } from '../core/state-manager.js';\nimport { writeFileAtomic } from '../utils/fs.js';\nimport { getRemoteUrl, normalizeRemoteUrl } from '../utils/git.js';\nimport {\n type GetGlobalRootOptions,\n getProjectDir,\n ISSUES_DIR_NAME,\n projectIdFromRemote,\n} from './paths.js';\nimport {\n type ProjectMetadata,\n projectMetadataSchema,\n STORAGE_SCHEMA_VERSION,\n type UserStoryNumberingDecision,\n} from './schemas.js';\n\n/**\n * Coexistence between the legacy per-project layout (`<projectRoot>/issues/`)\n * and the global storage tree (`~/.issue-flow/projects/<project-id>/`).\n *\n * The single rule of this module: **the legacy directory is read-only.** Nothing\n * here removes or rewrites a byte under `<projectRoot>/issues/` — not even\n * behind an opt-in flag — so adopting the global storage can never cost a user\n * their history. Migration is a copy, and a copy that refuses to overwrite.\n */\n\n/** Directory holding the legacy per-project artifacts. */\nexport const LEGACY_ISSUES_DIR_NAME = 'issues';\n\n/** File holding the per-project metadata inside the global storage. */\nexport const METADATA_FILENAME = 'metadata.json';\n\n/**\n * - `global`: the global directory is the source of truth (it already exists,\n * or neither layout does and a fresh project starts there).\n * - `needs-migration`: only the legacy directory exists, so its content has not\n * been copied over yet.\n */\nexport type StorageMode = 'global' | 'needs-migration';\n\nexport interface StorageStatus {\n mode: StorageMode;\n /** Deterministic id, derived from {@link projectIdFromRemote}. */\n projectId: string;\n /** `<globalRoot>/projects/<projectId>`. */\n globalDir: string;\n /** `<projectRoot>/issues`. */\n legacyDir: string;\n /** Whether `globalDir` currently exists on disk. */\n globalExists: boolean;\n /** Whether `legacyDir` currently exists on disk. */\n legacyExists: boolean;\n /**\n * Normalized `origin` remote of `projectRoot`, or `null` without one.\n * Resolved once here so `migrateLegacyStorage` can reuse it instead of\n * shelling out to git a second time for `metadata.json`.\n */\n remoteUrl: string | null;\n}\n\nexport interface MigrationResult {\n projectId: string;\n globalDir: string;\n legacyDir: string;\n /** Mode observed *before* the migration ran. */\n previousMode: StorageMode;\n /** Paths copied, relative to `legacyDir`. */\n copied: string[];\n /** Paths left alone because the destination already had a file. */\n skipped: string[];\n /** Absolute path of the metadata file written by this run. */\n metadataFile: string;\n /** Metadata as persisted. */\n metadata: ProjectMetadata;\n}\n\nexport interface MigrateLegacyStorageOptions extends GetGlobalRootOptions {\n /** Timestamp source, injectable so tests can assert createdAt/updatedAt. */\n now?: () => string;\n}\n\nasync function directoryExists(path: string): Promise<boolean> {\n try {\n return (await stat(path)).isDirectory();\n } catch {\n return false;\n }\n}\n\nasync function pathExists(path: string): Promise<boolean> {\n try {\n await stat(path);\n return true;\n } catch {\n return false;\n }\n}\n\n/**\n * Describe where a project's artifacts live, without touching anything.\n *\n * Read-only by construction: it stats the two candidate directories and reports\n * what it found. It never creates a directory, so a caller can ask the question\n * (a status command, a warning banner) without committing the user to the new\n * layout.\n *\n * An existing global directory always wins: once artifacts are there, that is\n * the source of truth, and a legacy directory left behind is simply preserved\n * rather than re-read or re-migrated.\n */\nexport async function resolveStorageMode(\n projectRoot: string,\n options: GetGlobalRootOptions = {},\n): Promise<StorageStatus> {\n const remoteUrl = normalizeRemoteUrl(await getRemoteUrl(projectRoot));\n const projectId = projectIdFromRemote(remoteUrl, projectRoot);\n const globalDir = getProjectDir(projectId, options);\n const legacyDir = join(resolve(projectRoot), LEGACY_ISSUES_DIR_NAME);\n\n const [globalExists, legacyExists] = await Promise.all([\n directoryExists(globalDir),\n directoryExists(legacyDir),\n ]);\n\n return {\n // Only one state calls for work: legacy content that has nowhere to be read\n // from yet. Everything else — including \"neither exists\" — is already the\n // global layout.\n mode: legacyExists && !globalExists ? 'needs-migration' : 'global',\n projectId,\n globalDir,\n legacyDir,\n globalExists,\n legacyExists,\n remoteUrl,\n };\n}\n\n/**\n * Wrap an IO failure with the issue and file it happened on, so a partial\n * migration is diagnosable instead of a bare `EACCES`.\n */\nfunction migrationFailure(issue: string, relativePath: string, err: unknown): Error {\n const reason = err instanceof Error ? err.message : String(err);\n\n return new Error(\n `Failed to migrate '${relativePath}' (issue '${issue}'): ${reason}. ` +\n 'The legacy directory was left untouched — fix the cause and run the migration again.',\n );\n}\n\n/** First path segment of a relative path — the issue a file belongs to. */\nfunction issueOf(relativePath: string): string {\n return relativePath.split(sep)[0] || '.';\n}\n\n/**\n * Copy one directory level, recursing into subdirectories.\n *\n * Symlinks and other non-regular entries are skipped on purpose: following them\n * could copy content from outside the legacy directory into the global tree.\n */\nasync function copyTree(\n sourceRoot: string,\n targetRoot: string,\n relativeDir: string,\n result: { copied: string[]; skipped: string[] },\n): Promise<void> {\n const source = join(sourceRoot, relativeDir);\n const target = join(targetRoot, relativeDir);\n\n let entries: Dirent[];\n try {\n entries = await readdir(source, { withFileTypes: true });\n } catch (err) {\n throw migrationFailure(issueOf(relativeDir), relativeDir || '.', err);\n }\n\n try {\n // recursive: true also makes re-running a no-op on an existing directory.\n await mkdir(target, { recursive: true });\n } catch (err) {\n throw migrationFailure(issueOf(relativeDir), relativeDir || '.', err);\n }\n\n for (const entry of entries) {\n const relativePath = join(relativeDir, entry.name);\n\n if (entry.isDirectory()) {\n await copyTree(sourceRoot, targetRoot, relativePath, result);\n continue;\n }\n if (!entry.isFile()) {\n continue;\n }\n\n const to = join(targetRoot, relativePath);\n try {\n // Idempotency and safety in one check: a destination that already exists\n // is never overwritten, so a second run copies nothing and a hand-edited\n // artifact in the global tree survives.\n if (await pathExists(to)) {\n result.skipped.push(relativePath);\n continue;\n }\n await copyFile(join(sourceRoot, relativePath), to);\n result.copied.push(relativePath);\n } catch (err) {\n throw migrationFailure(issueOf(relativePath), relativePath, err);\n }\n }\n}\n\n/**\n * Read `metadata.json`, tolerating every failure.\n *\n * A missing, corrupt or schema-invalid file must not block a migration: the\n * caller simply writes a fresh one. The only thing worth recovering is\n * `createdAt`/`lastAttemptAt` from a valid previous file.\n */\nasync function readExistingMetadata(file: string): Promise<ProjectMetadata | null> {\n let raw: string;\n try {\n raw = await readFile(file, 'utf-8');\n } catch {\n return null;\n }\n\n try {\n const parsed = projectMetadataSchema.safeParse(JSON.parse(raw));\n return parsed.success ? parsed.data : null;\n } catch {\n return null;\n }\n}\n\n/**\n * Copy `<projectRoot>/issues/` into the project's global directory and stamp\n * its `metadata.json`.\n *\n * Non-destructive and idempotent, by design rather than by convention:\n *\n * - the source is only ever read — there is no removal option in this release;\n * - a destination file that already exists is skipped, never overwritten, so a\n * second run reports everything as skipped and changes nothing but\n * `updatedAt`;\n * - a failure mid-way leaves the legacy directory intact and names the issue\n * and file that failed, so the run can be repeated after the fix and will\n * resume by skipping what already made it across.\n *\n * Called on a project with no legacy directory it is still useful: it creates\n * the project directory and its metadata.\n */\nexport async function migrateLegacyStorage(\n projectRoot: string,\n options: MigrateLegacyStorageOptions = {},\n): Promise<MigrationResult> {\n const status = await resolveStorageMode(projectRoot, { env: options.env });\n const result = { copied: [] as string[], skipped: [] as string[] };\n\n try {\n await mkdir(status.globalDir, { recursive: true });\n } catch (err) {\n throw migrationFailure('.', status.globalDir, err);\n }\n\n if (status.legacyExists) {\n await copyTree(status.legacyDir, join(status.globalDir, ISSUES_DIR_NAME), '', result);\n }\n\n const now = (options.now ?? isoNow)();\n const metadataFile = join(status.globalDir, METADATA_FILENAME);\n const existing = await readExistingMetadata(metadataFile);\n\n const metadata: ProjectMetadata = {\n schemaVersion: STORAGE_SCHEMA_VERSION,\n projectId: status.projectId,\n root: resolve(projectRoot),\n remoteUrl: status.remoteUrl,\n // The project was first seen whenever it was first written, not now.\n createdAt: existing?.createdAt ?? now,\n updatedAt: now,\n lastAttemptAt: existing?.lastAttemptAt ?? null,\n };\n\n await writeFileAtomic(metadataFile, `${JSON.stringify(metadata, null, 2)}\\n`);\n\n return {\n projectId: status.projectId,\n globalDir: status.globalDir,\n legacyDir: status.legacyDir,\n previousMode: status.mode,\n ...result,\n metadataFile,\n metadata,\n };\n}\n\nexport interface RecordUserStoryNumberingOptions extends GetGlobalRootOptions {\n /** Timestamp source, injectable so tests can assert `updatedAt`. */\n now?: () => string;\n}\n\n/**\n * Persist a `plan` run's User Story numbering decision into the project's\n * `metadata.json`, for audit (issue #36) — this is a read-modify-write on the\n * same file {@link migrateLegacyStorage} stamps, never a second file.\n *\n * The decision itself is never read back to resolve a *later* one: the next\n * `plan` run always re-scans `tasks.json` from scratch (see\n * `storage/user-story-numbering.ts`). Losing this field only costs an audit\n * trail entry, never correctness.\n *\n * A missing `metadata.json` (a project whose first command ever is a numbered\n * `plan`, with no legacy directory to trigger a migration) is created here\n * from scratch, exactly like {@link migrateLegacyStorage} would.\n */\nexport async function recordUserStoryNumbering(\n projectRoot: string,\n decision: UserStoryNumberingDecision,\n options: RecordUserStoryNumberingOptions = {},\n): Promise<void> {\n const status = await resolveStorageMode(projectRoot, { env: options.env });\n const metadataFile = join(status.globalDir, METADATA_FILENAME);\n const existing = await readExistingMetadata(metadataFile);\n const now = (options.now ?? isoNow)();\n\n const metadata: ProjectMetadata = {\n schemaVersion: STORAGE_SCHEMA_VERSION,\n projectId: status.projectId,\n root: resolve(projectRoot),\n remoteUrl: status.remoteUrl,\n createdAt: existing?.createdAt ?? now,\n updatedAt: now,\n lastAttemptAt: existing?.lastAttemptAt ?? null,\n userStoryNumbering: decision,\n };\n\n await mkdir(status.globalDir, { recursive: true });\n await writeFileAtomic(metadataFile, `${JSON.stringify(metadata, null, 2)}\\n`);\n}\n"],"mappings":"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","names":["policy","policy","resolve","policy","basename","basename","stat","basename","join","resolve","join","resolve","join","resolve","directoryExists","stat","join","resolve","basename"]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/policy/resolve.ts","../src/policy/discovery.ts","../src/policy/parsers/docs.ts","../src/policy/parsers/labels.ts","../src/policy/parsers/templates.ts","../src/policy/parsers/issue-forms.ts","../src/policy/types.ts"],"sourcesContent":["import { isAbsolute, normalize, sep } from 'node:path';\nimport { loadPolicyConfig, mergeConfigLayers } from '../config.js';\nimport type { PolicyConfig, PolicyConfigInput } from '../schemas.js';\nimport { printWarning } from '../ui/logger.js';\nimport { getProjectRoot } from '../utils/git.js';\nimport {\n discoverBaseBranch,\n discoverCodeowners,\n discoverDocuments,\n discoverGitHubSlug,\n discoverIssueTemplates,\n discoverIssueTypes,\n discoverLabels,\n discoverOrganizationForms,\n discoverOrganizationTemplates,\n discoverPullRequestTemplates,\n} from './discovery.js';\nimport {\n POLICY_SCHEMA_VERSION,\n type PolicyExec,\n type PolicyGit,\n type PolicyIssues,\n type PolicyPullRequests,\n type PolicySource,\n type RepositoryPolicy,\n} from './types.js';\n\n/**\n * Resolution of the repository policy: discovery, precedence, scope and cache.\n */\n\nexport interface LoadRepositoryPolicyOptions {\n /** Repository root. Defaults to the git project root. */\n root?: string;\n /** Subdirectory the policy applies to, for monorepos. Root when omitted. */\n scope?: string | null;\n /** CLI overrides of the `policy` key. */\n cli?: PolicyConfigInput;\n /** Environment source. Defaults to process.env. */\n env?: NodeJS.ProcessEnv;\n /** Warning sink. Defaults to printWarning. */\n warn?: (message: string) => void;\n /** External command seam, for tests. Defaults to `utils/shell.ts`'s `run`. */\n exec?: PolicyExec;\n /** Set to false to bypass the per-process cache. Defaults to true. */\n cache?: boolean;\n}\n\n/**\n * Normalize a scope into a repository-relative POSIX path, or null.\n *\n * An absolute path, a path escaping the root and `.` all mean \"the root\": a\n * scope is a hint about where in a monorepo the work is happening, never a way\n * to read outside the repository.\n */\nexport function normalizeScope(scope: string | null | undefined): string | null {\n if (scope === null || scope === undefined) return null;\n\n const trimmed = scope.trim();\n if (trimmed === '' || trimmed === '.' || isAbsolute(trimmed)) return null;\n\n const normalized = normalize(trimmed)\n .split(sep)\n .join('/')\n .replace(/^\\.\\//, '')\n .replace(/\\/+$/, '');\n if (normalized === '' || normalized === '.' || normalized.startsWith('..')) return null;\n\n return normalized;\n}\n\n/** An enabled policy with nothing in it — also the shape of a disabled one. */\nfunction emptyPolicy(\n root: string,\n scope: string | null,\n enabled: boolean,\n sources: PolicySource[],\n): RepositoryPolicy {\n return {\n schemaVersion: POLICY_SCHEMA_VERSION,\n root,\n scope,\n enabled,\n issues: { templates: [], types: [], labels: [], titleConvention: null },\n pullRequests: { template: null, templates: [], baseBranch: null, titleConvention: null },\n git: { branchConvention: null, commitConvention: null },\n docs: [],\n codeowners: null,\n sources,\n };\n}\n\n/** Provenance entry for a discovery pass the configuration turned off. */\nfunction disabledSource(kind: PolicySource['kind']): PolicySource {\n return {\n kind,\n origin: 'config',\n path: null,\n status: 'disabled',\n detail: 'disabled by configuration',\n };\n}\n\n/** Which `sources` entry a declared configuration key is accounted under. */\nconst DECLARATION_KINDS = {\n issues: 'issue-templates',\n pullRequests: 'pull-request-template',\n git: 'base-branch',\n} as const satisfies Record<string, PolicySource['kind']>;\n\n/**\n * Resolve the policy of `root` for `scope`, uncached.\n *\n * The ladder, exactly as `mergeConfigLayers` applies it:\n *\n * Issue Flow defaults < discovered repository policies\n * < \"policy\" key of .issue-flow.json < ISSUE_FLOW_POLICY_* < CLI flags\n *\n * The three explicit layers are already collapsed by `loadPolicyConfig()`, so\n * what arrives here is \"the defaults\" and \"what the user declared\", with the\n * discovery in between. A declaration therefore corrects a wrong discovery\n * without having to disable it, and `discovery.*: false` turns a whole pass off\n * for the case where the discovery is not merely wrong but unwanted.\n */\nasync function resolvePolicy(\n root: string,\n scope: string | null,\n options: LoadRepositoryPolicyOptions,\n): Promise<RepositoryPolicy> {\n const warn = options.warn ?? printWarning;\n const config: PolicyConfig = await loadPolicyConfig({\n projectRoot: root,\n env: options.env,\n cli: options.cli,\n warn,\n });\n\n if (!config.enabled) {\n // Not a single stat() or round-trip: \"off\" has to be free, otherwise it is\n // not really a way out of a discovery that is misbehaving.\n return emptyPolicy(root, scope, false, [\n { kind: 'docs', origin: 'config', path: null, status: 'disabled', detail: 'policy.enabled' },\n ]);\n }\n\n const toggles = config.discovery;\n\n const [templateDiscovery, prTemplates, documents, codeowners] = await Promise.all([\n toggles.issueTemplates\n ? discoverIssueTemplates(root)\n : Promise.resolve({ templates: [], sources: [disabledSource('issue-templates')] }),\n toggles.pullRequestTemplate\n ? discoverPullRequestTemplates(root)\n : Promise.resolve({ templates: [], sources: [disabledSource('pull-request-template')] }),\n toggles.docs\n ? discoverDocuments(root, scope)\n : Promise.resolve({ documents: [], sources: [disabledSource('docs')] }),\n toggles.codeowners\n ? discoverCodeowners(root)\n : Promise.resolve({ content: null, sources: [disabledSource('codeowners')] }),\n ]);\n\n // The organization defaults are only worth asking for when the local tree has\n // none — that is precisely the case local discovery cannot see.\n const wantsOrgTemplates = toggles.issueTemplates && templateDiscovery.templates.length === 0;\n const [baseBranch, slug] = await Promise.all([\n discoverBaseBranch(root, options.exec),\n toggles.issueTypes || wantsOrgTemplates\n ? discoverGitHubSlug(root, options.exec)\n : Promise.resolve(null),\n ]);\n\n const [labels, issueTypes, orgTemplates, orgForms] = await Promise.all([\n toggles.labels\n ? discoverLabels(root, options.exec)\n : Promise.resolve({ labels: [], sources: [disabledSource('labels')] }),\n toggles.issueTypes\n ? discoverIssueTypes(root, slug?.owner ?? null, options.exec)\n : Promise.resolve({ types: [], sources: [disabledSource('issue-types')] }),\n wantsOrgTemplates\n ? discoverOrganizationTemplates(root, slug, options.exec)\n : Promise.resolve({ templates: [], sources: [] }),\n // Two calls, because GitHub answers the two kinds of organization default\n // through different surfaces: `issueTemplates` covers markdown templates and\n // is blind to Issue Forms, which only exist as files in the org's `.github`\n // repository. A repository whose organization publishes forms would\n // otherwise look like one with no templates at all.\n wantsOrgTemplates\n ? discoverOrganizationForms(root, slug?.owner ?? null, options.exec)\n : Promise.resolve({ templates: [], sources: [] }),\n ]);\n\n const sources: PolicySource[] = [\n ...templateDiscovery.sources,\n ...orgTemplates.sources,\n ...orgForms.sources,\n ...issueTypes.sources,\n ...labels.sources,\n ...prTemplates.sources,\n ...baseBranch.sources,\n ...documents.sources,\n ...codeowners.sources,\n ];\n\n // Only the base branch is inferable today; the conventions have no reliable\n // signal in a repository and are therefore declared, never guessed.\n const discoveredPullRequests: Partial<PolicyPullRequests> = {};\n if (baseBranch.baseBranch !== null) {\n discoveredPullRequests.baseBranch = baseBranch.baseBranch;\n }\n\n const issues = mergeConfigLayers<PolicyIssues>({\n defaults: { titleConvention: null },\n project: config.issues,\n });\n const pullRequests = mergeConfigLayers<PolicyPullRequests>({\n defaults: { baseBranch: null, titleConvention: null },\n discovered: discoveredPullRequests,\n project: config.pullRequests,\n });\n const git = mergeConfigLayers<PolicyGit>({\n defaults: { branchConvention: null, commitConvention: null },\n project: config.git,\n });\n\n for (const group of ['issues', 'pullRequests', 'git'] as const) {\n for (const key of Object.keys(config[group])) {\n sources.push({\n kind: DECLARATION_KINDS[group],\n origin: 'config',\n path: null,\n status: 'found',\n detail: `${group}.${key} declared`,\n });\n }\n }\n\n return {\n schemaVersion: POLICY_SCHEMA_VERSION,\n root,\n scope,\n enabled: true,\n issues: {\n templates: [...templateDiscovery.templates, ...orgTemplates.templates, ...orgForms.templates],\n types: issueTypes.types,\n labels: labels.labels,\n titleConvention: issues.titleConvention ?? null,\n },\n pullRequests: {\n template: prTemplates.templates[0]?.content ?? null,\n templates: prTemplates.templates,\n baseBranch: pullRequests.baseBranch ?? null,\n titleConvention: pullRequests.titleConvention ?? null,\n },\n git: {\n branchConvention: git.branchConvention ?? null,\n commitConvention: git.commitConvention ?? null,\n },\n docs: documents.documents,\n codeowners: codeowners.content,\n sources,\n };\n}\n\n/**\n * Per-process cache, keyed by `(root, scope)`.\n *\n * Discovery walks the filesystem and may talk to `gh`; a pipeline that resolved\n * it once per phase would pay that several times over for an answer that cannot\n * change mid-run. The promise — not its result — is cached, so two concurrent\n * callers share a single discovery instead of racing two.\n */\nconst cache = new Map<string, Promise<RepositoryPolicy>>();\n\n/** Drop every cached resolution. Exported for tests and long-lived processes. */\nexport function resetPolicyCache(): void {\n cache.clear();\n}\n\n/**\n * The repository policy applying to `scope`, resolved once per process.\n *\n * Never throws for a repository that declares nothing: the result is an empty\n * policy, with no warning, and every consumer keeps its own defaults. The only\n * error it can raise is the one `getProjectRoot()` already raises when there is\n * no repository at all — and passing `root` avoids even that.\n */\nexport async function loadRepositoryPolicy(\n options: LoadRepositoryPolicyOptions = {},\n): Promise<RepositoryPolicy> {\n const root = options.root ?? (await getProjectRoot());\n const scope = normalizeScope(options.scope);\n\n if (options.cache === false) {\n return resolvePolicy(root, scope, options);\n }\n\n const key = `${root}\u0000${scope ?? ''}`;\n const cached = cache.get(key);\n if (cached !== undefined) {\n return cached;\n }\n\n const pending = resolvePolicy(root, scope, options);\n cache.set(key, pending);\n try {\n return await pending;\n } catch (err) {\n // A rejected promise must not be served to every later caller.\n cache.delete(key);\n throw err;\n }\n}\n","import { readdir, readFile, stat } from 'node:fs/promises';\nimport { join, relative, sep } from 'node:path';\nimport { run } from '../utils/shell.js';\nimport { classifyDocument, extractReferencedDocuments } from './parsers/docs.js';\nimport { parseIssueTypes, parseLabels } from './parsers/labels.js';\nimport {\n parseIssueTemplateFile,\n parseOrganizationForms,\n parseOrganizationTemplates,\n} from './parsers/templates.js';\nimport {\n type IssueTemplate,\n type LabelDefinition,\n MAX_POLICY_DOCUMENT_BYTES,\n type PolicyDocument,\n type PolicyExec,\n type PolicySource,\n type PullRequestTemplate,\n} from './types.js';\n\n/**\n * Locating the policy sources of a repository on disk and, when `gh` can\n * answer, on GitHub.\n *\n * Every function here is best-effort by construction: an unreadable directory,\n * a missing binary, a network that never answers and a repository that simply\n * declares nothing all produce the same thing — an empty result plus, when the\n * source *could* have answered, a `PolicySource` recording why it did not.\n * Discovery must never be able to break a flow that works offline today.\n */\n\n/**\n * Timeout of every `gh` and `git` invocation. Matches the `init` prerequisite\n * probes; the point is that no discovery call can hang a pipeline.\n */\nexport const DISCOVERY_TIMEOUT_MS = 10_000;\n\n/** The directories GitHub itself looks in, in the order it looks. */\nconst POLICY_DIRECTORIES = ['.github', '', 'docs'] as const;\n\nconst defaultExec: PolicyExec = (command, args, options) =>\n run(command, args, { cwd: options.cwd, timeout: options.timeout });\n\n/** Repository-relative path with POSIX separators, for stable output. */\nexport function toRelativePath(root: string, absolute: string): string {\n return relative(root, absolute).split(sep).join('/');\n}\n\n/** Directory entries, or [] when the directory is absent or unreadable. */\nasync function listDirectory(dir: string): Promise<string[]> {\n try {\n const entries = await readdir(dir, { withFileTypes: true });\n return entries.filter((entry) => !entry.isDirectory()).map((entry) => entry.name);\n } catch {\n return [];\n }\n}\n\nasync function isDirectory(path: string): Promise<boolean> {\n try {\n return (await stat(path)).isDirectory();\n } catch {\n return false;\n }\n}\n\n/**\n * Read a file, capped at {@link MAX_POLICY_DOCUMENT_BYTES}.\n *\n * Returns null when the file is absent or unreadable. The second element says\n * whether the content was truncated, so the caller can record it in `sources`\n * instead of silently handing a half document to an agent.\n */\nasync function readCapped(path: string): Promise<{ content: string; truncated: boolean } | null> {\n let raw: string;\n try {\n raw = await readFile(path, 'utf-8');\n } catch {\n return null;\n }\n\n if (Buffer.byteLength(raw, 'utf-8') <= MAX_POLICY_DOCUMENT_BYTES) {\n return { content: raw, truncated: false };\n }\n const clipped = Buffer.from(raw, 'utf-8')\n .subarray(0, MAX_POLICY_DOCUMENT_BYTES)\n .toString('utf-8');\n return { content: clipped, truncated: true };\n}\n\n/** Find an entry of `dir` whose name matches one of `names`, case-insensitively. */\nasync function findFile(dir: string, names: string[]): Promise<string | null> {\n const entries = await listDirectory(dir);\n const wanted = names.map((name) => name.toLowerCase());\n for (const name of wanted) {\n const hit = entries.find((entry) => entry.toLowerCase() === name);\n if (hit !== undefined) return hit;\n }\n return null;\n}\n\n// ── Issue Templates and Forms ───────────────────────────────────────────────\n\nconst TEMPLATE_EXTENSIONS = ['.yml', '.yaml', '.md', '.markdown'];\n\n/** The chooser configuration is not a template. */\nfunction isTemplateFile(name: string): boolean {\n const lower = name.toLowerCase();\n if (lower === 'config.yml' || lower === 'config.yaml') return false;\n return TEMPLATE_EXTENSIONS.some((extension) => lower.endsWith(extension));\n}\n\nexport interface IssueTemplateDiscovery {\n templates: IssueTemplate[];\n sources: PolicySource[];\n}\n\n/**\n * Issue Templates and Forms of the local tree.\n *\n * Searched in `.github/ISSUE_TEMPLATE/`, the repository root and `docs/`, plus\n * the single-file `ISSUE_TEMPLATE.md` variant of each — the three locations\n * GitHub honours. Files are sorted by name so two runs over the same tree\n * produce the same order.\n */\nexport async function discoverIssueTemplates(root: string): Promise<IssueTemplateDiscovery> {\n const templates: IssueTemplate[] = [];\n const sources: PolicySource[] = [];\n\n for (const base of POLICY_DIRECTORIES) {\n const baseDir = base === '' ? root : join(root, base);\n\n const templateDir = (await findDirectory(baseDir, 'ISSUE_TEMPLATE')) ?? null;\n if (templateDir !== null) {\n const names = (await listDirectory(templateDir)).filter(isTemplateFile).sort();\n for (const name of names) {\n const filePath = join(templateDir, name);\n const file = await readCapped(filePath);\n if (file === null) continue;\n const relPath = toRelativePath(root, filePath);\n templates.push(parseIssueTemplateFile(relPath, file.content));\n sources.push({\n kind: 'issue-templates',\n origin: 'filesystem',\n path: relPath,\n status: 'found',\n detail: file.truncated ? 'content truncated' : null,\n });\n }\n }\n\n const singleFile = await findFile(baseDir, [\n 'ISSUE_TEMPLATE.md',\n 'ISSUE_TEMPLATE.yml',\n 'ISSUE_TEMPLATE.yaml',\n 'ISSUE_TEMPLATE.markdown',\n ]);\n if (singleFile !== null) {\n const filePath = join(baseDir, singleFile);\n const file = await readCapped(filePath);\n if (file !== null) {\n const relPath = toRelativePath(root, filePath);\n templates.push(parseIssueTemplateFile(relPath, file.content));\n sources.push({\n kind: 'issue-templates',\n origin: 'filesystem',\n path: relPath,\n status: 'found',\n detail: file.truncated ? 'content truncated' : null,\n });\n }\n }\n }\n\n return { templates, sources };\n}\n\n/** Case-insensitive lookup of a subdirectory, returning its real path. */\nasync function findDirectory(parent: string, name: string): Promise<string | null> {\n let entries: string[];\n try {\n entries = (await readdir(parent, { withFileTypes: true }))\n .filter((entry) => entry.isDirectory())\n .map((entry) => entry.name);\n } catch {\n return null;\n }\n const hit = entries.find((entry) => entry.toLowerCase() === name.toLowerCase());\n return hit === undefined ? null : join(parent, hit);\n}\n\n// ── Pull Request template ───────────────────────────────────────────────────\n\nconst PR_TEMPLATE_FILES = [\n 'PULL_REQUEST_TEMPLATE.md',\n 'PULL_REQUEST_TEMPLATE.markdown',\n 'PULL_REQUEST_TEMPLATE.txt',\n 'PULL_REQUEST_TEMPLATE',\n];\n\nexport interface PullRequestTemplateDiscovery {\n templates: PullRequestTemplate[];\n sources: PolicySource[];\n}\n\n/**\n * Pull Request templates, in every layout GitHub supports: the single file in\n * `.github/`, the root or `docs/`, and the `PULL_REQUEST_TEMPLATE/` directory\n * that holds several named ones.\n *\n * The first template found is the default one; the rest are kept so a caller\n * can offer the choice the repository set up.\n */\nexport async function discoverPullRequestTemplates(\n root: string,\n): Promise<PullRequestTemplateDiscovery> {\n const templates: PullRequestTemplate[] = [];\n const sources: PolicySource[] = [];\n\n const collect = async (filePath: string): Promise<void> => {\n const file = await readCapped(filePath);\n if (file === null) return;\n const relPath = toRelativePath(root, filePath);\n templates.push({\n path: relPath,\n name: relPath.split('/').pop() ?? relPath,\n content: file.content,\n });\n sources.push({\n kind: 'pull-request-template',\n origin: 'filesystem',\n path: relPath,\n status: 'found',\n detail: file.truncated ? 'content truncated' : null,\n });\n };\n\n for (const base of POLICY_DIRECTORIES) {\n const baseDir = base === '' ? root : join(root, base);\n\n const single = await findFile(baseDir, PR_TEMPLATE_FILES);\n if (single !== null && !(await isDirectory(join(baseDir, single)))) {\n await collect(join(baseDir, single));\n }\n\n const templateDir = await findDirectory(baseDir, 'PULL_REQUEST_TEMPLATE');\n if (templateDir !== null) {\n for (const name of (await listDirectory(templateDir)).sort()) {\n if (!/\\.(md|markdown|txt)$/i.test(name)) continue;\n await collect(join(templateDir, name));\n }\n }\n }\n\n return { templates, sources };\n}\n\n// ── CODEOWNERS ──────────────────────────────────────────────────────────────\n\nexport interface CodeownersDiscovery {\n content: string | null;\n sources: PolicySource[];\n}\n\nexport async function discoverCodeowners(root: string): Promise<CodeownersDiscovery> {\n for (const base of POLICY_DIRECTORIES) {\n const baseDir = base === '' ? root : join(root, base);\n const name = await findFile(baseDir, ['CODEOWNERS']);\n if (name === null) continue;\n\n const file = await readCapped(join(baseDir, name));\n if (file === null) continue;\n\n const relPath = toRelativePath(root, join(baseDir, name));\n return {\n content: file.content,\n sources: [\n {\n kind: 'codeowners',\n origin: 'filesystem',\n path: relPath,\n status: 'found',\n detail: file.truncated ? 'content truncated' : null,\n },\n ],\n };\n }\n\n return { content: null, sources: [] };\n}\n\n// ── Policy documents ────────────────────────────────────────────────────────\n\n/** Root-only documents: GitHub reads these from one place, not per directory. */\nconst ROOT_DOCUMENT_FILES = ['CONTRIBUTING.md', 'CODE_OF_CONDUCT.md'];\n\n/** Per-directory agent instructions, composed from the root down to the scope. */\nconst SCOPED_DOCUMENT_FILES = ['AGENTS.md', 'CLAUDE.md'];\n\nexport interface DocumentDiscovery {\n documents: PolicyDocument[];\n sources: PolicySource[];\n}\n\n/**\n * The scope ladder: `''`, then every ancestor of `scope`, then `scope` itself.\n * Composing in this order is what makes the more specific document win — it is\n * simply the later one.\n */\nexport function scopeLadder(scope: string | null): string[] {\n if (scope === null || scope === '') return [''];\n\n const segments = scope.split('/').filter((segment) => segment !== '');\n const ladder = [''];\n let current = '';\n for (const segment of segments) {\n current = current === '' ? segment : `${current}/${segment}`;\n ladder.push(current);\n }\n return ladder;\n}\n\n/**\n * Agent instructions and governance documents applying to `scope`.\n *\n * `AGENTS.md` (the open standard) and `CLAUDE.md` are read at every level of\n * the ladder, root first, so a monorepo's `apps/api/AGENTS.md` arrives after —\n * and therefore wins over — the one at the root. `CONTRIBUTING.md` and\n * `CODE_OF_CONDUCT.md` are read once, at the root, because that is where\n * GitHub reads them from.\n *\n * Documents linked from an `AGENTS.md` or a `CLAUDE.md` are followed one level;\n * see {@link extractReferencedDocuments} for why that is a link walk rather than\n * a scan of `docs/`.\n */\nexport async function discoverDocuments(\n root: string,\n scope: string | null,\n): Promise<DocumentDiscovery> {\n const documents: PolicyDocument[] = [];\n const sources: PolicySource[] = [];\n const seen = new Set<string>();\n\n const collect = async (\n filePath: string,\n kind: PolicyDocument['kind'],\n docScope: string,\n referencedFrom: string | null,\n ): Promise<PolicyDocument | null> => {\n const relPath = toRelativePath(root, filePath);\n if (seen.has(relPath)) return null;\n\n const file = await readCapped(filePath);\n if (file === null) return null;\n seen.add(relPath);\n\n const document: PolicyDocument = {\n path: relPath,\n kind,\n scope: docScope,\n referencedFrom,\n content: file.content,\n };\n documents.push(document);\n sources.push({\n kind: 'docs',\n origin: 'filesystem',\n path: relPath,\n status: 'found',\n detail: file.truncated ? 'content truncated' : null,\n });\n return document;\n };\n\n for (const base of POLICY_DIRECTORIES) {\n const baseDir = base === '' ? root : join(root, base);\n for (const wanted of ROOT_DOCUMENT_FILES) {\n const name = await findFile(baseDir, [wanted]);\n if (name === null) continue;\n const kind = classifyDocument(name);\n if (kind === null) continue;\n await collect(join(baseDir, name), kind, '', null);\n }\n }\n\n const indexes: PolicyDocument[] = [];\n for (const level of scopeLadder(scope)) {\n const levelDir = level === '' ? root : join(root, level);\n for (const wanted of SCOPED_DOCUMENT_FILES) {\n const name = await findFile(levelDir, [wanted]);\n if (name === null) continue;\n const kind = classifyDocument(name);\n if (kind === null) continue;\n const document = await collect(join(levelDir, name), kind, level, null);\n // Both agent-instruction files are indexes. A `CLAUDE.md` whose entire\n // content is \"Read and follow the instructions in AGENTS.md\" is a real and\n // common shape: stopping at it would report a repository that declares\n // nothing, when in fact it forwards everything.\n if (document !== null && (kind === 'agents' || kind === 'claude')) {\n indexes.push(document);\n }\n }\n }\n\n // One level only: a referenced document that itself links onwards does not\n // drag its own bibliography into the context.\n for (const index of indexes) {\n for (const target of extractReferencedDocuments(index.content, index.path)) {\n await collect(join(root, target), 'referenced', index.scope, index.path);\n }\n }\n\n return { documents, sources };\n}\n\n// ── git ─────────────────────────────────────────────────────────────────────\n\nexport interface BaseBranchDiscovery {\n baseBranch: string | null;\n sources: PolicySource[];\n}\n\n/**\n * The repository's base branch, as git actually knows it.\n *\n * Unlike `utils/git.ts`'s `getBaseBranch()`, this never falls back to `'main'`:\n * a discovery layer that invents a value is indistinguishable from one that\n * found it, and `sources` would be lying. An undetermined base branch is null,\n * and the configuration layer above is free to declare one.\n */\nexport async function discoverBaseBranch(\n root: string,\n exec: PolicyExec = defaultExec,\n): Promise<BaseBranchDiscovery> {\n const options = { cwd: root, timeout: DISCOVERY_TIMEOUT_MS };\n\n try {\n const remoteHead = await exec(\n 'git',\n ['symbolic-ref', '--short', 'refs/remotes/origin/HEAD'],\n options,\n );\n if (remoteHead.exitCode === 0) {\n const name = remoteHead.stdout.trim().replace(/^origin\\//, '');\n if (name !== '') {\n return {\n baseBranch: name,\n sources: [\n {\n kind: 'base-branch',\n origin: 'git',\n path: null,\n status: 'found',\n detail: 'origin/HEAD',\n },\n ],\n };\n }\n }\n\n for (const candidate of ['main', 'master']) {\n const check = await exec(\n 'git',\n ['rev-parse', '--verify', '--quiet', `refs/heads/${candidate}`],\n options,\n );\n if (check.exitCode === 0) {\n return {\n baseBranch: candidate,\n sources: [\n {\n kind: 'base-branch',\n origin: 'git',\n path: null,\n status: 'found',\n detail: `refs/heads/${candidate}`,\n },\n ],\n };\n }\n }\n } catch {\n return {\n baseBranch: null,\n sources: [\n {\n kind: 'base-branch',\n origin: 'git',\n path: null,\n status: 'unavailable',\n detail: 'git could not be executed',\n },\n ],\n };\n }\n\n return { baseBranch: null, sources: [] };\n}\n\n/**\n * `owner` and `repo` of the `origin` remote, with their original case.\n *\n * Deliberately derived from git rather than from `gh repo view`: the identity\n * is already on disk, and spending a network round-trip on it would break the\n * \"at most one `gh` call per kind of data\" budget for nothing.\n */\nexport async function discoverGitHubSlug(\n root: string,\n exec: PolicyExec = defaultExec,\n): Promise<{ owner: string; repo: string } | null> {\n let url = '';\n try {\n const result = await exec('git', ['remote', 'get-url', 'origin'], {\n cwd: root,\n timeout: DISCOVERY_TIMEOUT_MS,\n });\n if (result.exitCode !== 0) return null;\n url = result.stdout.trim();\n } catch {\n return null;\n }\n if (url === '') return null;\n\n // Both the scp-like shorthand and every URL scheme end in `<owner>/<repo>`.\n const match = /[/:]([^/:\\s]+)\\/([^/\\s]+?)(?:\\.git)?\\/?$/.exec(url);\n if (match?.[1] === undefined || match[2] === undefined) return null;\n return { owner: match[1], repo: match[2] };\n}\n\n// ── GitHub, via gh ──────────────────────────────────────────────────────────\n\n/** One `gh` call, JSON-decoded. Returns null for every kind of failure. */\nasync function ghJson(\n root: string,\n args: string[],\n exec: PolicyExec,\n): Promise<{ value: unknown } | { error: string }> {\n let result: { stdout: string; stderr: string; exitCode: number };\n try {\n result = await exec('gh', args, { cwd: root, timeout: DISCOVERY_TIMEOUT_MS });\n } catch (err) {\n return { error: err instanceof Error ? err.message : String(err) };\n }\n if (result.exitCode !== 0) {\n const stderr = result.stderr.trim().split('\\n')[0] ?? '';\n return { error: stderr === '' ? `gh exited with ${result.exitCode}` : stderr };\n }\n try {\n return { value: JSON.parse(result.stdout) as unknown };\n } catch {\n return { error: 'gh returned unparseable JSON' };\n }\n}\n\n/** How many labels a single `gh label list` page returns. */\nconst LABEL_PAGE_SIZE = 200;\n\nexport interface LabelDiscovery {\n labels: LabelDefinition[];\n sources: PolicySource[];\n}\n\n/**\n * The labels that exist in the repository.\n *\n * A machine with no `gh`, no authentication or no network degrades to an empty\n * list and a single `unavailable` source — never to a warning, and never to a\n * failure: reading labels is an enrichment, not a prerequisite.\n */\nexport async function discoverLabels(\n root: string,\n exec: PolicyExec = defaultExec,\n): Promise<LabelDiscovery> {\n const outcome = await ghJson(\n root,\n ['label', 'list', '--json', 'name,description,color', '--limit', String(LABEL_PAGE_SIZE)],\n exec,\n );\n\n if ('error' in outcome) {\n return {\n labels: [],\n sources: [\n { kind: 'labels', origin: 'gh', path: null, status: 'unavailable', detail: outcome.error },\n ],\n };\n }\n\n const labels = parseLabels(outcome.value);\n return {\n labels,\n sources: [\n {\n kind: 'labels',\n origin: 'gh',\n path: null,\n status: 'found',\n detail: `${labels.length} label(s)`,\n },\n ],\n };\n}\n\nexport interface IssueTypeDiscovery {\n types: string[];\n sources: PolicySource[];\n}\n\n/**\n * Issue Types of the organization owning the repository.\n *\n * Requires the owner, which comes from the git remote; a repository with no\n * remote simply has no organization to ask about, which is an ordinary state\n * and not an `unavailable` source.\n */\nexport async function discoverIssueTypes(\n root: string,\n owner: string | null,\n exec: PolicyExec = defaultExec,\n): Promise<IssueTypeDiscovery> {\n if (owner === null || owner === '') {\n return { types: [], sources: [] };\n }\n\n const outcome = await ghJson(root, ['api', `orgs/${owner}/issue-types`], exec);\n if ('error' in outcome) {\n return {\n types: [],\n sources: [\n {\n kind: 'issue-types',\n origin: 'gh',\n path: null,\n // A personal account (404) and a plan without Issue Types (403) are\n // the common answers here, so the detail carries what gh said.\n status: 'unavailable',\n detail: outcome.error,\n },\n ],\n };\n }\n\n const types = parseIssueTypes(outcome.value);\n return {\n types,\n sources: [\n {\n kind: 'issue-types',\n origin: 'gh',\n path: null,\n status: 'found',\n detail: `${types.length} type(s)`,\n },\n ],\n };\n}\n\n/**\n * The `issueTemplates` connection, as one GraphQL document.\n *\n * Owner and repository travel as GraphQL variables rather than being spliced\n * into the query text: they come from a git remote, which is user-controlled\n * input, and a query built by concatenation is a query someone can rewrite.\n */\nconst ORGANIZATION_TEMPLATES_QUERY = `query($owner:String!,$name:String!){\n repository(owner:$owner,name:$name){\n issueTemplates{\n name\n title\n about\n filename\n body\n assignees(first:10){nodes{login}}\n labels(first:20){nodes{name}}\n }\n }\n}`;\n\n/**\n * The organization's `.github` repository, read as a tree.\n *\n * `issueTemplates` above only ever returns **markdown** templates: GitHub does\n * not expose organization-level Issue *Forms* through it, and a repository whose\n * organization publishes `.yml` forms therefore looks like a repository with no\n * templates at all. Reading the tree of the org's `.github` repository is the\n * only way to see them.\n *\n * One round-trip for names *and* contents, which is why it is a tree query\n * rather than the contents endpoint (a directory listing there carries no file\n * bodies, costing one call per template).\n */\nconst ORGANIZATION_FORMS_QUERY = `query($owner:String!){\n repository(owner:$owner,name:\".github\"){\n object(expression:\"HEAD:.github/ISSUE_TEMPLATE\"){\n ... on Tree {\n entries { name type object { ... on Blob { text } } }\n }\n }\n }\n}`;\n\n/**\n * Issue Forms published by the organization's `.github` repository.\n *\n * Answers an empty list for every ordinary case — no organization, no `.github`\n * repository, no `ISSUE_TEMPLATE` directory — so it is only ever an enrichment.\n */\nexport async function discoverOrganizationForms(\n root: string,\n owner: string | null,\n exec: PolicyExec = defaultExec,\n): Promise<IssueTemplateDiscovery> {\n if (owner === null || owner === '') {\n return { templates: [], sources: [] };\n }\n\n const outcome = await ghJson(\n root,\n ['api', 'graphql', '-f', `query=${ORGANIZATION_FORMS_QUERY}`, '-f', `owner=${owner}`],\n exec,\n );\n if ('error' in outcome) {\n return {\n templates: [],\n sources: [\n {\n kind: 'issue-templates',\n origin: 'gh',\n path: null,\n status: 'unavailable',\n detail: outcome.error,\n },\n ],\n };\n }\n\n const templates = parseOrganizationForms(outcome.value);\n if (templates.length === 0) {\n return { templates, sources: [] };\n }\n return {\n templates,\n sources: templates.map((template) => ({\n kind: 'issue-templates' as const,\n origin: 'gh' as const,\n path: null,\n status: 'found' as const,\n detail: `organization form \"${template.path}\"`,\n })),\n };\n}\n\n/**\n * Issue Templates served by GitHub, including the ones the organization's\n * `.github` repository provides.\n *\n * Only consulted when the local tree has none: that is precisely the case local\n * discovery cannot see, and consulting it otherwise would spend a round-trip to\n * re-learn what is already on disk.\n *\n * GraphQL, not REST: the REST API exposes no issue-template endpoint at all,\n * and the connection returns the bodies inline, so the organization defaults\n * cost a single round-trip instead of one listing plus one call per template.\n */\nexport async function discoverOrganizationTemplates(\n root: string,\n slug: { owner: string; repo: string } | null,\n exec: PolicyExec = defaultExec,\n): Promise<IssueTemplateDiscovery> {\n if (slug === null) {\n return { templates: [], sources: [] };\n }\n\n const outcome = await ghJson(\n root,\n [\n 'api',\n 'graphql',\n '-f',\n `query=${ORGANIZATION_TEMPLATES_QUERY}`,\n '-f',\n `owner=${slug.owner}`,\n '-f',\n `name=${slug.repo}`,\n ],\n exec,\n );\n if ('error' in outcome) {\n return {\n templates: [],\n sources: [\n {\n kind: 'issue-templates',\n origin: 'gh',\n path: null,\n status: 'unavailable',\n detail: outcome.error,\n },\n ],\n };\n }\n\n const templates = parseOrganizationTemplates(outcome.value);\n if (templates.length === 0) {\n return { templates, sources: [] };\n }\n return {\n templates,\n sources: templates.map((template) => ({\n kind: 'issue-templates' as const,\n origin: 'gh' as const,\n path: null,\n status: 'found' as const,\n detail: `organization default \"${template.name}\"`,\n })),\n };\n}\n","import { posix } from 'node:path';\nimport type { PolicyDocumentKind } from '../types.js';\n\n/**\n * Classification and link extraction for the prose documents that carry\n * repository policy.\n */\n\n/** Which kind of policy document a file name denotes, or null. */\nexport function classifyDocument(fileName: string): PolicyDocumentKind | null {\n switch (fileName.toLowerCase()) {\n case 'agents.md':\n return 'agents';\n case 'claude.md':\n return 'claude';\n case 'contributing.md':\n return 'contributing';\n case 'code_of_conduct.md':\n case 'code-of-conduct.md':\n return 'code-of-conduct';\n default:\n return null;\n }\n}\n\n/** How many referenced documents a single index may pull in. */\nexport const MAX_REFERENCED_DOCUMENTS = 20;\n\nconst MARKDOWN_LINK = /\\[[^\\]\\n]*\\]\\(([^)\\s]+)(?:\\s+\"[^\"]*\")?\\)/g;\n\n/**\n * Repository-relative paths of the markdown documents an index links to.\n *\n * `AGENTS.md` is an index, not a corpus: following its links one level respects\n * what the repository chose to point at, where scanning `docs/` blindly would\n * pull in changelogs, ADR archives and translated copies — a large context cost\n * for content the repository never nominated as policy.\n *\n * Only in-repository markdown targets survive: an absolute URL, a mailto, an\n * anchor, a path that escapes the root, or a link to a non-markdown asset are\n * all dropped. `fromPath` is the repository-relative path of the linking\n * document, used to resolve relative links.\n */\nexport function extractReferencedDocuments(content: string, fromPath: string): string[] {\n const fromDir = posix.dirname(fromPath.replace(/\\\\/g, '/'));\n const found: string[] = [];\n\n for (const match of content.matchAll(MARKDOWN_LINK)) {\n const raw = match[1];\n if (raw === undefined) continue;\n\n const target = raw.split('#')[0]?.split('?')[0]?.trim() ?? '';\n if (target === '') continue;\n // Absolute URLs, protocol-relative URLs and mail links point outside the\n // repository; a Windows-style drive letter is not a repository path either.\n if (/^[a-zA-Z][a-zA-Z0-9+.-]*:/.test(target) || target.startsWith('//')) continue;\n if (!/\\.(md|markdown)$/i.test(target)) continue;\n\n const resolved = posix.normalize(\n target.startsWith('/')\n ? target.slice(1)\n : fromDir === '.'\n ? target\n : posix.join(fromDir, target),\n );\n if (resolved.startsWith('..') || resolved === '' || resolved === '.') continue;\n if (resolved === fromPath || found.includes(resolved)) continue;\n\n found.push(resolved);\n if (found.length >= MAX_REFERENCED_DOCUMENTS) break;\n }\n\n return found;\n}\n","import type { LabelDefinition } from '../types.js';\n\n/**\n * Parse `gh label list --json name,description,color`.\n *\n * These are the labels that *really exist*. The distinction matters: a pipeline\n * that invents a label either fails to apply it or, worse, creates it — and a\n * taxonomy nobody agreed to is harder to undo than a missing label.\n *\n * Never throws: a payload that is not an array of label-shaped objects yields\n * an empty list, indistinguishable to the caller from \"gh answered nothing\".\n */\nexport function parseLabels(payload: unknown): LabelDefinition[] {\n if (!Array.isArray(payload)) return [];\n\n const labels: LabelDefinition[] = [];\n for (const entry of payload) {\n if (entry === null || typeof entry !== 'object') continue;\n const record = entry as Record<string, unknown>;\n const name = typeof record.name === 'string' ? record.name.trim() : '';\n if (name === '') continue;\n\n const description = typeof record.description === 'string' ? record.description.trim() : '';\n const color = typeof record.color === 'string' ? record.color.trim() : '';\n\n labels.push({\n name,\n description: description === '' ? null : description,\n color: color === '' ? null : color,\n });\n }\n return labels;\n}\n\n/**\n * Parse `gh api orgs/{org}/issue-types`.\n *\n * Issue Types are an organization-level taxonomy exposed only on some plans, so\n * an empty result is the normal case rather than a failure.\n */\nexport function parseIssueTypes(payload: unknown): string[] {\n const entries = Array.isArray(payload)\n ? payload\n : Array.isArray((payload as { issue_types?: unknown })?.issue_types)\n ? (payload as { issue_types: unknown[] }).issue_types\n : [];\n\n const types: string[] = [];\n for (const entry of entries) {\n const name =\n typeof entry === 'string'\n ? entry\n : entry !== null && typeof entry === 'object'\n ? (entry as Record<string, unknown>).name\n : undefined;\n if (typeof name !== 'string') continue;\n const trimmed = name.trim();\n if (trimmed !== '' && !types.includes(trimmed)) {\n types.push(trimmed);\n }\n }\n return types;\n}\n","import { basename, extname } from 'node:path';\nimport type { IssueTemplate } from '../types.js';\nimport { emptyTemplateMetadata, extractFrontMatter, parseTemplateMetadata } from './issue-forms.js';\n\n/**\n * Turn a template file into an {@link IssueTemplate}.\n *\n * The format is decided by the extension, not by the content: `.yml`/`.yaml` is\n * an Issue Form whose whole document is metadata, everything else is a legacy\n * markdown template whose metadata — if any — lives in a front matter block.\n */\nexport function parseIssueTemplateFile(relPath: string, content: string): IssueTemplate {\n const extension = extname(relPath).toLowerCase();\n const isForm = extension === '.yml' || extension === '.yaml';\n\n const metadataSource = isForm ? content : extractFrontMatter(content);\n const metadata =\n metadataSource === null ? emptyTemplateMetadata() : parseTemplateMetadata(metadataSource);\n\n return {\n path: relPath,\n format: isForm ? 'form' : 'markdown',\n origin: 'filesystem',\n // A template with no `name:` is still addressable by its file name, which\n // is what GitHub falls back to for legacy markdown templates.\n name: metadata.name ?? basename(relPath, extension),\n about: metadata.about,\n title: metadata.title,\n labels: metadata.labels,\n type: metadata.type,\n assignees: metadata.assignees,\n content,\n };\n}\n\nfunction asString(value: unknown): string | null {\n return typeof value === 'string' && value.trim() !== '' ? value : null;\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n\n/**\n * Read a GraphQL connection (`{ nodes: [{ name }] }`) as a list of names, also\n * accepting a plain array of strings so a shape change does not break parsing.\n */\nfunction asNameList(value: unknown, key: 'name' | 'login'): string[] {\n const entries = Array.isArray(value)\n ? value\n : isRecord(value) && Array.isArray(value.nodes)\n ? value.nodes\n : [];\n\n return entries\n .map((entry) =>\n typeof entry === 'string' ? entry : asString(isRecord(entry) ? entry[key] : null),\n )\n .filter((entry): entry is string => entry !== null && entry !== '');\n}\n\n/**\n * Parse the `repository.issueTemplates` connection of the GitHub GraphQL API.\n *\n * This is what makes the organization's `.github` repository visible: a\n * repository with no `.github/ISSUE_TEMPLATE/` of its own still serves the\n * organization defaults on github.com, and no amount of local filesystem\n * discovery can see them. GraphQL is used rather than REST because REST simply\n * has no endpoint for issue templates — and because the connection answers with\n * the bodies included, which keeps the whole discovery to a single round-trip.\n *\n * Never throws: an error payload, a missing repository or anything that is not\n * a list of template-shaped objects yields an empty list, which the caller\n * treats exactly like \"GitHub answered nothing\".\n */\nexport function parseOrganizationTemplates(payload: unknown): IssueTemplate[] {\n const repository = isRecord(payload) && isRecord(payload.data) ? payload.data.repository : null;\n const entries = isRecord(repository) ? repository.issueTemplates : null;\n if (!Array.isArray(entries)) return [];\n\n const templates: IssueTemplate[] = [];\n for (const entry of entries) {\n if (!isRecord(entry)) continue;\n const name = asString(entry.name);\n if (name === null) continue;\n\n templates.push({\n // The filename is the stable identity; the display name is not unique.\n path: asString(entry.filename) ?? name,\n format: 'markdown',\n origin: 'organization',\n name,\n about: asString(entry.about),\n title: asString(entry.title),\n labels: asNameList(entry.labels, 'name'),\n // GraphQL exposes no Issue Type on a template, so it stays unknown here\n // rather than being guessed from the name.\n type: null,\n assignees: asNameList(entry.assignees, 'login'),\n content: asString(entry.body) ?? '',\n });\n }\n return templates;\n}\n\n/**\n * Parse the organization `.github` repository's `ISSUE_TEMPLATE` tree.\n *\n * These are the Issue **Forms** an organization publishes, which the\n * `issueTemplates` connection cannot see. Each entry is a real file, so it is\n * parsed exactly like a local one — the only difference is where it came from,\n * and the caller marks that on `origin`.\n */\nexport function parseOrganizationForms(payload: unknown): IssueTemplate[] {\n const repository = isRecord(payload) && isRecord(payload.data) ? payload.data.repository : null;\n const tree = isRecord(repository) ? repository.object : null;\n const entries = isRecord(tree) && Array.isArray(tree.entries) ? tree.entries : [];\n\n const templates: IssueTemplate[] = [];\n for (const entry of entries) {\n if (!isRecord(entry)) continue;\n const name = asString(entry.name);\n if (name === null || entry.type !== 'blob') continue;\n // The chooser configuration is not a template, same as on the filesystem.\n if (/^config\\.(yml|yaml)$/i.test(name)) continue;\n if (!/\\.(yml|yaml|md|markdown)$/i.test(name)) continue;\n\n const text = isRecord(entry.object) ? asString(entry.object.text) : null;\n if (text === null) continue;\n\n templates.push({ ...parseIssueTemplateFile(name, text), origin: 'organization' });\n }\n\n return templates.sort((a, b) => a.path.localeCompare(b.path));\n}\n","/**\n * A deliberately small YAML reader for Issue Form and Issue Template metadata.\n *\n * Issue Flow does not need a YAML engine: it needs six top-level keys (`name`,\n * `description`/`about`, `title`, `labels`, `type`, `assignees`) out of a file\n * whose remaining structure — the `body:` array of form elements — it has no\n * use for. Pulling a parser dependency in to read those six keys would trade a\n * supply-chain surface for nothing.\n *\n * The reader therefore works on top-level keys only: it locates every line that\n * starts a key at indentation zero, and parses the region of the ones it cares\n * about. Anything nested (a `body:` element, its `attributes:`) is skipped\n * whole, which is why `- type: markdown` inside `body:` can never be mistaken\n * for the top-level `type:` of an Issue Type.\n */\n\n/** The metadata block shared by Issue Forms (`.yml`) and templates (`.md`). */\nexport interface TemplateMetadata {\n name: string | null;\n about: string | null;\n title: string | null;\n labels: string[];\n type: string | null;\n assignees: string[];\n}\n\nexport function emptyTemplateMetadata(): TemplateMetadata {\n return { name: null, about: null, title: null, labels: [], type: null, assignees: [] };\n}\n\nconst TOP_LEVEL_KEY = /^([A-Za-z_][A-Za-z0-9_-]*):(?:\\s+(.*))?$/;\n\ninterface KeyRegion {\n key: string;\n /** Text on the key's own line, after the colon. Empty when the value is a block. */\n inline: string;\n /** Lines below the key, up to the next top-level key. */\n block: string[];\n}\n\n/**\n * Split a YAML document into its top-level key regions.\n *\n * Exported for the templates parser, which reads the same regions out of a\n * markdown front matter block.\n */\nexport function topLevelRegions(source: string): Map<string, KeyRegion> {\n const regions = new Map<string, KeyRegion>();\n const lines = source.split(/\\r?\\n/);\n\n let current: KeyRegion | null = null;\n for (const line of lines) {\n // A line that begins with whitespace, a dash or a comment can never open a\n // top-level key, so it always belongs to the region already open.\n const match = /^[^\\s#-]/.test(line) ? TOP_LEVEL_KEY.exec(line) : null;\n if (match?.[1] !== undefined) {\n current = { key: match[1], inline: (match[2] ?? '').trim(), block: [] };\n // First occurrence wins, matching how a YAML loader would report a\n // duplicate key as a document error rather than silently taking the last.\n if (!regions.has(current.key)) {\n regions.set(current.key, current);\n }\n continue;\n }\n current?.block.push(line);\n }\n\n return regions;\n}\n\n/** Strip a trailing `# comment` that is not inside quotes. */\nfunction stripComment(value: string): string {\n let quote: string | null = null;\n for (let i = 0; i < value.length; i++) {\n const char = value[i];\n if (quote !== null) {\n if (char === quote) quote = null;\n continue;\n }\n if (char === '\"' || char === \"'\") {\n quote = char;\n continue;\n }\n if (char === '#' && (i === 0 || /\\s/.test(value[i - 1] ?? ''))) {\n return value.slice(0, i);\n }\n }\n return value;\n}\n\n/** Unquote a scalar, handling the two YAML quoting styles. */\nfunction unquote(value: string): string {\n const trimmed = value.trim();\n if (trimmed.length >= 2) {\n const first = trimmed[0];\n const last = trimmed[trimmed.length - 1];\n if (first === '\"' && last === '\"') {\n return trimmed.slice(1, -1).replace(/\\\\([\"\\\\])/g, '$1');\n }\n if (first === \"'\" && last === \"'\") {\n return trimmed.slice(1, -1).replace(/''/g, \"'\");\n }\n }\n return trimmed;\n}\n\n/** Split a flow sequence body (`a, \"b, c\", d`) on its top-level commas. */\nfunction splitFlow(inner: string): string[] {\n const parts: string[] = [];\n let buffer = '';\n let quote: string | null = null;\n\n for (const char of inner) {\n if (quote !== null) {\n if (char === quote) quote = null;\n buffer += char;\n continue;\n }\n if (char === '\"' || char === \"'\") {\n quote = char;\n buffer += char;\n continue;\n }\n if (char === ',') {\n parts.push(buffer);\n buffer = '';\n continue;\n }\n buffer += char;\n }\n parts.push(buffer);\n\n return parts.map(unquote).filter((part) => part !== '');\n}\n\n/** Read a region as a scalar, returning null when it holds no usable value. */\nfunction readScalar(region: KeyRegion | undefined): string | null {\n if (region === undefined) return null;\n\n const inline = stripComment(region.inline).trim();\n if (inline !== '' && inline !== '|' && inline !== '>' && inline !== '|-' && inline !== '>-') {\n const value = unquote(inline);\n return value === '' ? null : value;\n }\n\n // Block scalar (`|`, `>`) or a value that simply starts on the next line.\n const collected = region.block\n .filter((line) => line.trim() !== '')\n .map((line) => line.trim())\n .join(inline.startsWith('>') ? ' ' : '\\n')\n .trim();\n return collected === '' ? null : collected;\n}\n\n/** Read a region as a sequence, accepting both the flow and the block style. */\nfunction readSequence(region: KeyRegion | undefined): string[] {\n if (region === undefined) return [];\n\n const inline = stripComment(region.inline).trim();\n if (inline.startsWith('[')) {\n const closing = inline.lastIndexOf(']');\n return splitFlow(inline.slice(1, closing === -1 ? undefined : closing));\n }\n if (inline !== '') {\n // A bare scalar where a list was expected — GitHub accepts `labels: bug`.\n const single = unquote(inline);\n return single === '' ? [] : [single];\n }\n\n const items: string[] = [];\n for (const line of region.block) {\n const trimmed = line.trim();\n if (trimmed === '' || trimmed.startsWith('#')) continue;\n if (!trimmed.startsWith('- ') && trimmed !== '-') break;\n const value = unquote(stripComment(trimmed.slice(1)).trim());\n // `- key: value` is a mapping, not a label — a list of those is not a\n // taxonomy and taking its first line would invent one.\n if (value === '' || /^[A-Za-z_][A-Za-z0-9_-]*:\\s/.test(value)) continue;\n items.push(value);\n }\n return items;\n}\n\n/**\n * Read the metadata of an Issue Form or Issue Template body.\n *\n * Never throws: a file that is not YAML at all yields empty metadata, which is\n * the same thing the caller does with a template it cannot understand.\n */\nexport function parseTemplateMetadata(source: string): TemplateMetadata {\n const regions = topLevelRegions(source);\n\n return {\n name: readScalar(regions.get('name')),\n // Issue Forms spell it `description`, legacy markdown templates `about`.\n about: readScalar(regions.get('about')) ?? readScalar(regions.get('description')),\n title: readScalar(regions.get('title')),\n labels: readSequence(regions.get('labels')),\n type: readScalar(regions.get('type')),\n assignees: readSequence(regions.get('assignees')),\n };\n}\n\n/**\n * Extract the YAML front matter of a markdown template.\n *\n * Returns null when the file does not open with a `---` fence, which is the\n * common case for a plain `.md` template with no metadata at all.\n */\nexport function extractFrontMatter(source: string): string | null {\n const normalized = source.replace(/^\\uFEFF/, '');\n if (!/^---[ \\t]*\\r?\\n/.test(normalized)) return null;\n\n const rest = normalized.slice(normalized.indexOf('\\n') + 1);\n const closing = /^---[ \\t]*$/m.exec(rest);\n if (closing?.index === undefined) return null;\n\n return rest.slice(0, closing.index);\n}\n","/**\n * The repository policy layer: what the *consumer* repository already declares\n * about how issues, Pull Requests, branches and agents are supposed to work.\n *\n * Every field is discovered, never invented. A repository that declares nothing\n * produces an empty policy — which is exactly the behaviour Issue Flow had\n * before this layer existed.\n */\n\n/**\n * Version of the {@link RepositoryPolicy} shape. Bumped whenever a consumer\n * reading `issue-flow policy --json` would have to change to keep working;\n * purely additive fields do not bump it.\n */\nexport const POLICY_SCHEMA_VERSION = 1;\n\n/** What a {@link PolicySource} is accounting for. */\nexport type PolicySourceKind =\n | 'issue-templates'\n | 'issue-types'\n | 'labels'\n | 'pull-request-template'\n | 'base-branch'\n | 'docs'\n | 'codeowners';\n\n/** Where a {@link PolicySource} came from. */\nexport type PolicySourceOrigin = 'filesystem' | 'gh' | 'git' | 'config' | 'env' | 'cli';\n\n/**\n * Outcome of consulting a source.\n *\n * `found` and `disabled` are ordinary states. `unavailable` is the one that\n * matters for debugging: the source *could* have answered but did not — no\n * `gh`, no network, a timeout — and the policy is therefore incomplete rather\n * than empty. Nothing absent is recorded: a repository without a\n * `CONTRIBUTING.md` would otherwise drown `sources` in negative entries.\n */\nexport type PolicySourceStatus = 'found' | 'unavailable' | 'disabled';\n\n/**\n * Provenance of one piece of the resolved policy.\n *\n * Not an accessory: without it there is no way to explain why a decision was\n * taken, nor to debug a wrong discovery.\n */\nexport interface PolicySource {\n kind: PolicySourceKind;\n origin: PolicySourceOrigin;\n /** Repository-relative path (POSIX separators), or null for non-file sources. */\n path: string | null;\n status: PolicySourceStatus;\n /** Human-readable note: the gh command that failed, the reason, a count. */\n detail: string | null;\n}\n\n/** An Issue Template or Issue Form declared by the repository. */\nexport interface IssueTemplate {\n /** Repository-relative path, or the org-level template name when remote. */\n path: string;\n /** `form` for a GitHub Issue Form (`.yml`), `markdown` for a legacy template. */\n format: 'form' | 'markdown';\n /** Where the template was found — the local tree or the organization defaults. */\n origin: 'filesystem' | 'organization';\n name: string | null;\n about: string | null;\n /** Pre-filled title, which is also the closest thing to a title convention. */\n title: string | null;\n labels: string[];\n /** GitHub Issue Type declared by the template (`type:` key), when present. */\n type: string | null;\n assignees: string[];\n /** Raw file content, truncated at {@link MAX_POLICY_DOCUMENT_BYTES}. */\n content: string;\n}\n\n/** A label that really exists in the repository. */\nexport interface LabelDefinition {\n name: string;\n description: string | null;\n color: string | null;\n}\n\n/** A Pull Request template file. */\nexport interface PullRequestTemplate {\n path: string;\n name: string;\n content: string;\n}\n\n/** What a {@link PolicyDocument} is. */\nexport type PolicyDocumentKind =\n | 'agents'\n | 'claude'\n | 'contributing'\n | 'code-of-conduct'\n | 'referenced';\n\n/** A prose document that carries repository policy. */\nexport interface PolicyDocument {\n path: string;\n kind: PolicyDocumentKind;\n /**\n * Directory the document governs, repository-relative; `''` is the root.\n * Documents are ordered from the root down to the requested scope, so the\n * later entry is the more specific one.\n */\n scope: string;\n /** Repository-relative path of the document that linked to this one. */\n referencedFrom: string | null;\n /** File content, truncated at {@link MAX_POLICY_DOCUMENT_BYTES}. */\n content: string;\n}\n\n/** Conventions of the repository around issues. */\nexport interface PolicyIssues {\n templates: IssueTemplate[];\n /** Issue Types of the organization, when the plan exposes them. */\n types: string[];\n /** Labels that really exist in the repository, never a guessed taxonomy. */\n labels: LabelDefinition[];\n titleConvention: string | null;\n}\n\n/** Conventions of the repository around Pull Requests. */\nexport interface PolicyPullRequests {\n /** Content of the default Pull Request template, or null. */\n template: string | null;\n /** Every template found, for the multi-template directory layout. */\n templates: PullRequestTemplate[];\n baseBranch: string | null;\n titleConvention: string | null;\n}\n\n/** Conventions of the repository around git itself. */\nexport interface PolicyGit {\n branchConvention: string | null;\n commitConvention: string | null;\n}\n\n/** The resolved policy of a repository, at a given scope. */\nexport interface RepositoryPolicy {\n schemaVersion: typeof POLICY_SCHEMA_VERSION;\n /** Absolute path of the repository root the policy was resolved against. */\n root: string;\n /** Repository-relative subdirectory the policy was resolved for, or null. */\n scope: string | null;\n /** False when `policy.enabled` is off — every section is then empty. */\n enabled: boolean;\n issues: PolicyIssues;\n pullRequests: PolicyPullRequests;\n git: PolicyGit;\n docs: PolicyDocument[];\n /** Content of the CODEOWNERS file, or null. */\n codeowners: string | null;\n sources: PolicySource[];\n}\n\n/** Result of an external command, mirroring `utils/shell.ts`'s ExecResult. */\nexport interface PolicyExecResult {\n stdout: string;\n stderr: string;\n exitCode: number;\n}\n\n/**\n * Seam for every external command the discovery shells out to (`gh`, `git`).\n * Injectable so the suite can exercise both the answer and the absence without\n * a real binary.\n */\nexport type PolicyExec = (\n command: string,\n args: string[],\n options: { cwd: string; timeout: number },\n) => Promise<PolicyExecResult>;\n\n/**\n * Upper bound on the bytes read from any single policy document or template.\n *\n * Discovery feeds an agent's context window: an unbounded `AGENTS.md` would\n * cost more than it explains. Truncation is recorded in `sources`.\n */\nexport const MAX_POLICY_DOCUMENT_BYTES = 64 * 1024;\n"],"mappings":"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","names":["sep","sep"]}
|