issue-flow 0.11.0 → 0.12.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.
Files changed (118) hide show
  1. package/README.md +1 -1
  2. package/dist/{analyze-7B5TORNK.js → analyze-OEET7KGE.js} +17 -11
  3. package/dist/analyze-OEET7KGE.js.map +1 -0
  4. package/dist/{apply-WUJHDD7A.js → apply-ZIA3C52S.js} +6 -4
  5. package/dist/{apply-WUJHDD7A.js.map → apply-ZIA3C52S.js.map} +1 -1
  6. package/dist/{chunk-6N7LZZ5U.js → chunk-3QHK4YQA.js} +204 -107
  7. package/dist/chunk-3QHK4YQA.js.map +1 -0
  8. package/dist/chunk-42PJFMAR.js +605 -0
  9. package/dist/chunk-42PJFMAR.js.map +1 -0
  10. package/dist/{chunk-PL6CNMVS.js → chunk-4JSOYE4G.js} +105 -19
  11. package/dist/chunk-4JSOYE4G.js.map +1 -0
  12. package/dist/chunk-56KS3TFQ.js +703 -0
  13. package/dist/chunk-56KS3TFQ.js.map +1 -0
  14. package/dist/{chunk-AAY6KM7N.js → chunk-73ATPIYX.js} +160 -154
  15. package/dist/chunk-73ATPIYX.js.map +1 -0
  16. package/dist/{chunk-MKD2D7WE.js → chunk-A4SLHOW7.js} +70 -13
  17. package/dist/chunk-A4SLHOW7.js.map +1 -0
  18. package/dist/chunk-BAUPQXPE.js +173 -0
  19. package/dist/chunk-BAUPQXPE.js.map +1 -0
  20. package/dist/{chunk-HC2MT7BX.js → chunk-BYZDPR7N.js} +14 -581
  21. package/dist/chunk-BYZDPR7N.js.map +1 -0
  22. package/dist/chunk-CD5ULRVC.js +615 -0
  23. package/dist/chunk-CD5ULRVC.js.map +1 -0
  24. package/dist/chunk-DIDUVL6O.js +1365 -0
  25. package/dist/chunk-DIDUVL6O.js.map +1 -0
  26. package/dist/{chunk-47VIN33V.js → chunk-EZATO3DW.js} +7 -5
  27. package/dist/{chunk-47VIN33V.js.map → chunk-EZATO3DW.js.map} +1 -1
  28. package/dist/{chunk-JW7WLSZ6.js → chunk-FADTCQGM.js} +48 -12
  29. package/dist/chunk-FADTCQGM.js.map +1 -0
  30. package/dist/{chunk-GANSDJV7.js → chunk-FJP3UQGD.js} +15 -13
  31. package/dist/chunk-FJP3UQGD.js.map +1 -0
  32. package/dist/chunk-KMMASOA6.js +134 -0
  33. package/dist/chunk-KMMASOA6.js.map +1 -0
  34. package/dist/{chunk-HQYXVIH6.js → chunk-LAFNLLQP.js} +3 -3
  35. package/dist/chunk-LWRSMTTZ.js +18 -0
  36. package/dist/chunk-LWRSMTTZ.js.map +1 -0
  37. package/dist/{chunk-MSPUB635.js → chunk-M5UCXTIT.js} +15 -12
  38. package/dist/chunk-M5UCXTIT.js.map +1 -0
  39. package/dist/chunk-M66SJHQQ.js +206 -0
  40. package/dist/chunk-M66SJHQQ.js.map +1 -0
  41. package/dist/{chunk-OBLVLIUS.js → chunk-MJULIKQU.js} +42 -6
  42. package/dist/chunk-MJULIKQU.js.map +1 -0
  43. package/dist/{chunk-RKTEV6EG.js → chunk-NS6R37II.js} +4 -4
  44. package/dist/{run-H75MD5BQ.js → chunk-NTMMF5UD.js} +515 -544
  45. package/dist/chunk-NTMMF5UD.js.map +1 -0
  46. package/dist/{chunk-UBIQXCVE.js → chunk-QCPGHBSZ.js} +16 -13
  47. package/dist/chunk-QCPGHBSZ.js.map +1 -0
  48. package/dist/{chunk-LVKQ2AKT.js → chunk-TSOLWK7Y.js} +8 -6
  49. package/dist/chunk-TSOLWK7Y.js.map +1 -0
  50. package/dist/{chunk-VCKX262Z.js → chunk-XQUSIEKA.js} +13 -10
  51. package/dist/chunk-XQUSIEKA.js.map +1 -0
  52. package/dist/chunk-Y5ZDJ4PJ.js +76 -0
  53. package/dist/chunk-Y5ZDJ4PJ.js.map +1 -0
  54. package/dist/cli.js +156 -26
  55. package/dist/cli.js.map +1 -1
  56. package/dist/{config-4THHOXNN.js → config-PRJGWW7J.js} +15 -3
  57. package/dist/execute-XBNVL753.js +18 -0
  58. package/dist/{generate-6RA2MFJB.js → generate-EGB4KV2B.js} +18 -11
  59. package/dist/generate-EGB4KV2B.js.map +1 -0
  60. package/dist/init-AJHCXJBP.js +10 -0
  61. package/dist/operations-BYT626KQ.js +365 -0
  62. package/dist/operations-BYT626KQ.js.map +1 -0
  63. package/dist/plan-QOZ3RIO6.js +21 -0
  64. package/dist/{policy-XQW5NUIE.js → policy-6S4GFC76.js} +6 -4
  65. package/dist/{policy-XQW5NUIE.js.map → policy-6S4GFC76.js.map} +1 -1
  66. package/dist/pr-EBBHD2ZX.js +26 -0
  67. package/dist/pr-review-VVMW5PO4.js +20 -0
  68. package/dist/prd-JQSZF7PR.js +21 -0
  69. package/dist/resume-PPCNQVVV.js +221 -0
  70. package/dist/resume-PPCNQVVV.js.map +1 -0
  71. package/dist/review-3F6MARWH.js +19 -0
  72. package/dist/run-RXE5GDQO.js +36 -0
  73. package/dist/run-RXE5GDQO.js.map +1 -0
  74. package/dist/state-manager-2EDVDBAR.js +34 -0
  75. package/dist/state-manager-2EDVDBAR.js.map +1 -0
  76. package/dist/{web-ANQQTTRD.js → web-EF5WIIGG.js} +13 -8
  77. package/dist/{web-ANQQTTRD.js.map → web-EF5WIIGG.js.map} +1 -1
  78. package/package.json +1 -1
  79. package/prompts/pr.md +11 -4
  80. package/web/public/app.js +12 -0
  81. package/dist/analyze-7B5TORNK.js.map +0 -1
  82. package/dist/chunk-3BOHTH4V.js +0 -58
  83. package/dist/chunk-3BOHTH4V.js.map +0 -1
  84. package/dist/chunk-6N7LZZ5U.js.map +0 -1
  85. package/dist/chunk-AAY6KM7N.js.map +0 -1
  86. package/dist/chunk-GANSDJV7.js.map +0 -1
  87. package/dist/chunk-HC2MT7BX.js.map +0 -1
  88. package/dist/chunk-HDV3YDTG.js +0 -52
  89. package/dist/chunk-HDV3YDTG.js.map +0 -1
  90. package/dist/chunk-JW7WLSZ6.js.map +0 -1
  91. package/dist/chunk-LVKQ2AKT.js.map +0 -1
  92. package/dist/chunk-MKD2D7WE.js.map +0 -1
  93. package/dist/chunk-MSPUB635.js.map +0 -1
  94. package/dist/chunk-OBLVLIUS.js.map +0 -1
  95. package/dist/chunk-PL6CNMVS.js.map +0 -1
  96. package/dist/chunk-UBIQXCVE.js.map +0 -1
  97. package/dist/chunk-VCKX262Z.js.map +0 -1
  98. package/dist/chunk-VQ5VN7DF.js +0 -1018
  99. package/dist/chunk-VQ5VN7DF.js.map +0 -1
  100. package/dist/execute-5OEKLM5X.js +0 -15
  101. package/dist/generate-6RA2MFJB.js.map +0 -1
  102. package/dist/init-PRMOTQ7N.js +0 -9
  103. package/dist/plan-KENR6MJ3.js +0 -17
  104. package/dist/pr-KDGKG4BP.js +0 -21
  105. package/dist/pr-review-CAMAB7KO.js +0 -17
  106. package/dist/prd-V3MWDIHK.js +0 -17
  107. package/dist/review-7LN47SAN.js +0 -15
  108. package/dist/run-H75MD5BQ.js.map +0 -1
  109. /package/dist/{chunk-HQYXVIH6.js.map → chunk-LAFNLLQP.js.map} +0 -0
  110. /package/dist/{chunk-RKTEV6EG.js.map → chunk-NS6R37II.js.map} +0 -0
  111. /package/dist/{config-4THHOXNN.js.map → config-PRJGWW7J.js.map} +0 -0
  112. /package/dist/{execute-5OEKLM5X.js.map → execute-XBNVL753.js.map} +0 -0
  113. /package/dist/{init-PRMOTQ7N.js.map → init-AJHCXJBP.js.map} +0 -0
  114. /package/dist/{plan-KENR6MJ3.js.map → plan-QOZ3RIO6.js.map} +0 -0
  115. /package/dist/{pr-KDGKG4BP.js.map → pr-EBBHD2ZX.js.map} +0 -0
  116. /package/dist/{pr-review-CAMAB7KO.js.map → pr-review-VVMW5PO4.js.map} +0 -0
  117. /package/dist/{prd-V3MWDIHK.js.map → prd-JQSZF7PR.js.map} +0 -0
  118. /package/dist/{review-7LN47SAN.js.map → review-3F6MARWH.js.map} +0 -0
@@ -1 +1 @@
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"]}
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"]}
@@ -1,36 +1,42 @@
1
1
  #!/usr/bin/env node
2
+ import {
3
+ listPullRequests
4
+ } from "./chunk-TSOLWK7Y.js";
2
5
  import {
3
6
  runPhaseWithRetry
4
- } from "./chunk-3BOHTH4V.js";
7
+ } from "./chunk-Y5ZDJ4PJ.js";
5
8
  import {
6
9
  issuePlaceholders,
7
10
  resolveCommandIssue
8
- } from "./chunk-AAY6KM7N.js";
11
+ } from "./chunk-73ATPIYX.js";
9
12
  import {
13
+ DEFAULT_HEADLESS_TIMEOUT_MS,
10
14
  runHeadless
11
- } from "./chunk-PL6CNMVS.js";
15
+ } from "./chunk-4JSOYE4G.js";
12
16
  import {
13
17
  isTransientFailure
14
- } from "./chunk-HDV3YDTG.js";
18
+ } from "./chunk-LWRSMTTZ.js";
15
19
  import {
16
20
  publishPhaseMetrics,
17
21
  resolvePolicyPlaceholders
18
- } from "./chunk-OBLVLIUS.js";
22
+ } from "./chunk-MJULIKQU.js";
19
23
  import {
20
24
  applyPlaceholders,
21
25
  loadPrompt
22
- } from "./chunk-HQYXVIH6.js";
26
+ } from "./chunk-LAFNLLQP.js";
23
27
  import {
24
28
  resolveIssuePaths
25
- } from "./chunk-VQ5VN7DF.js";
29
+ } from "./chunk-DIDUVL6O.js";
26
30
  import {
27
31
  getGlobalTimeout,
32
+ printError,
33
+ printSuccess
34
+ } from "./chunk-BYZDPR7N.js";
35
+ import {
28
36
  isoNow,
29
37
  loadTaskPlan,
30
- printError,
31
- printSuccess,
32
38
  saveTaskPlan
33
- } from "./chunk-HC2MT7BX.js";
39
+ } from "./chunk-CD5ULRVC.js";
34
40
 
35
41
  // src/commands/pr.ts
36
42
  import { execa } from "execa";
@@ -87,6 +93,34 @@ function multiIssueContext(queue) {
87
93
  "how you tell which change belongs to which issue in `git log`."
88
94
  ].join("\n");
89
95
  }
96
+ async function adoptExistingPullRequest(branchName, tasksPath, issueNumber) {
97
+ let open;
98
+ try {
99
+ open = await listPullRequests(branchName, { state: "open" });
100
+ } catch {
101
+ return false;
102
+ }
103
+ const existing = open[0];
104
+ if (existing === void 0) return false;
105
+ try {
106
+ const plan = await loadTaskPlan(tasksPath);
107
+ plan.pipeline.prCreated = true;
108
+ plan.pullRequest = {
109
+ number: existing.number,
110
+ url: existing.url,
111
+ headBranch: branchName,
112
+ // The Pull Request predates this attempt; what is recorded is when this
113
+ // run adopted it, which is the only timestamp this process can vouch for.
114
+ createdAt: isoNow()
115
+ };
116
+ await saveTaskPlan(tasksPath, plan);
117
+ } catch {
118
+ }
119
+ printSuccess(
120
+ `Pull Request already open for ${branchName}: ${existing.url}. Adopting it instead of opening a second one for issue #${issueNumber}.`
121
+ );
122
+ return true;
123
+ }
90
124
  async function runPr(issue, resolvedIssue, options = {}) {
91
125
  const issueNumber = issue.replace(/^#/, "");
92
126
  const paths = await resolveIssuePaths(issueNumber);
@@ -107,6 +141,8 @@ async function runPr(issue, resolvedIssue, options = {}) {
107
141
  printError("Failed to get current branch");
108
142
  return 1;
109
143
  }
144
+ const adopted = await adoptExistingPullRequest(branchName, tasksPath, issueNumber);
145
+ if (adopted) return 0;
110
146
  const queue = options.queue;
111
147
  const consolidating = queue !== void 0 && queue.issues.length > 1;
112
148
  const template = await loadPrompt("pr");
@@ -129,7 +165,7 @@ async function runPr(issue, resolvedIssue, options = {}) {
129
165
  const result = await runHeadless({
130
166
  prompt,
131
167
  maxTurns: 15,
132
- timeout: getGlobalTimeout() ?? 3e5,
168
+ timeout: getGlobalTimeout() ?? DEFAULT_HEADLESS_TIMEOUT_MS,
133
169
  // json (not text) so the CLI reports usage: the envelope's `result`
134
170
  // field carries the same assistant text parsePrUrl() already consumed.
135
171
  outputFormat: "json",
@@ -182,4 +218,4 @@ export {
182
218
  multiIssueContext,
183
219
  runPr
184
220
  };
185
- //# sourceMappingURL=chunk-JW7WLSZ6.js.map
221
+ //# sourceMappingURL=chunk-FADTCQGM.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/commands/pr.ts"],"sourcesContent":["import { execa } from 'execa';\nimport { DEFAULT_HEADLESS_TIMEOUT_MS, runHeadless } from '../core/headless.js';\nimport { runPhaseWithRetry } from '../core/phase-runner.js';\nimport { applyPlaceholders, loadPrompt } from '../core/prompt-resolver.js';\nimport { listPullRequests } from '../core/session-git.js';\nimport { publishPhaseMetrics } from '../core/session-metrics.js';\nimport { isoNow, loadTaskPlan, saveTaskPlan } from '../core/state-manager.js';\nimport { getGlobalTimeout } from '../core/verbose.js';\nimport { issuePlaceholders, resolveCommandIssue } from '../issues/context.js';\nimport type { Issue, IssueSource, ResolvedIssue } from '../issues/types.js';\nimport { resolvePolicyPlaceholders } from '../policy/placeholders.js';\nimport { resolveIssuePaths } from '../storage/resolve.js';\nimport { printError, printSuccess } from '../ui/logger.js';\nimport { isTransientFailure } from '../utils/retry.js';\n\n/**\n * Extract a PR URL from headless output.\n */\nfunction parsePrUrl(output: string): string | null {\n const match = output.match(/(https:\\/\\/github\\.com\\/[^\\s]+\\/pull\\/\\d+)/);\n return match?.[1] ?? null;\n}\n\n/**\n * The numeric id inside a PR URL, so later phases (`pr-review`) address the\n * Pull Request without querying GitHub again.\n */\nfunction parsePrNumber(url: string): number | null {\n const match = url.match(/\\/pull\\/(\\d+)/);\n return match?.[1] === undefined ? null : Number(match[1]);\n}\n\n/**\n * The `Closes #N` line for the PR body, empty when the Issue has no remote\n * counterpart: GitHub only understands the reference for Issues it hosts, and\n * an invented `#N` would silently point at an unrelated Issue.\n */\nfunction issueClosesLine(issue: Issue, fallbackId: string): string {\n if (issue.remoteRef === null) {\n return '';\n }\n return `Closes #${issue.number ?? fallbackId}`;\n}\n\n/** One Issue of a queue, as the consolidated Pull Request has to describe it. */\nexport interface PrQueueIssue {\n id: string;\n number: number | null;\n title: string;\n /** `null` when the Issue was never read — see {@link issueClosesLines}. */\n url: string | null;\n /** Origin the Issue came from; `github` is what GitHub can close. */\n source: IssueSource;\n}\n\n/**\n * What a multi-issue queue hands to the `pr` phase.\n *\n * Absent for every standalone run, and that absence is what guarantees the\n * single-issue Pull Request is byte-for-byte the one this command has always\n * produced: the extra placeholders resolve to empty strings.\n */\nexport interface PrQueueContext {\n /** Issues of the queue, in the order they were executed. */\n issues: PrQueueIssue[];\n /** Issues discovered but not executed, with the reason. */\n excluded: { id: string; number: number | null; title: string; reason: string }[];\n /** Anything else worth reporting as pending (unresolved review findings). */\n pending: string[];\n}\n\nexport interface RunPrOptions {\n /** Set only when the Pull Request consolidates a queue. */\n queue?: PrQueueContext;\n}\n\n/** `#51` when the Issue has a number, its raw identifier otherwise. */\nfunction issueRef(entry: { id: string; number: number | null }): string {\n return entry.number === null ? entry.id : `#${entry.number}`;\n}\n\n/**\n * Every `Closes #N` line of a consolidated Pull Request, one per line.\n *\n * Issues that GitHub does not host are skipped for the same reason a\n * single-issue Pull Request skips them — it cannot close what it does not\n * host — and a queue that mixes both origins still gets a valid body.\n *\n * The test is the origin and the number, not the URL: an Issue whose read\n * failed during discovery reaches the queue with `url: null`, and it is still\n * executed and still closed by the queue. Filtering on the URL would leave it\n * out of the very body that is supposed to reference it.\n */\nexport function issueClosesLines(issues: readonly PrQueueIssue[]): string {\n return issues\n .filter((entry) => entry.source === 'github' && entry.number !== null)\n .map((entry) => `Closes #${entry.number}`)\n .join('\\n');\n}\n\n/**\n * The extra instructions the prompt receives when the Pull Request covers a\n * whole queue.\n *\n * Returns `''` for a standalone run, which is what keeps `pr.md` rendering\n * exactly as before for the single-issue path — no empty \"Issues implemented\"\n * section, no redundant ordering of a list of one.\n */\nexport function multiIssueContext(queue: PrQueueContext | undefined): string {\n if (queue === undefined || queue.issues.length <= 1) {\n return '';\n }\n\n const order = queue.issues\n .map(\n (entry, index) => `${index + 1}. ${issueRef(entry)}${entry.title ? ` — ${entry.title}` : ''}`,\n )\n .join('\\n');\n\n const pending = [\n ...queue.excluded.map(\n (entry) => `- ${issueRef(entry)}${entry.title ? ` — ${entry.title}` : ''}: ${entry.reason}`,\n ),\n ...queue.pending.map((note) => `- ${note}`),\n ].join('\\n');\n\n return [\n '',\n 'This Pull Request consolidates several issues implemented on this same branch,',\n 'in this execution order:',\n '',\n order,\n '',\n 'The PR body MUST additionally contain:',\n '- an \"Issues implemented\" section listing every issue above, in that order,',\n ' with one line per issue describing what it delivered;',\n '- a \"Pending\" section with the items below, verbatim, plus anything you find',\n ' unfinished while reviewing the diff. Write \"None\" when the list below is',\n ' empty and you find nothing else;',\n '',\n pending === '' ? '(no known pending items)' : pending,\n '',\n 'The commits of this branch are scoped per issue (`feat(issue-N): …`), which is',\n 'how you tell which change belongs to which issue in `git log`.',\n ].join('\\n');\n}\n\n/**\n * Adopt the Pull Request that is already open for this branch, if there is one.\n *\n * \"Already open\" is the only safe reading: a *closed* Pull Request for the same\n * branch is a decision someone made, and reopening or reusing it would undo it.\n * A merged one means the work is done and a new branch is the answer.\n *\n * Returns `true` when the phase has nothing left to do. Never throws — a `gh`\n * that cannot answer leaves the phase to run exactly as it did before.\n */\nasync function adoptExistingPullRequest(\n branchName: string,\n tasksPath: string,\n issueNumber: string,\n): Promise<boolean> {\n let open: Awaited<ReturnType<typeof listPullRequests>>;\n try {\n open = await listPullRequests(branchName, { state: 'open' });\n } catch {\n return false;\n }\n\n const existing = open[0];\n if (existing === undefined) return false;\n\n try {\n const plan = await loadTaskPlan(tasksPath);\n plan.pipeline.prCreated = true;\n plan.pullRequest = {\n number: existing.number,\n url: existing.url,\n headBranch: branchName,\n // The Pull Request predates this attempt; what is recorded is when this\n // run adopted it, which is the only timestamp this process can vouch for.\n createdAt: isoNow(),\n };\n await saveTaskPlan(tasksPath, plan);\n } catch {\n // No plan yet: the adoption still stands, there is just nowhere to note it.\n }\n\n printSuccess(\n `Pull Request already open for ${branchName}: ${existing.url}. Adopting it instead of opening a second one for issue #${issueNumber}.`,\n );\n return true;\n}\n\nexport async function runPr(\n issue: string,\n resolvedIssue?: ResolvedIssue,\n options: RunPrOptions = {},\n): Promise<number> {\n const issueNumber = issue.replace(/^#/, '');\n const paths = await resolveIssuePaths(issueNumber);\n const tasksPath = paths.tasksFile;\n\n const resolution = await resolveCommandIssue(issueNumber, resolvedIssue);\n if (!resolution.ok) {\n return resolution.code;\n }\n\n // Get current branch\n let branchName: string;\n try {\n const proc = await execa('git', ['branch', '--show-current'], { reject: false });\n branchName = proc.stdout?.toString().trim() ?? '';\n if (!branchName) {\n printError('Could not determine current branch');\n return 1;\n }\n } catch {\n printError('Failed to get current branch');\n return 1;\n }\n\n // Idempotence, before anything is invoked.\n //\n // `runPhaseWithRetry` retries this phase up to three times, and the timeout\n // that used to end a run is now correctly classified as transient — so an\n // attempt that created the Pull Request and *then* timed out would have the\n // next attempt create a second one. Asking GitHub what already exists is the\n // cheap half of the fix; the expensive half would be undoing a duplicate.\n const adopted = await adoptExistingPullRequest(branchName, tasksPath, issueNumber);\n if (adopted) return 0;\n\n const queue = options.queue;\n const consolidating = queue !== undefined && queue.issues.length > 1;\n\n const template = await loadPrompt('pr');\n const prompt = applyPlaceholders(template, {\n // The repository's own conventions. Empty when it declares none, which is\n // what keeps the rendered prompt identical to the pre-policy one.\n ...(await resolvePolicyPlaceholders()),\n __ISSUE_NUMBER__: issueNumber,\n __BRANCH_NAME__: branchName,\n __TASKS_PATH__: tasksPath,\n __ISSUE_CLOSES__: consolidating\n ? issueClosesLines(queue.issues)\n : issueClosesLine(resolution.resolved.issue, issueNumber),\n __MULTI_ISSUE_CONTEXT__: multiIssueContext(queue),\n ...issuePlaceholders(resolution.resolved),\n });\n\n let headlessOutput = '';\n\n const outcome = await runPhaseWithRetry({\n phase: 'pr',\n attempt: async () => {\n const startedAtMs = Date.now();\n const result = await runHeadless({\n prompt,\n maxTurns: 15,\n timeout: getGlobalTimeout() ?? DEFAULT_HEADLESS_TIMEOUT_MS,\n // json (not text) so the CLI reports usage: the envelope's `result`\n // field carries the same assistant text parsePrUrl() already consumed.\n outputFormat: 'json',\n allowedTools: ['Bash', 'Read', 'Glob', 'Grep'],\n addDirs: [paths.issueDir],\n statusMessage: `Creating PR for issue #${issueNumber}...`,\n });\n // One event per attempt; the reducer sums them into the phase total.\n publishPhaseMetrics('pr', result.cost, startedAtMs);\n\n if (!result.success) {\n return {\n ok: false,\n transient: isTransientFailure(1, result.error ?? ''),\n error: `PR creation failed: ${result.error}`,\n };\n }\n\n headlessOutput = result.result;\n return { ok: true };\n },\n });\n\n if (!outcome.ok) {\n printError(outcome.error ?? `PR creation failed for issue #${issueNumber}`);\n return 1;\n }\n\n const prUrl = parsePrUrl(headlessOutput);\n\n // Update pipeline state\n try {\n const plan = await loadTaskPlan(tasksPath);\n plan.pipeline.prCreated = true;\n const prNumber = prUrl === null ? null : parsePrNumber(prUrl);\n // Without a URL the PR is still assumed created (the phase succeeded), but\n // there is nothing trustworthy to record: an invented number would send\n // `pr-review` at an unrelated Pull Request.\n if (prUrl !== null && prNumber !== null) {\n plan.pullRequest = {\n number: prNumber,\n url: prUrl,\n headBranch: branchName,\n createdAt: isoNow(),\n };\n }\n await saveTaskPlan(tasksPath, plan);\n } catch {\n // tasks.json may not exist\n }\n\n if (prUrl) {\n printSuccess(`PR created: ${prUrl}`);\n } else {\n printSuccess('PR creation completed');\n }\n return 0;\n}\n"],"mappings":"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","names":[]}