issue-flow 0.10.0 → 0.11.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 (101) hide show
  1. package/dist/{analyze-UZYJAWAP.js → analyze-7B5TORNK.js} +13 -8
  2. package/dist/analyze-7B5TORNK.js.map +1 -0
  3. package/dist/apply-WUJHDD7A.js +788 -0
  4. package/dist/apply-WUJHDD7A.js.map +1 -0
  5. package/dist/{chunk-UUY4PCWW.js → chunk-3BOHTH4V.js} +2 -2
  6. package/dist/chunk-3D4A4B46.js +2 -0
  7. package/dist/{policy-NP4ZAVF6.js → chunk-47VIN33V.js} +82 -112
  8. package/dist/chunk-47VIN33V.js.map +1 -0
  9. package/dist/{chunk-67ZO5ROQ.js → chunk-6N7LZZ5U.js} +19 -13
  10. package/dist/chunk-6N7LZZ5U.js.map +1 -0
  11. package/dist/{chunk-S67Q4I2O.js → chunk-AAY6KM7N.js} +6 -3
  12. package/dist/chunk-AAY6KM7N.js.map +1 -0
  13. package/dist/{chunk-652DJBCN.js → chunk-GANSDJV7.js} +4 -4
  14. package/dist/{chunk-V5OHFSW4.js → chunk-HC2MT7BX.js} +23 -3
  15. package/dist/chunk-HC2MT7BX.js.map +1 -0
  16. package/dist/chunk-HQYXVIH6.js +110 -0
  17. package/dist/chunk-HQYXVIH6.js.map +1 -0
  18. package/dist/{chunk-2LNQL4XH.js → chunk-JW7WLSZ6.js} +15 -11
  19. package/dist/chunk-JW7WLSZ6.js.map +1 -0
  20. package/dist/{chunk-VOKNTBOO.js → chunk-LVKQ2AKT.js} +3 -3
  21. package/dist/{chunk-EWA3OGCM.js → chunk-MKD2D7WE.js} +12 -8
  22. package/dist/chunk-MKD2D7WE.js.map +1 -0
  23. package/dist/{chunk-UH4KAJBJ.js → chunk-MSPUB635.js} +15 -11
  24. package/dist/chunk-MSPUB635.js.map +1 -0
  25. package/dist/chunk-OBLVLIUS.js +285 -0
  26. package/dist/chunk-OBLVLIUS.js.map +1 -0
  27. package/dist/{chunk-MXTGCD5C.js → chunk-PL6CNMVS.js} +2 -2
  28. package/dist/chunk-RKTEV6EG.js +246 -0
  29. package/dist/chunk-RKTEV6EG.js.map +1 -0
  30. package/dist/{chunk-SP7DD4ID.js → chunk-UBIQXCVE.js} +15 -11
  31. package/dist/chunk-UBIQXCVE.js.map +1 -0
  32. package/dist/{chunk-2Z7OEZZT.js → chunk-VCKX262Z.js} +12 -8
  33. package/dist/chunk-VCKX262Z.js.map +1 -0
  34. package/dist/{chunk-VYFKOUUR.js → chunk-VQ5VN7DF.js} +9 -5
  35. package/dist/chunk-VQ5VN7DF.js.map +1 -0
  36. package/dist/cli.js +26 -22
  37. package/dist/cli.js.map +1 -1
  38. package/dist/{config-YCF3HIKR.js → config-4THHOXNN.js} +3 -3
  39. package/dist/execute-5OEKLM5X.js +15 -0
  40. package/dist/{generate-TW6QKSW5.js → generate-6RA2MFJB.js} +99 -12
  41. package/dist/generate-6RA2MFJB.js.map +1 -0
  42. package/dist/init-PRMOTQ7N.js +9 -0
  43. package/dist/plan-KENR6MJ3.js +17 -0
  44. package/dist/policy-XQW5NUIE.js +117 -0
  45. package/dist/policy-XQW5NUIE.js.map +1 -0
  46. package/dist/pr-KDGKG4BP.js +21 -0
  47. package/dist/pr-review-CAMAB7KO.js +17 -0
  48. package/dist/prd-V3MWDIHK.js +17 -0
  49. package/dist/review-7LN47SAN.js +15 -0
  50. package/dist/review-7LN47SAN.js.map +1 -0
  51. package/dist/{run-DIBC3YKX.js → run-H75MD5BQ.js} +26 -25
  52. package/dist/{run-DIBC3YKX.js.map → run-H75MD5BQ.js.map} +1 -1
  53. package/dist/{web-O2FVOYZW.js → web-ANQQTTRD.js} +5 -5
  54. package/package.json +1 -1
  55. package/prompts/analyze.md +24 -0
  56. package/prompts/execute.md +29 -1
  57. package/prompts/generate.md +61 -7
  58. package/prompts/plan.md +24 -1
  59. package/prompts/pr-review.md +65 -4
  60. package/prompts/pr.md +37 -3
  61. package/prompts/prd.md +17 -0
  62. package/prompts/review.md +40 -0
  63. package/dist/analyze-UZYJAWAP.js.map +0 -1
  64. package/dist/chunk-2LNQL4XH.js.map +0 -1
  65. package/dist/chunk-2Z7OEZZT.js.map +0 -1
  66. package/dist/chunk-67ZO5ROQ.js.map +0 -1
  67. package/dist/chunk-EWA3OGCM.js.map +0 -1
  68. package/dist/chunk-EZMOEBLU.js +0 -135
  69. package/dist/chunk-EZMOEBLU.js.map +0 -1
  70. package/dist/chunk-PPLIXZI5.js +0 -101
  71. package/dist/chunk-PPLIXZI5.js.map +0 -1
  72. package/dist/chunk-S67Q4I2O.js.map +0 -1
  73. package/dist/chunk-SLHZPB3C.js +0 -50
  74. package/dist/chunk-SLHZPB3C.js.map +0 -1
  75. package/dist/chunk-SP7DD4ID.js.map +0 -1
  76. package/dist/chunk-UH4KAJBJ.js.map +0 -1
  77. package/dist/chunk-V5OHFSW4.js.map +0 -1
  78. package/dist/chunk-VYFKOUUR.js.map +0 -1
  79. package/dist/execute-QYXA225W.js +0 -14
  80. package/dist/generate-TW6QKSW5.js.map +0 -1
  81. package/dist/init-G57UXKDG.js +0 -9
  82. package/dist/plan-YJLGBYBQ.js +0 -16
  83. package/dist/policy-NP4ZAVF6.js.map +0 -1
  84. package/dist/pr-V5H65MRW.js +0 -20
  85. package/dist/pr-review-RCMXKXVP.js +0 -16
  86. package/dist/prd-3XR5KYJS.js +0 -16
  87. package/dist/review-ZZG3CSUO.js +0 -14
  88. /package/dist/{chunk-UUY4PCWW.js.map → chunk-3BOHTH4V.js.map} +0 -0
  89. /package/dist/{config-YCF3HIKR.js.map → chunk-3D4A4B46.js.map} +0 -0
  90. /package/dist/{chunk-652DJBCN.js.map → chunk-GANSDJV7.js.map} +0 -0
  91. /package/dist/{chunk-VOKNTBOO.js.map → chunk-LVKQ2AKT.js.map} +0 -0
  92. /package/dist/{chunk-MXTGCD5C.js.map → chunk-PL6CNMVS.js.map} +0 -0
  93. /package/dist/{execute-QYXA225W.js.map → config-4THHOXNN.js.map} +0 -0
  94. /package/dist/{init-G57UXKDG.js.map → execute-5OEKLM5X.js.map} +0 -0
  95. /package/dist/{plan-YJLGBYBQ.js.map → init-PRMOTQ7N.js.map} +0 -0
  96. /package/dist/{pr-V5H65MRW.js.map → plan-KENR6MJ3.js.map} +0 -0
  97. /package/dist/{pr-review-RCMXKXVP.js.map → pr-KDGKG4BP.js.map} +0 -0
  98. /package/dist/{prd-3XR5KYJS.js.map → pr-review-CAMAB7KO.js.map} +0 -0
  99. /package/dist/{review-ZZG3CSUO.js.map → prd-V3MWDIHK.js.map} +0 -0
  100. /package/dist/{web-O2FVOYZW.js.map → web-ANQQTTRD.js.map} +0 -0
  101. /package/web/{CLAUDE.md → AGENTS.md} +0 -0
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/scaffold/apply.ts","../src/scaffold/plan.ts","../src/conventions/defaults.ts","../src/scaffold/assets.ts"],"sourcesContent":["import { access, mkdir, writeFile } from 'node:fs/promises';\nimport { basename, dirname, join } from 'node:path';\nimport { loadRepositoryPolicy } from '../policy/index.js';\nimport { getProjectRoot } from '../utils/git.js';\nimport { buildScaffoldPlan, type RepositoryState, type ScaffoldPlan } from './plan.js';\n\n/**\n * Turning a plan into files — and the one place that writes anything.\n *\n * Writing is deliberately dumber than planning: it creates what the plan marked\n * `create` and touches nothing else. Every judgement about whether a file should\n * exist was already made, from the resolved policy, in `plan.ts`.\n */\n\nexport interface ApplyResult {\n written: string[];\n /** Marked `create` but found on disk between planning and writing. */\n skipped: string[];\n}\n\nasync function exists(path: string): Promise<boolean> {\n try {\n await access(path);\n return true;\n } catch {\n return false;\n }\n}\n\n/**\n * Write the `create` actions of a plan.\n *\n * Re-checks existence immediately before each write. The plan is built from the\n * resolved policy, which is cached for the process, so a file created between\n * the two would otherwise be overwritten — and \"initialization is\n * non-destructive\" has to hold even against itself.\n */\nexport async function applyScaffoldPlan(plan: ScaffoldPlan): Promise<ApplyResult> {\n const written: string[] = [];\n const skipped: string[] = [];\n\n for (const item of plan.actions) {\n if (item.kind !== 'create' || item.content === undefined) continue;\n\n const absolute = join(plan.root, item.path);\n if (await exists(absolute)) {\n skipped.push(item.path);\n continue;\n }\n\n await mkdir(dirname(absolute), { recursive: true });\n await writeFile(absolute, item.content, 'utf-8');\n written.push(item.path);\n }\n\n return { written, skipped };\n}\n\nexport interface ResolveStateOptions {\n /** Repository root. Defaults to the git project root. */\n root?: string;\n /** Subdirectory the conventions apply to, for monorepos. */\n scope?: string | null;\n}\n\n/**\n * The repository state the planner reads, assembled from the policy layer.\n *\n * There is no second discovery here on purpose: initialization sees exactly the\n * repository every other flow sees, so it can never propose a convention that\n * discovery already found.\n */\nexport async function resolveRepositoryState(\n options: ResolveStateOptions = {},\n): Promise<RepositoryState> {\n const root = options.root ?? (await getProjectRoot());\n const policy = await loadRepositoryPolicy({ root, scope: options.scope ?? null });\n\n return {\n policy,\n projectName: basename(root),\n exists: (relPath: string) => exists(join(root, relPath)),\n };\n}\n\n/** Resolve the state and build the plan, without writing anything. */\nexport async function planRepositoryScaffold(\n options: ResolveStateOptions = {},\n): Promise<ScaffoldPlan> {\n return buildScaffoldPlan(await resolveRepositoryState(options));\n}\n","import { readFile } from 'node:fs/promises';\nimport { join } from 'node:path';\nimport { DEFAULT_ISSUE_TYPES } from '../conventions/defaults.js';\nimport type { RepositoryPolicy } from '../policy/types.js';\nimport {\n renderAgentsMd,\n renderClaudeMd,\n renderConventionsDoc,\n renderIssueForm,\n renderIssueTemplateConfig,\n renderLabelsFile,\n renderPullRequestTemplate,\n type ScaffoldAsset,\n} from './assets.js';\n\n/**\n * Deciding what a repository is missing — the shared core behind both the CLI\n * and the initialization skill.\n *\n * The plan is produced from the **resolved policy**, never from a fresh scan:\n * initialization and every other flow therefore see the same repository, and a\n * convention that discovery already found can never be proposed a second time.\n *\n * Two properties matter more than the file list.\n *\n * **Non-destructive.** Nothing that exists is ever overwritten. A repository\n * that already declares a convention keeps it, even when it differs from the\n * default — the whole point of the tool is to adapt to the repository, and\n * \"initialize\" must not become a euphemism for \"replace\".\n *\n * **Idempotent.** Running it twice changes nothing the second time, because the\n * plan is computed from what is present rather than from what a previous run\n * did.\n */\n\n/** What initialization intends to do with one file. */\nexport type ScaffoldActionKind =\n /** The file does not exist and the repository has no equivalent. */\n | 'create'\n /** Something equivalent already exists; initialization stays out of the way. */\n | 'keep'\n /**\n * Present, but inconsistent with the convention it claims to follow. Reported,\n * never rewritten: the repository may be right and the tool wrong.\n */\n | 'review';\n\nexport interface ScaffoldAction {\n path: string;\n kind: ScaffoldActionKind;\n tier: ScaffoldAsset['tier'];\n /** Why this action was chosen, in one sentence, for the report. */\n reason: string;\n /** Content to write. Only present for `create`. */\n content?: string;\n}\n\nexport interface ScaffoldPlan {\n root: string;\n /** Every action, in a stable order. */\n actions: ScaffoldAction[];\n /** Notes that are not tied to a single file. */\n notes: string[];\n}\n\n/** Everything the plan needs to know about the repository's current state. */\nexport interface RepositoryState {\n policy: RepositoryPolicy;\n /** Whether a repository-relative path exists. */\n exists(relPath: string): Promise<boolean>;\n /** Name to head `AGENTS.md` with. */\n projectName: string;\n}\n\nfunction action(\n path: string,\n kind: ScaffoldActionKind,\n tier: ScaffoldAsset['tier'],\n reason: string,\n content?: string,\n): ScaffoldAction {\n return content === undefined\n ? { path, kind, tier, reason }\n : { path, kind, tier, reason, content };\n}\n\n/**\n * Whether a `CLAUDE.md` is already the one-line bridge the convention asks for.\n *\n * A file that merely *mentions* `AGENTS.md` in the middle of its own\n * instructions is not a bridge — it is a second source that happens to cite the\n * first. The test is therefore about size and shape, not about a substring.\n */\nexport function isClaudeBridge(content: string): boolean {\n const meaningful = content\n .split('\\n')\n .map((line) => line.trim())\n .filter((line) => line !== '' && !line.startsWith('#'));\n\n if (meaningful.length === 0 || meaningful.length > 2) return false;\n return meaningful.every((line) => /agents\\.md/i.test(line));\n}\n\n/**\n * Build the plan.\n *\n * `write` is what the caller intends to do with it; the plan itself never\n * touches the filesystem, which is what lets the CLI show it and the skill\n * reason about it before anything happens.\n */\nexport async function buildScaffoldPlan(state: RepositoryState): Promise<ScaffoldPlan> {\n const { policy } = state;\n const actions: ScaffoldAction[] = [];\n const notes: string[] = [];\n\n // ── Issue Templates ───────────────────────────────────────────────────────\n const localTemplates = policy.issues.templates.filter((t) => t.origin === 'filesystem');\n const orgTemplates = policy.issues.templates.filter((t) => t.origin === 'organization');\n\n if (localTemplates.length > 0) {\n actions.push(\n action(\n '.github/ISSUE_TEMPLATE/',\n 'keep',\n 'required',\n `${localTemplates.length} Issue Template(s) already declared in this repository.`,\n ),\n );\n } else if (orgTemplates.length > 0) {\n // The organization already answered this, and a local copy would fork it:\n // the org file is then edited once and the repository silently stops\n // following it.\n actions.push(\n action(\n '.github/ISSUE_TEMPLATE/',\n 'keep',\n 'required',\n `The organization serves ${orgTemplates.length} Issue Template(s) to this repository; a local copy would fork them.`,\n ),\n );\n notes.push(\n 'Issue Templates come from the organization. Change them there, not here — a local copy stops tracking the original the moment either is edited.',\n );\n } else {\n for (const type of DEFAULT_ISSUE_TYPES) {\n const path = `.github/ISSUE_TEMPLATE/${type.order}-${type.slug}.yml`;\n actions.push(\n action(\n path,\n 'create',\n 'required',\n `Issue Form for the \"${type.name}\" type.`,\n renderIssueForm(type),\n ),\n );\n }\n const configPath = '.github/ISSUE_TEMPLATE/config.yml';\n actions.push(\n action(\n configPath,\n (await state.exists(configPath)) ? 'keep' : 'create',\n 'required',\n 'Template chooser: keeps blank issues enabled and links the conventions.',\n renderIssueTemplateConfig(),\n ),\n );\n }\n\n // ── Pull Request template ────────────────────────────────────────────────\n if (policy.pullRequests.templates.length > 0) {\n actions.push(\n action(\n policy.pullRequests.templates[0]?.path ?? '.github/PULL_REQUEST_TEMPLATE.md',\n 'keep',\n 'required',\n 'A Pull Request template already exists.',\n ),\n );\n } else {\n actions.push(\n action(\n '.github/PULL_REQUEST_TEMPLATE.md',\n 'create',\n 'required',\n 'Gives every Pull Request a body that review can rely on.',\n renderPullRequestTemplate(),\n ),\n );\n }\n\n // ── Agent entry points ───────────────────────────────────────────────────\n const agents = policy.docs.find((doc) => doc.kind === 'agents' && doc.scope === '');\n const claude = policy.docs.find((doc) => doc.kind === 'claude' && doc.scope === '');\n\n const referenced = policy.docs\n .filter((doc) => doc.kind === 'contributing' || doc.kind === 'referenced')\n .map((doc) => doc.path);\n\n if (agents === undefined) {\n actions.push(\n action(\n 'AGENTS.md',\n 'create',\n 'required',\n 'The canonical entry point for any coding agent, as an index of the real documentation.',\n renderAgentsMd(state.projectName, referenced),\n ),\n );\n } else {\n actions.push(action('AGENTS.md', 'keep', 'required', 'Already the canonical entry point.'));\n }\n\n if (claude === undefined) {\n actions.push(\n action(\n 'CLAUDE.md',\n 'create',\n 'recommended',\n 'One-line bridge to AGENTS.md, so Claude Code reads the canonical source.',\n renderClaudeMd(),\n ),\n );\n } else if (isClaudeBridge(claude.content)) {\n actions.push(action('CLAUDE.md', 'keep', 'recommended', 'Already a bridge to AGENTS.md.'));\n } else if (agents === undefined) {\n // The common migration: instructions live in CLAUDE.md and there is no\n // AGENTS.md. Promoting the content is the user's call — the tool says so\n // rather than moving text it did not write.\n notes.push(\n 'CLAUDE.md carries its own instructions and there is no AGENTS.md. Move that content into AGENTS.md and reduce CLAUDE.md to the one-line bridge; nothing here rewrites it for you.',\n );\n actions.push(\n action(\n 'CLAUDE.md',\n 'review',\n 'recommended',\n 'Holds instructions of its own; AGENTS.md should be the canonical source.',\n ),\n );\n } else {\n notes.push(\n 'AGENTS.md and CLAUDE.md both carry instructions. Two copies of the same rules diverge the first time only one is edited — keep AGENTS.md and reduce CLAUDE.md to the one-line bridge.',\n );\n actions.push(\n action(\n 'CLAUDE.md',\n 'review',\n 'recommended',\n 'Duplicates instructions that AGENTS.md already owns.',\n ),\n );\n }\n\n // ── Documented conventions ───────────────────────────────────────────────\n const hasContributing = policy.docs.some((doc) => doc.kind === 'contributing');\n const conventionsPath = 'docs/conventions.md';\n const conventionsExists = await state.exists(conventionsPath);\n\n if (conventionsExists) {\n actions.push(\n action(conventionsPath, 'keep', 'recommended', 'Conventions are already documented.'),\n );\n } else if (orgTemplates.length > 0) {\n // The conventions are the organization's, and they are documented wherever\n // the organization documents them. A per-repository copy would be a second\n // source that drifts from the one everybody else follows.\n actions.push(\n action(\n conventionsPath,\n 'keep',\n 'recommended',\n \"The conventions are the organization's; documenting them again here would fork them.\",\n ),\n );\n } else if (hasContributing && localTemplates.length > 0) {\n // The repository already says how it works, in its own words and its own\n // file. A second document would be a competing source, which is the exact\n // failure this tool exists to avoid.\n actions.push(\n action(\n conventionsPath,\n 'keep',\n 'recommended',\n 'CONTRIBUTING.md and the Issue Templates already document how this repository works.',\n ),\n );\n } else {\n actions.push(\n action(\n conventionsPath,\n 'create',\n 'recommended',\n 'The source of truth for issue types, labels, branches and commits.',\n renderConventionsDoc(policy.issues.types.length > 0),\n ),\n );\n }\n\n // ── Labels ───────────────────────────────────────────────────────────────\n const labelsPath = '.github/labels.json';\n if (policy.issues.labels.length > 0) {\n actions.push(\n action(\n labelsPath,\n 'keep',\n 'contextual',\n `This repository already has ${policy.issues.labels.length} label(s); Issue Flow never rewrites a taxonomy.`,\n ),\n );\n } else if (await state.exists(labelsPath)) {\n actions.push(action(labelsPath, 'keep', 'contextual', 'A labels file already exists.'));\n } else {\n const includeTypeLabels = policy.issues.types.length === 0;\n actions.push(\n action(\n labelsPath,\n 'create',\n 'contextual',\n includeTypeLabels\n ? 'A baseline label set, including type labels because this organization has no Issue Types.'\n : 'A baseline label set. Type labels are omitted: this organization has native Issue Types.',\n renderLabelsFile(includeTypeLabels),\n ),\n );\n }\n\n if (policy.issues.types.length === 0) {\n notes.push(\n 'No GitHub Issue Types were found. They are an organization-level setting and cannot be created from here; defining them there is better than the `type:*` labels this proposes as a fallback.',\n );\n }\n\n return { root: policy.root, actions, notes };\n}\n\n/** Load a repository file, or null. Used by the state adapter and by tests. */\nexport async function readIfPresent(root: string, relPath: string): Promise<string | null> {\n try {\n return await readFile(join(root, relPath), 'utf-8');\n } catch {\n return null;\n }\n}\n","/**\n * The default convention set — Issue Flow's fallback when a repository declares\n * nothing of its own.\n *\n * This is the **last** rung of the precedence ladder, and it only ever applies\n * where the repository, its organization and the user's configuration are all\n * silent. It exists so that a brand-new repository still gets a usable,\n * professional baseline instead of an empty one.\n *\n * Two rules shaped every choice here.\n *\n * **Native structure before textual convention.** The preference order is\n * `native feature > structured field > label > free text`. Anything GitHub\n * already models — the type of an issue, its priority, its state — is not\n * re-implemented as a title prefix or a label, because that creates a second\n * truth that ages on its own. GitHub's own defaults are `task`, `bug` and\n * `feature`, and an organization may define up to 25 Issue Types.\n *\n * **Do not fragment the backlog.** A type per flavour of work makes no query\n * better and every filter worse. Documentation, maintenance, refactoring and\n * technical debt are *characteristics*, not natures, and they belong on labels\n * over a real type.\n */\n\n/** A default Issue Type, with the reasoning a template and a report can quote. */\nexport interface DefaultIssueType {\n /** Display name, matching GitHub's Issue Type casing convention. */\n name: string;\n /** Slug used for the Issue Form file name, prefixed by `order`. */\n slug: string;\n /** Order in the template chooser: capture first, execution last. */\n order: number;\n /** One line: what this type is. */\n summary: string;\n /**\n * Whether an issue of this type is executable once it is ready.\n *\n * The distinction matters more than it looks: an open issue is not approved\n * work. `Idea`, `Research` and `Epic` record intent and never authorize an\n * agent to start implementing.\n */\n executable: boolean;\n /** GitHub's own default types, which exist in every organization. */\n native: boolean;\n}\n\n/**\n * Six types, not thirteen.\n *\n * `Bug`, `Feature` and `Task` are GitHub's native defaults and cover all\n * executable work. `Idea`, `Research` and `Epic` are added because each answers\n * a question the other three cannot: *is this even worth doing*, *what is the\n * answer*, and *what is the umbrella*. Everything else the user might expect —\n * documentation, maintenance, refactor, technical debt, security, spike — is\n * deliberately **not** a type; see {@link NON_TYPES}.\n */\nexport const DEFAULT_ISSUE_TYPES: readonly DefaultIssueType[] = [\n {\n name: 'Idea',\n slug: 'idea',\n order: 1,\n summary: 'A hypothesis, opportunity or perceived problem, not yet analysed.',\n executable: false,\n native: false,\n },\n {\n name: 'Research',\n slug: 'research',\n order: 2,\n summary: 'An investigation that produces knowledge: analysis, benchmark, feasibility.',\n executable: false,\n native: false,\n },\n {\n name: 'Epic',\n slug: 'epic',\n order: 3,\n summary: 'An umbrella objective delivered through sub-issues.',\n executable: false,\n native: false,\n },\n {\n name: 'Feature',\n slug: 'feature',\n order: 4,\n summary: 'A new capability or a change to what the product does.',\n executable: true,\n native: true,\n },\n {\n name: 'Bug',\n slug: 'bug',\n order: 5,\n summary: 'Existing behaviour that diverges from what is expected.',\n executable: true,\n native: true,\n },\n {\n name: 'Task',\n slug: 'task',\n order: 6,\n summary: 'Concrete work that is neither a feature nor a bug.',\n executable: true,\n native: true,\n },\n] as const;\n\n/** Something people often expect to be a type, and why it is not one here. */\nexport interface NonType {\n concept: string;\n /** How to represent it instead. */\n instead: string;\n why: string;\n}\n\n/**\n * The list is as much a part of the convention as the types themselves: without\n * it, every repository re-derives the same six arguments and reaches a different\n * answer.\n */\nexport const NON_TYPES: readonly NonType[] = [\n {\n concept: 'Documentation',\n instead: '`Task` + label `docs`',\n why: 'The work is a task; what varies is the area it touches.',\n },\n {\n concept: 'Maintenance, chore',\n instead: '`Task`',\n why: 'That is already what `Task` means.',\n },\n {\n concept: 'Refactor, technical debt',\n instead: '`Task` + label `tech-debt`',\n why: 'A cross-cutting characteristic, not a distinct nature of work.',\n },\n {\n concept: 'Security',\n instead: 'the real type (`Bug`/`Task`/`Feature`) + label `security`',\n why: 'It cuts across every type; as a type it would hide whether the item is a flaw or preventive work.',\n },\n {\n concept: 'Spike, investigation',\n instead: '`Research`',\n why: 'The same concept under a different name.',\n },\n {\n concept: 'Enhancement',\n instead: '`Feature`',\n why: 'A change to what the product does is a feature, whether it is new or an improvement.',\n },\n {\n concept: 'Proposal, RFC',\n instead: '`Research`, and an ADR for the decision it produces',\n why: 'The issue carries the investigation; the decision belongs in a document that outlives it.',\n },\n {\n concept: 'Question',\n instead: 'a Discussion, or `Research` when it needs work',\n why: 'A question that needs no work is not a backlog item.',\n },\n] as const;\n\n/** A default label, with the reason it exists. */\nexport interface DefaultLabel {\n name: string;\n description: string;\n /** Hex colour without `#`, matching GitHub's palette conventions. */\n color: string;\n}\n\n/**\n * A deliberately small vocabulary, for what has **no native representation**:\n * technical area, component and cross-cutting characteristic.\n *\n * Nothing here duplicates a field. There is no `priority`, no `status`, no\n * `type` and no size label: GitHub models all four, and a label alongside a\n * field is a second truth that drifts. The one exception is\n * {@link FALLBACK_TYPE_LABELS}, for repositories whose organization has no Issue\n * Types at all.\n */\nexport const DEFAULT_LABELS: readonly DefaultLabel[] = [\n { name: 'api', description: 'Public interfaces and contracts', color: '006b75' },\n { name: 'backend', description: 'Server-side code', color: '5319e7' },\n { name: 'frontend', description: 'User-facing code', color: 'bfd4f2' },\n { name: 'database', description: 'Schema, queries and migrations', color: 'c2e0c6' },\n { name: 'infra', description: 'Infrastructure, CI and deployment', color: 'f9d0c4' },\n { name: 'docs', description: 'Documentation', color: '0075ca' },\n { name: 'security', description: 'Security or privacy impact', color: 'd93f0b' },\n {\n name: 'tech-debt',\n description: 'Debt paid down rather than capability added',\n color: 'e4e669',\n },\n { name: 'blocked', description: 'Waiting on something outside this repository', color: 'b60205' },\n { name: 'good first issue', description: 'Good for newcomers', color: '7057ff' },\n] as const;\n\n/**\n * Type labels, used **only** where the organization has no Issue Types.\n *\n * A repository that cannot use the native field still needs the type to be\n * queryable, and a label is the next best representation. The moment Issue Types\n * exist, these become the second truth the convention warns about — which is why\n * scaffolding proposes them only after finding none.\n */\nexport const FALLBACK_TYPE_LABELS: readonly DefaultLabel[] = DEFAULT_ISSUE_TYPES.map((type) => ({\n name: `type:${type.slug}`,\n description: type.summary,\n color: type.executable ? '1d76db' : 'd4c5f9',\n}));\n\n/**\n * The sections an issue body is expected to carry, by default.\n *\n * Kept short on purpose. A twelve-section template is excellent for an\n * architectural proposal and hostile to capturing an idea, and a form nobody\n * fills in honestly is worse than no form. Each default Issue Form asks only\n * what its type actually needs; this list is what the *prose* fallback uses when\n * a repository has no forms at all.\n */\nexport const DEFAULT_BODY_SECTIONS: readonly string[] = [\n 'Context',\n 'Problem',\n 'Objective',\n 'Scope',\n 'Out of scope',\n 'Acceptance criteria',\n 'Dependencies',\n 'Risks',\n 'References',\n] as const;\n\n/** Branch naming Issue Flow has always used. */\nexport const DEFAULT_BRANCH_CONVENTION = 'issue/{N}-{slug}';\n\n/**\n * Commit convention.\n *\n * Conventional Commits is the closest thing to a lingua franca, and it is what\n * makes a changelog and a semver bump derivable from the history — which is\n * exactly why the type must reflect the change instead of always being `feat`.\n */\nexport const DEFAULT_COMMIT_CONVENTION = 'Conventional Commits — type(scope): subject';\n\n/**\n * Issue title convention.\n *\n * `null` on purpose: with Issue Types, a `[Bug]` prefix restates in text what a\n * structured field already carries. A repository whose organization has no Issue\n * Types may want one, and declares it.\n */\nexport const DEFAULT_ISSUE_TITLE_CONVENTION: string | null = null;\n\n/** Everything above, as one object, for the report and the scaffolding plan. */\nexport const DEFAULT_CONVENTIONS = {\n issueTypes: DEFAULT_ISSUE_TYPES,\n nonTypes: NON_TYPES,\n labels: DEFAULT_LABELS,\n fallbackTypeLabels: FALLBACK_TYPE_LABELS,\n bodySections: DEFAULT_BODY_SECTIONS,\n branchConvention: DEFAULT_BRANCH_CONVENTION,\n commitConvention: DEFAULT_COMMIT_CONVENTION,\n issueTitleConvention: DEFAULT_ISSUE_TITLE_CONVENTION,\n} as const;\n","import {\n DEFAULT_COMMIT_CONVENTION,\n DEFAULT_ISSUE_TYPES,\n DEFAULT_LABELS,\n type DefaultIssueType,\n FALLBACK_TYPE_LABELS,\n NON_TYPES,\n} from '../conventions/defaults.js';\n\n/**\n * The files initialization can write, rendered from the default convention set.\n *\n * Nothing here is a literal copy of a template: every asset is generated from\n * `conventions/defaults.ts`, so the convention and the files that express it can\n * never disagree. Changing a type or a label changes the forms, the labels file\n * and the documentation in one edit.\n */\n\nexport interface ScaffoldAsset {\n /** Repository-relative path, POSIX separators. */\n path: string;\n content: string;\n /**\n * Why this file exists. Shown in the plan, so a user can decline a specific\n * one and understand what they are declining.\n */\n purpose: string;\n /**\n * `required` files are what makes a repository legible to an agent at all.\n * `recommended` ones are worth having; `contextual` ones are only proposed\n * when the repository's state calls for them.\n */\n tier: 'required' | 'recommended' | 'contextual';\n}\n\n/** Fields every executable type needs, and no type should be without. */\nfunction acceptanceCriteriaField(): string {\n return [\n ' - type: textarea',\n ' id: acceptance',\n ' attributes:',\n ' label: Acceptance criteria',\n ' description: How anyone can tell this is done. Checks that fail today and pass afterwards.',\n ' value: |',\n ' - [ ] ',\n ' validations:',\n ' required: true',\n ].join('\\n');\n}\n\nfunction referencesField(): string {\n return [\n ' - type: textarea',\n ' id: references',\n ' attributes:',\n ' label: References',\n ' description: Links, related issues, prior discussion, evidence.',\n ].join('\\n');\n}\n\n/**\n * One Issue Form per default type.\n *\n * `type:` is emitted so that an organization with Issue Types gets the native\n * field filled in; GitHub ignores the key when the type does not exist, so the\n * same file works either way.\n *\n * Required fields are rationed on purpose. `Idea` asks for exactly one, because\n * the cost of recording a thought has to stay near zero — a capture form with\n * eight mandatory fields is a form nobody uses, and the ideas then live in\n * someone's head instead.\n */\nexport function renderIssueForm(type: DefaultIssueType): string {\n const header = [\n `name: \"${emojiFor(type.slug)} ${type.name}\"`,\n `description: \"${type.summary}\"`,\n `type: \"${type.name}\"`,\n 'body:',\n ];\n\n const bodies: Record<string, string[]> = {\n idea: [\n ' - type: markdown',\n ' attributes:',\n ' value: |',\n ' Quick capture. Only the first field is required — triage fills in the rest.',\n ' An Idea is **not approved work**: no agent implements it.',\n ' - type: textarea',\n ' id: idea',\n ' attributes:',\n ' label: The idea',\n ' description: What it is, in a few lines. It does not have to be solved or complete.',\n ' validations:',\n ' required: true',\n ' - type: textarea',\n ' id: problem',\n ' attributes:',\n ' label: Problem or opportunity',\n ' description: What pain or gain is behind it? Leave blank if you do not know yet.',\n referencesField(),\n ' - type: textarea',\n ' id: questions',\n ' attributes:',\n ' label: Open questions',\n ' description: What would have to be answered before deciding anything.',\n ],\n research: [\n ' - type: textarea',\n ' id: question',\n ' attributes:',\n ' label: Question to answer',\n ' description: The decision this investigation has to unblock.',\n ' validations:',\n ' required: true',\n ' - type: textarea',\n ' id: context',\n ' attributes:',\n ' label: Context',\n ' description: What is already known, and why the question is open.',\n ' - type: textarea',\n ' id: deliverable',\n ' attributes:',\n ' label: Expected deliverable',\n ' description: A conclusion and a recommendation. Say where durable knowledge will be written down.',\n ' validations:',\n ' required: true',\n referencesField(),\n ],\n epic: [\n ' - type: markdown',\n ' attributes:',\n ' value: |',\n ' An Epic is never executed directly: it is delivered through its sub-issues.',\n ' - type: textarea',\n ' id: objective',\n ' attributes:',\n ' label: Objective',\n ' description: The state of the product this Epic describes.',\n ' validations:',\n ' required: true',\n ' - type: textarea',\n ' id: scope',\n ' attributes:',\n ' label: Scope',\n ' validations:',\n ' required: true',\n ' - type: textarea',\n ' id: out-of-scope',\n ' attributes:',\n ' label: Out of scope',\n ' description: What this Epic deliberately does not cover. Absence here is what makes scope creep arguable.',\n ' - type: textarea',\n ' id: children',\n ' attributes:',\n ' label: Known sub-issues',\n ' description: Add them as sub-issues once they exist; list what you already foresee.',\n ],\n feature: [\n ' - type: textarea',\n ' id: problem',\n ' attributes:',\n ' label: Problem',\n ' description: What is not possible today, and for whom.',\n ' validations:',\n ' required: true',\n ' - type: textarea',\n ' id: objective',\n ' attributes:',\n ' label: Objective',\n ' description: What becomes true when this is done.',\n ' validations:',\n ' required: true',\n ' - type: textarea',\n ' id: scope',\n ' attributes:',\n ' label: Scope and out of scope',\n ' description: What is included, and what is deliberately not.',\n acceptanceCriteriaField(),\n ' - type: textarea',\n ' id: dependencies',\n ' attributes:',\n ' label: Dependencies and risks',\n ' description: What has to exist first, and what could go wrong.',\n referencesField(),\n ],\n bug: [\n ' - type: textarea',\n ' id: current',\n ' attributes:',\n ' label: Current behaviour',\n ' validations:',\n ' required: true',\n ' - type: textarea',\n ' id: expected',\n ' attributes:',\n ' label: Expected behaviour',\n ' validations:',\n ' required: true',\n ' - type: textarea',\n ' id: steps',\n ' attributes:',\n ' label: Steps to reproduce',\n ' value: |',\n ' 1.',\n ' 2.',\n ' 3.',\n ' validations:',\n ' required: true',\n ' - type: input',\n ' id: environment',\n ' attributes:',\n ' label: Environment',\n ' description: Where it happens — environment, version, component, browser.',\n ' validations:',\n ' required: true',\n ' - type: textarea',\n ' id: evidence',\n ' attributes:',\n ' label: Evidence',\n ' description: Logs, request and response, screenshot, trace id, affected URL.',\n ' - type: dropdown',\n ' id: impact',\n ' attributes:',\n ' label: Impact',\n ' options:',\n ' - \"Data loss or improper exposure\"',\n ' - \"Feature unavailable, no workaround\"',\n ' - \"Feature degraded, workaround exists\"',\n ' - \"Cosmetic or low consequence\"',\n ' validations:',\n ' required: true',\n ' - type: textarea',\n ' id: done',\n ' attributes:',\n ' label: How to tell it is fixed',\n ' description: The check that fails today and passes afterwards.',\n ' validations:',\n ' required: true',\n ],\n task: [\n ' - type: textarea',\n ' id: what',\n ' attributes:',\n ' label: What has to be done',\n ' validations:',\n ' required: true',\n ' - type: textarea',\n ' id: why',\n ' attributes:',\n ' label: Why',\n ' description: The reason this is worth doing now.',\n ' validations:',\n ' required: true',\n acceptanceCriteriaField(),\n referencesField(),\n ],\n };\n\n const body = bodies[type.slug];\n if (body === undefined) {\n throw new Error(`No Issue Form body defined for type \"${type.slug}\"`);\n }\n\n return `${[...header, ...body].join('\\n')}\\n`;\n}\n\nfunction emojiFor(slug: string): string {\n const map: Record<string, string> = {\n idea: '💡',\n research: '🔬',\n epic: '🗺️',\n feature: '✨',\n bug: '🐛',\n task: '🔧',\n };\n return map[slug] ?? '📌';\n}\n\n/**\n * The template chooser configuration.\n *\n * `blank_issues_enabled: true` is deliberate: a contributor who does not fit any\n * form should open the issue anyway. A closed chooser turns a report into\n * silence, which costs more than an unstructured issue.\n */\nexport function renderIssueTemplateConfig(): string {\n return [\n 'blank_issues_enabled: true',\n 'contact_links:',\n ' - name: How this backlog works',\n ' url: https://github.com/fabioassuncao/issue-flow/blob/main/docs/conventions.md',\n ' about: Issue types, what each one authorizes, labels and the lifecycle.',\n '',\n ].join('\\n');\n}\n\n/** The Pull Request template. */\nexport function renderPullRequestTemplate(): string {\n return [\n '## What changed',\n '',\n '<!-- What this Pull Request does, and why. One paragraph is enough. -->',\n '',\n '## How it was tested',\n '',\n '<!-- The checks you ran, and what they proved. \"CI is green\" is not a test plan. -->',\n '',\n '## Related issues',\n '',\n '<!--',\n 'One `Closes #N` per issue this resolves, each on its own line and as plain body',\n 'text — GitHub only auto-closes when the line is not inside a code fence.',\n '-->',\n '',\n '## Notes for reviewers',\n '',\n '<!-- Where to start, and anything you are unsure about. Write \"N/A\" rather than deleting a section. -->',\n '',\n ].join('\\n');\n}\n\n/** `AGENTS.md`, generated as an index and nothing else. */\nexport function renderAgentsMd(projectName: string, referenced: string[]): string {\n const lines = [\n `# ${projectName}`,\n '',\n 'This file is an **index**. It holds no rule, command or convention of its own —',\n 'those live in the documents referenced below, which are the source of truth.',\n '',\n '> Document once. Reference everywhere it is needed.',\n '',\n '## Start here',\n '',\n ];\n\n for (const path of referenced) {\n lines.push(`- [\\`${path}\\`](${path})`);\n }\n if (referenced.length === 0) {\n lines.push('- [`README.md`](README.md) — what this project is, and how to run it');\n }\n\n lines.push(\n '',\n '## Working on an issue',\n '',\n '- [`docs/conventions.md`](docs/conventions.md) — issue types, labels, branches,',\n ' commits and Pull Requests: what this repository expects and what it does not',\n '',\n '## What does not belong in this file',\n '',\n 'Anything that can live in a document of its own: build and test commands, code',\n 'style, architecture rules, testing strategy, operational procedures. If it is a',\n 'rule, a standard or reusable knowledge, it belongs in its own document and this',\n 'file only points at it.',\n '',\n 'An instruction that today exists **only** here does not stay here: move it to the',\n 'right document and leave a reference behind. Duplicated instructions in an agent',\n 'file age out of sight and start contradicting the source without anyone noticing.',\n '',\n );\n\n return lines.join('\\n');\n}\n\n/**\n * `CLAUDE.md`, which is one line by design.\n *\n * Claude Code reads `CLAUDE.md` as its memory file, and the temptation is to\n * put the instructions there too. That produces two copies of the same rules\n * which diverge the first time only one of them is edited — so this file is a\n * bridge, never a second source.\n */\nexport function renderClaudeMd(): string {\n return 'Read and follow the instructions in AGENTS.md.\\n';\n}\n\n/** The conventions document: the source of truth this scaffolding creates. */\nexport function renderConventionsDoc(hasIssueTypes: boolean): string {\n const typeRows = DEFAULT_ISSUE_TYPES.map(\n (t) =>\n `| **${t.name}** | ${t.summary} | ${t.executable ? 'Yes, once it is ready' : '**No**'} |`,\n ).join('\\n');\n\n const nonTypeRows = NON_TYPES.map((n) => `| ${n.concept} | ${n.instead} | ${n.why} |`).join('\\n');\n\n const labelRows = DEFAULT_LABELS.map((l) => `| \\`${l.name}\\` | ${l.description} |`).join('\\n');\n\n const typeMechanism = hasIssueTypes\n ? 'This organization has GitHub Issue Types, so the type lives in the **native field**.'\n : [\n 'This organization has no GitHub Issue Types, so the type is carried by a',\n '`type:*` label. If Issue Types are enabled later, move to them and drop these',\n 'labels — keeping both would create a second truth that drifts.',\n ].join('\\n');\n\n return `# Conventions\n\nHow work is recorded, classified and executed in this repository.\n\n## Principles\n\n1. **An open issue is not approved work.** Recording is cheap; executing requires\n explicit authorization.\n2. **Native structure before textual convention.** The order of preference is\n native feature > structured field > label > free text. Nothing GitHub already\n models is re-implemented as a title prefix or a label.\n3. **Each piece of information in one place.** If something has a field, it does\n not also have a label.\n4. **Capturing an idea is not bureaucratic.** The Idea form has one required\n field. The cost of recording a thought has to stay near zero.\n\n## Issue types\n\n${typeMechanism}\n\n| Type | What it is | Authorizes execution? |\n|---|---|---|\n${typeRows}\n\n### Choosing the type\n\nIn order — the first \"yes\" decides:\n\n1. **Is something broken?** → \\`Bug\\`, even if the expected behaviour never worked.\n2. **Is the question still unanswered?** → \\`Research\\`.\n3. **Is it just a record, with no analysis yet?** → \\`Idea\\`.\n4. **Is it large and delivered in parts?** → \\`Epic\\`, with sub-issues.\n5. **Does it change what the product does?** → \\`Feature\\`.\n6. **None of the above** → \\`Task\\`.\n\n### What is deliberately not a type\n\nA type per flavour of work fragments the backlog without improving a single\nquery.\n\n| Concept | Represent it as | Why |\n|---|---|---|\n${nonTypeRows}\n\n## Labels\n\nLabels are the last resort, for what has no native representation: technical\narea, component and cross-cutting characteristic.\n\n| Label | Meaning |\n|---|---|\n${labelRows}\n\n**Never use a label for** priority, type, state or size when a field exists for\nit: the label becomes a second truth that ages on its own.\n\n## Branches and commits\n\n- Branch: \\`issue/{N}-{slug}\\`\n- Commit: ${DEFAULT_COMMIT_CONVENTION}\n\nThe commit type must match the nature of the change. A bug fix committed as\n\\`feat:\\` corrupts the changelog and any version bump derived from the history.\n\n## Pull Requests\n\nFollow \\`.github/PULL_REQUEST_TEMPLATE.md\\`. Keep every section it defines and\nanswer the ones that do not apply with one line saying why — a deleted section\nreads as an unanswered one to automated review.\n\nLink the issues with one \\`Closes #N\\` per issue, as plain body text.\n\n## Agent entry points\n\n\\`AGENTS.md\\` is the canonical entry point for any coding agent, of any vendor.\nIt is an index: it says which documents to read, and holds no rule of its own.\n\n\\`CLAUDE.md\\` exists only as the Claude Code integration and contains a single\nline pointing at \\`AGENTS.md\\`. Any other agent adapter follows the same rule.\n\n\\`\\`\\`text\nCLAUDE.md → AGENTS.md → specialized documentation → single source of truth\n\\`\\`\\`\n`;\n}\n\n/** The labels file, consumed by the label sync step and readable by a human. */\nexport function renderLabelsFile(includeTypeLabels: boolean): string {\n const labels = includeTypeLabels ? [...FALLBACK_TYPE_LABELS, ...DEFAULT_LABELS] : DEFAULT_LABELS;\n\n return `${JSON.stringify(\n labels.map((label) => ({\n name: label.name,\n description: label.description,\n color: label.color,\n })),\n null,\n 2,\n )}\\n`;\n}\n"],"mappings":"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","names":["join","join"]}
@@ -7,7 +7,7 @@ import {
7
7
  getSessionPublisher,
8
8
  isoNow,
9
9
  printRetry
10
- } from "./chunk-V5OHFSW4.js";
10
+ } from "./chunk-HC2MT7BX.js";
11
11
 
12
12
  // src/core/phase-runner.ts
13
13
  import { readFile } from "fs/promises";
@@ -55,4 +55,4 @@ export {
55
55
  runPhaseWithRetry,
56
56
  readFileWithGrace
57
57
  };
58
- //# sourceMappingURL=chunk-UUY4PCWW.js.map
58
+ //# sourceMappingURL=chunk-3BOHTH4V.js.map
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ //# sourceMappingURL=chunk-3D4A4B46.js.map
@@ -4,12 +4,13 @@ import {
4
4
  loadPolicyConfig,
5
5
  mergeConfigLayers,
6
6
  run
7
- } from "./chunk-VYFKOUUR.js";
7
+ } from "./chunk-VQ5VN7DF.js";
8
8
  import {
9
- printError,
10
- printInfo,
11
9
  printWarning
12
- } from "./chunk-V5OHFSW4.js";
10
+ } from "./chunk-HC2MT7BX.js";
11
+
12
+ // src/policy/resolve.ts
13
+ import { isAbsolute, normalize, sep as sep2 } from "path";
13
14
 
14
15
  // src/policy/discovery.ts
15
16
  import { readdir, readFile, stat } from "fs/promises";
@@ -283,6 +284,23 @@ function parseOrganizationTemplates(payload) {
283
284
  }
284
285
  return templates;
285
286
  }
287
+ function parseOrganizationForms(payload) {
288
+ const repository = isRecord(payload) && isRecord(payload.data) ? payload.data.repository : null;
289
+ const tree = isRecord(repository) ? repository.object : null;
290
+ const entries = isRecord(tree) && Array.isArray(tree.entries) ? tree.entries : [];
291
+ const templates = [];
292
+ for (const entry of entries) {
293
+ if (!isRecord(entry)) continue;
294
+ const name = asString(entry.name);
295
+ if (name === null || entry.type !== "blob") continue;
296
+ if (/^config\.(yml|yaml)$/i.test(name)) continue;
297
+ if (!/\.(yml|yaml|md|markdown)$/i.test(name)) continue;
298
+ const text = isRecord(entry.object) ? asString(entry.object.text) : null;
299
+ if (text === null) continue;
300
+ templates.push({ ...parseIssueTemplateFile(name, text), origin: "organization" });
301
+ }
302
+ return templates.sort((a, b) => a.path.localeCompare(b.path));
303
+ }
286
304
 
287
305
  // src/policy/types.ts
288
306
  var POLICY_SCHEMA_VERSION = 1;
@@ -519,7 +537,7 @@ async function discoverDocuments(root, scope) {
519
537
  const kind = classifyDocument(name);
520
538
  if (kind === null) continue;
521
539
  const document = await collect(join(levelDir, name), kind, level, null);
522
- if (document !== null && kind === "agents") {
540
+ if (document !== null && (kind === "agents" || kind === "claude")) {
523
541
  indexes.push(document);
524
542
  }
525
543
  }
@@ -704,6 +722,53 @@ var ORGANIZATION_TEMPLATES_QUERY = `query($owner:String!,$name:String!){
704
722
  }
705
723
  }
706
724
  }`;
725
+ var ORGANIZATION_FORMS_QUERY = `query($owner:String!){
726
+ repository(owner:$owner,name:".github"){
727
+ object(expression:"HEAD:.github/ISSUE_TEMPLATE"){
728
+ ... on Tree {
729
+ entries { name type object { ... on Blob { text } } }
730
+ }
731
+ }
732
+ }
733
+ }`;
734
+ async function discoverOrganizationForms(root, owner, exec = defaultExec) {
735
+ if (owner === null || owner === "") {
736
+ return { templates: [], sources: [] };
737
+ }
738
+ const outcome = await ghJson(
739
+ root,
740
+ ["api", "graphql", "-f", `query=${ORGANIZATION_FORMS_QUERY}`, "-f", `owner=${owner}`],
741
+ exec
742
+ );
743
+ if ("error" in outcome) {
744
+ return {
745
+ templates: [],
746
+ sources: [
747
+ {
748
+ kind: "issue-templates",
749
+ origin: "gh",
750
+ path: null,
751
+ status: "unavailable",
752
+ detail: outcome.error
753
+ }
754
+ ]
755
+ };
756
+ }
757
+ const templates = parseOrganizationForms(outcome.value);
758
+ if (templates.length === 0) {
759
+ return { templates, sources: [] };
760
+ }
761
+ return {
762
+ templates,
763
+ sources: templates.map((template) => ({
764
+ kind: "issue-templates",
765
+ origin: "gh",
766
+ path: null,
767
+ status: "found",
768
+ detail: `organization form "${template.path}"`
769
+ }))
770
+ };
771
+ }
707
772
  async function discoverOrganizationTemplates(root, slug, exec = defaultExec) {
708
773
  if (slug === null) {
709
774
  return { templates: [], sources: [] };
@@ -753,7 +818,6 @@ async function discoverOrganizationTemplates(root, slug, exec = defaultExec) {
753
818
  }
754
819
 
755
820
  // src/policy/resolve.ts
756
- import { isAbsolute, normalize, sep as sep2 } from "path";
757
821
  function normalizeScope(scope) {
758
822
  if (scope === null || scope === void 0) return null;
759
823
  const trimmed = scope.trim();
@@ -815,14 +879,21 @@ async function resolvePolicy(root, scope, options) {
815
879
  discoverBaseBranch(root, options.exec),
816
880
  toggles.issueTypes || wantsOrgTemplates ? discoverGitHubSlug(root, options.exec) : Promise.resolve(null)
817
881
  ]);
818
- const [labels, issueTypes, orgTemplates] = await Promise.all([
882
+ const [labels, issueTypes, orgTemplates, orgForms] = await Promise.all([
819
883
  toggles.labels ? discoverLabels(root, options.exec) : Promise.resolve({ labels: [], sources: [disabledSource("labels")] }),
820
884
  toggles.issueTypes ? discoverIssueTypes(root, slug?.owner ?? null, options.exec) : Promise.resolve({ types: [], sources: [disabledSource("issue-types")] }),
821
- wantsOrgTemplates ? discoverOrganizationTemplates(root, slug, options.exec) : Promise.resolve({ templates: [], sources: [] })
885
+ wantsOrgTemplates ? discoverOrganizationTemplates(root, slug, options.exec) : Promise.resolve({ templates: [], sources: [] }),
886
+ // Two calls, because GitHub answers the two kinds of organization default
887
+ // through different surfaces: `issueTemplates` covers markdown templates and
888
+ // is blind to Issue Forms, which only exist as files in the org's `.github`
889
+ // repository. A repository whose organization publishes forms would
890
+ // otherwise look like one with no templates at all.
891
+ wantsOrgTemplates ? discoverOrganizationForms(root, slug?.owner ?? null, options.exec) : Promise.resolve({ templates: [], sources: [] })
822
892
  ]);
823
893
  const sources = [
824
894
  ...templateDiscovery.sources,
825
895
  ...orgTemplates.sources,
896
+ ...orgForms.sources,
826
897
  ...issueTypes.sources,
827
898
  ...labels.sources,
828
899
  ...prTemplates.sources,
@@ -864,7 +935,7 @@ async function resolvePolicy(root, scope, options) {
864
935
  scope,
865
936
  enabled: true,
866
937
  issues: {
867
- templates: [...templateDiscovery.templates, ...orgTemplates.templates],
938
+ templates: [...templateDiscovery.templates, ...orgTemplates.templates, ...orgForms.templates],
868
939
  types: issueTypes.types,
869
940
  labels: labels.labels,
870
941
  titleConvention: issues.titleConvention ?? null
@@ -906,108 +977,7 @@ async function loadRepositoryPolicy(options = {}) {
906
977
  }
907
978
  }
908
979
 
909
- // src/commands/policy.ts
910
- function groupSources(sources) {
911
- const grouped = /* @__PURE__ */ new Map();
912
- for (const source of sources) {
913
- const bucket = grouped.get(source.kind);
914
- if (bucket === void 0) {
915
- grouped.set(source.kind, [source]);
916
- } else {
917
- bucket.push(source);
918
- }
919
- }
920
- return grouped;
921
- }
922
- function describeSource(source) {
923
- const where = source.path ?? source.origin;
924
- const detail = source.detail === null ? "" : ` \u2014 ${source.detail}`;
925
- return `${source.status === "found" ? "" : `[${source.status}] `}${where}${detail}`;
926
- }
927
- function render(policy) {
928
- const lines = [];
929
- const push = (line = "") => {
930
- lines.push(line);
931
- };
932
- push(`Repository: ${policy.root}`);
933
- push(`Scope: ${policy.scope ?? "(root)"}`);
934
- if (!policy.enabled) {
935
- push();
936
- push("Policy discovery is disabled (policy.enabled = false).");
937
- console.log(lines.join("\n"));
938
- return;
939
- }
940
- push();
941
- push("Issues");
942
- push(` templates: ${policy.issues.templates.length}`);
943
- for (const template of policy.issues.templates) {
944
- const labels = template.labels.length > 0 ? ` labels=[${template.labels.join(", ")}]` : "";
945
- const type = template.type === null ? "" : ` type=${template.type}`;
946
- push(` - ${template.path} (${template.format}, ${template.origin})${type}${labels}`);
947
- }
948
- push(` types: ${policy.issues.types.length > 0 ? policy.issues.types.join(", ") : "(none)"}`);
949
- push(` labels: ${policy.issues.labels.length}`);
950
- for (const label of policy.issues.labels) {
951
- push(` - ${label.name}${label.description === null ? "" : ` \u2014 ${label.description}`}`);
952
- }
953
- push(` titleConvention: ${policy.issues.titleConvention ?? "(none)"}`);
954
- push();
955
- push("Pull Requests");
956
- push(` template: ${policy.pullRequests.templates[0]?.path ?? "(none)"}`);
957
- for (const template of policy.pullRequests.templates.slice(1)) {
958
- push(` - ${template.path}`);
959
- }
960
- push(` baseBranch: ${policy.pullRequests.baseBranch ?? "(none)"}`);
961
- push(` titleConvention: ${policy.pullRequests.titleConvention ?? "(none)"}`);
962
- push();
963
- push("Git");
964
- push(` branchConvention: ${policy.git.branchConvention ?? "(none)"}`);
965
- push(` commitConvention: ${policy.git.commitConvention ?? "(none)"}`);
966
- push();
967
- push(`Documents: ${policy.docs.length}`);
968
- for (const doc of policy.docs) {
969
- const scope = doc.scope === "" ? "root" : doc.scope;
970
- const from = doc.referencedFrom === null ? "" : ` \u2190 ${doc.referencedFrom}`;
971
- push(` - ${doc.path} (${doc.kind}, scope=${scope})${from}`);
972
- }
973
- push();
974
- push(`CODEOWNERS: ${policy.codeowners === null ? "(none)" : "found"}`);
975
- push();
976
- push("Sources");
977
- for (const [kind, entries] of groupSources(policy.sources)) {
978
- push(` ${kind}`);
979
- for (const entry of entries) {
980
- push(` - ${describeSource(entry)}`);
981
- }
982
- }
983
- console.log(lines.join("\n"));
984
- }
985
- async function runPolicy(options = {}) {
986
- let policy;
987
- try {
988
- policy = await loadRepositoryPolicy({ scope: options.scope ?? null });
989
- } catch (err) {
990
- printError(err instanceof Error ? err.message : String(err));
991
- return 1;
992
- }
993
- if (options.json === true) {
994
- console.log(JSON.stringify(policy, null, 2));
995
- return 0;
996
- }
997
- render(policy);
998
- const unavailable = policy.sources.filter((source) => source.status === "unavailable");
999
- if (unavailable.length > 0) {
1000
- console.log("");
1001
- printWarning(
1002
- `${unavailable.length} source(s) could not be consulted; the policy above is incomplete.`
1003
- );
1004
- } else if (policy.sources.length === 0) {
1005
- console.log("");
1006
- printInfo("This repository declares no policy sources; Issue Flow defaults apply.");
1007
- }
1008
- return 0;
1009
- }
1010
980
  export {
1011
- runPolicy
981
+ loadRepositoryPolicy
1012
982
  };
1013
- //# sourceMappingURL=policy-NP4ZAVF6.js.map
983
+ //# sourceMappingURL=chunk-47VIN33V.js.map
@@ -0,0 +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"]}