dw-mc 0.4.0 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bin.js +236 -131
- package/dist/bin.js.map +1 -1
- package/package.json +1 -1
package/dist/bin.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"bin.js","names":["merge","header","tint","layer","layer","asked","lines","PaintService","comments","resolve","resolve","nothing","decide","askedOf","readConfig","FactsSchema","where","block","lines","readConfig","PaintService","asJson","FindingsSchema","header","lines","readConfig","asJson","promptFor","printFlag","choicesOf","lines","readConfig","header","promptFor","effortFlag","merge","decide","readConfig","decide","decide","readConfig","readConfig","decide","readConfig","decide","readConfig","forceFlag","readConfig","lines","readConfig","PaintService","Store.layer","Header.layer","Paint.layer"],"sources":["../src/adapters/xdg.ts","../src/adapters/yaml.ts","../src/adapters/config.ts","../src/adapters/paint.ts","../src/adapters/store.ts","../src/adapters/spawner.ts","../src/adapters/git.ts","../src/adapters/picker.ts","../src/cli/table.ts","../src/domain/cleanup.ts","../src/cli/cleanup.ts","../src/adapters/gh.ts","../src/adapters/conversation.ts","../src/domain/moment.ts","../src/domain/bucket.ts","../src/domain/reference.ts","../src/cli/pr.ts","../src/cli/row.ts","../src/adapters/ci.ts","../src/domain/flaky.ts","../src/domain/quiet.ts","../src/domain/rebase.ts","../src/domain/findings.ts","../src/domain/review.ts","../src/cli/sweep.ts","../src/domain/comments.ts","../src/cli/comments.ts","../src/cli/findings.ts","../src/adapters/agent.ts","../src/adapters/claude.ts","../src/domain/fix.ts","../src/cli/fix.ts","../src/cli/init.ts","../src/domain/stamp.ts","../src/domain/merge.ts","../src/cli/merge.ts","../src/domain/pick.ts","../src/domain/rerun.ts","../src/cli/pick.ts","../src/cli/rebase.ts","../src/cli/rerun.ts","../src/domain/resolve.ts","../src/cli/resolve.ts","../src/adapters/notify.ts","../src/adapters/progress.ts","../src/domain/persona.ts","../src/cli/review.ts","../src/cli/stamp.ts","../src/cli/status.ts","../src/cli/uninstall.ts","../src/cli/cli.ts","../src/cli/header.ts","../src/cli/bin.ts"],"sourcesContent":["import { Config, Effect, Option, Path } from \"effect\"\n\n/**\n * One of `dw-mc`'s XDG base directories: `$<variable>/dw-mc` where the\n * environment sets `variable`, and `$HOME/<fallback>/dw-mc` where it does not.\n */\nexport const xdgDirectory = Effect.fnUntraced(function* (variable: string, ...fallback: ReadonlyArray<string>) {\n const path = yield* Path.Path\n const configured = yield* Config.String(variable).pipe(Config.option)\n const home = Option.isSome(configured) ? configured.value : path.join(yield* Config.String(\"HOME\"), ...fallback)\n return path.join(home, \"dw-mc\")\n})\n","import { Predicate } from \"effect\"\n\n/** A value this writer can put on paper: what `Yaml.parse` gives back. */\nexport type Value = null | boolean | number | string | ReadonlyArray<Value> | { readonly [key: string]: Value }\n\n/**\n * A word YAML reads as itself. Anything else - a glob, an empty string, a\n * branch with a space - is quoted, and the JSON escapes are a subset of the\n * YAML double-quoted ones, so `JSON.stringify` is the quoting.\n */\nconst word = /^[A-Za-z][\\w./-]*$/\n\n/** Words the YAML 1.2 core schema reads as something other than a string. */\nconst reserved = new Set([\"true\", \"false\", \"null\", \"yes\", \"no\", \"on\", \"off\", \"y\", \"n\"])\n\nconst scalar = (value: null | boolean | number | string): string => {\n if (value === null) {\n return \"null\"\n }\n if (typeof value === \"boolean\") {\n return value ? \"true\" : \"false\"\n }\n if (typeof value === \"number\") {\n if (Number.isNaN(value)) {\n return \".nan\"\n }\n if (!Number.isFinite(value)) {\n return value > 0 ? \".inf\" : \"-.inf\"\n }\n return String(value)\n }\n return word.test(value) && !reserved.has(value.toLowerCase()) ? value : JSON.stringify(value)\n}\n\nconst isMapping = (value: Value): value is { readonly [key: string]: Value } => Predicate.isObject(value)\n\n/** `Array.isArray` widens to `any[]`, which leaves the union unnarrowed. */\nconst isSequence = (value: Value): value is ReadonlyArray<Value> => Array.isArray(value)\n\nconst pad = (depth: number): string => \" \".repeat(depth)\n\n/**\n * Writes one entry, where `prefix` is everything up to the value: a mapping's\n * `key:` or a sequence's `-`. `depth` is where this entry's children go, which\n * a sequence item sets one deeper than the dash it hangs from.\n */\nconst writeEntry = (prefix: string, value: Value, depth: number, out: Array<string>): void => {\n if (isSequence(value)) {\n if (value.length === 0) {\n out.push(`${prefix} []`)\n return\n }\n out.push(prefix)\n writeSequence(value, depth, out)\n return\n }\n if (isMapping(value)) {\n const entries = Object.entries(value)\n if (entries.length === 0) {\n out.push(`${prefix} {}`)\n return\n }\n out.push(prefix)\n writeMapping(entries, depth, out)\n return\n }\n out.push(`${prefix} ${scalar(value)}`)\n}\n\nconst writeMapping = (entries: ReadonlyArray<readonly [string, Value]>, depth: number, out: Array<string>): void => {\n for (const [key, value] of entries) {\n writeEntry(`${pad(depth)}${scalar(key)}:`, value, depth + 1, out)\n }\n}\n\nconst writeSequence = (items: ReadonlyArray<Value>, depth: number, out: Array<string>): void => {\n for (const item of items) {\n const entries = isMapping(item) ? Object.entries(item) : []\n const [first, ...rest] = entries\n if (first === undefined) {\n writeEntry(`${pad(depth)}-`, item, depth + 1, out)\n continue\n }\n writeEntry(`${pad(depth)}- ${scalar(first[0])}:`, first[1], depth + 2, out)\n writeMapping(rest, depth + 1, out)\n }\n}\n\n/**\n * Writes one YAML document, in the order the keys were built in.\n *\n * Effect parses YAML but does not write it, and the configuration file is one\n * the tool rewrites on every `init`. Collections are written as blocks, so the\n * file stays diffable and editable by hand.\n */\nexport const encodeYaml = (value: Value): string => {\n const out: Array<string> = []\n if (isSequence(value)) {\n if (value.length === 0) {\n return \"[]\\n\"\n }\n writeSequence(value, 0, out)\n } else if (isMapping(value)) {\n const entries = Object.entries(value)\n if (entries.length === 0) {\n return \"{}\\n\"\n }\n writeMapping(entries, 0, out)\n } else {\n out.push(scalar(value))\n }\n return `${out.join(\"\\n\")}\\n`\n}\n","import type { Config, Types } from \"effect\"\nimport { Context, Effect, FileSystem, Layer, Option, Path, PlatformError, Schema } from \"effect\"\nimport { Yaml } from \"effect/unstable/encoding\"\nimport { KeyValueStore } from \"effect/unstable/persistence\"\n\nimport { xdgDirectory } from \"#adapters/xdg.ts\"\nimport type { Value } from \"#adapters/yaml.ts\"\nimport { encodeYaml } from \"#adapters/yaml.ts\"\n\n/**\n * How much a review run spends, in the words the slash command takes.\n *\n * The set is Claude Code's and not this tool's, so it is wider than the three\n * words a review used to be held to: a run that would be worth `max` is one I\n * should be able to ask for without spelling the whole command out.\n */\nexport const Effort = Schema.Literals([\"low\", \"medium\", \"high\", \"xhigh\", \"max\"])\nexport type Effort = typeof Effort.Type\n\n/** How much a finding weighs. */\nexport const Severity = Schema.Literals([\"error\", \"warning\", \"info\"])\nexport type Severity = typeof Severity.Type\n\n/**\n * What one section of the file may say. Every key is optional: what the file\n * leaves out is inherited rather than reset, so `defaults` and a repository's\n * overrides are the same shape.\n */\nconst SettingsPatch = Schema.Struct({\n base: Schema.optionalKey(Schema.NullOr(Schema.String)),\n review: Schema.optionalKey(\n Schema.Struct({\n command: Schema.optionalKey(Schema.NullOr(Schema.String)),\n effort: Schema.optionalKey(Schema.NullOr(Effort)),\n prompt: Schema.optionalKey(Schema.NullOr(Schema.String)),\n model: Schema.optionalKey(Schema.NullOr(Schema.String)),\n docs_only: Schema.optionalKey(Schema.Array(Schema.String))\n })\n ),\n ci: Schema.optionalKey(\n Schema.Struct({\n ignore: Schema.optionalKey(Schema.Array(Schema.String)),\n flaky_patterns: Schema.optionalKey(Schema.Array(Schema.String))\n })\n ),\n fix: Schema.optionalKey(\n Schema.Struct({\n commits: Schema.optionalKey(Schema.Boolean)\n })\n ),\n rebase: Schema.optionalKey(\n Schema.Struct({\n enabled: Schema.optionalKey(Schema.Boolean)\n })\n ),\n stamp: Schema.optionalKey(\n Schema.Struct({\n blocks_on: Schema.optionalKey(Severity)\n })\n )\n})\nexport type SettingsPatch = typeof SettingsPatch.Type\n\n/** How this machine starts Claude Code, where it does not start `claude` itself. */\nconst LauncherPatch = Schema.Struct({\n // An argv list and never a shell string: a shell string needs `sh -c` in\n // front of it, and that extra process sits in the terminal's foreground\n // group, where it takes the inherited standard input and the Ctrl-C of a fix\n // session with it.\n command: Schema.optionalKey(\n Schema.Array(Schema.String).pipe(\n Schema.check(Schema.isMinLength(1, { message: \"Expected the launcher command to name a program\" }))\n )\n ),\n fix_args: Schema.optionalKey(Schema.Array(Schema.String))\n})\n\n/** A repository, as `gh` spells it: `owner/name`. */\nexport const Repo = Schema.String.pipe(\n Schema.check(Schema.isPattern(/^[^\\s/]+\\/[^\\s/]+$/, { message: \"Expected a repository as owner/name\" }))\n)\n\n/** The whole configuration file: global defaults and per-repository overrides. */\nexport const ConfigFile = Schema.Struct({\n launcher: Schema.optionalKey(LauncherPatch),\n defaults: Schema.optionalKey(SettingsPatch),\n repos: Schema.optionalKey(Schema.Record(Repo, SettingsPatch))\n})\nexport type ConfigFile = typeof ConfigFile.Type\n\ntype Section<K extends keyof SettingsPatch> = Required<NonNullable<SettingsPatch[K]>>\n\n/** What one repository's settings come to once the file has been resolved. */\nexport interface Settings {\n readonly base: string | null\n readonly review: Section<\"review\">\n readonly ci: Section<\"ci\">\n readonly fix: Section<\"fix\">\n readonly rebase: Section<\"rebase\">\n readonly stamp: Section<\"stamp\">\n}\n\n/** What every setting is worth before the file says anything. */\nexport const builtIn: Settings = {\n base: null,\n review: {\n command: \"/code-review\",\n effort: \"low\",\n prompt: null,\n model: null,\n docs_only: [\"**/*.md\", \"docs/**\"]\n },\n ci: { ignore: [], flaky_patterns: [] },\n fix: { commits: false },\n rebase: { enabled: false },\n stamp: { blocks_on: \"error\" }\n}\n\n/** What this machine spawns Claude Code with, once the file has been read. */\nexport interface Launcher {\n /** The program, then the arguments it takes before mission control's own. */\n readonly command: readonly [string, ...Array<string>]\n /** The flags only a fix session gets, the one run that is no review run. */\n readonly fix_args: ReadonlyArray<string>\n}\n\n/** `claude` itself, which is what a machine that spawns it directly needs. */\nexport const builtInLauncher: Launcher = { command: [\"claude\"], fix_args: [] }\n\n/**\n * The patch's value where it has one, the inherited value otherwise. A key the\n * file spells out counts even when it says `null`, which is how a repository\n * resets a global default.\n */\nconst over = <A>(patch: A | undefined, inherited: A): A => (patch === undefined ? inherited : patch)\n\nconst apply = (settings: Settings, patch: SettingsPatch | undefined): Settings =>\n patch === undefined\n ? settings\n : {\n base: over(patch.base, settings.base),\n review: {\n command: over(patch.review?.command, settings.review.command),\n effort: over(patch.review?.effort, settings.review.effort),\n prompt: over(patch.review?.prompt, settings.review.prompt),\n model: over(patch.review?.model, settings.review.model),\n docs_only: over(patch.review?.docs_only, settings.review.docs_only)\n },\n ci: {\n ignore: over(patch.ci?.ignore, settings.ci.ignore),\n flaky_patterns: over(patch.ci?.flaky_patterns, settings.ci.flaky_patterns)\n },\n fix: { commits: over(patch.fix?.commits, settings.fix.commits) },\n rebase: { enabled: over(patch.rebase?.enabled, settings.rebase.enabled) },\n stamp: { blocks_on: over(patch.stamp?.blocks_on, settings.stamp.blocks_on) }\n }\n\n/**\n * `delta` over `patch`, keeping every key `delta` does not mention.\n *\n * Sections merge key by key rather than being replaced, which is what lets a\n * second `init` change one of a repository's settings and lose none of the rest.\n */\nexport const merge = (patch: SettingsPatch, delta: SettingsPatch): SettingsPatch => {\n const merged: Types.Mutable<SettingsPatch> = { ...patch, ...delta }\n if (patch.review !== undefined && delta.review !== undefined) {\n merged.review = { ...patch.review, ...delta.review }\n }\n if (patch.ci !== undefined && delta.ci !== undefined) {\n merged.ci = { ...patch.ci, ...delta.ci }\n }\n if (patch.fix !== undefined && delta.fix !== undefined) {\n merged.fix = { ...patch.fix, ...delta.fix }\n }\n if (patch.rebase !== undefined && delta.rebase !== undefined) {\n merged.rebase = { ...patch.rebase, ...delta.rebase }\n }\n if (patch.stamp !== undefined && delta.stamp !== undefined) {\n merged.stamp = { ...patch.stamp, ...delta.stamp }\n }\n return merged\n}\n\n/** Whether a patch decides anything at all. */\nconst decidesNothing = (patch: SettingsPatch): boolean => Object.keys(patch).length === 0\n\n/**\n * `file` with `defaults` as its global defaults, and with the section left out\n * where those defaults decide nothing, so an empty `defaults:` is never written.\n */\nexport const withDefaults = (file: ConfigFile, defaults: SettingsPatch): ConfigFile =>\n decidesNothing(defaults) ? file : { ...file, defaults }\n\n/** `file` with `patch` over `repo`'s settings, registering `repo` when it is new. */\nexport const withRepo = (file: ConfigFile, repo: string, patch: SettingsPatch): ConfigFile => ({\n ...file,\n repos: { ...file.repos, [repo]: merge(file.repos?.[repo] ?? {}, patch) }\n})\n\n/**\n * What this machine starts Claude Code with: the file's launcher over `claude`.\n *\n * It is no repository's business. What spawns the agent CLI is a fact of the\n * machine, which is why it sits beside `defaults` rather than inside it.\n */\nexport const launcherOf = (file: ConfigFile): Launcher => {\n const [program = builtInLauncher.command[0], ...prefix] = file.launcher?.command ?? []\n return {\n command: [program, ...prefix],\n fix_args: file.launcher?.fix_args ?? builtInLauncher.fix_args\n }\n}\n\n/** What `repo` is worth: its own overrides over the global defaults. */\nexport const settingsFor = (file: ConfigFile, repo: string): Settings =>\n apply(apply(builtIn, file.defaults), file.repos?.[repo])\n\n/**\n * Where the configuration lives: `$XDG_CONFIG_HOME/dw-mc`, or\n * `$HOME/.config/dw-mc` when XDG says nothing.\n */\nexport const configDirectory: Effect.Effect<string, Config.ConfigError, Path.Path> = xdgDirectory(\n \"XDG_CONFIG_HOME\",\n \".config\"\n)\n\nconst fileName = \"config.yaml\"\n\n/** The one file, in the one place, that I can read, edit and keep in my dotfiles. */\nexport const configPath: Effect.Effect<string, Config.ConfigError, Path.Path> = Effect.gen(function* () {\n const path = yield* Path.Path\n const directory = yield* configDirectory\n return path.join(directory, fileName)\n}).pipe(Effect.withSpan(\"config.configPath\"))\n\nconst service = Effect.gen(function* () {\n const store = yield* KeyValueStore.KeyValueStore\n const path = yield* configPath\n return { path, store }\n})\n\nconst onDisk = Layer.unwrap(Effect.map(configDirectory, (directory) => KeyValueStore.layerFileSystem(directory)))\n\n/**\n * The configuration file, behind the key/value seam.\n *\n * A file store over the configuration directory writes `config.yaml` at exactly\n * the path the design promises, so the seam costs the file nothing. Its store is\n * built fresh, so it is never the one the state directory is using.\n */\nexport class ConfigStore extends Context.Service<\n ConfigStore,\n {\n readonly path: string\n readonly store: KeyValueStore.KeyValueStore\n }\n>()(\"dw-mc/config/ConfigStore\") {\n /** The configuration file on disk. */\n static readonly layer: Layer.Layer<\n ConfigStore,\n Config.ConfigError | PlatformError.PlatformError,\n FileSystem.FileSystem | Path.Path\n > = Layer.effect(ConfigStore, service).pipe(Layer.provide(Layer.fresh(onDisk)))\n\n /** A configuration file that lives only as long as the test that builds it. */\n static readonly layerTest: Layer.Layer<ConfigStore, Config.ConfigError, Path.Path> = Layer.effect(\n ConfigStore,\n service\n ).pipe(Layer.provide(Layer.fresh(KeyValueStore.layerMemory)))\n}\n\n/** A configuration file that is there but is not configuration. */\nexport class ConfigMalformed extends Schema.TaggedError<ConfigMalformed>()(\"ConfigMalformed\", {\n path: Schema.String,\n reason: Schema.String\n}) {\n override get message(): string {\n return (\n `${this.path} is not valid dw-mc configuration: ${this.reason}\\n` +\n `Fix the file, or delete it and run 'dw-mc init' again.`\n )\n }\n}\n\nconst reasonOf = (cause: unknown): string => (cause instanceof Error ? cause.message : String(cause))\n\n/** The keys an earlier version had, read off a file loosely enough to find them. */\nconst LegacySection = Schema.Struct({\n review: Schema.optionalKey(\n Schema.Struct({\n runners: Schema.optionalKey(Schema.Unknown),\n skill: Schema.optionalKey(Schema.Unknown),\n path_instructions: Schema.optionalKey(Schema.Unknown)\n })\n ),\n stamp: Schema.optionalKey(Schema.Struct({ supporting_blocks: Schema.optionalKey(Schema.Unknown) }))\n})\n\nconst Legacy = Schema.Struct({\n launcher: Schema.optionalKey(Schema.Struct({ codex: Schema.optionalKey(Schema.Unknown) })),\n defaults: Schema.optionalKey(LegacySection),\n repos: Schema.optionalKey(Schema.Record(Schema.String, LegacySection))\n})\n\nconst asLegacy = Schema.decodeUnknownOption(Legacy)\n\n/**\n * What a file from an earlier version says, and what to do about each of it.\n *\n * The excess-property error names a key and stops there, which is enough for a\n * key that is simply gone and not enough for one that moved: `review.skill` is\n * `review.prompt` now, and a file quietly stripped of it is a review brief lost.\n * This is here to be deleted once no file has those keys left.\n */\nconst legacyIn = (decided: unknown): string | null => {\n const legacy = asLegacy(decided)\n if (Option.isNone(legacy)) {\n return null\n }\n const sections = [legacy.value.defaults, ...Object.values(legacy.value.repos ?? {})]\n const spelled = (says: (section: typeof LegacySection.Type) => unknown): boolean =>\n sections.some((section) => section !== undefined && says(section) !== undefined)\n\n const said = [\n legacy.value.launcher?.codex === undefined ? null : \"launcher.codex is gone: reviews run on Claude Code alone.\",\n spelled((section) => section.review?.runners)\n ? \"review.runners is gone: a head carries one review run, which review.command configures.\"\n : null,\n spelled((section) => section.review?.skill)\n ? \"review.skill is review.prompt now, unchanged in what it does - move the text across rather than losing it.\"\n : null,\n spelled((section) => section.review?.path_instructions)\n ? \"review.path_instructions is gone: nothing ever read it.\"\n : null,\n spelled((section) => section.stamp?.supporting_blocks)\n ? \"stamp.supporting_blocks is gone: there is no second opinion to let through.\"\n : null\n ].filter((sentence) => sentence !== null)\n\n return said.length === 0 ? null : `it names keys this version does not have.\\n${said.join(\"\\n\")}`\n}\n\n/**\n * The configuration file, or `None` when this machine has none yet.\n *\n * A file that is there and is wrong stops the caller: an unreadable key, a\n * value of the wrong type and a misspelled key all fail here rather than\n * turning into a default that quietly means something else.\n */\nexport const read = Effect.gen(function* () {\n const config = yield* ConfigStore\n const raw = yield* config.store.get(fileName)\n if (raw === undefined) {\n return Option.none<ConfigFile>()\n }\n\n const malformed = (reason: string) => new ConfigMalformed({ path: config.path, reason })\n const parsed = yield* Effect.try({\n try: () => Yaml.parse(raw),\n catch: (cause) => malformed(reasonOf(cause))\n })\n // An empty document parses to null: the file is there and decides nothing.\n const decided: unknown = parsed ?? {}\n\n const legacy = legacyIn(decided)\n if (legacy !== null) {\n return yield* malformed(legacy)\n }\n\n return Option.some(\n yield* Schema.decodeUnknownEffect(ConfigFile)(decided, {\n onExcessProperty: \"error\",\n errors: \"all\"\n }).pipe(Effect.mapError((error) => malformed(error.message)))\n )\n}).pipe(Effect.withSpan(\"config.read\"))\n\nconst mapping = (entries: ReadonlyArray<readonly [string, Value | undefined]>): { readonly [key: string]: Value } => {\n const out: Record<string, Value> = {}\n for (const [key, value] of entries) {\n if (value !== undefined) {\n out[key] = value\n }\n }\n return out\n}\n\nconst settingsDocument = (patch: SettingsPatch): Value =>\n mapping([\n [\"base\", patch.base],\n [\n \"review\",\n patch.review === undefined\n ? undefined\n : mapping([\n [\"command\", patch.review.command],\n [\"effort\", patch.review.effort],\n [\"prompt\", patch.review.prompt],\n [\"model\", patch.review.model],\n [\"docs_only\", patch.review.docs_only]\n ])\n ],\n [\n \"ci\",\n patch.ci === undefined\n ? undefined\n : mapping([\n [\"ignore\", patch.ci.ignore],\n [\"flaky_patterns\", patch.ci.flaky_patterns]\n ])\n ],\n [\"fix\", patch.fix === undefined ? undefined : mapping([[\"commits\", patch.fix.commits]])],\n [\"rebase\", patch.rebase === undefined ? undefined : mapping([[\"enabled\", patch.rebase.enabled]])],\n [\"stamp\", patch.stamp === undefined ? undefined : mapping([[\"blocks_on\", patch.stamp.blocks_on]])]\n ])\n\n/**\n * The file as a YAML document, in the order of the schema.\n *\n * Writing the keys in a fixed order rather than the order they were built in\n * keeps the file stable across runs, so a rewrite shows only what changed.\n */\nconst fileDocument = (file: ConfigFile): Value =>\n mapping([\n [\n \"launcher\",\n file.launcher === undefined\n ? undefined\n : mapping([\n [\"command\", file.launcher.command],\n [\"fix_args\", file.launcher.fix_args]\n ])\n ],\n [\"defaults\", file.defaults === undefined ? undefined : settingsDocument(file.defaults)],\n [\n \"repos\",\n file.repos === undefined\n ? undefined\n : mapping(Object.entries(file.repos).map(([name, patch]) => [name, settingsDocument(patch)] as const))\n ]\n ])\n\nconst header = \"# dw-mc configuration. 'dw-mc init' rewrites this file and keeps no comments.\"\n\n/**\n * The file as it would be written.\n *\n * Exposed so a caller can tell whether writing would decide anything\n * differently, and leave the file alone when it would not.\n */\nexport const encode = (file: ConfigFile): string => `${header}\\n${encodeYaml(fileDocument(file))}`\n\n/** Writes the whole file, replacing what was there. */\nexport const write = Effect.fn(\"config.write\")(function* (file: ConfigFile) {\n const config = yield* ConfigStore\n yield* config.store.set(fileName, encode(file))\n})\n","import { Config, Context, Effect, Layer, Option, Stdio } from \"effect\"\n\n/**\n * The ink the screen is written in.\n *\n * Eight colours and two weights, which is what every terminal has had since\n * before any of them had a theme. Asking for one of the eight rather than for a\n * shade means the screen is drawn in my terminal's own palette, so it keeps its\n * contrast whatever I set that palette to.\n *\n * A link is ink as well, and the one piece of it that is not a colour: it says\n * where a word leads rather than what it is worth, so it withholds nothing from\n * the marker and takes no colour of its own.\n *\n * What each colour is worth is not decided here. This is the ink; which word\n * takes which colour belongs to whatever is doing the writing.\n */\nexport interface Paint {\n readonly red: (text: string) => string\n readonly yellow: (text: string) => string\n readonly green: (text: string) => string\n readonly cyan: (text: string) => string\n readonly bold: (text: string) => string\n readonly dim: (text: string) => string\n /** `text`, carrying `url` for the terminal to open. */\n readonly link: (text: string, url: string) => string\n}\n\nconst same = (text: string): string => text\n\n/** The same screen, written where nothing is watching in colour. */\nexport const plain: Paint = { red: same, yellow: same, green: same, cyan: same, bold: same, dim: same, link: same }\n\nconst tint =\n (code: string) =>\n (text: string): string =>\n `\u001b[${code}m${text}\u001b[0m`\n\n/** What a colour costs a line: the escape that opens it and the one that closes it. */\nexport const ink = 9\n\n/**\n * A word a terminal opens: OSC 8, which wraps the text in the URL rather than\n * printing it.\n *\n * The text on the screen is unchanged, so a row reads the same where the\n * terminal knows the sequence and where it does not, and a pipe never sees it\n * at all - the ink below is chosen once, from whether a terminal is watching.\n */\nconst opens = (text: string, url: string): string => `\\x1b]8;;${url}\\x1b\\\\${text}\\x1b]8;;\\x1b\\\\`\n\n/** The screen written in colour. */\nexport const coloured: Paint = {\n red: tint(\"31\"),\n yellow: tint(\"33\"),\n green: tint(\"32\"),\n cyan: tint(\"36\"),\n bold: tint(\"1\"),\n dim: tint(\"2\"),\n link: opens\n}\n\n/** The ink for a screen that may or may not be watched. */\nexport const paintFor = (colors: boolean): Paint => (colors ? coloured : plain)\n\n/**\n * Whether the screen may be coloured: a terminal is watching and `NO_COLOR` is\n * unset.\n *\n * It is asked of the services rather than of `process`, so a test can put\n * either answer in, and an empty `NO_COLOR` reads as unset on both sides - the\n * configuration provider drops it, and `CliOutput.defaultFormatter` takes it\n * for the falsy value it is.\n */\nexport const screened: Effect.Effect<boolean, Config.ConfigError, Stdio.Stdio> = Effect.gen(function* () {\n const stdio = yield* Stdio.Stdio\n const noColor = yield* Config.String(\"NO_COLOR\").pipe(Config.option)\n return (yield* stdio.stdoutIsTerminal) && Option.isNone(noColor)\n})\n\n/**\n * The ink every command writes with.\n *\n * It defaults to no colour, so anything that provides nothing - a test, a\n * command reached some way I have not thought of - prints the text and only the\n * text. Colour arrives when the entry point builds the layer below, which is\n * the one place that knows what stdout is.\n */\nexport const Paint: Context.Reference<Paint> = Context.Reference(\"dw-mc/Paint\", { defaultValue: (): Paint => plain })\n\n/** The ink the machine deserves, as the layer the entry point provides. */\nexport const layer: Layer.Layer<never, Config.ConfigError, Stdio.Stdio> = Layer.effect(\n Paint,\n Effect.map(screened, paintFor)\n)\n","import type { Config } from \"effect\"\nimport { ByteSize, Effect, FileSystem, Layer, Path, Schema } from \"effect\"\nimport { KeyValueStore } from \"effect/unstable/persistence\"\n\nimport { xdgDirectory } from \"#adapters/xdg.ts\"\n\n/**\n * Where the tool keeps its state: `$XDG_STATE_HOME/dw-mc`, or\n * `$HOME/.local/state/dw-mc` when XDG says nothing.\n */\nexport const stateDirectory: Effect.Effect<string, Config.ConfigError, Path.Path> = xdgDirectory(\n \"XDG_STATE_HOME\",\n \".local\",\n \"state\"\n)\n\n/**\n * How the state directory names one pull request, whichever namespace it is in.\n *\n * The facts a sweep wrote and the stamp I withdrew are the same pull request\n * under two namespaces, so the key format is spelled once here rather than in\n * each of them.\n */\nexport const prKey = (repo: string, number: number): string => `${repo}#${number}`\n\n/**\n * A schema-typed view of the store, with every key under `namespace`.\n *\n * Tracked PRs, review runs and stamps share one directory, so the namespace is\n * what keeps them apart. Note that `clear`, `size` and `isEmpty` are not\n * namespaced - they still see the whole store.\n */\nexport const storeFor = Effect.fn(\"store.storeFor\")(function* <S extends Schema.Constraint>(\n namespace: string,\n schema: S\n) {\n const store = yield* KeyValueStore.KeyValueStore\n return KeyValueStore.toSchemaStore(KeyValueStore.prefix(store, `${namespace}/`), schema)\n})\n\n/**\n * The same namespace, kept as text rather than as JSON.\n *\n * A review run's report is Markdown, and the state directory is meant to hold\n * what I can open: through a schema store the same report would be one long\n * JSON string with its newlines escaped.\n */\nexport const textStoreFor = Effect.fn(\"store.textStoreFor\")(function* (namespace: string) {\n const store = yield* KeyValueStore.KeyValueStore\n return KeyValueStore.prefix(store, `${namespace}/`)\n})\n\n/** The state directory on disk. */\nexport const layer = Layer.unwrap(Effect.map(stateDirectory, (directory) => KeyValueStore.layerFileSystem(directory)))\n\n/** A store that lives only as long as the test that builds it. */\nexport const layerTest: Layer.Layer<KeyValueStore.KeyValueStore> = KeyValueStore.layerMemory\n\n/** The three directories the tool cuts a checkout into, under the state directory. */\nexport const cuts = [\"worktrees\", \"fixes\", \"rebases\"] as const\n\n/** Which of them one checkout sits in, which is also what it was cut for. */\nexport type Cut = (typeof cuts)[number]\n\n/** What a standing worktree is for, which names its branch and the directory it is cut in. */\nexport type Session = \"fix\" | \"rebase\"\n\n/** Where each kind of session's worktrees live under the state directory. */\nexport const under = { fix: \"fixes\", rebase: \"rebases\" } as const\n\n/**\n * Which session a checkout belongs to, and nothing where it belongs to none.\n *\n * `worktrees` is the review run's own, cut and taken down inside one run, so it\n * stands for no session at all: that is the difference every command that\n * removes something turns on.\n */\nexport const sessionOf = (cut: Cut): Session | undefined =>\n (({ fixes: \"fix\", rebases: \"rebase\", worktrees: undefined }) as const)[cut]\n\n/** Where the bare clones sit, under the state directory. */\nexport const clonesIn = \"repos\"\n\n/** A directory under the state directory, and what everything below it weighs. */\nexport interface Weighed {\n readonly directory: string\n readonly size: ByteSize.ByteSize\n}\n\n/** One repository's bare clone. */\nexport interface Clone extends Weighed {\n readonly repo: string\n}\n\n/** One checkout the tool cut, named by the pull request it stands on. */\nexport interface Cutting extends Weighed {\n readonly cut: Cut\n readonly repo: string\n readonly number: number\n}\n\n/**\n * Everything the state directory holds, read as directories rather than as\n * keys.\n *\n * The key/value seam is the wrong window for this: a store answers about the\n * keys of one namespace, and what a cleanup is about is the clones and the\n * checkouts, which no namespace ever sees. So this reads the directory itself,\n * and `records` is the one line it has to say about the keys - their number and\n * their weight together, because which pull request a key belongs to is #58's\n * question and not this one's.\n */\nexport interface Inventory {\n readonly directory: string\n readonly clones: ReadonlyArray<Clone>\n readonly cuttings: ReadonlyArray<Cutting>\n readonly records: { readonly keys: number; readonly size: ByteSize.ByteSize }\n}\n\n/** What a directory holds, or nothing at all where it is not there. */\nconst entriesOf = Effect.fnUntraced(function* (directory: string) {\n const fs = yield* FileSystem.FileSystem\n return yield* Effect.orElseSucceed(fs.readDirectory(directory), (): ReadonlyArray<string> => [])\n})\n\n/**\n * What `directory` and everything below it weighs.\n *\n * A file that is gone by the time it is asked about weighs nothing rather than\n * failing the walk: the directory is being read while the tool may be writing\n * to it, and a size on a screen is worth less than the listing it sits in.\n */\nexport const weigh = Effect.fn(\"store.weigh\")(function* (directory: string) {\n const fs = yield* FileSystem.FileSystem\n const path = yield* Path.Path\n const entries = yield* Effect.orElseSucceed(\n fs.readDirectory(directory, { recursive: true }),\n (): ReadonlyArray<string> => []\n )\n const sizes = yield* Effect.forEach(\n entries,\n (entry) =>\n Effect.orElseSucceed(\n Effect.map(fs.stat(path.join(directory, entry)), (info) => ByteSize.toBigInt(info.size)),\n () => BigInt(0)\n ),\n { concurrency: 16 }\n )\n return ByteSize.bytes(sizes.reduce((total, size) => total + size, BigInt(0)))\n})\n\n/** The bare clones, named by the `owner/repo` the two directory levels spell. */\nconst clonesOf = Effect.fnUntraced(function* (state: string) {\n const path = yield* Path.Path\n const root = path.join(state, clonesIn)\n const clones: Array<Clone> = []\n\n for (const owner of yield* entriesOf(root)) {\n for (const name of yield* entriesOf(path.join(root, owner))) {\n if (!name.endsWith(\".git\")) {\n continue\n }\n const directory = path.join(root, owner, name)\n clones.push({ repo: `${owner}/${name.slice(0, -\".git\".length)}`, directory, size: yield* weigh(directory) })\n }\n }\n return clones\n})\n\n/** The checkouts, named by the `owner/repo/number` the three directory levels spell. */\nconst cuttingsOf = Effect.fnUntraced(function* (state: string) {\n const path = yield* Path.Path\n const cuttings: Array<Cutting> = []\n\n for (const cut of cuts) {\n for (const owner of yield* entriesOf(path.join(state, cut))) {\n for (const name of yield* entriesOf(path.join(state, cut, owner))) {\n for (const number of yield* entriesOf(path.join(state, cut, owner, name))) {\n if (!/^\\d+$/.test(number)) {\n continue\n }\n const directory = path.join(state, cut, owner, name, number)\n cuttings.push({\n cut,\n repo: `${owner}/${name}`,\n number: Number(number),\n directory,\n size: yield* weigh(directory)\n })\n }\n }\n }\n }\n return cuttings\n})\n\n/** Everything the state directory holds, in one pass over the disk. */\nexport const inventory: Effect.Effect<Inventory, Config.ConfigError, FileSystem.FileSystem | Path.Path> = Effect.gen(\n function* () {\n const fs = yield* FileSystem.FileSystem\n const path = yield* Path.Path\n const directory = yield* stateDirectory\n\n const directories = new Set<string>([clonesIn, ...cuts])\n const top = yield* entriesOf(directory)\n const keys = top.filter((entry) => !directories.has(entry))\n const sizes = yield* Effect.forEach(\n keys,\n (entry) =>\n Effect.orElseSucceed(\n Effect.map(fs.stat(path.join(directory, entry)), (info) => ByteSize.toBigInt(info.size)),\n () => BigInt(0)\n ),\n { concurrency: 16 }\n )\n\n return {\n directory,\n clones: yield* clonesOf(directory),\n cuttings: yield* cuttingsOf(directory),\n records: { keys: keys.length, size: ByteSize.bytes(sizes.reduce((a, b) => a + b, BigInt(0))) }\n }\n }\n).pipe(Effect.withSpan(\"store.inventory\"))\n\n/**\n * Takes a directory and everything below it off the disk.\n *\n * A path that is not there is the ordinary case rather than a failure: two\n * commands may ask for the same thing gone, and the second one is right about\n * the outcome.\n */\nexport const discard = Effect.fn(\"store.discard\")(function* (directory: string) {\n const fs = yield* FileSystem.FileSystem\n yield* fs.remove(directory, { recursive: true, force: true })\n})\n\n/**\n * Removes what discarding left empty above `directory`, and stops at `upTo`.\n *\n * The layout spells an owner and a repository as directories, so taking one\n * clone away leaves the owner's directory standing with nothing in it. It is a\n * few bytes, and it is also a listing that says the tool still keeps something\n * there when it does not.\n *\n * A directory with anything left in it ends the walk rather than being emptied:\n * what is beside the thing removed belongs to something else.\n */\nexport const tidy = Effect.fn(\"store.tidy\")(function* (directory: string, upTo: string) {\n const fs = yield* FileSystem.FileSystem\n const path = yield* Path.Path\n\n let at = path.dirname(directory)\n while (at !== upTo && at.startsWith(upTo)) {\n const entries = yield* Effect.orElseSucceed(fs.readDirectory(at), (): ReadonlyArray<string> => [\"stop\"])\n if (entries.length > 0) {\n return\n }\n // Recursive over a directory the line above found empty, because that is\n // what removing a directory at all takes; it can still take nothing away.\n yield* Effect.ignore(fs.remove(at, { recursive: true }))\n at = path.dirname(at)\n }\n})\n","import { Effect, Layer, Schema, Sink, Stream } from \"effect\"\nimport { ChildProcess, ChildProcessSpawner } from \"effect/unstable/process\"\n\nconst encoder = new TextEncoder()\n\n/** A program that ran but ended badly. */\nexport class CommandFailed extends Schema.TaggedError<CommandFailed>()(\"CommandFailed\", {\n command: Schema.String,\n args: Schema.Array(Schema.String),\n exitCode: Schema.Int,\n stderr: Schema.String\n}) {\n override get message(): string {\n return `${[this.command, ...this.args].join(\" \")} exited ${this.exitCode}: ${this.stderr}`\n }\n}\n\n/**\n * Runs a program to completion and returns its trimmed standard output.\n *\n * The spawner's own `string` collects stdout without ever reading the exit\n * code, so a program that failed would come back as an empty success. This\n * reads both, and a non-zero exit is a failure carrying whatever the program\n * said on stderr. The two output streams drain together, because draining one\n * to the end first can block a program that is still writing to the other.\n */\nexport const capture = Effect.fn(\"spawner.capture\")(function* (command: string, args: ReadonlyArray<string>) {\n const spawner = yield* ChildProcessSpawner.ChildProcessSpawner\n const handle = yield* spawner.spawn(ChildProcess.make(command, args))\n\n const [stdout, stderr] = yield* Effect.all(\n [Stream.mkString(Stream.decodeText(handle.stdout)), Stream.mkString(Stream.decodeText(handle.stderr))],\n { concurrency: 2 }\n )\n const exitCode = yield* handle.exitCode\n\n if (exitCode !== 0) {\n return yield* new CommandFailed({ command, args, exitCode, stderr: stderr.trim() })\n }\n return stdout.trim()\n}, Effect.scoped)\n\n/**\n * A `ChildProcessSpawner` built from a fake spawn function, for tests.\n *\n * `ChildProcessSpawner.make` derives `string`, `lines`, `exitCode` and the\n * streams from the spawn function alone, which is how the Node spawner is built\n * too, so one fake spawn gives the whole service.\n */\nexport const layerFake = (\n spawn: ChildProcessSpawner.ChildProcessSpawner[\"Service\"][\"spawn\"]\n): Layer.Layer<ChildProcessSpawner.ChildProcessSpawner> =>\n Layer.succeed(ChildProcessSpawner.ChildProcessSpawner, ChildProcessSpawner.make(spawn))\n\n/**\n * A finished process for a fake spawn function to return.\n *\n * `ChildProcessHandle` carries a private brand, so an object literal cannot\n * stand in for one.\n */\nexport const fakeHandle = (options: {\n readonly stdout?: string | undefined\n readonly stderr?: string | undefined\n readonly exitCode?: number | undefined\n readonly pid?: number | undefined\n}): ChildProcessSpawner.ChildProcessHandle => {\n const stdout = Stream.succeed(encoder.encode(options.stdout ?? \"\"))\n const stderr = Stream.succeed(encoder.encode(options.stderr ?? \"\"))\n return ChildProcessSpawner.makeHandle({\n pid: ChildProcessSpawner.ProcessId(options.pid ?? 1),\n exitCode: Effect.succeed(ChildProcessSpawner.ExitCode(options.exitCode ?? 0)),\n isRunning: Effect.succeed(false),\n kill: () => Effect.void,\n stdin: Sink.drain,\n stdout,\n stderr,\n all: Stream.merge(stdout, stderr),\n getInputFd: () => Sink.drain,\n getOutputFd: () => Stream.empty,\n unref: Effect.succeed(Effect.void)\n })\n}\n","import { Effect, Path, Result, Schema } from \"effect\"\n\nimport { capture } from \"#adapters/spawner.ts\"\nimport type { Cut, Session } from \"#adapters/store.ts\"\nimport { clonesIn, stateDirectory, under } from \"#adapters/store.ts\"\n\n/** A `git` command that ran and refused, or would not run at all. */\nexport class GitFailed extends Schema.TaggedError<GitFailed>()(\"GitFailed\", {\n args: Schema.Array(Schema.String),\n detail: Schema.String\n}) {\n override get message(): string {\n return `git ${this.args.join(\" \")} failed: ${this.detail}`\n }\n}\n\n/** One `git` command, with both ways it can go wrong in our words. */\nconst git = (args: ReadonlyArray<string>) =>\n capture(\"git\", args).pipe(\n Effect.catchTags({\n PlatformError: (error) => Effect.fail(new GitFailed({ args, detail: error.message })),\n CommandFailed: (error) => Effect.fail(new GitFailed({ args, detail: error.stderr }))\n })\n )\n\n/** A checkout cut for one run, and the commit it stands on. */\nexport interface Worktree {\n readonly directory: string\n readonly head: string\n}\n\n/** A fix worktree that still holds work of mine, which nothing may cut away. */\nexport class WorktreeHeld extends Schema.TaggedError<WorktreeHeld>()(\"WorktreeHeld\", {\n directory: Schema.String,\n detail: Schema.String\n}) {\n override get message(): string {\n return `${this.detail}\\nThe fix worktree's directory is ${this.directory}.`\n }\n}\n\n/**\n * Where a worktree is cut from, what it is cut at, and where it goes: the tool's\n * own bare clone of `repo`, the pull request's head, and a directory under\n * `cut` in the state directory.\n *\n * Everything happens in this clone and never in my checkout: a run that reached\n * into the directory I am working in would read whatever I had half finished\n * there.\n *\n * The clone is made once and fetched on every run after that. The fetch brings\n * the branch heads with the pull request's own, because a bare clone is made\n * with no refspec at all: without them the base branch stays at whatever it was\n * the day the clone was made, and a review that diffs against it would report\n * every commit since as the pull request's.\n *\n * The head comes from the pull request's ref rather than from what a sweep last\n * saw, so what is cut is the commit the run really reads.\n */\nconst whereToCut = Effect.fn(\"git.whereToCut\")(function* (repo: string, number: number, cut: Cut) {\n const path = yield* Path.Path\n const state = yield* stateDirectory\n const clone = path.join(state, clonesIn, `${repo}.git`)\n\n const bare = yield* Effect.orElseSucceed(git([\"-C\", clone, \"rev-parse\", \"--is-bare-repository\"]), () => \"\")\n if (bare !== \"true\") {\n yield* git([\"clone\", \"--bare\", \"--filter=blob:none\", `https://github.com/${repo}.git`, clone])\n }\n\n const pullRef = `refs/dw-mc/pr/${number}`\n yield* git([\n \"-C\",\n clone,\n \"fetch\",\n \"--no-tags\",\n \"--force\",\n \"origin\",\n `+refs/pull/${number}/head:${pullRef}`,\n \"+refs/heads/*:refs/heads/*\"\n ])\n const head = yield* git([\"-C\", clone, \"rev-parse\", pullRef])\n return { clone, head, directory: path.join(state, cut, repo, String(number)) }\n})\n\n/**\n * Runs `use` in a throwaway worktree at the head of `number`, and takes the\n * worktree down afterwards however the run ended.\n *\n * A worktree left behind would grow the state directory by a copy of the\n * repository per run, and nothing in a review run is worth keeping: what the\n * run found is recorded, and the checkout it read it in is not.\n *\n * The worktree is removed before it is cut as well as after, because the run\n * before this one may have been killed rather than ended.\n */\nexport const withWorktree = Effect.fn(\"git.withWorktree\")(function* <A, E, R>(\n repo: string,\n number: number,\n use: (worktree: Worktree) => Effect.Effect<A, E, R>\n) {\n const { clone, directory, head } = yield* whereToCut(repo, number, \"worktrees\")\n\n // A worktree that is not there cannot be removed, and that is the ordinary\n // case rather than a problem: both ends of the run ask for the same thing.\n const remove = Effect.ignore(git([\"-C\", clone, \"worktree\", \"remove\", \"--force\", directory]))\n\n return yield* Effect.acquireUseRelease(\n Effect.flatMap(remove, () => git([\"-C\", clone, \"worktree\", \"add\", \"--detach\", directory, head])),\n () => use({ directory, head }),\n () => remove\n )\n})\n\n/** The worktrees the clone knows it has, by directory. */\nconst worktreesOf = Effect.fn(\"git.worktreesOf\")(function* (clone: string) {\n const listed = yield* git([\"-C\", clone, \"worktree\", \"list\", \"--porcelain\"])\n return listed.split(\"\\n\").flatMap((line) => (line.startsWith(\"worktree \") ? [line.slice(\"worktree \".length)] : []))\n})\n\n/**\n * How far the branch a fix session works on has gone past `head`.\n *\n * Asked of the branch and never of the worktree that stands on it: a worktree\n * can be pruned or moved away by hand, and the branch it left behind still\n * holds the commits. A branch that is not there yet is nothing to hold.\n */\nconst aheadOf = Effect.fn(\"git.aheadOf\")(function* (clone: string, branch: string, head: string) {\n const ref = `refs/heads/${branch}`\n const found = yield* Effect.orElseSucceed(git([\"-C\", clone, \"rev-parse\", \"--verify\", \"--quiet\", ref]), () => \"\")\n if (found === \"\") {\n return 0\n }\n const counted = yield* git([\"-C\", clone, \"rev-list\", \"--count\", ref, `^${head}`])\n return Number(counted.trim())\n})\n\n/**\n * The clone, ready to keep a setting per worktree rather than for all of them.\n *\n * Verified by running it: turning `extensions.worktreeConfig` on in a bare\n * repository makes its linked worktrees read `core.bare` too, and every one of\n * them then refuses to work as a checkout. Git's own answer is to move\n * `core.bare` into the main worktree's config, which is what these three lines\n * do. `--unset` on a key already moved is not a failure, it is the second run.\n */\nconst perWorktreeConfig = Effect.fn(\"git.perWorktreeConfig\")(function* (clone: string) {\n yield* git([\"-C\", clone, \"config\", \"extensions.worktreeConfig\", \"true\"])\n yield* git([\"-C\", clone, \"config\", \"--worktree\", \"core.bare\", \"true\"])\n yield* Effect.ignore(git([\"-C\", clone, \"config\", \"--unset\", \"core.bare\"]))\n})\n\n/**\n * Turns the clone's reuse of a resolution on, which is what the session on a\n * conflict is worth beyond the one conflict.\n *\n * The recording lives in the clone rather than in the worktree, so a conflict I\n * resolve here is one `git` replays by itself the next time a throwaway rebase\n * hits it, with no model involved at all. `autoUpdate` is what makes that a\n * replay rather than a reminder: without it the resolution is written into the\n * worktree and left unstaged, and the rebase stops on a file that is already\n * resolved.\n */\nconst reuseResolutions = Effect.fn(\"git.reuseResolutions\")(function* (clone: string) {\n yield* git([\"-C\", clone, \"config\", \"rerere.enabled\", \"true\"])\n yield* git([\"-C\", clone, \"config\", \"rerere.autoUpdate\", \"true\"])\n})\n\n/**\n * A worktree for a session I steer, on a branch of the tool's own, and left\n * standing when the session ends.\n *\n * It outlives the session because the work in it is mine: I commit and push\n * from inside the session, and a worktree taken down at the end would take an\n * unpushed commit with it.\n *\n * The branch is `dw-mc/<session>/<number>` and never the pull request's own,\n * which is verified rather than a preference: `git` refuses to fetch into a\n * branch that a worktree has checked out, so a worktree standing on the pull\n * request's branch would fail the next fetch of this clone and take every\n * command that reads it down with it. It carries the session's name because a\n * fix session and a session on a conflict stand at the same time on the same\n * pull request, and one branch between them would be one holding the other's\n * commits. The branch tracks the pull request's, so a plain `git push` from\n * inside the session lands on the pull request.\n *\n * A previous session's work stops this before anything is cut: a branch that\n * has gone past the head says so in its own words rather than being reset over\n * commits I have not pushed, and that is asked of the branch alone, so a\n * worktree pruned or removed by hand does not let the commits through. Where\n * the branch is clear, the previous worktree is removed without `--force`, so\n * changes I have not committed refuse in `git`'s own words.\n */\nexport const standingWorktree = Effect.fn(\"git.standingWorktree\")(function* (\n repo: string,\n number: number,\n prBranch: string,\n session: Session\n) {\n const { clone, directory, head } = yield* whereToCut(repo, number, under[session])\n const branch = `dw-mc/${session}/${number}`\n\n const ahead = yield* aheadOf(clone, branch, head)\n if (ahead > 0) {\n return yield* new WorktreeHeld({\n directory,\n detail:\n `The last fix session on ${repo}#${number} left ${ahead} commit${ahead === 1 ? \"\" : \"s\"} ` +\n `that the pull request's head does not have. Push them or drop them before opening another session.`\n })\n }\n if ((yield* worktreesOf(clone)).includes(directory)) {\n yield* git([\"-C\", clone, \"worktree\", \"remove\", directory])\n }\n yield* perWorktreeConfig(clone)\n if (session === \"rebase\") {\n yield* reuseResolutions(clone)\n }\n yield* git([\"-C\", clone, \"worktree\", \"add\", \"-B\", branch, directory, head])\n\n // What makes `git push` inside the session land on the pull request: the\n // branch tracks the pull request's, and a push follows the upstream's name\n // rather than the branch's own. Where the branch is tracked is the clone's\n // business, but how a push behaves is this worktree's alone: a review run's\n // worktree must not inherit it.\n yield* git([\"-C\", clone, \"config\", `branch.${branch}.remote`, \"origin\"])\n yield* git([\"-C\", clone, \"config\", `branch.${branch}.merge`, `refs/heads/${prBranch}`])\n yield* git([\"-C\", directory, \"config\", \"--worktree\", \"push.default\", \"upstream\"])\n\n return { directory, head } satisfies Worktree\n})\n\n/** What a rebase of a pull request's branch onto its base came to. */\nexport type Rebased =\n | { readonly _tag: \"up-to-date\" }\n | { readonly _tag: \"conflicted\"; readonly paths: ReadonlyArray<string> }\n | { readonly _tag: \"pushed\"; readonly before: string; readonly after: string; readonly behind: number }\n\n/** How many commits the base has that the worktree's head does not. */\nconst behindBy = Effect.fn(\"git.behindBy\")(function* (directory: string, base: string) {\n const counted = yield* git([\"-C\", directory, \"rev-list\", \"--count\", `HEAD..refs/heads/${base}`])\n return Number(counted.trim())\n})\n\n/**\n * The files the stopped replay left unmerged, which is what the conflict is\n * about.\n *\n * `git` names them itself rather than being read out of its prose, and a\n * listing that refuses is no reason to leave a rebase standing: the paths are\n * worth less than the abort, so the conflict is recorded with none of them.\n */\nconst unmergedIn = Effect.fn(\"git.unmergedIn\")(function* (directory: string) {\n const listed = yield* Effect.orElseSucceed(git([\"-C\", directory, \"diff\", \"--name-only\", \"--diff-filter=U\"]), () => \"\")\n return listed.split(\"\\n\").filter((line) => line !== \"\")\n})\n\n/** What replaying a branch's commits onto its base came to, inside the worktree. */\ntype Replayed = { readonly _tag: \"replayed\" } | { readonly _tag: \"conflicted\"; readonly paths: ReadonlyArray<string> }\n\n/**\n * Whether a rebase is in progress in `directory`.\n *\n * Asked of `git` by the one command that answers it with an exit code alone:\n * the stopped replay's patch is there to show while the rebase is, and gone\n * when it is not.\n */\nconst rebasing = Effect.fn(\"git.rebasing\")(function* (directory: string) {\n return Result.isSuccess(yield* Effect.result(git([\"-C\", directory, \"rebase\", \"--show-current-patch\"])))\n})\n\n/** Whether anything is staged in `directory`, which `git` says by refusing. */\nconst stagedIn = Effect.fn(\"git.stagedIn\")(function* (directory: string) {\n return Result.isFailure(yield* Effect.result(git([\"-C\", directory, \"diff\", \"--cached\", \"--quiet\"])))\n})\n\n/**\n * How many stops one replay may be carried past before this gives up on it.\n *\n * A replay of n commits can stop n times and `rerere` can answer every one of\n * them, so the number is only here so that a stop which neither resolves nor\n * moves cannot spin forever.\n */\nconst stops = 100\n\n/**\n * Replays the worktree's commits onto `base`, and says where the replay\n * stopped: nowhere, or on the files it could not merge.\n *\n * A conflict is told from every other way `git rebase` refuses by what it left\n * unmerged, which `git` names itself rather than being read out of its prose.\n * The unmerged files are read before anything is aborted, because that is the\n * only moment they exist.\n *\n * A stop with nothing unmerged is where `rerere` has been: verified by running\n * it, a replay of a conflict I resolved once stages the old resolution and\n * still exits non-zero, with no unmerged file left to name. That is a replay to\n * carry on rather than one to report, so it is continued - with `core.editor`\n * off, because the continue is the tool's and the message is the commit's own.\n * Anything else with nothing unmerged and nothing staged never started, and is\n * worth `git`'s own words rather than a conflict that did not happen.\n *\n * `onConflict` is the whole difference between the two worktrees that replay.\n * `abort` is for the one the tool cuts and throws away, where nothing\n * half-finished may be left behind; `leave` is for the one I asked for and\n * which stands, where the stopped rebase is what I came for.\n */\nconst replayOnto = Effect.fn(\"git.replayOnto\")(function* (\n directory: string,\n base: string,\n onConflict: \"abort\" | \"leave\"\n) {\n let stopped = yield* Effect.result(git([\"-C\", directory, \"rebase\", `refs/heads/${base}`]))\n\n for (let step = 0; step < stops; step += 1) {\n if (Result.isSuccess(stopped)) {\n return { _tag: \"replayed\" } satisfies Replayed\n }\n const paths = yield* unmergedIn(directory)\n if (paths.length > 0) {\n if (onConflict === \"abort\") {\n const aborted = yield* Effect.result(git([\"-C\", directory, \"rebase\", \"--abort\"]))\n if (Result.isFailure(aborted)) {\n return yield* stopped.failure\n }\n }\n return { _tag: \"conflicted\", paths } satisfies Replayed\n }\n if (!((yield* rebasing(directory)) && (yield* stagedIn(directory)))) {\n return yield* stopped.failure\n }\n stopped = yield* Effect.result(git([\"-C\", directory, \"-c\", \"core.editor=true\", \"rebase\", \"--continue\"]))\n }\n\n return Result.isSuccess(stopped) ? ({ _tag: \"replayed\" } satisfies Replayed) : yield* stopped.failure\n})\n\n/**\n * Replays onto `base` in a worktree that stands, and leaves a conflict exactly\n * where it stopped.\n *\n * This is the other half of the rebase the throwaway worktree aborts: the\n * conflict is the point here, so the rebase stays in progress and the files\n * stay unmerged for the session to work on and for me to finish. The two are\n * not the same invariant - nothing half-finished is left in a worktree the tool\n * cuts and throws away, and this one is mine, asked for and left standing.\n */\nexport const rebaseInPlace = Effect.fn(\"git.rebaseInPlace\")(function* (directory: string, base: string) {\n return yield* replayOnto(directory, base, \"leave\")\n})\n\n/**\n * Brings a pull request's branch up to date with its base: rebase onto the\n * base and push with a lease, in a worktree thrown away either way.\n *\n * This is the only write the tool makes to GitHub, and everything about how it\n * is done is about that. The lease names the commit the rebase started from,\n * so a push lands only where the branch is still where this run read it, and a\n * commit pushed from somewhere else while the rebase ran refuses rather than\n * being overwritten. The branch is named in full on both sides of the push,\n * because the worktree stands on a detached head and has no branch of its own\n * to push from.\n *\n * My own checkout is not involved: the worktree is cut from the tool's own\n * clone, like every other run's.\n */\nexport const rebaseOnto = Effect.fn(\"git.rebaseOnto\")(function* (\n repo: string,\n number: number,\n base: string,\n branch: string\n) {\n return yield* withWorktree(repo, number, (worktree) =>\n Effect.gen(function* () {\n const behind = yield* behindBy(worktree.directory, base)\n if (behind === 0) {\n return { _tag: \"up-to-date\" } satisfies Rebased\n }\n const replayed = yield* replayOnto(worktree.directory, base, \"abort\")\n if (replayed._tag === \"conflicted\") {\n return replayed satisfies Rebased\n }\n\n const after = yield* git([\"-C\", worktree.directory, \"rev-parse\", \"HEAD\"])\n yield* git([\n \"-C\",\n worktree.directory,\n \"push\",\n `--force-with-lease=refs/heads/${branch}:${worktree.head}`,\n \"origin\",\n `HEAD:refs/heads/${branch}`\n ])\n return { _tag: \"pushed\", before: worktree.head, after, behind } satisfies Rebased\n })\n )\n})\n\n/** What a standing session worktree still holds, or nothing at all. */\nexport type Holding = { readonly _tag: \"clear\" } | { readonly _tag: \"held\"; readonly detail: string }\n\nconst clear: Holding = { _tag: \"clear\" }\n\n/**\n * What the worktree of a fix or resolve session still holds, asked without\n * reaching GitHub.\n *\n * Nothing that takes a directory away may fetch first: a command asked to\n * remove things would be cloning to answer whether it may, and a machine that\n * is offline would be told its work is gone. So the pull request's head is read\n * from the ref the last run left in the clone, and where there is no ref to\n * read the answer is that this cannot be told - which holds the worktree rather\n * than letting it through, because the one mistake worth avoiding here is\n * taking away a commit I have not pushed.\n *\n * Uncommitted changes are asked of the worktree and commits are asked of the\n * branch, for the reason `standingWorktree` asks the same two: a worktree\n * pruned or moved by hand still leaves the branch holding the commits.\n */\nexport const holding = Effect.fn(\"git.holding\")(function* (repo: string, number: number, session: Session) {\n const path = yield* Path.Path\n const state = yield* stateDirectory\n const clone = path.join(state, clonesIn, `${repo}.git`)\n const directory = path.join(state, under[session], repo, String(number))\n const branch = `dw-mc/${session}/${number}`\n\n const changes = yield* Effect.orElseSucceed(git([\"-C\", directory, \"status\", \"--porcelain\"]), () => \"\")\n if (changes.trim() !== \"\") {\n return { _tag: \"held\", detail: \"changes that are not committed\" } satisfies Holding\n }\n\n const ref = `refs/heads/${branch}`\n const found = yield* Effect.orElseSucceed(git([\"-C\", clone, \"rev-parse\", \"--verify\", \"--quiet\", ref]), () => \"\")\n if (found.trim() === \"\") {\n return clear\n }\n\n const head = yield* Effect.orElseSucceed(git([\"-C\", clone, \"rev-parse\", `refs/dw-mc/pr/${number}`]), () => \"\")\n if (head.trim() === \"\") {\n return {\n _tag: \"held\",\n detail: `the clone no longer knows what ${repo}#${number} points at, so what ${branch} holds cannot be told`\n } satisfies Holding\n }\n\n const ahead = yield* aheadOf(clone, branch, head.trim())\n return ahead === 0\n ? clear\n : ({\n _tag: \"held\",\n detail: `${ahead} commit${ahead === 1 ? \"\" : \"s\"} that the pull request's head does not have`\n } satisfies Holding)\n})\n\n/**\n * Forgets the worktrees a clone has been left with, once their directories are\n * gone.\n *\n * A directory taken from under the clone leaves the clone's record of it\n * behind, and the next session on that pull request is cut at the same path,\n * which is then refused as already registered. Pruning is the whole repair, and\n * only a clone that stays needs it: one being removed takes its records with\n * it.\n */\nexport const prune = Effect.fn(\"git.prune\")(function* (clone: string) {\n yield* Effect.ignore(git([\"-C\", clone, \"worktree\", \"prune\"]))\n})\n","import type { Cause } from \"effect\"\nimport { Effect, Layer, Option, Queue, Terminal } from \"effect\"\nimport { Prompt } from \"effect/unstable/cli\"\n\nimport { Paint, plain } from \"#adapters/paint.ts\"\n\n/**\n * Turns quitting into an answer rather than a failure.\n *\n * Bailing out of a prompt gives `None`, so no caller has to catch an error to\n * learn that I walked away. The prompt itself decides what a `Some` carries.\n */\nconst orNone = <A, R>(\n prompt: Effect.Effect<Option.Option<A>, Terminal.QuitError, R>\n): Effect.Effect<Option.Option<A>, never, R> => Effect.catchTag(prompt, \"QuitError\", () => Effect.succeedNone)\n\n/**\n * What a prompt looks like in this tool: the marker the rows already use, and\n * the same colour for the choice I am standing on.\n *\n * It is set here rather than at each prompt, because this module is the only\n * thing that opens one and four prompts that themed themselves would be four\n * looks.\n */\nconst theme = (paint: Paint): Partial<Prompt.Theme> =>\n paint === plain\n ? { prefix: \"▸\", pointer: \"●\" }\n : { prefix: \"▸\", pointer: \"●\", primaryColor: \"cyan\", mutedColor: \"gray\" }\n\n/**\n * What the keyboard does, said under the question.\n *\n * It rides in the message rather than being printed above the prompt, so it\n * leaves with the prompt: a hint that outlives the answer is scrollback I did\n * not ask for.\n */\nconst moves = \"↑↓ move · enter choose · q quit\"\n\nconst asked = (paint: Paint, message: string): string => `${message}\\n${paint.dim(moves)}`\n\n/** Asks which one of `choices` to act on. */\nexport const pick = <A>(\n message: string,\n choices: ReadonlyArray<Prompt.SelectChoice<A>>\n): Effect.Effect<Option.Option<A>, never, Prompt.Environment> =>\n Effect.flatMap(Paint, (paint) =>\n orNone(Effect.asSome(Prompt.Select({ message: asked(paint, message), choices, theme: theme(paint) })))\n )\n\n/**\n * Asks which of `choices` to act on, as many as I like.\n *\n * Nothing is selected to begin with, so what reaches the caller is what I\n * picked rather than what I failed to unpick. Quitting is not the same as\n * picking nothing: it gives `None`.\n */\nexport const choose = <A>(\n message: string,\n choices: ReadonlyArray<Prompt.SelectChoice<A>>\n): Effect.Effect<Option.Option<ReadonlyArray<A>>, never, Prompt.Environment> =>\n Effect.flatMap(Paint, (paint) =>\n orNone(\n Effect.asSome(\n Prompt.MultiSelect({\n message: `${message}\\n${paint.dim(\"↑↓ move · space pick · enter confirm · q quit\")}`,\n choices,\n theme: theme(paint)\n })\n )\n )\n )\n\n/**\n * Asks a yes-or-no question about something that cannot be taken back.\n *\n * It starts on no, and walking away is no as well: the answer this returns is\n * the one I typed, and every other way out of the prompt leaves the thing\n * undone. A confirmation that defaulted to yes would be one keystroke, which is\n * exactly what it exists to stop being.\n */\nexport const confirm = (message: string): Effect.Effect<boolean, never, Prompt.Environment> =>\n Effect.flatMap(Paint, (paint) =>\n Effect.map(\n orNone(Effect.asSome(Prompt.Confirm({ message, initial: false, theme: theme(paint) }))),\n Option.getOrElse(() => false)\n )\n )\n\n/**\n * Asks for a line of prose, where having nothing to say is the ordinary answer.\n *\n * An empty line is no note, and that is not a failure: the prompt is optional\n * by design. Quitting is the one thing it does not swallow. Ctrl-C part way\n * through a list of notes means I want out of the whole command, and a prompt\n * that turned it into \"no note\" would walk me through the rest of the list and\n * then act on findings I was no longer sure about.\n */\nexport const note = (message: string): Effect.Effect<Option.Option<string>, Terminal.QuitError, Prompt.Environment> =>\n Effect.map(Prompt.String({ message }), (text) => (text.trim() === \"\" ? Option.none() : Option.some(text.trim())))\n\n/**\n * How wide the screen is, or zero where there is no screen to measure.\n *\n * A prompt has to fit its row on one line: a row that wraps takes the list's\n * alignment with it. Nothing is piping into a prompt, so zero means the writing\n * is going somewhere that does not wrap either.\n */\nexport const width: Effect.Effect<number, never, Terminal.Terminal> = Effect.gen(function* () {\n const terminal = yield* Terminal.Terminal\n return yield* terminal.columns\n})\n\n/** One keypress for a scripted terminal. */\nexport const key = (name: string): Terminal.UserInput => ({\n input: Option.none(),\n key: { name, ctrl: false, meta: false, shift: false }\n})\n\n/**\n * A line of typing for a scripted terminal, one keypress to the character.\n *\n * A keypress carries one code unit, which is what a terminal really delivers,\n * so the text is split the way a keyboard produces it rather than by grapheme.\n */\nexport const typed = (text: string): ReadonlyArray<Terminal.UserInput> =>\n text.split(\"\").map((character) => ({\n input: Option.some(character),\n key: { name: character, ctrl: false, meta: false, shift: false }\n }))\n\n/**\n * A terminal that answers with `keys` and draws into `drawn`, for tests.\n *\n * Effect ships no test terminal, so this builds one from `Terminal.make`. A\n * prompt only ever asks for `columns`, `display` and `readInput`; it never\n * calls `readLine`. The keys are queued once, so a second prompt over the same\n * terminal finds the script spent rather than replaying it. Running out of keys\n * ends the queue, which a prompt reads as the user quitting.\n *\n * What is drawn is kept only where a caller asks for it: a prompt redraws\n * itself on every keypress, and a test that is about the answer does not want\n * the frames.\n */\nexport const layerScripted = (\n keys: ReadonlyArray<Terminal.UserInput>,\n drawn?: Array<string>,\n columns: number = 80\n): Layer.Layer<Terminal.Terminal> =>\n Layer.effect(\n Terminal.Terminal,\n Effect.gen(function* () {\n const queue = yield* Queue.make<Terminal.UserInput, Cause.Done>()\n for (const stroke of keys) {\n Queue.offerUnsafe(queue, stroke)\n }\n Queue.endUnsafe(queue)\n\n return Terminal.make({\n columns: Effect.succeed(columns),\n rows: Effect.succeed(24),\n readInput: Effect.succeed(queue),\n readLine: Effect.die(\"picker: a prompt never reads a line\"),\n display: (text) => Effect.sync(() => drawn?.push(text))\n })\n })\n )\n","/**\n * The rows of a table, padded so the columns line up and with the trailing\n * blanks cut. Effect ships no table and a table is what these commands print.\n *\n * A cell may arrive with colour on it or with a link under it, and both are\n * characters a terminal never shows, so every width here is measured in what is\n * shown rather than in what the string holds. Padding is added outside them, so\n * no line ends in blanks a terminal is still colouring or still linking.\n *\n * `separator` is what sits between two columns. Two spaces are enough where a\n * row is short; a row that runs to a sentence needs a rule, or the eye loses\n * which column it is in.\n */\nexport const table = (rows: ReadonlyArray<ReadonlyArray<string>>, separator: string = \" \"): ReadonlyArray<string> => {\n const widths = rows.reduce<ReadonlyArray<number>>(\n (widest, row) => row.map((cell, index) => Math.max(visible(cell), widest[index] ?? 0)),\n []\n )\n return rows.map((row) =>\n row\n .map((cell, index) => `${cell}${\" \".repeat(Math.max((widths[index] ?? 0) - visible(cell), 0))}`)\n .join(separator)\n .trimEnd()\n )\n}\n\n// oxlint-disable-next-line no-control-regex -- colour and links are control characters; matching them is the point\nconst escapes = /(\\x1b\\[\\d+m|\\x1b\\]8;;[^\\x1b]*\\x1b\\\\)/\nconst escape = new RegExp(`^${escapes.source}$`)\n\n/** How much of a cell a terminal shows: its characters, less the sequences they are wrapped in. */\nexport const visible = (text: string): number =>\n text.split(escapes).reduce((width, piece) => width + (escape.test(piece) ? 0 : piece.length), 0)\n\n/**\n * `text` at most `width` wide, with an ellipsis where it was cut.\n *\n * The width is what is shown, and the sequences the cut text was wrapped in are\n * kept whole: a string cut through one spills it onto the screen, and a string\n * that loses the one that closes it colours - or links - everything after it.\n */\nexport const truncate = (text: string, width: number): string => {\n if (visible(text) <= width) {\n return text\n }\n let shown = 0\n const kept = text.split(escapes).map((piece) => {\n if (escape.test(piece)) {\n return piece\n }\n const taken = piece.slice(0, Math.max(width - 1 - shown, 0))\n shown = shown + taken.length\n return taken\n })\n const last = kept.findLastIndex((piece) => !escape.test(piece) && piece !== \"\")\n return kept.map((piece, index) => (index === last ? `${piece.trimEnd()}…` : piece)).join(\"\")\n}\n\n/** `n` of something, pluralised the one way English usually is. */\nexport const count = (n: number, noun: string): string => `${n} ${noun}${n === 1 ? \"\" : \"s\"}`\n","import { ByteSize } from \"effect\"\n\nimport type { Clone, Cutting, Inventory, Session } from \"#adapters/store.ts\"\nimport { sessionOf } from \"#adapters/store.ts\"\n\n/** A checkout the tool cut for a session I steer, which stands until I take it down. */\nexport interface Standing extends Cutting {\n readonly session: Session\n}\n\n/** A clone that stays, and the session that is the reason. */\nexport interface Kept {\n readonly clone: Clone\n readonly because: string\n}\n\n/**\n * What `dw-mc cleanup` takes back, and what it leaves where it stands.\n *\n * The rule is one line: everything the tool can build again goes, and nothing\n * else is touched. A bare clone is a `git clone` away, and a review run's\n * worktree is cut fresh on every run, so both are the tool's own cost rather\n * than anything of mine. The records are not in here at all - what a pull\n * request is worth forgetting is decided when it is done, not by how much disk\n * it takes.\n */\nexport interface Plan {\n readonly clones: ReadonlyArray<Clone>\n readonly worktrees: ReadonlyArray<Cutting>\n readonly kept: ReadonlyArray<Kept>\n readonly size: ByteSize.ByteSize\n}\n\n/** The checkouts that stand for a session, named by the session they stand for. */\nexport const standing = (inventory: Inventory): ReadonlyArray<Standing> =>\n inventory.cuttings.flatMap((cutting) => {\n const session = sessionOf(cutting.cut)\n return session === undefined ? [] : [{ ...cutting, session }]\n })\n\n/** The checkouts a review run cut, which no run that ended still needs. */\nexport const orphaned = (inventory: Inventory): ReadonlyArray<Cutting> =>\n inventory.cuttings.filter((cutting) => cutting.cut === \"worktrees\")\n\nconst sum = (sizes: ReadonlyArray<ByteSize.ByteSize>): ByteSize.ByteSize =>\n ByteSize.bytes(sizes.reduce((total, size) => total + ByteSize.toBigInt(size), BigInt(0)))\n\nconst reason = (sessions: ReadonlyArray<Standing>): string =>\n sessions\n .map((it) => `a ${it.session === \"fix\" ? \"fix\" : \"resolve\"} session stands on ${it.repo}#${it.number}`)\n .join(\", \")\n\n/**\n * What a cleanup would take, weighed.\n *\n * A clone whose repository has a session standing on it stays, and that is not\n * politeness: a standing worktree keeps its history inside the clone, so a\n * clone removed from under one leaves a directory of files with nothing behind\n * them. The worktrees of that session stay with it; the review run's own go\n * either way, because they belong to a run that has ended.\n */\nexport const plan = (inventory: Inventory): Plan => {\n const sessions = standing(inventory)\n const worktrees = orphaned(inventory)\n\n const held = new Map<string, ReadonlyArray<Standing>>()\n for (const session of sessions) {\n held.set(session.repo, [...(held.get(session.repo) ?? []), session])\n }\n\n const clones = inventory.clones.filter((clone) => !held.has(clone.repo))\n const kept = inventory.clones.flatMap((clone) => {\n const sessionsHere = held.get(clone.repo)\n return sessionsHere === undefined ? [] : [{ clone, because: reason(sessionsHere) }]\n })\n\n return {\n clones,\n worktrees,\n kept,\n size: sum([...clones, ...worktrees].map((it) => it.size))\n }\n}\n\n/** What the whole state directory weighs: the clones, the checkouts and the records. */\nexport const everything = (inventory: Inventory): ByteSize.ByteSize =>\n sum([...inventory.clones.map((it) => it.size), ...inventory.cuttings.map((it) => it.size), inventory.records.size])\n\n/** Whether a plan has anything to do at all. */\nexport const empty = (it: Plan): boolean => it.clones.length === 0 && it.worktrees.length === 0\n\n/** A size as a line says it: three digits at most, and the unit the terminal reads. */\nexport const weight = (size: ByteSize.ByteSize): string => ByteSize.format(size, { system: \"decimal\", precision: 1 })\n","import { Console, Effect, Path } from \"effect\"\nimport { Command, Flag } from \"effect/unstable/cli\"\n\nimport { prune } from \"#adapters/git.ts\"\nimport type { Paint } from \"#adapters/paint.ts\"\nimport { Paint as PaintService } from \"#adapters/paint.ts\"\nimport { confirm } from \"#adapters/picker.ts\"\nimport { discard, inventory, tidy } from \"#adapters/store.ts\"\nimport { table } from \"#cli/table.ts\"\nimport type { Plan } from \"#domain/cleanup.ts\"\nimport { empty, plan, weight } from \"#domain/cleanup.ts\"\n\nexport const yesFlag = Flag.Boolean(\"yes\").pipe(\n Flag.withDefault(false),\n Flag.withDescription(\"Do it without asking, for a machine that has no terminal to ask at\")\n)\n\n/** A path said as the state directory's own, which is the heading it sits under. */\nconst inside = (path: Path.Path, state: string, directory: string): string => path.relative(state, directory)\n\n/**\n * The two blocks a cleanup writes: what it takes and what it leaves.\n *\n * The weight is on every row because the whole question is whether this is\n * worth doing, and the reason is on every row because a clone and a worktree\n * are taken back for different reasons and both read as \"a directory of mine\"\n * on the screen.\n */\nconst lines = (it: Plan, state: string, path: Path.Path, paint: Paint): ReadonlyArray<string> => {\n const taking = table([\n ...it.worktrees.map((worktree) => [\n paint.dim(inside(path, state, worktree.directory)),\n weight(worktree.size),\n \"a review worktree a run left behind\"\n ]),\n ...it.clones.map((clone) => [\n paint.dim(inside(path, state, clone.directory)),\n weight(clone.size),\n \"a bare clone, cloned again on the next run\"\n ])\n ])\n\n const staying = table(\n it.kept.map((kept) => [paint.dim(inside(path, state, kept.clone.directory)), weight(kept.clone.size), kept.because])\n )\n\n return [\n \"Takes back\",\n ...taking.map((line) => ` ${line}`),\n \"\",\n ...(staying.length === 0 ? [] : [\"Stays\", ...staying.map((line) => ` ${line}`), \"\"])\n ]\n}\n\n/**\n * Takes back the disk the tool spent on itself, and nothing that is mine.\n *\n * What it removes is what the tool builds again by itself: the bare clones and\n * the worktrees a review run cut. What it never removes is what I decided - the\n * configuration file - and what I worked in - the worktree of a fix or resolve\n * session, which stands on a branch of the tool's own and holds what I\n * committed there. Forgetting a pull request's records is a different question\n * with a different answer (#58), and it is not asked here.\n *\n * A clone with a session standing on it stays with the session: a standing\n * worktree keeps its history inside the clone, so a clone taken from under one\n * would leave a directory of files with nothing behind them. The clones that\n * stay are pruned instead, because a worktree directory removed under `git`\n * leaves the clone's record of it behind and the next session cut at that path\n * is refused as already registered.\n */\nexport const cleanup = Command.make(\n \"cleanup\",\n { yes: yesFlag },\n Effect.fn(\"cleanup\")(function* ({ yes }) {\n const path = yield* Path.Path\n const paint = yield* PaintService\n const found = yield* inventory\n const it = plan(found)\n\n if (empty(it)) {\n yield* Console.log(`Nothing to take back in ${found.directory}.`)\n return\n }\n\n yield* Effect.forEach(lines(it, found.directory, path, paint), (line) => Console.log(line))\n\n if (!yes && !(yield* confirm(`Take back ${weight(it.size)}?`))) {\n yield* Console.log(\"Nothing was removed.\")\n return\n }\n\n yield* Effect.forEach([...it.worktrees, ...it.clones], (taken) =>\n Effect.andThen(discard(taken.directory), tidy(taken.directory, found.directory))\n )\n yield* Effect.forEach(it.kept, (kept) => prune(kept.clone.directory))\n\n yield* Console.log(`Took back ${weight(it.size)}.`)\n })\n).pipe(Command.withDescription(\"Take back the disk the tool spent on clones and review worktrees\"))\n","import { DateTime, Effect, Match, PlatformError, Schema } from \"effect\"\nimport type { ChildProcessSpawner } from \"effect/unstable/process\"\n\nimport { capture } from \"#adapters/spawner.ts\"\n\n/** `gh` is on the machine but would not run. */\nexport class GhUnavailable extends Schema.TaggedError<GhUnavailable>()(\"GhUnavailable\", {\n detail: Schema.String\n}) {\n override get message(): string {\n return `gh could not be run: ${this.detail}\\nInstall it from https://cli.github.com, then run 'gh auth login'.`\n }\n}\n\n/** `gh` runs but is not logged in, so every read of GitHub would fail. */\nexport class GhUnauthenticated extends Schema.TaggedError<GhUnauthenticated>()(\"GhUnauthenticated\", {\n detail: Schema.String\n}) {\n override get message(): string {\n return `gh is not authenticated. Run 'gh auth login'.\\n${this.detail}`\n }\n}\n\n/** The working directory is not inside a repository `gh` can name. */\nexport class NoRepository extends Schema.TaggedError<NoRepository>()(\"NoRepository\", {\n detail: Schema.String\n}) {\n override get message(): string {\n return `This directory is not a GitHub repository dw-mc can register.\\n${this.detail}`\n }\n}\n\n/** `gh` answered, in a shape this version of dw-mc does not know. */\nexport class GhUnreadable extends Schema.TaggedError<GhUnreadable>()(\"GhUnreadable\", {\n command: Schema.String,\n reason: Schema.String\n}) {\n override get message(): string {\n return `gh ${this.command} answered with something dw-mc cannot read: ${this.reason}`\n }\n}\n\n/** What a `gh` that would not even start comes to. */\nexport const unavailable = (error: PlatformError.PlatformError): GhUnavailable =>\n new GhUnavailable({\n detail: error.reason._tag === \"NotFound\" ? \"it is not installed\" : error.message\n })\n\n/**\n * Stops unless `gh` is installed and logged in.\n *\n * Every read of GitHub goes through `gh` as me, so a missing or logged-out `gh`\n * is worth saying once, up front, rather than as an empty table later.\n */\nexport const requireAuth: Effect.Effect<\n void,\n GhUnavailable | GhUnauthenticated,\n ChildProcessSpawner.ChildProcessSpawner\n> = capture(\"gh\", [\"auth\", \"status\"]).pipe(\n Effect.asVoid,\n Effect.catchTags({\n PlatformError: (error) => Effect.fail(unavailable(error)),\n CommandFailed: (error) => Effect.fail(new GhUnauthenticated({ detail: error.stderr }))\n }),\n Effect.withSpan(\"gh.requireAuth\")\n)\n\nconst RepoView = Schema.fromJsonString(Schema.Struct({ nameWithOwner: Schema.String }))\n\n/** The `owner/repo` of the repository the working directory is in. */\nexport const currentRepo: Effect.Effect<\n string,\n GhUnavailable | NoRepository | GhUnreadable,\n ChildProcessSpawner.ChildProcessSpawner\n> = Effect.gen(function* () {\n const json = yield* capture(\"gh\", [\"repo\", \"view\", \"--json\", \"nameWithOwner\"]).pipe(\n Effect.catchTags({\n PlatformError: (error) => Effect.fail(unavailable(error)),\n CommandFailed: (error) => Effect.fail(new NoRepository({ detail: error.stderr }))\n })\n )\n\n const view = yield* Schema.decodeEffect(RepoView)(json).pipe(\n Effect.mapError((error) => new GhUnreadable({ command: \"repo view\", reason: error.message }))\n )\n return view.nameWithOwner\n}).pipe(Effect.withSpan(\"gh.currentRepo\"))\n\n/** A call to GitHub that `gh` itself refused, whether it was reading or writing. */\nexport class GhReadFailed extends Schema.TaggedError<GhReadFailed>()(\"GhReadFailed\", {\n command: Schema.String,\n detail: Schema.String\n}) {\n override get message(): string {\n return `gh ${this.command} failed: ${this.detail}`\n }\n}\n\n/** Anything that can go wrong reading GitHub through `gh`. */\nexport type GhError = GhUnavailable | GhReadFailed | GhUnreadable\n\n/** One `gh` read, decoded, with every way it can go wrong in our words. */\nexport const readJson = <A>(\n label: string,\n command: string,\n args: ReadonlyArray<string>,\n schema: Schema.Codec<A, string>\n): Effect.Effect<A, GhError, ChildProcessSpawner.ChildProcessSpawner> =>\n capture(command, args).pipe(\n Effect.catchTags({\n PlatformError: (error) => Effect.fail(unavailable(error)),\n CommandFailed: (error) => Effect.fail(new GhReadFailed({ command: label, detail: error.stderr }))\n }),\n Effect.flatMap((json) =>\n Schema.decodeEffect(schema)(json).pipe(\n Effect.mapError((error) => new GhUnreadable({ command: label, reason: error.message }))\n )\n ),\n Effect.withSpan(`gh.${label}`)\n )\n\nconst User = Schema.fromJsonString(Schema.Struct({ login: Schema.String }))\n\n/** The login `gh` is authenticated as: the \"me\" every read is scoped to. */\nexport const viewer: Effect.Effect<string, GhError, ChildProcessSpawner.ChildProcessSpawner> = readJson(\n \"api user\",\n \"gh\",\n [\"api\", \"user\"],\n User\n).pipe(Effect.map((user) => user.login))\n\nconst SearchResults = Schema.fromJsonString(\n Schema.Array(\n Schema.Struct({\n number: Schema.Int,\n repository: Schema.Struct({ nameWithOwner: Schema.String })\n })\n )\n)\n\n/** One open pull request the search found. */\nexport interface Found {\n readonly repo: string\n readonly number: number\n}\n\n/**\n * The open pull requests I authored in `repo`.\n *\n * One search per repository rather than one for all of them: a repository `gh`\n * cannot read then costs me that repository's rows and not the whole table.\n */\nexport const searchPrs = Effect.fnUntraced(function* (repo: string) {\n const found = yield* readJson(\n \"search prs\",\n \"gh\",\n [\"search\", \"prs\", \"--author=@me\", \"--state=open\", \"--repo\", repo, \"--limit\", \"100\", \"--json\", \"number,repository\"],\n SearchResults\n )\n\n return found.map((it): Found => ({ repo: it.repository.nameWithOwner, number: it.number }))\n})\n\n/**\n * One entry of a PR's status check rollup.\n *\n * A rollup mixes two shapes: a `CheckRun` reports a `status` and a `conclusion`,\n * a `StatusContext` an overall `state`. Every field is optional because which\n * ones arrive depends on which shape it is.\n */\nexport const CheckEntry = Schema.Struct({\n name: Schema.optionalKey(Schema.String),\n context: Schema.optionalKey(Schema.String),\n status: Schema.optionalKey(Schema.String),\n conclusion: Schema.optionalKey(Schema.String),\n state: Schema.optionalKey(Schema.String),\n /** The workflow the check runs in. A commit status belongs to no workflow. */\n workflowName: Schema.optionalKey(Schema.String),\n /** Where the check reports, which is the only place its job id appears. */\n detailsUrl: Schema.optionalKey(Schema.String)\n})\nexport type CheckEntry = typeof CheckEntry.Type\n\nconst PrView = Schema.fromJsonString(\n Schema.Struct({\n number: Schema.Int,\n title: Schema.String,\n url: Schema.String,\n isDraft: Schema.Boolean,\n headRefOid: Schema.String,\n headRefName: Schema.String,\n baseRefName: Schema.String,\n /** Who opened it, which is what says whether its branch is mine to push to. */\n author: Schema.NullOr(Schema.Struct({ login: Schema.String })),\n /** Whether the head branch lives in a fork rather than in this repository. */\n isCrossRepository: Schema.Boolean,\n mergeable: Schema.String,\n reviewDecision: Schema.String,\n statusCheckRollup: Schema.NullOr(Schema.Array(CheckEntry))\n })\n)\nexport type PrView = typeof PrView.Type\n\nconst viewFields =\n \"number,title,url,isDraft,headRefOid,headRefName,baseRefName,author,isCrossRepository,mergeable,\" +\n \"reviewDecision,statusCheckRollup\"\n\n/**\n * Everything about one pull request that arrives without paging through it:\n * its head, what GitHub thinks of merging it, and where CI got to.\n */\nexport const prView = Effect.fnUntraced(function* (repo: string, number: number) {\n return yield* readJson(\"pr view\", \"gh\", [\"pr\", \"view\", String(number), \"--repo\", repo, \"--json\", viewFields], PrView)\n})\n\nconst OpenPrs = Schema.fromJsonString(\n Schema.Array(\n Schema.Struct({\n number: Schema.Int,\n headRefName: Schema.String,\n baseRefName: Schema.String\n })\n )\n)\n\n/** One open pull request, as the branch it stands on and the one it merges into. */\nexport interface OpenPr {\n readonly number: number\n readonly head: string\n readonly base: string\n}\n\n/**\n * Every open pull request on a repository, by branch.\n *\n * Everyone's and not only mine: a stack is recognised from branches built on\n * branches, and a pull request of mine can sit on one somebody else opened.\n *\n * The page is deep because a pull request this misses is one that looks like it\n * is in no stack, and a stack the tool cannot see is one it could drive.\n */\nexport const openPrs = Effect.fnUntraced(function* (repo: string) {\n const open = yield* readJson(\n \"pr list\",\n \"gh\",\n [\"pr\", \"list\", \"--repo\", repo, \"--state\", \"open\", \"--limit\", \"500\", \"--json\", \"number,headRefName,baseRefName\"],\n OpenPrs\n )\n\n return open.map((it): OpenPr => ({ number: it.number, head: it.headRefName, base: it.baseRefName }))\n})\n\nconst Comments = Schema.fromJsonString(\n Schema.Array(\n Schema.Struct({\n created_at: Schema.DateTimeUtcFromString,\n user: Schema.NullOr(Schema.Struct({ login: Schema.String, type: Schema.String }))\n })\n )\n)\n\n/** Who wrote a comment and when. */\nexport interface Comment {\n readonly login: string\n readonly bot: boolean\n readonly at: DateTime.Utc\n}\n\nconst comments = (label: string, path: string) =>\n readJson(label, \"gh\", [\"api\", path], Comments).pipe(\n Effect.map((all) =>\n all.flatMap((comment): ReadonlyArray<Comment> =>\n comment.user === null\n ? []\n : [{ login: comment.user.login, bot: comment.user.type === \"Bot\", at: comment.created_at }]\n )\n )\n )\n\n/**\n * Every comment on a pull request: the ones on the conversation and the ones\n * left on the diff.\n *\n * REST is what says whether an author is a person or an app - `gh pr view`\n * reports a bot's login with no sign that it is one - and the bucket rules turn\n * on exactly that. Verified by running both: the endpoints ignore `direction`,\n * so a page is asked for at its maximum and the newest comment is picked out of\n * it rather than asked for first.\n */\nexport const prComments = Effect.fnUntraced(function* (repo: string, number: number) {\n const page = \"per_page=100\"\n const [conversation, onDiff] = yield* Effect.all(\n [\n comments(\"api issue comments\", `repos/${repo}/issues/${number}/comments?${page}`),\n comments(\"api review comments\", `repos/${repo}/pulls/${number}/comments?${page}`)\n ],\n { concurrency: 2 }\n )\n return [...conversation, ...onDiff]\n})\n\nconst Reviews = Schema.fromJsonString(\n Schema.Array(\n Schema.Struct({\n submitted_at: Schema.DateTimeUtcFromString,\n body: Schema.String,\n user: Schema.NullOr(Schema.Struct({ login: Schema.String, type: Schema.String }))\n })\n )\n)\n\n/**\n * The reviews on a pull request that said something, as comments.\n *\n * A review carries a body of its own, which is where a reviewer writes the\n * sentence that is not attached to any line. An empty body is a verdict and\n * nothing more, and the verdict arrives with the PR as `reviewDecision`.\n */\nexport const prReviews = Effect.fnUntraced(function* (repo: string, number: number) {\n const all = yield* readJson(\n \"api reviews\",\n \"gh\",\n [\"api\", `repos/${repo}/pulls/${number}/reviews?per_page=100`],\n Reviews\n )\n\n return all.flatMap((review): ReadonlyArray<Comment> =>\n review.user === null || review.body.trim() === \"\"\n ? []\n : [{ login: review.user.login, bot: review.user.type === \"Bot\", at: review.submitted_at }]\n )\n})\n\nconst Compare = Schema.fromJsonString(\n Schema.Struct({ files: Schema.optionalKey(Schema.Array(Schema.Struct({ filename: Schema.String }))) })\n)\n\n/**\n * The repository paths that changed between two commits.\n *\n * GitHub compares them rather than git, because the commit a run was recorded\n * against is not one the tool's own clone is promised to still have: a force\n * push moves the pull request's ref and the old commit goes with it, where\n * GitHub keeps both sides of the comparison. A comparison of a commit with\n * itself reports no files at all, and so does one of two commits with nothing\n * between them, which is why the key is optional.\n *\n * `base...head` measures from where the two commits last agreed, so two heads\n * on one branch report what was pushed between them, and a branch rebased since\n * reports its whole diff. The second is the right answer for a caller deciding\n * whether the code has moved: after a rebase it has, all of it.\n */\nexport const comparedFiles = Effect.fnUntraced(function* (repo: string, base: string, head: string) {\n const compare = yield* readJson(\"api compare\", \"gh\", [\"api\", `repos/${repo}/compare/${base}...${head}`], Compare)\n return (compare.files ?? []).map((file) => file.filename)\n})\n\nconst Commits = Schema.fromJsonString(\n Schema.Struct({\n commits: Schema.Array(\n Schema.Struct({\n committedDate: Schema.DateTimeUtcFromString,\n authors: Schema.Array(Schema.Struct({ login: Schema.NullOr(Schema.String) }))\n })\n )\n })\n)\n\n/** One commit on a pull request, and who wrote it. */\nexport interface Commit {\n readonly logins: ReadonlyArray<string>\n readonly at: DateTime.Utc\n}\n\n/**\n * The commits on a pull request.\n *\n * This is the expensive read of the three: `gh` returns every commit with its\n * whole message, so a sweep only asks for it when something about the PR has\n * actually moved.\n */\nexport const prCommits = Effect.fnUntraced(function* (repo: string, number: number) {\n const view = yield* readJson(\n \"pr view commits\",\n \"gh\",\n [\"pr\", \"view\", String(number), \"--repo\", repo, \"--json\", \"commits\"],\n Commits\n )\n\n return view.commits.map((commit): Commit => ({\n logins: commit.authors.flatMap((author) => (author.login === null ? [] : [author.login])),\n at: commit.committedDate\n }))\n})\n\ntype Mergeability = \"mergeable\" | \"conflicting\" | \"unknown\"\n\n/**\n * What `gh` says about merging, in our words. Anything else is `unknown`:\n * GitHub answers that too, for a PR whose mergeability it is still computing.\n *\n * `Match.withReturnType` comes first in the pipeline or the return type is not\n * enforced: a handler's literal widens to `string` on its own.\n */\nexport const mergeabilityOf = (raw: string): Mergeability =>\n Match.value(raw).pipe(\n Match.withReturnType<Mergeability>(),\n Match.when(\"MERGEABLE\", () => \"mergeable\"),\n Match.when(\"CONFLICTING\", () => \"conflicting\"),\n Match.orElse(() => \"unknown\")\n )\n\ntype ReviewDecision = \"approved\" | \"changes-requested\" | \"review-required\" | \"none\"\n\n/**\n * What `gh` says the reviewers decided, in our words. A repository that requires\n * no reviewer reports an empty string, which is `none` rather than pending.\n */\nexport const reviewDecisionOf = (raw: string): ReviewDecision =>\n Match.value(raw).pipe(\n Match.withReturnType<ReviewDecision>(),\n Match.when(\"APPROVED\", () => \"approved\"),\n Match.when(\"CHANGES_REQUESTED\", () => \"changes-requested\"),\n Match.when(\"REVIEW_REQUIRED\", () => \"review-required\"),\n Match.orElse(() => \"none\")\n )\n\n/**\n * Squash-merges a pull request and deletes the branch it stood on.\n *\n * The one write the tool makes that no reflog of mine undoes, and the whole of\n * it: a squash, because that is how the repository lands a pull request and the\n * squash subject is its title, and the branch, because squashing kills it\n * anyway. No `--auto`, which would hand GitHub a merge to make at a head\n * nothing here has read (ADR 0008).\n *\n * Whether this pull request is one to merge is decided before we get here, and\n * `gh` still has the last word: a branch protection this machine cannot see\n * comes back as a failure and is printed as one.\n */\nexport const mergePr = Effect.fnUntraced(function* (repo: string, number: number) {\n yield* capture(\"gh\", [\"pr\", \"merge\", String(number), \"--repo\", repo, \"--squash\", \"--delete-branch\"]).pipe(\n Effect.catchTags({\n PlatformError: (error) => Effect.fail(unavailable(error)),\n CommandFailed: (error) => Effect.fail(new GhReadFailed({ command: \"pr merge\", detail: error.stderr }))\n })\n )\n})\n","import { DateTime, Effect, Schema } from \"effect\"\n\nimport { readJson } from \"#adapters/gh.ts\"\n\n/**\n * A pull request's conversation, which is the one read that leaves REST.\n *\n * It sits beside `gh.ts` rather than in it because it is a boundary of its own:\n * one GraphQL document, decoded into the threads a command prints, where every\n * other read of GitHub here is a `gh` subcommand or a REST endpoint.\n */\n\n/** One thing somebody said on a pull request, in full. */\nexport interface Remark {\n readonly login: string\n readonly bot: boolean\n readonly at: DateTime.Utc\n readonly body: string\n}\n\n/**\n * One strand of a pull request's conversation: a review thread on a line of the\n * diff, or the pull request's own comments, which hang off no path at all.\n */\nexport interface Thread {\n readonly path: string | null\n readonly line: number | null\n readonly resolved: boolean\n readonly outdated: boolean\n readonly comments: ReadonlyArray<Remark>\n}\n\nconst Actor = Schema.NullOr(Schema.Struct({ login: Schema.String, __typename: Schema.String }))\n\nconst Said = Schema.Struct({ author: Actor, body: Schema.String, createdAt: Schema.DateTimeUtcFromString })\n\nconst Conversation = Schema.fromJsonString(\n Schema.Struct({\n data: Schema.Struct({\n repository: Schema.Struct({\n pullRequest: Schema.Struct({\n comments: Schema.Struct({ nodes: Schema.Array(Said) }),\n reviews: Schema.Struct({\n nodes: Schema.Array(\n Schema.Struct({\n author: Actor,\n body: Schema.String,\n submittedAt: Schema.NullOr(Schema.DateTimeUtcFromString)\n })\n )\n }),\n reviewThreads: Schema.Struct({\n nodes: Schema.Array(\n Schema.Struct({\n isResolved: Schema.Boolean,\n isOutdated: Schema.Boolean,\n path: Schema.NullOr(Schema.String),\n line: Schema.NullOr(Schema.Int),\n comments: Schema.Struct({ nodes: Schema.Array(Said) })\n })\n )\n })\n })\n })\n })\n })\n)\n\nconst remark = (\n said: { readonly author: typeof Actor.Type; readonly body: string },\n at: DateTime.Utc | null\n): ReadonlyArray<Remark> =>\n said.author === null || at === null || said.body.trim() === \"\"\n ? []\n : [{ login: said.author.login, bot: said.author.__typename === \"Bot\", at, body: said.body.trim() }]\n\nconst byTime = (self: Remark, other: Remark): number => DateTime.Order(self.at, other.at)\n\n/**\n * A pull request's whole conversation: the comments on it, the bodies of the\n * reviews, and every thread on the diff with whether it is settled.\n *\n * GraphQL rather than the two REST endpoints a sweep reads, because resolution\n * is not in REST at all: a review comment's payload carries `body`, `path`,\n * `line`, `diff_hunk` and `side`, and nothing saying whether somebody closed\n * the thread it belongs to. A thread that was settled a week ago is not\n * something to answer, so the state that says so has to arrive with it.\n *\n * The pull request's own comments and the reviews' bodies come back as one\n * strand under no path, in the order they were written: they are one\n * conversation as it happened, and which endpoint each line came from is an\n * accident of GitHub's model rather than anything to read.\n *\n * `__typename` is what says a bot is a bot, the way `user.type` does in REST.\n */\nexport const prConversation = Effect.fnUntraced(function* (repo: string, number: number) {\n const [owner = repo, name = repo] = repo.split(\"/\")\n // The document is spelled out here rather than held in a constant, because\n // every GraphQL call is a POST and the document is the only thing that says\n // whether it reads or writes: `no-gh-writes` reads it at this call site and\n // refuses one it cannot.\n const answer = yield* readJson(\n \"api graphql\",\n \"gh\",\n [\n \"api\",\n \"graphql\",\n \"-f\",\n `query=query($owner:String!,$name:String!,$number:Int!){\n repository(owner:$owner,name:$name){\n pullRequest(number:$number){\n comments(last:100){nodes{author{login __typename} body createdAt}}\n reviews(last:100){nodes{author{login __typename} body submittedAt}}\n reviewThreads(last:100){nodes{\n isResolved isOutdated path line\n comments(first:100){nodes{author{login __typename} body createdAt}}\n }}\n }\n }\n }`,\n \"-F\",\n `owner=${owner}`,\n \"-F\",\n `name=${name}`,\n \"-F\",\n `number=${number}`\n ],\n Conversation\n )\n\n const pr = answer.data.repository.pullRequest\n const conversation = [\n ...pr.comments.nodes.flatMap((it) => remark(it, it.createdAt)),\n ...pr.reviews.nodes.flatMap((it) => remark(it, it.submittedAt))\n ].toSorted(byTime)\n\n const threads = pr.reviewThreads.nodes.map((it): Thread => ({\n path: it.path,\n line: it.line,\n resolved: it.isResolved,\n outdated: it.isOutdated,\n comments: it.comments.nodes.flatMap((comment) => remark(comment, comment.createdAt)).toSorted(byTime)\n }))\n\n return [\n ...(conversation.length === 0\n ? []\n : [{ path: null, line: null, resolved: false, outdated: false, comments: conversation } satisfies Thread]),\n ...threads\n ] satisfies ReadonlyArray<Thread>\n})\n","import { DateTime, Order, Predicate } from \"effect\"\n\n/**\n * A moment something happened, or that it never did.\n *\n * Three of the facts a sweep reads are timestamps that a pull request may\n * simply not have - nobody has commented, nobody has pushed - and the rules\n * compare them all the same way. These are that comparison, in one place, over\n * `DateTime`'s own `Order` and `Equivalence`.\n */\nexport type Moment = DateTime.Utc | null\n\nconst isLater = Order.isGreaterThan(DateTime.Order)\n\n/** Whether `self` happened after `other`, counting never as before anything. */\nexport const isAfter = (self: Moment, other: Moment): boolean =>\n Predicate.isNotNull(self) && (other === null || isLater(self, other))\n\n/** The later of the two. */\nexport const later = (self: Moment, other: Moment): Moment => (isAfter(self, other) ? self : other)\n\n/** Whether the two are the same moment, counting never as the same as never. */\nexport const isSame = (self: Moment, other: Moment): boolean =>\n self === null || other === null ? self === other : DateTime.Equivalence(self, other)\n\n/** The latest of many, or never when there are none. */\nexport const newest = (moments: ReadonlyArray<DateTime.Utc>): Moment => moments.reduce<Moment>(later, null)\n","import { Schema } from \"effect\"\n\nimport { isAfter, later } from \"#domain/moment.ts\"\n\n/** How far GitHub has got towards letting a tracked PR merge. */\nexport const Mergeability = Schema.Literals([\"mergeable\", \"conflicting\", \"unknown\"])\nexport type Mergeability = typeof Mergeability.Type\n\n/** What the reviewers have decided, or that nobody is required to. */\nexport const ReviewDecision = Schema.Literals([\"approved\", \"changes-requested\", \"review-required\", \"none\"])\nexport type ReviewDecision = typeof ReviewDecision.Type\n\n/** What CI says about the current head. */\nexport const ChecksState = Schema.Literals([\"green\", \"red\", \"pending\", \"none\"])\nexport type ChecksState = typeof ChecksState.Type\n\n/**\n * Everything the bucket rules are allowed to know about a tracked PR.\n *\n * It is a schema because a sweep writes it to the state directory and reads it\n * back on the next one: the same facts that decide a bucket are what a quiet PR\n * is recognised by.\n */\nexport const Facts = Schema.Struct({\n repo: Schema.String,\n number: Schema.Int,\n title: Schema.String,\n url: Schema.String,\n /** Shown, never acted on unless I ask. */\n draft: Schema.Boolean,\n /** The head commit every other fact here is about. */\n head: Schema.String,\n mergeable: Mergeability,\n reviewDecision: ReviewDecision,\n checks: ChecksState,\n /** Why the flaky classifier excuses this red CI, or null where it does not. */\n ciFlaky: Schema.NullOr(Schema.String),\n /** The head a rebase onto the base conflicted at, or null where none has. */\n rebaseConflictAt: Schema.NullOr(Schema.String),\n /** The newest comment from a person who is not me, bots excluded. */\n newestHumanCommentAt: Schema.NullOr(Schema.DateTimeUtcFromString),\n myLastCommentAt: Schema.NullOr(Schema.DateTimeUtcFromString),\n myLastCommitAt: Schema.NullOr(Schema.DateTimeUtcFromString),\n /** The head a review run has already covered, or null where none has. */\n reviewRunHead: Schema.NullOr(Schema.String),\n /** Findings on this head that withhold the stamp, at the bar `stamp.blocks_on` sets. */\n blockingFindings: Schema.Int\n})\nexport type Facts = typeof Facts.Type\n\n/** The one place a tracked PR sits at a time, named for what it waits on. */\nexport const Bucket = Schema.Literals([\"needs-me\", \"needs-review-run\", \"waiting-on-others\", \"ready\"])\nexport type Bucket = typeof Bucket.Type\n\n/** The bucket a tracked PR is in, and why it is in that one. */\nexport interface Placement {\n readonly bucket: Bucket\n readonly reason: string\n}\n\n/** A tracked PR beside the placement its facts earned. */\nexport interface Placed {\n readonly facts: Facts\n readonly placement: Placement\n}\n\n/** The buckets in the order I act on them: the top of the table is my next move. */\nexport const order: ReadonlyArray<Bucket> = [\"needs-me\", \"needs-review-run\", \"waiting-on-others\", \"ready\"]\n\n/**\n * Why a PR is mine to move when somebody has said something I have not\n * answered.\n *\n * It is named because it is read twice: here, where it puts the PR in Needs me,\n * and by `dw-mc comments`, which says what settles that one branch of the\n * bucket. A sentence matched from the other side of the tool is a rule that\n * breaks on a reword.\n */\nexport const unanswered = \"a comment I have not answered\"\n\n/**\n * The first of the rules that makes a PR mine to move, or null when none\n * does. The order is the order I would fix them in: a conflict makes every\n * other signal on the PR stale, and a red build is worth more than a comment.\n */\nconst needsMe = (facts: Facts): string | null => {\n if (facts.mergeable === \"conflicting\") {\n return \"merge conflict\"\n }\n if (facts.rebaseConflictAt === facts.head) {\n return \"a rebase onto the base conflicted\"\n }\n if (facts.checks === \"red\" && facts.ciFlaky === null) {\n return \"CI is red\"\n }\n if (facts.reviewDecision === \"changes-requested\") {\n return \"changes requested\"\n }\n if (facts.blockingFindings > 0) {\n return `${facts.blockingFindings} blocking finding${facts.blockingFindings === 1 ? \"\" : \"s\"}`\n }\n if (isAfter(facts.newestHumanCommentAt, later(facts.myLastCommentAt, facts.myLastCommitAt))) {\n return unanswered\n }\n return null\n}\n\n/**\n * What is actually true of a PR nothing is waiting on.\n *\n * Ready is reached by having no reason not to be, so the reason says only what\n * holds: a repository that requires no reviewer produces no approval, and a\n * pull request with no CI at all is not green.\n *\n * A red CI the classifier excused is said out loud, because GitHub does not\n * excuse it: the check is still red, and Ready is what `dw-mc merge` reads.\n */\nconst readyReason = (facts: Facts): string => {\n const held = [\n facts.reviewDecision === \"approved\" ? \"approved\" : null,\n facts.checks === \"green\" ? \"green\" : null,\n facts.mergeable === \"mergeable\" ? \"mergeable\" : null\n ].filter((it) => it !== null)\n const standing = held.length === 0 ? \"nothing left to wait on\" : held.join(\", \")\n return facts.checks === \"red\" && facts.ciFlaky !== null\n ? `${standing} (red CI called flaky: ${facts.ciFlaky})`\n : standing\n}\n\n/**\n * The bucket a tracked PR sits in, and the reason for it.\n *\n * This is the single place the bucket rules exist. Every tracked PR lands in\n * exactly one bucket, so the rules are tried in priority order and the first\n * that claims the PR wins: a PR that both needs a review run and has changes\n * requested is mine to move, not the review's.\n *\n * Ready does not insist on an approval, because a repository that requires no\n * reviewer never produces one. What it insists on is that nobody else has been\n * asked and is yet to answer.\n */\nexport const place = (facts: Facts): Placement => {\n const mine = needsMe(facts)\n if (mine !== null) {\n return { bucket: \"needs-me\", reason: mine }\n }\n if (facts.reviewRunHead !== facts.head) {\n return { bucket: \"needs-review-run\", reason: \"no review run on this head\" }\n }\n if (facts.reviewDecision === \"review-required\") {\n return { bucket: \"waiting-on-others\", reason: \"a review from someone else\" }\n }\n if (facts.checks === \"pending\") {\n return { bucket: \"waiting-on-others\", reason: \"CI is still running\" }\n }\n return { bucket: \"ready\", reason: readyReason(facts) }\n}\n\n/**\n * The tracked PRs grouped into their buckets, in the order I act on them, with\n * the empty buckets left out so the table is only what there is to do.\n */\n/** One bucket with what is in it: a heading in the table, and the rows under it. */\nexport interface Grouped {\n readonly bucket: Bucket\n readonly placed: ReadonlyArray<Placed>\n}\n\nexport const group = (facts: ReadonlyArray<Facts>): ReadonlyArray<Grouped> => {\n const placed = facts\n .map((it): Placed => ({ facts: it, placement: place(it) }))\n .toSorted((a, b) => a.facts.repo.localeCompare(b.facts.repo) || a.facts.number - b.facts.number)\n\n return order\n .map((bucket) => ({ bucket, placed: placed.filter((it) => it.placement.bucket === bucket) }))\n .filter((bucket) => bucket.placed.length > 0)\n}\n","/**\n * What I typed to name a pull request, once it is known which one that is.\n *\n * A number alone is what I actually type, so it resolves against the registered\n * repositories rather than being refused; where that cannot decide, the answer\n * says so instead of guessing a repository.\n */\nexport type Reference =\n | { readonly _tag: \"resolved\"; readonly repo: string; readonly number: number }\n | { readonly _tag: \"ambiguous\"; readonly repos: ReadonlyArray<string> }\n | { readonly _tag: \"unreadable\"; readonly text: string }\n\n/** `owner/name#12`, or `12` on its own. */\nconst spelled = /^(?:([^\\s/]+\\/[^\\s/]+)#)?(\\d+)$/\n\n/**\n * A segment of nothing but dots, which no repository is called.\n *\n * The repository names a directory under the state directory before it names\n * anything else, so `../x` would be a way out of it.\n */\nconst onlyDots = /^\\.+$/\n\n/**\n * The pull request a reference names.\n *\n * A reference that spells its repository out is taken as it is, registered or\n * not: reviewing someone else's pull request is a thing to ask for, and the\n * settings a repository nothing registered gets are the global defaults.\n */\nexport const resolve = (text: string, registered: ReadonlyArray<string>): Reference => {\n const found = spelled.exec(text)\n const number = found?.[2]\n if (number === undefined) {\n return { _tag: \"unreadable\", text }\n }\n\n const spelledRepo = found?.[1]\n if (spelledRepo !== undefined && spelledRepo.split(\"/\").some((segment) => onlyDots.test(segment))) {\n return { _tag: \"unreadable\", text }\n }\n\n const repo = spelledRepo ?? (registered.length === 1 ? registered[0] : undefined)\n if (repo === undefined) {\n return { _tag: \"ambiguous\", repos: registered }\n }\n return { _tag: \"resolved\", repo, number: Number(number) }\n}\n","import { Effect, Option } from \"effect\"\nimport { Argument, CliError } from \"effect/unstable/cli\"\n\nimport { prKey, storeFor } from \"#adapters/store.ts\"\nimport { Facts } from \"#domain/bucket.ts\"\nimport type { Reference } from \"#domain/reference.ts\"\nimport { resolve } from \"#domain/reference.ts\"\n\n/** The pull request a command acts on, named the way I actually type it. */\nexport const prArgument = Argument.String(\"pr\").pipe(\n Argument.withDescription(\"The pull request, as 28 or owner/name#28\")\n)\n\n/** What to say about a reference that named no one pull request. */\nconst whyNothingNamed = (reference: Exclude<Reference, { readonly _tag: \"resolved\" }>): string => {\n if (reference._tag === \"unreadable\") {\n return `'${reference.text}' is not a pull request. Name one as 28, or as owner/name#28.`\n }\n const example = `${reference.repos[0] ?? \"owner/name\"}#28`\n return reference.repos.length === 0\n ? `No repositories are registered, so a number alone names nothing. ` +\n `Run dw-mc init inside a repository, or name the pull request as ${example}.`\n : `${reference.repos.length} repositories are registered, so a number alone could be any of them. ` +\n `Name the pull request as ${example}.`\n}\n\n/** The pull request the argument names, or the sentence saying why it names none. */\nexport const named = (pr: string, registered: ReadonlyArray<string>) => {\n const reference = resolve(pr, registered)\n return reference._tag === \"resolved\"\n ? Effect.succeed(reference)\n : Effect.fail(new CliError.UserError({ cause: whyNothingNamed(reference) }))\n}\n\n/**\n * A domain guard's word, as the command's own failure.\n *\n * Every guard in the tool answers the same shape - the sentence saying why not,\n * or null - so turning that answer into a refusal is spelled once here rather\n * than beside each command that asks one.\n */\nexport const refuse = (why: string | null): Effect.Effect<void, CliError.UserError> =>\n why === null ? Effect.void : Effect.fail(new CliError.UserError({ cause: why }))\n\n/**\n * What the last sweep learned about one pull request, or the sentence sending\n * me to a sweep.\n *\n * A command that reads these rather than GitHub says what the table said: the\n * stamp and the cutoff a conversation is measured against are both computed\n * from the facts a sweep wrote down, and asking GitHub again would make them a\n * different answer from the one `dw-mc status` printed.\n *\n * Facts this version cannot read are facts another version of them wrote, and a\n * sweep can write them again, so both cases say the same thing.\n */\nexport const swept = Effect.fn(\"pr.swept\")(function* (repo: string, number: number) {\n const store = yield* storeFor(\"prs\", Facts)\n const facts = yield* Effect.orElseSucceed(store.get(prKey(repo, number)), () => Option.none<Facts>())\n if (Option.isNone(facts)) {\n return yield* new CliError.UserError({\n cause: `Nothing is known about ${repo}#${number} yet. Run dw-mc sweep first.`\n })\n }\n return facts.value\n})\n","import type { Paint } from \"#adapters/paint.ts\"\nimport { truncate } from \"#cli/table.ts\"\nimport type { Bucket, Placed } from \"#domain/bucket.ts\"\n\n/**\n * How one tracked PR is written down, wherever it is written down.\n *\n * The table `dw-mc status` prints and the list the picker asks me to choose\n * from are the same rows, so a pull request reads the same in both and neither\n * command owns how the other draws it.\n *\n * Colour here says one thing: which bucket the pull request is in, and so what\n * it waits on. Everything else on the row is either `dim`, because it is\n * context rather than state, or left alone. A row read with no colour at all\n * says the same, which is what the marker is for.\n *\n * On a table, the pull request opens itself: the reference carries the URL for\n * the terminal to follow, and nothing else on the row does. What it leads to is\n * where the row already says it is, so a row read where no link can be followed\n * - a pipe, a paste, a terminal that ignores the sequence - loses nothing.\n */\n\n/** The glossary's name for each bucket, which is what the heading says. */\nexport const heading: Record<Bucket, string> = {\n \"needs-me\": \"Needs me\",\n \"needs-review-run\": \"Needs review run\",\n \"waiting-on-others\": \"Waiting on others\",\n ready: \"Ready\"\n}\n\n/**\n * The mark that says which bucket a row is in without being read.\n *\n * One character apiece, from the part of Unicode a terminal font has: the\n * padding is counted in characters, and a glyph a terminal draws double width\n * takes a column the count never gave it. How full the mark looks tracks how\n * much of the pull request is done, so the column reads at a glance even where\n * the colour is off.\n */\nexport const marker: Record<Bucket, string> = {\n \"needs-me\": \"●\",\n \"needs-review-run\": \"◐\",\n \"waiting-on-others\": \"○\",\n ready: \"◆\"\n}\n\n/** The colour a bucket is said in: red is mine, yellow is next, green is done, dim is not my turn. */\nexport const tint = (paint: Paint, bucket: Bucket): ((text: string) => string) =>\n ({\n \"needs-me\": paint.red,\n \"needs-review-run\": paint.yellow,\n \"waiting-on-others\": paint.dim,\n ready: paint.green\n })[bucket]\n\n/** Long enough for a conventional-commit subject, short enough to keep a row on one line. */\nexport const titleWidth = 56\n\n/** What sits between two columns: three columns of prose run into one another without a rule. */\nexport const rule = \" │ \"\n\n/**\n * How the bucket is said on a row: glued to the pull request, or a column of\n * its own that names it too.\n *\n * A table has a heading over each bucket, so the mark alone is all a row there\n * needs. A prompt has no headings to group under, so the bucket is named on\n * every row of it.\n */\nexport type Lead = \"marker\" | \"named\"\n\n/**\n * One row: which pull request, what it is, and what it waits on.\n *\n * A stamp is a mark beside the pull request rather than a column of its own, so\n * a table where nothing is stamped is exactly the table it was before: the\n * stamp is a thing I look for, not a thing I read every row of.\n *\n * The title is the only cell with give in it, so how much room it gets is the\n * caller's to say: a table printed down the screen can afford a whole commit\n * subject, and a row inside a prompt has a column more to carry and a frame\n * around it.\n *\n * A named lead carries the colour for the whole row. It is the one place a\n * prompt's row is coloured, and it carries no link at all: a prompt counts the\n * lines it has to erase from the length of what it drew, escape sequences and\n * all, so every colour on a row costs the title characters it could have shown,\n * and a link costs it the whole URL. The table has no such arithmetic to keep\n * straight, so its rows say it in more than one place and open the pull request\n * besides.\n */\nexport const cells = (\n placed: Placed,\n stamped: boolean,\n room: number,\n paint: Paint,\n lead: Lead\n): ReadonlyArray<string> => {\n const { facts } = placed\n const { bucket } = placed.placement\n const say = tint(paint, bucket)\n const reference = `${facts.repo}#${facts.number}`\n const named = lead === \"named\"\n const pr = `${named ? reference : paint.link(reference, facts.url)}${\n facts.draft ? paint.dim(\" (draft)\") : \"\"\n }${stamped ? ` ${paint.green(\"✓\")}` : \"\"}`\n\n return named\n ? [say(`${marker[bucket]} ${heading[bucket]}`), pr, truncate(facts.title, room), placed.placement.reason]\n : [`${say(marker[bucket])} ${pr}`, paint.dim(truncate(facts.title, room)), say(placed.placement.reason)]\n}\n","import { Effect, Schema } from \"effect\"\n\nimport type { CheckEntry } from \"#adapters/gh.ts\"\nimport { GhReadFailed, readJson, unavailable } from \"#adapters/gh.ts\"\nimport { capture } from \"#adapters/spawner.ts\"\n\n/**\n * What GitHub says about a pull request's checks, and the evidence a red one\n * is classified on. Every read here goes through the same `gh` the rest of the\n * tool does; what it owns is the checks, not the boundary.\n */\n\nconst failing = new Set([\"FAILURE\", \"TIMED_OUT\", \"CANCELLED\", \"STARTUP_FAILURE\", \"ACTION_REQUIRED\", \"ERROR\"])\nconst running = new Set([\"QUEUED\", \"IN_PROGRESS\", \"WAITING\", \"PENDING\", \"REQUESTED\", \"EXPECTED\"])\n\nconst nameOf = (entry: CheckEntry): string => entry.name ?? entry.context ?? \"\"\n\nconst checksThatCount = (entries: ReadonlyArray<CheckEntry> | null, ignore: ReadonlyArray<string>) =>\n (entries ?? []).filter((entry) => !ignore.includes(nameOf(entry)))\n\nconst hasFailed = (entry: CheckEntry): boolean => failing.has(entry.conclusion ?? \"\") || failing.has(entry.state ?? \"\")\n\n/**\n * What the rollup comes to: red when anything failed, pending only while\n * nothing has failed yet, green when every check that counts has passed.\n *\n * `ci.ignore` names the checks that do not count towards green, so a check I\n * have decided to live with cannot hold a PR out of Ready.\n */\nexport const rollupState = (\n entries: ReadonlyArray<CheckEntry> | null,\n ignore: ReadonlyArray<string>\n): \"green\" | \"red\" | \"pending\" | \"none\" => {\n const checks = checksThatCount(entries, ignore)\n if (checks.length === 0) {\n return \"none\"\n }\n if (checks.some(hasFailed)) {\n return \"red\"\n }\n if (\n checks.some(\n (entry) => (entry.status !== undefined && entry.status !== \"COMPLETED\") || running.has(entry.state ?? \"\")\n )\n ) {\n return \"pending\"\n }\n return \"green\"\n}\n\n/**\n * The checks that failed and count, which are the ones there is a log to read.\n *\n * `ci.ignore` is applied here as well as in the rollup: a check that cannot\n * hold a PR out of Ready is not one the classifier should be explaining either.\n */\nexport const failedChecks = (\n entries: ReadonlyArray<CheckEntry> | null,\n ignore: ReadonlyArray<string>\n): ReadonlyArray<CheckEntry> => checksThatCount(entries, ignore).filter(hasFailed)\n\n/** Where a check run reports: the workflow run it belongs to, and its job in it. */\nexport interface Reported {\n readonly run: string\n readonly job: string\n}\n\n/**\n * What a check reports on, out of the URL it reports at.\n *\n * A check run details URL ends `/actions/runs/<run>/job/<job>`. The job id is\n * what the logs endpoint takes and the run id is what `gh run rerun` takes, so\n * the two ids the tool needs are the two halves of one URL and are read\n * together. A commit status points somewhere else entirely, which is null:\n * there is no log of ours to read and no run of ours to re-run.\n */\nexport const reportedAt = (detailsUrl: string | undefined): Reported | null => {\n const found = detailsUrl?.match(/\\/actions\\/runs\\/(\\d+)\\/job\\/(\\d+)/)\n return found?.[1] === undefined || found[2] === undefined ? null : { run: found[1], job: found[2] }\n}\n\nconst RepoDefaultBranch = Schema.fromJsonString(\n Schema.Struct({ defaultBranchRef: Schema.NullOr(Schema.Struct({ name: Schema.String })) })\n)\n\n/**\n * The branch a repository merges into, which is the one the first flaky signal\n * asks about. An empty repository has none, and `main` is the better guess than\n * failing the sweep over it.\n */\nexport const defaultBranch = Effect.fnUntraced(function* (repo: string) {\n const view = yield* readJson(\n \"repo view defaultBranchRef\",\n \"gh\",\n [\"repo\", \"view\", repo, \"--json\", \"defaultBranchRef\"],\n RepoDefaultBranch\n )\n return view.defaultBranchRef?.name ?? \"main\"\n})\n\nconst Runs = Schema.fromJsonString(Schema.Array(Schema.Struct({ conclusion: Schema.String })))\n\n/** How far back to look for a run that reached a verdict at all. */\nconst recentRuns = 5\n\n/** `gh run list` reports a conclusion in lower case, unlike every check on a PR. */\nconst failedRun = new Set([\"failure\", \"timed_out\"])\n\n/** A run that decided something. A skipped or cancelled run says nothing either way. */\nconst verdicts = new Set([\"failure\", \"timed_out\", \"success\"])\n\n/**\n * Whether `workflow` is red on `branch` right now.\n *\n * The newest run that reached a verdict is the whole answer: a workflow that\n * broke last week and was fixed since is not red, and excusing a pull request\n * for it would hide a failure that is real. A handful of runs are asked for\n * because the newest ones are often skipped by a path filter.\n */\nexport const workflowFailsOn = Effect.fnUntraced(function* (repo: string, branch: string, workflow: string) {\n const runs = yield* readJson(\n \"run list\",\n \"gh\",\n [\n \"run\",\n \"list\",\n \"--repo\",\n repo,\n \"--branch\",\n branch,\n \"--workflow\",\n workflow,\n \"--limit\",\n String(recentRuns),\n \"--json\",\n \"conclusion\"\n ],\n Runs\n )\n // `gh run list` answers newest first.\n const newest = runs.find((run) => verdicts.has(run.conclusion))\n return newest !== undefined && failedRun.has(newest.conclusion)\n})\n\nconst PrFiles = Schema.fromJsonString(Schema.Struct({ files: Schema.Array(Schema.Struct({ path: Schema.String })) }))\n\n/** The repository paths a pull request changes. */\nexport const prFiles = Effect.fnUntraced(function* (repo: string, number: number) {\n const view = yield* readJson(\n \"pr view files\",\n \"gh\",\n [\"pr\", \"view\", String(number), \"--repo\", repo, \"--json\", \"files\"],\n PrFiles\n )\n return view.files.map((file) => file.path)\n})\n\n/**\n * How much of a failing job's log is kept.\n *\n * A job that failed prints what went wrong at the end, so the tail is the part\n * worth classifying, and a build that logged a whole dependency tree is not\n * worth holding in memory beyond it.\n */\nconst logTailBytes = 64 * 1024\n\n/**\n * What one failing job printed, from the end.\n *\n * `gh api` refuses a response carrying terminal escape sequences unless it is\n * told otherwise, and a runner log is full of them. Verified by running it: the\n * endpoint answers with the plain log once the flag is passed.\n */\nexport const jobLog = Effect.fnUntraced(function* (repo: string, jobId: string) {\n const log = yield* capture(\"gh\", [\n \"api\",\n `repos/${repo}/actions/jobs/${jobId}/logs`,\n \"--allow-escape-sequences\"\n ]).pipe(\n Effect.catchTags({\n PlatformError: (error) => Effect.fail(unavailable(error)),\n CommandFailed: (error) => Effect.fail(new GhReadFailed({ command: \"api job logs\", detail: error.stderr }))\n })\n )\n return log.length <= logTailBytes ? log : log.slice(-logTailBytes)\n})\n\n/**\n * The workflow runs behind the failing checks that count, each named once.\n *\n * One broken run usually fails several jobs, and re-running it once per failing\n * job would start the same run over and over.\n *\n * `ci.ignore` decides which checks get a run into this list, and no more than\n * that: a run is re-run whole, so an ignored job sharing a run with a counted\n * one is re-run beside it. What the setting buys is that an ignored check is\n * never on its own a reason to spend CI minutes.\n */\nexport const failedRuns = (\n entries: ReadonlyArray<CheckEntry> | null,\n ignore: ReadonlyArray<string>\n): ReadonlyArray<string> => [\n ...new Set(\n failedChecks(entries, ignore).flatMap((check) => {\n const reported = reportedAt(check.detailsUrl)\n return reported === null ? [] : [reported.run]\n })\n )\n]\n\n/**\n * Asks GitHub to run one workflow run's failed jobs again.\n *\n * `--failed` is what makes this cheap: the jobs that passed are not run a\n * second time, so a flaky job costs the minutes it costs and no more. This is a\n * write to GitHub, and it is one of the three ADR 0002 allows.\n */\nexport const rerunFailed = Effect.fnUntraced(function* (repo: string, runId: string) {\n yield* capture(\"gh\", [\"run\", \"rerun\", runId, \"--repo\", repo, \"--failed\"]).pipe(\n Effect.catchTags({\n PlatformError: (error) => Effect.fail(unavailable(error)),\n CommandFailed: (error) => Effect.fail(new GhReadFailed({ command: \"run rerun\", detail: error.stderr }))\n })\n )\n})\n","import { Effect } from \"effect\"\n\nimport { defaultBranch, failedChecks, jobLog, prFiles, reportedAt, workflowFailsOn } from \"#adapters/ci.ts\"\nimport type { CheckEntry } from \"#adapters/gh.ts\"\n\n/**\n * Everything the classifier is allowed to know about one red CI.\n *\n * All three are facts a sweep reads off GitHub, which is what keeps the verdict\n * reproducible: the same evidence always yields the same answer.\n */\nexport interface Evidence {\n /** The workflows failing here that are failing on the default branch as well. */\n readonly alsoRedOnDefaultBranch: ReadonlyArray<string>\n /** The files the pull request changes, as repository paths. */\n readonly changedFiles: ReadonlyArray<string>\n /** What the failing jobs printed. */\n readonly log: string\n}\n\n/** Whether a red CI is mine to fix. */\nexport type Classification = \"flaky\" | \"legitimate\"\n\n/** What the classifier decided, and the sentence that says why. */\nexport interface Verdict {\n readonly classification: Classification\n readonly reason: string\n}\n\n/**\n * The failures that are flaky wherever they appear: a machine, a network or a\n * runner giving up, never a test disagreeing with the code.\n *\n * `ci.flaky_patterns` adds to this list rather than replacing it, because the\n * failures a repository of mine produces are extra ones, not different ones.\n */\nexport const builtInPatterns: ReadonlyArray<string> = [\n \"timed out\",\n \"deadline exceeded\",\n \"ETIMEDOUT\",\n \"ECONNRESET\",\n \"ECONNREFUSED\",\n \"connection refused\",\n \"socket hang up\",\n \"lock timeout\",\n \"could not obtain lock\",\n \"runner lost communication\",\n \"The runner has received a shutdown signal\",\n \"net/http: request canceled\",\n \"ResourceExhausted\",\n \"Too many open files\",\n \"no space left on device\"\n]\n\nconst baseName = (path: string): string => path.slice(path.lastIndexOf(\"/\") + 1)\n\n/**\n * The changed file the log names, preferring one it spells in full.\n *\n * A bare file name is worth matching - a stack trace often prints nothing else\n * - and it is worth matching second, because a name as ordinary as `index.ts`\n * belongs to more repositories than mine.\n */\nconst escaped = (text: string): string => text.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\")\n\n/**\n * Whether the log names a file called `base` rather than some longer name\n * ending in it: a changed `src/a.ts` is not what a log printing `data.ts` is\n * complaining about.\n */\nconst namesFile = (log: string, base: string): boolean => new RegExp(`(^|[^\\\\w.-])${escaped(base)}`).test(log)\n\nconst namedChangedFile = (log: string, changedFiles: ReadonlyArray<string>): string | null =>\n changedFiles.find((file) => log.includes(file)) ?? changedFiles.find((file) => namesFile(log, baseName(file))) ?? null\n\n/**\n * The flaky pattern the log matches, mine before the built-in ones.\n *\n * A pattern is text and not a regular expression: it comes out of a\n * configuration file I edit by hand, where a stray `*` should cost me a missed\n * match and never a crash.\n */\nconst matchedPattern = (log: string, patterns: ReadonlyArray<string>): string | null => {\n const haystack = log.toLowerCase()\n return [...patterns, ...builtInPatterns].find((pattern) => haystack.includes(pattern.toLowerCase())) ?? null\n}\n\n/**\n * Whether a red CI is mine to fix, and why.\n *\n * Two of the signals say flaky and one says legitimate, and the one outranks\n * the two: a log that names a file this pull request changes is the failure\n * pointing at my own work, and a workflow that is broken everywhere does not\n * stop it pointing there.\n *\n * Everything else that is unexplained is mine as well. The two mistakes do not\n * cost the same - a real failure called flaky is a broken pull request nobody\n * tells me about, while a flake called mine costs me one look - so the default\n * is the one I can recover from.\n */\nexport const classify = (evidence: Evidence, flakyPatterns: ReadonlyArray<string>): Verdict => {\n const named = namedChangedFile(evidence.log, evidence.changedFiles)\n if (named !== null) {\n return { classification: \"legitimate\", reason: `the log names ${named}, which this PR changes` }\n }\n\n const redOnDefaultBranch = evidence.alsoRedOnDefaultBranch[0]\n const pattern = matchedPattern(evidence.log, flakyPatterns)\n const excuses = [\n redOnDefaultBranch === undefined ? null : `${redOnDefaultBranch} is red on the default branch too`,\n pattern === null ? null : `the log matches \"${pattern}\"`\n ].filter((it) => it !== null)\n\n return excuses.length === 0\n ? { classification: \"legitimate\", reason: \"nothing explains the failure\" }\n : { classification: \"flaky\", reason: excuses.join(\", and \") }\n}\n\n/**\n * How many failing jobs the log is read from.\n *\n * One broken workflow usually fails several jobs with the same cause, and the\n * logs are the one read here that is measured in megabytes.\n */\nconst loggedJobs = 3\n\n/** The values of `xs` that `f` has one for. */\nconst filterMap = <A, B>(xs: ReadonlyArray<A>, f: (a: A) => B | null): ReadonlyArray<B> =>\n xs.flatMap((x) => {\n const b = f(x)\n return b === null ? [] : [b]\n })\n\n/** No evidence at all, which is what an unreadable CI comes to. */\nconst nothing: Evidence = { alsoRedOnDefaultBranch: [], changedFiles: [], log: \"\" }\n\n/**\n * What a red CI looks like to the classifier.\n *\n * A read that fails costs its own signal and nothing else. GitHub drops an\n * Actions log after ninety days, so a pull request open that long would\n * otherwise lose its row over a log nobody can fetch any more - and a missing\n * signal only ever moves the verdict towards legitimate, which is the answer\n * that puts the pull request in front of me rather than hiding it.\n */\nexport const evidenceFor = Effect.fn(\"flaky.evidenceFor\")(function* (\n repo: string,\n number: number,\n entries: ReadonlyArray<CheckEntry> | null,\n ignore: ReadonlyArray<string>\n) {\n const failed = failedChecks(entries, ignore)\n const workflows = [...new Set(filterMap(failed, (check) => check.workflowName ?? null))]\n const jobs = filterMap(failed, (check) => reportedAt(check.detailsUrl)?.job ?? null).slice(0, loggedJobs)\n\n const branch = yield* Effect.orElseSucceed(defaultBranch(repo), () => null)\n if (branch === null) {\n return nothing\n }\n\n const [alsoRed, changedFiles, logs] = yield* Effect.all(\n [\n Effect.forEach(workflows, (workflow) =>\n Effect.map(\n Effect.orElseSucceed(workflowFailsOn(repo, branch, workflow), () => false),\n (red) => (red ? [workflow] : [])\n )\n ),\n Effect.orElseSucceed(prFiles(repo, number), (): ReadonlyArray<string> => []),\n Effect.forEach(jobs, (job) => Effect.orElseSucceed(jobLog(repo, job), () => \"\"))\n ],\n { concurrency: 3 }\n )\n\n return { alsoRedOnDefaultBranch: alsoRed.flat(), changedFiles, log: logs.join(\"\\n\") } satisfies Evidence\n})\n\n/**\n * Why a red CI is excused, or null where it is mine to fix.\n *\n * Reading the evidence and classifying it is one act, so it is one function:\n * a sweep writes what it returns down as `ciFlaky`, and `dw-mc rerun` asks it\n * again live. Two callers asking the same question have to get the same answer,\n * which they cannot if each of them spells the question out.\n */\nexport const flakyReason = Effect.fn(\"flaky.flakyReason\")(function* (\n repo: string,\n number: number,\n entries: ReadonlyArray<CheckEntry> | null,\n ignore: ReadonlyArray<string>,\n patterns: ReadonlyArray<string>\n) {\n const verdict = classify(yield* evidenceFor(repo, number, entries, ignore), patterns)\n return verdict.classification === \"flaky\" ? verdict.reason : null\n})\n","import type { ChecksState, Facts } from \"#domain/bucket.ts\"\nimport type { Moment } from \"#domain/moment.ts\"\nimport { isSame } from \"#domain/moment.ts\"\n\n/**\n * The three signals that say whether a tracked PR has moved at all.\n *\n * They are the cheap facts: a sweep can read them without paging through a PR's\n * history, which is the whole point of comparing them.\n */\nexport interface Pulse {\n readonly head: string\n readonly checks: ChecksState\n readonly newestHumanCommentAt: Moment\n}\n\n/** The pulse of a PR a previous sweep recorded. */\nexport const pulseOf = (facts: Facts): Pulse => ({\n head: facts.head,\n checks: facts.checks,\n newestHumanCommentAt: facts.newestHumanCommentAt\n})\n\n/**\n * Whether a PR is where the last sweep left it.\n *\n * A quiet PR keeps the facts it already had rather than being read out again,\n * so a sweep over many pull requests spends its time on the few that moved.\n */\nexport const isQuiet = (previous: Pulse, current: Pulse): boolean =>\n previous.head === current.head &&\n previous.checks === current.checks &&\n isSame(previous.newestHumanCommentAt, current.newestHumanCommentAt)\n","import { Effect, Option, Schema } from \"effect\"\n\nimport { prKey, storeFor } from \"#adapters/store.ts\"\nimport type { ChecksState } from \"#domain/bucket.ts\"\n\n/** One open pull request as a stack is read from: the branch it stands on and the one it merges into. */\nexport interface Branches {\n readonly number: number\n readonly head: string\n readonly base: string\n}\n\n/** Where a pull request sits among the pull requests built on each other. */\nexport interface Position {\n readonly position: number\n readonly length: number\n}\n\n/**\n * How many pull requests this one stands on.\n *\n * A branch is walked to what it merges into and on from there, however deep the\n * stack goes. Every pull request the walk has already counted is left alone,\n * which is what keeps two branches that merge into each other from being walked\n * around forever.\n */\nconst ancestorsOf = (pr: Branches, open: ReadonlyArray<Branches>, seen: Set<number>): number => {\n let count = 0\n let current = pr\n for (;;) {\n const parent = open.find((it) => it.head === current.base && !seen.has(it.number))\n if (parent === undefined) {\n return count\n }\n seen.add(parent.number)\n count += 1\n current = parent\n }\n}\n\n/**\n * How deep the stack goes above this pull request.\n *\n * Two branches cut from the same one are not two stacks deep, they are two\n * branches, so what counts is the deepest single line of them rather than how\n * many pull requests stand above it in total.\n */\nconst descendantsOf = (pr: Branches, open: ReadonlyArray<Branches>, seen: Set<number>): number => {\n let deepest = 0\n for (const child of open.filter((it) => it.base === pr.head && !seen.has(it.number))) {\n seen.add(child.number)\n deepest = Math.max(deepest, 1 + descendantsOf(child, open, seen))\n }\n return deepest\n}\n\n/**\n * Where a pull request sits in its stack, or null where it is in none.\n *\n * A stack is read off the branches alone: a pull request that merges into\n * another pull request's branch, or that another one merges into, is part of\n * one. The tool does not understand stacks and never drives them, so this\n * exists to recognise one and say where the pull request sits in it.\n */\nexport const stackOf = (number: number, open: ReadonlyArray<Branches>): Position | null => {\n const pr = open.find((it) => it.number === number)\n if (pr === undefined) {\n return null\n }\n const seen = new Set([number])\n const below = ancestorsOf(pr, open, seen)\n const above = descendantsOf(pr, open, seen)\n return below === 0 && above === 0 ? null : { position: below + 1, length: below + above + 1 }\n}\n\n/** What every guard about the branch itself reads, whatever is about to be done to it. */\nexport interface Branch {\n readonly repo: string\n readonly number: number\n /** Whether I opened the pull request, which is the only kind whose branch is mine to push. */\n readonly mine: boolean\n /** Whether the head branch lives in a fork rather than in the repository that was read. */\n readonly fromFork: boolean\n /** Whether the pull request was among the open ones the stack was read from. */\n readonly listed: boolean\n readonly stack: Position | null\n}\n\n/**\n * Why this branch is nobody's to touch here, or null where it is mine.\n *\n * These are the guards about the branch rather than about what is done to it,\n * which is why they are their own and why they say nothing about pushing: who\n * authored the pull request and where its branch lives is the boundary itself -\n * a branch somebody else authored and a branch in a fork are not mine to work\n * on, whatever else is true of them and whichever command asks. A stack comes\n * next, and a pull request the stack was not read from counts as one, because a\n * stack the tool cannot see is one it could drive: the tool does not understand\n * stacks, so the one thing it has to say about one is where the pull request\n * sits in it.\n */\nexport const boundary = (branch: Branch): string | null => {\n const where = `${branch.repo}#${branch.number}`\n if (!branch.mine) {\n return `${where} is not mine. dw-mc works on branches I author and on nothing else.`\n }\n if (branch.fromFork) {\n return (\n `${where} is opened from a fork, so its branch is not in ${branch.repo}. ` +\n `dw-mc works only on a branch in the repository it read.`\n )\n }\n if (!branch.listed) {\n return (\n `${where} was not among the open pull requests of ${branch.repo}, so nothing here can say whether ` +\n `it is in a stack. Read it again before touching the branch.`\n )\n }\n if (branch.stack !== null) {\n return (\n `${where} is ${branch.stack.position} of ${branch.stack.length} in a stack. ` +\n `dw-mc does not understand stacks and will not drive one; rebase it with whatever built the stack.`\n )\n }\n return null\n}\n\n/** Everything the rebase guards are allowed to know about a pull request. */\nexport interface Situation extends Branch {\n /** The branch the pull request merges into, which is what it would be rebased onto. */\n readonly base: string\n readonly enabled: boolean\n readonly checks: ChecksState\n}\n\n/**\n * Why this branch is not one to rebase, or null where it is.\n *\n * This is the single place the guards live, and they matter more than the\n * rebase itself: a force push is the one write the tool makes that can lose\n * work, and every rule here is about it never being a surprise.\n *\n * Being off is said first, because a repository that has not turned rebase on\n * has decided the question and nothing else about the pull request changes it.\n * The branch's own guards come next. CI is last and costs the most to get\n * wrong - rebasing while a run is in flight cancels the run I am waiting on,\n * and a red build is mine to fix where it is.\n */\nexport const decide = (situation: Situation): string | null => {\n const where = `${situation.repo}#${situation.number}`\n if (!situation.enabled) {\n return (\n `Rebase is off for ${situation.repo}. Set rebase.enabled: true for it in the config to turn it on, ` +\n `so a force push is never a surprise.`\n )\n }\n const refused = boundary(situation)\n if (refused !== null) {\n return refused\n }\n if (situation.checks === \"pending\") {\n return `CI is still running on ${where}. A rebase now would cancel the run you are waiting on.`\n }\n if (situation.checks === \"red\") {\n return `CI is red on ${where}, which is yours to fix before the branch moves.`\n }\n return null\n}\n\n/**\n * A rebase that conflicted: the head it conflicted at and the files it stopped\n * on.\n *\n * The head is what the record is scoped to, as it is for a withdrawn stamp: a\n * conflict is about the code the branch is at, so it lasts exactly as long as\n * that code is what the pull request is. A branch that moved is a branch\n * nothing here has tried to rebase yet.\n *\n * The paths are what makes the conflict something to open: `a rebase\n * conflicted` cannot tell a stale lockfile from half the pull request. They are\n * an optional key rather than a required one so a record an older version wrote\n * still reads, and a conflict with no paths still puts the pull request in\n * Needs me.\n */\nexport const Conflict = Schema.Struct({\n head: Schema.String,\n paths: Schema.optionalKey(Schema.Array(Schema.String))\n})\nexport type Conflict = typeof Conflict.Type\n\n/**\n * The conflict a rebase last left on this pull request, or null where it left\n * none.\n *\n * A record this version cannot read is one another version of it wrote, and a\n * conflict is worth a bucket rather than a failed sweep: forgetting it costs\n * the pull request one reason to be in Needs me, where failing here would cost\n * me the whole table.\n */\nexport const conflictFor = Effect.fn(\"rebase.conflictFor\")(function* (repo: string, number: number) {\n const store = yield* storeFor(\"rebases\", Conflict)\n const conflict = yield* Effect.orElseSucceed(store.get(prKey(repo, number)), () => Option.none<Conflict>())\n return Option.getOrNull(conflict)\n})\n\n/** Writes down that a rebase of `head` conflicted on `paths`, which is the only head it holds for. */\nexport const recordConflict = Effect.fn(\"rebase.recordConflict\")(function* (\n repo: string,\n number: number,\n head: string,\n paths: ReadonlyArray<string>\n) {\n const store = yield* storeFor(\"rebases\", Conflict)\n yield* store.set(prKey(repo, number), { head, paths })\n})\n","import { Schema, SchemaRepresentation, SchemaTransformation } from \"effect\"\n\nimport { Severity } from \"#adapters/config.ts\"\n\n/** Whether a review run found anything at all. */\nexport const Verdict = Schema.Literals([\"clean\", \"findings\"])\nexport type Verdict = typeof Verdict.Type\n\n/**\n * Every severity word a review may answer with.\n *\n * The first three are ours, and the only ones a run is asked for. The rest are\n * the persona a run with no slash command carries, which grades in its own\n * words: a turn that comes back in them is worth reading rather than throwing\n * away.\n */\nconst Spelling = Schema.Literals([\"error\", \"warning\", \"info\", \"Critical\", \"Required\", \"Optional\", \"Nit\", \"FYI\"])\n\n/** What each of those words weighs. The record is exhaustive, so neither list can drift. */\nconst severityOf: Record<typeof Spelling.Type, Severity> = {\n error: \"error\",\n warning: \"warning\",\n info: \"info\",\n Critical: \"error\",\n Required: \"error\",\n Optional: \"warning\",\n Nit: \"info\",\n FYI: \"info\"\n}\n\nconst Weighed = Spelling.pipe(\n Schema.decodeTo(\n Severity,\n SchemaTransformation.transform({\n decode: (word: typeof Spelling.Type) => severityOf[word],\n encode: (severity: Severity): typeof Spelling.Type => severity\n })\n )\n)\n\n/** The fields both spellings of a finding share. Only the severity differs. */\nconst shared = { file: Schema.String, line: Schema.Int, summary: Schema.String }\n\n/** One problem a review run reports, at a file and line. */\nexport const Finding = Schema.Struct({ ...shared, severity: Severity })\nexport type Finding = typeof Finding.Type\n\n/**\n * What a review run found: the shape the tool keeps, and the one a fix session\n * is later handed.\n */\nexport const Findings = Schema.Struct({\n verdict: Verdict,\n findings: Schema.Array(Finding)\n})\nexport type Findings = typeof Findings.Type\n\n/**\n * The same findings as a runner may spell them, which is what the second turn's\n * output is read with.\n *\n * A word nothing maps fails here, and a failed read is a failure of the run:\n * findings the tool cannot weigh are not findings it can act on.\n */\nexport const Reported = Schema.Struct({\n verdict: Verdict,\n findings: Schema.Array(Schema.Struct({ ...shared, severity: Weighed }))\n})\n\n/**\n * The schema every runner must satisfy, as the JSON Schema a runner is handed.\n *\n * It is derived from the schema the findings are kept under rather than written\n * out beside it, so a runner is asked for exactly the shape that is persisted.\n * `Reported` is wider on purpose and only on the severity: what a runner is\n * asked for is our three words, and a persona's five are read where they arrive\n * anyway rather than being asked for.\n */\nexport const jsonSchema: string = JSON.stringify(\n SchemaRepresentation.toJsonSchemaDocument(SchemaRepresentation.toRepresentation(Findings.ast)).schema\n)\n\n/**\n * The findings as the Markdown a report is written in.\n *\n * It is what a schema-held run's report says: with a schema in force a run\n * answers in findings and not in prose, so the report kept beside it is written\n * from the findings themselves rather than left empty.\n */\nexport const asMarkdown = (found: Findings): string =>\n found.findings.length === 0\n ? \"Clean: the run found nothing to report.\"\n : found.findings\n .map((finding) => `- \\`${finding.file}:${finding.line}\\` ${finding.severity}: ${finding.summary}`)\n .join(\"\\n\")\n\n/** Where each severity sits against the others, so the bar can be compared with it. */\nconst rank: Record<Severity, number> = { info: 0, warning: 1, error: 2 }\n\n/**\n * The findings that withhold the stamp: everything at `blocksOn` or above it.\n *\n * `stamp.blocks_on` is my bar rather than a constant, so a repository whose\n * warnings I do not want to merge past is configured rather than coded. An\n * error blocks wherever the bar is, because nothing weighs more than one.\n */\nexport const blocking = (findings: ReadonlyArray<Finding>, blocksOn: Severity): ReadonlyArray<Finding> =>\n findings.filter((finding) => rank[finding.severity] >= rank[blocksOn])\n","// `Path` from `effect` joins and resolves paths and has no glob matcher. This is\n// the platform's own, and matching a `docs_only` glob against a repository path\n// reads nothing and decides nothing about this machine.\n// oxlint-disable-next-line effecttsgo/node-builtin-import\nimport { matchesGlob } from \"node:path\"\n\nimport { DateTime, Effect, Option, Schema } from \"effect\"\n\nimport type { Settings, Severity } from \"#adapters/config.ts\"\nimport { Effort } from \"#adapters/config.ts\"\nimport { storeFor } from \"#adapters/store.ts\"\nimport type { Findings } from \"#domain/findings.ts\"\nimport { blocking, Finding, Verdict } from \"#domain/findings.ts\"\n\n/**\n * What a review run came to, which is what its second turn reported.\n *\n * A failure is recorded as one and is never a clean verdict: a turn that exited\n * badly, ran out of patience or answered in a shape that does not validate has\n * found nothing, which is not the same as having found nothing wrong.\n */\nexport const Outcome = Schema.Union([\n Schema.TaggedStruct(\"reported\", { verdict: Verdict, findings: Schema.Array(Finding) }),\n Schema.TaggedStruct(\"failed\", { detail: Schema.String })\n])\nexport type Outcome = typeof Outcome.Type\n\n/**\n * One review run against a tracked PR at a specific head commit.\n *\n * It is a schema because a review run outlives the command that started it: the\n * state directory is where the next sweep learns that this head has been\n * reviewed, and where a fix session finds what there is to fix.\n */\nexport const ReviewRun = Schema.Struct({\n repo: Schema.String,\n number: Schema.Int,\n /** The head the run covers. A run never vouches for code it did not see. */\n head: Schema.String,\n /**\n * The slash command line the run opened on, or null where it opened on the\n * tool's own prompt. A report found months later says what it was asked, and a\n * record an earlier version wrote carries no such field and is forgotten.\n */\n command: Schema.NullOr(Schema.String),\n effort: Schema.NullOr(Effort),\n /**\n * The agent session the run happened in, or null where it never reached one.\n *\n * A run that would not start or exited before it said anything has no session,\n * and the run is still recorded: a failure is recorded as what it is.\n */\n sessionId: Schema.NullOr(Schema.String),\n ranAt: Schema.DateTimeUtcFromString,\n outcome: Outcome\n})\nexport type ReviewRun = typeof ReviewRun.Type\n\n/** A head as it is read out loud: the seven characters git itself abbreviates to. */\nexport const short = (head: string): string => head.slice(0, 7)\n\n/**\n * Where a run is kept: one key per head, so a run and the code it read cannot\n * drift apart, and a re-review replaces the run before it.\n */\nexport const runKey = (repo: string, number: number, head: string): string => `${repo}#${number}@${head}`\n\n/** Where the run's report is kept: beside the run, as the Markdown it is. */\nexport const reportKey = (repo: string, number: number, head: string): string => `${runKey(repo, number, head)}.md`\n\n/**\n * Which head a pull request was last reviewed at: an index beside `runKey` and\n * `reportKey` rather than a thing the glossary names.\n *\n * A run is kept under the head it read, which answers the question a sweep asks\n * of one head. The re-run rule and `dw-mc findings` ask the other one - which\n * head the last run was at - and this is where they read it, so neither has to\n * ask GitHub what is current before it can look anything up.\n */\nexport const LastReviewed = Schema.Struct({ head: Schema.String })\nexport type LastReviewed = typeof LastReviewed.Type\n\n/** Where that head is kept. No head is spelled `latest`, so nothing collides. */\nexport const latestKey = (repo: string, number: number): string => `${repo}#${number}@latest`\n\n/**\n * The run at one head, or none where nothing has reviewed it.\n *\n * A head is where the question is asked - the stamp, the bucket and `dw-mc\n * findings` all ask about one commit - and one read off the disk answers it\n * without an index to keep in step.\n *\n * A run this version cannot read is a run another version of this record wrote,\n * and the state directory is a cache of work that can be done again: forgetting\n * it costs one review, where failing here would cost me the command I asked for.\n */\nexport const runAt = Effect.fn(\"review.runAt\")(function* (repo: string, number: number, head: string) {\n const runs = yield* storeFor(\"runs\", ReviewRun)\n return yield* Effect.orElseSucceed(runs.get(runKey(repo, number, head)), () => Option.none<ReviewRun>())\n})\n\n/** The last review run on a pull request, or none where it has had none. */\nexport const lastRun = Effect.fn(\"review.lastRun\")(function* (repo: string, number: number) {\n const heads = yield* storeFor(\"runs\", LastReviewed)\n const at = yield* Effect.orElseSucceed(heads.get(latestKey(repo, number)), () => Option.none<LastReviewed>())\n return Option.isNone(at) ? Option.none<ReviewRun>() : yield* runAt(repo, number, at.value.head)\n})\n\n/**\n * What a run reported, or null where it reported nothing at all.\n *\n * A failure is not a clean verdict: a run that could not report has found\n * nothing, which is not the same as having found nothing wrong. Everything that\n * reads a run's findings reads them through here, so the distinction is drawn\n * once rather than at every caller that might forget it.\n */\nexport const reportedBy = (run: ReviewRun): Findings | null =>\n run.outcome._tag === \"reported\" ? { verdict: run.outcome.verdict, findings: run.outcome.findings } : null\n\n/**\n * Why a run reported nothing, or null where it reported.\n *\n * The sibling of `reportedBy`, and here for the same reason: the two halves of\n * an outcome are read through one place each rather than re-narrowed at every\n * caller.\n */\nexport const detailOf = (run: ReviewRun): string | null => (run.outcome._tag === \"failed\" ? run.outcome.detail : null)\n\n/**\n * Whether the files changed since the last run are worth paying for another.\n *\n * The question is deliberately about what changed rather than how much: one\n * line outside the `docs_only` globs is code nobody has reviewed, and a\n * thousand lines inside them are still prose.\n */\nexport const worthRerunning = (changed: ReadonlyArray<string>, docsOnly: ReadonlyArray<string>): boolean =>\n changed.some((file) => !docsOnly.some((glob) => matchesGlob(file, glob)))\n\n/** Everything the re-run rule is allowed to know about the run that was asked for. */\nexport interface Asked {\n /** The last run on this pull request, or null where it has had none. */\n readonly last: ReviewRun | null\n /** The head the run would cover. */\n readonly head: string\n /** What changed since the last run's head, or null where GitHub would not say. */\n readonly changed: ReadonlyArray<string> | null\n}\n\n/**\n * The re-run rule: the head this run is skipped against, or null where it runs.\n *\n * A review costs real money and minutes of my attention, and a typo fix is not\n * worth either. Four things are never skipped, because the rule is here to save\n * me a review and not to stand between me and one I asked for: a pull request\n * with no run behind it, a run that reported nothing, a comparison GitHub would\n * not answer, and anything that changed outside the globs. A head that has\n * already had a run changed nothing at all, which is the one case that needs no\n * comparison to decide.\n */\nexport const skippedSince = (asked: Asked, docsOnly: ReadonlyArray<string>): string | null => {\n if (asked.last === null || reportedBy(asked.last) === null) {\n return null\n }\n const changed = asked.last.head === asked.head ? [] : asked.changed\n return changed === null || worthRerunning(changed, docsOnly) ? null : asked.last.head\n}\n\n/** What a run was opened on, as the report says it. */\nexport const askedOf = (run: ReviewRun): string =>\n run.command === null ? \"the tool's own prompt\" : [run.command, run.effort].filter((part) => part !== null).join(\" \")\n\n/**\n * The report as it is written down: what it is of, then what the run said.\n *\n * The heading is the whole point of writing it rather than storing the prose\n * alone - a file found months later says which pull request, which commit and\n * what the run was asked, without anything else having to be open.\n */\nexport const reportDocument = (run: ReviewRun, title: string, prose: string): string =>\n [\n `# ${run.repo}#${run.number} ${title}`,\n \"\",\n `- head: ${run.head}`,\n `- run: ${askedOf(run)}`,\n `- ran: ${DateTime.formatIso(run.ranAt)}`,\n \"\",\n prose.trim(),\n \"\"\n ].join(\"\\n\")\n\n/**\n * Whether `head` has the review it needs.\n *\n * A run that reported nothing does not count, which is the same rule\n * `reportedBy` draws everywhere else: a failure has found nothing, not found\n * nothing wrong.\n */\nexport const reviewedBy = (run: ReviewRun | null): boolean => run !== null && reportedBy(run) !== null\n\n/** The findings at one head that withhold the stamp. */\nexport const blockingIn = (run: ReviewRun | null, blocksOn: Severity): ReadonlyArray<Finding> => {\n const found = run === null ? null : reportedBy(run)\n return found === null ? [] : blocking(found.findings, blocksOn)\n}\n\n/** What the stamp rule reads out of the review runs this machine holds on one head. */\nexport interface Reviewed {\n /** The head a review run has already covered, or null where none has. */\n readonly reviewRunHead: string | null\n /** Findings on that head that withhold the stamp, at the bar `stamp.blocks_on` sets. */\n readonly blockingFindings: number\n}\n\n/**\n * What the review runs on `head` say about it, for the stamp to rest on.\n *\n * Whether a head has been reviewed is the runs' to say and no sweep's: a run is\n * recorded against one head, and a head with no run of its own has not been\n * reviewed however many sweeps have seen the pull request. A run that could not\n * report findings does not count either: its verdict is what takes a pull\n * request out of Needs review run, and it reached none.\n *\n * It is one function because the two callers are a sweep and `dw-mc merge`, and\n * the second exists to land what the first only describes: two spellings of\n * this would be two answers to whether a head has been reviewed.\n */\nexport const reviewedAt = Effect.fn(\"review.reviewedAt\")(function* (\n repo: string,\n number: number,\n head: string,\n settings: Settings\n) {\n const run = Option.getOrNull(yield* runAt(repo, number, head))\n return {\n reviewRunHead: reviewedBy(run) ? head : null,\n blockingFindings: blockingIn(run, settings.stamp.blocks_on).length\n } satisfies Reviewed\n})\n","import type { DateTime } from \"effect\"\nimport { Console, Effect, Option } from \"effect\"\nimport { CliError, Command } from \"effect/unstable/cli\"\nimport type { KeyValueStore } from \"effect/unstable/persistence\"\n\nimport { rollupState } from \"#adapters/ci.ts\"\nimport type { ConfigFile, Settings } from \"#adapters/config.ts\"\nimport { read as readConfig, settingsFor } from \"#adapters/config.ts\"\nimport type { Comment, Found } from \"#adapters/gh.ts\"\nimport {\n mergeabilityOf,\n prComments,\n prCommits,\n prReviews,\n prView,\n reviewDecisionOf,\n searchPrs,\n viewer\n} from \"#adapters/gh.ts\"\nimport { prKey, storeFor } from \"#adapters/store.ts\"\nimport { count } from \"#cli/table.ts\"\nimport type { Facts } from \"#domain/bucket.ts\"\nimport { Facts as FactsSchema } from \"#domain/bucket.ts\"\nimport { flakyReason } from \"#domain/flaky.ts\"\nimport { newest } from \"#domain/moment.ts\"\nimport { isQuiet, pulseOf } from \"#domain/quiet.ts\"\nimport { conflictFor } from \"#domain/rebase.ts\"\nimport { reviewedAt } from \"#domain/review.ts\"\n\n/** Something a sweep could not read, and what GitHub said about it. */\nexport interface Trouble {\n readonly where: string\n readonly detail: string\n}\n\n/** What one pass over every tracked PR came back with. */\nexport interface Report {\n readonly repos: ReadonlyArray<string>\n readonly facts: ReadonlyArray<Facts>\n readonly troubles: ReadonlyArray<Trouble>\n}\n\ntype Store = KeyValueStore.SchemaStore<typeof FactsSchema>\n\nconst writtenBy = (comments: ReadonlyArray<Comment>, login: string): ReadonlyArray<DateTime.Utc> =>\n comments.filter((comment) => comment.login === login).map((comment) => comment.at)\n\nconst byHumansOtherThan = (comments: ReadonlyArray<Comment>, login: string): ReadonlyArray<DateTime.Utc> =>\n comments.filter((comment) => !comment.bot && comment.login !== login).map((comment) => comment.at)\n\n/**\n * The facts about one tracked PR, read from GitHub and kept on disk.\n *\n * The cheap reads happen every time, because they are what says whether the PR\n * moved. The commits are asked for only when it did: `gh` returns every commit\n * message in full, and on a PR that is where the last sweep left it that whole\n * read buys a timestamp the state directory already has.\n */\nconst sweepPr = Effect.fn(\"sweep.pullRequest\")(function* (store: Store, me: string, found: Found, settings: Settings) {\n const view = yield* prView(found.repo, found.number)\n const [onThePr, inReviews] = yield* Effect.all(\n [prComments(found.repo, found.number), prReviews(found.repo, found.number)],\n { concurrency: 2 }\n )\n const comments = [...onThePr, ...inReviews]\n\n const checks = rollupState(view.statusCheckRollup, settings.ci.ignore)\n const newestHumanCommentAt = newest(byHumansOtherThan(comments, me))\n\n const key = prKey(found.repo, found.number)\n // State this version cannot read is state from another version of these\n // facts, and these facts are a cache of GitHub: reading them again costs a\n // sweep some calls, where failing here would cost the PR its row for good.\n const previous = Option.getOrUndefined(yield* Effect.orElseSucceed(store.get(key), () => Option.none<Facts>()))\n const reviewed = yield* reviewedAt(found.repo, found.number, view.headRefOid, settings)\n const quiet =\n previous !== undefined && isQuiet(pulseOf(previous), { head: view.headRefOid, checks, newestHumanCommentAt })\n ? previous\n : undefined\n\n const myLastCommitAt =\n quiet !== undefined\n ? quiet.myLastCommitAt\n : newest(\n (yield* prCommits(found.repo, found.number))\n .filter((commit) => commit.logins.includes(me))\n .map((commit) => commit.at)\n )\n\n // A red CI is classified once per state of the PR: while it sits where the\n // last sweep left it, the verdict it earned there still stands.\n const ciFlaky =\n checks !== \"red\"\n ? null\n : quiet !== undefined\n ? quiet.ciFlaky\n : yield* flakyReason(\n found.repo,\n found.number,\n view.statusCheckRollup,\n settings.ci.ignore,\n settings.ci.flaky_patterns\n )\n\n const rebaseConflictAt = yield* Effect.map(conflictFor(found.repo, found.number), (it) => it?.head ?? null)\n\n const facts: Facts = {\n repo: found.repo,\n number: found.number,\n title: view.title,\n url: view.url,\n draft: view.isDraft,\n head: view.headRefOid,\n mergeable: mergeabilityOf(view.mergeable),\n reviewDecision: reviewDecisionOf(view.reviewDecision),\n checks,\n ciFlaky,\n rebaseConflictAt,\n newestHumanCommentAt,\n myLastCommentAt: newest(writtenBy(comments, me)),\n myLastCommitAt,\n ...reviewed\n }\n\n yield* store.set(key, facts)\n return facts\n})\n\ntype Attempt<A> = { readonly got: ReadonlyArray<A>; readonly troubles: ReadonlyArray<Trouble> }\n\n/** A read that came back, or the trouble it came back with instead. */\nconst attempt = <A, E extends { readonly message: string }, R>(\n where: string,\n read: Effect.Effect<ReadonlyArray<A>, E, R>\n): Effect.Effect<Attempt<A>, never, R> =>\n read.pipe(\n Effect.map((got): Attempt<A> => ({ got, troubles: [] })),\n Effect.catch((error) => Effect.succeed<Attempt<A>>({ got: [], troubles: [{ where, detail: error.message }] }))\n )\n\nconst gather = <A>(attempts: ReadonlyArray<Attempt<A>>): Attempt<A> => ({\n got: attempts.flatMap((it) => it.got),\n troubles: attempts.flatMap((it) => it.troubles)\n})\n\n/** How many reads of GitHub are in flight at once. */\nconst concurrency = 4\n\n/**\n * One pass over every tracked PR, and nothing else: a sweep only ever reads.\n *\n * Every repository and every pull request is read on its own, so one of them\n * failing costs me its rows and leaves the rest of the table standing. What\n * failed comes back beside the facts rather than instead of them.\n */\nexport const sweep = Effect.gen(function* () {\n const file: ConfigFile = Option.getOrElse(yield* readConfig, (): ConfigFile => ({}))\n const repos = Object.keys(file.repos ?? {}).toSorted()\n if (repos.length === 0) {\n return { repos, facts: [], troubles: [] } satisfies Report\n }\n\n const store = yield* storeFor(\"prs\", FactsSchema)\n const me = yield* viewer\n\n const found = gather(yield* Effect.forEach(repos, (repo) => attempt(repo, searchPrs(repo)), { concurrency }))\n\n const swept = gather(\n yield* Effect.forEach(\n found.got,\n (pr: Found) =>\n attempt(\n `${pr.repo}#${pr.number}`,\n Effect.map(sweepPr(store, me, pr, settingsFor(file, pr.repo)), (facts) => [facts])\n ),\n { concurrency }\n )\n )\n\n return {\n repos,\n facts: swept.got,\n troubles: [...found.troubles, ...swept.troubles]\n } satisfies Report\n}).pipe(Effect.withSpan(\"sweep\"))\n\n/**\n * The failures a sweep can hit before it has a single row, which are the ones\n * worth a sentence: a machine or a file that needs fixing says what to fix\n * instead of printing a stack.\n */\nexport const userFacing = [\"ConfigMalformed\", \"GhUnavailable\", \"GhReadFailed\", \"GhUnreadable\"] as const\n\n/** Turns one of those into the sentence the CLI prints. */\nexport const asUserError = (cause: unknown): Effect.Effect<never, CliError.UserError> =>\n Effect.fail(new CliError.UserError({ cause }))\n\n/** What a sweep could not read, under a heading, so the table above it stands alone. */\nexport const printTroubles = Effect.fn(\"sweep.printTroubles\")(function* (troubles: ReadonlyArray<Trouble>) {\n if (troubles.length === 0) {\n return\n }\n yield* Console.log(\"\")\n yield* Console.log(\"Could not load\")\n for (const trouble of troubles) {\n yield* Console.log(` ${trouble.where} ${trouble.detail}`)\n }\n})\n\n/**\n * Refreshes what mission control knows about every tracked PR.\n *\n * `dw-mc status` does this too, so this command is for the pass on its own:\n * warming the state directory, or seeing what GitHub would not answer.\n */\nexport const sweepCommand = Command.make(\n \"sweep\",\n {},\n Effect.fn(\"sweep.command\")(\n function* () {\n const report = yield* sweep\n yield* Console.log(\n report.repos.length === 0\n ? \"No repositories registered. Run dw-mc init inside a repository to register it.\"\n : `Swept ${count(report.facts.length, \"pull request\")} across ${report.repos.length === 1 ? \"1 repository\" : `${report.repos.length} repositories`}`\n )\n yield* printTroubles(report.troubles)\n },\n Effect.catchTag(userFacing, asUserError)\n )\n).pipe(Command.withDescription(\"Refresh what mission control knows about every tracked pull request\"))\n","import type { Thread } from \"#adapters/conversation.ts\"\nimport type { Moment } from \"#domain/moment.ts\"\nimport { isAfter } from \"#domain/moment.ts\"\n\n/**\n * A pull request's conversation as it goes on screen: what people said, and\n * under a rule of its own what the bots did.\n *\n * They are kept apart rather than ordered together because they are read for\n * different reasons. A person's comment is a thing to answer; a bot's is a\n * thing to look at, and the bucket rules already ignore it.\n */\nexport interface Shown {\n readonly people: ReadonlyArray<Thread>\n readonly bots: ReadonlyArray<Thread>\n}\n\n/**\n * One thread's share of a strand, cut to what is worth reading.\n *\n * A review thread is answered as a whole, so a single comment newer than my\n * last activity brings the whole thread with it: the follow-up on its own is a\n * line answering something the screen does not show, which is what sends me to\n * the browser.\n *\n * The pull request's own comments are not a thread but a stream, and there is\n * no reply to lose the question of, so they are cut comment by comment.\n */\nconst only = (thread: Thread, keep: (bot: boolean) => boolean, since: Moment, all: boolean): ReadonlyArray<Thread> => {\n const strand = thread.comments.filter((it) => keep(it.bot))\n const comments = all\n ? strand\n : thread.path === null\n ? strand.filter((it) => isAfter(it.at, since))\n : strand.some((it) => isAfter(it.at, since))\n ? strand\n : []\n return comments.length === 0 ? [] : [{ ...thread, comments }]\n}\n\n/**\n * The threads worth putting on screen, given what I have already done.\n *\n * `since` is my last activity on the pull request - the later of my last\n * comment and my last commit - which is the same moment the bucket rule\n * measures a comment against. Showing exactly what is newer than it means the\n * command answers the question the bucket asked.\n *\n * A thread somebody resolved and one against code that is gone are left out:\n * neither is something to answer, and both are still there to read under\n * `--all`, which asks for the whole conversation and so measures nothing\n * against anything.\n */\nexport const shown = (threads: ReadonlyArray<Thread>, options: { readonly since: Moment; readonly all: boolean }) => {\n const kept = options.all ? threads : threads.filter((it) => !it.resolved && !it.outdated)\n return {\n people: kept.flatMap((it) => only(it, (bot) => !bot, options.since, options.all)),\n bots: kept.flatMap((it) => only(it, (bot) => bot, options.since, options.all))\n } satisfies Shown\n}\n","import { Console, DateTime, Effect, Option } from \"effect\"\nimport { Command, Flag } from \"effect/unstable/cli\"\n\nimport type { ConfigFile } from \"#adapters/config.ts\"\nimport { read as readConfig } from \"#adapters/config.ts\"\nimport type { Thread } from \"#adapters/conversation.ts\"\nimport { prConversation } from \"#adapters/conversation.ts\"\nimport type { Paint } from \"#adapters/paint.ts\"\nimport { Paint as PaintService } from \"#adapters/paint.ts\"\nimport { named, prArgument, swept } from \"#cli/pr.ts\"\nimport { heading } from \"#cli/row.ts\"\nimport { asUserError, userFacing } from \"#cli/sweep.ts\"\nimport type { Facts } from \"#domain/bucket.ts\"\nimport { place, unanswered } from \"#domain/bucket.ts\"\nimport type { Shown } from \"#domain/comments.ts\"\nimport { shown } from \"#domain/comments.ts\"\nimport { later } from \"#domain/moment.ts\"\n\nconst allFlag = Flag.Boolean(\"all\").pipe(\n Flag.withDefault(false),\n Flag.withDescription(\"Print the whole conversation, including what is resolved, outdated and already answered\")\n)\n\n/** Where a thread hangs: a line of the diff, or the pull request itself. */\nconst where = (thread: Thread): string =>\n thread.path === null ? \"Conversation\" : thread.line === null ? thread.path : `${thread.path}:${thread.line}`\n\n/**\n * What is true of a thread beyond where it hangs.\n *\n * It is only ever printed under `--all`, which is the only way a settled thread\n * reaches the screen at all, and it is there so that reading one is never\n * reading it as something still open.\n */\nconst settled = (thread: Thread): string =>\n [thread.resolved ? \"resolved\" : null, thread.outdated ? \"outdated\" : null].filter((it) => it !== null).join(\", \")\n\n/**\n * One thread as a block: where it hangs, then everybody who said something in\n * it, then what they said in full.\n *\n * In full because a review comment is usually a paragraph carrying a\n * suggestion, and a first line is what sends me to the browser this command\n * exists to replace. No diff hunk with it: the code is on this machine, under\n * the path the heading already prints.\n */\nconst block = (thread: Thread, paint: Paint): ReadonlyArray<string> => [\n `${paint.bold(where(thread))}${settled(thread) === \"\" ? \"\" : paint.dim(` (${settled(thread)})`)}`,\n ...thread.comments.flatMap((comment) => [\n ` ${paint.dim(`@${comment.login} ${DateTime.formatIso(comment.at)}`)}`,\n ...comment.body.split(\"\\n\").map((line) => ` ${line}`)\n ])\n]\n\nconst separated = (blocks: ReadonlyArray<ReadonlyArray<string>>): ReadonlyArray<string> =>\n blocks.flatMap((lines, index) => (index === 0 ? lines : [\"\", ...lines]))\n\n/**\n * The conversation on screen: people first, then a rule, then the bots.\n *\n * The rule is there so the two are never read as one list. A bot's comment is\n * observed and never answered, and the bucket rules ignore bots for exactly\n * this reason.\n *\n * A bot is cut at the same moment I am measured against, because the window is\n * what has happened since I last acted rather than what is owed an answer. A\n * verdict older than my last push is one I have already had the chance to read,\n * and `--all` is where it still is.\n */\nexport const lines = (view: Shown, paint: Paint): ReadonlyArray<string> => {\n const people = view.people.map((thread) => block(thread, paint))\n const bots = view.bots.map((thread) => block(thread, paint))\n return separated([...people, ...(bots.length === 0 ? [] : [[paint.dim(\"── bots ──\")], ...bots])])\n}\n\n/** What to say where there is nothing to print, which depends on why there is not. */\nconst nothing = (facts: Facts, all: boolean): ReadonlyArray<string> => {\n const pr = `${facts.repo}#${facts.number}`\n if (all) {\n return [`Nothing has been said on ${pr}.`]\n }\n const placement = place(facts)\n const rest = `dw-mc comments ${facts.number} --all prints the whole conversation.`\n return placement.bucket === \"needs-me\" && placement.reason === unanswered\n ? [\n `Nothing here is waiting on you: every thread is resolved, outdated, or older than your last comment ` +\n `or commit.`,\n `${pr} sits in ${heading[placement.bucket]} all the same, and a reply or a push is what settles it.`,\n rest\n ]\n : [`Nothing has been said on ${pr} since your last comment or commit.`, rest]\n}\n\n/**\n * The conversation on one tracked pull request, and nothing else.\n *\n * What it shows by default is what the bucket rule measures: the comments newer\n * than the later of my last comment and my last commit, which are the ones that\n * put the pull request in Needs me. Reading it answers the question the table\n * asked.\n *\n * The cutoff is read off the last sweep rather than worked out again here, so\n * the command shows exactly what `dw-mc status` counted rather than a second\n * opinion about it.\n *\n * It writes nothing, here or on GitHub: no reply, no resolve, no reaction\n * (ADR 0002). Reading is the whole command.\n */\nexport const comments = Command.make(\n \"comments\",\n { pr: prArgument, all: allFlag },\n Effect.fn(\"comments\")(\n function* ({ all, pr }) {\n const file: ConfigFile = Option.getOrElse(yield* readConfig, (): ConfigFile => ({}))\n const { number, repo } = yield* named(pr, Object.keys(file.repos ?? {}).toSorted())\n\n const facts = yield* swept(repo, number)\n const paint = yield* PaintService\n const view = shown(yield* prConversation(repo, number), {\n since: later(facts.myLastCommentAt, facts.myLastCommitAt),\n all\n })\n\n if (view.people.length === 0 && view.bots.length === 0) {\n yield* Effect.forEach(nothing(facts, all), (line) => Console.log(line))\n return\n }\n\n yield* Console.log(paint.bold(`${repo}#${number}`) + ` ${paint.dim(facts.title)}`)\n yield* Console.log(\"\")\n yield* Effect.forEach(lines(view, paint), (line) => Console.log(line))\n },\n Effect.catchTag([\"ConfigMalformed\", ...userFacing], asUserError)\n )\n).pipe(Command.withDescription(\"Print the conversation on one pull request, and what is waiting on me in it\"))\n","import { Console, Effect, Option, Schema } from \"effect\"\nimport { CliError, Command, Flag } from \"effect/unstable/cli\"\n\nimport type { ConfigFile, Severity } from \"#adapters/config.ts\"\nimport { read as readConfig, settingsFor } from \"#adapters/config.ts\"\nimport { named, prArgument } from \"#cli/pr.ts\"\nimport { asUserError } from \"#cli/sweep.ts\"\nimport { count, table } from \"#cli/table.ts\"\nimport type { Findings } from \"#domain/findings.ts\"\nimport { blocking, Findings as FindingsSchema } from \"#domain/findings.ts\"\nimport type { ReviewRun } from \"#domain/review.ts\"\nimport { lastRun, reportedBy, short } from \"#domain/review.ts\"\n\n/** The findings as the JSON the schema defines, rather than as this file spells it. */\nconst asJson = Schema.encodeEffect(Schema.fromJsonString(FindingsSchema))\n\nconst jsonFlag = Flag.Boolean(\"json\").pipe(\n Flag.withDefault(false),\n Flag.withDescription(\"Print the findings as the JSON a fix session is handed\")\n)\n\n/** What a run's findings come to in one line, against the bar that blocks. */\nexport const summary = (found: Findings, blocksOn: Severity): string => {\n if (found.findings.length === 0) {\n return \"clean, nothing to fix\"\n }\n const blocked = blocking(found.findings, blocksOn).length\n return `${count(found.findings.length, \"finding\")}, ${blocked} blocking`\n}\n\n/** Which run these findings are, and what they come to: the line above the list. */\nexport const header = (run: ReviewRun, found: Findings, blocksOn: Severity): string =>\n `${run.repo}#${run.number} ${short(run.head)} ${summary(found, blocksOn)}`\n\n/**\n * The findings one to a line, in the order the run reported them, ruled so the\n * three columns read apart.\n */\nexport const lines = (found: Findings): ReadonlyArray<string> =>\n table(\n found.findings.map((finding) => [`${finding.file}:${finding.line}`, finding.severity, finding.summary]),\n \" │ \"\n )\n\n/**\n * The review run whose findings are the current ones, or the sentence saying\n * there are none.\n *\n * The last run on the pull request is what \"current\" means here, and it is read\n * off the state directory rather than worked out from GitHub: this command is\n * one I run inside a fix session, where another round trip to GitHub buys\n * nothing the run it is about to fix does not already say.\n */\nexport const currentRun = Effect.fn(\"findings.currentRun\")(function* (repo: string, number: number) {\n const run = yield* lastRun(repo, number)\n return Option.isSome(run)\n ? run.value\n : yield* asUserError(`No review run on ${repo}#${number}. Run dw-mc review ${number} first.`)\n})\n\n/**\n * What the run reported, or the sentence saying it reported nothing at all.\n *\n * A run that failed is not a clean one: a pipe must never be handed \"no\n * findings\" when what happened is that nothing could be read.\n */\nexport const whatItFound = (run: ReviewRun): Effect.Effect<Findings, CliError.UserError> => {\n const found = reportedBy(run)\n return found === null\n ? Effect.fail(\n new CliError.UserError({\n cause:\n `The review run on ${short(run.head)} reported no findings: ` +\n `${run.outcome._tag === \"failed\" ? run.outcome.detail : \"\"}\\n` +\n `Run dw-mc review ${run.number} --force to run it again.`\n })\n )\n : Effect.succeed(found)\n}\n\n/**\n * What the current review run found, as a table or as the JSON it is kept in.\n *\n * `--json` is the whole point of the command: it prints the findings and\n * nothing else, so I can pipe them anywhere, and a fix session inside an open\n * agent reads exactly what the tool recorded rather than a retelling of it.\n *\n * A run that failed prints no findings and fails: a review run that could not\n * report has found nothing, which is not the same as having found nothing\n * wrong, and a pipe must never be handed the second when the first is true.\n */\nexport const findings = Command.make(\n \"findings\",\n { pr: prArgument, json: jsonFlag },\n Effect.fn(\"findings\")(\n function* ({ json, pr }) {\n const file: ConfigFile = Option.getOrElse(yield* readConfig, (): ConfigFile => ({}))\n const { number, repo } = yield* named(pr, Object.keys(file.repos ?? {}).toSorted())\n const settings = settingsFor(file, repo)\n\n const run = yield* currentRun(repo, number)\n const found = yield* whatItFound(run)\n if (json) {\n yield* Console.log(yield* asJson(found))\n return\n }\n\n yield* Console.log(header(run, found, settings.stamp.blocks_on))\n for (const line of lines(found)) {\n yield* Console.log(` ${line}`)\n }\n },\n Effect.catchTag([\"ConfigMalformed\"], asUserError)\n )\n).pipe(Command.withDescription(\"Print what the current review run found on one pull request\"))\n","/**\n * How one turn of Claude Code is spawned and given up on, and what a turn that\n * answers against a schema comes back with.\n *\n * Every turn is reached this way, so the spawn, the patience and the one failure\n * they can end in live here rather than once per turn.\n */\nimport { Duration, Effect, Schema, Stream } from \"effect\"\nimport { ChildProcess, ChildProcessSpawner } from \"effect/unstable/process\"\n\n/** A review run that would not start, would not finish, or finished badly. */\nexport class AgentFailed extends Schema.TaggedError<AgentFailed>()(\"AgentFailed\", {\n /** The program that was spawned, which is what a search for it has to name. */\n program: Schema.String,\n detail: Schema.String\n}) {\n override get message(): string {\n return `The ${this.program} review run failed: ${this.detail}`\n }\n}\n\n/** What a review turn held to a schema came back with. */\nexport interface Reported {\n /** What the run validated against the schema, handed on unread. */\n readonly findings: unknown\n /** The session the run happened in, which is what a run is recorded against. */\n readonly sessionId: string\n /** What the run said in prose beside its findings, or null where it said none. */\n readonly prose: string | null\n}\n\n/**\n * Failures in the name of the program that was spawned.\n *\n * Where the launcher starts `claude` through another program, it is that\n * program that would not start or exited badly, and saying `claude` sends the\n * search to the wrong process.\n */\nexport const failedBy = (program: string) => (detail: string) => new AgentFailed({ program, detail })\n\n/**\n * How long each turn gets before it is given up on.\n *\n * The review is the turn that thinks, and a high-effort one that fans out to\n * subagents takes real minutes, so its limit is there to catch a run that has\n * stopped rather than one that is slow. The second turn reads no code and\n * decides nothing - the review it reports on is already in the session it\n * resumes - and every run of it by hand came back in seconds.\n *\n * Either way, a command that hangs forever is worse than one that says it\n * failed: a review I walked away from is one I need to be able to come back to.\n */\nexport const patience = {\n reviewing: Duration.minutes(45),\n reporting: Duration.minutes(5)\n}\n\n/**\n * One turn of the launcher in `directory`, with `read` over its standard output.\n *\n * The launcher's own arguments go in front of the turn's, because they are what\n * gets `claude` started at all. The two output streams are drained together,\n * because draining one to the end first can block a run that is still writing to\n * the other. Every way a turn can fail to finish comes back from here as a\n * `AgentFailed`, so a caller is left with the turn's own answer and nothing else\n * to translate - a turn that never comes back included.\n */\nexport const turn = Effect.fnUntraced(function* <A, E extends { readonly message: string }, R>(options: {\n /** The program and the prefix that starts Claude Code. */\n readonly command: readonly [string, ...Array<string>]\n readonly directory: string\n readonly args: ReadonlyArray<string>\n /** What this turn is called when it is late, and how long it has. */\n readonly patience: { readonly turn: string; readonly duration: Duration.Duration }\n readonly read: (stdout: ChildProcessSpawner.ChildProcessHandle[\"stdout\"]) => Effect.Effect<A, E, R>\n}) {\n const [program, ...prefix] = options.command\n const failed = failedBy(program)\n const spawner = yield* ChildProcessSpawner.ChildProcessSpawner\n\n const running = Effect.gen(function* () {\n const handle = yield* Effect.mapError(\n spawner.spawn(\n ChildProcess.make(program, [...prefix, ...options.args], { cwd: options.directory, stdin: \"pipe\" })\n ),\n (error) => failed(error.message)\n )\n\n const [got, stderr] = yield* Effect.mapError(\n Effect.all([options.read(handle.stdout), Stream.mkString(Stream.decodeText(handle.stderr))], { concurrency: 2 }),\n (error) => failed(error.message)\n )\n\n const exitCode = yield* Effect.mapError(handle.exitCode, (error) => failed(error.message))\n if (exitCode !== 0) {\n return yield* failed(stderr.trim() === \"\" ? `${program} exited ${exitCode}` : stderr.trim())\n }\n return got\n })\n\n return yield* Effect.timeoutOrElse(running, {\n duration: options.patience.duration,\n orElse: () =>\n failed(`${options.patience.turn} did not come back within ${Duration.format(options.patience.duration)}`)\n })\n})\n","/**\n * Claude Code: a review on a slash command, a review on the tool's own prompt,\n * and the sessions I steer.\n */\nimport { Effect, Option, PlatformError, Result, Schema, Stream } from \"effect\"\nimport { ChildProcess, ChildProcessSpawner } from \"effect/unstable/process\"\n\nimport type { Reported, AgentFailed } from \"#adapters/agent.ts\"\nimport { failedBy, patience, turn } from \"#adapters/agent.ts\"\nimport type { Launcher } from \"#adapters/config.ts\"\n\n/**\n * What one review run opens on: a slash command, or the tool's own prompt.\n *\n * Which of the two it is decides how many turns the run takes, and that is a\n * fact about Claude Code rather than about reviewing, so the shape is declared\n * here and filled in by the domain (ADR 0006).\n */\nexport type ReviewTurn =\n | {\n readonly _tag: \"command\"\n /** The slash command and whatever follows it, as one line. */\n readonly line: string\n /** What else the run is told to look at, on the system prompt beside the command. */\n readonly instructions: string | null\n }\n | { readonly _tag: \"prompt\"; readonly text: string }\n\n/** What one turn came back with. */\nexport interface Turn {\n /** What the run said, as the prose it says it in. */\n readonly report: string\n /** The session the turn ran in, which the follow-up turn resumes. */\n readonly sessionId: string\n}\n\n/** What a whole review run came to, however many turns it took. */\nexport interface Reviewed {\n readonly sessionId: string\n /** What the run said in prose, or null where a schema left it none to say. */\n readonly prose: string | null\n /** What it reported, or the failure the reporting was. */\n readonly findings: Result.Result<unknown, AgentFailed>\n}\n\n/**\n * The two events of a stream-json run this reads, as the runner really writes\n * them. Every other field of both, and every other event, is ignored: a\n * transcript carries hooks, rate limits, thinking and tool results, and a\n * version that adds another must not stop a run from being read.\n */\nconst Working = Schema.Struct({\n type: Schema.Literal(\"assistant\"),\n message: Schema.Struct({\n content: Schema.Array(\n Schema.Struct({\n type: Schema.String,\n name: Schema.optionalKey(Schema.String),\n text: Schema.optionalKey(Schema.String)\n })\n )\n })\n})\n\nconst Ended = Schema.Struct({\n type: Schema.Literal(\"result\"),\n subtype: Schema.String,\n is_error: Schema.Boolean,\n session_id: Schema.String,\n result: Schema.optionalKey(Schema.String),\n /** What a turn given a JSON schema validated, which this hands on unread. */\n structured_output: Schema.optionalKey(Schema.Unknown)\n})\n\nconst asWorking = Schema.decodeUnknownOption(Schema.fromJsonString(Working))\nconst asResult = Schema.decodeUnknownOption(Schema.fromJsonString(Ended))\n\n/** What one event says the runner reached for, and what it said out loud. */\ninterface Heard {\n readonly tools: ReadonlyArray<string>\n readonly said: ReadonlyArray<string>\n}\n\nconst heardIn = (line: string): Heard => {\n const blocks = Option.match(asWorking(line), { onNone: () => [], onSome: (event) => event.message.content })\n return {\n tools: blocks.flatMap((block) => (block.type === \"tool_use\" && block.name !== undefined ? [block.name] : [])),\n said: blocks.flatMap((block) => (block.type === \"text\" && block.text !== undefined ? [block.text] : []))\n }\n}\n\n/** What the run comes to while it is still going. */\ninterface SoFar {\n readonly said: ReadonlyArray<string>\n readonly result: Option.Option<typeof Ended.Type>\n}\n\n/**\n * The result a turn ended on, or the failure it really was.\n *\n * A turn that said nothing this can read and a turn Claude Code itself calls an\n * error are both failures: `subtype` is where a run that hit its turn limit or\n * lost its connection says so, and its `result` is the only word on why.\n */\nconst ended = (program: string, result: Option.Option<typeof Ended.Type>) => {\n const failed = failedBy(program)\n if (Option.isNone(result)) {\n return Effect.fail(failed(\"the turn came back with no result\"))\n }\n const { is_error, result: lastWord, subtype } = result.value\n return is_error || subtype !== \"success\"\n ? Effect.fail(failed(`${subtype}: ${lastWord ?? \"nothing else was said\"}`))\n : Effect.succeed(result.value)\n}\n\n/**\n * A Claude Code `stream-json` turn, read as it arrives: what it reached for goes\n * to `onTool` while the run is still going, and what it said and how it ended\n * are what comes back.\n *\n * Both shapes of review read a turn the same way, so the fold is here rather\n * than once per shape.\n */\nconst transcript =\n (onTool: (tool: string) => Effect.Effect<void>) =>\n (stdout: ChildProcessSpawner.ChildProcessHandle[\"stdout\"]): Effect.Effect<SoFar, PlatformError.PlatformError> =>\n stdout.pipe(\n Stream.decodeText(),\n Stream.splitLines,\n Stream.mapEffect((line) => {\n const heard = heardIn(line)\n return Effect.as(Effect.forEach(heard.tools, onTool, { discard: true }), { line, heard })\n }),\n Stream.runFold(\n (): SoFar => ({ said: [], result: Option.none() }),\n (soFar, { heard, line }): SoFar => ({\n said: [...soFar.said, ...heard.said],\n result: Option.orElse(asResult(line), () => soFar.result)\n })\n )\n )\n\n/**\n * One review run on a slash command, headless, in `directory`.\n *\n * The run is in the foreground and says what it is doing as it does it, which\n * is what `onTool` is for: a review takes minutes, and a terminal that prints\n * nothing for minutes is one I stop trusting.\n *\n * `--json-schema` is never passed here: verified by running it, the flag beside\n * `/code-review` breaks the run, which is why a slash command costs a second\n * turn that resumes the session and asks for the findings. My own instructions\n * ride on `--append-system-prompt` rather than on the command's own line,\n * because what a slash command does with its arguments is its business and not\n * this tool's.\n *\n * `--comment` is the flag that makes the built-in review post on the pull\n * request, and it is never passed either (ADR 0002). The report is everything\n * the run said on its own turns rather than the `result` alone: verified by\n * running it, a repository whose review command fans out to subagents can end on\n * a remark about them, and the report is the turn before that.\n */\nexport const commandReview = Effect.fn(\"claude.commandReview\")(function* (options: {\n readonly launcher: Launcher\n readonly directory: string\n readonly line: string\n readonly instructions: string | null\n readonly model: string | null\n readonly onTool: (tool: string) => Effect.Effect<void>\n}) {\n const [program] = options.launcher.command\n const run = yield* turn({\n command: options.launcher.command,\n directory: options.directory,\n args: [\n \"-p\",\n options.line,\n \"--output-format\",\n \"stream-json\",\n \"--verbose\",\n ...(options.instructions === null ? [] : [\"--append-system-prompt\", options.instructions]),\n ...(options.model === null ? [] : [\"--model\", options.model])\n ],\n patience: { turn: \"the review\", duration: patience.reviewing },\n read: transcript(options.onTool)\n })\n\n const { result: lastWord, session_id } = yield* ended(program, run.result)\n\n // The result is the run's last word, which is its whole answer on a run that\n // said nothing before it.\n const report = (run.said.length === 0 ? (lastWord ?? \"\") : run.said.join(\"\\n\\n\")).trim()\n if (report === \"\") {\n return yield* failedBy(program)(\"the run came back with an empty report\")\n }\n return { report, sessionId: session_id } satisfies Turn\n}, Effect.scoped)\n\n/**\n * What the second turn asks for.\n *\n * It asks for a report of what was already said rather than for another look:\n * the prose is the review, and this turn is only what makes it machine\n * readable. The shape it must answer in arrives as a JSON schema beside it, so\n * the prompt does not describe the schema twice.\n */\nconst reportFindings = [\n \"Report the findings of the review you just gave as structured output.\",\n \"Every finding carries the file it is in as a repository path, the line it is at,\",\n \"its severity and a one-sentence summary.\",\n \"The verdict is clean when there is nothing to report and findings otherwise.\",\n \"Report nothing you did not already say.\"\n].join(\" \")\n\n/**\n * The second turn of a review run: the prose the first one wrote, back as\n * findings that validate.\n *\n * It resumes the first turn's session rather than reading the diff again, which\n * is what makes it cheap and what makes it accurate - verified by running it,\n * the line numbers it reports beat the ones the prose gives. The output is\n * handed on as it arrived: what the findings must look like belongs to the\n * domain, and the schema the run is held to comes in from there too.\n *\n * Every way this can end badly ends as an `AgentFailed`, because a review run\n * that could not report is a failure and never a clean verdict.\n */\nexport const findingsTurn = Effect.fn(\"claude.findingsTurn\")(function* (options: {\n readonly launcher: Launcher\n readonly directory: string\n readonly sessionId: string\n readonly jsonSchema: string\n}) {\n const [program] = options.launcher.command\n const printed = yield* turn({\n command: options.launcher.command,\n directory: options.directory,\n patience: { turn: \"the findings turn\", duration: patience.reporting },\n args: [\n \"-p\",\n \"--resume\",\n options.sessionId,\n reportFindings,\n \"--output-format\",\n \"json\",\n \"--json-schema\",\n options.jsonSchema\n ],\n read: (stdout) => Stream.mkString(Stream.decodeText(stdout))\n })\n\n const { structured_output } = yield* ended(program, asResult(printed.trim()))\n if (structured_output === undefined) {\n return yield* failedBy(program)(\"the findings turn came back with no structured output\")\n }\n return structured_output\n}, Effect.scoped)\n\n/**\n * One review run of the tool's own review prompt, in `directory`.\n *\n * It is one turn rather than two: verified by running it, `--json-schema` beside\n * an ordinary prompt gives both the prose the run wrote and the\n * `structured_output` it validated, where the same flag on a slash command\n * breaks the run. The schema arrives as inline JSON and never as a path - a path\n * is where Claude Code reports `--json-schema is not valid JSON`.\n */\nexport const promptReview = Effect.fn(\"claude.promptReview\")(function* (options: {\n readonly launcher: Launcher\n readonly directory: string\n readonly prompt: string\n /** The model to run the prompt on, or null for whatever the CLI would pick. */\n readonly model: string | null\n readonly jsonSchema: string\n readonly onTool: (tool: string) => Effect.Effect<void>\n}) {\n const [program] = options.launcher.command\n const run = yield* turn({\n command: options.launcher.command,\n directory: options.directory,\n patience: { turn: \"the review\", duration: patience.reviewing },\n args: [\n \"-p\",\n options.prompt,\n \"--output-format\",\n \"stream-json\",\n \"--verbose\",\n \"--json-schema\",\n options.jsonSchema,\n ...(options.model === null ? [] : [\"--model\", options.model])\n ],\n read: transcript(options.onTool)\n })\n\n const { session_id, structured_output } = yield* ended(program, run.result)\n if (structured_output === undefined) {\n return yield* failedBy(program)(\"the review came back with no structured output\")\n }\n\n const prose = run.said.join(\"\\n\\n\").trim()\n return { findings: structured_output, sessionId: session_id, prose: prose === \"\" ? null : prose } satisfies Reported\n}, Effect.scoped)\n\n/**\n * One review run, in whichever shape it was configured in.\n *\n * A slash command takes two turns and the tool's own prompt takes one, which is\n * Claude Code's doing and nobody else's: a caller hands over the turn and gets\n * the same answer back either way.\n *\n * The second turn's failure is kept beside the first turn's prose rather than\n * replacing it. A review that ran and could not report is still worth reading,\n * and it is recorded as the failure it is.\n */\nexport const reviewTurns = Effect.fn(\"claude.reviewTurns\")(function* (options: {\n readonly launcher: Launcher\n readonly directory: string\n readonly turn: ReviewTurn\n readonly model: string | null\n readonly jsonSchema: string\n readonly onTool: (tool: string) => Effect.Effect<void>\n}) {\n const { directory, jsonSchema, launcher, model, onTool } = options\n if (options.turn._tag === \"prompt\") {\n const run = yield* promptReview({ launcher, directory, prompt: options.turn.text, model, jsonSchema, onTool })\n return { sessionId: run.sessionId, prose: run.prose, findings: Result.succeed(run.findings) } satisfies Reviewed\n }\n\n const run = yield* commandReview({\n launcher,\n directory,\n line: options.turn.line,\n instructions: options.turn.instructions,\n model,\n onTool\n })\n const findings = yield* Effect.result(findingsTurn({ launcher, directory, sessionId: run.sessionId, jsonSchema }))\n return { sessionId: run.sessionId, prose: run.report, findings } satisfies Reviewed\n})\n\n/**\n * An interactive `claude` in `directory`, opened on `prompt`, with my terminal\n * handed straight to it.\n *\n * The launcher's `fix_args` go here and nowhere else: they are the flags of\n * every session I steer - the one on findings and the one on a conflict - which\n * no headless review turn wants. They sit in front of the\n * prompt, because `claude` takes its flags before its positional argument.\n *\n * This is the one place a run is not read: the three streams are inherited,\n * so what is on the screen is the session itself and not a transcript of it,\n * and what I type reaches it. The child is not detached for the same reason -\n * a detached child sits outside the terminal's foreground process group, where\n * neither my keystrokes nor Ctrl-C would reach it.\n *\n * There is no patience here either. A session I steer lasts as long as I am in\n * it, and a timeout would be the tool closing a session I was still working in.\n *\n * What comes back is the code the session ended on. A session I left with\n * Ctrl-C ended badly for `claude` and not for me, so this reports it rather\n * than failing on it; only a `claude` that would not start at all is a failure.\n */\nexport const steeredSession = Effect.fn(\"claude.steeredSession\")(function* (options: {\n readonly launcher: Launcher\n readonly directory: string\n readonly prompt: string\n}) {\n const [program, ...prefix] = options.launcher.command\n const failed = failedBy(program)\n const spawner = yield* ChildProcessSpawner.ChildProcessSpawner\n\n const handle = yield* Effect.mapError(\n spawner.spawn(\n ChildProcess.make(program, [...prefix, ...options.launcher.fix_args, options.prompt], {\n cwd: options.directory,\n stdin: \"inherit\",\n stdout: \"inherit\",\n stderr: \"inherit\",\n detached: false\n })\n ),\n (error) => failed(error.message)\n )\n\n return yield* Effect.mapError(handle.exitCode, (error) => failed(error.message))\n}, Effect.scoped)\n","import { Effect, Schema } from \"effect\"\n\nimport { Finding } from \"#domain/findings.ts\"\nimport { short } from \"#domain/review.ts\"\n\n/** One finding I chose to act on, carrying what I think about it. */\nexport const Chosen = Schema.Struct({ ...Finding.fields, note: Schema.optionalKey(Schema.String) })\nexport type Chosen = typeof Chosen.Type\n\n/**\n * What a fix session is handed: the findings I picked, and the review run they\n * came from.\n *\n * The head is in it because a fix session opens on the commit that was\n * reviewed, and a finding's line means nothing away from it.\n */\nexport const Selection = Schema.Struct({\n repo: Schema.String,\n number: Schema.Int,\n head: Schema.String,\n findings: Schema.Array(Chosen)\n})\nexport type Selection = typeof Selection.Type\n\n/** The selection as the JSON the schema defines, rather than as this file spells it. */\nconst asJson = Schema.encodeEffect(Schema.fromJsonString(Selection))\n\n/**\n * The prompt a fix session opens on: what these findings are, and the findings\n * themselves as JSON.\n *\n * The findings go in verbatim rather than described, because a re-description\n * is where a file, a line or my own note quietly changes. A note outranks the\n * finding it is on: the finding is what the review thought, the note is what I\n * think, and I am the one who picked it.\n *\n * Pushing is mine either way, and `commits` says whether committing is too.\n * The tool itself never commits and never pushes; what the session may do\n * inside the worktree is my call, made once in `fix.commits` or for one session\n * with the flag.\n */\nexport const promptFor = (selection: Selection, commits: boolean): Effect.Effect<string, Schema.SchemaError> =>\n Effect.map(asJson(selection), (json) =>\n [\n `These are the findings I picked from a dw-mc review run on ${selection.repo}#${selection.number}, ` +\n `at ${short(selection.head)}, the commit their lines are counted from.`,\n `Work through them one at a time. Where a finding carries a note, the note is mine and outranks the ` +\n `finding's own summary; where it carries none, the summary is the whole brief.`,\n commits\n ? `Commit what you change, one logical change to a commit. Do not push: I read the commits and push them myself.`\n : `Do not commit and do not push: I do both myself when I have read what you changed.`,\n json\n ].join(\"\\n\\n\")\n )\n\n/**\n * Why these findings cannot be fixed where the pull request now is, or nothing\n * where they can.\n *\n * A pull request that moved since its last review run has findings at lines\n * that may no longer be there, and a worktree cut at the new head would carry\n * them into code they were never about. Reviewing again is cheap next to fixing\n * the wrong thing.\n */\nexport const staleAt = (number: number, run: string, now: string): string | null =>\n run === now\n ? null\n : `The findings are from ${short(run)} and the pull request is now at ${short(now)}. ` +\n `Run dw-mc review ${number} again to review the head you would be fixing.`\n","import { Console, Effect, Option } from \"effect\"\nimport { CliError, Command, Flag } from \"effect/unstable/cli\"\n\nimport { steeredSession } from \"#adapters/claude.ts\"\nimport type { ConfigFile } from \"#adapters/config.ts\"\nimport { launcherOf, read as readConfig, settingsFor } from \"#adapters/config.ts\"\nimport { prView } from \"#adapters/gh.ts\"\nimport { standingWorktree } from \"#adapters/git.ts\"\nimport { choose, note, width } from \"#adapters/picker.ts\"\nimport { currentRun, header, lines, whatItFound } from \"#cli/findings.ts\"\nimport { named, prArgument } from \"#cli/pr.ts\"\nimport { asUserError, userFacing } from \"#cli/sweep.ts\"\nimport { truncate } from \"#cli/table.ts\"\nimport type { Finding, Findings } from \"#domain/findings.ts\"\nimport type { Chosen } from \"#domain/fix.ts\"\nimport { promptFor, staleAt } from \"#domain/fix.ts\"\n\nconst printFlag = Flag.Boolean(\"print\").pipe(\n Flag.withDefault(false),\n Flag.withDescription(\"Print the prompt a session would open on, and open none\")\n)\n\nconst commitFlag = Flag.Boolean(\"commit\").pipe(\n Flag.withDescription(\"Let this session commit what it changes, over what the repository configured\"),\n Flag.optional\n)\n\n/**\n * The findings to pick from, each on the line `dw-mc findings` gives it.\n *\n * The rows come from there rather than being built again here, so the list I\n * pick from and the list I read are the same list. A row that does not fit the\n * screen is cut: a prompt draws its own frame around the row, and a row that\n * wraps takes the whole list's alignment with it.\n */\nconst choicesOf = (found: Findings, screen: number) => {\n const rows = lines(found)\n const room = screen === 0 ? Number.POSITIVE_INFINITY : screen - 6\n return found.findings.map((finding, index) => ({\n title: truncate(rows[index] ?? finding.summary, room),\n value: finding\n }))\n}\n\n/**\n * Each picked finding with whatever I have to say about it.\n *\n * The note is asked for one finding at a time, in the order I see them, and\n * having nothing to say is the ordinary answer rather than a step I have to get\n * past.\n */\nconst noted = Effect.fn(\"fix.noted\")(function* (picked: ReadonlyArray<Finding>) {\n const chosen: Array<Chosen> = []\n for (const finding of picked) {\n const said = yield* note(`Note on ${finding.file}:${finding.line}, or nothing`)\n chosen.push(Option.match(said, { onNone: () => finding, onSome: (text) => ({ ...finding, note: text }) }))\n }\n return chosen\n})\n\n/** The domain's word on a head that has moved, as the command's own failure. */\nconst fixable = (number: number, run: string, now: string) => {\n const stale = staleAt(number, run, now)\n return stale === null ? Effect.void : Effect.fail(new CliError.UserError({ cause: stale }))\n}\n\n/**\n * A fix session: the findings I picked, in an agent session I steer.\n *\n * The tool fixes nothing. It picks the findings apart with me, cuts a worktree\n * on a branch of its own that tracks the pull request's, and hands the session\n * what I chose as JSON; then it is out of the way. I steer and I push. Nothing\n * here writes to GitHub, and the tool itself commits nothing: whether the\n * session may commit inside the worktree is `fix.commits`, or `--commit` for\n * one session.\n *\n * The worktree is left standing when the session ends, because the work in it\n * is mine and an unpushed commit lives nowhere else. Re-reviewing the result is\n * a new review run against the new head, never a continuation of the run that\n * produced these findings, so what was reviewed at which commit stays honest.\n */\nexport const fix = Command.make(\n \"fix\",\n { pr: prArgument, commit: commitFlag, print: printFlag },\n Effect.fn(\"fix\")(\n function* ({ commit, pr, print }) {\n const file: ConfigFile = Option.getOrElse(yield* readConfig, (): ConfigFile => ({}))\n const { number, repo } = yield* named(pr, Object.keys(file.repos ?? {}).toSorted())\n const settings = settingsFor(file, repo)\n\n const run = yield* currentRun(repo, number)\n const found = yield* whatItFound(run)\n yield* Console.log(header(run, found, settings.stamp.blocks_on))\n if (found.findings.length === 0) {\n return\n }\n\n const view = yield* prView(repo, number)\n yield* fixable(number, run.head, view.headRefOid)\n\n const picked = yield* choose(\"Which findings does the session carry?\", choicesOf(found, yield* width))\n const chosen = yield* Effect.catchTag(noted(Option.getOrElse(picked, () => [])), \"QuitError\", () =>\n Effect.succeed<ReadonlyArray<Chosen>>([])\n )\n if (chosen.length === 0) {\n yield* Console.log(\"Nothing picked, so no session was opened.\")\n return\n }\n\n const commits = Option.getOrElse(commit, () => settings.fix.commits)\n // The prompt on its own, for the session I already have open. Nothing is\n // cut and nothing is spawned: the session this is pasted into is one I am\n // steering already, in whatever checkout I am steering it from.\n if (print) {\n yield* Console.log(yield* promptFor({ repo, number, head: run.head, findings: chosen }, commits))\n return\n }\n\n const worktree = yield* standingWorktree(repo, number, view.headRefName, \"fix\")\n yield* Console.log(\n ` ${chosen.length} of ${found.findings.length} findings, ${commits ? \"committing\" : \"not committing\"}`\n )\n yield* Console.log(` ${worktree.directory}, pushing to ${view.headRefName}`)\n\n const ended = yield* steeredSession({\n launcher: launcherOf(file),\n directory: worktree.directory,\n prompt: yield* promptFor({ repo, number, head: worktree.head, findings: chosen }, commits)\n })\n\n yield* Console.log(ended === 0 ? \"The session is over.\" : `The session ended with ${ended}.`)\n yield* Console.log(\n `${commits ? \"Nothing was pushed\" : \"Nothing was committed or pushed\"} for you; ` +\n `the worktree stands at ${worktree.directory}.`\n )\n yield* Console.log(`Once you have pushed, dw-mc review ${number} reviews the new head as a new run.`)\n },\n Effect.catchTag([...userFacing, \"GitFailed\", \"WorktreeHeld\", \"AgentFailed\"], asUserError)\n )\n).pipe(Command.withDescription(\"Pick findings from the current review run and open a fix session on them\"))\n","import { Console, Effect, Option } from \"effect\"\nimport { Command, Flag } from \"effect/unstable/cli\"\n\nimport type { ConfigFile, Effort, SettingsPatch } from \"#adapters/config.ts\"\nimport { builtIn, ConfigStore, encode, merge, read, withDefaults, withRepo, write } from \"#adapters/config.ts\"\nimport { currentRepo, requireAuth } from \"#adapters/gh.ts\"\nimport { stateDirectory } from \"#adapters/store.ts\"\nimport { asUserError } from \"#cli/sweep.ts\"\n\nconst effortFlag = Flag.Literals(\"effort\", [\"low\", \"medium\", \"high\", \"xhigh\", \"max\"]).pipe(\n Flag.withDescription(\"How much a review run spends on this repository\"),\n Flag.optional\n)\n\nconst baseFlag = Flag.String(\"base\").pipe(\n Flag.withDescription(\"The branch this repository's pull requests target, over the default one\"),\n Flag.optional\n)\n\n/** The settings the flags asked for, and only those. */\nconst asked = (base: Option.Option<string>, effort: Option.Option<Effort>): SettingsPatch => ({\n ...(Option.isSome(base) ? { base: base.value } : {}),\n ...(Option.isSome(effort) ? { review: { effort: effort.value } } : {})\n})\n\n/** What a review will open on, as the setup prints it back. */\nconst opening = (defaults: SettingsPatch): string => {\n const review = { ...builtIn.review, ...defaults.review }\n return review.command === null\n ? \"my own prompt\"\n : [review.command, review.effort].filter((part) => part !== null).join(\" \")\n}\n\nconst row = (label: string, value: string): string => `${label.padEnd(12)}${value}`\n\n/**\n * Both the machine setup and the repository registration: there is deliberately\n * no separate `setup` command.\n *\n * The first run on a machine checks `gh` and spells the defaults out in the\n * configuration file. Run inside a repository, it also registers that\n * `owner/repo`, taking the name from `gh` so I never type it. Run again, it\n * changes what the flags name, keeps every other setting the file already had,\n * and leaves the file untouched where nothing was decided differently.\n *\n * It asks nothing. Reviews run on Claude Code, and what a run opens on is\n * `review.command` and `review.prompt` - a line and a paragraph that belong in\n * the file rather than in a terminal prompt.\n *\n * `--effort` and `--base` are about one repository, so they land on the\n * repository this ran in, or in the defaults when it ran outside one.\n */\nexport const init = Command.make(\n \"init\",\n { effort: effortFlag, base: baseFlag },\n Effect.fn(\"init\")(\n function* ({ base, effort }) {\n yield* requireAuth\n\n const config = yield* ConfigStore\n const before = yield* read\n const file: ConfigFile = Option.getOrElse(before, (): ConfigFile => ({}))\n\n // A defaults block is what says this machine has been set up. On a first\n // run the built-in defaults go under whatever the file already said, so\n // spelling them out cannot overwrite a setting I chose by hand.\n const firstRun = file.defaults === undefined\n const defaults = firstRun ? merge(builtIn, file.defaults ?? {}) : (file.defaults ?? {})\n\n const state = yield* stateDirectory\n const repo = yield* currentRepo.pipe(\n Effect.asSome,\n Effect.catchTag(\"NoRepository\", () => Effect.succeedNone)\n )\n\n const overrides = asked(base, effort)\n const written = Option.isSome(repo)\n ? withRepo(withDefaults(file, defaults), repo.value, overrides)\n : withDefaults(file, merge(defaults, overrides))\n\n if (encode(written) !== encode(file) || Option.isNone(before)) {\n yield* write(written)\n }\n\n yield* Console.log(row(\"review\", opening(written.defaults ?? {})))\n yield* Console.log(row(\"config\", config.path))\n yield* Console.log(row(\"state\", state))\n yield* Console.log(\n Option.isNone(repo)\n ? row(\"repository\", \"none here - run dw-mc init inside a repository to register it\")\n : row(\n \"repository\",\n `${repo.value} (${file.repos?.[repo.value] === undefined ? \"registered\" : \"already registered\"})`\n )\n )\n },\n // The failures worth a sentence become one, so a machine or a file that\n // needs fixing says what to fix instead of printing a stack.\n Effect.catchTag([\"ConfigMalformed\", \"GhUnauthenticated\", \"GhUnavailable\", \"GhUnreadable\"], asUserError)\n )\n).pipe(Command.withDescription(\"Set this machine up and register the repository I am in\"))\n","import { Effect, Option, Schema } from \"effect\"\n\nimport { prKey, storeFor } from \"#adapters/store.ts\"\nimport type { Facts } from \"#domain/bucket.ts\"\n\n/** My local mark that a tracked PR has passed my bar, and what it rests on. */\nexport interface Stamp {\n readonly stamped: boolean\n /** What the mark says, or the first thing that withholds it. */\n readonly reason: string\n}\n\n/**\n * A stamp I took off a pull request by hand, and the head I took it off at.\n *\n * The head is the whole record: a withdrawal is my overruling the computation\n * on code I have read, so it lasts exactly as long as that code is what the\n * pull request is.\n */\nexport const Withdrawal = Schema.Struct({ head: Schema.String })\nexport type Withdrawal = typeof Withdrawal.Type\n\n/**\n * The facts a stamp rests on, which are fewer than a sweep writes down.\n *\n * It is spelled out because the stamp is asked for in two places that know\n * different amounts: `dw-mc status` has the whole of a swept `Facts`, and\n * `dw-mc merge` has what it just read off GitHub and out of the state\n * directory. Both compute the same mark from the same five facts.\n */\nexport type Stampable = Pick<Facts, \"head\" | \"reviewRunHead\" | \"blockingFindings\" | \"checks\" | \"mergeable\">\n\n/** The stamp a pull request has not earned, and the first reason it has not. */\nconst withheld = (reason: string): Stamp => ({ stamped: false, reason })\n\n/** What CI has to say before the stamp will rest on it, which is green and nothing else. */\nexport const whyNotGreen: Record<Facts[\"checks\"], string | null> = {\n green: null,\n red: \"CI is red\",\n pending: \"CI is still running\",\n none: \"no CI ran on this head\"\n}\n\n/** What GitHub has to say about merging, which is that it would. */\nexport const whyNotMergeable: Record<Facts[\"mergeable\"], string | null> = {\n mergeable: null,\n conflicting: \"merge conflict\",\n unknown: \"GitHub has not said whether it merges\"\n}\n\n/**\n * The stamp of one tracked PR: whether it has passed my bar, and why.\n *\n * The mark is computed rather than clicked, so it means the same thing every\n * time: a review run on this head that found nothing blocking, CI green as the\n * repository's `ci.ignore` defines green, and a pull request GitHub would\n * merge. A red CI the flaky classifier excused is still not green here: an\n * excuse is a reason not to fix a check, not a reason to land code behind one,\n * and this mark is what clears `dw-mc merge` (ADR 0008).\n *\n * Nothing about this rests on a previous stamp, which is what makes a head\n * change clear it: facts are about one head, and a run is recorded against one.\n *\n * A withdrawal comes first, because it is the one thing here I decided rather\n * than computed.\n */\nexport const stampFor = (facts: Stampable, withdrawnAt: string | null): Stamp => {\n if (withdrawnAt === facts.head) {\n return withheld(\"withdrawn by hand\")\n }\n if (facts.reviewRunHead !== facts.head) {\n return withheld(\"no review run on this head\")\n }\n if (facts.blockingFindings > 0) {\n return withheld(`${facts.blockingFindings} blocking finding${facts.blockingFindings === 1 ? \"\" : \"s\"}`)\n }\n const ci = whyNotGreen[facts.checks]\n if (ci !== null) {\n return withheld(ci)\n }\n const merge = whyNotMergeable[facts.mergeable]\n if (merge !== null) {\n return withheld(merge)\n }\n return { stamped: true, reason: \"a clean review run on this head, green CI, mergeable\" }\n}\n\n/**\n * The head a stamp was withdrawn at, or null where none was.\n *\n * A withdrawal this version cannot read is one another version of this record\n * wrote, and a stamp is computed from everything else: forgetting it hands the\n * pull request back to the computation, where failing here would cost me the\n * command I asked for.\n */\nexport const withdrawnAt = Effect.fn(\"stamp.withdrawnAt\")(function* (repo: string, number: number) {\n const store = yield* storeFor(\"stamps\", Withdrawal)\n const withdrawal = yield* Effect.orElseSucceed(store.get(prKey(repo, number)), () => Option.none<Withdrawal>())\n return Option.match(withdrawal, { onNone: () => null, onSome: (it) => it.head })\n})\n\n/** Takes the stamp off a pull request at `head`, which is the only head it stays off. */\nexport const withdraw = Effect.fn(\"stamp.withdraw\")(function* (repo: string, number: number, head: string) {\n const store = yield* storeFor(\"stamps\", Withdrawal)\n yield* store.set(prKey(repo, number), { head })\n})\n\n/** The stamp of one tracked PR, with the withdrawal this machine holds against it. */\nexport const stampOf = Effect.fn(\"stamp.stampOf\")(function* (facts: Facts) {\n return stampFor(facts, yield* withdrawnAt(facts.repo, facts.number))\n})\n\n/**\n * Which of these tracked PRs carry a stamp, keyed the way their facts are.\n *\n * A table asks the question of every row at once, and the withdrawals are the\n * only thing here that has to be read off the disk.\n */\nexport const stampedAmong = Effect.fn(\"stamp.stampedAmong\")(function* (facts: ReadonlyArray<Facts>) {\n const marks = yield* Effect.forEach(facts, (it) =>\n Effect.map(stampOf(it), (stamp) => ({ key: prKey(it.repo, it.number), stamped: stamp.stamped }))\n )\n return new Set(marks.filter((mark) => mark.stamped).map((mark) => mark.key))\n})\n","import type { Facts } from \"#domain/bucket.ts\"\nimport type { Stampable } from \"#domain/stamp.ts\"\nimport { stampFor, whyNotGreen, whyNotMergeable } from \"#domain/stamp.ts\"\n\n/** Everything the merge guards are allowed to know about a pull request. */\nexport interface Situation extends Stampable {\n readonly repo: string\n readonly number: number\n /** Whether I opened it, which is the only kind of pull request this lands. */\n readonly mine: boolean\n readonly draft: boolean\n readonly reviewDecision: Facts[\"reviewDecision\"]\n /** The head I took the stamp off at, or null where I took it off none. */\n readonly withdrawnAt: string | null\n}\n\n/**\n * Why GitHub would not call this pull request Ready, or null where it would.\n *\n * Ready is GitHub's opinion and nothing of mine: approved, green, mergeable.\n * A repository that requires no reviewer produces no approval, which is why\n * `none` passes and `review-required` does not - what holds a merge is somebody\n * having been asked and not yet answered.\n *\n * A red CI the flaky classifier excused is still red here. The excuse is a\n * reason not to fix a check; it is not a reason to land code behind one.\n */\nconst whyNotReady = (situation: Situation): string | null => {\n if (situation.reviewDecision === \"changes-requested\") {\n return \"changes are requested\"\n }\n if (situation.reviewDecision === \"review-required\") {\n return \"a review from someone else is still wanted\"\n }\n return whyNotGreen[situation.checks] ?? whyNotMergeable[situation.mergeable]\n}\n\n/**\n * What to do about a pull request that is Ready and carries no stamp.\n *\n * The stamp is withheld for one of three reasons and each has its own next\n * step, so the refusal names that step rather than leaving me to work out which\n * of the three it was. A withdrawal is the one with no command: I took the mark\n * off code I had read, and only that code changing puts it back.\n */\nconst earnsIt = (situation: Situation): string => {\n if (situation.withdrawnAt === situation.head) {\n return \"\\n\\nYou took it off at this head, and it stays off until the head changes.\"\n }\n const next =\n situation.reviewRunHead !== situation.head\n ? {\n command: `dw-mc review ${situation.number}`,\n says: \"That reviews this head, and a run that finds nothing blocking stamps it.\"\n }\n : {\n command: `dw-mc fix ${situation.number}`,\n says:\n \"That opens a session on the findings. \" +\n \"The stamp is back once the head has moved and a review run has read it.\"\n }\n return `\\n\\n ${next.command}\\n\\n${next.says}`\n}\n\n/**\n * Why this pull request is not one to merge, or null where it is.\n *\n * This is the single place the merge guards live, and they carry more than the\n * merge does: it is the one write the tool makes that no reflog of mine undoes\n * (ADR 0008). Two bars have to be clear, because each is blind to what the\n * other sees - GitHub does not know whether anything read the diff, and the\n * stamp does not know whether a reviewer asked for changes.\n *\n * Whose pull request it is comes first, as it does everywhere else: one\n * somebody else opened is none of this tool's business, whatever is true of it.\n * A draft is next, because a pull request I have not offered to anybody is not\n * one to land however green it is.\n *\n * Ready is asked before the stamp so that the refusal names the bar I am\n * actually under. The stamp insists on green CI and a mergeable pull request\n * too, so everything it can be withheld for here is mine rather than GitHub's.\n */\nexport const decide = (situation: Situation): string | null => {\n const where = `${situation.repo}#${situation.number}`\n if (!situation.mine) {\n return `${where} is not mine. dw-mc merges pull requests I author and nothing else.`\n }\n if (situation.draft) {\n return `${where} is a draft. Mark it ready for review before merging it.`\n }\n const ready = whyNotReady(situation)\n if (ready !== null) {\n return `${where} is not Ready: ${ready}. dw-mc merges nothing GitHub would not merge itself.`\n }\n\n const stamp = stampFor(situation, situation.withdrawnAt)\n return stamp.stamped ? null : `${where} is Ready and carries no stamp: ${stamp.reason}.${earnsIt(situation)}`\n}\n","import { Console, Effect, Option } from \"effect\"\nimport { Command } from \"effect/unstable/cli\"\n\nimport { rollupState } from \"#adapters/ci.ts\"\nimport type { ConfigFile } from \"#adapters/config.ts\"\nimport { read as readConfig, settingsFor } from \"#adapters/config.ts\"\nimport { mergeabilityOf, mergePr, prView, reviewDecisionOf, viewer } from \"#adapters/gh.ts\"\nimport { named, prArgument, refuse } from \"#cli/pr.ts\"\nimport { asUserError, userFacing } from \"#cli/sweep.ts\"\nimport { decide } from \"#domain/merge.ts\"\nimport { reviewedAt, short } from \"#domain/review.ts\"\nimport { withdrawnAt } from \"#domain/stamp.ts\"\n\n/**\n * Lands one pull request of mine: squashed, with its branch deleted.\n *\n * This is the write ADR 0008 is about, and the only one the tool makes that no\n * reflog of mine brings back. It is outside ADR 0002's three because it moves a\n * shared branch; everything 0002 bars - comment, reply, thread resolve, label,\n * review, approval, status - still holds here as it does everywhere.\n *\n * The threshold is two bars at one head: Ready, which is GitHub's opinion, and\n * my stamp, which is mine. Each is blind to what the other sees, so the write\n * that cannot be undone clears both.\n *\n * GitHub's half is read live from a fresh `pr view` rather than off the last\n * sweep, the way the rebase and re-run guards are. A stale verdict costs a\n * re-run some CI minutes; here it costs merging code nobody read. My half comes\n * from the state directory, because the review runs and the withdrawal live\n * there and are already scoped to the head this read just named.\n *\n * Typing the command is the confirmation, so it takes no flag. The picker,\n * where a keystroke is cheaper, asks before it dispatches.\n */\nexport const merge = Command.make(\n \"merge\",\n { pr: prArgument },\n Effect.fn(\"merge\")(\n function* ({ pr }) {\n const file: ConfigFile = Option.getOrElse(yield* readConfig, (): ConfigFile => ({}))\n const { number, repo } = yield* named(pr, Object.keys(file.repos ?? {}).toSorted())\n const settings = settingsFor(file, repo)\n\n const view = yield* prView(repo, number)\n const me = yield* viewer\n\n const head = view.headRefOid\n\n yield* refuse(\n decide({\n repo,\n number,\n head,\n mine: view.author?.login === me,\n draft: view.isDraft,\n reviewDecision: reviewDecisionOf(view.reviewDecision),\n checks: rollupState(view.statusCheckRollup, settings.ci.ignore),\n mergeable: mergeabilityOf(view.mergeable),\n ...(yield* reviewedAt(repo, number, head, settings)),\n withdrawnAt: yield* withdrawnAt(repo, number)\n })\n )\n\n yield* mergePr(repo, number)\n\n yield* Console.log(\n `${repo}#${number} ${short(head)} squash-merged into ${view.baseRefName}, ` +\n `and ${view.headRefName} deleted`\n )\n yield* Console.log(`The squash subject is the pull request title: ${view.title}`)\n },\n Effect.catchTag(userFacing, asUserError)\n )\n).pipe(Command.withDescription(\"Squash-merge a Ready, stamped pull request of mine and delete its branch\"))\n","import type { Facts, Placed } from \"#domain/bucket.ts\"\n\n/**\n * One of the moves the picker can make on a tracked PR.\n *\n * Every one of them is a command that already exists, because the picker is a\n * front door rather than a second implementation: what it does with my answer\n * is run the command I would have typed.\n */\nexport type Action = \"resolve\" | \"rerun\" | \"review\" | \"findings\" | \"fix\" | \"rebase\" | \"withdraw\" | \"merge\"\n\n/** An action on offer, with the words the picker shows for it. */\nexport interface Offer {\n readonly action: Action\n readonly title: string\n /**\n * The question asked before this one runs, where a keystroke is not enough.\n *\n * It rides on the offer rather than being a rule the picker keeps, so what\n * gets confirmed is decided beside what gets offered. Everything without one\n * is cheap or reversible, and asking about those would teach me to answer\n * without reading.\n */\n readonly confirm?: string\n}\n\n/** A tracked PR as the picker sees it: where it sits, and what is true of it now. */\nexport interface Standing {\n readonly placed: Placed\n /** Whether it carries my stamp at this head. */\n readonly stamped: boolean\n /** Whether its repository turned rebase on. */\n readonly rebasing: boolean\n /** The head its flaky CI was already re-run at, or null where none has been. */\n readonly rerunAt: string | null\n}\n\n/**\n * The actions worth offering on one tracked PR, in the order I would take them.\n *\n * An action is offered only where it has something to act on, so the list is\n * what I can do rather than what the binary can spell: a report nothing has\n * written is not on it, and neither is a rebase the repository has not turned\n * on. A command still refuses for its own reasons when I pick it; this only\n * keeps me from picking one that was never going to do anything.\n *\n * Everything is measured against the current head. A review run or a conflict\n * recorded at a head that has gone says nothing about the branch as it is now,\n * which is the same rule the buckets are placed by.\n *\n * Resolving a conflict comes first for the reason a conflict is the first thing\n * that makes a PR mine: it makes every other signal on the PR stale.\n *\n * Merging comes last, and not because it is the least likely. The cursor rests\n * on the first row, and the one action here that no reflog undoes should not be\n * the one a stray return key reaches. It carries a confirmation of its own on\n * top of that. What it is offered on is the bucket and the mark a sweep already\n * computed; the threshold itself is `dw-mc merge`'s, read live when I pick it\n * (ADR 0008), and a draft is left out here because a sweep shows one without\n * ever acting on it.\n */\nexport const actionsFor = ({ placed, rebasing, rerunAt, stamped }: Standing): ReadonlyArray<Offer> => {\n const { facts } = placed\n const reviewed = facts.reviewRunHead === facts.head\n\n return [\n facts.rebaseConflictAt === facts.head\n ? { action: \"resolve\" as const, title: \"Open a session on the rebase conflict\" }\n : null,\n facts.checks === \"red\" && facts.ciFlaky !== null && rerunAt !== facts.head\n ? { action: \"rerun\" as const, title: \"Run the flaky CI again, once\" }\n : null,\n { action: \"review\" as const, title: reviewed ? \"Review this head again\" : \"Run a review\" },\n reviewed ? { action: \"findings\" as const, title: \"Show the review-run report\" } : null,\n reviewed ? { action: \"fix\" as const, title: \"Open a fix session on the findings\" } : null,\n rebasing ? { action: \"rebase\" as const, title: \"Rebase onto the base and push\" } : null,\n stamped ? { action: \"withdraw\" as const, title: \"Withdraw the stamp, until the head changes\" } : null,\n placed.placement.bucket === \"ready\" && stamped && !facts.draft\n ? {\n action: \"merge\" as const,\n title: \"Squash-merge it and delete the branch\",\n confirm: `Squash-merge ${facts.repo}#${facts.number} and delete its branch? Nothing here undoes that.`\n }\n : null\n ].filter((offer) => offer !== null)\n}\n\n/**\n * The arguments the picked action runs as, named the way the commands take it.\n *\n * The pull request is spelled in full, repository and all, so the argument\n * names one pull request whatever else is registered.\n */\nexport const argvFor = (action: Action, facts: Facts): ReadonlyArray<string> => {\n const pr = `${facts.repo}#${facts.number}`\n return action === \"withdraw\" ? [\"stamp\", pr, \"--withdraw\"] : [action, pr]\n}\n","import { Effect, Option, Schema } from \"effect\"\n\nimport { prKey, storeFor } from \"#adapters/store.ts\"\nimport type { ChecksState } from \"#domain/bucket.ts\"\nimport { short } from \"#domain/review.ts\"\n\n/**\n * Everything the re-run guards know before the classifier has been asked.\n *\n * It is its own type because asking the classifier costs several reads of\n * GitHub, and the guards here cost nothing: a pull request that is not mine, or\n * a head that has had its re-run, is refused without spending one of them.\n */\nexport interface Unclassified {\n readonly repo: string\n readonly number: number\n /** The head every other fact here is about, and the one the cap is scoped to. */\n readonly head: string\n /** Whether I opened the pull request, which is the only kind whose CI is mine to re-run. */\n readonly mine: boolean\n readonly checks: ChecksState\n /** The head a re-run was already asked for at, or null where none has been. */\n readonly rerunAt: string | null\n /** The workflow runs behind the failing checks, which are what there is to re-run. */\n readonly runs: ReadonlyArray<string>\n}\n\n/** The same, once the classifier has had its say. */\nexport interface Situation extends Unclassified {\n /** Why the classifier excuses this red CI, or null where it calls it mine to fix. */\n readonly flaky: string | null\n}\n\n/**\n * Why this red CI is not one to re-run without asking the classifier, or null\n * where only the classifier is left to ask.\n *\n * The order is what each refusal is about rather than what it costs, and it\n * happens that the cheapest questions are also the first worth asking. Whose\n * pull request it is comes first, because a pull request somebody else opened\n * is none of this tool's business whatever its CI says.\n *\n * The cap is one re-run per head, and it is what keeps this from being a loop:\n * a job that was flaky once and fails again at the same code is a job that is\n * not flaky. It is scoped to the head, as a withdrawn stamp and a conflict\n * record are, so a branch that moved is a branch nothing has re-run yet.\n */\nexport const refusedUnclassified = (situation: Unclassified): string | null => {\n const where = `${situation.repo}#${situation.number}`\n if (!situation.mine) {\n return `${where} is not mine. dw-mc works on pull requests I author and on nothing else.`\n }\n if (situation.checks !== \"red\") {\n return `CI is not red on ${where}, so there is nothing to re-run.`\n }\n if (situation.rerunAt === situation.head) {\n return (\n `${where} has already had its flaky CI re-run at ${short(situation.head)}. ` +\n `One re-run per head is the cap, so a job that fails twice is not flaky.`\n )\n }\n if (situation.runs.length === 0) {\n return (\n `Nothing red on ${where} is a workflow run dw-mc can re-run. ` +\n `A commit status is reported by whatever produced it, and re-running it is that thing's to do.`\n )\n }\n return null\n}\n\n/**\n * Why this red CI is not one to re-run, or null where it is.\n *\n * This is the single place the re-run guards live, and it is the whole of them:\n * the cheap ones first, and then the one that matters. A legitimate failure is\n * reported and never re-run - re-running it would hide the failure behind a\n * second identical one and cost me the minutes it takes.\n */\nexport const decide = (situation: Situation): string | null =>\n refusedUnclassified(situation) ??\n (situation.flaky === null\n ? `CI is red on ${situation.repo}#${situation.number} and nothing excuses it, so it is yours to fix. ` +\n `dw-mc reports a legitimate failure and never re-runs it.`\n : null)\n\n/**\n * A re-run that has been asked for: the head it was asked for at.\n *\n * The head is the whole record, because the head is what the cap is scoped to.\n * A branch that moved has different code, a different CI run and a re-run of\n * its own to earn.\n */\nexport const Rerun = Schema.Struct({\n head: Schema.String\n})\nexport type Rerun = typeof Rerun.Type\n\n/**\n * The head a re-run was last asked for at on this pull request, or null where\n * none has been.\n *\n * A record this version cannot read is one another version of it wrote. Reading\n * it again as nothing costs a flaky pull request one extra re-run, where failing\n * here would cost the command outright.\n */\nexport const rerunFor = Effect.fn(\"rerun.rerunFor\")(function* (repo: string, number: number) {\n const store = yield* storeFor(\"reruns\", Rerun)\n const rerun = yield* Effect.orElseSucceed(store.get(prKey(repo, number)), () => Option.none<Rerun>())\n return Option.getOrNull(rerun)?.head ?? null\n})\n\n/** Writes down that a re-run was asked for at `head`, which is the only head it caps. */\nexport const recordRerun = Effect.fn(\"rerun.recordRerun\")(function* (repo: string, number: number, head: string) {\n const store = yield* storeFor(\"reruns\", Rerun)\n yield* store.set(prKey(repo, number), { head })\n})\n","import { Console, Effect, Option } from \"effect\"\nimport type { Prompt } from \"effect/unstable/cli\"\n\nimport type { ConfigFile } from \"#adapters/config.ts\"\nimport { read as readConfig, settingsFor } from \"#adapters/config.ts\"\nimport { Paint, ink, plain } from \"#adapters/paint.ts\"\nimport { confirm, pick, width } from \"#adapters/picker.ts\"\nimport { prKey } from \"#adapters/store.ts\"\nimport { cells, rule } from \"#cli/row.ts\"\nimport { asUserError, printTroubles, sweep, userFacing } from \"#cli/sweep.ts\"\nimport { table, truncate, visible } from \"#cli/table.ts\"\nimport type { Facts } from \"#domain/bucket.ts\"\nimport { group } from \"#domain/bucket.ts\"\nimport type { Offer, Standing } from \"#domain/pick.ts\"\nimport { actionsFor, argvFor } from \"#domain/pick.ts\"\nimport { rerunFor } from \"#domain/rerun.ts\"\nimport { stampedAmong } from \"#domain/stamp.ts\"\n\n/** A title cut this short says nothing, so a row that tight loses the column instead. */\nconst shortest = 12\n\n/** The cursor, the marker and the padding a prompt draws around every row of its list. */\nconst frame = 6\n\n/**\n * How much of the screen a prompt leaves for the row itself.\n *\n * A row that wraps takes the whole list's alignment with it. Nothing is piping\n * into a prompt, so no screen to measure means the writing is going somewhere\n * that does not wrap either.\n *\n * A prompt counts the rows it has to erase from the length of what it drew,\n * and colour is length it never shows, so a coloured row has to be shorter by\n * exactly what the colour costs or the prompt erases a line above itself on\n * every keypress.\n */\nconst screenRoom = (screen: number, paint: Paint): number =>\n screen === 0 ? Number.POSITIVE_INFINITY : screen - frame - (paint === plain ? 0 : ink)\n\n/**\n * One row of the list I pick a pull request from: its bucket, and then the row\n * `dw-mc status` gives it.\n *\n * The cells come from there rather than being built again here, so the list I\n * pick from and the table I read are the same rows with the bucket moved onto\n * each of them. A prompt has no headings to group under, so the bucket is named\n * on every row; the rows are still in the order the buckets are acted on.\n */\nconst cellsOf = ({ placed, stamped }: Standing, room: number, paint: Paint): ReadonlyArray<string> =>\n cells(placed, stamped, room, paint, \"named\")\n\n/**\n * Every tracked PR as something to pick, aligned down the whole list.\n *\n * The title is the one cell worth cutting, and then the one worth dropping. The\n * bucket and what the PR waits on are why I am looking at the list at all, and\n * the pull request is how I know which one I am picking; a commit subject I\n * have half of still tells me which pull request it is, and one cut to nothing\n * tells me less than the room it took. A screen too narrow for all four columns\n * loses the title's column rather than the reason's words.\n */\nconst choicesOf = (\n standings: ReadonlyArray<Standing>,\n screen: number,\n paint: Paint\n): ReadonlyArray<Prompt.SelectChoice<Standing>> => {\n const measured = standings.map((it) => cellsOf(it, Number.POSITIVE_INFINITY, paint))\n const widest = (index: number) => Math.max(...measured.map((row) => visible(row[index] ?? \"\")))\n const room = screenRoom(screen, paint) - (widest(0) + widest(1) + widest(3)) - rule.length * 3\n const told = room >= shortest\n\n const rows = table(\n standings.map((it) => {\n const row = cellsOf(it, told ? room : 0, paint)\n return told ? row : [row[0] ?? \"\", row[1] ?? \"\", row[3] ?? \"\"]\n }),\n rule\n )\n return standings.map((standing, index) => ({\n title: truncate(rows[index] ?? \"\", screenRoom(screen, paint)),\n value: standing\n }))\n}\n\nconst actionChoices = (offers: ReadonlyArray<Offer>): ReadonlyArray<Prompt.SelectChoice<Offer>> =>\n offers.map((offer) => ({ title: offer.title, value: offer }))\n\nconst where = (facts: Facts): string => `${facts.repo}#${facts.number}`\n\n/**\n * The front door: pick a pull request, pick what to do with it, read the report.\n *\n * It sweeps first, every time, for the reason `dw-mc status` does: a list I\n * pick from is never one I forgot to refresh. What the sweep could not read is\n * said before the prompt opens, so a pull request missing from the list has its\n * explanation above it rather than after I have chosen.\n *\n * The picker runs nothing of its own. The action I choose is dispatched as the\n * arguments I would have typed, through the same parser and into the same\n * command, so there is one implementation of every action and the picker is\n * only a way of reaching it without remembering the flags.\n *\n * Walking away at any prompt is an answer rather than a failure, and it leaves\n * nothing behind: nothing has been dispatched until every question is answered.\n * An action that carries its own question is asked it here, between the choice\n * and the dispatch, because the picker is where an action costs one keystroke\n * and a merge must never cost only that (ADR 0008).\n */\nexport const picker = <E, R>(dispatch: (argv: ReadonlyArray<string>) => Effect.Effect<void, E, R>) =>\n Effect.fn(\"pick\")(\n function* () {\n const report = yield* sweep\n\n if (report.repos.length === 0) {\n yield* Console.log(\"No repositories registered. Run dw-mc init inside a repository to register it.\")\n return\n }\n\n const stamped = yield* stampedAmong(report.facts)\n const file: ConfigFile = Option.getOrElse(yield* readConfig, (): ConfigFile => ({}))\n const standings = yield* Effect.forEach(\n group(report.facts).flatMap((grouped) => grouped.placed),\n Effect.fnUntraced(function* (placed) {\n return {\n placed,\n stamped: stamped.has(prKey(placed.facts.repo, placed.facts.number)),\n rebasing: settingsFor(file, placed.facts.repo).rebase.enabled,\n rerunAt: yield* rerunFor(placed.facts.repo, placed.facts.number)\n }\n })\n )\n\n yield* printTroubles(report.troubles)\n if (standings.length === 0) {\n yield* Console.log(\"No open pull requests.\")\n return\n }\n\n const chosen = yield* pick(\"Which pull request?\", choicesOf(standings, yield* width, yield* Paint))\n if (Option.isNone(chosen)) {\n return\n }\n\n const facts = chosen.value.placed.facts\n const offer = yield* pick(`What do I do with ${where(facts)}?`, actionChoices(actionsFor(chosen.value)))\n if (Option.isNone(offer)) {\n return\n }\n\n const question = offer.value.confirm\n if (question !== undefined && !(yield* confirm(question))) {\n yield* Console.log(`Nothing done to ${where(facts)}.`)\n return\n }\n\n yield* dispatch(argvFor(offer.value.action, facts))\n },\n Effect.catchTag(userFacing, asUserError)\n )\n","import { Console, Effect, Option } from \"effect\"\nimport { Command } from \"effect/unstable/cli\"\n\nimport { rollupState } from \"#adapters/ci.ts\"\nimport type { ConfigFile } from \"#adapters/config.ts\"\nimport { read as readConfig, settingsFor } from \"#adapters/config.ts\"\nimport { openPrs, prView, viewer } from \"#adapters/gh.ts\"\nimport { rebaseOnto } from \"#adapters/git.ts\"\nimport { named, prArgument, refuse } from \"#cli/pr.ts\"\nimport { asUserError, userFacing } from \"#cli/sweep.ts\"\nimport { count } from \"#cli/table.ts\"\nimport { decide, recordConflict, stackOf } from \"#domain/rebase.ts\"\nimport { short } from \"#domain/review.ts\"\n\n/**\n * Brings one branch up to date with its base, with the guards that matter more\n * than the rebase does.\n *\n * This is the heavier of the two writes ADR 0002 admits, and it writes one\n * thing: a push to a branch I author, in the repository the branch is in, with\n * a lease, onto the head this run read. Who opened the pull request and where\n * its branch lives are read from GitHub and checked before anything is cut. No\n * comment, reply, thread resolve, label, review, approval or status, here or\n * anywhere. The merge is a write of its own and lives in `dw-mc merge` alone\n * (ADR 0008).\n *\n * Every guard is read live rather than off the last sweep, because each of them\n * is about the branch as it is now: a sweep from ten minutes ago cannot say\n * whether CI is running, and a rebase decided on that would cancel the run I am\n * waiting on.\n *\n * A conflict is written down against the head it conflicted at, with the files\n * it stopped on, which puts the pull request in Needs me until the branch\n * moves. The files are what makes it something to open, and `dw-mc resolve` is\n * what opens it - said here, because a conflict is where the next step stops\n * being obvious, and never taken here, because a session is opened when I ask\n * for one. Nothing half-finished is left behind either way: the rebase aborts\n * and the worktree it ran in goes with the run.\n *\n * A stack is recognised and never driven. The tool does not understand stacks,\n * so what it has to say about one is where the pull request sits in it.\n */\nexport const rebase = Command.make(\n \"rebase\",\n { pr: prArgument },\n Effect.fn(\"rebase\")(\n function* ({ pr }) {\n const file: ConfigFile = Option.getOrElse(yield* readConfig, (): ConfigFile => ({}))\n const { number, repo } = yield* named(pr, Object.keys(file.repos ?? {}).toSorted())\n const settings = settingsFor(file, repo)\n\n const view = yield* prView(repo, number)\n const open = yield* openPrs(repo)\n const me = yield* viewer\n\n yield* refuse(\n decide({\n repo,\n number,\n base: view.baseRefName,\n enabled: settings.rebase.enabled,\n mine: view.author?.login === me,\n fromFork: view.isCrossRepository,\n listed: open.some((it) => it.number === number),\n checks: rollupState(view.statusCheckRollup, settings.ci.ignore),\n stack: stackOf(number, open)\n })\n )\n\n const where = `${repo}#${number}`\n const done = yield* rebaseOnto(repo, number, view.baseRefName, view.headRefName)\n\n if (done._tag === \"up-to-date\") {\n yield* Console.log(`${where} ${short(view.headRefOid)} already on ${view.baseRefName}`)\n return\n }\n if (done._tag === \"conflicted\") {\n yield* recordConflict(repo, number, view.headRefOid, done.paths)\n yield* Console.log(\n `${where} ${short(view.headRefOid)} the rebase onto ${view.baseRefName} conflicted, ` +\n `so it was aborted and nothing was pushed.`\n )\n if (done.paths.length > 0) {\n yield* Console.log(`It stopped on ${count(done.paths.length, \"file\")}:`)\n yield* Effect.forEach(done.paths, (path) => Console.log(` ${path}`))\n }\n\n // A conflict is where the next step stops being obvious, so the step is\n // on screen as itself. Nothing follows it on its own: the session is\n // opened when I ask for it and never because a rebase stopped.\n yield* Effect.forEach([``, ` dw-mc resolve ${number}`, ``], (line) => Console.log(line))\n yield* Console.log(\n `That opens a session on the conflict, in a worktree of your own. ` +\n `The next sweep puts it in Needs me, and it stays there until the branch moves.`\n )\n return\n }\n\n yield* Console.log(\n `${where} ${short(done.before)} → ${short(done.after)} ` +\n `rebased ${count(done.behind, \"commit\")} of ${view.baseRefName} and pushed with a lease`\n )\n },\n Effect.catchTag([...userFacing, \"GitFailed\"], asUserError)\n )\n).pipe(Command.withDescription(\"Rebase one branch onto its base and push it with a lease\"))\n","import { Console, Effect, Option } from \"effect\"\nimport { Command } from \"effect/unstable/cli\"\n\nimport { failedRuns, rerunFailed, rollupState } from \"#adapters/ci.ts\"\nimport type { ConfigFile } from \"#adapters/config.ts\"\nimport { read as readConfig, settingsFor } from \"#adapters/config.ts\"\nimport { prView, viewer } from \"#adapters/gh.ts\"\nimport { named, prArgument, refuse } from \"#cli/pr.ts\"\nimport { asUserError, userFacing } from \"#cli/sweep.ts\"\nimport { count } from \"#cli/table.ts\"\nimport { flakyReason } from \"#domain/flaky.ts\"\nimport { decide, recordRerun, refusedUnclassified, rerunFor } from \"#domain/rerun.ts\"\nimport { short } from \"#domain/review.ts\"\n\n/**\n * Runs a flaky CI again, once, and never a CI that is mine to fix.\n *\n * This is the lighter of the two writes ADR 0002 admits: `gh run rerun\n * --failed` starts the jobs that failed over on a workflow run of a pull\n * request I author. No comment, reply, thread resolve, label, review, approval\n * or status, here or anywhere. The merge is a write of its own and lives in\n * `dw-mc merge` alone (ADR 0008).\n *\n * Every signal is read live rather than off the last sweep, for the reason the\n * rebase guards are: a verdict from ten minutes ago can be about a head that\n * has gone, and spending CI minutes on that is spending them on nothing.\n *\n * The head is written down before a single run is asked for, because the cap is\n * what keeps this from looping and a cap a crash can lose is no cap. The cost of\n * getting it wrong that way is one re-run I have to ask for again; the other way\n * it is a pull request re-running itself until the minutes run out.\n */\nexport const rerun = Command.make(\n \"rerun\",\n { pr: prArgument },\n Effect.fn(\"rerun\")(\n function* ({ pr }) {\n const file: ConfigFile = Option.getOrElse(yield* readConfig, (): ConfigFile => ({}))\n const { number, repo } = yield* named(pr, Object.keys(file.repos ?? {}).toSorted())\n const settings = settingsFor(file, repo)\n\n const view = yield* prView(repo, number)\n const me = yield* viewer\n\n const unclassified = {\n repo,\n number,\n head: view.headRefOid,\n mine: view.author?.login === me,\n checks: rollupState(view.statusCheckRollup, settings.ci.ignore),\n rerunAt: yield* rerunFor(repo, number),\n runs: failedRuns(view.statusCheckRollup, settings.ci.ignore)\n }\n // Asking the classifier costs a handful of reads of GitHub and up to\n // three job logs, so the guards that cost nothing are asked first: a\n // head that has had its re-run is refused without paying for a verdict\n // about it.\n yield* refuse(refusedUnclassified(unclassified))\n\n const flaky = yield* flakyReason(\n repo,\n number,\n view.statusCheckRollup,\n settings.ci.ignore,\n settings.ci.flaky_patterns\n )\n yield* refuse(decide({ ...unclassified, flaky }))\n\n yield* recordRerun(repo, number, view.headRefOid)\n yield* Effect.forEach(unclassified.runs, (run) => rerunFailed(repo, run))\n\n const where = `${repo}#${number}`\n yield* Console.log(\n `${where} ${short(view.headRefOid)} re-ran the failed jobs of ${count(unclassified.runs.length, \"workflow run\")}`\n )\n yield* Console.log(`It is flaky because ${flaky}.`)\n yield* Console.log(`This head gets no second re-run; if it fails again, the failure is yours.`)\n },\n Effect.catchTag(userFacing, asUserError)\n )\n).pipe(Command.withDescription(\"Run a flaky red CI again, once per head\"))\n","import { Effect, Schema } from \"effect\"\n\nimport type { Branch } from \"#domain/rebase.ts\"\nimport { boundary } from \"#domain/rebase.ts\"\nimport { short } from \"#domain/review.ts\"\n\n/** Everything the resolve guards are allowed to know about a pull request. */\nexport interface Situation extends Branch {\n /** Where the pull request is now. */\n readonly head: string\n /** The head a conflict was recorded at, or null where none was. */\n readonly conflictAt: string | null\n}\n\n/**\n * Why this conflict is not one to open a session on, or null where it is.\n *\n * `rebase.enabled` is not asked. That key exists so a force push is never a\n * surprise, and the only push here is my own from the worktree; a session that\n * writes nothing needs no permission to push.\n *\n * The branch's own guards are the same ones a rebase reads, because they are\n * about the branch rather than about what is done to it: a pull request I did\n * not author, one whose branch lives in a fork and one in a stack are none of\n * this tool's business whichever command asks.\n *\n * What is left is this command's own: a conflict is recorded against the head\n * it happened at, so a branch that has moved past it is one nothing here has\n * tried to rebase yet. Either way the answer is the same command, because a\n * conflict to resolve is one a rebase hit.\n */\nexport const decide = (situation: Situation): string | null => {\n const refused = boundary(situation)\n if (refused !== null) {\n return refused\n }\n const where = `${situation.repo}#${situation.number}`\n if (situation.conflictAt === null) {\n return (\n `${where} has no conflict recorded at ${short(situation.head)}. ` +\n `Run dw-mc rebase ${situation.number}: a conflict to resolve is one a rebase hit.`\n )\n }\n if (situation.conflictAt !== situation.head) {\n return (\n `The conflict on ${where} was recorded at ${short(situation.conflictAt)} and the branch is now at ` +\n `${short(situation.head)}. Run dw-mc rebase ${situation.number} to see what the head it is at hits.`\n )\n }\n return null\n}\n\n/** The conflict a session opens on: where it happened, and what it stopped on. */\nexport const Conflicted = Schema.Struct({\n repo: Schema.String,\n number: Schema.Int,\n head: Schema.String,\n /** The branch the pull request merges into, which is what the replay stopped against. */\n base: Schema.String,\n /** What the pull request is for, which is what its conflicting hunks have to keep meaning. */\n title: Schema.String,\n paths: Schema.Array(Schema.String)\n})\nexport type Conflicted = typeof Conflicted.Type\n\n/** The conflict as the JSON the schema defines, rather than as this file spells it. */\nconst asJson = Schema.encodeEffect(Schema.fromJsonString(Conflicted))\n\n/**\n * The prompt a resolve session opens on: what stopped the replay, and the\n * conflict itself as JSON.\n *\n * The paths go in verbatim rather than described, for the reason a fix\n * session's findings do: a re-description is where a path quietly changes. The\n * title goes in because a hunk is resolved against what the pull request is\n * for, and the base because the two sides of every conflict are the branch and\n * it.\n *\n * The rebase stays mine to finish. The session works the files and stops\n * there: continuing the rebase, committing and pushing are three things I do\n * after reading what it did, and a session that did them would be resolving the\n * conflict for me rather than with me.\n */\nexport const promptFor = (conflicted: Conflicted): Effect.Effect<string, Schema.SchemaError> =>\n Effect.map(asJson(conflicted), (json) =>\n [\n `A dw-mc rebase of ${conflicted.repo}#${conflicted.number} onto ${conflicted.base} stopped on a conflict. ` +\n `You are in a worktree standing on the pull request's commits at ${short(conflicted.head)}, ` +\n `with that rebase in progress and the files below unmerged.`,\n `The pull request is \"${conflicted.title}\". Resolve each file so it keeps meaning that and keeps ` +\n `whatever ${conflicted.base} changed underneath it; where the two cannot both hold, say so and stop.`,\n `Do not run git rebase --continue, do not commit and do not push. I read the resolution and do all three ` +\n `myself.`,\n json\n ].join(\"\\n\\n\")\n )\n","import { Console, Effect, Option } from \"effect\"\nimport { CliError, Command, Flag } from \"effect/unstable/cli\"\n\nimport { steeredSession } from \"#adapters/claude.ts\"\nimport type { ConfigFile } from \"#adapters/config.ts\"\nimport { launcherOf, read as readConfig } from \"#adapters/config.ts\"\nimport { openPrs, prView, viewer } from \"#adapters/gh.ts\"\nimport { rebaseInPlace, standingWorktree } from \"#adapters/git.ts\"\nimport { named, prArgument } from \"#cli/pr.ts\"\nimport { asUserError, userFacing } from \"#cli/sweep.ts\"\nimport { count } from \"#cli/table.ts\"\nimport { conflictFor, stackOf } from \"#domain/rebase.ts\"\nimport type { Situation } from \"#domain/resolve.ts\"\nimport { decide, promptFor } from \"#domain/resolve.ts\"\nimport { short } from \"#domain/review.ts\"\n\nconst printFlag = Flag.Boolean(\"print\").pipe(\n Flag.withDefault(false),\n Flag.withDescription(\"Print the prompt a session would open on, and open none\")\n)\n\n/** The domain's word on a conflict that is not one to open, as the command's own failure. */\nconst allowed = (situation: Situation) => {\n const refused = decide(situation)\n return refused === null ? Effect.void : Effect.fail(new CliError.UserError({ cause: refused }))\n}\n\n/**\n * A session on the conflict that stopped a rebase, in a worktree that is mine.\n *\n * `dw-mc rebase` is untouched by this: it aborts, pushes nothing and leaves no\n * partial state. This is the deliberate step afterwards, and it redoes the\n * rebase itself rather than inheriting a half-finished one - the worktree here\n * is one I asked for and it stands, so a rebase in progress in it is the whole\n * point rather than a broken invariant.\n *\n * The tool resolves nothing. It replays onto the base, shows what the replay\n * stopped on and hands an interactive session what conflicted and what the pull\n * request is for; then it is out of the way. Finishing the rebase, committing\n * and pushing are mine, from the worktree, which is why the worktree outlives\n * the session. Nothing here writes to GitHub.\n *\n * `rerere` is turned on in the clone before the replay, so the resolution I\n * make once is one `git` replays by itself the next time a rebase hits it, with\n * no model involved at all. That is also why a replay can go through with\n * nothing to resolve.\n */\nexport const resolve = Command.make(\n \"resolve\",\n { pr: prArgument, print: printFlag },\n Effect.fn(\"resolve\")(\n function* ({ pr, print }) {\n const file: ConfigFile = Option.getOrElse(yield* readConfig, (): ConfigFile => ({}))\n const { number, repo } = yield* named(pr, Object.keys(file.repos ?? {}).toSorted())\n\n const view = yield* prView(repo, number)\n const open = yield* openPrs(repo)\n const me = yield* viewer\n const conflict = yield* conflictFor(repo, number)\n\n yield* allowed({\n repo,\n number,\n mine: view.author?.login === me,\n fromFork: view.isCrossRepository,\n listed: open.some((it) => it.number === number),\n stack: stackOf(number, open),\n head: view.headRefOid,\n conflictAt: conflict === null ? null : conflict.head\n })\n\n /** The conflict as the prompt takes it, around whichever paths are known by then. */\n const conflicted = (paths: ReadonlyArray<string>) => ({\n repo,\n number,\n head: view.headRefOid,\n base: view.baseRefName,\n title: view.title,\n paths\n })\n\n // The prompt on its own, for the session I already have open. Nothing is\n // cut and no replay is run: the paths are the ones the rebase wrote down,\n // which is everything a prompt has to carry.\n if (print) {\n yield* Console.log(yield* promptFor(conflicted(conflict?.paths ?? [])))\n return\n }\n\n const where = `${repo}#${number}`\n const worktree = yield* standingWorktree(repo, number, view.headRefName, \"rebase\")\n yield* Console.log(\n `${where} ${short(view.headRefOid)} replaying onto ${view.baseRefName} in ${worktree.directory}`\n )\n\n const stopped = yield* rebaseInPlace(worktree.directory, view.baseRefName)\n if (stopped._tag === \"replayed\") {\n yield* Console.log(\n `The replay went through, so there is nothing to resolve: git replayed a resolution you made before, ` +\n `or the conflict is gone.`\n )\n yield* Console.log(`The worktree stands where it replayed, and the push onto ${view.headRefName} is yours:`)\n yield* Effect.forEach([``, ` cd ${worktree.directory}`, ` git push`, ``], (line) => Console.log(line))\n yield* Console.log(`Once you have pushed, dw-mc review ${number} reviews the new head as a new run.`)\n return\n }\n\n yield* Console.log(`It stopped on ${count(stopped.paths.length, \"file\")}:`)\n yield* Effect.forEach(stopped.paths, (path) => Console.log(` ${path}`))\n\n const ended = yield* steeredSession({\n launcher: launcherOf(file),\n directory: worktree.directory,\n prompt: yield* promptFor(conflicted(stopped.paths))\n })\n\n yield* Console.log(ended === 0 ? \"The session is over.\" : `The session ended with ${ended}.`)\n yield* Console.log(\"Nothing was committed or pushed for you; the rebase stands where it stopped.\")\n\n // What is left to do is what is left to run, so it is on screen as\n // itself: the rebase is finished and pushed by me, from the worktree,\n // and a sentence about it is one more thing to translate.\n yield* Effect.forEach([``, ` cd ${worktree.directory}`, ` git rebase --continue`, ` git push`, ``], (line) =>\n Console.log(line)\n )\n yield* Console.log(`Once you have pushed, dw-mc review ${number} reviews the new head as a new run.`)\n },\n Effect.catchTag([...userFacing, \"GitFailed\", \"WorktreeHeld\", \"AgentFailed\"], asUserError)\n )\n).pipe(Command.withDescription(\"Open a session on the conflict that stopped a rebase, in a worktree of my own\"))\n","import { Effect, Terminal } from \"effect\"\n\nimport { capture } from \"#adapters/spawner.ts\"\n\n/** A string as AppleScript spells one, so a quotation mark cannot end it early. */\nconst quoted = (text: string): string => `\"${text.replaceAll(\"\\\\\", \"\\\\\\\\\").replaceAll(`\"`, `\\\\\"`)}\"`\n\n/**\n * Says a foreground run has ended, twice: the bell for the terminal I left, and\n * a desktop notification for the window I went to instead.\n *\n * A review run takes minutes, and the whole point of it running in the\n * foreground is that I go and do something else while it does. Neither half is\n * worth failing a finished run over: `osascript` is macOS's, and a machine\n * without it still finished the review.\n */\nexport const announce = Effect.fn(\"notify.announce\")(function* (title: string, message: string) {\n const terminal = yield* Terminal.Terminal\n yield* Effect.ignore(terminal.display(\"\\u0007\"))\n yield* Effect.ignore(\n capture(\"osascript\", [\"-e\", `display notification ${quoted(message)} with title ${quoted(title)}`])\n )\n})\n","import { Clock, Console, Duration, Effect, Fiber, Terminal } from \"effect\"\n\n/** The frames of the spinner, in the order they turn. */\nconst frames = [\"⠋\", \"⠙\", \"⠹\", \"⠸\", \"⠼\", \"⠴\", \"⠦\", \"⠧\", \"⠇\", \"⠏\"]\n\n/** How long one frame is on the screen. */\nconst frameFor = Duration.millis(120)\n\n/** What the run has reached for so far. */\nexport interface Doing {\n readonly tools: number\n readonly subagents: number\n}\n\n/** A stretch of time as a terminal says it: `1m12s`, or `9s` under the minute. */\nconst elapsed = (millis: number): string => {\n const seconds = Math.floor(millis / 1000)\n return seconds < 60 ? `${seconds}s` : `${Math.floor(seconds / 60)}m${String(seconds % 60).padStart(2, \"0\")}s`\n}\n\n/** How the line reads: the spinner, and whatever the caller makes of the counts. */\ntype Reads = (doing: Doing, since: string) => string\n\n/**\n * Runs `use` while the screen says it is still going, and hands `use` the way\n * to report what the run reached for.\n *\n * A review takes minutes, and a terminal that prints nothing for minutes is one\n * I stop trusting. What it printed instead was a line per tool call, which is a\n * wall of `· Bash` that says as little as silence did. This keeps one line and\n * rewrites it: the spinner says the run is alive, the counts say how far it has\n * got, and the line is gone when the run is over, so what stays on the screen is\n * the report.\n *\n * How that line reads is `reads` and not this module's business. What a count\n * is worth saying belongs to the command that is counting, and a screen that\n * worded it here would need the words a command already has.\n *\n * Where there is no screen to measure - a pipe, a CI log, a test - the counts\n * would be a mess of half-drawn lines, so the tools go out one to a line as\n * they did before. `columns` is zero exactly there.\n */\nexport const spinning = Effect.fnUntraced(function* <A, E, R>(\n reads: Reads,\n use: (onTool: (tool: string) => Effect.Effect<void>) => Effect.Effect<A, E, R>\n) {\n const terminal = yield* Terminal.Terminal\n const columns = yield* terminal.columns\n if (columns === 0) {\n return yield* use((tool) => Console.log(` · ${tool}`))\n }\n\n let doing: Doing = { tools: 0, subagents: 0 }\n const onTool = (tool: string) =>\n Effect.sync(() => {\n doing = { tools: doing.tools + 1, subagents: doing.subagents + (tool === \"Agent\" ? 1 : 0) }\n })\n\n const started = yield* Clock.currentTimeMillis\n const draw = (text: string) => Effect.ignore(terminal.display(`\\r${text.slice(0, columns - 1).padEnd(columns - 1)}`))\n\n // The first frame is drawn here rather than in the fiber, so the line is on\n // the screen the moment the run starts rather than one frame into it.\n const frame = (since: number, turn: number) => `${frames[turn % frames.length]} ${reads(doing, elapsed(since))}`\n\n yield* draw(frame(0, 0))\n const turning = yield* Effect.forkChild(\n Effect.gen(function* () {\n for (let turn = 1; ; turn = turn + 1) {\n yield* Effect.sleep(frameFor)\n yield* draw(frame((yield* Clock.currentTimeMillis) - started, turn))\n }\n })\n )\n\n return yield* Effect.onExit(use(onTool), () =>\n Effect.flatMap(Fiber.interrupt(turning), () => Effect.ignore(terminal.display(`\\r${\" \".repeat(columns - 1)}\\r`)))\n )\n})\n","/**\n * What a review run is opened on, and the prompt the tool carries.\n *\n * Where a slash command drives the agent's own review, this is a prompt of the\n * tool's own, so my bar is not one agent's idea of a code review. How a turn is\n * spawned belongs to the Claude Code adapter; which turn it is belongs here.\n */\nimport type { ReviewTurn } from \"#adapters/claude.ts\"\n\n/**\n * The reviewer persona, derived from Addy Osmani's `code-reviewer` agent\n * (`addyosmani/agent-skills`, MIT, see `NOTICE.md`).\n *\n * The five dimensions, their questions and the four severity words are his. The\n * Markdown report template is not: a run here answers as structured output\n * against a schema, so a template that asks for headings would be a second\n * shape to reconcile. `docs/adr/0006-source-layout.md` puts it in the domain\n * because it is text and a decision about text, with nothing outside to reach.\n */\nconst persona = `You are an experienced staff engineer conducting a thorough code review. Evaluate the\nchange and report actionable, categorised findings.\n\nEvaluate every change across these five dimensions.\n\n1. Correctness. Does the code do what the task says it should? Are edge cases handled - null, empty,\n boundary values, error paths? Do the tests verify the behaviour, and are they testing the right\n things? Are there race conditions, off-by-one errors or state inconsistencies?\n2. Readability. Can another engineer understand this without explanation? Are names descriptive and\n consistent with the project's conventions? Is the control flow straightforward? Is related code\n grouped, with clear boundaries?\n3. Architecture. Does the change follow the existing patterns, or introduce a new one, and is a new\n one justified? Are module boundaries maintained? Is the abstraction level appropriate - neither\n over-engineered nor too coupled? Do dependencies flow in the right direction?\n4. Security. Is input validated at the system boundaries? Are secrets kept out of code, logs and\n version control? Is authorisation checked where it is needed? Are queries parameterised and output\n encoded? Does a new dependency carry known vulnerabilities?\n5. Performance. Any N+1 query patterns? Any unbounded loop or unconstrained fetch? Any synchronous\n work that should be asynchronous? Any missing pagination?\n\nGrade every finding with one of four words, and report the severity each maps to:\n\n- Critical, which blocks the merge - a security hole, a risk of data loss, broken functionality - is\n reported as error.\n- Required, which must be addressed before merge - a missing test, the wrong abstraction, poor error\n handling - is reported as error.\n- Optional, which is worth considering and not required - a simpler design, a useful refactor - is\n reported as warning.\n- Nit, which is minor and the author may ignore, and FYI, which is context rather than a request, are\n reported as info.\n\nWork by these rules. Read the tests first: they say what the change intends and what it covers. Read\nthe task or the pull request description before the code. Every Critical and Required finding names a\nspecific fix in its summary. Where you are uncertain, say so in the summary and say what would settle\nit, rather than guessing.`\n\n/** What a review run is about, as much of it as the prompt needs to say. */\nexport interface Reviewing {\n readonly repo: string\n readonly number: number\n readonly title: string\n /** The branch the pull request targets, which is what the change is measured against. */\n readonly base: string\n /** My own review instructions, passed through untouched, or null. */\n readonly prompt: string | null\n}\n\n/**\n * The prompt a review run with no slash command opens on.\n *\n * It says what to review and how to answer, and nothing about how the answer is\n * validated: the schema arrives beside the prompt, so describing it here would\n * be the same shape written twice.\n *\n * `review.prompt` is a passthrough and goes in first, spelled exactly as the\n * file spells it. A repository with its own instructions gets its review with\n * the persona behind it, and the tool does not try to interpret the value.\n */\nexport const reviewPrompt = (reviewing: Reviewing): string =>\n [\n ...(reviewing.prompt === null ? [] : [reviewing.prompt, \"\"]),\n persona,\n \"\",\n `The change is ${reviewing.repo}#${reviewing.number}, \"${reviewing.title}\".`,\n `This worktree stands at its head. \\`git diff ${reviewing.base}...HEAD\\` is the change under`,\n \"review; read whatever file it names in full where the change needs the context.\",\n \"\",\n \"Answer as structured output. Every finding carries the file it is in as a repository path, the\",\n \"line it is at, its severity and a one-sentence summary. The verdict is clean when there is\",\n \"nothing to report, and findings otherwise.\"\n ].join(\"\\n\")\n\n/**\n * What one review run opens on, decided by what the repository configured.\n *\n * A slash command is the review, so the persona stays out of its way and my own\n * instructions ride beside it. Without one the review is the tool's own, and my\n * instructions go in front of the persona. The effort word follows the command\n * because that is where a slash command takes its arguments; a repository that\n * spells its own arguments out sets `review.effort` to null and keeps the line.\n */\nexport const turnFor = (\n review: { readonly command: string | null; readonly effort: string | null; readonly prompt: string | null },\n about: Reviewing\n): ReviewTurn =>\n review.command === null\n ? { _tag: \"prompt\", text: reviewPrompt({ ...about, prompt: review.prompt }) }\n : {\n _tag: \"command\",\n line: [review.command, review.effort].filter((part) => part !== null).join(\" \"),\n instructions: review.prompt\n }\n","import { Console, DateTime, Effect, Exit, Option, Result, Schema } from \"effect\"\nimport { CliError, Command, Flag } from \"effect/unstable/cli\"\n\nimport type { AgentFailed } from \"#adapters/agent.ts\"\nimport type { ReviewTurn } from \"#adapters/claude.ts\"\nimport { reviewTurns } from \"#adapters/claude.ts\"\nimport type { ConfigFile, Effort, Launcher, Settings } from \"#adapters/config.ts\"\nimport { launcherOf, read as readConfig, settingsFor } from \"#adapters/config.ts\"\nimport { comparedFiles, prView } from \"#adapters/gh.ts\"\nimport { withWorktree } from \"#adapters/git.ts\"\nimport { announce } from \"#adapters/notify.ts\"\nimport type { Doing } from \"#adapters/progress.ts\"\nimport { spinning } from \"#adapters/progress.ts\"\nimport { stateDirectory, storeFor, textStoreFor } from \"#adapters/store.ts\"\nimport { lines, summary } from \"#cli/findings.ts\"\nimport { named, prArgument } from \"#cli/pr.ts\"\nimport { asUserError, userFacing } from \"#cli/sweep.ts\"\nimport { count } from \"#cli/table.ts\"\nimport { asMarkdown, jsonSchema, Reported } from \"#domain/findings.ts\"\nimport type { Reviewing } from \"#domain/persona.ts\"\nimport { turnFor } from \"#domain/persona.ts\"\nimport type { Asked, Outcome } from \"#domain/review.ts\"\nimport {\n LastReviewed,\n lastRun,\n latestKey,\n detailOf,\n reportDocument,\n reportedBy,\n reportKey,\n ReviewRun,\n runKey,\n short,\n skippedSince\n} from \"#domain/review.ts\"\n\n/** What the spinner says a run has got through, while it is still going. */\nconst saying = (doing: Doing, since: string): string =>\n [\"reviewing\", count(doing.tools, \"tool\"), doing.subagents === 0 ? null : count(doing.subagents, \"subagent\"), since]\n .filter((part) => part !== null)\n .join(\" · \")\n\nconst commandFlag = Flag.String(\"command\").pipe(\n Flag.withDescription(\"The slash command this run opens on, over what the repository configured\"),\n Flag.optional\n)\n\nconst promptFlag = Flag.String(\"prompt\").pipe(\n Flag.withDescription(\"The review instructions this run carries, over what the repository configured\"),\n Flag.optional\n)\n\nconst effortFlag = Flag.Literals(\"effort\", [\"low\", \"medium\", \"high\", \"xhigh\", \"max\"]).pipe(\n Flag.withDescription(\"How much this run spends, over what the repository configured\"),\n Flag.optional\n)\n\nconst modelFlag = Flag.String(\"model\").pipe(\n Flag.withDescription(\"The model this run reads the code on, over what the repository configured\"),\n Flag.optional\n)\n\nconst promptOnlyFlag = Flag.Boolean(\"prompt-only\").pipe(\n Flag.withDefault(false),\n Flag.withDescription(\"Review on the prompt alone, whatever slash command the repository configured\")\n)\n\nconst commandOnlyFlag = Flag.Boolean(\"command-only\").pipe(\n Flag.withDefault(false),\n Flag.withDescription(\"Review on the slash command alone, whatever instructions the repository configured\")\n)\n\nconst forceFlag = Flag.Boolean(\"force\").pipe(\n Flag.withDefault(false),\n Flag.withDescription(\"Review even where the re-run rule would skip it\")\n)\n\n/** A flag that names a value beside the flag that clears it: one of the two, never both. */\nconst opposite = (flag: string, given: Option.Option<string>, only: string) =>\n Option.isSome(given) ? [`--${flag} and --${only} say opposite things. Pass one.`] : []\n\n/** What this run is asked, once the flags have had their say over the file. */\nconst asking = (options: {\n readonly settings: Settings\n readonly command: Option.Option<string>\n readonly prompt: Option.Option<string>\n readonly effort: Option.Option<Effort>\n readonly model: Option.Option<string>\n readonly promptOnly: boolean\n readonly commandOnly: boolean\n}) => {\n const clash = [\n ...(options.promptOnly ? opposite(\"command\", options.command, \"prompt-only\") : []),\n ...(options.commandOnly ? opposite(\"prompt\", options.prompt, \"command-only\") : [])\n ]\n if (clash.length > 0) {\n return Effect.fail(new CliError.UserError({ cause: clash.join(\" \") }))\n }\n\n const { review } = options.settings\n return Effect.succeed({\n command: options.promptOnly ? null : Option.getOrElse(options.command, () => review.command),\n effort: Option.getOrElse(options.effort, () => review.effort),\n prompt: options.commandOnly ? null : Option.getOrElse(options.prompt, () => review.prompt),\n model: Option.getOrElse(options.model, () => review.model)\n })\n}\n\n/**\n * What the re-run rule is asked about, read before anything is cut or spawned:\n * the whole point of the rule is not paying for the run.\n *\n * GitHub is asked what changed only where there is a run to measure from and a\n * different head to measure to. Neither is the rule deciding anything - there is\n * simply nothing to compare - and a comparison GitHub would not answer comes\n * back as nothing known rather than as a failure of the command.\n */\nconst askedOf = Effect.fn(\"review.askedOf\")(function* (repo: string, number: number, head: string) {\n const last = Option.getOrNull(yield* lastRun(repo, number))\n const changed =\n last === null || last.head === head\n ? null\n : Option.getOrNull(yield* Effect.option(comparedFiles(repo, last.head, head)))\n return { last, head, changed } satisfies Asked\n})\n\n/** How a run reads on the line above it: what it opens on, and on which model. */\nconst spending = (turn: ReviewTurn, model: string | null): string =>\n [\n turn._tag === \"command\" ? turn.line : \"the tool's own prompt\",\n turn._tag === \"command\" && turn.instructions !== null ? \"with my own instructions\" : null,\n model === null ? null : `model ${model}`\n ]\n .filter((part) => part !== null)\n .join(\", \")\n\n/** What the review came back with, as far as the adapter itself gets. */\ninterface Reviewed {\n /** The session the run happened in. */\n readonly sessionId: string\n /** What the run said in prose, or null where a schema left it none to say. */\n readonly prose: string | null\n /**\n * The findings as they weighed, or whatever stopped them weighing: a turn\n * that could not report, and a turn that answered in a shape that does not\n * validate, are the same kind of failure of the same run.\n */\n readonly reported: Result.Result<typeof Reported.Type, { readonly message: string }>\n}\n\n/** What is written down about the review. */\ninterface Ran {\n readonly sessionId: string | null\n /** The prose the report document is written from, or null where there is none. */\n readonly prose: string | null\n readonly outcome: Outcome\n}\n\n/**\n * The review of the head in the worktree.\n *\n * Whatever the reporting comes to is a value and not a failure: the review is\n * already worth keeping, and a turn that could not report is recorded as the\n * failure it is rather than lost with it.\n */\nconst reviewOn = Effect.fn(\"review.reviewOn\")(function* (options: {\n readonly launcher: Launcher\n readonly directory: string\n readonly turn: ReviewTurn\n readonly model: string | null\n}) {\n const { directory, launcher, model, turn } = options\n const run = yield* spinning(saying, (onTool) => reviewTurns({ launcher, directory, turn, model, jsonSchema, onTool }))\n // Both halves answer `message`, which is all `ranBy` reads: a turn that could\n // not report and a turn that answered in a shape that does not validate are\n // the same kind of failure of the same run.\n const answered: Effect.Effect<unknown, { readonly message: string }> = Result.isFailure(run.findings)\n ? Effect.fail(run.findings.failure)\n : Effect.succeed(run.findings.success)\n const reported = yield* Effect.result(\n Effect.flatMap(answered, (output) => Schema.decodeUnknownEffect(Reported)(output))\n )\n return { sessionId: run.sessionId, prose: run.prose, reported } satisfies Reviewed\n})\n\n/**\n * What the review comes to on disk: the findings it reported, or the failure it\n * reached instead.\n *\n * A run that would not start is as much a failure as a turn that answered in a\n * shape that does not validate, and both are recorded: the head has been tried\n * and nothing was found, which is not the same as nothing being wrong.\n */\nconst ranBy = (got: Result.Result<Reviewed, AgentFailed>): Ran => {\n if (Result.isFailure(got)) {\n return { sessionId: null, prose: null, outcome: { _tag: \"failed\", detail: got.failure.detail } }\n }\n const { prose, reported, sessionId } = got.success\n if (Result.isFailure(reported)) {\n return { sessionId, prose, outcome: { _tag: \"failed\", detail: reported.failure.message } }\n }\n const found = reported.success\n return {\n sessionId,\n // A run held to a schema answers in findings and not in prose, so the report\n // kept beside it is written from what it found.\n prose: prose ?? asMarkdown(found),\n outcome: { _tag: \"reported\", verdict: found.verdict, findings: found.findings }\n }\n}\n\n/**\n * The command's own failure where the review reported nothing.\n *\n * The run is written down either way; what the exit code says is whether the\n * review I asked for is one to trust.\n */\nconst unreported = (run: ReviewRun, number: number) => {\n const detail = detailOf(run)\n return detail === null\n ? Effect.void\n : Effect.fail(\n new CliError.UserError({\n cause:\n `The review ran and its findings did not: ${detail}. ` +\n `Run dw-mc review ${number} --force to run it again.`\n })\n )\n}\n\n/**\n * One review run, started by hand, in the foreground.\n *\n * A model runs here and nowhere else in the tool: there is no watch mode and\n * nothing reviews in the background, because a review costs real money and I am\n * the one who decides to spend it.\n *\n * The run happens in a throwaway worktree of the tool's own clone, so what is\n * reviewed is the pull request's head rather than whatever I have open.\n *\n * What it found is kept against that head, which is what takes the pull request\n * out of Needs review run and what a blocking finding later puts into Needs me.\n *\n * The report is printed as well as kept. A run I waited minutes for should not\n * need a second command to read.\n */\nexport const review = Command.make(\n \"review\",\n {\n pr: prArgument,\n command: commandFlag,\n prompt: promptFlag,\n effort: effortFlag,\n model: modelFlag,\n promptOnly: promptOnlyFlag,\n commandOnly: commandOnlyFlag,\n force: forceFlag\n },\n Effect.fn(\"review\")(\n function* ({ command, commandOnly, effort, force, model, pr, prompt, promptOnly }) {\n const file: ConfigFile = Option.getOrElse(yield* readConfig, (): ConfigFile => ({}))\n const { number, repo } = yield* named(pr, Object.keys(file.repos ?? {}).toSorted())\n const settings = settingsFor(file, repo)\n const launcher = launcherOf(file)\n const asked = yield* asking({ settings, command, prompt, effort, model, promptOnly, commandOnly })\n\n const view = yield* prView(repo, number)\n yield* Console.log(`${repo}#${number} ${view.title}`)\n\n const since = force\n ? null\n : skippedSince(yield* askedOf(repo, number, view.headRefOid), settings.review.docs_only)\n if (since !== null) {\n yield* Console.log(\n ` only documentation changed since ${short(since)}, so this run is skipped. ` +\n `Pass --force to review it anyway.`\n )\n return\n }\n\n const about: Reviewing = {\n repo,\n number,\n title: view.title,\n base: view.baseRefName,\n prompt: asked.prompt\n }\n const turn = turnFor(asked, about)\n\n // The bell and the notification are what let me walk away from a run that\n // takes minutes, so they ring however it ended: a run that gave up while I\n // was elsewhere is the one I most need to hear about.\n yield* Effect.gen(function* () {\n const ran = yield* withWorktree(repo, number, (worktree) =>\n Effect.gen(function* () {\n yield* Console.log(` head ${short(worktree.head)} ${spending(turn, asked.model)}`)\n const got = yield* Effect.result(\n reviewOn({ launcher, directory: worktree.directory, turn, model: asked.model })\n )\n return { head: worktree.head, ran: ranBy(got) }\n })\n )\n\n const ranAt = yield* DateTime.now\n const runs = yield* storeFor(\"runs\", ReviewRun)\n const latest = yield* storeFor(\"runs\", LastReviewed)\n const reports = yield* textStoreFor(\"runs\")\n\n const got = ran.ran\n const run: ReviewRun = {\n repo,\n number,\n head: ran.head,\n command: asked.command,\n effort: asked.command === null ? null : asked.effort,\n sessionId: got.sessionId,\n ranAt,\n outcome: got.outcome\n }\n yield* runs.set(runKey(repo, number, run.head), run)\n yield* latest.set(latestKey(repo, number), { head: run.head })\n yield* reports.set(reportKey(repo, number, run.head), reportDocument(run, view.title, got.prose ?? \"\"))\n\n yield* Console.log(\"\")\n const detail = detailOf(run)\n if (detail !== null) {\n yield* Console.log(` reported nothing: ${detail}`)\n } else {\n const found = reportedBy(run)\n if (found !== null) {\n if (got.prose !== null) {\n yield* Console.log(got.prose)\n yield* Console.log(\"\")\n }\n yield* Console.log(summary(found, settings.stamp.blocks_on))\n for (const line of lines(found)) {\n yield* Console.log(` ${line}`)\n }\n }\n }\n\n yield* Console.log(`Recorded against ${short(ran.head)} in ${yield* stateDirectory}`)\n yield* unreported(run, number)\n }).pipe(\n Effect.onExit((exit) =>\n announce(\"dw-mc review\", `${repo}#${number} ${Exit.isSuccess(exit) ? \"reviewed\" : \"could not be reviewed\"}`)\n )\n )\n },\n // No `AgentFailed` here: the run's own failure is caught where it happens\n // and written down as the run's outcome, so it never reaches this far.\n Effect.catchTag([...userFacing, \"GitFailed\"], asUserError)\n )\n).pipe(Command.withDescription(\"Review one pull request on Claude Code, in a throwaway worktree\"))\n","import { Console, Effect, Option } from \"effect\"\nimport { Command, Flag } from \"effect/unstable/cli\"\n\nimport type { ConfigFile } from \"#adapters/config.ts\"\nimport { read as readConfig } from \"#adapters/config.ts\"\nimport { named, prArgument, swept } from \"#cli/pr.ts\"\nimport { asUserError } from \"#cli/sweep.ts\"\nimport { short } from \"#domain/review.ts\"\nimport { stampOf, withdraw } from \"#domain/stamp.ts\"\n\nconst withdrawFlag = Flag.Boolean(\"withdraw\").pipe(\n Flag.withDefault(false),\n Flag.withDescription(\"Take the stamp off this pull request, until its head changes\")\n)\n\n/**\n * The stamp of one pull request, and the one way to take it off by hand.\n *\n * Printing it is the whole command without `--withdraw`: the mark is computed,\n * so what is worth reading is the reason, which is either what it rests on or\n * the first thing that withholds it.\n *\n * `--withdraw` is where I overrule the computation, and it takes the stamp off\n * the head the facts are about rather than whatever GitHub has moved on to\n * since: the stamp I am withdrawing is the one the table showed me, on code I\n * have read, so the withdrawal is pinned to exactly that head. A head that has\n * moved is a stamp the next sweep computes again anyway.\n *\n * Neither path reaches past this machine at all: the stamp is mine, it is\n * computed from what a sweep already wrote down, and nobody else ever sees it\n * (ADR 0001, ADR 0002).\n */\nexport const stampCommand = Command.make(\n \"stamp\",\n { pr: prArgument, withdraw: withdrawFlag },\n Effect.fn(\"stamp\")(\n function* ({ pr, withdraw: byHand }) {\n const file: ConfigFile = Option.getOrElse(yield* readConfig, (): ConfigFile => ({}))\n const { number, repo } = yield* named(pr, Object.keys(file.repos ?? {}).toSorted())\n\n const facts = yield* swept(repo, number)\n const where = `${repo}#${number} ${short(facts.head)}`\n\n if (byHand) {\n yield* withdraw(repo, number, facts.head)\n yield* Console.log(`${where} stamp withdrawn, until the head changes`)\n return\n }\n\n const stamp = yield* stampOf(facts)\n yield* Console.log(`${where} ${stamp.stamped ? \"stamped\" : `not stamped: ${stamp.reason}`}`)\n },\n Effect.catchTag([\"ConfigMalformed\"], asUserError)\n )\n).pipe(Command.withDescription(\"Print my stamp on one pull request, or withdraw it by hand\"))\n","import { Console, Effect } from \"effect\"\nimport { Command } from \"effect/unstable/cli\"\n\nimport { Paint } from \"#adapters/paint.ts\"\nimport { prKey } from \"#adapters/store.ts\"\nimport { cells, heading, rule, titleWidth } from \"#cli/row.ts\"\nimport { asUserError, printTroubles, sweep, userFacing } from \"#cli/sweep.ts\"\nimport { table } from \"#cli/table.ts\"\nimport type { Grouped } from \"#domain/bucket.ts\"\nimport { group } from \"#domain/bucket.ts\"\nimport { stampedAmong } from \"#domain/stamp.ts\"\n\n/**\n * Every tracked PR under the bucket it sits in, in the order I act on them.\n *\n * The rows of every bucket are measured together, so the columns line up down\n * the whole table rather than restarting under each heading, and they are ruled\n * apart: three columns of prose run into one another without a rule, and the\n * middle one is a commit subject that can end in anything.\n */\nconst lines = (grouped: ReadonlyArray<Grouped>, stamped: ReadonlySet<string>, paint: Paint): ReadonlyArray<string> => {\n const rows = table(\n grouped.flatMap((it) =>\n it.placed.map((placed) =>\n cells(placed, stamped.has(prKey(placed.facts.repo, placed.facts.number)), titleWidth, paint, \"marker\")\n )\n ),\n rule\n )\n let taken = 0\n return grouped.flatMap((it, index) => {\n const mine = rows.slice(taken, taken + it.placed.length)\n taken += it.placed.length\n return [...(index === 0 ? [] : [\"\"]), heading[it.bucket], ...mine.map((row) => ` ${row}`)]\n })\n}\n\n/**\n * The table of what every tracked PR waits on.\n *\n * It sweeps first, every time: a table I read is never one I forgot to refresh.\n */\nexport const status = Command.make(\n \"status\",\n {},\n Effect.fn(\"status\")(\n function* () {\n const report = yield* sweep\n\n if (report.repos.length === 0) {\n yield* Console.log(\"No repositories registered. Run dw-mc init inside a repository to register it.\")\n return\n }\n\n const grouped = group(report.facts)\n if (grouped.length === 0) {\n yield* Console.log(\"No open pull requests.\")\n }\n for (const line of lines(grouped, yield* stampedAmong(report.facts), yield* Paint)) {\n yield* Console.log(line)\n }\n yield* printTroubles(report.troubles)\n },\n Effect.catchTag(userFacing, asUserError)\n )\n).pipe(Command.withDescription(\"Show which bucket every tracked pull request sits in, and which ones I have stamped\"))\n","import { Console, Effect, FileSystem, Path } from \"effect\"\nimport { Command, Flag } from \"effect/unstable/cli\"\n\nimport { configDirectory, configPath } from \"#adapters/config.ts\"\nimport { holding } from \"#adapters/git.ts\"\nimport type { Paint } from \"#adapters/paint.ts\"\nimport { Paint as PaintService } from \"#adapters/paint.ts\"\nimport { confirm } from \"#adapters/picker.ts\"\nimport { discard, inventory } from \"#adapters/store.ts\"\nimport { yesFlag } from \"#cli/cleanup.ts\"\nimport { table } from \"#cli/table.ts\"\nimport type { Standing } from \"#domain/cleanup.ts\"\nimport { everything, standing, weight } from \"#domain/cleanup.ts\"\n\nconst configFlag = Flag.Boolean(\"config\").pipe(\n Flag.withDefault(false),\n Flag.withDescription(\"Take the configuration file too, and not only the state\")\n)\n\nconst forceFlag = Flag.Boolean(\"force\").pipe(\n Flag.withDefault(false),\n Flag.withDescription(\"Remove a session worktree that still holds work of mine\")\n)\n\n/** A worktree that still holds something, and what it holds. */\ninterface Held {\n readonly at: Standing\n readonly detail: string\n}\n\nconst block = (heading: string, rows: ReadonlyArray<ReadonlyArray<string>>): ReadonlyArray<string> =>\n rows.length === 0 ? [] : [heading, ...table(rows).map((line) => ` ${line}`), \"\"]\n\nconst removes = (\n state: { readonly directory: string; readonly size: string },\n config: string | undefined,\n paint: Paint\n): ReadonlyArray<string> =>\n block(\"Removes\", [\n [paint.dim(state.directory), state.size, \"every record, report, clone and worktree\"],\n ...(config === undefined ? [] : [[paint.dim(config), \"\", \"the runner and every repository registered\"]])\n ])\n\nconst held = (holds: ReadonlyArray<Held>, paint: Paint): ReadonlyArray<string> =>\n block(\n \"Holds work of mine\",\n holds.map((it) => [paint.dim(it.at.directory), it.detail])\n )\n\n/**\n * Takes the tool's own footprint off this machine, which no package manager\n * does.\n *\n * Removing the package removes the binary and nothing else - verified by\n * running it: `pnpm remove` runs no `uninstall` script of any name, so a tool\n * that writes outside its own directory has to say goodbye itself. This is that\n * goodbye, and the one step it cannot take is printed rather than pretended.\n *\n * The state goes in full, because every byte of it is the tool's own record of\n * what it read. The configuration file is the one thing I decided rather than\n * the tool, and it is small, readable and possibly in my dotfiles, so it stays\n * unless `--config` asks for it.\n *\n * A worktree that a fix or resolve session left standing is asked what it still\n * holds before anything is removed, and one holding uncommitted changes or a\n * commit the pull request's head does not have stops the whole command. That is\n * the one thing here no reflog of mine brings back.\n */\nexport const uninstall = Command.make(\n \"uninstall\",\n { config: configFlag, force: forceFlag, yes: yesFlag },\n Effect.fn(\"uninstall\")(function* ({ config: alsoConfig, force, yes }) {\n const fs = yield* FileSystem.FileSystem\n const path = yield* Path.Path\n const paint = yield* PaintService\n\n const found = yield* inventory\n const file = yield* configPath\n const configured = yield* fs.exists(file)\n\n const holds: ReadonlyArray<Held> = yield* Effect.forEach(standing(found), (at) =>\n Effect.map(holding(at.repo, at.number, at.session), (holding_) =>\n holding_._tag === \"held\" ? [{ at, detail: holding_.detail }] : []\n )\n ).pipe(Effect.map((found_) => found_.flat()))\n\n yield* Effect.forEach(\n removes(\n { directory: found.directory, size: weight(everything(found)) },\n alsoConfig && configured ? file : undefined,\n paint\n ),\n (line) => Console.log(line)\n )\n\n if (holds.length > 0) {\n yield* Effect.forEach(held(holds, paint), (line) => Console.log(line))\n if (!force) {\n yield* Console.log(\"Nothing was removed. Push that work or drop it, or run this again with --force.\")\n return\n }\n }\n\n if (!yes && !(yield* confirm(\"Remove it all?\"))) {\n yield* Console.log(\"Nothing was removed.\")\n return\n }\n\n yield* discard(found.directory)\n if (alsoConfig) {\n yield* discard(yield* configDirectory)\n }\n\n yield* Console.log(`Removed ${found.directory}${alsoConfig ? ` and ${path.dirname(file)}` : \"\"}.`)\n if (!alsoConfig && configured) {\n yield* Console.log(`The configuration file stays at ${file}. Run this again with --config to take it too.`)\n }\n yield* Console.log(\"Run pnpm remove -g dw-mc to take the binary, which is all that is left.\")\n })\n).pipe(Command.withDescription(\"Take everything this tool wrote off the machine\"))\n","import { Command } from \"effect/unstable/cli\"\n\nimport { cleanup } from \"#cli/cleanup.ts\"\nimport { comments } from \"#cli/comments.ts\"\nimport { findings } from \"#cli/findings.ts\"\nimport { fix } from \"#cli/fix.ts\"\nimport { init } from \"#cli/init.ts\"\nimport { merge } from \"#cli/merge.ts\"\nimport { picker } from \"#cli/pick.ts\"\nimport { rebase } from \"#cli/rebase.ts\"\nimport { rerun } from \"#cli/rerun.ts\"\nimport { resolve } from \"#cli/resolve.ts\"\nimport { review } from \"#cli/review.ts\"\nimport { stampCommand } from \"#cli/stamp.ts\"\nimport { status } from \"#cli/status.ts\"\nimport { sweepCommand } from \"#cli/sweep.ts\"\nimport { uninstall } from \"#cli/uninstall.ts\"\n\ndeclare const __VERSION__: string | undefined\n\n/**\n * The version the CLI reports: the build stamps it in from `package.json`.\n *\n * Running from source leaves the constant undeclared rather than undefined, so\n * the check has to be `typeof` and the fallback is what a test reads.\n */\nexport const version: string = typeof __VERSION__ === \"string\" ? __VERSION__ : \"0.0.0\"\n\n/** Where the project lives, printed beside the version in the header. */\nexport const projectUrl = \"github.com/dominikwozniak/dw-mc\"\n\nconst subcommands = [\n init,\n review,\n comments,\n findings,\n fix,\n rebase,\n rerun,\n resolve,\n merge,\n sweepCommand,\n status,\n stampCommand,\n cleanup,\n uninstall\n] as const\n\n/**\n * The same subcommands under a root that opens no picker, which is what the\n * picker dispatches into.\n *\n * It exists so that what the picker runs is the command I would have typed,\n * parsed by the parser that would have parsed it. Dispatching into `dwMc`\n * itself would be the command referring to its own definition, and a picker\n * that reached its own root with no arguments would open a second picker.\n */\nconst dispatcher = Command.make(\"dw-mc\").pipe(Command.withSubcommands(subcommands))\n\nexport const dwMc = Command.make(\"dw-mc\", {}, picker(Command.runWith(dispatcher, { version }))).pipe(\n Command.withDescription(\"Keeps the state of my open pull requests on disk and shows what every PR waits on\"),\n Command.withSubcommands(subcommands)\n)\n","import type { Config, Stdio } from \"effect\"\nimport { Effect, Layer } from \"effect\"\nimport type { HelpDoc } from \"effect/unstable/cli\"\nimport { CliConfig, CliOutput, GlobalFlag } from \"effect/unstable/cli\"\n\nimport { paintFor, screened } from \"#adapters/paint.ts\"\nimport { projectUrl, version } from \"#cli/cli.ts\"\n\n/** The tool's name, drawn. Plain ASCII, so a pipe and a paste show one picture. */\nconst logo = [\n \" _\",\n \" __| |__ __ _ __ ___ ___\",\n \" / _` |\\\\ \\\\ /\\\\ / / ___ | '_ ` _ \\\\ / __|\",\n \"| (_| | \\\\ V V / |___| | | | | | || (__\",\n \" \\\\__,_| \\\\_/\\\\_/ |_| |_| |_| \\\\___|\"\n]\n\n/** What stands beside the logo, row by row: what this is, and where it lives. */\nconst beside = [\"\", `mission control ${version}`, projectUrl, \"\"]\n\nconst gap = \" \"\n\n/** The logo, with the tool's name and home set beside it. */\nconst header = (colors: boolean): string => {\n const width = Math.max(...logo.map((line) => line.length))\n const paint = paintFor(colors)\n return logo\n .map((line, index) => {\n const meta = beside[index] ?? \"\"\n // A row with nothing beside it keeps its own width, so no line of the\n // header ends in padding a terminal would still be colouring.\n return meta === \"\" ? paint.cyan(line) : `${paint.cyan(line.padEnd(width))}${gap}${paint.dim(meta)}`\n })\n .join(\"\\n\")\n}\n\n/**\n * The formatter for the two screens the tool introduces itself on, with the\n * header above what the default formatter draws.\n *\n * The root command is the one whose help document lists subcommands, which is\n * what keeps the header off `dw-mc status --help`.\n */\nconst formatter = (colors: boolean): CliOutput.Formatter => {\n const inner = CliOutput.defaultFormatter({ colors })\n const drawn = header(colors)\n return {\n formatHelpDoc: (doc: HelpDoc.HelpDoc) =>\n doc.subcommands === undefined ? inner.formatHelpDoc(doc) : `${drawn}\\n\\n${inner.formatHelpDoc(doc)}`,\n formatVersion: (name: string, printed: string) => `${drawn}\\n\\n${inner.formatVersion(name, printed)}`,\n formatCliError: inner.formatCliError,\n formatError: inner.formatError,\n formatErrors: inner.formatErrors\n }\n}\n\n/**\n * The header, as the layer the entry point provides for the whole CLI.\n *\n * It replaces the formatter under `--help` and `--version` rather than for the\n * run, because a failed parse prints the help screen through the same\n * formatter: decorating that one would bury the error under a logo.\n */\nexport const layer: Layer.Layer<never, Config.ConfigError, Stdio.Stdio> = Layer.unwrap(\n Effect.map(screened, (colors) => {\n const introducing = Effect.provideService(CliOutput.Formatter, formatter(colors))\n return CliConfig.layer({\n builtIns: CliConfig.defaults.builtIns.map((builtIn) =>\n builtIn === GlobalFlag.Help || builtIn === GlobalFlag.Version\n ? GlobalFlag.Action({ flag: builtIn.flag, run: (value, context) => introducing(builtIn.run(value, context)) })\n : builtIn\n )\n })\n })\n)\n","#!/usr/bin/env node\n// oxlint-disable effecttsgo/strict-effect-provide -- the rule exempts entry points, and this file is the one\nimport { NodeRuntime, NodeServices } from \"@effect/platform-node\"\nimport { Effect, Layer } from \"effect\"\nimport { Command } from \"effect/unstable/cli\"\n\nimport { ConfigStore } from \"#adapters/config.ts\"\nimport * as Paint from \"#adapters/paint.ts\"\nimport * as Store from \"#adapters/store.ts\"\nimport { dwMc, version } from \"#cli/cli.ts\"\nimport * as Header from \"#cli/header.ts\"\n\n// Both stores are built here, for the whole CLI rather than for `init` alone:\n// the filesystem store makes its directory as its layer is built, so any run of\n// dw-mc leaves the state and configuration directories behind it.\ndwMc.pipe(\n Command.run({ version }),\n Effect.provide(\n Layer.provideMerge(Layer.mergeAll(ConfigStore.layer, Store.layer, Header.layer, Paint.layer), NodeServices.layer)\n ),\n NodeRuntime.runMain\n)\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"bin.js","names":["merge","header","tint","layer","layer","asked","lines","PaintService","comments","resolve","resolve","nothing","decide","askedOf","saying","readConfig","FactsSchema","read","where","block","lines","readConfig","PaintService","asJson","FindingsSchema","header","lines","readConfig","asJson","promptFor","printFlag","choicesOf","lines","readConfig","header","promptFor","effortFlag","merge","decide","readConfig","decide","decide","readConfig","readConfig","decide","readConfig","decide","readConfig","forceFlag","readConfig","lines","readConfig","PaintService","Store.layer","Header.layer","Paint.layer"],"sources":["../src/adapters/xdg.ts","../src/adapters/yaml.ts","../src/terms/review.ts","../src/adapters/config.ts","../src/adapters/paint.ts","../src/adapters/store.ts","../src/adapters/heartbeat.ts","../src/adapters/spawner.ts","../src/adapters/git.ts","../src/adapters/picker.ts","../src/cli/table.ts","../src/domain/cleanup.ts","../src/cli/cleanup.ts","../src/adapters/gh.ts","../src/adapters/conversation.ts","../src/domain/moment.ts","../src/terms/pr.ts","../src/domain/bucket.ts","../src/domain/reference.ts","../src/cli/pr.ts","../src/cli/row.ts","../src/adapters/ci.ts","../src/domain/flaky.ts","../src/domain/quiet.ts","../src/domain/rebase.ts","../src/domain/findings.ts","../src/domain/review.ts","../src/cli/sweep.ts","../src/domain/comments.ts","../src/cli/comments.ts","../src/cli/findings.ts","../src/adapters/agent.ts","../src/adapters/claude.ts","../src/domain/fix.ts","../src/cli/fix.ts","../src/cli/init.ts","../src/domain/stamp.ts","../src/domain/merge.ts","../src/cli/merge.ts","../src/domain/pick.ts","../src/domain/rerun.ts","../src/cli/pick.ts","../src/cli/rebase.ts","../src/cli/rerun.ts","../src/domain/resolve.ts","../src/cli/resolve.ts","../src/adapters/notify.ts","../src/domain/persona.ts","../src/cli/review.ts","../src/cli/stamp.ts","../src/cli/status.ts","../src/cli/uninstall.ts","../src/cli/cli.ts","../src/cli/header.ts","../src/cli/bin.ts"],"sourcesContent":["import { Config, Effect, Option, Path } from \"effect\"\n\n/**\n * One of `dw-mc`'s XDG base directories: `$<variable>/dw-mc` where the\n * environment sets `variable`, and `$HOME/<fallback>/dw-mc` where it does not.\n */\nexport const xdgDirectory = Effect.fnUntraced(function* (variable: string, ...fallback: ReadonlyArray<string>) {\n const path = yield* Path.Path\n const configured = yield* Config.String(variable).pipe(Config.option)\n const home = Option.isSome(configured) ? configured.value : path.join(yield* Config.String(\"HOME\"), ...fallback)\n return path.join(home, \"dw-mc\")\n})\n","import { Predicate } from \"effect\"\n\n/** A value this writer can put on paper: what `Yaml.parse` gives back. */\nexport type Value = null | boolean | number | string | ReadonlyArray<Value> | { readonly [key: string]: Value }\n\n/**\n * A word YAML reads as itself. Anything else - a glob, an empty string, a\n * branch with a space - is quoted, and the JSON escapes are a subset of the\n * YAML double-quoted ones, so `JSON.stringify` is the quoting.\n */\nconst word = /^[A-Za-z][\\w./-]*$/\n\n/** Words the YAML 1.2 core schema reads as something other than a string. */\nconst reserved = new Set([\"true\", \"false\", \"null\", \"yes\", \"no\", \"on\", \"off\", \"y\", \"n\"])\n\nconst scalar = (value: null | boolean | number | string): string => {\n if (value === null) {\n return \"null\"\n }\n if (typeof value === \"boolean\") {\n return value ? \"true\" : \"false\"\n }\n if (typeof value === \"number\") {\n if (Number.isNaN(value)) {\n return \".nan\"\n }\n if (!Number.isFinite(value)) {\n return value > 0 ? \".inf\" : \"-.inf\"\n }\n return String(value)\n }\n return word.test(value) && !reserved.has(value.toLowerCase()) ? value : JSON.stringify(value)\n}\n\nconst isMapping = (value: Value): value is { readonly [key: string]: Value } => Predicate.isObject(value)\n\n/** `Array.isArray` widens to `any[]`, which leaves the union unnarrowed. */\nconst isSequence = (value: Value): value is ReadonlyArray<Value> => Array.isArray(value)\n\nconst pad = (depth: number): string => \" \".repeat(depth)\n\n/**\n * Writes one entry, where `prefix` is everything up to the value: a mapping's\n * `key:` or a sequence's `-`. `depth` is where this entry's children go, which\n * a sequence item sets one deeper than the dash it hangs from.\n */\nconst writeEntry = (prefix: string, value: Value, depth: number, out: Array<string>): void => {\n if (isSequence(value)) {\n if (value.length === 0) {\n out.push(`${prefix} []`)\n return\n }\n out.push(prefix)\n writeSequence(value, depth, out)\n return\n }\n if (isMapping(value)) {\n const entries = Object.entries(value)\n if (entries.length === 0) {\n out.push(`${prefix} {}`)\n return\n }\n out.push(prefix)\n writeMapping(entries, depth, out)\n return\n }\n out.push(`${prefix} ${scalar(value)}`)\n}\n\nconst writeMapping = (entries: ReadonlyArray<readonly [string, Value]>, depth: number, out: Array<string>): void => {\n for (const [key, value] of entries) {\n writeEntry(`${pad(depth)}${scalar(key)}:`, value, depth + 1, out)\n }\n}\n\nconst writeSequence = (items: ReadonlyArray<Value>, depth: number, out: Array<string>): void => {\n for (const item of items) {\n const entries = isMapping(item) ? Object.entries(item) : []\n const [first, ...rest] = entries\n if (first === undefined) {\n writeEntry(`${pad(depth)}-`, item, depth + 1, out)\n continue\n }\n writeEntry(`${pad(depth)}- ${scalar(first[0])}:`, first[1], depth + 2, out)\n writeMapping(rest, depth + 1, out)\n }\n}\n\n/**\n * Writes one YAML document, in the order the keys were built in.\n *\n * Effect parses YAML but does not write it, and the configuration file is one\n * the tool rewrites on every `init`. Collections are written as blocks, so the\n * file stays diffable and editable by hand.\n */\nexport const encodeYaml = (value: Value): string => {\n const out: Array<string> = []\n if (isSequence(value)) {\n if (value.length === 0) {\n return \"[]\\n\"\n }\n writeSequence(value, 0, out)\n } else if (isMapping(value)) {\n const entries = Object.entries(value)\n if (entries.length === 0) {\n return \"{}\\n\"\n }\n writeMapping(entries, 0, out)\n } else {\n out.push(scalar(value))\n }\n return `${out.join(\"\\n\")}\\n`\n}\n","/**\n * The words a review run is described in.\n *\n * What a run opens on and how much it spends are facts about Claude Code, and\n * how much a finding weighs is a rule of mine, but all three are read on both\n * sides of the adapter seam: the domain writes the turn and weighs the\n * findings, the adapter spawns the turn and is held to the same words.\n */\nimport { Schema } from \"effect\"\n\n/**\n * How much a review run spends, in the words the slash command takes.\n *\n * The set is Claude Code's and not this tool's, so it is wider than three\n * words: a run that would be worth `max` is one I should be able to ask for\n * without spelling the whole command out.\n */\nexport const Effort = Schema.Literals([\"low\", \"medium\", \"high\", \"xhigh\", \"max\"])\nexport type Effort = typeof Effort.Type\n\n/** How much a finding weighs. */\nexport const Severity = Schema.Literals([\"error\", \"warning\", \"info\"])\nexport type Severity = typeof Severity.Type\n\n/**\n * What one review run opens on: a slash command, or the tool's own prompt.\n *\n * Which of the two it is decides how many turns the run takes, which is a fact\n * about Claude Code; what the turn says is the domain's. Neither owns the\n * shape, so it sits under both.\n */\nexport type ReviewTurn =\n | {\n readonly _tag: \"command\"\n /** The slash command and whatever follows it, as one line. */\n readonly line: string\n /** What else the run is told to look at, on the system prompt beside the command. */\n readonly instructions: string | null\n }\n | { readonly _tag: \"prompt\"; readonly text: string }\n","import type { Config, Types } from \"effect\"\nimport { Context, Effect, FileSystem, Layer, Option, Path, PlatformError, Schema } from \"effect\"\nimport { Yaml } from \"effect/unstable/encoding\"\nimport { KeyValueStore } from \"effect/unstable/persistence\"\n\nimport { xdgDirectory } from \"#adapters/xdg.ts\"\nimport type { Value } from \"#adapters/yaml.ts\"\nimport { encodeYaml } from \"#adapters/yaml.ts\"\nimport { Effort, Severity } from \"#terms/review.ts\"\n\n/**\n * What one section of the file may say. Every key is optional: what the file\n * leaves out is inherited rather than reset, so `defaults` and a repository's\n * overrides are the same shape.\n */\nconst SettingsPatch = Schema.Struct({\n base: Schema.optionalKey(Schema.NullOr(Schema.String)),\n review: Schema.optionalKey(\n Schema.Struct({\n command: Schema.optionalKey(Schema.NullOr(Schema.String)),\n effort: Schema.optionalKey(Schema.NullOr(Effort)),\n prompt: Schema.optionalKey(Schema.NullOr(Schema.String)),\n model: Schema.optionalKey(Schema.NullOr(Schema.String)),\n docs_only: Schema.optionalKey(Schema.Array(Schema.String))\n })\n ),\n ci: Schema.optionalKey(\n Schema.Struct({\n ignore: Schema.optionalKey(Schema.Array(Schema.String)),\n flaky_patterns: Schema.optionalKey(Schema.Array(Schema.String))\n })\n ),\n fix: Schema.optionalKey(\n Schema.Struct({\n commits: Schema.optionalKey(Schema.Boolean)\n })\n ),\n rebase: Schema.optionalKey(\n Schema.Struct({\n enabled: Schema.optionalKey(Schema.Boolean)\n })\n ),\n stamp: Schema.optionalKey(\n Schema.Struct({\n blocks_on: Schema.optionalKey(Severity)\n })\n )\n})\nexport type SettingsPatch = typeof SettingsPatch.Type\n\n/** How this machine starts Claude Code, where it does not start `claude` itself. */\nconst LauncherPatch = Schema.Struct({\n // An argv list and never a shell string: a shell string needs `sh -c` in\n // front of it, and that extra process sits in the terminal's foreground\n // group, where it takes the inherited standard input and the Ctrl-C of a fix\n // session with it.\n command: Schema.optionalKey(\n Schema.Array(Schema.String).pipe(\n Schema.check(Schema.isMinLength(1, { message: \"Expected the launcher command to name a program\" }))\n )\n ),\n fix_args: Schema.optionalKey(Schema.Array(Schema.String))\n})\n\n/** A repository, as `gh` spells it: `owner/name`. */\nexport const Repo = Schema.String.pipe(\n Schema.check(Schema.isPattern(/^[^\\s/]+\\/[^\\s/]+$/, { message: \"Expected a repository as owner/name\" }))\n)\n\n/** The whole configuration file: global defaults and per-repository overrides. */\nexport const ConfigFile = Schema.Struct({\n launcher: Schema.optionalKey(LauncherPatch),\n defaults: Schema.optionalKey(SettingsPatch),\n repos: Schema.optionalKey(Schema.Record(Repo, SettingsPatch))\n})\nexport type ConfigFile = typeof ConfigFile.Type\n\ntype Section<K extends keyof SettingsPatch> = Required<NonNullable<SettingsPatch[K]>>\n\n/** What one repository's settings come to once the file has been resolved. */\nexport interface Settings {\n readonly base: string | null\n readonly review: Section<\"review\">\n readonly ci: Section<\"ci\">\n readonly fix: Section<\"fix\">\n readonly rebase: Section<\"rebase\">\n readonly stamp: Section<\"stamp\">\n}\n\n/** What every setting is worth before the file says anything. */\nexport const builtIn: Settings = {\n base: null,\n review: {\n command: \"/code-review\",\n effort: \"low\",\n prompt: null,\n model: null,\n docs_only: [\"**/*.md\", \"docs/**\"]\n },\n ci: { ignore: [], flaky_patterns: [] },\n fix: { commits: false },\n rebase: { enabled: false },\n stamp: { blocks_on: \"error\" }\n}\n\n/** What this machine spawns Claude Code with, once the file has been read. */\nexport interface Launcher {\n /** The program, then the arguments it takes before mission control's own. */\n readonly command: readonly [string, ...Array<string>]\n /** The flags only a fix session gets, the one run that is no review run. */\n readonly fix_args: ReadonlyArray<string>\n}\n\n/** `claude` itself, which is what a machine that spawns it directly needs. */\nexport const builtInLauncher: Launcher = { command: [\"claude\"], fix_args: [] }\n\n/**\n * The patch's value where it has one, the inherited value otherwise. A key the\n * file spells out counts even when it says `null`, which is how a repository\n * resets a global default.\n */\nconst over = <A>(patch: A | undefined, inherited: A): A => (patch === undefined ? inherited : patch)\n\nconst apply = (settings: Settings, patch: SettingsPatch | undefined): Settings =>\n patch === undefined\n ? settings\n : {\n base: over(patch.base, settings.base),\n review: {\n command: over(patch.review?.command, settings.review.command),\n effort: over(patch.review?.effort, settings.review.effort),\n prompt: over(patch.review?.prompt, settings.review.prompt),\n model: over(patch.review?.model, settings.review.model),\n docs_only: over(patch.review?.docs_only, settings.review.docs_only)\n },\n ci: {\n ignore: over(patch.ci?.ignore, settings.ci.ignore),\n flaky_patterns: over(patch.ci?.flaky_patterns, settings.ci.flaky_patterns)\n },\n fix: { commits: over(patch.fix?.commits, settings.fix.commits) },\n rebase: { enabled: over(patch.rebase?.enabled, settings.rebase.enabled) },\n stamp: { blocks_on: over(patch.stamp?.blocks_on, settings.stamp.blocks_on) }\n }\n\n/**\n * `delta` over `patch`, keeping every key `delta` does not mention.\n *\n * Sections merge key by key rather than being replaced, which is what lets a\n * second `init` change one of a repository's settings and lose none of the rest.\n */\nexport const merge = (patch: SettingsPatch, delta: SettingsPatch): SettingsPatch => {\n const merged: Types.Mutable<SettingsPatch> = { ...patch, ...delta }\n if (patch.review !== undefined && delta.review !== undefined) {\n merged.review = { ...patch.review, ...delta.review }\n }\n if (patch.ci !== undefined && delta.ci !== undefined) {\n merged.ci = { ...patch.ci, ...delta.ci }\n }\n if (patch.fix !== undefined && delta.fix !== undefined) {\n merged.fix = { ...patch.fix, ...delta.fix }\n }\n if (patch.rebase !== undefined && delta.rebase !== undefined) {\n merged.rebase = { ...patch.rebase, ...delta.rebase }\n }\n if (patch.stamp !== undefined && delta.stamp !== undefined) {\n merged.stamp = { ...patch.stamp, ...delta.stamp }\n }\n return merged\n}\n\n/** Whether a patch decides anything at all. */\nconst decidesNothing = (patch: SettingsPatch): boolean => Object.keys(patch).length === 0\n\n/**\n * `file` with `defaults` as its global defaults, and with the section left out\n * where those defaults decide nothing, so an empty `defaults:` is never written.\n */\nexport const withDefaults = (file: ConfigFile, defaults: SettingsPatch): ConfigFile =>\n decidesNothing(defaults) ? file : { ...file, defaults }\n\n/** `file` with `patch` over `repo`'s settings, registering `repo` when it is new. */\nexport const withRepo = (file: ConfigFile, repo: string, patch: SettingsPatch): ConfigFile => ({\n ...file,\n repos: { ...file.repos, [repo]: merge(file.repos?.[repo] ?? {}, patch) }\n})\n\n/**\n * What this machine starts Claude Code with: the file's launcher over `claude`.\n *\n * It is no repository's business. What spawns the agent CLI is a fact of the\n * machine, which is why it sits beside `defaults` rather than inside it.\n */\nexport const launcherOf = (file: ConfigFile): Launcher => {\n const [program = builtInLauncher.command[0], ...prefix] = file.launcher?.command ?? []\n return {\n command: [program, ...prefix],\n fix_args: file.launcher?.fix_args ?? builtInLauncher.fix_args\n }\n}\n\n/** What `repo` is worth: its own overrides over the global defaults. */\nexport const settingsFor = (file: ConfigFile, repo: string): Settings =>\n apply(apply(builtIn, file.defaults), file.repos?.[repo])\n\n/**\n * Where the configuration lives: `$XDG_CONFIG_HOME/dw-mc`, or\n * `$HOME/.config/dw-mc` when XDG says nothing.\n */\nexport const configDirectory: Effect.Effect<string, Config.ConfigError, Path.Path> = xdgDirectory(\n \"XDG_CONFIG_HOME\",\n \".config\"\n)\n\nconst fileName = \"config.yaml\"\n\n/** The one file, in the one place, that I can read, edit and keep in my dotfiles. */\nexport const configPath: Effect.Effect<string, Config.ConfigError, Path.Path> = Effect.gen(function* () {\n const path = yield* Path.Path\n const directory = yield* configDirectory\n return path.join(directory, fileName)\n}).pipe(Effect.withSpan(\"config.configPath\"))\n\nconst service = Effect.gen(function* () {\n const store = yield* KeyValueStore.KeyValueStore\n const path = yield* configPath\n return { path, store }\n})\n\nconst onDisk = Layer.unwrap(Effect.map(configDirectory, (directory) => KeyValueStore.layerFileSystem(directory)))\n\n/**\n * The configuration file, behind the key/value seam.\n *\n * A file store over the configuration directory writes `config.yaml` at exactly\n * the path the design promises, so the seam costs the file nothing. Its store is\n * built fresh, so it is never the one the state directory is using.\n */\nexport class ConfigStore extends Context.Service<\n ConfigStore,\n {\n readonly path: string\n readonly store: KeyValueStore.KeyValueStore\n }\n>()(\"dw-mc/config/ConfigStore\") {\n /** The configuration file on disk. */\n static readonly layer: Layer.Layer<\n ConfigStore,\n Config.ConfigError | PlatformError.PlatformError,\n FileSystem.FileSystem | Path.Path\n > = Layer.effect(ConfigStore, service).pipe(Layer.provide(Layer.fresh(onDisk)))\n\n /** A configuration file that lives only as long as the test that builds it. */\n static readonly layerTest: Layer.Layer<ConfigStore, Config.ConfigError, Path.Path> = Layer.effect(\n ConfigStore,\n service\n ).pipe(Layer.provide(Layer.fresh(KeyValueStore.layerMemory)))\n}\n\n/** A configuration file that is there but is not configuration. */\nexport class ConfigMalformed extends Schema.TaggedError<ConfigMalformed>()(\"ConfigMalformed\", {\n path: Schema.String,\n reason: Schema.String\n}) {\n override get message(): string {\n return (\n `${this.path} is not valid dw-mc configuration: ${this.reason}\\n` +\n `Fix the file, or delete it and run 'dw-mc init' again.`\n )\n }\n}\n\nconst reasonOf = (cause: unknown): string => (cause instanceof Error ? cause.message : String(cause))\n\n/** The keys an earlier version had, read off a file loosely enough to find them. */\nconst LegacySection = Schema.Struct({\n review: Schema.optionalKey(\n Schema.Struct({\n runners: Schema.optionalKey(Schema.Unknown),\n skill: Schema.optionalKey(Schema.Unknown),\n path_instructions: Schema.optionalKey(Schema.Unknown)\n })\n ),\n stamp: Schema.optionalKey(Schema.Struct({ supporting_blocks: Schema.optionalKey(Schema.Unknown) }))\n})\n\nconst Legacy = Schema.Struct({\n launcher: Schema.optionalKey(Schema.Struct({ codex: Schema.optionalKey(Schema.Unknown) })),\n defaults: Schema.optionalKey(LegacySection),\n repos: Schema.optionalKey(Schema.Record(Schema.String, LegacySection))\n})\n\nconst asLegacy = Schema.decodeUnknownOption(Legacy)\n\n/**\n * What a file from an earlier version says, and what to do about each of it.\n *\n * The excess-property error names a key and stops there, which is enough for a\n * key that is simply gone and not enough for one that moved: `review.skill` is\n * `review.prompt` now, and a file quietly stripped of it is a review brief lost.\n * This is here to be deleted once no file has those keys left.\n */\nconst legacyIn = (decided: unknown): string | null => {\n const legacy = asLegacy(decided)\n if (Option.isNone(legacy)) {\n return null\n }\n const sections = [legacy.value.defaults, ...Object.values(legacy.value.repos ?? {})]\n const spelled = (says: (section: typeof LegacySection.Type) => unknown): boolean =>\n sections.some((section) => section !== undefined && says(section) !== undefined)\n\n const said = [\n legacy.value.launcher?.codex === undefined ? null : \"launcher.codex is gone: reviews run on Claude Code alone.\",\n spelled((section) => section.review?.runners)\n ? \"review.runners is gone: a head carries one review run, which review.command configures.\"\n : null,\n spelled((section) => section.review?.skill)\n ? \"review.skill is review.prompt now, unchanged in what it does - move the text across rather than losing it.\"\n : null,\n spelled((section) => section.review?.path_instructions)\n ? \"review.path_instructions is gone: nothing ever read it.\"\n : null,\n spelled((section) => section.stamp?.supporting_blocks)\n ? \"stamp.supporting_blocks is gone: there is no second opinion to let through.\"\n : null\n ].filter((sentence) => sentence !== null)\n\n return said.length === 0 ? null : `it names keys this version does not have.\\n${said.join(\"\\n\")}`\n}\n\n/**\n * The configuration file, or `None` when this machine has none yet.\n *\n * A file that is there and is wrong stops the caller: an unreadable key, a\n * value of the wrong type and a misspelled key all fail here rather than\n * turning into a default that quietly means something else.\n */\nexport const read = Effect.gen(function* () {\n const config = yield* ConfigStore\n const raw = yield* config.store.get(fileName)\n if (raw === undefined) {\n return Option.none<ConfigFile>()\n }\n\n const malformed = (reason: string) => new ConfigMalformed({ path: config.path, reason })\n const parsed = yield* Effect.try({\n try: () => Yaml.parse(raw),\n catch: (cause) => malformed(reasonOf(cause))\n })\n // An empty document parses to null: the file is there and decides nothing.\n const decided: unknown = parsed ?? {}\n\n const legacy = legacyIn(decided)\n if (legacy !== null) {\n return yield* malformed(legacy)\n }\n\n return Option.some(\n yield* Schema.decodeUnknownEffect(ConfigFile)(decided, {\n onExcessProperty: \"error\",\n errors: \"all\"\n }).pipe(Effect.mapError((error) => malformed(error.message)))\n )\n}).pipe(Effect.withSpan(\"config.read\"))\n\nconst mapping = (entries: ReadonlyArray<readonly [string, Value | undefined]>): { readonly [key: string]: Value } => {\n const out: Record<string, Value> = {}\n for (const [key, value] of entries) {\n if (value !== undefined) {\n out[key] = value\n }\n }\n return out\n}\n\nconst settingsDocument = (patch: SettingsPatch): Value =>\n mapping([\n [\"base\", patch.base],\n [\n \"review\",\n patch.review === undefined\n ? undefined\n : mapping([\n [\"command\", patch.review.command],\n [\"effort\", patch.review.effort],\n [\"prompt\", patch.review.prompt],\n [\"model\", patch.review.model],\n [\"docs_only\", patch.review.docs_only]\n ])\n ],\n [\n \"ci\",\n patch.ci === undefined\n ? undefined\n : mapping([\n [\"ignore\", patch.ci.ignore],\n [\"flaky_patterns\", patch.ci.flaky_patterns]\n ])\n ],\n [\"fix\", patch.fix === undefined ? undefined : mapping([[\"commits\", patch.fix.commits]])],\n [\"rebase\", patch.rebase === undefined ? undefined : mapping([[\"enabled\", patch.rebase.enabled]])],\n [\"stamp\", patch.stamp === undefined ? undefined : mapping([[\"blocks_on\", patch.stamp.blocks_on]])]\n ])\n\n/**\n * The file as a YAML document, in the order of the schema.\n *\n * Writing the keys in a fixed order rather than the order they were built in\n * keeps the file stable across runs, so a rewrite shows only what changed.\n */\nconst fileDocument = (file: ConfigFile): Value =>\n mapping([\n [\n \"launcher\",\n file.launcher === undefined\n ? undefined\n : mapping([\n [\"command\", file.launcher.command],\n [\"fix_args\", file.launcher.fix_args]\n ])\n ],\n [\"defaults\", file.defaults === undefined ? undefined : settingsDocument(file.defaults)],\n [\n \"repos\",\n file.repos === undefined\n ? undefined\n : mapping(Object.entries(file.repos).map(([name, patch]) => [name, settingsDocument(patch)] as const))\n ]\n ])\n\nconst header = \"# dw-mc configuration. 'dw-mc init' rewrites this file and keeps no comments.\"\n\n/**\n * The file as it would be written.\n *\n * Exposed so a caller can tell whether writing would decide anything\n * differently, and leave the file alone when it would not.\n */\nexport const encode = (file: ConfigFile): string => `${header}\\n${encodeYaml(fileDocument(file))}`\n\n/** Writes the whole file, replacing what was there. */\nexport const write = Effect.fn(\"config.write\")(function* (file: ConfigFile) {\n const config = yield* ConfigStore\n yield* config.store.set(fileName, encode(file))\n})\n","import { Config, Context, Effect, Layer, Option, Stdio } from \"effect\"\n\n/**\n * The ink the screen is written in.\n *\n * Eight colours and two weights, which is what every terminal has had since\n * before any of them had a theme. Asking for one of the eight rather than for a\n * shade means the screen is drawn in my terminal's own palette, so it keeps its\n * contrast whatever I set that palette to.\n *\n * A link is ink as well, and the one piece of it that is not a colour: it says\n * where a word leads rather than what it is worth, so it withholds nothing from\n * the marker and takes no colour of its own.\n *\n * What each colour is worth is not decided here. This is the ink; which word\n * takes which colour belongs to whatever is doing the writing.\n */\nexport interface Paint {\n readonly red: (text: string) => string\n readonly yellow: (text: string) => string\n readonly green: (text: string) => string\n readonly cyan: (text: string) => string\n readonly bold: (text: string) => string\n readonly dim: (text: string) => string\n /** `text`, carrying `url` for the terminal to open. */\n readonly link: (text: string, url: string) => string\n}\n\nconst same = (text: string): string => text\n\n/** The same screen, written where nothing is watching in colour. */\nexport const plain: Paint = { red: same, yellow: same, green: same, cyan: same, bold: same, dim: same, link: same }\n\nconst tint =\n (code: string) =>\n (text: string): string =>\n `\u001b[${code}m${text}\u001b[0m`\n\n/** What a colour costs a line: the escape that opens it and the one that closes it. */\nexport const ink = 9\n\n/**\n * A word a terminal opens: OSC 8, which wraps the text in the URL rather than\n * printing it.\n *\n * The text on the screen is unchanged, so a row reads the same where the\n * terminal knows the sequence and where it does not, and a pipe never sees it\n * at all - the ink below is chosen once, from whether a terminal is watching.\n */\nconst opens = (text: string, url: string): string => `\\x1b]8;;${url}\\x1b\\\\${text}\\x1b]8;;\\x1b\\\\`\n\n/** The screen written in colour. */\nexport const coloured: Paint = {\n red: tint(\"31\"),\n yellow: tint(\"33\"),\n green: tint(\"32\"),\n cyan: tint(\"36\"),\n bold: tint(\"1\"),\n dim: tint(\"2\"),\n link: opens\n}\n\n/** The ink for a screen that may or may not be watched. */\nexport const paintFor = (colors: boolean): Paint => (colors ? coloured : plain)\n\n/**\n * Whether the screen may be coloured: a terminal is watching and `NO_COLOR` is\n * unset.\n *\n * It is asked of the services rather than of `process`, so a test can put\n * either answer in, and an empty `NO_COLOR` reads as unset on both sides - the\n * configuration provider drops it, and `CliOutput.defaultFormatter` takes it\n * for the falsy value it is.\n */\nexport const screened: Effect.Effect<boolean, Config.ConfigError, Stdio.Stdio> = Effect.gen(function* () {\n const stdio = yield* Stdio.Stdio\n const noColor = yield* Config.String(\"NO_COLOR\").pipe(Config.option)\n return (yield* stdio.stdoutIsTerminal) && Option.isNone(noColor)\n})\n\n/**\n * The ink every command writes with.\n *\n * It defaults to no colour, so anything that provides nothing - a test, a\n * command reached some way I have not thought of - prints the text and only the\n * text. Colour arrives when the entry point builds the layer below, which is\n * the one place that knows what stdout is.\n */\nexport const Paint: Context.Reference<Paint> = Context.Reference(\"dw-mc/Paint\", { defaultValue: (): Paint => plain })\n\n/** The ink the machine deserves, as the layer the entry point provides. */\nexport const layer: Layer.Layer<never, Config.ConfigError, Stdio.Stdio> = Layer.effect(\n Paint,\n Effect.map(screened, paintFor)\n)\n","import type { Config } from \"effect\"\nimport { ByteSize, Effect, FileSystem, Layer, Path, Schema } from \"effect\"\nimport { KeyValueStore } from \"effect/unstable/persistence\"\n\nimport { xdgDirectory } from \"#adapters/xdg.ts\"\n\n/**\n * Where the tool keeps its state: `$XDG_STATE_HOME/dw-mc`, or\n * `$HOME/.local/state/dw-mc` when XDG says nothing.\n */\nexport const stateDirectory: Effect.Effect<string, Config.ConfigError, Path.Path> = xdgDirectory(\n \"XDG_STATE_HOME\",\n \".local\",\n \"state\"\n)\n\n/**\n * How the state directory names one pull request, whichever namespace it is in.\n *\n * The facts a sweep wrote and the stamp I withdrew are the same pull request\n * under two namespaces, so the key format is spelled once here rather than in\n * each of them.\n */\nexport const prKey = (repo: string, number: number): string => `${repo}#${number}`\n\n/**\n * A schema-typed view of the store, with every key under `namespace`.\n *\n * Tracked PRs, review runs and stamps share one directory, so the namespace is\n * what keeps them apart. Note that `clear`, `size` and `isEmpty` are not\n * namespaced - they still see the whole store.\n */\nexport const storeFor = Effect.fn(\"store.storeFor\")(function* <S extends Schema.Constraint>(\n namespace: string,\n schema: S\n) {\n const store = yield* KeyValueStore.KeyValueStore\n return KeyValueStore.toSchemaStore(KeyValueStore.prefix(store, `${namespace}/`), schema)\n})\n\n/**\n * The same namespace, kept as text rather than as JSON.\n *\n * A review run's report is Markdown, and the state directory is meant to hold\n * what I can open: through a schema store the same report would be one long\n * JSON string with its newlines escaped.\n */\nexport const textStoreFor = Effect.fn(\"store.textStoreFor\")(function* (namespace: string) {\n const store = yield* KeyValueStore.KeyValueStore\n return KeyValueStore.prefix(store, `${namespace}/`)\n})\n\n/** The state directory on disk. */\nexport const layer = Layer.unwrap(Effect.map(stateDirectory, (directory) => KeyValueStore.layerFileSystem(directory)))\n\n/** A store that lives only as long as the test that builds it. */\nexport const layerTest: Layer.Layer<KeyValueStore.KeyValueStore> = KeyValueStore.layerMemory\n\n/** The three directories the tool cuts a checkout into, under the state directory. */\nexport const cuts = [\"worktrees\", \"fixes\", \"rebases\"] as const\n\n/** Which of them one checkout sits in, which is also what it was cut for. */\nexport type Cut = (typeof cuts)[number]\n\n/** What a standing worktree is for, which names its branch and the directory it is cut in. */\nexport type Session = \"fix\" | \"rebase\"\n\n/** Where each kind of session's worktrees live under the state directory. */\nexport const under = { fix: \"fixes\", rebase: \"rebases\" } as const\n\n/**\n * Which session a checkout belongs to, and nothing where it belongs to none.\n *\n * `worktrees` is the review run's own, cut and taken down inside one run, so it\n * stands for no session at all: that is the difference every command that\n * removes something turns on.\n */\nexport const sessionOf = (cut: Cut): Session | undefined =>\n (({ fixes: \"fix\", rebases: \"rebase\", worktrees: undefined }) as const)[cut]\n\n/** Where the bare clones sit, under the state directory. */\nexport const clonesIn = \"repos\"\n\n/** A directory under the state directory, and what everything below it weighs. */\nexport interface Weighed {\n readonly directory: string\n readonly size: ByteSize.ByteSize\n}\n\n/** One repository's bare clone. */\nexport interface Clone extends Weighed {\n readonly repo: string\n}\n\n/** One checkout the tool cut, named by the pull request it stands on. */\nexport interface Cutting extends Weighed {\n readonly cut: Cut\n readonly repo: string\n readonly number: number\n}\n\n/**\n * Everything the state directory holds, read as directories rather than as\n * keys.\n *\n * The key/value seam is the wrong window for this: a store answers about the\n * keys of one namespace, and what a cleanup is about is the clones and the\n * checkouts, which no namespace ever sees. So this reads the directory itself,\n * and `records` is the one line it has to say about the keys - their number and\n * their weight together, because which pull request a key belongs to is #58's\n * question and not this one's.\n */\nexport interface Inventory {\n readonly directory: string\n readonly clones: ReadonlyArray<Clone>\n readonly cuttings: ReadonlyArray<Cutting>\n readonly records: { readonly keys: number; readonly size: ByteSize.ByteSize }\n}\n\n/** What a directory holds, or nothing at all where it is not there. */\nconst entriesOf = Effect.fnUntraced(function* (directory: string) {\n const fs = yield* FileSystem.FileSystem\n return yield* Effect.orElseSucceed(fs.readDirectory(directory), (): ReadonlyArray<string> => [])\n})\n\n/**\n * What `directory` and everything below it weighs.\n *\n * A file that is gone by the time it is asked about weighs nothing rather than\n * failing the walk: the directory is being read while the tool may be writing\n * to it, and a size on a screen is worth less than the listing it sits in.\n */\nexport const weigh = Effect.fn(\"store.weigh\")(function* (directory: string) {\n const fs = yield* FileSystem.FileSystem\n const path = yield* Path.Path\n const entries = yield* Effect.orElseSucceed(\n fs.readDirectory(directory, { recursive: true }),\n (): ReadonlyArray<string> => []\n )\n const sizes = yield* Effect.forEach(\n entries,\n (entry) =>\n Effect.orElseSucceed(\n Effect.map(fs.stat(path.join(directory, entry)), (info) => ByteSize.toBigInt(info.size)),\n () => BigInt(0)\n ),\n { concurrency: 16 }\n )\n return ByteSize.bytes(sizes.reduce((total, size) => total + size, BigInt(0)))\n})\n\n/** The bare clones, named by the `owner/repo` the two directory levels spell. */\nconst clonesOf = Effect.fnUntraced(function* (state: string) {\n const path = yield* Path.Path\n const root = path.join(state, clonesIn)\n const clones: Array<Clone> = []\n\n for (const owner of yield* entriesOf(root)) {\n for (const name of yield* entriesOf(path.join(root, owner))) {\n if (!name.endsWith(\".git\")) {\n continue\n }\n const directory = path.join(root, owner, name)\n clones.push({ repo: `${owner}/${name.slice(0, -\".git\".length)}`, directory, size: yield* weigh(directory) })\n }\n }\n return clones\n})\n\n/** The checkouts, named by the `owner/repo/number` the three directory levels spell. */\nconst cuttingsOf = Effect.fnUntraced(function* (state: string) {\n const path = yield* Path.Path\n const cuttings: Array<Cutting> = []\n\n for (const cut of cuts) {\n for (const owner of yield* entriesOf(path.join(state, cut))) {\n for (const name of yield* entriesOf(path.join(state, cut, owner))) {\n for (const number of yield* entriesOf(path.join(state, cut, owner, name))) {\n if (!/^\\d+$/.test(number)) {\n continue\n }\n const directory = path.join(state, cut, owner, name, number)\n cuttings.push({\n cut,\n repo: `${owner}/${name}`,\n number: Number(number),\n directory,\n size: yield* weigh(directory)\n })\n }\n }\n }\n }\n return cuttings\n})\n\n/** Everything the state directory holds, in one pass over the disk. */\nexport const inventory: Effect.Effect<Inventory, Config.ConfigError, FileSystem.FileSystem | Path.Path> = Effect.gen(\n function* () {\n const fs = yield* FileSystem.FileSystem\n const path = yield* Path.Path\n const directory = yield* stateDirectory\n\n const directories = new Set<string>([clonesIn, ...cuts])\n const top = yield* entriesOf(directory)\n const keys = top.filter((entry) => !directories.has(entry))\n const sizes = yield* Effect.forEach(\n keys,\n (entry) =>\n Effect.orElseSucceed(\n Effect.map(fs.stat(path.join(directory, entry)), (info) => ByteSize.toBigInt(info.size)),\n () => BigInt(0)\n ),\n { concurrency: 16 }\n )\n\n return {\n directory,\n clones: yield* clonesOf(directory),\n cuttings: yield* cuttingsOf(directory),\n records: { keys: keys.length, size: ByteSize.bytes(sizes.reduce((a, b) => a + b, BigInt(0))) }\n }\n }\n).pipe(Effect.withSpan(\"store.inventory\"))\n\n/**\n * Takes a directory and everything below it off the disk.\n *\n * A path that is not there is the ordinary case rather than a failure: two\n * commands may ask for the same thing gone, and the second one is right about\n * the outcome.\n */\nexport const discard = Effect.fn(\"store.discard\")(function* (directory: string) {\n const fs = yield* FileSystem.FileSystem\n yield* fs.remove(directory, { recursive: true, force: true })\n})\n\n/**\n * Removes what discarding left empty above `directory`, and stops at `upTo`.\n *\n * The layout spells an owner and a repository as directories, so taking one\n * clone away leaves the owner's directory standing with nothing in it. It is a\n * few bytes, and it is also a listing that says the tool still keeps something\n * there when it does not.\n *\n * A directory with anything left in it ends the walk rather than being emptied:\n * what is beside the thing removed belongs to something else.\n */\nexport const tidy = Effect.fn(\"store.tidy\")(function* (directory: string, upTo: string) {\n const fs = yield* FileSystem.FileSystem\n const path = yield* Path.Path\n\n let at = path.dirname(directory)\n while (at !== upTo && at.startsWith(upTo)) {\n const entries = yield* Effect.orElseSucceed(fs.readDirectory(at), (): ReadonlyArray<string> => [\"stop\"])\n if (entries.length > 0) {\n return\n }\n // Recursive over a directory the line above found empty, because that is\n // what removing a directory at all takes; it can still take nothing away.\n yield* Effect.ignore(fs.remove(at, { recursive: true }))\n at = path.dirname(at)\n }\n})\n","import { Clock, Console, Duration, Effect, Fiber, Terminal } from \"effect\"\n\n/** The frames of the spinner, in the order they turn. */\nconst frames = [\"⠋\", \"⠙\", \"⠹\", \"⠸\", \"⠼\", \"⠴\", \"⠦\", \"⠧\", \"⠇\", \"⠏\"]\n\n/** How long one frame is on the screen. */\nconst frameFor = Duration.millis(120)\n\n/** A stretch of time as a terminal says it: `1m12s`, or `9s` under the minute. */\nconst elapsed = (millis: number): string => {\n const seconds = Math.floor(millis / 1000)\n return seconds < 60 ? `${seconds}s` : `${Math.floor(seconds / 60)}m${String(seconds % 60).padStart(2, \"0\")}s`\n}\n\n/**\n * How the line reads, given how long the work has taken so far.\n *\n * The clock is the heartbeat's, because only the heartbeat knows when the work\n * started. Where in the line it goes is the command's, because only the command\n * knows what the rest of the line says.\n */\nexport type Reads = (since: string) => string\n\n/**\n * How a command says the line reads from now on.\n *\n * `aside` is what goes out on its own line where there is no screen to rewrite.\n * A command with nothing worth a line there leaves it out, and that screen stays\n * as empty as it is today.\n */\nexport type Says = (reads: Reads, aside?: string) => Effect.Effect<void>\n\n/**\n * Runs `use` while one line says the work is still going, and hands `use` the\n * way to say how that line reads.\n *\n * Work that takes seconds and prints nothing while it does is work I stop\n * trusting. What the screen showed instead was either silence or a line per\n * step, and a wall of `· Bash` says as little as silence did. This keeps one\n * line and rewrites it: the spinner says the work is alive, the words say how\n * far it has got, and the line is gone when the work is over, so what stays on\n * the screen is the report.\n *\n * What the line counts is the command's and not this module's business. A\n * review counts tools, a sweep counts pull requests, and a command reading two\n * guards counts nothing at all - and a screen that worded any of them here\n * would need the words a command already has.\n *\n * Where there is no screen to measure - a pipe, a CI log, a test - a rewritten\n * line would be a mess of half-drawn ones, so nothing is drawn. What goes out\n * instead is whatever `aside` the command gives, one to a line, and where it\n * gives none the output is what it was before there was a heartbeat at all.\n * `columns` is zero exactly there.\n */\nexport const beating = Effect.fnUntraced(function* <A, E, R>(from: Reads, use: (says: Says) => Effect.Effect<A, E, R>) {\n const terminal = yield* Terminal.Terminal\n const columns = yield* terminal.columns\n if (columns === 0) {\n return yield* use((_, aside) => (aside === undefined ? Effect.void : Console.log(aside)))\n }\n\n const started = yield* Clock.currentTimeMillis\n const draw = (text: string) => Effect.ignore(terminal.display(`\\r${text.slice(0, columns - 1).padEnd(columns - 1)}`))\n\n let reads = from\n let at = 0\n\n /** The line as it stands: this frame of the spinner, and the latest wording. */\n const paint = Effect.flatMap(Clock.currentTimeMillis, (now) =>\n draw(`${frames[at % frames.length]} ${reads(elapsed(now - started))}`)\n )\n\n // The wording is painted the moment it changes rather than at the next frame.\n // Work that gets further every few milliseconds would otherwise show a count\n // up to a frame out of date, which is a line saying something untrue.\n const says: Says = (next) =>\n Effect.andThen(\n Effect.sync(() => void (reads = next)),\n paint\n )\n\n // The first frame is painted here rather than in the fiber, so the line is on\n // the screen the moment the work starts rather than one frame into it.\n yield* paint\n const beat = yield* Effect.forkChild(\n Effect.gen(function* () {\n for (;;) {\n yield* Effect.sleep(frameFor)\n at = at + 1\n yield* paint\n }\n })\n )\n\n return yield* Effect.onExit(use(says), () =>\n Effect.flatMap(Fiber.interrupt(beat), () => Effect.ignore(terminal.display(`\\r${\" \".repeat(columns - 1)}\\r`)))\n )\n})\n","import { Effect, Layer, Schema, Sink, Stream } from \"effect\"\nimport { ChildProcess, ChildProcessSpawner } from \"effect/unstable/process\"\n\nconst encoder = new TextEncoder()\n\n/** A program that ran but ended badly. */\nexport class CommandFailed extends Schema.TaggedError<CommandFailed>()(\"CommandFailed\", {\n command: Schema.String,\n args: Schema.Array(Schema.String),\n exitCode: Schema.Int,\n stderr: Schema.String\n}) {\n override get message(): string {\n return `${[this.command, ...this.args].join(\" \")} exited ${this.exitCode}: ${this.stderr}`\n }\n}\n\n/**\n * Runs a program to completion and returns its trimmed standard output.\n *\n * The spawner's own `string` collects stdout without ever reading the exit\n * code, so a program that failed would come back as an empty success. This\n * reads both, and a non-zero exit is a failure carrying whatever the program\n * said on stderr. The two output streams drain together, because draining one\n * to the end first can block a program that is still writing to the other.\n */\nexport const capture = Effect.fn(\"spawner.capture\")(function* (command: string, args: ReadonlyArray<string>) {\n const spawner = yield* ChildProcessSpawner.ChildProcessSpawner\n const handle = yield* spawner.spawn(ChildProcess.make(command, args))\n\n const [stdout, stderr] = yield* Effect.all(\n [Stream.mkString(Stream.decodeText(handle.stdout)), Stream.mkString(Stream.decodeText(handle.stderr))],\n { concurrency: 2 }\n )\n const exitCode = yield* handle.exitCode\n\n if (exitCode !== 0) {\n return yield* new CommandFailed({ command, args, exitCode, stderr: stderr.trim() })\n }\n return stdout.trim()\n}, Effect.scoped)\n\n/**\n * A `ChildProcessSpawner` built from a fake spawn function, for tests.\n *\n * `ChildProcessSpawner.make` derives `string`, `lines`, `exitCode` and the\n * streams from the spawn function alone, which is how the Node spawner is built\n * too, so one fake spawn gives the whole service.\n */\nexport const layerFake = (\n spawn: ChildProcessSpawner.ChildProcessSpawner[\"Service\"][\"spawn\"]\n): Layer.Layer<ChildProcessSpawner.ChildProcessSpawner> =>\n Layer.succeed(ChildProcessSpawner.ChildProcessSpawner, ChildProcessSpawner.make(spawn))\n\n/**\n * A finished process for a fake spawn function to return.\n *\n * `ChildProcessHandle` carries a private brand, so an object literal cannot\n * stand in for one.\n */\nexport const fakeHandle = (options: {\n readonly stdout?: string | undefined\n readonly stderr?: string | undefined\n readonly exitCode?: number | undefined\n readonly pid?: number | undefined\n}): ChildProcessSpawner.ChildProcessHandle => {\n const stdout = Stream.succeed(encoder.encode(options.stdout ?? \"\"))\n const stderr = Stream.succeed(encoder.encode(options.stderr ?? \"\"))\n return ChildProcessSpawner.makeHandle({\n pid: ChildProcessSpawner.ProcessId(options.pid ?? 1),\n exitCode: Effect.succeed(ChildProcessSpawner.ExitCode(options.exitCode ?? 0)),\n isRunning: Effect.succeed(false),\n kill: () => Effect.void,\n stdin: Sink.drain,\n stdout,\n stderr,\n all: Stream.merge(stdout, stderr),\n getInputFd: () => Sink.drain,\n getOutputFd: () => Stream.empty,\n unref: Effect.succeed(Effect.void)\n })\n}\n","import { Effect, Path, Result, Schema } from \"effect\"\n\nimport type { Reads } from \"#adapters/heartbeat.ts\"\nimport { beating } from \"#adapters/heartbeat.ts\"\nimport { capture } from \"#adapters/spawner.ts\"\nimport type { Cut, Session } from \"#adapters/store.ts\"\nimport { clonesIn, stateDirectory, under } from \"#adapters/store.ts\"\n\n/** A `git` command that ran and refused, or would not run at all. */\nexport class GitFailed extends Schema.TaggedError<GitFailed>()(\"GitFailed\", {\n args: Schema.Array(Schema.String),\n detail: Schema.String\n}) {\n override get message(): string {\n return `git ${this.args.join(\" \")} failed: ${this.detail}`\n }\n}\n\n/** One `git` command, with both ways it can go wrong in our words. */\nconst git = (args: ReadonlyArray<string>) =>\n capture(\"git\", args).pipe(\n Effect.catchTags({\n PlatformError: (error) => Effect.fail(new GitFailed({ args, detail: error.message })),\n CommandFailed: (error) => Effect.fail(new GitFailed({ args, detail: error.stderr }))\n })\n )\n\n/** A checkout cut for one run, and the commit it stands on. */\nexport interface Worktree {\n readonly directory: string\n readonly head: string\n}\n\n/** A fix worktree that still holds work of mine, which nothing may cut away. */\nexport class WorktreeHeld extends Schema.TaggedError<WorktreeHeld>()(\"WorktreeHeld\", {\n directory: Schema.String,\n detail: Schema.String\n}) {\n override get message(): string {\n return `${this.detail}\\nThe fix worktree's directory is ${this.directory}.`\n }\n}\n\n/**\n * Where a worktree is cut from, what it is cut at, and where it goes: the tool's\n * own bare clone of `repo`, the pull request's head, and a directory under\n * `cut` in the state directory.\n *\n * Everything happens in this clone and never in my checkout: a run that reached\n * into the directory I am working in would read whatever I had half finished\n * there.\n *\n * The clone is made once and fetched on every run after that. The fetch brings\n * the branch heads with the pull request's own, because a bare clone is made\n * with no refspec at all: without them the base branch stays at whatever it was\n * the day the clone was made, and a review that diffs against it would report\n * every commit since as the pull request's.\n *\n * The head comes from the pull request's ref rather than from what a sweep last\n * saw, so what is cut is the commit the run really reads.\n */\n/**\n * How the heartbeat of a cut reads.\n *\n * The stages are named apart because a first clone and a hundredth fetch take\n * wildly different times, and the line is what explains the difference: a\n * `cloning` that sits there for two minutes is a large repository arriving\n * once, not a tool that has hung.\n */\nconst cutting =\n (what: string, repo: string): Reads =>\n (since) =>\n `${what} ${repo} · ${since}`\n\nconst whereToCut = Effect.fn(\"git.whereToCut\")(function* (repo: string, number: number, cut: Cut) {\n const path = yield* Path.Path\n const state = yield* stateDirectory\n const clone = path.join(state, clonesIn, `${repo}.git`)\n\n const bare = yield* Effect.orElseSucceed(git([\"-C\", clone, \"rev-parse\", \"--is-bare-repository\"]), () => \"\")\n const pullRef = `refs/dw-mc/pr/${number}`\n\n const head = yield* beating(cutting(bare === \"true\" ? \"fetching\" : \"cloning\", repo), (says) =>\n Effect.gen(function* () {\n if (bare !== \"true\") {\n yield* git([\"clone\", \"--bare\", \"--filter=blob:none\", `https://github.com/${repo}.git`, clone])\n yield* says(cutting(\"fetching\", repo))\n }\n yield* git([\n \"-C\",\n clone,\n \"fetch\",\n \"--no-tags\",\n \"--force\",\n \"origin\",\n `+refs/pull/${number}/head:${pullRef}`,\n \"+refs/heads/*:refs/heads/*\"\n ])\n return yield* git([\"-C\", clone, \"rev-parse\", pullRef])\n })\n )\n return { clone, head, directory: path.join(state, cut, repo, String(number)) }\n})\n\n/**\n * Runs `use` in a throwaway worktree at the head of `number`, and takes the\n * worktree down afterwards however the run ended.\n *\n * A worktree left behind would grow the state directory by a copy of the\n * repository per run, and nothing in a review run is worth keeping: what the\n * run found is recorded, and the checkout it read it in is not.\n *\n * The worktree is removed before it is cut as well as after, because the run\n * before this one may have been killed rather than ended.\n */\nexport const withWorktree = Effect.fn(\"git.withWorktree\")(function* <A, E, R>(\n repo: string,\n number: number,\n use: (worktree: Worktree) => Effect.Effect<A, E, R>\n) {\n const { clone, directory, head } = yield* whereToCut(repo, number, \"worktrees\")\n\n // A worktree that is not there cannot be removed, and that is the ordinary\n // case rather than a problem: both ends of the run ask for the same thing.\n const remove = Effect.ignore(git([\"-C\", clone, \"worktree\", \"remove\", \"--force\", directory]))\n\n return yield* Effect.acquireUseRelease(\n beating(cutting(\"cutting a worktree of\", repo), () =>\n Effect.flatMap(remove, () => git([\"-C\", clone, \"worktree\", \"add\", \"--detach\", directory, head]))\n ),\n () => use({ directory, head }),\n () => remove\n )\n})\n\n/** The worktrees the clone knows it has, by directory. */\nconst worktreesOf = Effect.fn(\"git.worktreesOf\")(function* (clone: string) {\n const listed = yield* git([\"-C\", clone, \"worktree\", \"list\", \"--porcelain\"])\n return listed.split(\"\\n\").flatMap((line) => (line.startsWith(\"worktree \") ? [line.slice(\"worktree \".length)] : []))\n})\n\n/**\n * How far the branch a fix session works on has gone past `head`.\n *\n * Asked of the branch and never of the worktree that stands on it: a worktree\n * can be pruned or moved away by hand, and the branch it left behind still\n * holds the commits. A branch that is not there yet is nothing to hold.\n */\nconst aheadOf = Effect.fn(\"git.aheadOf\")(function* (clone: string, branch: string, head: string) {\n const ref = `refs/heads/${branch}`\n const found = yield* Effect.orElseSucceed(git([\"-C\", clone, \"rev-parse\", \"--verify\", \"--quiet\", ref]), () => \"\")\n if (found === \"\") {\n return 0\n }\n const counted = yield* git([\"-C\", clone, \"rev-list\", \"--count\", ref, `^${head}`])\n return Number(counted.trim())\n})\n\n/**\n * The clone, ready to keep a setting per worktree rather than for all of them.\n *\n * Verified by running it: turning `extensions.worktreeConfig` on in a bare\n * repository makes its linked worktrees read `core.bare` too, and every one of\n * them then refuses to work as a checkout. Git's own answer is to move\n * `core.bare` into the main worktree's config, which is what these three lines\n * do. `--unset` on a key already moved is not a failure, it is the second run.\n */\nconst perWorktreeConfig = Effect.fn(\"git.perWorktreeConfig\")(function* (clone: string) {\n yield* git([\"-C\", clone, \"config\", \"extensions.worktreeConfig\", \"true\"])\n yield* git([\"-C\", clone, \"config\", \"--worktree\", \"core.bare\", \"true\"])\n yield* Effect.ignore(git([\"-C\", clone, \"config\", \"--unset\", \"core.bare\"]))\n})\n\n/**\n * Turns the clone's reuse of a resolution on, which is what the session on a\n * conflict is worth beyond the one conflict.\n *\n * The recording lives in the clone rather than in the worktree, so a conflict I\n * resolve here is one `git` replays by itself the next time a throwaway rebase\n * hits it, with no model involved at all. `autoUpdate` is what makes that a\n * replay rather than a reminder: without it the resolution is written into the\n * worktree and left unstaged, and the rebase stops on a file that is already\n * resolved.\n */\nconst reuseResolutions = Effect.fn(\"git.reuseResolutions\")(function* (clone: string) {\n yield* git([\"-C\", clone, \"config\", \"rerere.enabled\", \"true\"])\n yield* git([\"-C\", clone, \"config\", \"rerere.autoUpdate\", \"true\"])\n})\n\n/**\n * A worktree for a session I steer, on a branch of the tool's own, and left\n * standing when the session ends.\n *\n * It outlives the session because the work in it is mine: I commit and push\n * from inside the session, and a worktree taken down at the end would take an\n * unpushed commit with it.\n *\n * The branch is `dw-mc/<session>/<number>` and never the pull request's own,\n * which is verified rather than a preference: `git` refuses to fetch into a\n * branch that a worktree has checked out, so a worktree standing on the pull\n * request's branch would fail the next fetch of this clone and take every\n * command that reads it down with it. It carries the session's name because a\n * fix session and a session on a conflict stand at the same time on the same\n * pull request, and one branch between them would be one holding the other's\n * commits. The branch tracks the pull request's, so a plain `git push` from\n * inside the session lands on the pull request.\n *\n * A previous session's work stops this before anything is cut: a branch that\n * has gone past the head says so in its own words rather than being reset over\n * commits I have not pushed, and that is asked of the branch alone, so a\n * worktree pruned or removed by hand does not let the commits through. Where\n * the branch is clear, the previous worktree is removed without `--force`, so\n * changes I have not committed refuse in `git`'s own words.\n */\nexport const standingWorktree = Effect.fn(\"git.standingWorktree\")(function* (\n repo: string,\n number: number,\n prBranch: string,\n session: Session\n) {\n const { clone, directory, head } = yield* whereToCut(repo, number, under[session])\n const branch = `dw-mc/${session}/${number}`\n\n const ahead = yield* aheadOf(clone, branch, head)\n if (ahead > 0) {\n return yield* new WorktreeHeld({\n directory,\n detail:\n `The last fix session on ${repo}#${number} left ${ahead} commit${ahead === 1 ? \"\" : \"s\"} ` +\n `that the pull request's head does not have. Push them or drop them before opening another session.`\n })\n }\n if ((yield* worktreesOf(clone)).includes(directory)) {\n yield* git([\"-C\", clone, \"worktree\", \"remove\", directory])\n }\n yield* perWorktreeConfig(clone)\n if (session === \"rebase\") {\n yield* reuseResolutions(clone)\n }\n yield* beating(cutting(\"cutting a worktree of\", repo), () =>\n git([\"-C\", clone, \"worktree\", \"add\", \"-B\", branch, directory, head])\n )\n\n // What makes `git push` inside the session land on the pull request: the\n // branch tracks the pull request's, and a push follows the upstream's name\n // rather than the branch's own. Where the branch is tracked is the clone's\n // business, but how a push behaves is this worktree's alone: a review run's\n // worktree must not inherit it.\n yield* git([\"-C\", clone, \"config\", `branch.${branch}.remote`, \"origin\"])\n yield* git([\"-C\", clone, \"config\", `branch.${branch}.merge`, `refs/heads/${prBranch}`])\n yield* git([\"-C\", directory, \"config\", \"--worktree\", \"push.default\", \"upstream\"])\n\n return { directory, head } satisfies Worktree\n})\n\n/** What a rebase of a pull request's branch onto its base came to. */\nexport type Rebased =\n | { readonly _tag: \"up-to-date\" }\n | { readonly _tag: \"conflicted\"; readonly paths: ReadonlyArray<string> }\n | { readonly _tag: \"pushed\"; readonly before: string; readonly after: string; readonly behind: number }\n\n/** How many commits the base has that the worktree's head does not. */\nconst behindBy = Effect.fn(\"git.behindBy\")(function* (directory: string, base: string) {\n const counted = yield* git([\"-C\", directory, \"rev-list\", \"--count\", `HEAD..refs/heads/${base}`])\n return Number(counted.trim())\n})\n\n/**\n * The files the stopped replay left unmerged, which is what the conflict is\n * about.\n *\n * `git` names them itself rather than being read out of its prose, and a\n * listing that refuses is no reason to leave a rebase standing: the paths are\n * worth less than the abort, so the conflict is recorded with none of them.\n */\nconst unmergedIn = Effect.fn(\"git.unmergedIn\")(function* (directory: string) {\n const listed = yield* Effect.orElseSucceed(git([\"-C\", directory, \"diff\", \"--name-only\", \"--diff-filter=U\"]), () => \"\")\n return listed.split(\"\\n\").filter((line) => line !== \"\")\n})\n\n/** What replaying a branch's commits onto its base came to, inside the worktree. */\ntype Replayed = { readonly _tag: \"replayed\" } | { readonly _tag: \"conflicted\"; readonly paths: ReadonlyArray<string> }\n\n/**\n * Whether a rebase is in progress in `directory`.\n *\n * Asked of `git` by the one command that answers it with an exit code alone:\n * the stopped replay's patch is there to show while the rebase is, and gone\n * when it is not.\n */\nconst rebasing = Effect.fn(\"git.rebasing\")(function* (directory: string) {\n return Result.isSuccess(yield* Effect.result(git([\"-C\", directory, \"rebase\", \"--show-current-patch\"])))\n})\n\n/** Whether anything is staged in `directory`, which `git` says by refusing. */\nconst stagedIn = Effect.fn(\"git.stagedIn\")(function* (directory: string) {\n return Result.isFailure(yield* Effect.result(git([\"-C\", directory, \"diff\", \"--cached\", \"--quiet\"])))\n})\n\n/**\n * How many stops one replay may be carried past before this gives up on it.\n *\n * A replay of n commits can stop n times and `rerere` can answer every one of\n * them, so the number is only here so that a stop which neither resolves nor\n * moves cannot spin forever.\n */\nconst stops = 100\n\n/**\n * Replays the worktree's commits onto `base`, and says where the replay\n * stopped: nowhere, or on the files it could not merge.\n *\n * A conflict is told from every other way `git rebase` refuses by what it left\n * unmerged, which `git` names itself rather than being read out of its prose.\n * The unmerged files are read before anything is aborted, because that is the\n * only moment they exist.\n *\n * A stop with nothing unmerged is where `rerere` has been: verified by running\n * it, a replay of a conflict I resolved once stages the old resolution and\n * still exits non-zero, with no unmerged file left to name. That is a replay to\n * carry on rather than one to report, so it is continued - with `core.editor`\n * off, because the continue is the tool's and the message is the commit's own.\n * Anything else with nothing unmerged and nothing staged never started, and is\n * worth `git`'s own words rather than a conflict that did not happen.\n *\n * `onConflict` is the whole difference between the two worktrees that replay.\n * `abort` is for the one the tool cuts and throws away, where nothing\n * half-finished may be left behind; `leave` is for the one I asked for and\n * which stands, where the stopped rebase is what I came for.\n */\nconst replayOnto = Effect.fn(\"git.replayOnto\")(function* (\n directory: string,\n base: string,\n onConflict: \"abort\" | \"leave\"\n) {\n let stopped = yield* Effect.result(git([\"-C\", directory, \"rebase\", `refs/heads/${base}`]))\n\n for (let step = 0; step < stops; step += 1) {\n if (Result.isSuccess(stopped)) {\n return { _tag: \"replayed\" } satisfies Replayed\n }\n const paths = yield* unmergedIn(directory)\n if (paths.length > 0) {\n if (onConflict === \"abort\") {\n const aborted = yield* Effect.result(git([\"-C\", directory, \"rebase\", \"--abort\"]))\n if (Result.isFailure(aborted)) {\n return yield* stopped.failure\n }\n }\n return { _tag: \"conflicted\", paths } satisfies Replayed\n }\n if (!((yield* rebasing(directory)) && (yield* stagedIn(directory)))) {\n return yield* stopped.failure\n }\n stopped = yield* Effect.result(git([\"-C\", directory, \"-c\", \"core.editor=true\", \"rebase\", \"--continue\"]))\n }\n\n return Result.isSuccess(stopped) ? ({ _tag: \"replayed\" } satisfies Replayed) : yield* stopped.failure\n})\n\n/**\n * Replays onto `base` in a worktree that stands, and leaves a conflict exactly\n * where it stopped.\n *\n * This is the other half of the rebase the throwaway worktree aborts: the\n * conflict is the point here, so the rebase stays in progress and the files\n * stay unmerged for the session to work on and for me to finish. The two are\n * not the same invariant - nothing half-finished is left in a worktree the tool\n * cuts and throws away, and this one is mine, asked for and left standing.\n */\nexport const rebaseInPlace = Effect.fn(\"git.rebaseInPlace\")(function* (directory: string, base: string) {\n return yield* replayOnto(directory, base, \"leave\")\n})\n\n/**\n * Brings a pull request's branch up to date with its base: rebase onto the\n * base and push with a lease, in a worktree thrown away either way.\n *\n * This is the only write the tool makes to GitHub, and everything about how it\n * is done is about that. The lease names the commit the rebase started from,\n * so a push lands only where the branch is still where this run read it, and a\n * commit pushed from somewhere else while the rebase ran refuses rather than\n * being overwritten. The branch is named in full on both sides of the push,\n * because the worktree stands on a detached head and has no branch of its own\n * to push from.\n *\n * My own checkout is not involved: the worktree is cut from the tool's own\n * clone, like every other run's.\n */\nexport const rebaseOnto = Effect.fn(\"git.rebaseOnto\")(function* (\n repo: string,\n number: number,\n base: string,\n branch: string\n) {\n return yield* withWorktree(repo, number, (worktree) =>\n Effect.gen(function* () {\n const behind = yield* behindBy(worktree.directory, base)\n if (behind === 0) {\n return { _tag: \"up-to-date\" } satisfies Rebased\n }\n const replayed = yield* replayOnto(worktree.directory, base, \"abort\")\n if (replayed._tag === \"conflicted\") {\n return replayed satisfies Rebased\n }\n\n const after = yield* git([\"-C\", worktree.directory, \"rev-parse\", \"HEAD\"])\n yield* git([\n \"-C\",\n worktree.directory,\n \"push\",\n `--force-with-lease=refs/heads/${branch}:${worktree.head}`,\n \"origin\",\n `HEAD:refs/heads/${branch}`\n ])\n return { _tag: \"pushed\", before: worktree.head, after, behind } satisfies Rebased\n })\n )\n})\n\n/** What a standing session worktree still holds, or nothing at all. */\nexport type Holding = { readonly _tag: \"clear\" } | { readonly _tag: \"held\"; readonly detail: string }\n\nconst clear: Holding = { _tag: \"clear\" }\n\n/**\n * What the worktree of a fix or resolve session still holds, asked without\n * reaching GitHub.\n *\n * Nothing that takes a directory away may fetch first: a command asked to\n * remove things would be cloning to answer whether it may, and a machine that\n * is offline would be told its work is gone. So the pull request's head is read\n * from the ref the last run left in the clone, and where there is no ref to\n * read the answer is that this cannot be told - which holds the worktree rather\n * than letting it through, because the one mistake worth avoiding here is\n * taking away a commit I have not pushed.\n *\n * Uncommitted changes are asked of the worktree and commits are asked of the\n * branch, for the reason `standingWorktree` asks the same two: a worktree\n * pruned or moved by hand still leaves the branch holding the commits.\n */\nexport const holding = Effect.fn(\"git.holding\")(function* (repo: string, number: number, session: Session) {\n const path = yield* Path.Path\n const state = yield* stateDirectory\n const clone = path.join(state, clonesIn, `${repo}.git`)\n const directory = path.join(state, under[session], repo, String(number))\n const branch = `dw-mc/${session}/${number}`\n\n const changes = yield* Effect.orElseSucceed(git([\"-C\", directory, \"status\", \"--porcelain\"]), () => \"\")\n if (changes.trim() !== \"\") {\n return { _tag: \"held\", detail: \"changes that are not committed\" } satisfies Holding\n }\n\n const ref = `refs/heads/${branch}`\n const found = yield* Effect.orElseSucceed(git([\"-C\", clone, \"rev-parse\", \"--verify\", \"--quiet\", ref]), () => \"\")\n if (found.trim() === \"\") {\n return clear\n }\n\n const head = yield* Effect.orElseSucceed(git([\"-C\", clone, \"rev-parse\", `refs/dw-mc/pr/${number}`]), () => \"\")\n if (head.trim() === \"\") {\n return {\n _tag: \"held\",\n detail: `the clone no longer knows what ${repo}#${number} points at, so what ${branch} holds cannot be told`\n } satisfies Holding\n }\n\n const ahead = yield* aheadOf(clone, branch, head.trim())\n return ahead === 0\n ? clear\n : ({\n _tag: \"held\",\n detail: `${ahead} commit${ahead === 1 ? \"\" : \"s\"} that the pull request's head does not have`\n } satisfies Holding)\n})\n\n/**\n * Forgets the worktrees a clone has been left with, once their directories are\n * gone.\n *\n * A directory taken from under the clone leaves the clone's record of it\n * behind, and the next session on that pull request is cut at the same path,\n * which is then refused as already registered. Pruning is the whole repair, and\n * only a clone that stays needs it: one being removed takes its records with\n * it.\n */\nexport const prune = Effect.fn(\"git.prune\")(function* (clone: string) {\n yield* Effect.ignore(git([\"-C\", clone, \"worktree\", \"prune\"]))\n})\n","import type { Cause } from \"effect\"\nimport { Effect, Layer, Option, Queue, Terminal } from \"effect\"\nimport { Prompt } from \"effect/unstable/cli\"\n\nimport { Paint, plain } from \"#adapters/paint.ts\"\n\n/**\n * Turns quitting into an answer rather than a failure.\n *\n * Bailing out of a prompt gives `None`, so no caller has to catch an error to\n * learn that I walked away. The prompt itself decides what a `Some` carries.\n */\nconst orNone = <A, R>(\n prompt: Effect.Effect<Option.Option<A>, Terminal.QuitError, R>\n): Effect.Effect<Option.Option<A>, never, R> => Effect.catchTag(prompt, \"QuitError\", () => Effect.succeedNone)\n\n/**\n * What a prompt looks like in this tool: the marker the rows already use, and\n * the same colour for the choice I am standing on.\n *\n * It is set here rather than at each prompt, because this module is the only\n * thing that opens one and four prompts that themed themselves would be four\n * looks.\n */\nconst theme = (paint: Paint): Partial<Prompt.Theme> =>\n paint === plain\n ? { prefix: \"▸\", pointer: \"●\" }\n : { prefix: \"▸\", pointer: \"●\", primaryColor: \"cyan\", mutedColor: \"gray\" }\n\n/**\n * What the keyboard does, said under the question.\n *\n * It rides in the message rather than being printed above the prompt, so it\n * leaves with the prompt: a hint that outlives the answer is scrollback I did\n * not ask for.\n */\nconst moves = \"↑↓ move · enter choose · q quit\"\n\nconst asked = (paint: Paint, message: string): string => `${message}\\n${paint.dim(moves)}`\n\n/** Asks which one of `choices` to act on. */\nexport const pick = <A>(\n message: string,\n choices: ReadonlyArray<Prompt.SelectChoice<A>>\n): Effect.Effect<Option.Option<A>, never, Prompt.Environment> =>\n Effect.flatMap(Paint, (paint) =>\n orNone(Effect.asSome(Prompt.Select({ message: asked(paint, message), choices, theme: theme(paint) })))\n )\n\n/**\n * Asks which of `choices` to act on, as many as I like.\n *\n * Nothing is selected to begin with, so what reaches the caller is what I\n * picked rather than what I failed to unpick. Quitting is not the same as\n * picking nothing: it gives `None`.\n */\nexport const choose = <A>(\n message: string,\n choices: ReadonlyArray<Prompt.SelectChoice<A>>\n): Effect.Effect<Option.Option<ReadonlyArray<A>>, never, Prompt.Environment> =>\n Effect.flatMap(Paint, (paint) =>\n orNone(\n Effect.asSome(\n Prompt.MultiSelect({\n message: `${message}\\n${paint.dim(\"↑↓ move · space pick · enter confirm · q quit\")}`,\n choices,\n theme: theme(paint)\n })\n )\n )\n )\n\n/**\n * Asks a yes-or-no question about something that cannot be taken back.\n *\n * It starts on no, and walking away is no as well: the answer this returns is\n * the one I typed, and every other way out of the prompt leaves the thing\n * undone. A confirmation that defaulted to yes would be one keystroke, which is\n * exactly what it exists to stop being.\n */\nexport const confirm = (message: string): Effect.Effect<boolean, never, Prompt.Environment> =>\n Effect.flatMap(Paint, (paint) =>\n Effect.map(\n orNone(Effect.asSome(Prompt.Confirm({ message, initial: false, theme: theme(paint) }))),\n Option.getOrElse(() => false)\n )\n )\n\n/**\n * Asks for a line of prose, where having nothing to say is the ordinary answer.\n *\n * An empty line is no note, and that is not a failure: the prompt is optional\n * by design. Quitting is the one thing it does not swallow. Ctrl-C part way\n * through a list of notes means I want out of the whole command, and a prompt\n * that turned it into \"no note\" would walk me through the rest of the list and\n * then act on findings I was no longer sure about.\n */\nexport const note = (message: string): Effect.Effect<Option.Option<string>, Terminal.QuitError, Prompt.Environment> =>\n Effect.map(Prompt.String({ message }), (text) => (text.trim() === \"\" ? Option.none() : Option.some(text.trim())))\n\n/**\n * How wide the screen is, or zero where there is no screen to measure.\n *\n * A prompt has to fit its row on one line: a row that wraps takes the list's\n * alignment with it. Nothing is piping into a prompt, so zero means the writing\n * is going somewhere that does not wrap either.\n */\nexport const width: Effect.Effect<number, never, Terminal.Terminal> = Effect.gen(function* () {\n const terminal = yield* Terminal.Terminal\n return yield* terminal.columns\n})\n\n/** One keypress for a scripted terminal. */\nexport const key = (name: string): Terminal.UserInput => ({\n input: Option.none(),\n key: { name, ctrl: false, meta: false, shift: false }\n})\n\n/**\n * A line of typing for a scripted terminal, one keypress to the character.\n *\n * A keypress carries one code unit, which is what a terminal really delivers,\n * so the text is split the way a keyboard produces it rather than by grapheme.\n */\nexport const typed = (text: string): ReadonlyArray<Terminal.UserInput> =>\n text.split(\"\").map((character) => ({\n input: Option.some(character),\n key: { name: character, ctrl: false, meta: false, shift: false }\n }))\n\n/**\n * A terminal that answers with `keys` and draws into `drawn`, for tests.\n *\n * Effect ships no test terminal, so this builds one from `Terminal.make`. A\n * prompt only ever asks for `columns`, `display` and `readInput`; it never\n * calls `readLine`. The keys are queued once, so a second prompt over the same\n * terminal finds the script spent rather than replaying it. Running out of keys\n * ends the queue, which a prompt reads as the user quitting.\n *\n * What is drawn is kept only where a caller asks for it: a prompt redraws\n * itself on every keypress, and a test that is about the answer does not want\n * the frames.\n */\nexport const layerScripted = (\n keys: ReadonlyArray<Terminal.UserInput>,\n drawn?: Array<string>,\n columns: number = 80\n): Layer.Layer<Terminal.Terminal> =>\n Layer.effect(\n Terminal.Terminal,\n Effect.gen(function* () {\n const queue = yield* Queue.make<Terminal.UserInput, Cause.Done>()\n for (const stroke of keys) {\n Queue.offerUnsafe(queue, stroke)\n }\n Queue.endUnsafe(queue)\n\n return Terminal.make({\n columns: Effect.succeed(columns),\n rows: Effect.succeed(24),\n readInput: Effect.succeed(queue),\n readLine: Effect.die(\"picker: a prompt never reads a line\"),\n display: (text) => Effect.sync(() => drawn?.push(text))\n })\n })\n )\n","/**\n * The rows of a table, padded so the columns line up and with the trailing\n * blanks cut. Effect ships no table and a table is what these commands print.\n *\n * A cell may arrive with colour on it or with a link under it, and both are\n * characters a terminal never shows, so every width here is measured in what is\n * shown rather than in what the string holds. Padding is added outside them, so\n * no line ends in blanks a terminal is still colouring or still linking.\n *\n * `separator` is what sits between two columns. Two spaces are enough where a\n * row is short; a row that runs to a sentence needs a rule, or the eye loses\n * which column it is in.\n */\nexport const table = (rows: ReadonlyArray<ReadonlyArray<string>>, separator: string = \" \"): ReadonlyArray<string> => {\n const widths = rows.reduce<ReadonlyArray<number>>(\n (widest, row) => row.map((cell, index) => Math.max(visible(cell), widest[index] ?? 0)),\n []\n )\n return rows.map((row) =>\n row\n .map((cell, index) => `${cell}${\" \".repeat(Math.max((widths[index] ?? 0) - visible(cell), 0))}`)\n .join(separator)\n .trimEnd()\n )\n}\n\n// oxlint-disable-next-line no-control-regex -- colour and links are control characters; matching them is the point\nconst escapes = /(\\x1b\\[\\d+m|\\x1b\\]8;;[^\\x1b]*\\x1b\\\\)/\nconst escape = new RegExp(`^${escapes.source}$`)\n\n/** How much of a cell a terminal shows: its characters, less the sequences they are wrapped in. */\nexport const visible = (text: string): number =>\n text.split(escapes).reduce((width, piece) => width + (escape.test(piece) ? 0 : piece.length), 0)\n\n/**\n * `text` at most `width` wide, with an ellipsis where it was cut.\n *\n * The width is what is shown, and the sequences the cut text was wrapped in are\n * kept whole: a string cut through one spills it onto the screen, and a string\n * that loses the one that closes it colours - or links - everything after it.\n */\nexport const truncate = (text: string, width: number): string => {\n if (visible(text) <= width) {\n return text\n }\n let shown = 0\n const kept = text.split(escapes).map((piece) => {\n if (escape.test(piece)) {\n return piece\n }\n const taken = piece.slice(0, Math.max(width - 1 - shown, 0))\n shown = shown + taken.length\n return taken\n })\n const last = kept.findLastIndex((piece) => !escape.test(piece) && piece !== \"\")\n return kept.map((piece, index) => (index === last ? `${piece.trimEnd()}…` : piece)).join(\"\")\n}\n\n/** `n` of something, pluralised the one way English usually is. */\nexport const count = (n: number, noun: string): string => `${n} ${noun}${n === 1 ? \"\" : \"s\"}`\n","import { ByteSize } from \"effect\"\n\nimport type { Clone, Cutting, Inventory, Session } from \"#adapters/store.ts\"\nimport { sessionOf } from \"#adapters/store.ts\"\n\n/** A checkout the tool cut for a session I steer, which stands until I take it down. */\nexport interface Standing extends Cutting {\n readonly session: Session\n}\n\n/** A clone that stays, and the session that is the reason. */\nexport interface Kept {\n readonly clone: Clone\n readonly because: string\n}\n\n/**\n * What `dw-mc cleanup` takes back, and what it leaves where it stands.\n *\n * The rule is one line: everything the tool can build again goes, and nothing\n * else is touched. A bare clone is a `git clone` away, and a review run's\n * worktree is cut fresh on every run, so both are the tool's own cost rather\n * than anything of mine. The records are not in here at all - what a pull\n * request is worth forgetting is decided when it is done, not by how much disk\n * it takes.\n */\nexport interface Plan {\n readonly clones: ReadonlyArray<Clone>\n readonly worktrees: ReadonlyArray<Cutting>\n readonly kept: ReadonlyArray<Kept>\n readonly size: ByteSize.ByteSize\n}\n\n/** The checkouts that stand for a session, named by the session they stand for. */\nexport const standing = (inventory: Inventory): ReadonlyArray<Standing> =>\n inventory.cuttings.flatMap((cutting) => {\n const session = sessionOf(cutting.cut)\n return session === undefined ? [] : [{ ...cutting, session }]\n })\n\n/** The checkouts a review run cut, which no run that ended still needs. */\nexport const orphaned = (inventory: Inventory): ReadonlyArray<Cutting> =>\n inventory.cuttings.filter((cutting) => cutting.cut === \"worktrees\")\n\nconst sum = (sizes: ReadonlyArray<ByteSize.ByteSize>): ByteSize.ByteSize =>\n ByteSize.bytes(sizes.reduce((total, size) => total + ByteSize.toBigInt(size), BigInt(0)))\n\nconst reason = (sessions: ReadonlyArray<Standing>): string =>\n sessions\n .map((it) => `a ${it.session === \"fix\" ? \"fix\" : \"resolve\"} session stands on ${it.repo}#${it.number}`)\n .join(\", \")\n\n/**\n * What a cleanup would take, weighed.\n *\n * A clone whose repository has a session standing on it stays, and that is not\n * politeness: a standing worktree keeps its history inside the clone, so a\n * clone removed from under one leaves a directory of files with nothing behind\n * them. The worktrees of that session stay with it; the review run's own go\n * either way, because they belong to a run that has ended.\n */\nexport const plan = (inventory: Inventory): Plan => {\n const sessions = standing(inventory)\n const worktrees = orphaned(inventory)\n\n const held = new Map<string, ReadonlyArray<Standing>>()\n for (const session of sessions) {\n held.set(session.repo, [...(held.get(session.repo) ?? []), session])\n }\n\n const clones = inventory.clones.filter((clone) => !held.has(clone.repo))\n const kept = inventory.clones.flatMap((clone) => {\n const sessionsHere = held.get(clone.repo)\n return sessionsHere === undefined ? [] : [{ clone, because: reason(sessionsHere) }]\n })\n\n return {\n clones,\n worktrees,\n kept,\n size: sum([...clones, ...worktrees].map((it) => it.size))\n }\n}\n\n/** What the whole state directory weighs: the clones, the checkouts and the records. */\nexport const everything = (inventory: Inventory): ByteSize.ByteSize =>\n sum([...inventory.clones.map((it) => it.size), ...inventory.cuttings.map((it) => it.size), inventory.records.size])\n\n/** Whether a plan has anything to do at all. */\nexport const empty = (it: Plan): boolean => it.clones.length === 0 && it.worktrees.length === 0\n\n/** A size as a line says it: three digits at most, and the unit the terminal reads. */\nexport const weight = (size: ByteSize.ByteSize): string => ByteSize.format(size, { system: \"decimal\", precision: 1 })\n","import { Console, Effect, Path } from \"effect\"\nimport { Command, Flag } from \"effect/unstable/cli\"\n\nimport { prune } from \"#adapters/git.ts\"\nimport { beating } from \"#adapters/heartbeat.ts\"\nimport type { Paint } from \"#adapters/paint.ts\"\nimport { Paint as PaintService } from \"#adapters/paint.ts\"\nimport { confirm } from \"#adapters/picker.ts\"\nimport { discard, inventory, tidy } from \"#adapters/store.ts\"\nimport { table } from \"#cli/table.ts\"\nimport type { Plan } from \"#domain/cleanup.ts\"\nimport { empty, plan, weight } from \"#domain/cleanup.ts\"\n\nexport const yesFlag = Flag.Boolean(\"yes\").pipe(\n Flag.withDefault(false),\n Flag.withDescription(\"Do it without asking, for a machine that has no terminal to ask at\")\n)\n\n/** A path said as the state directory's own, which is the heading it sits under. */\nconst inside = (path: Path.Path, state: string, directory: string): string => path.relative(state, directory)\n\n/**\n * The two blocks a cleanup writes: what it takes and what it leaves.\n *\n * The weight is on every row because the whole question is whether this is\n * worth doing, and the reason is on every row because a clone and a worktree\n * are taken back for different reasons and both read as \"a directory of mine\"\n * on the screen.\n */\nconst lines = (it: Plan, state: string, path: Path.Path, paint: Paint): ReadonlyArray<string> => {\n const taking = table([\n ...it.worktrees.map((worktree) => [\n paint.dim(inside(path, state, worktree.directory)),\n weight(worktree.size),\n \"a review worktree a run left behind\"\n ]),\n ...it.clones.map((clone) => [\n paint.dim(inside(path, state, clone.directory)),\n weight(clone.size),\n \"a bare clone, cloned again on the next run\"\n ])\n ])\n\n const staying = table(\n it.kept.map((kept) => [paint.dim(inside(path, state, kept.clone.directory)), weight(kept.clone.size), kept.because])\n )\n\n return [\n \"Takes back\",\n ...taking.map((line) => ` ${line}`),\n \"\",\n ...(staying.length === 0 ? [] : [\"Stays\", ...staying.map((line) => ` ${line}`), \"\"])\n ]\n}\n\n/**\n * Takes back the disk the tool spent on itself, and nothing that is mine.\n *\n * What it removes is what the tool builds again by itself: the bare clones and\n * the worktrees a review run cut. What it never removes is what I decided - the\n * configuration file - and what I worked in - the worktree of a fix or resolve\n * session, which stands on a branch of the tool's own and holds what I\n * committed there. Forgetting a pull request's records is a different question\n * with a different answer (#58), and it is not asked here.\n *\n * A clone with a session standing on it stays with the session: a standing\n * worktree keeps its history inside the clone, so a clone taken from under one\n * would leave a directory of files with nothing behind them. The clones that\n * stay are pruned instead, because a worktree directory removed under `git`\n * leaves the clone's record of it behind and the next session cut at that path\n * is refused as already registered.\n */\nexport const cleanup = Command.make(\n \"cleanup\",\n { yes: yesFlag },\n Effect.fn(\"cleanup\")(function* ({ yes }) {\n const path = yield* Path.Path\n const paint = yield* PaintService\n // The whole reason to run this is that the clones have grown large, and the\n // larger they are the longer the walk that weighs them. A blank screen that\n // gets blanker the more there is to take back is exactly backwards.\n const found = yield* beating(\n (since) => `measuring the state directory · ${since}`,\n () => inventory\n )\n const it = plan(found)\n\n if (empty(it)) {\n yield* Console.log(`Nothing to take back in ${found.directory}.`)\n return\n }\n\n yield* Effect.forEach(lines(it, found.directory, path, paint), (line) => Console.log(line))\n\n if (!yes && !(yield* confirm(`Take back ${weight(it.size)}?`))) {\n yield* Console.log(\"Nothing was removed.\")\n return\n }\n\n yield* Effect.forEach([...it.worktrees, ...it.clones], (taken) =>\n Effect.andThen(discard(taken.directory), tidy(taken.directory, found.directory))\n )\n yield* Effect.forEach(it.kept, (kept) => prune(kept.clone.directory))\n\n yield* Console.log(`Took back ${weight(it.size)}.`)\n })\n).pipe(Command.withDescription(\"Take back the disk the tool spent on clones and review worktrees\"))\n","import { DateTime, Effect, Match, PlatformError, Schema } from \"effect\"\nimport type { ChildProcessSpawner } from \"effect/unstable/process\"\n\nimport { capture } from \"#adapters/spawner.ts\"\nimport type { Mergeability, ReviewDecision } from \"#terms/pr.ts\"\n\n/** `gh` is on the machine but would not run. */\nexport class GhUnavailable extends Schema.TaggedError<GhUnavailable>()(\"GhUnavailable\", {\n detail: Schema.String\n}) {\n override get message(): string {\n return `gh could not be run: ${this.detail}\\nInstall it from https://cli.github.com, then run 'gh auth login'.`\n }\n}\n\n/** `gh` runs but is not logged in, so every read of GitHub would fail. */\nexport class GhUnauthenticated extends Schema.TaggedError<GhUnauthenticated>()(\"GhUnauthenticated\", {\n detail: Schema.String\n}) {\n override get message(): string {\n return `gh is not authenticated. Run 'gh auth login'.\\n${this.detail}`\n }\n}\n\n/** The working directory is not inside a repository `gh` can name. */\nexport class NoRepository extends Schema.TaggedError<NoRepository>()(\"NoRepository\", {\n detail: Schema.String\n}) {\n override get message(): string {\n return `This directory is not a GitHub repository dw-mc can register.\\n${this.detail}`\n }\n}\n\n/** `gh` answered, in a shape this version of dw-mc does not know. */\nexport class GhUnreadable extends Schema.TaggedError<GhUnreadable>()(\"GhUnreadable\", {\n command: Schema.String,\n reason: Schema.String\n}) {\n override get message(): string {\n return `gh ${this.command} answered with something dw-mc cannot read: ${this.reason}`\n }\n}\n\n/** What a `gh` that would not even start comes to. */\nexport const unavailable = (error: PlatformError.PlatformError): GhUnavailable =>\n new GhUnavailable({\n detail: error.reason._tag === \"NotFound\" ? \"it is not installed\" : error.message\n })\n\n/**\n * Stops unless `gh` is installed and logged in.\n *\n * Every read of GitHub goes through `gh` as me, so a missing or logged-out `gh`\n * is worth saying once, up front, rather than as an empty table later.\n */\nexport const requireAuth: Effect.Effect<\n void,\n GhUnavailable | GhUnauthenticated,\n ChildProcessSpawner.ChildProcessSpawner\n> = capture(\"gh\", [\"auth\", \"status\"]).pipe(\n Effect.asVoid,\n Effect.catchTags({\n PlatformError: (error) => Effect.fail(unavailable(error)),\n CommandFailed: (error) => Effect.fail(new GhUnauthenticated({ detail: error.stderr }))\n }),\n Effect.withSpan(\"gh.requireAuth\")\n)\n\nconst RepoView = Schema.fromJsonString(Schema.Struct({ nameWithOwner: Schema.String }))\n\n/** The `owner/repo` of the repository the working directory is in. */\nexport const currentRepo: Effect.Effect<\n string,\n GhUnavailable | NoRepository | GhUnreadable,\n ChildProcessSpawner.ChildProcessSpawner\n> = Effect.gen(function* () {\n const json = yield* capture(\"gh\", [\"repo\", \"view\", \"--json\", \"nameWithOwner\"]).pipe(\n Effect.catchTags({\n PlatformError: (error) => Effect.fail(unavailable(error)),\n CommandFailed: (error) => Effect.fail(new NoRepository({ detail: error.stderr }))\n })\n )\n\n const view = yield* Schema.decodeEffect(RepoView)(json).pipe(\n Effect.mapError((error) => new GhUnreadable({ command: \"repo view\", reason: error.message }))\n )\n return view.nameWithOwner\n}).pipe(Effect.withSpan(\"gh.currentRepo\"))\n\n/** A call to GitHub that `gh` itself refused, whether it was reading or writing. */\nexport class GhReadFailed extends Schema.TaggedError<GhReadFailed>()(\"GhReadFailed\", {\n command: Schema.String,\n detail: Schema.String\n}) {\n override get message(): string {\n return `gh ${this.command} failed: ${this.detail}`\n }\n}\n\n/** Anything that can go wrong reading GitHub through `gh`. */\nexport type GhError = GhUnavailable | GhReadFailed | GhUnreadable\n\n/** One `gh` read, decoded, with every way it can go wrong in our words. */\nexport const readJson = <A>(\n label: string,\n command: string,\n args: ReadonlyArray<string>,\n schema: Schema.Codec<A, string>\n): Effect.Effect<A, GhError, ChildProcessSpawner.ChildProcessSpawner> =>\n capture(command, args).pipe(\n Effect.catchTags({\n PlatformError: (error) => Effect.fail(unavailable(error)),\n CommandFailed: (error) => Effect.fail(new GhReadFailed({ command: label, detail: error.stderr }))\n }),\n Effect.flatMap((json) =>\n Schema.decodeEffect(schema)(json).pipe(\n Effect.mapError((error) => new GhUnreadable({ command: label, reason: error.message }))\n )\n ),\n Effect.withSpan(`gh.${label}`)\n )\n\nconst User = Schema.fromJsonString(Schema.Struct({ login: Schema.String }))\n\n/** The login `gh` is authenticated as: the \"me\" every read is scoped to. */\nexport const viewer: Effect.Effect<string, GhError, ChildProcessSpawner.ChildProcessSpawner> = readJson(\n \"api user\",\n \"gh\",\n [\"api\", \"user\"],\n User\n).pipe(Effect.map((user) => user.login))\n\nconst SearchResults = Schema.fromJsonString(\n Schema.Array(\n Schema.Struct({\n number: Schema.Int,\n repository: Schema.Struct({ nameWithOwner: Schema.String })\n })\n )\n)\n\n/** One open pull request the search found. */\nexport interface Found {\n readonly repo: string\n readonly number: number\n}\n\n/**\n * The open pull requests I authored in `repo`.\n *\n * One search per repository rather than one for all of them: a repository `gh`\n * cannot read then costs me that repository's rows and not the whole table.\n */\nexport const searchPrs = Effect.fnUntraced(function* (repo: string) {\n const found = yield* readJson(\n \"search prs\",\n \"gh\",\n [\"search\", \"prs\", \"--author=@me\", \"--state=open\", \"--repo\", repo, \"--limit\", \"100\", \"--json\", \"number,repository\"],\n SearchResults\n )\n\n return found.map((it): Found => ({ repo: it.repository.nameWithOwner, number: it.number }))\n})\n\n/**\n * One entry of a PR's status check rollup.\n *\n * A rollup mixes two shapes: a `CheckRun` reports a `status` and a `conclusion`,\n * a `StatusContext` an overall `state`. Every field is optional because which\n * ones arrive depends on which shape it is.\n */\nexport const CheckEntry = Schema.Struct({\n name: Schema.optionalKey(Schema.String),\n context: Schema.optionalKey(Schema.String),\n status: Schema.optionalKey(Schema.String),\n conclusion: Schema.optionalKey(Schema.String),\n state: Schema.optionalKey(Schema.String),\n /** The workflow the check runs in. A commit status belongs to no workflow. */\n workflowName: Schema.optionalKey(Schema.String),\n /** Where the check reports, which is the only place its job id appears. */\n detailsUrl: Schema.optionalKey(Schema.String)\n})\nexport type CheckEntry = typeof CheckEntry.Type\n\nconst PrView = Schema.fromJsonString(\n Schema.Struct({\n number: Schema.Int,\n title: Schema.String,\n url: Schema.String,\n isDraft: Schema.Boolean,\n headRefOid: Schema.String,\n headRefName: Schema.String,\n baseRefName: Schema.String,\n /** Who opened it, which is what says whether its branch is mine to push to. */\n author: Schema.NullOr(Schema.Struct({ login: Schema.String })),\n /** Whether the head branch lives in a fork rather than in this repository. */\n isCrossRepository: Schema.Boolean,\n mergeable: Schema.String,\n reviewDecision: Schema.String,\n statusCheckRollup: Schema.NullOr(Schema.Array(CheckEntry))\n })\n)\nexport type PrView = typeof PrView.Type\n\nconst viewFields =\n \"number,title,url,isDraft,headRefOid,headRefName,baseRefName,author,isCrossRepository,mergeable,\" +\n \"reviewDecision,statusCheckRollup\"\n\n/**\n * Everything about one pull request that arrives without paging through it:\n * its head, what GitHub thinks of merging it, and where CI got to.\n */\nexport const prView = Effect.fnUntraced(function* (repo: string, number: number) {\n return yield* readJson(\"pr view\", \"gh\", [\"pr\", \"view\", String(number), \"--repo\", repo, \"--json\", viewFields], PrView)\n})\n\nconst OpenPrs = Schema.fromJsonString(\n Schema.Array(\n Schema.Struct({\n number: Schema.Int,\n headRefName: Schema.String,\n baseRefName: Schema.String\n })\n )\n)\n\n/** One open pull request, as the branch it stands on and the one it merges into. */\nexport interface OpenPr {\n readonly number: number\n readonly head: string\n readonly base: string\n}\n\n/**\n * Every open pull request on a repository, by branch.\n *\n * Everyone's and not only mine: a stack is recognised from branches built on\n * branches, and a pull request of mine can sit on one somebody else opened.\n *\n * The page is deep because a pull request this misses is one that looks like it\n * is in no stack, and a stack the tool cannot see is one it could drive.\n */\nexport const openPrs = Effect.fnUntraced(function* (repo: string) {\n const open = yield* readJson(\n \"pr list\",\n \"gh\",\n [\"pr\", \"list\", \"--repo\", repo, \"--state\", \"open\", \"--limit\", \"500\", \"--json\", \"number,headRefName,baseRefName\"],\n OpenPrs\n )\n\n return open.map((it): OpenPr => ({ number: it.number, head: it.headRefName, base: it.baseRefName }))\n})\n\nconst Comments = Schema.fromJsonString(\n Schema.Array(\n Schema.Struct({\n created_at: Schema.DateTimeUtcFromString,\n user: Schema.NullOr(Schema.Struct({ login: Schema.String, type: Schema.String }))\n })\n )\n)\n\n/** Who wrote a comment and when. */\nexport interface Comment {\n readonly login: string\n readonly bot: boolean\n readonly at: DateTime.Utc\n}\n\nconst comments = (label: string, path: string) =>\n readJson(label, \"gh\", [\"api\", path], Comments).pipe(\n Effect.map((all) =>\n all.flatMap((comment): ReadonlyArray<Comment> =>\n comment.user === null\n ? []\n : [{ login: comment.user.login, bot: comment.user.type === \"Bot\", at: comment.created_at }]\n )\n )\n )\n\n/**\n * Every comment on a pull request: the ones on the conversation and the ones\n * left on the diff.\n *\n * REST is what says whether an author is a person or an app - `gh pr view`\n * reports a bot's login with no sign that it is one - and the bucket rules turn\n * on exactly that. Verified by running both: the endpoints ignore `direction`,\n * so a page is asked for at its maximum and the newest comment is picked out of\n * it rather than asked for first.\n */\nexport const prComments = Effect.fnUntraced(function* (repo: string, number: number) {\n const page = \"per_page=100\"\n const [conversation, onDiff] = yield* Effect.all(\n [\n comments(\"api issue comments\", `repos/${repo}/issues/${number}/comments?${page}`),\n comments(\"api review comments\", `repos/${repo}/pulls/${number}/comments?${page}`)\n ],\n { concurrency: 2 }\n )\n return [...conversation, ...onDiff]\n})\n\nconst Reviews = Schema.fromJsonString(\n Schema.Array(\n Schema.Struct({\n submitted_at: Schema.DateTimeUtcFromString,\n body: Schema.String,\n user: Schema.NullOr(Schema.Struct({ login: Schema.String, type: Schema.String }))\n })\n )\n)\n\n/**\n * The reviews on a pull request that said something, as comments.\n *\n * A review carries a body of its own, which is where a reviewer writes the\n * sentence that is not attached to any line. An empty body is a verdict and\n * nothing more, and the verdict arrives with the PR as `reviewDecision`.\n */\nexport const prReviews = Effect.fnUntraced(function* (repo: string, number: number) {\n const all = yield* readJson(\n \"api reviews\",\n \"gh\",\n [\"api\", `repos/${repo}/pulls/${number}/reviews?per_page=100`],\n Reviews\n )\n\n return all.flatMap((review): ReadonlyArray<Comment> =>\n review.user === null || review.body.trim() === \"\"\n ? []\n : [{ login: review.user.login, bot: review.user.type === \"Bot\", at: review.submitted_at }]\n )\n})\n\nconst Compare = Schema.fromJsonString(\n Schema.Struct({ files: Schema.optionalKey(Schema.Array(Schema.Struct({ filename: Schema.String }))) })\n)\n\n/**\n * The repository paths that changed between two commits.\n *\n * GitHub compares them rather than git, because the commit a run was recorded\n * against is not one the tool's own clone is promised to still have: a force\n * push moves the pull request's ref and the old commit goes with it, where\n * GitHub keeps both sides of the comparison. A comparison of a commit with\n * itself reports no files at all, and so does one of two commits with nothing\n * between them, which is why the key is optional.\n *\n * `base...head` measures from where the two commits last agreed, so two heads\n * on one branch report what was pushed between them, and a branch rebased since\n * reports its whole diff. The second is the right answer for a caller deciding\n * whether the code has moved: after a rebase it has, all of it.\n */\nexport const comparedFiles = Effect.fnUntraced(function* (repo: string, base: string, head: string) {\n const compare = yield* readJson(\"api compare\", \"gh\", [\"api\", `repos/${repo}/compare/${base}...${head}`], Compare)\n return (compare.files ?? []).map((file) => file.filename)\n})\n\nconst Commits = Schema.fromJsonString(\n Schema.Struct({\n commits: Schema.Array(\n Schema.Struct({\n committedDate: Schema.DateTimeUtcFromString,\n authors: Schema.Array(Schema.Struct({ login: Schema.NullOr(Schema.String) }))\n })\n )\n })\n)\n\n/** One commit on a pull request, and who wrote it. */\nexport interface Commit {\n readonly logins: ReadonlyArray<string>\n readonly at: DateTime.Utc\n}\n\n/**\n * The commits on a pull request.\n *\n * This is the expensive read of the three: `gh` returns every commit with its\n * whole message, so a sweep only asks for it when something about the PR has\n * actually moved.\n */\nexport const prCommits = Effect.fnUntraced(function* (repo: string, number: number) {\n const view = yield* readJson(\n \"pr view commits\",\n \"gh\",\n [\"pr\", \"view\", String(number), \"--repo\", repo, \"--json\", \"commits\"],\n Commits\n )\n\n return view.commits.map((commit): Commit => ({\n logins: commit.authors.flatMap((author) => (author.login === null ? [] : [author.login])),\n at: commit.committedDate\n }))\n})\n\n/**\n * What `gh` says about merging, in our words. Anything else is `unknown`:\n * GitHub answers that too, for a PR whose mergeability it is still computing.\n *\n * `Match.withReturnType` comes first in the pipeline or the return type is not\n * enforced: a handler's literal widens to `string` on its own.\n */\nexport const mergeabilityOf = (raw: string): Mergeability =>\n Match.value(raw).pipe(\n Match.withReturnType<Mergeability>(),\n Match.when(\"MERGEABLE\", () => \"mergeable\"),\n Match.when(\"CONFLICTING\", () => \"conflicting\"),\n Match.orElse(() => \"unknown\")\n )\n\n/**\n * What `gh` says the reviewers decided, in our words. A repository that requires\n * no reviewer reports an empty string, which is `none` rather than pending.\n */\nexport const reviewDecisionOf = (raw: string): ReviewDecision =>\n Match.value(raw).pipe(\n Match.withReturnType<ReviewDecision>(),\n Match.when(\"APPROVED\", () => \"approved\"),\n Match.when(\"CHANGES_REQUESTED\", () => \"changes-requested\"),\n Match.when(\"REVIEW_REQUIRED\", () => \"review-required\"),\n Match.orElse(() => \"none\")\n )\n\n/**\n * Squash-merges a pull request and deletes the branch it stood on.\n *\n * The one write the tool makes that no reflog of mine undoes, and the whole of\n * it: a squash, because that is how the repository lands a pull request and the\n * squash subject is its title, and the branch, because squashing kills it\n * anyway. No `--auto`, which would hand GitHub a merge to make at a head\n * nothing here has read (ADR 0008).\n *\n * Whether this pull request is one to merge is decided before we get here, and\n * `gh` still has the last word: a branch protection this machine cannot see\n * comes back as a failure and is printed as one.\n */\nexport const mergePr = Effect.fnUntraced(function* (repo: string, number: number) {\n yield* capture(\"gh\", [\"pr\", \"merge\", String(number), \"--repo\", repo, \"--squash\", \"--delete-branch\"]).pipe(\n Effect.catchTags({\n PlatformError: (error) => Effect.fail(unavailable(error)),\n CommandFailed: (error) => Effect.fail(new GhReadFailed({ command: \"pr merge\", detail: error.stderr }))\n })\n )\n})\n","import { DateTime, Effect, Schema } from \"effect\"\n\nimport { readJson } from \"#adapters/gh.ts\"\n\n/**\n * A pull request's conversation, which is the one read that leaves REST.\n *\n * It sits beside `gh.ts` rather than in it because it is a boundary of its own:\n * one GraphQL document, decoded into the threads a command prints, where every\n * other read of GitHub here is a `gh` subcommand or a REST endpoint.\n */\n\n/** One thing somebody said on a pull request, in full. */\nexport interface Remark {\n readonly login: string\n readonly bot: boolean\n readonly at: DateTime.Utc\n readonly body: string\n}\n\n/**\n * One strand of a pull request's conversation: a review thread on a line of the\n * diff, or the pull request's own comments, which hang off no path at all.\n */\nexport interface Thread {\n readonly path: string | null\n readonly line: number | null\n readonly resolved: boolean\n readonly outdated: boolean\n readonly comments: ReadonlyArray<Remark>\n}\n\nconst Actor = Schema.NullOr(Schema.Struct({ login: Schema.String, __typename: Schema.String }))\n\nconst Said = Schema.Struct({ author: Actor, body: Schema.String, createdAt: Schema.DateTimeUtcFromString })\n\nconst Conversation = Schema.fromJsonString(\n Schema.Struct({\n data: Schema.Struct({\n repository: Schema.Struct({\n pullRequest: Schema.Struct({\n comments: Schema.Struct({ nodes: Schema.Array(Said) }),\n reviews: Schema.Struct({\n nodes: Schema.Array(\n Schema.Struct({\n author: Actor,\n body: Schema.String,\n submittedAt: Schema.NullOr(Schema.DateTimeUtcFromString)\n })\n )\n }),\n reviewThreads: Schema.Struct({\n nodes: Schema.Array(\n Schema.Struct({\n isResolved: Schema.Boolean,\n isOutdated: Schema.Boolean,\n path: Schema.NullOr(Schema.String),\n line: Schema.NullOr(Schema.Int),\n comments: Schema.Struct({ nodes: Schema.Array(Said) })\n })\n )\n })\n })\n })\n })\n })\n)\n\nconst remark = (\n said: { readonly author: typeof Actor.Type; readonly body: string },\n at: DateTime.Utc | null\n): ReadonlyArray<Remark> =>\n said.author === null || at === null || said.body.trim() === \"\"\n ? []\n : [{ login: said.author.login, bot: said.author.__typename === \"Bot\", at, body: said.body.trim() }]\n\nconst byTime = (self: Remark, other: Remark): number => DateTime.Order(self.at, other.at)\n\n/**\n * A pull request's whole conversation: the comments on it, the bodies of the\n * reviews, and every thread on the diff with whether it is settled.\n *\n * GraphQL rather than the two REST endpoints a sweep reads, because resolution\n * is not in REST at all: a review comment's payload carries `body`, `path`,\n * `line`, `diff_hunk` and `side`, and nothing saying whether somebody closed\n * the thread it belongs to. A thread that was settled a week ago is not\n * something to answer, so the state that says so has to arrive with it.\n *\n * The pull request's own comments and the reviews' bodies come back as one\n * strand under no path, in the order they were written: they are one\n * conversation as it happened, and which endpoint each line came from is an\n * accident of GitHub's model rather than anything to read.\n *\n * `__typename` is what says a bot is a bot, the way `user.type` does in REST.\n */\nexport const prConversation = Effect.fnUntraced(function* (repo: string, number: number) {\n const [owner = repo, name = repo] = repo.split(\"/\")\n // The document is spelled out here rather than held in a constant, because\n // every GraphQL call is a POST and the document is the only thing that says\n // whether it reads or writes: `no-gh-writes` reads it at this call site and\n // refuses one it cannot.\n const answer = yield* readJson(\n \"api graphql\",\n \"gh\",\n [\n \"api\",\n \"graphql\",\n \"-f\",\n `query=query($owner:String!,$name:String!,$number:Int!){\n repository(owner:$owner,name:$name){\n pullRequest(number:$number){\n comments(last:100){nodes{author{login __typename} body createdAt}}\n reviews(last:100){nodes{author{login __typename} body submittedAt}}\n reviewThreads(last:100){nodes{\n isResolved isOutdated path line\n comments(first:100){nodes{author{login __typename} body createdAt}}\n }}\n }\n }\n }`,\n \"-F\",\n `owner=${owner}`,\n \"-F\",\n `name=${name}`,\n \"-F\",\n `number=${number}`\n ],\n Conversation\n )\n\n const pr = answer.data.repository.pullRequest\n const conversation = [\n ...pr.comments.nodes.flatMap((it) => remark(it, it.createdAt)),\n ...pr.reviews.nodes.flatMap((it) => remark(it, it.submittedAt))\n ].toSorted(byTime)\n\n const threads = pr.reviewThreads.nodes.map((it): Thread => ({\n path: it.path,\n line: it.line,\n resolved: it.isResolved,\n outdated: it.isOutdated,\n comments: it.comments.nodes.flatMap((comment) => remark(comment, comment.createdAt)).toSorted(byTime)\n }))\n\n return [\n ...(conversation.length === 0\n ? []\n : [{ path: null, line: null, resolved: false, outdated: false, comments: conversation } satisfies Thread]),\n ...threads\n ] satisfies ReadonlyArray<Thread>\n})\n","import { DateTime, Order, Predicate } from \"effect\"\n\n/**\n * A moment something happened, or that it never did.\n *\n * Three of the facts a sweep reads are timestamps that a pull request may\n * simply not have - nobody has commented, nobody has pushed - and the rules\n * compare them all the same way. These are that comparison, in one place, over\n * `DateTime`'s own `Order` and `Equivalence`.\n */\nexport type Moment = DateTime.Utc | null\n\nconst isLater = Order.isGreaterThan(DateTime.Order)\n\n/** Whether `self` happened after `other`, counting never as before anything. */\nexport const isAfter = (self: Moment, other: Moment): boolean =>\n Predicate.isNotNull(self) && (other === null || isLater(self, other))\n\n/** The later of the two. */\nexport const later = (self: Moment, other: Moment): Moment => (isAfter(self, other) ? self : other)\n\n/** Whether the two are the same moment, counting never as the same as never. */\nexport const isSame = (self: Moment, other: Moment): boolean =>\n self === null || other === null ? self === other : DateTime.Equivalence(self, other)\n\n/** The latest of many, or never when there are none. */\nexport const newest = (moments: ReadonlyArray<DateTime.Utc>): Moment => moments.reduce<Moment>(later, null)\n","/**\n * What GitHub says about a pull request, in this tool's words.\n *\n * The three of them are here because both sides need the same one: `gh` and the\n * checks adapter answer in these words, and the bucket rules decide on them. A\n * union restated on each side is a case that goes unreachable the day the other\n * side gains a member.\n */\nimport { Schema } from \"effect\"\n\n/** How far GitHub has got towards letting a tracked PR merge. */\nexport const Mergeability = Schema.Literals([\"mergeable\", \"conflicting\", \"unknown\"])\nexport type Mergeability = typeof Mergeability.Type\n\n/** What the reviewers have decided, or that nobody is required to. */\nexport const ReviewDecision = Schema.Literals([\"approved\", \"changes-requested\", \"review-required\", \"none\"])\nexport type ReviewDecision = typeof ReviewDecision.Type\n\n/** What CI says about the current head. */\nexport const ChecksState = Schema.Literals([\"green\", \"red\", \"pending\", \"none\"])\nexport type ChecksState = typeof ChecksState.Type\n","import { Schema } from \"effect\"\n\nimport { isAfter, later } from \"#domain/moment.ts\"\nimport { ChecksState, Mergeability, ReviewDecision } from \"#terms/pr.ts\"\n\n/**\n * Everything the bucket rules are allowed to know about a tracked PR.\n *\n * It is a schema because a sweep writes it to the state directory and reads it\n * back on the next one: the same facts that decide a bucket are what a quiet PR\n * is recognised by.\n */\nexport const Facts = Schema.Struct({\n repo: Schema.String,\n number: Schema.Int,\n title: Schema.String,\n url: Schema.String,\n /** Shown, never acted on unless I ask. */\n draft: Schema.Boolean,\n /** The head commit every other fact here is about. */\n head: Schema.String,\n mergeable: Mergeability,\n reviewDecision: ReviewDecision,\n checks: ChecksState,\n /** Why the flaky classifier excuses this red CI, or null where it does not. */\n ciFlaky: Schema.NullOr(Schema.String),\n /** The head a rebase onto the base conflicted at, or null where none has. */\n rebaseConflictAt: Schema.NullOr(Schema.String),\n /** The newest comment from a person who is not me, bots excluded. */\n newestHumanCommentAt: Schema.NullOr(Schema.DateTimeUtcFromString),\n myLastCommentAt: Schema.NullOr(Schema.DateTimeUtcFromString),\n myLastCommitAt: Schema.NullOr(Schema.DateTimeUtcFromString),\n /** The head a review run has already covered, or null where none has. */\n reviewRunHead: Schema.NullOr(Schema.String),\n /** Findings on this head that withhold the stamp, at the bar `stamp.blocks_on` sets. */\n blockingFindings: Schema.Int\n})\nexport type Facts = typeof Facts.Type\n\n/** The one place a tracked PR sits at a time, named for what it waits on. */\nexport const Bucket = Schema.Literals([\"needs-me\", \"needs-review-run\", \"waiting-on-others\", \"ready\"])\nexport type Bucket = typeof Bucket.Type\n\n/** The bucket a tracked PR is in, and why it is in that one. */\nexport interface Placement {\n readonly bucket: Bucket\n readonly reason: string\n}\n\n/** A tracked PR beside the placement its facts earned. */\nexport interface Placed {\n readonly facts: Facts\n readonly placement: Placement\n}\n\n/** The buckets in the order I act on them: the top of the table is my next move. */\nexport const order: ReadonlyArray<Bucket> = [\"needs-me\", \"needs-review-run\", \"waiting-on-others\", \"ready\"]\n\n/**\n * Why a PR is mine to move when somebody has said something I have not\n * answered.\n *\n * It is named because it is read twice: here, where it puts the PR in Needs me,\n * and by `dw-mc comments`, which says what settles that one branch of the\n * bucket. A sentence matched from the other side of the tool is a rule that\n * breaks on a reword.\n */\nexport const unanswered = \"a comment I have not answered\"\n\n/**\n * The first of the rules that makes a PR mine to move, or null when none\n * does. The order is the order I would fix them in: a conflict makes every\n * other signal on the PR stale, and a red build is worth more than a comment.\n */\nconst needsMe = (facts: Facts): string | null => {\n if (facts.mergeable === \"conflicting\") {\n return \"merge conflict\"\n }\n if (facts.rebaseConflictAt === facts.head) {\n return \"a rebase onto the base conflicted\"\n }\n if (facts.checks === \"red\" && facts.ciFlaky === null) {\n return \"CI is red\"\n }\n if (facts.reviewDecision === \"changes-requested\") {\n return \"changes requested\"\n }\n if (facts.blockingFindings > 0) {\n return `${facts.blockingFindings} blocking finding${facts.blockingFindings === 1 ? \"\" : \"s\"}`\n }\n if (isAfter(facts.newestHumanCommentAt, later(facts.myLastCommentAt, facts.myLastCommitAt))) {\n return unanswered\n }\n return null\n}\n\n/**\n * What is actually true of a PR nothing is waiting on.\n *\n * Ready is reached by having no reason not to be, so the reason says only what\n * holds: a repository that requires no reviewer produces no approval, and a\n * pull request with no CI at all is not green.\n *\n * A red CI the classifier excused is said out loud, because GitHub does not\n * excuse it: the check is still red, and Ready is what `dw-mc merge` reads.\n */\nconst readyReason = (facts: Facts): string => {\n const held = [\n facts.reviewDecision === \"approved\" ? \"approved\" : null,\n facts.checks === \"green\" ? \"green\" : null,\n facts.mergeable === \"mergeable\" ? \"mergeable\" : null\n ].filter((it) => it !== null)\n const standing = held.length === 0 ? \"nothing left to wait on\" : held.join(\", \")\n return facts.checks === \"red\" && facts.ciFlaky !== null\n ? `${standing} (red CI called flaky: ${facts.ciFlaky})`\n : standing\n}\n\n/**\n * The bucket a tracked PR sits in, and the reason for it.\n *\n * This is the single place the bucket rules exist. Every tracked PR lands in\n * exactly one bucket, so the rules are tried in priority order and the first\n * that claims the PR wins: a PR that both needs a review run and has changes\n * requested is mine to move, not the review's.\n *\n * Ready does not insist on an approval, because a repository that requires no\n * reviewer never produces one. What it insists on is that nobody else has been\n * asked and is yet to answer.\n */\nexport const place = (facts: Facts): Placement => {\n const mine = needsMe(facts)\n if (mine !== null) {\n return { bucket: \"needs-me\", reason: mine }\n }\n if (facts.reviewRunHead !== facts.head) {\n return { bucket: \"needs-review-run\", reason: \"no review run on this head\" }\n }\n if (facts.reviewDecision === \"review-required\") {\n return { bucket: \"waiting-on-others\", reason: \"a review from someone else\" }\n }\n if (facts.checks === \"pending\") {\n return { bucket: \"waiting-on-others\", reason: \"CI is still running\" }\n }\n return { bucket: \"ready\", reason: readyReason(facts) }\n}\n\n/**\n * The tracked PRs grouped into their buckets, in the order I act on them, with\n * the empty buckets left out so the table is only what there is to do.\n */\n/** One bucket with what is in it: a heading in the table, and the rows under it. */\nexport interface Grouped {\n readonly bucket: Bucket\n readonly placed: ReadonlyArray<Placed>\n}\n\nexport const group = (facts: ReadonlyArray<Facts>): ReadonlyArray<Grouped> => {\n const placed = facts\n .map((it): Placed => ({ facts: it, placement: place(it) }))\n .toSorted((a, b) => a.facts.repo.localeCompare(b.facts.repo) || a.facts.number - b.facts.number)\n\n return order\n .map((bucket) => ({ bucket, placed: placed.filter((it) => it.placement.bucket === bucket) }))\n .filter((bucket) => bucket.placed.length > 0)\n}\n","/**\n * What I typed to name a pull request, once it is known which one that is.\n *\n * A number alone is what I actually type, so it resolves against the registered\n * repositories rather than being refused; where that cannot decide, the answer\n * says so instead of guessing a repository.\n */\nexport type Reference =\n | { readonly _tag: \"resolved\"; readonly repo: string; readonly number: number }\n | { readonly _tag: \"ambiguous\"; readonly repos: ReadonlyArray<string> }\n | { readonly _tag: \"unreadable\"; readonly text: string }\n\n/** `owner/name#12`, or `12` on its own. */\nconst spelled = /^(?:([^\\s/]+\\/[^\\s/]+)#)?(\\d+)$/\n\n/**\n * A segment of nothing but dots, which no repository is called.\n *\n * The repository names a directory under the state directory before it names\n * anything else, so `../x` would be a way out of it.\n */\nconst onlyDots = /^\\.+$/\n\n/**\n * The pull request a reference names.\n *\n * A reference that spells its repository out is taken as it is, registered or\n * not: reviewing someone else's pull request is a thing to ask for, and the\n * settings a repository nothing registered gets are the global defaults.\n */\nexport const resolve = (text: string, registered: ReadonlyArray<string>): Reference => {\n const found = spelled.exec(text)\n const number = found?.[2]\n if (number === undefined) {\n return { _tag: \"unreadable\", text }\n }\n\n const spelledRepo = found?.[1]\n if (spelledRepo !== undefined && spelledRepo.split(\"/\").some((segment) => onlyDots.test(segment))) {\n return { _tag: \"unreadable\", text }\n }\n\n const repo = spelledRepo ?? (registered.length === 1 ? registered[0] : undefined)\n if (repo === undefined) {\n return { _tag: \"ambiguous\", repos: registered }\n }\n return { _tag: \"resolved\", repo, number: Number(number) }\n}\n","import { Effect, Option } from \"effect\"\nimport { Argument, CliError } from \"effect/unstable/cli\"\n\nimport { beating } from \"#adapters/heartbeat.ts\"\nimport { prKey, storeFor } from \"#adapters/store.ts\"\nimport { Facts } from \"#domain/bucket.ts\"\nimport type { Reference } from \"#domain/reference.ts\"\nimport { resolve } from \"#domain/reference.ts\"\n\n/** The pull request a command acts on, named the way I actually type it. */\nexport const prArgument = Argument.String(\"pr\").pipe(\n Argument.withDescription(\"The pull request, as 28 or owner/name#28\")\n)\n\n/** What to say about a reference that named no one pull request. */\nconst whyNothingNamed = (reference: Exclude<Reference, { readonly _tag: \"resolved\" }>): string => {\n if (reference._tag === \"unreadable\") {\n return `'${reference.text}' is not a pull request. Name one as 28, or as owner/name#28.`\n }\n const example = `${reference.repos[0] ?? \"owner/name\"}#28`\n return reference.repos.length === 0\n ? `No repositories are registered, so a number alone names nothing. ` +\n `Run dw-mc init inside a repository, or name the pull request as ${example}.`\n : `${reference.repos.length} repositories are registered, so a number alone could be any of them. ` +\n `Name the pull request as ${example}.`\n}\n\n/** The pull request the argument names, or the sentence saying why it names none. */\nexport const named = (pr: string, registered: ReadonlyArray<string>) => {\n const reference = resolve(pr, registered)\n return reference._tag === \"resolved\"\n ? Effect.succeed(reference)\n : Effect.fail(new CliError.UserError({ cause: whyNothingNamed(reference) }))\n}\n\n/**\n * A domain guard's word, as the command's own failure.\n *\n * Every guard in the tool answers the same shape - the sentence saying why not,\n * or null - so turning that answer into a refusal is spelled once here rather\n * than beside each command that asks one.\n */\nexport const refuse = (why: string | null): Effect.Effect<void, CliError.UserError> =>\n why === null ? Effect.void : Effect.fail(new CliError.UserError({ cause: why }))\n\n/**\n * What the last sweep learned about one pull request, or the sentence sending\n * me to a sweep.\n *\n * A command that reads these rather than GitHub says what the table said: the\n * stamp and the cutoff a conversation is measured against are both computed\n * from the facts a sweep wrote down, and asking GitHub again would make them a\n * different answer from the one `dw-mc status` printed.\n *\n * Facts this version cannot read are facts another version of them wrote, and a\n * sweep can write them again, so both cases say the same thing.\n */\nexport const swept = Effect.fn(\"pr.swept\")(function* (repo: string, number: number) {\n const store = yield* storeFor(\"prs\", Facts)\n const facts = yield* Effect.orElseSucceed(store.get(prKey(repo, number)), () => Option.none<Facts>())\n if (Option.isNone(facts)) {\n return yield* new CliError.UserError({\n cause: `Nothing is known about ${repo}#${number} yet. Run dw-mc sweep first.`\n })\n }\n return facts.value\n})\n\n/**\n * The guard reads of one command, under a heartbeat.\n *\n * Every command that acts on a pull request reads its guards live rather than\n * off the last sweep, because each of them is about the pull request as it is\n * now. That read is a second or two against GitHub before a word can be\n * printed, and it used to be spent on a blank screen.\n *\n * There is nothing to count here - two or three calls, and a number counting to\n * three says less than the words do - so the line is what is being read and how\n * long it has taken. It gives the heartbeat no aside, so a piped command prints\n * what it always printed.\n */\nexport const reading = <A, E, R>(where: string, read: Effect.Effect<A, E, R>) =>\n beating(\n (since) => `reading ${where} · ${since}`,\n () => read\n )\n","import type { Paint } from \"#adapters/paint.ts\"\nimport { truncate } from \"#cli/table.ts\"\nimport type { Bucket, Placed } from \"#domain/bucket.ts\"\n\n/**\n * How one tracked PR is written down, wherever it is written down.\n *\n * The table `dw-mc status` prints and the list the picker asks me to choose\n * from are the same rows, so a pull request reads the same in both and neither\n * command owns how the other draws it.\n *\n * Colour here says one thing: which bucket the pull request is in, and so what\n * it waits on. Everything else on the row is either `dim`, because it is\n * context rather than state, or left alone. A row read with no colour at all\n * says the same, which is what the marker is for.\n *\n * On a table, the pull request opens itself: the reference carries the URL for\n * the terminal to follow, and nothing else on the row does. What it leads to is\n * where the row already says it is, so a row read where no link can be followed\n * - a pipe, a paste, a terminal that ignores the sequence - loses nothing.\n */\n\n/** The glossary's name for each bucket, which is what the heading says. */\nexport const heading: Record<Bucket, string> = {\n \"needs-me\": \"Needs me\",\n \"needs-review-run\": \"Needs review run\",\n \"waiting-on-others\": \"Waiting on others\",\n ready: \"Ready\"\n}\n\n/**\n * The mark that says which bucket a row is in without being read.\n *\n * One character apiece, from the part of Unicode a terminal font has: the\n * padding is counted in characters, and a glyph a terminal draws double width\n * takes a column the count never gave it. How full the mark looks tracks how\n * much of the pull request is done, so the column reads at a glance even where\n * the colour is off.\n */\nexport const marker: Record<Bucket, string> = {\n \"needs-me\": \"●\",\n \"needs-review-run\": \"◐\",\n \"waiting-on-others\": \"○\",\n ready: \"◆\"\n}\n\n/** The colour a bucket is said in: red is mine, yellow is next, green is done, dim is not my turn. */\nexport const tint = (paint: Paint, bucket: Bucket): ((text: string) => string) =>\n ({\n \"needs-me\": paint.red,\n \"needs-review-run\": paint.yellow,\n \"waiting-on-others\": paint.dim,\n ready: paint.green\n })[bucket]\n\n/** Long enough for a conventional-commit subject, short enough to keep a row on one line. */\nexport const titleWidth = 56\n\n/** What sits between two columns: three columns of prose run into one another without a rule. */\nexport const rule = \" │ \"\n\n/**\n * How the bucket is said on a row: glued to the pull request, or a column of\n * its own that names it too.\n *\n * A table has a heading over each bucket, so the mark alone is all a row there\n * needs. A prompt has no headings to group under, so the bucket is named on\n * every row of it.\n */\nexport type Lead = \"marker\" | \"named\"\n\n/**\n * One row: which pull request, what it is, and what it waits on.\n *\n * A stamp is a mark beside the pull request rather than a column of its own, so\n * a table where nothing is stamped is exactly the table it was before: the\n * stamp is a thing I look for, not a thing I read every row of.\n *\n * The title is the only cell with give in it, so how much room it gets is the\n * caller's to say: a table printed down the screen can afford a whole commit\n * subject, and a row inside a prompt has a column more to carry and a frame\n * around it.\n *\n * A named lead carries the colour for the whole row. It is the one place a\n * prompt's row is coloured, and it carries no link at all: a prompt counts the\n * lines it has to erase from the length of what it drew, escape sequences and\n * all, so every colour on a row costs the title characters it could have shown,\n * and a link costs it the whole URL. The table has no such arithmetic to keep\n * straight, so its rows say it in more than one place and open the pull request\n * besides.\n */\nexport const cells = (\n placed: Placed,\n stamped: boolean,\n room: number,\n paint: Paint,\n lead: Lead\n): ReadonlyArray<string> => {\n const { facts } = placed\n const { bucket } = placed.placement\n const say = tint(paint, bucket)\n const reference = `${facts.repo}#${facts.number}`\n const named = lead === \"named\"\n const pr = `${named ? reference : paint.link(reference, facts.url)}${\n facts.draft ? paint.dim(\" (draft)\") : \"\"\n }${stamped ? ` ${paint.green(\"✓\")}` : \"\"}`\n\n return named\n ? [say(`${marker[bucket]} ${heading[bucket]}`), pr, truncate(facts.title, room), placed.placement.reason]\n : [`${say(marker[bucket])} ${pr}`, paint.dim(truncate(facts.title, room)), say(placed.placement.reason)]\n}\n","import { Effect, Schema } from \"effect\"\n\nimport type { CheckEntry } from \"#adapters/gh.ts\"\nimport { GhReadFailed, readJson, unavailable } from \"#adapters/gh.ts\"\nimport { capture } from \"#adapters/spawner.ts\"\nimport type { ChecksState } from \"#terms/pr.ts\"\n\n/**\n * What GitHub says about a pull request's checks, and the evidence a red one\n * is classified on. Every read here goes through the same `gh` the rest of the\n * tool does; what it owns is the checks, not the boundary.\n */\n\nconst failing = new Set([\"FAILURE\", \"TIMED_OUT\", \"CANCELLED\", \"STARTUP_FAILURE\", \"ACTION_REQUIRED\", \"ERROR\"])\nconst running = new Set([\"QUEUED\", \"IN_PROGRESS\", \"WAITING\", \"PENDING\", \"REQUESTED\", \"EXPECTED\"])\n\nconst nameOf = (entry: CheckEntry): string => entry.name ?? entry.context ?? \"\"\n\nconst checksThatCount = (entries: ReadonlyArray<CheckEntry> | null, ignore: ReadonlyArray<string>) =>\n (entries ?? []).filter((entry) => !ignore.includes(nameOf(entry)))\n\nconst hasFailed = (entry: CheckEntry): boolean => failing.has(entry.conclusion ?? \"\") || failing.has(entry.state ?? \"\")\n\n/**\n * What the rollup comes to: red when anything failed, pending only while\n * nothing has failed yet, green when every check that counts has passed.\n *\n * `ci.ignore` names the checks that do not count towards green, so a check I\n * have decided to live with cannot hold a PR out of Ready.\n */\nexport const rollupState = (entries: ReadonlyArray<CheckEntry> | null, ignore: ReadonlyArray<string>): ChecksState => {\n const checks = checksThatCount(entries, ignore)\n if (checks.length === 0) {\n return \"none\"\n }\n if (checks.some(hasFailed)) {\n return \"red\"\n }\n if (\n checks.some(\n (entry) => (entry.status !== undefined && entry.status !== \"COMPLETED\") || running.has(entry.state ?? \"\")\n )\n ) {\n return \"pending\"\n }\n return \"green\"\n}\n\n/**\n * The checks that failed and count, which are the ones there is a log to read.\n *\n * `ci.ignore` is applied here as well as in the rollup: a check that cannot\n * hold a PR out of Ready is not one the classifier should be explaining either.\n */\nexport const failedChecks = (\n entries: ReadonlyArray<CheckEntry> | null,\n ignore: ReadonlyArray<string>\n): ReadonlyArray<CheckEntry> => checksThatCount(entries, ignore).filter(hasFailed)\n\n/** Where a check run reports: the workflow run it belongs to, and its job in it. */\nexport interface Reported {\n readonly run: string\n readonly job: string\n}\n\n/**\n * What a check reports on, out of the URL it reports at.\n *\n * A check run details URL ends `/actions/runs/<run>/job/<job>`. The job id is\n * what the logs endpoint takes and the run id is what `gh run rerun` takes, so\n * the two ids the tool needs are the two halves of one URL and are read\n * together. A commit status points somewhere else entirely, which is null:\n * there is no log of ours to read and no run of ours to re-run.\n */\nexport const reportedAt = (detailsUrl: string | undefined): Reported | null => {\n const found = detailsUrl?.match(/\\/actions\\/runs\\/(\\d+)\\/job\\/(\\d+)/)\n return found?.[1] === undefined || found[2] === undefined ? null : { run: found[1], job: found[2] }\n}\n\nconst RepoDefaultBranch = Schema.fromJsonString(\n Schema.Struct({ defaultBranchRef: Schema.NullOr(Schema.Struct({ name: Schema.String })) })\n)\n\n/**\n * The branch a repository merges into, which is the one the first flaky signal\n * asks about. An empty repository has none, and `main` is the better guess than\n * failing the sweep over it.\n */\nexport const defaultBranch = Effect.fnUntraced(function* (repo: string) {\n const view = yield* readJson(\n \"repo view defaultBranchRef\",\n \"gh\",\n [\"repo\", \"view\", repo, \"--json\", \"defaultBranchRef\"],\n RepoDefaultBranch\n )\n return view.defaultBranchRef?.name ?? \"main\"\n})\n\nconst Runs = Schema.fromJsonString(Schema.Array(Schema.Struct({ conclusion: Schema.String })))\n\n/** How far back to look for a run that reached a verdict at all. */\nconst recentRuns = 5\n\n/** `gh run list` reports a conclusion in lower case, unlike every check on a PR. */\nconst failedRun = new Set([\"failure\", \"timed_out\"])\n\n/** A run that decided something. A skipped or cancelled run says nothing either way. */\nconst verdicts = new Set([\"failure\", \"timed_out\", \"success\"])\n\n/**\n * Whether `workflow` is red on `branch` right now.\n *\n * The newest run that reached a verdict is the whole answer: a workflow that\n * broke last week and was fixed since is not red, and excusing a pull request\n * for it would hide a failure that is real. A handful of runs are asked for\n * because the newest ones are often skipped by a path filter.\n */\nexport const workflowFailsOn = Effect.fnUntraced(function* (repo: string, branch: string, workflow: string) {\n const runs = yield* readJson(\n \"run list\",\n \"gh\",\n [\n \"run\",\n \"list\",\n \"--repo\",\n repo,\n \"--branch\",\n branch,\n \"--workflow\",\n workflow,\n \"--limit\",\n String(recentRuns),\n \"--json\",\n \"conclusion\"\n ],\n Runs\n )\n // `gh run list` answers newest first.\n const newest = runs.find((run) => verdicts.has(run.conclusion))\n return newest !== undefined && failedRun.has(newest.conclusion)\n})\n\nconst PrFiles = Schema.fromJsonString(Schema.Struct({ files: Schema.Array(Schema.Struct({ path: Schema.String })) }))\n\n/** The repository paths a pull request changes. */\nexport const prFiles = Effect.fnUntraced(function* (repo: string, number: number) {\n const view = yield* readJson(\n \"pr view files\",\n \"gh\",\n [\"pr\", \"view\", String(number), \"--repo\", repo, \"--json\", \"files\"],\n PrFiles\n )\n return view.files.map((file) => file.path)\n})\n\n/**\n * How much of a failing job's log is kept.\n *\n * A job that failed prints what went wrong at the end, so the tail is the part\n * worth classifying, and a build that logged a whole dependency tree is not\n * worth holding in memory beyond it.\n */\nconst logTailBytes = 64 * 1024\n\n/**\n * What one failing job printed, from the end.\n *\n * `gh api` refuses a response carrying terminal escape sequences unless it is\n * told otherwise, and a runner log is full of them. Verified by running it: the\n * endpoint answers with the plain log once the flag is passed.\n */\nexport const jobLog = Effect.fnUntraced(function* (repo: string, jobId: string) {\n const log = yield* capture(\"gh\", [\n \"api\",\n `repos/${repo}/actions/jobs/${jobId}/logs`,\n \"--allow-escape-sequences\"\n ]).pipe(\n Effect.catchTags({\n PlatformError: (error) => Effect.fail(unavailable(error)),\n CommandFailed: (error) => Effect.fail(new GhReadFailed({ command: \"api job logs\", detail: error.stderr }))\n })\n )\n return log.length <= logTailBytes ? log : log.slice(-logTailBytes)\n})\n\n/**\n * The workflow runs behind the failing checks that count, each named once.\n *\n * One broken run usually fails several jobs, and re-running it once per failing\n * job would start the same run over and over.\n *\n * `ci.ignore` decides which checks get a run into this list, and no more than\n * that: a run is re-run whole, so an ignored job sharing a run with a counted\n * one is re-run beside it. What the setting buys is that an ignored check is\n * never on its own a reason to spend CI minutes.\n */\nexport const failedRuns = (\n entries: ReadonlyArray<CheckEntry> | null,\n ignore: ReadonlyArray<string>\n): ReadonlyArray<string> => [\n ...new Set(\n failedChecks(entries, ignore).flatMap((check) => {\n const reported = reportedAt(check.detailsUrl)\n return reported === null ? [] : [reported.run]\n })\n )\n]\n\n/**\n * Asks GitHub to run one workflow run's failed jobs again.\n *\n * `--failed` is what makes this cheap: the jobs that passed are not run a\n * second time, so a flaky job costs the minutes it costs and no more. This is a\n * write to GitHub, and it is one of the three ADR 0002 allows.\n */\nexport const rerunFailed = Effect.fnUntraced(function* (repo: string, runId: string) {\n yield* capture(\"gh\", [\"run\", \"rerun\", runId, \"--repo\", repo, \"--failed\"]).pipe(\n Effect.catchTags({\n PlatformError: (error) => Effect.fail(unavailable(error)),\n CommandFailed: (error) => Effect.fail(new GhReadFailed({ command: \"run rerun\", detail: error.stderr }))\n })\n )\n})\n","import { Effect } from \"effect\"\n\nimport { defaultBranch, failedChecks, jobLog, prFiles, reportedAt, workflowFailsOn } from \"#adapters/ci.ts\"\nimport type { CheckEntry } from \"#adapters/gh.ts\"\n\n/**\n * Everything the classifier is allowed to know about one red CI.\n *\n * All three are facts a sweep reads off GitHub, which is what keeps the verdict\n * reproducible: the same evidence always yields the same answer.\n */\nexport interface Evidence {\n /** The workflows failing here that are failing on the default branch as well. */\n readonly alsoRedOnDefaultBranch: ReadonlyArray<string>\n /** The files the pull request changes, as repository paths. */\n readonly changedFiles: ReadonlyArray<string>\n /** What the failing jobs printed. */\n readonly log: string\n}\n\n/** Whether a red CI is mine to fix. */\nexport type Classification = \"flaky\" | \"legitimate\"\n\n/** What the classifier decided, and the sentence that says why. */\nexport interface Verdict {\n readonly classification: Classification\n readonly reason: string\n}\n\n/**\n * The failures that are flaky wherever they appear: a machine, a network or a\n * runner giving up, never a test disagreeing with the code.\n *\n * `ci.flaky_patterns` adds to this list rather than replacing it, because the\n * failures a repository of mine produces are extra ones, not different ones.\n */\nexport const builtInPatterns: ReadonlyArray<string> = [\n \"timed out\",\n \"deadline exceeded\",\n \"ETIMEDOUT\",\n \"ECONNRESET\",\n \"ECONNREFUSED\",\n \"connection refused\",\n \"socket hang up\",\n \"lock timeout\",\n \"could not obtain lock\",\n \"runner lost communication\",\n \"The runner has received a shutdown signal\",\n \"net/http: request canceled\",\n \"ResourceExhausted\",\n \"Too many open files\",\n \"no space left on device\"\n]\n\nconst baseName = (path: string): string => path.slice(path.lastIndexOf(\"/\") + 1)\n\n/**\n * The changed file the log names, preferring one it spells in full.\n *\n * A bare file name is worth matching - a stack trace often prints nothing else\n * - and it is worth matching second, because a name as ordinary as `index.ts`\n * belongs to more repositories than mine.\n */\nconst escaped = (text: string): string => text.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\")\n\n/**\n * Whether the log names a file called `base` rather than some longer name\n * ending in it: a changed `src/a.ts` is not what a log printing `data.ts` is\n * complaining about.\n */\nconst namesFile = (log: string, base: string): boolean => new RegExp(`(^|[^\\\\w.-])${escaped(base)}`).test(log)\n\nconst namedChangedFile = (log: string, changedFiles: ReadonlyArray<string>): string | null =>\n changedFiles.find((file) => log.includes(file)) ?? changedFiles.find((file) => namesFile(log, baseName(file))) ?? null\n\n/**\n * The flaky pattern the log matches, mine before the built-in ones.\n *\n * A pattern is text and not a regular expression: it comes out of a\n * configuration file I edit by hand, where a stray `*` should cost me a missed\n * match and never a crash.\n */\nconst matchedPattern = (log: string, patterns: ReadonlyArray<string>): string | null => {\n const haystack = log.toLowerCase()\n return [...patterns, ...builtInPatterns].find((pattern) => haystack.includes(pattern.toLowerCase())) ?? null\n}\n\n/**\n * Whether a red CI is mine to fix, and why.\n *\n * Two of the signals say flaky and one says legitimate, and the one outranks\n * the two: a log that names a file this pull request changes is the failure\n * pointing at my own work, and a workflow that is broken everywhere does not\n * stop it pointing there.\n *\n * Everything else that is unexplained is mine as well. The two mistakes do not\n * cost the same - a real failure called flaky is a broken pull request nobody\n * tells me about, while a flake called mine costs me one look - so the default\n * is the one I can recover from.\n */\nexport const classify = (evidence: Evidence, flakyPatterns: ReadonlyArray<string>): Verdict => {\n const named = namedChangedFile(evidence.log, evidence.changedFiles)\n if (named !== null) {\n return { classification: \"legitimate\", reason: `the log names ${named}, which this PR changes` }\n }\n\n const redOnDefaultBranch = evidence.alsoRedOnDefaultBranch[0]\n const pattern = matchedPattern(evidence.log, flakyPatterns)\n const excuses = [\n redOnDefaultBranch === undefined ? null : `${redOnDefaultBranch} is red on the default branch too`,\n pattern === null ? null : `the log matches \"${pattern}\"`\n ].filter((it) => it !== null)\n\n return excuses.length === 0\n ? { classification: \"legitimate\", reason: \"nothing explains the failure\" }\n : { classification: \"flaky\", reason: excuses.join(\", and \") }\n}\n\n/**\n * How many failing jobs the log is read from.\n *\n * One broken workflow usually fails several jobs with the same cause, and the\n * logs are the one read here that is measured in megabytes.\n */\nconst loggedJobs = 3\n\n/** The values of `xs` that `f` has one for. */\nconst filterMap = <A, B>(xs: ReadonlyArray<A>, f: (a: A) => B | null): ReadonlyArray<B> =>\n xs.flatMap((x) => {\n const b = f(x)\n return b === null ? [] : [b]\n })\n\n/** No evidence at all, which is what an unreadable CI comes to. */\nconst nothing: Evidence = { alsoRedOnDefaultBranch: [], changedFiles: [], log: \"\" }\n\n/**\n * What a red CI looks like to the classifier.\n *\n * A read that fails costs its own signal and nothing else. GitHub drops an\n * Actions log after ninety days, so a pull request open that long would\n * otherwise lose its row over a log nobody can fetch any more - and a missing\n * signal only ever moves the verdict towards legitimate, which is the answer\n * that puts the pull request in front of me rather than hiding it.\n */\nexport const evidenceFor = Effect.fn(\"flaky.evidenceFor\")(function* (\n repo: string,\n number: number,\n entries: ReadonlyArray<CheckEntry> | null,\n ignore: ReadonlyArray<string>\n) {\n const failed = failedChecks(entries, ignore)\n const workflows = [...new Set(filterMap(failed, (check) => check.workflowName ?? null))]\n const jobs = filterMap(failed, (check) => reportedAt(check.detailsUrl)?.job ?? null).slice(0, loggedJobs)\n\n const branch = yield* Effect.orElseSucceed(defaultBranch(repo), () => null)\n if (branch === null) {\n return nothing\n }\n\n const [alsoRed, changedFiles, logs] = yield* Effect.all(\n [\n Effect.forEach(workflows, (workflow) =>\n Effect.map(\n Effect.orElseSucceed(workflowFailsOn(repo, branch, workflow), () => false),\n (red) => (red ? [workflow] : [])\n )\n ),\n Effect.orElseSucceed(prFiles(repo, number), (): ReadonlyArray<string> => []),\n Effect.forEach(jobs, (job) => Effect.orElseSucceed(jobLog(repo, job), () => \"\"))\n ],\n { concurrency: 3 }\n )\n\n return { alsoRedOnDefaultBranch: alsoRed.flat(), changedFiles, log: logs.join(\"\\n\") } satisfies Evidence\n})\n\n/**\n * Why a red CI is excused, or null where it is mine to fix.\n *\n * Reading the evidence and classifying it is one act, so it is one function:\n * a sweep writes what it returns down as `ciFlaky`, and `dw-mc rerun` asks it\n * again live. Two callers asking the same question have to get the same answer,\n * which they cannot if each of them spells the question out.\n */\nexport const flakyReason = Effect.fn(\"flaky.flakyReason\")(function* (\n repo: string,\n number: number,\n entries: ReadonlyArray<CheckEntry> | null,\n ignore: ReadonlyArray<string>,\n patterns: ReadonlyArray<string>\n) {\n const verdict = classify(yield* evidenceFor(repo, number, entries, ignore), patterns)\n return verdict.classification === \"flaky\" ? verdict.reason : null\n})\n","import type { Facts } from \"#domain/bucket.ts\"\nimport type { Moment } from \"#domain/moment.ts\"\nimport { isSame } from \"#domain/moment.ts\"\nimport type { ChecksState } from \"#terms/pr.ts\"\n\n/**\n * The three signals that say whether a tracked PR has moved at all.\n *\n * They are the cheap facts: a sweep can read them without paging through a PR's\n * history, which is the whole point of comparing them.\n */\nexport interface Pulse {\n readonly head: string\n readonly checks: ChecksState\n readonly newestHumanCommentAt: Moment\n}\n\n/** The pulse of a PR a previous sweep recorded. */\nexport const pulseOf = (facts: Facts): Pulse => ({\n head: facts.head,\n checks: facts.checks,\n newestHumanCommentAt: facts.newestHumanCommentAt\n})\n\n/**\n * Whether a PR is where the last sweep left it.\n *\n * A quiet PR keeps the facts it already had rather than being read out again,\n * so a sweep over many pull requests spends its time on the few that moved.\n */\nexport const isQuiet = (previous: Pulse, current: Pulse): boolean =>\n previous.head === current.head &&\n previous.checks === current.checks &&\n isSame(previous.newestHumanCommentAt, current.newestHumanCommentAt)\n","import { Effect, Option, Schema } from \"effect\"\n\nimport { prKey, storeFor } from \"#adapters/store.ts\"\nimport type { ChecksState } from \"#terms/pr.ts\"\n\n/** One open pull request as a stack is read from: the branch it stands on and the one it merges into. */\nexport interface Branches {\n readonly number: number\n readonly head: string\n readonly base: string\n}\n\n/** Where a pull request sits among the pull requests built on each other. */\nexport interface Position {\n readonly position: number\n readonly length: number\n}\n\n/**\n * How many pull requests this one stands on.\n *\n * A branch is walked to what it merges into and on from there, however deep the\n * stack goes. Every pull request the walk has already counted is left alone,\n * which is what keeps two branches that merge into each other from being walked\n * around forever.\n */\nconst ancestorsOf = (pr: Branches, open: ReadonlyArray<Branches>, seen: Set<number>): number => {\n let count = 0\n let current = pr\n for (;;) {\n const parent = open.find((it) => it.head === current.base && !seen.has(it.number))\n if (parent === undefined) {\n return count\n }\n seen.add(parent.number)\n count += 1\n current = parent\n }\n}\n\n/**\n * How deep the stack goes above this pull request.\n *\n * Two branches cut from the same one are not two stacks deep, they are two\n * branches, so what counts is the deepest single line of them rather than how\n * many pull requests stand above it in total.\n */\nconst descendantsOf = (pr: Branches, open: ReadonlyArray<Branches>, seen: Set<number>): number => {\n let deepest = 0\n for (const child of open.filter((it) => it.base === pr.head && !seen.has(it.number))) {\n seen.add(child.number)\n deepest = Math.max(deepest, 1 + descendantsOf(child, open, seen))\n }\n return deepest\n}\n\n/**\n * Where a pull request sits in its stack, or null where it is in none.\n *\n * A stack is read off the branches alone: a pull request that merges into\n * another pull request's branch, or that another one merges into, is part of\n * one. The tool does not understand stacks and never drives them, so this\n * exists to recognise one and say where the pull request sits in it.\n */\nexport const stackOf = (number: number, open: ReadonlyArray<Branches>): Position | null => {\n const pr = open.find((it) => it.number === number)\n if (pr === undefined) {\n return null\n }\n const seen = new Set([number])\n const below = ancestorsOf(pr, open, seen)\n const above = descendantsOf(pr, open, seen)\n return below === 0 && above === 0 ? null : { position: below + 1, length: below + above + 1 }\n}\n\n/** What every guard about the branch itself reads, whatever is about to be done to it. */\nexport interface Branch {\n readonly repo: string\n readonly number: number\n /** Whether I opened the pull request, which is the only kind whose branch is mine to push. */\n readonly mine: boolean\n /** Whether the head branch lives in a fork rather than in the repository that was read. */\n readonly fromFork: boolean\n /** Whether the pull request was among the open ones the stack was read from. */\n readonly listed: boolean\n readonly stack: Position | null\n}\n\n/**\n * Why this branch is nobody's to touch here, or null where it is mine.\n *\n * These are the guards about the branch rather than about what is done to it,\n * which is why they are their own and why they say nothing about pushing: who\n * authored the pull request and where its branch lives is the boundary itself -\n * a branch somebody else authored and a branch in a fork are not mine to work\n * on, whatever else is true of them and whichever command asks. A stack comes\n * next, and a pull request the stack was not read from counts as one, because a\n * stack the tool cannot see is one it could drive: the tool does not understand\n * stacks, so the one thing it has to say about one is where the pull request\n * sits in it.\n */\nexport const boundary = (branch: Branch): string | null => {\n const where = `${branch.repo}#${branch.number}`\n if (!branch.mine) {\n return `${where} is not mine. dw-mc works on branches I author and on nothing else.`\n }\n if (branch.fromFork) {\n return (\n `${where} is opened from a fork, so its branch is not in ${branch.repo}. ` +\n `dw-mc works only on a branch in the repository it read.`\n )\n }\n if (!branch.listed) {\n return (\n `${where} was not among the open pull requests of ${branch.repo}, so nothing here can say whether ` +\n `it is in a stack. Read it again before touching the branch.`\n )\n }\n if (branch.stack !== null) {\n return (\n `${where} is ${branch.stack.position} of ${branch.stack.length} in a stack. ` +\n `dw-mc does not understand stacks and will not drive one; rebase it with whatever built the stack.`\n )\n }\n return null\n}\n\n/** Everything the rebase guards are allowed to know about a pull request. */\nexport interface Situation extends Branch {\n /** The branch the pull request merges into, which is what it would be rebased onto. */\n readonly base: string\n readonly enabled: boolean\n readonly checks: ChecksState\n}\n\n/**\n * Why this branch is not one to rebase, or null where it is.\n *\n * This is the single place the guards live, and they matter more than the\n * rebase itself: a force push is the one write the tool makes that can lose\n * work, and every rule here is about it never being a surprise.\n *\n * Being off is said first, because a repository that has not turned rebase on\n * has decided the question and nothing else about the pull request changes it.\n * The branch's own guards come next. CI is last and costs the most to get\n * wrong - rebasing while a run is in flight cancels the run I am waiting on,\n * and a red build is mine to fix where it is.\n */\nexport const decide = (situation: Situation): string | null => {\n const where = `${situation.repo}#${situation.number}`\n if (!situation.enabled) {\n return (\n `Rebase is off for ${situation.repo}. Set rebase.enabled: true for it in the config to turn it on, ` +\n `so a force push is never a surprise.`\n )\n }\n const refused = boundary(situation)\n if (refused !== null) {\n return refused\n }\n if (situation.checks === \"pending\") {\n return `CI is still running on ${where}. A rebase now would cancel the run you are waiting on.`\n }\n if (situation.checks === \"red\") {\n return `CI is red on ${where}, which is yours to fix before the branch moves.`\n }\n return null\n}\n\n/**\n * A rebase that conflicted: the head it conflicted at and the files it stopped\n * on.\n *\n * The head is what the record is scoped to, as it is for a withdrawn stamp: a\n * conflict is about the code the branch is at, so it lasts exactly as long as\n * that code is what the pull request is. A branch that moved is a branch\n * nothing here has tried to rebase yet.\n *\n * The paths are what makes the conflict something to open: `a rebase\n * conflicted` cannot tell a stale lockfile from half the pull request. They are\n * an optional key rather than a required one so a record an older version wrote\n * still reads, and a conflict with no paths still puts the pull request in\n * Needs me.\n */\nexport const Conflict = Schema.Struct({\n head: Schema.String,\n paths: Schema.optionalKey(Schema.Array(Schema.String))\n})\nexport type Conflict = typeof Conflict.Type\n\n/**\n * The conflict a rebase last left on this pull request, or null where it left\n * none.\n *\n * A record this version cannot read is one another version of it wrote, and a\n * conflict is worth a bucket rather than a failed sweep: forgetting it costs\n * the pull request one reason to be in Needs me, where failing here would cost\n * me the whole table.\n */\nexport const conflictFor = Effect.fn(\"rebase.conflictFor\")(function* (repo: string, number: number) {\n const store = yield* storeFor(\"rebases\", Conflict)\n const conflict = yield* Effect.orElseSucceed(store.get(prKey(repo, number)), () => Option.none<Conflict>())\n return Option.getOrNull(conflict)\n})\n\n/** Writes down that a rebase of `head` conflicted on `paths`, which is the only head it holds for. */\nexport const recordConflict = Effect.fn(\"rebase.recordConflict\")(function* (\n repo: string,\n number: number,\n head: string,\n paths: ReadonlyArray<string>\n) {\n const store = yield* storeFor(\"rebases\", Conflict)\n yield* store.set(prKey(repo, number), { head, paths })\n})\n","import { Schema, SchemaRepresentation, SchemaTransformation } from \"effect\"\n\nimport { Severity } from \"#terms/review.ts\"\n\n/** Whether a review run found anything at all. */\nexport const Verdict = Schema.Literals([\"clean\", \"findings\"])\nexport type Verdict = typeof Verdict.Type\n\n/**\n * Every severity word a review may answer with.\n *\n * The first three are ours, and the only ones a run is asked for. The rest are\n * the persona a run with no slash command carries, which grades in its own\n * words: a turn that comes back in them is worth reading rather than throwing\n * away.\n */\nconst Spelling = Schema.Literals([\"error\", \"warning\", \"info\", \"Critical\", \"Required\", \"Optional\", \"Nit\", \"FYI\"])\n\n/** What each of those words weighs. The record is exhaustive, so neither list can drift. */\nconst severityOf: Record<typeof Spelling.Type, Severity> = {\n error: \"error\",\n warning: \"warning\",\n info: \"info\",\n Critical: \"error\",\n Required: \"error\",\n Optional: \"warning\",\n Nit: \"info\",\n FYI: \"info\"\n}\n\nconst Weighed = Spelling.pipe(\n Schema.decodeTo(\n Severity,\n SchemaTransformation.transform({\n decode: (word: typeof Spelling.Type) => severityOf[word],\n encode: (severity: Severity): typeof Spelling.Type => severity\n })\n )\n)\n\n/** The fields both spellings of a finding share. Only the severity differs. */\nconst shared = { file: Schema.String, line: Schema.Int, summary: Schema.String }\n\n/** One problem a review run reports, at a file and line. */\nexport const Finding = Schema.Struct({ ...shared, severity: Severity })\nexport type Finding = typeof Finding.Type\n\n/**\n * What a review run found: the shape the tool keeps, and the one a fix session\n * is later handed.\n */\nexport const Findings = Schema.Struct({\n verdict: Verdict,\n findings: Schema.Array(Finding)\n})\nexport type Findings = typeof Findings.Type\n\n/**\n * The same findings as a runner may spell them, which is what the second turn's\n * output is read with.\n *\n * A word nothing maps fails here, and a failed read is a failure of the run:\n * findings the tool cannot weigh are not findings it can act on.\n */\nexport const Reported = Schema.Struct({\n verdict: Verdict,\n findings: Schema.Array(Schema.Struct({ ...shared, severity: Weighed }))\n})\n\n/**\n * The schema every runner must satisfy, as the JSON Schema a runner is handed.\n *\n * It is derived from the schema the findings are kept under rather than written\n * out beside it, so a runner is asked for exactly the shape that is persisted.\n * `Reported` is wider on purpose and only on the severity: what a runner is\n * asked for is our three words, and a persona's five are read where they arrive\n * anyway rather than being asked for.\n */\nexport const jsonSchema: string = JSON.stringify(\n SchemaRepresentation.toJsonSchemaDocument(SchemaRepresentation.toRepresentation(Findings.ast)).schema\n)\n\n/**\n * The findings as the Markdown a report is written in.\n *\n * It is what a schema-held run's report says: with a schema in force a run\n * answers in findings and not in prose, so the report kept beside it is written\n * from the findings themselves rather than left empty.\n */\nexport const asMarkdown = (found: Findings): string =>\n found.findings.length === 0\n ? \"Clean: the run found nothing to report.\"\n : found.findings\n .map((finding) => `- \\`${finding.file}:${finding.line}\\` ${finding.severity}: ${finding.summary}`)\n .join(\"\\n\")\n\n/** Where each severity sits against the others, so the bar can be compared with it. */\nconst rank: Record<Severity, number> = { info: 0, warning: 1, error: 2 }\n\n/**\n * The findings that withhold the stamp: everything at `blocksOn` or above it.\n *\n * `stamp.blocks_on` is my bar rather than a constant, so a repository whose\n * warnings I do not want to merge past is configured rather than coded. An\n * error blocks wherever the bar is, because nothing weighs more than one.\n */\nexport const blocking = (findings: ReadonlyArray<Finding>, blocksOn: Severity): ReadonlyArray<Finding> =>\n findings.filter((finding) => rank[finding.severity] >= rank[blocksOn])\n","// `Path` from `effect` joins and resolves paths and has no glob matcher. This is\n// the platform's own, and matching a `docs_only` glob against a repository path\n// reads nothing and decides nothing about this machine.\n// oxlint-disable-next-line effecttsgo/node-builtin-import\nimport { matchesGlob } from \"node:path\"\n\nimport { DateTime, Effect, Option, Schema } from \"effect\"\n\nimport { prKey, storeFor } from \"#adapters/store.ts\"\nimport type { Findings } from \"#domain/findings.ts\"\nimport { blocking, Finding, Verdict } from \"#domain/findings.ts\"\nimport type { Severity } from \"#terms/review.ts\"\nimport { Effort } from \"#terms/review.ts\"\n\n/**\n * What a review run came to, which is what its second turn reported.\n *\n * A failure is recorded as one and is never a clean verdict: a turn that exited\n * badly, ran out of patience or answered in a shape that does not validate has\n * found nothing, which is not the same as having found nothing wrong.\n */\nexport const Outcome = Schema.Union([\n Schema.TaggedStruct(\"reported\", { verdict: Verdict, findings: Schema.Array(Finding) }),\n Schema.TaggedStruct(\"failed\", { detail: Schema.String })\n])\nexport type Outcome = typeof Outcome.Type\n\n/**\n * One review run against a tracked PR at a specific head commit.\n *\n * It is a schema because a review run outlives the command that started it: the\n * state directory is where the next sweep learns that this head has been\n * reviewed, and where a fix session finds what there is to fix.\n */\nexport const ReviewRun = Schema.Struct({\n repo: Schema.String,\n number: Schema.Int,\n /** The head the run covers. A run never vouches for code it did not see. */\n head: Schema.String,\n /**\n * The slash command line the run opened on, or null where it opened on the\n * tool's own prompt. A report found months later says what it was asked, and a\n * record an earlier version wrote carries no such field and is forgotten.\n */\n command: Schema.NullOr(Schema.String),\n effort: Schema.NullOr(Effort),\n /**\n * The agent session the run happened in, or null where it never reached one.\n *\n * A run that would not start or exited before it said anything has no session,\n * and the run is still recorded: a failure is recorded as what it is.\n */\n sessionId: Schema.NullOr(Schema.String),\n ranAt: Schema.DateTimeUtcFromString,\n outcome: Outcome\n})\nexport type ReviewRun = typeof ReviewRun.Type\n\n/** A head as it is read out loud: the seven characters git itself abbreviates to. */\nexport const short = (head: string): string => head.slice(0, 7)\n\n/**\n * Where a run is kept: one key per head, so a run and the code it read cannot\n * drift apart, and a re-review replaces the run before it.\n */\nexport const runKey = (repo: string, number: number, head: string): string => `${prKey(repo, number)}@${head}`\n\n/** Where the run's report is kept: beside the run, as the Markdown it is. */\nexport const reportKey = (repo: string, number: number, head: string): string => `${runKey(repo, number, head)}.md`\n\n/**\n * Which head a pull request was last reviewed at: an index beside `runKey` and\n * `reportKey` rather than a thing the glossary names.\n *\n * A run is kept under the head it read, which answers the question a sweep asks\n * of one head. The re-run rule and `dw-mc findings` ask the other one - which\n * head the last run was at - and this is where they read it, so neither has to\n * ask GitHub what is current before it can look anything up.\n */\nexport const LastReviewed = Schema.Struct({ head: Schema.String })\nexport type LastReviewed = typeof LastReviewed.Type\n\n/** Where that head is kept. No head is spelled `latest`, so nothing collides. */\nexport const latestKey = (repo: string, number: number): string => `${prKey(repo, number)}@latest`\n\n/**\n * The run at one head, or none where nothing has reviewed it.\n *\n * A head is where the question is asked - the stamp, the bucket and `dw-mc\n * findings` all ask about one commit - and one read off the disk answers it\n * without an index to keep in step.\n *\n * A run this version cannot read is a run another version of this record wrote,\n * and the state directory is a cache of work that can be done again: forgetting\n * it costs one review, where failing here would cost me the command I asked for.\n */\nexport const runAt = Effect.fn(\"review.runAt\")(function* (repo: string, number: number, head: string) {\n const runs = yield* storeFor(\"runs\", ReviewRun)\n return yield* Effect.orElseSucceed(runs.get(runKey(repo, number, head)), () => Option.none<ReviewRun>())\n})\n\n/** The last review run on a pull request, or none where it has had none. */\nexport const lastRun = Effect.fn(\"review.lastRun\")(function* (repo: string, number: number) {\n const heads = yield* storeFor(\"runs\", LastReviewed)\n const at = yield* Effect.orElseSucceed(heads.get(latestKey(repo, number)), () => Option.none<LastReviewed>())\n return Option.isNone(at) ? Option.none<ReviewRun>() : yield* runAt(repo, number, at.value.head)\n})\n\n/**\n * What a run reported, or null where it reported nothing at all.\n *\n * A failure is not a clean verdict: a run that could not report has found\n * nothing, which is not the same as having found nothing wrong. Everything that\n * reads a run's findings reads them through here, so the distinction is drawn\n * once rather than at every caller that might forget it.\n */\nexport const reportedBy = (run: ReviewRun): Findings | null =>\n run.outcome._tag === \"reported\" ? { verdict: run.outcome.verdict, findings: run.outcome.findings } : null\n\n/**\n * Why a run reported nothing, or null where it reported.\n *\n * The sibling of `reportedBy`, and here for the same reason: the two halves of\n * an outcome are read through one place each rather than re-narrowed at every\n * caller.\n */\nexport const detailOf = (run: ReviewRun): string | null => (run.outcome._tag === \"failed\" ? run.outcome.detail : null)\n\n/**\n * Whether the files changed since the last run are worth paying for another.\n *\n * The question is deliberately about what changed rather than how much: one\n * line outside the `docs_only` globs is code nobody has reviewed, and a\n * thousand lines inside them are still prose.\n */\nexport const worthRerunning = (changed: ReadonlyArray<string>, docsOnly: ReadonlyArray<string>): boolean =>\n changed.some((file) => !docsOnly.some((glob) => matchesGlob(file, glob)))\n\n/** Everything the re-run rule is allowed to know about the run that was asked for. */\nexport interface Asked {\n /** The last run on this pull request, or null where it has had none. */\n readonly last: ReviewRun | null\n /** The head the run would cover. */\n readonly head: string\n /** What changed since the last run's head, or null where GitHub would not say. */\n readonly changed: ReadonlyArray<string> | null\n}\n\n/**\n * The re-run rule: the head this run is skipped against, or null where it runs.\n *\n * A review costs real money and minutes of my attention, and a typo fix is not\n * worth either. Four things are never skipped, because the rule is here to save\n * me a review and not to stand between me and one I asked for: a pull request\n * with no run behind it, a run that reported nothing, a comparison GitHub would\n * not answer, and anything that changed outside the globs. A head that has\n * already had a run changed nothing at all, which is the one case that needs no\n * comparison to decide.\n */\nexport const skippedSince = (asked: Asked, docsOnly: ReadonlyArray<string>): string | null => {\n if (asked.last === null || reportedBy(asked.last) === null) {\n return null\n }\n const changed = asked.last.head === asked.head ? [] : asked.changed\n return changed === null || worthRerunning(changed, docsOnly) ? null : asked.last.head\n}\n\n/** What a run was opened on, as the report says it. */\nexport const askedOf = (run: ReviewRun): string =>\n run.command === null ? \"the tool's own prompt\" : [run.command, run.effort].filter((part) => part !== null).join(\" \")\n\n/**\n * The report as it is written down: what it is of, then what the run said.\n *\n * The heading is the whole point of writing it rather than storing the prose\n * alone - a file found months later says which pull request, which commit and\n * what the run was asked, without anything else having to be open.\n */\nexport const reportDocument = (run: ReviewRun, title: string, prose: string): string =>\n [\n `# ${run.repo}#${run.number} ${title}`,\n \"\",\n `- head: ${run.head}`,\n `- run: ${askedOf(run)}`,\n `- ran: ${DateTime.formatIso(run.ranAt)}`,\n \"\",\n prose.trim(),\n \"\"\n ].join(\"\\n\")\n\n/**\n * Whether `head` has the review it needs.\n *\n * A run that reported nothing does not count, which is the same rule\n * `reportedBy` draws everywhere else: a failure has found nothing, not found\n * nothing wrong.\n */\nexport const reviewedBy = (run: ReviewRun | null): boolean => run !== null && reportedBy(run) !== null\n\n/** The findings at one head that withhold the stamp. */\nexport const blockingIn = (run: ReviewRun | null, blocksOn: Severity): ReadonlyArray<Finding> => {\n const found = run === null ? null : reportedBy(run)\n return found === null ? [] : blocking(found.findings, blocksOn)\n}\n\n/** What the stamp rule reads out of the review runs this machine holds on one head. */\nexport interface Reviewed {\n /** The head a review run has already covered, or null where none has. */\n readonly reviewRunHead: string | null\n /** Findings on that head that withhold the stamp, at the bar `stamp.blocks_on` sets. */\n readonly blockingFindings: number\n}\n\n/**\n * What the review runs on `head` say about it, for the stamp to rest on.\n *\n * Whether a head has been reviewed is the runs' to say and no sweep's: a run is\n * recorded against one head, and a head with no run of its own has not been\n * reviewed however many sweeps have seen the pull request. A run that could not\n * report findings does not count either: its verdict is what takes a pull\n * request out of Needs review run, and it reached none.\n *\n * It is one function because the two callers are a sweep and `dw-mc merge`, and\n * the second exists to land what the first only describes: two spellings of\n * this would be two answers to whether a head has been reviewed.\n */\nexport const reviewedAt = Effect.fn(\"review.reviewedAt\")(function* (\n repo: string,\n number: number,\n head: string,\n blocksOn: Severity\n) {\n const run = Option.getOrNull(yield* runAt(repo, number, head))\n return {\n reviewRunHead: reviewedBy(run) ? head : null,\n blockingFindings: blockingIn(run, blocksOn).length\n } satisfies Reviewed\n})\n","import type { DateTime } from \"effect\"\nimport { Console, Effect, Option } from \"effect\"\nimport { CliError, Command } from \"effect/unstable/cli\"\nimport type { KeyValueStore } from \"effect/unstable/persistence\"\n\nimport { rollupState } from \"#adapters/ci.ts\"\nimport type { ConfigFile, Settings } from \"#adapters/config.ts\"\nimport { read as readConfig, settingsFor } from \"#adapters/config.ts\"\nimport type { Comment, Found } from \"#adapters/gh.ts\"\nimport {\n mergeabilityOf,\n prComments,\n prCommits,\n prReviews,\n prView,\n reviewDecisionOf,\n searchPrs,\n viewer\n} from \"#adapters/gh.ts\"\nimport type { Reads } from \"#adapters/heartbeat.ts\"\nimport { beating } from \"#adapters/heartbeat.ts\"\nimport { prKey, storeFor } from \"#adapters/store.ts\"\nimport { count } from \"#cli/table.ts\"\nimport type { Facts } from \"#domain/bucket.ts\"\nimport { Facts as FactsSchema } from \"#domain/bucket.ts\"\nimport { flakyReason } from \"#domain/flaky.ts\"\nimport { newest } from \"#domain/moment.ts\"\nimport { isQuiet, pulseOf } from \"#domain/quiet.ts\"\nimport { conflictFor } from \"#domain/rebase.ts\"\nimport { reviewedAt } from \"#domain/review.ts\"\n\n/** Something a sweep could not read, and what GitHub said about it. */\nexport interface Trouble {\n readonly where: string\n readonly detail: string\n}\n\n/** What one pass over every tracked PR came back with. */\nexport interface Report {\n readonly repos: ReadonlyArray<string>\n readonly facts: ReadonlyArray<Facts>\n readonly troubles: ReadonlyArray<Trouble>\n}\n\ntype Store = KeyValueStore.SchemaStore<typeof FactsSchema>\n\nconst writtenBy = (comments: ReadonlyArray<Comment>, login: string): ReadonlyArray<DateTime.Utc> =>\n comments.filter((comment) => comment.login === login).map((comment) => comment.at)\n\nconst byHumansOtherThan = (comments: ReadonlyArray<Comment>, login: string): ReadonlyArray<DateTime.Utc> =>\n comments.filter((comment) => !comment.bot && comment.login !== login).map((comment) => comment.at)\n\n/**\n * The facts about one tracked PR, read from GitHub and kept on disk.\n *\n * The cheap reads happen every time, because they are what says whether the PR\n * moved. The commits are asked for only when it did: `gh` returns every commit\n * message in full, and on a PR that is where the last sweep left it that whole\n * read buys a timestamp the state directory already has.\n */\nconst sweepPr = Effect.fn(\"sweep.pullRequest\")(function* (store: Store, me: string, found: Found, settings: Settings) {\n const view = yield* prView(found.repo, found.number)\n const [onThePr, inReviews] = yield* Effect.all(\n [prComments(found.repo, found.number), prReviews(found.repo, found.number)],\n { concurrency: 2 }\n )\n const comments = [...onThePr, ...inReviews]\n\n const checks = rollupState(view.statusCheckRollup, settings.ci.ignore)\n const newestHumanCommentAt = newest(byHumansOtherThan(comments, me))\n\n const key = prKey(found.repo, found.number)\n // State this version cannot read is state from another version of these\n // facts, and these facts are a cache of GitHub: reading them again costs a\n // sweep some calls, where failing here would cost the PR its row for good.\n const previous = Option.getOrUndefined(yield* Effect.orElseSucceed(store.get(key), () => Option.none<Facts>()))\n const reviewed = yield* reviewedAt(found.repo, found.number, view.headRefOid, settings.stamp.blocks_on)\n const quiet =\n previous !== undefined && isQuiet(pulseOf(previous), { head: view.headRefOid, checks, newestHumanCommentAt })\n ? previous\n : undefined\n\n const myLastCommitAt =\n quiet !== undefined\n ? quiet.myLastCommitAt\n : newest(\n (yield* prCommits(found.repo, found.number))\n .filter((commit) => commit.logins.includes(me))\n .map((commit) => commit.at)\n )\n\n // A red CI is classified once per state of the PR: while it sits where the\n // last sweep left it, the verdict it earned there still stands.\n const ciFlaky =\n checks !== \"red\"\n ? null\n : quiet !== undefined\n ? quiet.ciFlaky\n : yield* flakyReason(\n found.repo,\n found.number,\n view.statusCheckRollup,\n settings.ci.ignore,\n settings.ci.flaky_patterns\n )\n\n const rebaseConflictAt = yield* Effect.map(conflictFor(found.repo, found.number), (it) => it?.head ?? null)\n\n const facts: Facts = {\n repo: found.repo,\n number: found.number,\n title: view.title,\n url: view.url,\n draft: view.isDraft,\n head: view.headRefOid,\n mergeable: mergeabilityOf(view.mergeable),\n reviewDecision: reviewDecisionOf(view.reviewDecision),\n checks,\n ciFlaky,\n rebaseConflictAt,\n newestHumanCommentAt,\n myLastCommentAt: newest(writtenBy(comments, me)),\n myLastCommitAt,\n ...reviewed\n }\n\n yield* store.set(key, facts)\n return facts\n})\n\ntype Attempt<A> = { readonly got: ReadonlyArray<A>; readonly troubles: ReadonlyArray<Trouble> }\n\n/** A read that came back, or the trouble it came back with instead. */\nconst attempt = <A, E extends { readonly message: string }, R>(\n where: string,\n read: Effect.Effect<ReadonlyArray<A>, E, R>\n): Effect.Effect<Attempt<A>, never, R> =>\n read.pipe(\n Effect.map((got): Attempt<A> => ({ got, troubles: [] })),\n Effect.catch((error) => Effect.succeed<Attempt<A>>({ got: [], troubles: [{ where, detail: error.message }] }))\n )\n\nconst gather = <A>(attempts: ReadonlyArray<Attempt<A>>): Attempt<A> => ({\n got: attempts.flatMap((it) => it.got),\n troubles: attempts.flatMap((it) => it.troubles)\n})\n\n/** How many reads of GitHub are in flight at once. */\nconst concurrency = 4\n\n/**\n * How far a sweep has got, which is what the heartbeat of a sweep counts.\n *\n * The two stages are told apart because the second has a total the first cannot\n * know: how many pull requests there are to read is what the searches answer,\n * so counting towards it before they come back would count towards a number\n * made up.\n */\nexport type Swept =\n | { readonly _tag: \"searching\"; readonly done: number; readonly of: number }\n | { readonly _tag: \"reading\"; readonly done: number; readonly of: number }\n\n/** `n` repositories, which `count` cannot say: the plural is not the noun plus s. */\nconst repositories = (n: number): string => (n === 1 ? \"1 repository\" : `${n} repositories`)\n\n/** How the heartbeat of a sweep reads, wherever a command turns one. */\nconst saying =\n (swept: Swept): Reads =>\n (since) =>\n [\n \"sweeping\",\n swept._tag === \"searching\"\n ? `${swept.done} of ${repositories(swept.of)}`\n : `${swept.done} of ${count(swept.of, \"pull request\")}`,\n since\n ].join(\" · \")\n\n/**\n * One pass over every tracked PR, and nothing else: a sweep only ever reads.\n *\n * Every repository and every pull request is read on its own, so one of them\n * failing costs me its rows and leaves the rest of the table standing. What\n * failed comes back beside the facts rather than instead of them.\n *\n * `report` is told how far the pass has got, every time it gets further. What\n * that is worth saying is the caller's, which is why it is handed a count and\n * not a sentence.\n */\nexport const sweep = Effect.fn(\"sweep\")(function* (report: (swept: Swept) => Effect.Effect<void>) {\n const file: ConfigFile = Option.getOrElse(yield* readConfig, (): ConfigFile => ({}))\n const repos = Object.keys(file.repos ?? {}).toSorted()\n if (repos.length === 0) {\n return { repos, facts: [], troubles: [] } satisfies Report\n }\n\n const store = yield* storeFor(\"prs\", FactsSchema)\n const me = yield* viewer\n\n let searched = 0\n yield* report({ _tag: \"searching\", done: 0, of: repos.length })\n const found = gather(\n yield* Effect.forEach(\n repos,\n (repo) =>\n Effect.tap(attempt(repo, searchPrs(repo)), () => {\n searched = searched + 1\n return report({ _tag: \"searching\", done: searched, of: repos.length })\n }),\n { concurrency }\n )\n )\n\n let read = 0\n yield* report({ _tag: \"reading\", done: 0, of: found.got.length })\n const swept = gather(\n yield* Effect.forEach(\n found.got,\n (pr: Found) =>\n Effect.tap(\n attempt(\n `${pr.repo}#${pr.number}`,\n Effect.map(sweepPr(store, me, pr, settingsFor(file, pr.repo)), (facts) => [facts])\n ),\n () => {\n read = read + 1\n return report({ _tag: \"reading\", done: read, of: found.got.length })\n }\n ),\n { concurrency }\n )\n )\n\n return {\n repos,\n facts: swept.got,\n troubles: [...found.troubles, ...swept.troubles]\n } satisfies Report\n})\n\n/**\n * A sweep under its heartbeat, which is how every command that sweeps runs one.\n *\n * The three of them want the same line, so they say it once here rather than\n * three times over. It gives the heartbeat no aside, so a piped `dw-mc status`\n * prints exactly what it printed before there was a heartbeat at all.\n */\nexport const sweeping = beating(\n // Before the config is read there is no total to count towards, and a zero\n // there would be a number the sweep has not earned yet.\n (since) => `sweeping · ${since}`,\n (says) => sweep((swept) => says(saying(swept)))\n)\n\n/**\n * The failures a sweep can hit before it has a single row, which are the ones\n * worth a sentence: a machine or a file that needs fixing says what to fix\n * instead of printing a stack.\n */\nexport const userFacing = [\"ConfigMalformed\", \"GhUnavailable\", \"GhReadFailed\", \"GhUnreadable\"] as const\n\n/** Turns one of those into the sentence the CLI prints. */\nexport const asUserError = (cause: unknown): Effect.Effect<never, CliError.UserError> =>\n Effect.fail(new CliError.UserError({ cause }))\n\n/** What a sweep could not read, under a heading, so the table above it stands alone. */\nexport const printTroubles = Effect.fn(\"sweep.printTroubles\")(function* (troubles: ReadonlyArray<Trouble>) {\n if (troubles.length === 0) {\n return\n }\n yield* Console.log(\"\")\n yield* Console.log(\"Could not load\")\n for (const trouble of troubles) {\n yield* Console.log(` ${trouble.where} ${trouble.detail}`)\n }\n})\n\n/**\n * Refreshes what mission control knows about every tracked PR.\n *\n * `dw-mc status` does this too, so this command is for the pass on its own:\n * warming the state directory, or seeing what GitHub would not answer.\n */\nexport const sweepCommand = Command.make(\n \"sweep\",\n {},\n Effect.fn(\"sweep.command\")(\n function* () {\n const report = yield* sweeping\n yield* Console.log(\n report.repos.length === 0\n ? \"No repositories registered. Run dw-mc init inside a repository to register it.\"\n : `Swept ${count(report.facts.length, \"pull request\")} across ${report.repos.length === 1 ? \"1 repository\" : `${report.repos.length} repositories`}`\n )\n yield* printTroubles(report.troubles)\n },\n Effect.catchTag(userFacing, asUserError)\n )\n).pipe(Command.withDescription(\"Refresh what mission control knows about every tracked pull request\"))\n","import type { Thread } from \"#adapters/conversation.ts\"\nimport type { Moment } from \"#domain/moment.ts\"\nimport { isAfter } from \"#domain/moment.ts\"\n\n/**\n * A pull request's conversation as it goes on screen: what people said, and\n * under a rule of its own what the bots did.\n *\n * They are kept apart rather than ordered together because they are read for\n * different reasons. A person's comment is a thing to answer; a bot's is a\n * thing to look at, and the bucket rules already ignore it.\n */\nexport interface Shown {\n readonly people: ReadonlyArray<Thread>\n readonly bots: ReadonlyArray<Thread>\n}\n\n/**\n * One thread's share of a strand, cut to what is worth reading.\n *\n * A review thread is answered as a whole, so a single comment newer than my\n * last activity brings the whole thread with it: the follow-up on its own is a\n * line answering something the screen does not show, which is what sends me to\n * the browser.\n *\n * The pull request's own comments are not a thread but a stream, and there is\n * no reply to lose the question of, so they are cut comment by comment.\n */\nconst only = (thread: Thread, keep: (bot: boolean) => boolean, since: Moment, all: boolean): ReadonlyArray<Thread> => {\n const strand = thread.comments.filter((it) => keep(it.bot))\n const comments = all\n ? strand\n : thread.path === null\n ? strand.filter((it) => isAfter(it.at, since))\n : strand.some((it) => isAfter(it.at, since))\n ? strand\n : []\n return comments.length === 0 ? [] : [{ ...thread, comments }]\n}\n\n/**\n * The threads worth putting on screen, given what I have already done.\n *\n * `since` is my last activity on the pull request - the later of my last\n * comment and my last commit - which is the same moment the bucket rule\n * measures a comment against. Showing exactly what is newer than it means the\n * command answers the question the bucket asked.\n *\n * A thread somebody resolved and one against code that is gone are left out:\n * neither is something to answer, and both are still there to read under\n * `--all`, which asks for the whole conversation and so measures nothing\n * against anything.\n */\nexport const shown = (threads: ReadonlyArray<Thread>, options: { readonly since: Moment; readonly all: boolean }) => {\n const kept = options.all ? threads : threads.filter((it) => !it.resolved && !it.outdated)\n return {\n people: kept.flatMap((it) => only(it, (bot) => !bot, options.since, options.all)),\n bots: kept.flatMap((it) => only(it, (bot) => bot, options.since, options.all))\n } satisfies Shown\n}\n","import { Console, DateTime, Effect, Option } from \"effect\"\nimport { Command, Flag } from \"effect/unstable/cli\"\n\nimport type { ConfigFile } from \"#adapters/config.ts\"\nimport { read as readConfig } from \"#adapters/config.ts\"\nimport type { Thread } from \"#adapters/conversation.ts\"\nimport { prConversation } from \"#adapters/conversation.ts\"\nimport type { Paint } from \"#adapters/paint.ts\"\nimport { Paint as PaintService } from \"#adapters/paint.ts\"\nimport { named, prArgument, reading, swept } from \"#cli/pr.ts\"\nimport { heading } from \"#cli/row.ts\"\nimport { asUserError, userFacing } from \"#cli/sweep.ts\"\nimport type { Facts } from \"#domain/bucket.ts\"\nimport { place, unanswered } from \"#domain/bucket.ts\"\nimport type { Shown } from \"#domain/comments.ts\"\nimport { shown } from \"#domain/comments.ts\"\nimport { later } from \"#domain/moment.ts\"\n\nconst allFlag = Flag.Boolean(\"all\").pipe(\n Flag.withDefault(false),\n Flag.withDescription(\"Print the whole conversation, including what is resolved, outdated and already answered\")\n)\n\n/** Where a thread hangs: a line of the diff, or the pull request itself. */\nconst where = (thread: Thread): string =>\n thread.path === null ? \"Conversation\" : thread.line === null ? thread.path : `${thread.path}:${thread.line}`\n\n/**\n * What is true of a thread beyond where it hangs.\n *\n * It is only ever printed under `--all`, which is the only way a settled thread\n * reaches the screen at all, and it is there so that reading one is never\n * reading it as something still open.\n */\nconst settled = (thread: Thread): string =>\n [thread.resolved ? \"resolved\" : null, thread.outdated ? \"outdated\" : null].filter((it) => it !== null).join(\", \")\n\n/**\n * One thread as a block: where it hangs, then everybody who said something in\n * it, then what they said in full.\n *\n * In full because a review comment is usually a paragraph carrying a\n * suggestion, and a first line is what sends me to the browser this command\n * exists to replace. No diff hunk with it: the code is on this machine, under\n * the path the heading already prints.\n */\nconst block = (thread: Thread, paint: Paint): ReadonlyArray<string> => [\n `${paint.bold(where(thread))}${settled(thread) === \"\" ? \"\" : paint.dim(` (${settled(thread)})`)}`,\n ...thread.comments.flatMap((comment) => [\n ` ${paint.dim(`@${comment.login} ${DateTime.formatIso(comment.at)}`)}`,\n ...comment.body.split(\"\\n\").map((line) => ` ${line}`)\n ])\n]\n\nconst separated = (blocks: ReadonlyArray<ReadonlyArray<string>>): ReadonlyArray<string> =>\n blocks.flatMap((lines, index) => (index === 0 ? lines : [\"\", ...lines]))\n\n/**\n * The conversation on screen: people first, then a rule, then the bots.\n *\n * The rule is there so the two are never read as one list. A bot's comment is\n * observed and never answered, and the bucket rules ignore bots for exactly\n * this reason.\n *\n * A bot is cut at the same moment I am measured against, because the window is\n * what has happened since I last acted rather than what is owed an answer. A\n * verdict older than my last push is one I have already had the chance to read,\n * and `--all` is where it still is.\n */\nexport const lines = (view: Shown, paint: Paint): ReadonlyArray<string> => {\n const people = view.people.map((thread) => block(thread, paint))\n const bots = view.bots.map((thread) => block(thread, paint))\n return separated([...people, ...(bots.length === 0 ? [] : [[paint.dim(\"── bots ──\")], ...bots])])\n}\n\n/** What to say where there is nothing to print, which depends on why there is not. */\nconst nothing = (facts: Facts, all: boolean): ReadonlyArray<string> => {\n const pr = `${facts.repo}#${facts.number}`\n if (all) {\n return [`Nothing has been said on ${pr}.`]\n }\n const placement = place(facts)\n const rest = `dw-mc comments ${facts.number} --all prints the whole conversation.`\n return placement.bucket === \"needs-me\" && placement.reason === unanswered\n ? [\n `Nothing here is waiting on you: every thread is resolved, outdated, or older than your last comment ` +\n `or commit.`,\n `${pr} sits in ${heading[placement.bucket]} all the same, and a reply or a push is what settles it.`,\n rest\n ]\n : [`Nothing has been said on ${pr} since your last comment or commit.`, rest]\n}\n\n/**\n * The conversation on one tracked pull request, and nothing else.\n *\n * What it shows by default is what the bucket rule measures: the comments newer\n * than the later of my last comment and my last commit, which are the ones that\n * put the pull request in Needs me. Reading it answers the question the table\n * asked.\n *\n * The cutoff is read off the last sweep rather than worked out again here, so\n * the command shows exactly what `dw-mc status` counted rather than a second\n * opinion about it.\n *\n * It writes nothing, here or on GitHub: no reply, no resolve, no reaction\n * (ADR 0002). Reading is the whole command.\n */\nexport const comments = Command.make(\n \"comments\",\n { pr: prArgument, all: allFlag },\n Effect.fn(\"comments\")(\n function* ({ all, pr }) {\n const file: ConfigFile = Option.getOrElse(yield* readConfig, (): ConfigFile => ({}))\n const { number, repo } = yield* named(pr, Object.keys(file.repos ?? {}).toSorted())\n\n const facts = yield* swept(repo, number)\n const paint = yield* PaintService\n const view = shown(yield* reading(`${repo}#${number}`, prConversation(repo, number)), {\n since: later(facts.myLastCommentAt, facts.myLastCommitAt),\n all\n })\n\n if (view.people.length === 0 && view.bots.length === 0) {\n yield* Effect.forEach(nothing(facts, all), (line) => Console.log(line))\n return\n }\n\n yield* Console.log(paint.bold(`${repo}#${number}`) + ` ${paint.dim(facts.title)}`)\n yield* Console.log(\"\")\n yield* Effect.forEach(lines(view, paint), (line) => Console.log(line))\n },\n Effect.catchTag([\"ConfigMalformed\", ...userFacing], asUserError)\n )\n).pipe(Command.withDescription(\"Print the conversation on one pull request, and what is waiting on me in it\"))\n","import { Console, Effect, Option, Schema } from \"effect\"\nimport { CliError, Command, Flag } from \"effect/unstable/cli\"\n\nimport type { ConfigFile } from \"#adapters/config.ts\"\nimport { read as readConfig, settingsFor } from \"#adapters/config.ts\"\nimport { named, prArgument } from \"#cli/pr.ts\"\nimport { asUserError } from \"#cli/sweep.ts\"\nimport { count, table } from \"#cli/table.ts\"\nimport type { Findings } from \"#domain/findings.ts\"\nimport { blocking, Findings as FindingsSchema } from \"#domain/findings.ts\"\nimport type { ReviewRun } from \"#domain/review.ts\"\nimport { lastRun, reportedBy, short } from \"#domain/review.ts\"\nimport type { Severity } from \"#terms/review.ts\"\n\n/** The findings as the JSON the schema defines, rather than as this file spells it. */\nconst asJson = Schema.encodeEffect(Schema.fromJsonString(FindingsSchema))\n\nconst jsonFlag = Flag.Boolean(\"json\").pipe(\n Flag.withDefault(false),\n Flag.withDescription(\"Print the findings as the JSON a fix session is handed\")\n)\n\n/** What a run's findings come to in one line, against the bar that blocks. */\nexport const summary = (found: Findings, blocksOn: Severity): string => {\n if (found.findings.length === 0) {\n return \"clean, nothing to fix\"\n }\n const blocked = blocking(found.findings, blocksOn).length\n return `${count(found.findings.length, \"finding\")}, ${blocked} blocking`\n}\n\n/** Which run these findings are, and what they come to: the line above the list. */\nexport const header = (run: ReviewRun, found: Findings, blocksOn: Severity): string =>\n `${run.repo}#${run.number} ${short(run.head)} ${summary(found, blocksOn)}`\n\n/**\n * The findings one to a line, in the order the run reported them, ruled so the\n * three columns read apart.\n */\nexport const lines = (found: Findings): ReadonlyArray<string> =>\n table(\n found.findings.map((finding) => [`${finding.file}:${finding.line}`, finding.severity, finding.summary]),\n \" │ \"\n )\n\n/**\n * The review run whose findings are the current ones, or the sentence saying\n * there are none.\n *\n * The last run on the pull request is what \"current\" means here, and it is read\n * off the state directory rather than worked out from GitHub: this command is\n * one I run inside a fix session, where another round trip to GitHub buys\n * nothing the run it is about to fix does not already say.\n */\nexport const currentRun = Effect.fn(\"findings.currentRun\")(function* (repo: string, number: number) {\n const run = yield* lastRun(repo, number)\n return Option.isSome(run)\n ? run.value\n : yield* asUserError(`No review run on ${repo}#${number}. Run dw-mc review ${number} first.`)\n})\n\n/**\n * What the run reported, or the sentence saying it reported nothing at all.\n *\n * A run that failed is not a clean one: a pipe must never be handed \"no\n * findings\" when what happened is that nothing could be read.\n */\nexport const whatItFound = (run: ReviewRun): Effect.Effect<Findings, CliError.UserError> => {\n const found = reportedBy(run)\n return found === null\n ? Effect.fail(\n new CliError.UserError({\n cause:\n `The review run on ${short(run.head)} reported no findings: ` +\n `${run.outcome._tag === \"failed\" ? run.outcome.detail : \"\"}\\n` +\n `Run dw-mc review ${run.number} --force to run it again.`\n })\n )\n : Effect.succeed(found)\n}\n\n/**\n * What the current review run found, as a table or as the JSON it is kept in.\n *\n * `--json` is the whole point of the command: it prints the findings and\n * nothing else, so I can pipe them anywhere, and a fix session inside an open\n * agent reads exactly what the tool recorded rather than a retelling of it.\n *\n * A run that failed prints no findings and fails: a review run that could not\n * report has found nothing, which is not the same as having found nothing\n * wrong, and a pipe must never be handed the second when the first is true.\n */\nexport const findings = Command.make(\n \"findings\",\n { pr: prArgument, json: jsonFlag },\n Effect.fn(\"findings\")(\n function* ({ json, pr }) {\n const file: ConfigFile = Option.getOrElse(yield* readConfig, (): ConfigFile => ({}))\n const { number, repo } = yield* named(pr, Object.keys(file.repos ?? {}).toSorted())\n const settings = settingsFor(file, repo)\n\n const run = yield* currentRun(repo, number)\n const found = yield* whatItFound(run)\n if (json) {\n yield* Console.log(yield* asJson(found))\n return\n }\n\n yield* Console.log(header(run, found, settings.stamp.blocks_on))\n for (const line of lines(found)) {\n yield* Console.log(` ${line}`)\n }\n },\n Effect.catchTag([\"ConfigMalformed\"], asUserError)\n )\n).pipe(Command.withDescription(\"Print what the current review run found on one pull request\"))\n","/**\n * How one turn of Claude Code is spawned and given up on, and what a turn that\n * answers against a schema comes back with.\n *\n * Every turn is reached this way, so the spawn, the patience and the one failure\n * they can end in live here rather than once per turn.\n */\nimport { Duration, Effect, Schema, Stream } from \"effect\"\nimport { ChildProcess, ChildProcessSpawner } from \"effect/unstable/process\"\n\n/** A review run that would not start, would not finish, or finished badly. */\nexport class AgentFailed extends Schema.TaggedError<AgentFailed>()(\"AgentFailed\", {\n /** The program that was spawned, which is what a search for it has to name. */\n program: Schema.String,\n detail: Schema.String\n}) {\n override get message(): string {\n return `The ${this.program} review run failed: ${this.detail}`\n }\n}\n\n/** What a review turn held to a schema came back with. */\nexport interface Reported {\n /** What the run validated against the schema, handed on unread. */\n readonly findings: unknown\n /** The session the run happened in, which is what a run is recorded against. */\n readonly sessionId: string\n /** What the run said in prose beside its findings, or null where it said none. */\n readonly prose: string | null\n}\n\n/**\n * Failures in the name of the program that was spawned.\n *\n * Where the launcher starts `claude` through another program, it is that\n * program that would not start or exited badly, and saying `claude` sends the\n * search to the wrong process.\n */\nexport const failedBy = (program: string) => (detail: string) => new AgentFailed({ program, detail })\n\n/**\n * How long each turn gets before it is given up on.\n *\n * The review is the turn that thinks, and a high-effort one that fans out to\n * subagents takes real minutes, so its limit is there to catch a run that has\n * stopped rather than one that is slow. The second turn reads no code and\n * decides nothing - the review it reports on is already in the session it\n * resumes - and every run of it by hand came back in seconds.\n *\n * Either way, a command that hangs forever is worse than one that says it\n * failed: a review I walked away from is one I need to be able to come back to.\n */\nexport const patience = {\n reviewing: Duration.minutes(45),\n reporting: Duration.minutes(5)\n}\n\n/**\n * One turn of the launcher in `directory`, with `read` over its standard output.\n *\n * The launcher's own arguments go in front of the turn's, because they are what\n * gets `claude` started at all. The two output streams are drained together,\n * because draining one to the end first can block a run that is still writing to\n * the other. Every way a turn can fail to finish comes back from here as a\n * `AgentFailed`, so a caller is left with the turn's own answer and nothing else\n * to translate - a turn that never comes back included.\n */\nexport const turn = Effect.fnUntraced(function* <A, E extends { readonly message: string }, R>(options: {\n /** The program and the prefix that starts Claude Code. */\n readonly command: readonly [string, ...Array<string>]\n readonly directory: string\n readonly args: ReadonlyArray<string>\n /** What this turn is called when it is late, and how long it has. */\n readonly patience: { readonly turn: string; readonly duration: Duration.Duration }\n readonly read: (stdout: ChildProcessSpawner.ChildProcessHandle[\"stdout\"]) => Effect.Effect<A, E, R>\n}) {\n const [program, ...prefix] = options.command\n const failed = failedBy(program)\n const spawner = yield* ChildProcessSpawner.ChildProcessSpawner\n\n const running = Effect.gen(function* () {\n const handle = yield* Effect.mapError(\n spawner.spawn(\n ChildProcess.make(program, [...prefix, ...options.args], { cwd: options.directory, stdin: \"pipe\" })\n ),\n (error) => failed(error.message)\n )\n\n const [got, stderr] = yield* Effect.mapError(\n Effect.all([options.read(handle.stdout), Stream.mkString(Stream.decodeText(handle.stderr))], { concurrency: 2 }),\n (error) => failed(error.message)\n )\n\n const exitCode = yield* Effect.mapError(handle.exitCode, (error) => failed(error.message))\n if (exitCode !== 0) {\n return yield* failed(stderr.trim() === \"\" ? `${program} exited ${exitCode}` : stderr.trim())\n }\n return got\n })\n\n return yield* Effect.timeoutOrElse(running, {\n duration: options.patience.duration,\n orElse: () =>\n failed(`${options.patience.turn} did not come back within ${Duration.format(options.patience.duration)}`)\n })\n})\n","/**\n * Claude Code: a review on a slash command, a review on the tool's own prompt,\n * and the sessions I steer.\n */\nimport { Effect, Option, PlatformError, Result, Schema, Stream } from \"effect\"\nimport { ChildProcess, ChildProcessSpawner } from \"effect/unstable/process\"\n\nimport type { Reported, AgentFailed } from \"#adapters/agent.ts\"\nimport { failedBy, patience, turn } from \"#adapters/agent.ts\"\nimport type { Launcher } from \"#adapters/config.ts\"\nimport type { ReviewTurn } from \"#terms/review.ts\"\n\n/** What one turn came back with. */\nexport interface Turn {\n /** What the run said, as the prose it says it in. */\n readonly report: string\n /** The session the turn ran in, which the follow-up turn resumes. */\n readonly sessionId: string\n}\n\n/** What a whole review run came to, however many turns it took. */\nexport interface Reviewed {\n readonly sessionId: string\n /** What the run said in prose, or null where a schema left it none to say. */\n readonly prose: string | null\n /** What it reported, or the failure the reporting was. */\n readonly findings: Result.Result<unknown, AgentFailed>\n}\n\n/**\n * The two events of a stream-json run this reads, as the runner really writes\n * them. Every other field of both, and every other event, is ignored: a\n * transcript carries hooks, rate limits, thinking and tool results, and a\n * version that adds another must not stop a run from being read.\n */\nconst Working = Schema.Struct({\n type: Schema.Literal(\"assistant\"),\n message: Schema.Struct({\n content: Schema.Array(\n Schema.Struct({\n type: Schema.String,\n name: Schema.optionalKey(Schema.String),\n text: Schema.optionalKey(Schema.String)\n })\n )\n })\n})\n\nconst Ended = Schema.Struct({\n type: Schema.Literal(\"result\"),\n subtype: Schema.String,\n is_error: Schema.Boolean,\n session_id: Schema.String,\n result: Schema.optionalKey(Schema.String),\n /** What a turn given a JSON schema validated, which this hands on unread. */\n structured_output: Schema.optionalKey(Schema.Unknown)\n})\n\nconst asWorking = Schema.decodeUnknownOption(Schema.fromJsonString(Working))\nconst asResult = Schema.decodeUnknownOption(Schema.fromJsonString(Ended))\n\n/** What one event says the runner reached for, and what it said out loud. */\ninterface Heard {\n readonly tools: ReadonlyArray<string>\n readonly said: ReadonlyArray<string>\n}\n\nconst heardIn = (line: string): Heard => {\n const blocks = Option.match(asWorking(line), { onNone: () => [], onSome: (event) => event.message.content })\n return {\n tools: blocks.flatMap((block) => (block.type === \"tool_use\" && block.name !== undefined ? [block.name] : [])),\n said: blocks.flatMap((block) => (block.type === \"text\" && block.text !== undefined ? [block.text] : []))\n }\n}\n\n/** What the run comes to while it is still going. */\ninterface SoFar {\n readonly said: ReadonlyArray<string>\n readonly result: Option.Option<typeof Ended.Type>\n}\n\n/**\n * The result a turn ended on, or the failure it really was.\n *\n * A turn that said nothing this can read and a turn Claude Code itself calls an\n * error are both failures: `subtype` is where a run that hit its turn limit or\n * lost its connection says so, and its `result` is the only word on why.\n */\nconst ended = (program: string, result: Option.Option<typeof Ended.Type>) => {\n const failed = failedBy(program)\n if (Option.isNone(result)) {\n return Effect.fail(failed(\"the turn came back with no result\"))\n }\n const { is_error, result: lastWord, subtype } = result.value\n return is_error || subtype !== \"success\"\n ? Effect.fail(failed(`${subtype}: ${lastWord ?? \"nothing else was said\"}`))\n : Effect.succeed(result.value)\n}\n\n/**\n * A Claude Code `stream-json` turn, read as it arrives: what it reached for goes\n * to `onTool` while the run is still going, and what it said and how it ended\n * are what comes back.\n *\n * Both shapes of review read a turn the same way, so the fold is here rather\n * than once per shape.\n */\nconst transcript =\n (onTool: (tool: string) => Effect.Effect<void>) =>\n (stdout: ChildProcessSpawner.ChildProcessHandle[\"stdout\"]): Effect.Effect<SoFar, PlatformError.PlatformError> =>\n stdout.pipe(\n Stream.decodeText(),\n Stream.splitLines,\n Stream.mapEffect((line) => {\n const heard = heardIn(line)\n return Effect.as(Effect.forEach(heard.tools, onTool, { discard: true }), { line, heard })\n }),\n Stream.runFold(\n (): SoFar => ({ said: [], result: Option.none() }),\n (soFar, { heard, line }): SoFar => ({\n said: [...soFar.said, ...heard.said],\n result: Option.orElse(asResult(line), () => soFar.result)\n })\n )\n )\n\n/**\n * One review run on a slash command, headless, in `directory`.\n *\n * The run is in the foreground and says what it is doing as it does it, which\n * is what `onTool` is for: a review takes minutes, and a terminal that prints\n * nothing for minutes is one I stop trusting.\n *\n * `--json-schema` is never passed here: verified by running it, the flag beside\n * `/code-review` breaks the run, which is why a slash command costs a second\n * turn that resumes the session and asks for the findings. My own instructions\n * ride on `--append-system-prompt` rather than on the command's own line,\n * because what a slash command does with its arguments is its business and not\n * this tool's.\n *\n * `--comment` is the flag that makes the built-in review post on the pull\n * request, and it is never passed either (ADR 0002). The report is everything\n * the run said on its own turns rather than the `result` alone: verified by\n * running it, a repository whose review command fans out to subagents can end on\n * a remark about them, and the report is the turn before that.\n */\nexport const commandReview = Effect.fn(\"claude.commandReview\")(function* (options: {\n readonly launcher: Launcher\n readonly directory: string\n readonly line: string\n readonly instructions: string | null\n readonly model: string | null\n readonly onTool: (tool: string) => Effect.Effect<void>\n}) {\n const [program] = options.launcher.command\n const run = yield* turn({\n command: options.launcher.command,\n directory: options.directory,\n args: [\n \"-p\",\n options.line,\n \"--output-format\",\n \"stream-json\",\n \"--verbose\",\n ...(options.instructions === null ? [] : [\"--append-system-prompt\", options.instructions]),\n ...(options.model === null ? [] : [\"--model\", options.model])\n ],\n patience: { turn: \"the review\", duration: patience.reviewing },\n read: transcript(options.onTool)\n })\n\n const { result: lastWord, session_id } = yield* ended(program, run.result)\n\n // The result is the run's last word, which is its whole answer on a run that\n // said nothing before it.\n const report = (run.said.length === 0 ? (lastWord ?? \"\") : run.said.join(\"\\n\\n\")).trim()\n if (report === \"\") {\n return yield* failedBy(program)(\"the run came back with an empty report\")\n }\n return { report, sessionId: session_id } satisfies Turn\n}, Effect.scoped)\n\n/**\n * What the second turn asks for.\n *\n * It asks for a report of what was already said rather than for another look:\n * the prose is the review, and this turn is only what makes it machine\n * readable. The shape it must answer in arrives as a JSON schema beside it, so\n * the prompt does not describe the schema twice.\n */\nconst reportFindings = [\n \"Report the findings of the review you just gave as structured output.\",\n \"Every finding carries the file it is in as a repository path, the line it is at,\",\n \"its severity and a one-sentence summary.\",\n \"The verdict is clean when there is nothing to report and findings otherwise.\",\n \"Report nothing you did not already say.\"\n].join(\" \")\n\n/**\n * The second turn of a review run: the prose the first one wrote, back as\n * findings that validate.\n *\n * It resumes the first turn's session rather than reading the diff again, which\n * is what makes it cheap and what makes it accurate - verified by running it,\n * the line numbers it reports beat the ones the prose gives. The output is\n * handed on as it arrived: what the findings must look like belongs to the\n * domain, and the schema the run is held to comes in from there too.\n *\n * Every way this can end badly ends as an `AgentFailed`, because a review run\n * that could not report is a failure and never a clean verdict.\n */\nexport const findingsTurn = Effect.fn(\"claude.findingsTurn\")(function* (options: {\n readonly launcher: Launcher\n readonly directory: string\n readonly sessionId: string\n readonly jsonSchema: string\n}) {\n const [program] = options.launcher.command\n const printed = yield* turn({\n command: options.launcher.command,\n directory: options.directory,\n patience: { turn: \"the findings turn\", duration: patience.reporting },\n args: [\n \"-p\",\n \"--resume\",\n options.sessionId,\n reportFindings,\n \"--output-format\",\n \"json\",\n \"--json-schema\",\n options.jsonSchema\n ],\n read: (stdout) => Stream.mkString(Stream.decodeText(stdout))\n })\n\n const { structured_output } = yield* ended(program, asResult(printed.trim()))\n if (structured_output === undefined) {\n return yield* failedBy(program)(\"the findings turn came back with no structured output\")\n }\n return structured_output\n}, Effect.scoped)\n\n/**\n * One review run of the tool's own review prompt, in `directory`.\n *\n * It is one turn rather than two: verified by running it, `--json-schema` beside\n * an ordinary prompt gives both the prose the run wrote and the\n * `structured_output` it validated, where the same flag on a slash command\n * breaks the run. The schema arrives as inline JSON and never as a path - a path\n * is where Claude Code reports `--json-schema is not valid JSON`.\n */\nexport const promptReview = Effect.fn(\"claude.promptReview\")(function* (options: {\n readonly launcher: Launcher\n readonly directory: string\n readonly prompt: string\n /** The model to run the prompt on, or null for whatever the CLI would pick. */\n readonly model: string | null\n readonly jsonSchema: string\n readonly onTool: (tool: string) => Effect.Effect<void>\n}) {\n const [program] = options.launcher.command\n const run = yield* turn({\n command: options.launcher.command,\n directory: options.directory,\n patience: { turn: \"the review\", duration: patience.reviewing },\n args: [\n \"-p\",\n options.prompt,\n \"--output-format\",\n \"stream-json\",\n \"--verbose\",\n \"--json-schema\",\n options.jsonSchema,\n ...(options.model === null ? [] : [\"--model\", options.model])\n ],\n read: transcript(options.onTool)\n })\n\n const { session_id, structured_output } = yield* ended(program, run.result)\n if (structured_output === undefined) {\n return yield* failedBy(program)(\"the review came back with no structured output\")\n }\n\n const prose = run.said.join(\"\\n\\n\").trim()\n return { findings: structured_output, sessionId: session_id, prose: prose === \"\" ? null : prose } satisfies Reported\n}, Effect.scoped)\n\n/**\n * One review run, in whichever shape it was configured in.\n *\n * A slash command takes two turns and the tool's own prompt takes one, which is\n * Claude Code's doing and nobody else's: a caller hands over the turn and gets\n * the same answer back either way.\n *\n * The second turn's failure is kept beside the first turn's prose rather than\n * replacing it. A review that ran and could not report is still worth reading,\n * and it is recorded as the failure it is.\n */\nexport const reviewTurns = Effect.fn(\"claude.reviewTurns\")(function* (options: {\n readonly launcher: Launcher\n readonly directory: string\n readonly turn: ReviewTurn\n readonly model: string | null\n readonly jsonSchema: string\n readonly onTool: (tool: string) => Effect.Effect<void>\n}) {\n const { directory, jsonSchema, launcher, model, onTool } = options\n if (options.turn._tag === \"prompt\") {\n const run = yield* promptReview({ launcher, directory, prompt: options.turn.text, model, jsonSchema, onTool })\n return { sessionId: run.sessionId, prose: run.prose, findings: Result.succeed(run.findings) } satisfies Reviewed\n }\n\n const run = yield* commandReview({\n launcher,\n directory,\n line: options.turn.line,\n instructions: options.turn.instructions,\n model,\n onTool\n })\n const findings = yield* Effect.result(findingsTurn({ launcher, directory, sessionId: run.sessionId, jsonSchema }))\n return { sessionId: run.sessionId, prose: run.report, findings } satisfies Reviewed\n})\n\n/**\n * An interactive `claude` in `directory`, opened on `prompt`, with my terminal\n * handed straight to it.\n *\n * The launcher's `fix_args` go here and nowhere else: they are the flags of\n * every session I steer - the one on findings and the one on a conflict - which\n * no headless review turn wants. They sit in front of the\n * prompt, because `claude` takes its flags before its positional argument.\n *\n * This is the one place a run is not read: the three streams are inherited,\n * so what is on the screen is the session itself and not a transcript of it,\n * and what I type reaches it. The child is not detached for the same reason -\n * a detached child sits outside the terminal's foreground process group, where\n * neither my keystrokes nor Ctrl-C would reach it.\n *\n * There is no patience here either. A session I steer lasts as long as I am in\n * it, and a timeout would be the tool closing a session I was still working in.\n *\n * What comes back is the code the session ended on. A session I left with\n * Ctrl-C ended badly for `claude` and not for me, so this reports it rather\n * than failing on it; only a `claude` that would not start at all is a failure.\n */\nexport const steeredSession = Effect.fn(\"claude.steeredSession\")(function* (options: {\n readonly launcher: Launcher\n readonly directory: string\n readonly prompt: string\n}) {\n const [program, ...prefix] = options.launcher.command\n const failed = failedBy(program)\n const spawner = yield* ChildProcessSpawner.ChildProcessSpawner\n\n const handle = yield* Effect.mapError(\n spawner.spawn(\n ChildProcess.make(program, [...prefix, ...options.launcher.fix_args, options.prompt], {\n cwd: options.directory,\n stdin: \"inherit\",\n stdout: \"inherit\",\n stderr: \"inherit\",\n detached: false\n })\n ),\n (error) => failed(error.message)\n )\n\n return yield* Effect.mapError(handle.exitCode, (error) => failed(error.message))\n}, Effect.scoped)\n","import { Effect, Schema } from \"effect\"\n\nimport { Finding } from \"#domain/findings.ts\"\nimport { short } from \"#domain/review.ts\"\n\n/** One finding I chose to act on, carrying what I think about it. */\nexport const Chosen = Schema.Struct({ ...Finding.fields, note: Schema.optionalKey(Schema.String) })\nexport type Chosen = typeof Chosen.Type\n\n/**\n * What a fix session is handed: the findings I picked, and the review run they\n * came from.\n *\n * The head is in it because a fix session opens on the commit that was\n * reviewed, and a finding's line means nothing away from it.\n */\nexport const Selection = Schema.Struct({\n repo: Schema.String,\n number: Schema.Int,\n head: Schema.String,\n findings: Schema.Array(Chosen)\n})\nexport type Selection = typeof Selection.Type\n\n/** The selection as the JSON the schema defines, rather than as this file spells it. */\nconst asJson = Schema.encodeEffect(Schema.fromJsonString(Selection))\n\n/**\n * The prompt a fix session opens on: what these findings are, and the findings\n * themselves as JSON.\n *\n * The findings go in verbatim rather than described, because a re-description\n * is where a file, a line or my own note quietly changes. A note outranks the\n * finding it is on: the finding is what the review thought, the note is what I\n * think, and I am the one who picked it.\n *\n * Pushing is mine either way, and `commits` says whether committing is too.\n * The tool itself never commits and never pushes; what the session may do\n * inside the worktree is my call, made once in `fix.commits` or for one session\n * with the flag.\n */\nexport const promptFor = (selection: Selection, commits: boolean): Effect.Effect<string, Schema.SchemaError> =>\n Effect.map(asJson(selection), (json) =>\n [\n `These are the findings I picked from a dw-mc review run on ${selection.repo}#${selection.number}, ` +\n `at ${short(selection.head)}, the commit their lines are counted from.`,\n `Work through them one at a time. Where a finding carries a note, the note is mine and outranks the ` +\n `finding's own summary; where it carries none, the summary is the whole brief.`,\n commits\n ? `Commit what you change, one logical change to a commit. Do not push: I read the commits and push them myself.`\n : `Do not commit and do not push: I do both myself when I have read what you changed.`,\n json\n ].join(\"\\n\\n\")\n )\n\n/**\n * Why these findings cannot be fixed where the pull request now is, or nothing\n * where they can.\n *\n * A pull request that moved since its last review run has findings at lines\n * that may no longer be there, and a worktree cut at the new head would carry\n * them into code they were never about. Reviewing again is cheap next to fixing\n * the wrong thing.\n */\nexport const staleAt = (number: number, run: string, now: string): string | null =>\n run === now\n ? null\n : `The findings are from ${short(run)} and the pull request is now at ${short(now)}. ` +\n `Run dw-mc review ${number} again to review the head you would be fixing.`\n","import { Console, Effect, Option } from \"effect\"\nimport { CliError, Command, Flag } from \"effect/unstable/cli\"\n\nimport { steeredSession } from \"#adapters/claude.ts\"\nimport type { ConfigFile } from \"#adapters/config.ts\"\nimport { launcherOf, read as readConfig, settingsFor } from \"#adapters/config.ts\"\nimport { prView } from \"#adapters/gh.ts\"\nimport { standingWorktree } from \"#adapters/git.ts\"\nimport { choose, note, width } from \"#adapters/picker.ts\"\nimport { currentRun, header, lines, whatItFound } from \"#cli/findings.ts\"\nimport { named, prArgument, reading } from \"#cli/pr.ts\"\nimport { asUserError, userFacing } from \"#cli/sweep.ts\"\nimport { truncate } from \"#cli/table.ts\"\nimport type { Finding, Findings } from \"#domain/findings.ts\"\nimport type { Chosen } from \"#domain/fix.ts\"\nimport { promptFor, staleAt } from \"#domain/fix.ts\"\n\nconst printFlag = Flag.Boolean(\"print\").pipe(\n Flag.withDefault(false),\n Flag.withDescription(\"Print the prompt a session would open on, and open none\")\n)\n\nconst commitFlag = Flag.Boolean(\"commit\").pipe(\n Flag.withDescription(\"Let this session commit what it changes, over what the repository configured\"),\n Flag.optional\n)\n\n/**\n * The findings to pick from, each on the line `dw-mc findings` gives it.\n *\n * The rows come from there rather than being built again here, so the list I\n * pick from and the list I read are the same list. A row that does not fit the\n * screen is cut: a prompt draws its own frame around the row, and a row that\n * wraps takes the whole list's alignment with it.\n */\nconst choicesOf = (found: Findings, screen: number) => {\n const rows = lines(found)\n const room = screen === 0 ? Number.POSITIVE_INFINITY : screen - 6\n return found.findings.map((finding, index) => ({\n title: truncate(rows[index] ?? finding.summary, room),\n value: finding\n }))\n}\n\n/**\n * Each picked finding with whatever I have to say about it.\n *\n * The note is asked for one finding at a time, in the order I see them, and\n * having nothing to say is the ordinary answer rather than a step I have to get\n * past.\n */\nconst noted = Effect.fn(\"fix.noted\")(function* (picked: ReadonlyArray<Finding>) {\n const chosen: Array<Chosen> = []\n for (const finding of picked) {\n const said = yield* note(`Note on ${finding.file}:${finding.line}, or nothing`)\n chosen.push(Option.match(said, { onNone: () => finding, onSome: (text) => ({ ...finding, note: text }) }))\n }\n return chosen\n})\n\n/** The domain's word on a head that has moved, as the command's own failure. */\nconst fixable = (number: number, run: string, now: string) => {\n const stale = staleAt(number, run, now)\n return stale === null ? Effect.void : Effect.fail(new CliError.UserError({ cause: stale }))\n}\n\n/**\n * A fix session: the findings I picked, in an agent session I steer.\n *\n * The tool fixes nothing. It picks the findings apart with me, cuts a worktree\n * on a branch of its own that tracks the pull request's, and hands the session\n * what I chose as JSON; then it is out of the way. I steer and I push. Nothing\n * here writes to GitHub, and the tool itself commits nothing: whether the\n * session may commit inside the worktree is `fix.commits`, or `--commit` for\n * one session.\n *\n * The worktree is left standing when the session ends, because the work in it\n * is mine and an unpushed commit lives nowhere else. Re-reviewing the result is\n * a new review run against the new head, never a continuation of the run that\n * produced these findings, so what was reviewed at which commit stays honest.\n */\nexport const fix = Command.make(\n \"fix\",\n { pr: prArgument, commit: commitFlag, print: printFlag },\n Effect.fn(\"fix\")(\n function* ({ commit, pr, print }) {\n const file: ConfigFile = Option.getOrElse(yield* readConfig, (): ConfigFile => ({}))\n const { number, repo } = yield* named(pr, Object.keys(file.repos ?? {}).toSorted())\n const settings = settingsFor(file, repo)\n\n const run = yield* currentRun(repo, number)\n const found = yield* whatItFound(run)\n yield* Console.log(header(run, found, settings.stamp.blocks_on))\n if (found.findings.length === 0) {\n return\n }\n\n const view = yield* reading(`${repo}#${number}`, prView(repo, number))\n yield* fixable(number, run.head, view.headRefOid)\n\n const picked = yield* choose(\"Which findings does the session carry?\", choicesOf(found, yield* width))\n const chosen = yield* Effect.catchTag(noted(Option.getOrElse(picked, () => [])), \"QuitError\", () =>\n Effect.succeed<ReadonlyArray<Chosen>>([])\n )\n if (chosen.length === 0) {\n yield* Console.log(\"Nothing picked, so no session was opened.\")\n return\n }\n\n const commits = Option.getOrElse(commit, () => settings.fix.commits)\n // The prompt on its own, for the session I already have open. Nothing is\n // cut and nothing is spawned: the session this is pasted into is one I am\n // steering already, in whatever checkout I am steering it from.\n if (print) {\n yield* Console.log(yield* promptFor({ repo, number, head: run.head, findings: chosen }, commits))\n return\n }\n\n const worktree = yield* standingWorktree(repo, number, view.headRefName, \"fix\")\n yield* Console.log(\n ` ${chosen.length} of ${found.findings.length} findings, ${commits ? \"committing\" : \"not committing\"}`\n )\n yield* Console.log(` ${worktree.directory}, pushing to ${view.headRefName}`)\n\n const ended = yield* steeredSession({\n launcher: launcherOf(file),\n directory: worktree.directory,\n prompt: yield* promptFor({ repo, number, head: worktree.head, findings: chosen }, commits)\n })\n\n yield* Console.log(ended === 0 ? \"The session is over.\" : `The session ended with ${ended}.`)\n yield* Console.log(\n `${commits ? \"Nothing was pushed\" : \"Nothing was committed or pushed\"} for you; ` +\n `the worktree stands at ${worktree.directory}.`\n )\n yield* Console.log(`Once you have pushed, dw-mc review ${number} reviews the new head as a new run.`)\n },\n Effect.catchTag([...userFacing, \"GitFailed\", \"WorktreeHeld\", \"AgentFailed\"], asUserError)\n )\n).pipe(Command.withDescription(\"Pick findings from the current review run and open a fix session on them\"))\n","import { Console, Effect, Option } from \"effect\"\nimport { Command, Flag } from \"effect/unstable/cli\"\n\nimport type { ConfigFile, SettingsPatch } from \"#adapters/config.ts\"\nimport { builtIn, ConfigStore, encode, merge, read, withDefaults, withRepo, write } from \"#adapters/config.ts\"\nimport { currentRepo, requireAuth } from \"#adapters/gh.ts\"\nimport { stateDirectory } from \"#adapters/store.ts\"\nimport { asUserError } from \"#cli/sweep.ts\"\nimport type { Effort } from \"#terms/review.ts\"\n\nconst effortFlag = Flag.Literals(\"effort\", [\"low\", \"medium\", \"high\", \"xhigh\", \"max\"]).pipe(\n Flag.withDescription(\"How much a review run spends on this repository\"),\n Flag.optional\n)\n\nconst baseFlag = Flag.String(\"base\").pipe(\n Flag.withDescription(\"The branch this repository's pull requests target, over the default one\"),\n Flag.optional\n)\n\n/** The settings the flags asked for, and only those. */\nconst asked = (base: Option.Option<string>, effort: Option.Option<Effort>): SettingsPatch => ({\n ...(Option.isSome(base) ? { base: base.value } : {}),\n ...(Option.isSome(effort) ? { review: { effort: effort.value } } : {})\n})\n\n/** What a review will open on, as the setup prints it back. */\nconst opening = (defaults: SettingsPatch): string => {\n const review = { ...builtIn.review, ...defaults.review }\n return review.command === null\n ? \"my own prompt\"\n : [review.command, review.effort].filter((part) => part !== null).join(\" \")\n}\n\nconst row = (label: string, value: string): string => `${label.padEnd(12)}${value}`\n\n/**\n * Both the machine setup and the repository registration: there is deliberately\n * no separate `setup` command.\n *\n * The first run on a machine checks `gh` and spells the defaults out in the\n * configuration file. Run inside a repository, it also registers that\n * `owner/repo`, taking the name from `gh` so I never type it. Run again, it\n * changes what the flags name, keeps every other setting the file already had,\n * and leaves the file untouched where nothing was decided differently.\n *\n * It asks nothing. Reviews run on Claude Code, and what a run opens on is\n * `review.command` and `review.prompt` - a line and a paragraph that belong in\n * the file rather than in a terminal prompt.\n *\n * `--effort` and `--base` are about one repository, so they land on the\n * repository this ran in, or in the defaults when it ran outside one.\n */\nexport const init = Command.make(\n \"init\",\n { effort: effortFlag, base: baseFlag },\n Effect.fn(\"init\")(\n function* ({ base, effort }) {\n yield* requireAuth\n\n const config = yield* ConfigStore\n const before = yield* read\n const file: ConfigFile = Option.getOrElse(before, (): ConfigFile => ({}))\n\n // A defaults block is what says this machine has been set up. On a first\n // run the built-in defaults go under whatever the file already said, so\n // spelling them out cannot overwrite a setting I chose by hand.\n const firstRun = file.defaults === undefined\n const defaults = firstRun ? merge(builtIn, file.defaults ?? {}) : (file.defaults ?? {})\n\n const state = yield* stateDirectory\n const repo = yield* currentRepo.pipe(\n Effect.asSome,\n Effect.catchTag(\"NoRepository\", () => Effect.succeedNone)\n )\n\n const overrides = asked(base, effort)\n const written = Option.isSome(repo)\n ? withRepo(withDefaults(file, defaults), repo.value, overrides)\n : withDefaults(file, merge(defaults, overrides))\n\n if (encode(written) !== encode(file) || Option.isNone(before)) {\n yield* write(written)\n }\n\n yield* Console.log(row(\"review\", opening(written.defaults ?? {})))\n yield* Console.log(row(\"config\", config.path))\n yield* Console.log(row(\"state\", state))\n yield* Console.log(\n Option.isNone(repo)\n ? row(\"repository\", \"none here - run dw-mc init inside a repository to register it\")\n : row(\n \"repository\",\n `${repo.value} (${file.repos?.[repo.value] === undefined ? \"registered\" : \"already registered\"})`\n )\n )\n },\n // The failures worth a sentence become one, so a machine or a file that\n // needs fixing says what to fix instead of printing a stack.\n Effect.catchTag([\"ConfigMalformed\", \"GhUnauthenticated\", \"GhUnavailable\", \"GhUnreadable\"], asUserError)\n )\n).pipe(Command.withDescription(\"Set this machine up and register the repository I am in\"))\n","import { Effect, Option, Schema } from \"effect\"\n\nimport { prKey, storeFor } from \"#adapters/store.ts\"\nimport type { Facts } from \"#domain/bucket.ts\"\n\n/** My local mark that a tracked PR has passed my bar, and what it rests on. */\nexport interface Stamp {\n readonly stamped: boolean\n /** What the mark says, or the first thing that withholds it. */\n readonly reason: string\n}\n\n/**\n * A stamp I took off a pull request by hand, and the head I took it off at.\n *\n * The head is the whole record: a withdrawal is my overruling the computation\n * on code I have read, so it lasts exactly as long as that code is what the\n * pull request is.\n */\nexport const Withdrawal = Schema.Struct({ head: Schema.String })\nexport type Withdrawal = typeof Withdrawal.Type\n\n/**\n * The facts a stamp rests on, which are fewer than a sweep writes down.\n *\n * It is spelled out because the stamp is asked for in two places that know\n * different amounts: `dw-mc status` has the whole of a swept `Facts`, and\n * `dw-mc merge` has what it just read off GitHub and out of the state\n * directory. Both compute the same mark from the same five facts.\n */\nexport type Stampable = Pick<Facts, \"head\" | \"reviewRunHead\" | \"blockingFindings\" | \"checks\" | \"mergeable\">\n\n/** The stamp a pull request has not earned, and the first reason it has not. */\nconst withheld = (reason: string): Stamp => ({ stamped: false, reason })\n\n/** What CI has to say before the stamp will rest on it, which is green and nothing else. */\nexport const whyNotGreen: Record<Facts[\"checks\"], string | null> = {\n green: null,\n red: \"CI is red\",\n pending: \"CI is still running\",\n none: \"no CI ran on this head\"\n}\n\n/** What GitHub has to say about merging, which is that it would. */\nexport const whyNotMergeable: Record<Facts[\"mergeable\"], string | null> = {\n mergeable: null,\n conflicting: \"merge conflict\",\n unknown: \"GitHub has not said whether it merges\"\n}\n\n/**\n * The stamp of one tracked PR: whether it has passed my bar, and why.\n *\n * The mark is computed rather than clicked, so it means the same thing every\n * time: a review run on this head that found nothing blocking, CI green as the\n * repository's `ci.ignore` defines green, and a pull request GitHub would\n * merge. A red CI the flaky classifier excused is still not green here: an\n * excuse is a reason not to fix a check, not a reason to land code behind one,\n * and this mark is what clears `dw-mc merge` (ADR 0008).\n *\n * Nothing about this rests on a previous stamp, which is what makes a head\n * change clear it: facts are about one head, and a run is recorded against one.\n *\n * A withdrawal comes first, because it is the one thing here I decided rather\n * than computed.\n */\nexport const stampFor = (facts: Stampable, withdrawnAt: string | null): Stamp => {\n if (withdrawnAt === facts.head) {\n return withheld(\"withdrawn by hand\")\n }\n if (facts.reviewRunHead !== facts.head) {\n return withheld(\"no review run on this head\")\n }\n if (facts.blockingFindings > 0) {\n return withheld(`${facts.blockingFindings} blocking finding${facts.blockingFindings === 1 ? \"\" : \"s\"}`)\n }\n const ci = whyNotGreen[facts.checks]\n if (ci !== null) {\n return withheld(ci)\n }\n const merge = whyNotMergeable[facts.mergeable]\n if (merge !== null) {\n return withheld(merge)\n }\n return { stamped: true, reason: \"a clean review run on this head, green CI, mergeable\" }\n}\n\n/**\n * The head a stamp was withdrawn at, or null where none was.\n *\n * A withdrawal this version cannot read is one another version of this record\n * wrote, and a stamp is computed from everything else: forgetting it hands the\n * pull request back to the computation, where failing here would cost me the\n * command I asked for.\n */\nexport const withdrawnAt = Effect.fn(\"stamp.withdrawnAt\")(function* (repo: string, number: number) {\n const store = yield* storeFor(\"stamps\", Withdrawal)\n const withdrawal = yield* Effect.orElseSucceed(store.get(prKey(repo, number)), () => Option.none<Withdrawal>())\n return Option.match(withdrawal, { onNone: () => null, onSome: (it) => it.head })\n})\n\n/** Takes the stamp off a pull request at `head`, which is the only head it stays off. */\nexport const withdraw = Effect.fn(\"stamp.withdraw\")(function* (repo: string, number: number, head: string) {\n const store = yield* storeFor(\"stamps\", Withdrawal)\n yield* store.set(prKey(repo, number), { head })\n})\n\n/** The stamp of one tracked PR, with the withdrawal this machine holds against it. */\nexport const stampOf = Effect.fn(\"stamp.stampOf\")(function* (facts: Facts) {\n return stampFor(facts, yield* withdrawnAt(facts.repo, facts.number))\n})\n\n/**\n * Which of these tracked PRs carry a stamp, keyed the way their facts are.\n *\n * A table asks the question of every row at once, and the withdrawals are the\n * only thing here that has to be read off the disk.\n */\nexport const stampedAmong = Effect.fn(\"stamp.stampedAmong\")(function* (facts: ReadonlyArray<Facts>) {\n const marks = yield* Effect.forEach(facts, (it) =>\n Effect.map(stampOf(it), (stamp) => ({ key: prKey(it.repo, it.number), stamped: stamp.stamped }))\n )\n return new Set(marks.filter((mark) => mark.stamped).map((mark) => mark.key))\n})\n","import type { Facts } from \"#domain/bucket.ts\"\nimport type { Stampable } from \"#domain/stamp.ts\"\nimport { stampFor, whyNotGreen, whyNotMergeable } from \"#domain/stamp.ts\"\n\n/** Everything the merge guards are allowed to know about a pull request. */\nexport interface Situation extends Stampable {\n readonly repo: string\n readonly number: number\n /** Whether I opened it, which is the only kind of pull request this lands. */\n readonly mine: boolean\n readonly draft: boolean\n readonly reviewDecision: Facts[\"reviewDecision\"]\n /** The head I took the stamp off at, or null where I took it off none. */\n readonly withdrawnAt: string | null\n}\n\n/**\n * Why GitHub would not call this pull request Ready, or null where it would.\n *\n * Ready is GitHub's opinion and nothing of mine: approved, green, mergeable.\n * A repository that requires no reviewer produces no approval, which is why\n * `none` passes and `review-required` does not - what holds a merge is somebody\n * having been asked and not yet answered.\n *\n * A red CI the flaky classifier excused is still red here. The excuse is a\n * reason not to fix a check; it is not a reason to land code behind one.\n */\nconst whyNotReady = (situation: Situation): string | null => {\n if (situation.reviewDecision === \"changes-requested\") {\n return \"changes are requested\"\n }\n if (situation.reviewDecision === \"review-required\") {\n return \"a review from someone else is still wanted\"\n }\n return whyNotGreen[situation.checks] ?? whyNotMergeable[situation.mergeable]\n}\n\n/**\n * What to do about a pull request that is Ready and carries no stamp.\n *\n * The stamp is withheld for one of three reasons and each has its own next\n * step, so the refusal names that step rather than leaving me to work out which\n * of the three it was. A withdrawal is the one with no command: I took the mark\n * off code I had read, and only that code changing puts it back.\n */\nconst earnsIt = (situation: Situation): string => {\n if (situation.withdrawnAt === situation.head) {\n return \"\\n\\nYou took it off at this head, and it stays off until the head changes.\"\n }\n const next =\n situation.reviewRunHead !== situation.head\n ? {\n command: `dw-mc review ${situation.number}`,\n says: \"That reviews this head, and a run that finds nothing blocking stamps it.\"\n }\n : {\n command: `dw-mc fix ${situation.number}`,\n says:\n \"That opens a session on the findings. \" +\n \"The stamp is back once the head has moved and a review run has read it.\"\n }\n return `\\n\\n ${next.command}\\n\\n${next.says}`\n}\n\n/**\n * Why this pull request is not one to merge, or null where it is.\n *\n * This is the single place the merge guards live, and they carry more than the\n * merge does: it is the one write the tool makes that no reflog of mine undoes\n * (ADR 0008). Two bars have to be clear, because each is blind to what the\n * other sees - GitHub does not know whether anything read the diff, and the\n * stamp does not know whether a reviewer asked for changes.\n *\n * Whose pull request it is comes first, as it does everywhere else: one\n * somebody else opened is none of this tool's business, whatever is true of it.\n * A draft is next, because a pull request I have not offered to anybody is not\n * one to land however green it is.\n *\n * Ready is asked before the stamp so that the refusal names the bar I am\n * actually under. The stamp insists on green CI and a mergeable pull request\n * too, so everything it can be withheld for here is mine rather than GitHub's.\n */\nexport const decide = (situation: Situation): string | null => {\n const where = `${situation.repo}#${situation.number}`\n if (!situation.mine) {\n return `${where} is not mine. dw-mc merges pull requests I author and nothing else.`\n }\n if (situation.draft) {\n return `${where} is a draft. Mark it ready for review before merging it.`\n }\n const ready = whyNotReady(situation)\n if (ready !== null) {\n return `${where} is not Ready: ${ready}. dw-mc merges nothing GitHub would not merge itself.`\n }\n\n const stamp = stampFor(situation, situation.withdrawnAt)\n return stamp.stamped ? null : `${where} is Ready and carries no stamp: ${stamp.reason}.${earnsIt(situation)}`\n}\n","import { Console, Effect, Option } from \"effect\"\nimport { Command } from \"effect/unstable/cli\"\n\nimport { rollupState } from \"#adapters/ci.ts\"\nimport type { ConfigFile } from \"#adapters/config.ts\"\nimport { read as readConfig, settingsFor } from \"#adapters/config.ts\"\nimport { mergeabilityOf, mergePr, prView, reviewDecisionOf, viewer } from \"#adapters/gh.ts\"\nimport { named, prArgument, reading, refuse } from \"#cli/pr.ts\"\nimport { asUserError, userFacing } from \"#cli/sweep.ts\"\nimport { decide } from \"#domain/merge.ts\"\nimport { reviewedAt, short } from \"#domain/review.ts\"\nimport { withdrawnAt } from \"#domain/stamp.ts\"\n\n/**\n * Lands one pull request of mine: squashed, with its branch deleted.\n *\n * This is the write ADR 0008 is about, and the only one the tool makes that no\n * reflog of mine brings back. It is outside ADR 0002's three because it moves a\n * shared branch; everything 0002 bars - comment, reply, thread resolve, label,\n * review, approval, status - still holds here as it does everywhere.\n *\n * The threshold is two bars at one head: Ready, which is GitHub's opinion, and\n * my stamp, which is mine. Each is blind to what the other sees, so the write\n * that cannot be undone clears both.\n *\n * GitHub's half is read live from a fresh `pr view` rather than off the last\n * sweep, the way the rebase and re-run guards are. A stale verdict costs a\n * re-run some CI minutes; here it costs merging code nobody read. My half comes\n * from the state directory, because the review runs and the withdrawal live\n * there and are already scoped to the head this read just named.\n *\n * Typing the command is the confirmation, so it takes no flag. The picker,\n * where a keystroke is cheaper, asks before it dispatches.\n */\nexport const merge = Command.make(\n \"merge\",\n { pr: prArgument },\n Effect.fn(\"merge\")(\n function* ({ pr }) {\n const file: ConfigFile = Option.getOrElse(yield* readConfig, (): ConfigFile => ({}))\n const { number, repo } = yield* named(pr, Object.keys(file.repos ?? {}).toSorted())\n const settings = settingsFor(file, repo)\n\n const [view, me] = yield* reading(`${repo}#${number}`, Effect.all([prView(repo, number), viewer]))\n\n const head = view.headRefOid\n\n yield* refuse(\n decide({\n repo,\n number,\n head,\n mine: view.author?.login === me,\n draft: view.isDraft,\n reviewDecision: reviewDecisionOf(view.reviewDecision),\n checks: rollupState(view.statusCheckRollup, settings.ci.ignore),\n mergeable: mergeabilityOf(view.mergeable),\n ...(yield* reviewedAt(repo, number, head, settings.stamp.blocks_on)),\n withdrawnAt: yield* withdrawnAt(repo, number)\n })\n )\n\n yield* mergePr(repo, number)\n\n yield* Console.log(\n `${repo}#${number} ${short(head)} squash-merged into ${view.baseRefName}, ` +\n `and ${view.headRefName} deleted`\n )\n yield* Console.log(`The squash subject is the pull request title: ${view.title}`)\n },\n Effect.catchTag(userFacing, asUserError)\n )\n).pipe(Command.withDescription(\"Squash-merge a Ready, stamped pull request of mine and delete its branch\"))\n","import type { Facts, Placed } from \"#domain/bucket.ts\"\n\n/**\n * One of the moves the picker can make on a tracked PR.\n *\n * Every one of them is a command that already exists, because the picker is a\n * front door rather than a second implementation: what it does with my answer\n * is run the command I would have typed.\n */\nexport type Action = \"resolve\" | \"rerun\" | \"review\" | \"findings\" | \"fix\" | \"rebase\" | \"withdraw\" | \"merge\"\n\n/** An action on offer, with the words the picker shows for it. */\nexport interface Offer {\n readonly action: Action\n readonly title: string\n /**\n * The question asked before this one runs, where a keystroke is not enough.\n *\n * It rides on the offer rather than being a rule the picker keeps, so what\n * gets confirmed is decided beside what gets offered. Everything without one\n * is cheap or reversible, and asking about those would teach me to answer\n * without reading.\n */\n readonly confirm?: string\n}\n\n/** A tracked PR as the picker sees it: where it sits, and what is true of it now. */\nexport interface Standing {\n readonly placed: Placed\n /** Whether it carries my stamp at this head. */\n readonly stamped: boolean\n /** Whether its repository turned rebase on. */\n readonly rebasing: boolean\n /** The head its flaky CI was already re-run at, or null where none has been. */\n readonly rerunAt: string | null\n}\n\n/**\n * The actions worth offering on one tracked PR, in the order I would take them.\n *\n * An action is offered only where it has something to act on, so the list is\n * what I can do rather than what the binary can spell: a report nothing has\n * written is not on it, and neither is a rebase the repository has not turned\n * on. A command still refuses for its own reasons when I pick it; this only\n * keeps me from picking one that was never going to do anything.\n *\n * Everything is measured against the current head. A review run or a conflict\n * recorded at a head that has gone says nothing about the branch as it is now,\n * which is the same rule the buckets are placed by.\n *\n * Resolving a conflict comes first for the reason a conflict is the first thing\n * that makes a PR mine: it makes every other signal on the PR stale.\n *\n * Merging comes last, and not because it is the least likely. The cursor rests\n * on the first row, and the one action here that no reflog undoes should not be\n * the one a stray return key reaches. It carries a confirmation of its own on\n * top of that. What it is offered on is the bucket and the mark a sweep already\n * computed; the threshold itself is `dw-mc merge`'s, read live when I pick it\n * (ADR 0008), and a draft is left out here because a sweep shows one without\n * ever acting on it.\n */\nexport const actionsFor = ({ placed, rebasing, rerunAt, stamped }: Standing): ReadonlyArray<Offer> => {\n const { facts } = placed\n const reviewed = facts.reviewRunHead === facts.head\n\n return [\n facts.rebaseConflictAt === facts.head\n ? { action: \"resolve\" as const, title: \"Open a session on the rebase conflict\" }\n : null,\n facts.checks === \"red\" && facts.ciFlaky !== null && rerunAt !== facts.head\n ? { action: \"rerun\" as const, title: \"Run the flaky CI again, once\" }\n : null,\n { action: \"review\" as const, title: reviewed ? \"Review this head again\" : \"Run a review\" },\n reviewed ? { action: \"findings\" as const, title: \"Show the review-run report\" } : null,\n reviewed ? { action: \"fix\" as const, title: \"Open a fix session on the findings\" } : null,\n rebasing ? { action: \"rebase\" as const, title: \"Rebase onto the base and push\" } : null,\n stamped ? { action: \"withdraw\" as const, title: \"Withdraw the stamp, until the head changes\" } : null,\n placed.placement.bucket === \"ready\" && stamped && !facts.draft\n ? {\n action: \"merge\" as const,\n title: \"Squash-merge it and delete the branch\",\n confirm: `Squash-merge ${facts.repo}#${facts.number} and delete its branch? Nothing here undoes that.`\n }\n : null\n ].filter((offer) => offer !== null)\n}\n\n/**\n * The arguments the picked action runs as, named the way the commands take it.\n *\n * The pull request is spelled in full, repository and all, so the argument\n * names one pull request whatever else is registered.\n */\nexport const argvFor = (action: Action, facts: Facts): ReadonlyArray<string> => {\n const pr = `${facts.repo}#${facts.number}`\n return action === \"withdraw\" ? [\"stamp\", pr, \"--withdraw\"] : [action, pr]\n}\n","import { Effect, Option, Schema } from \"effect\"\n\nimport { prKey, storeFor } from \"#adapters/store.ts\"\nimport { short } from \"#domain/review.ts\"\nimport type { ChecksState } from \"#terms/pr.ts\"\n\n/**\n * Everything the re-run guards know before the classifier has been asked.\n *\n * It is its own type because asking the classifier costs several reads of\n * GitHub, and the guards here cost nothing: a pull request that is not mine, or\n * a head that has had its re-run, is refused without spending one of them.\n */\nexport interface Unclassified {\n readonly repo: string\n readonly number: number\n /** The head every other fact here is about, and the one the cap is scoped to. */\n readonly head: string\n /** Whether I opened the pull request, which is the only kind whose CI is mine to re-run. */\n readonly mine: boolean\n readonly checks: ChecksState\n /** The head a re-run was already asked for at, or null where none has been. */\n readonly rerunAt: string | null\n /** The workflow runs behind the failing checks, which are what there is to re-run. */\n readonly runs: ReadonlyArray<string>\n}\n\n/** The same, once the classifier has had its say. */\nexport interface Situation extends Unclassified {\n /** Why the classifier excuses this red CI, or null where it calls it mine to fix. */\n readonly flaky: string | null\n}\n\n/**\n * Why this red CI is not one to re-run without asking the classifier, or null\n * where only the classifier is left to ask.\n *\n * The order is what each refusal is about rather than what it costs, and it\n * happens that the cheapest questions are also the first worth asking. Whose\n * pull request it is comes first, because a pull request somebody else opened\n * is none of this tool's business whatever its CI says.\n *\n * The cap is one re-run per head, and it is what keeps this from being a loop:\n * a job that was flaky once and fails again at the same code is a job that is\n * not flaky. It is scoped to the head, as a withdrawn stamp and a conflict\n * record are, so a branch that moved is a branch nothing has re-run yet.\n */\nexport const refusedUnclassified = (situation: Unclassified): string | null => {\n const where = `${situation.repo}#${situation.number}`\n if (!situation.mine) {\n return `${where} is not mine. dw-mc works on pull requests I author and on nothing else.`\n }\n if (situation.checks !== \"red\") {\n return `CI is not red on ${where}, so there is nothing to re-run.`\n }\n if (situation.rerunAt === situation.head) {\n return (\n `${where} has already had its flaky CI re-run at ${short(situation.head)}. ` +\n `One re-run per head is the cap, so a job that fails twice is not flaky.`\n )\n }\n if (situation.runs.length === 0) {\n return (\n `Nothing red on ${where} is a workflow run dw-mc can re-run. ` +\n `A commit status is reported by whatever produced it, and re-running it is that thing's to do.`\n )\n }\n return null\n}\n\n/**\n * Why this red CI is not one to re-run, or null where it is.\n *\n * This is the single place the re-run guards live, and it is the whole of them:\n * the cheap ones first, and then the one that matters. A legitimate failure is\n * reported and never re-run - re-running it would hide the failure behind a\n * second identical one and cost me the minutes it takes.\n */\nexport const decide = (situation: Situation): string | null =>\n refusedUnclassified(situation) ??\n (situation.flaky === null\n ? `CI is red on ${situation.repo}#${situation.number} and nothing excuses it, so it is yours to fix. ` +\n `dw-mc reports a legitimate failure and never re-runs it.`\n : null)\n\n/**\n * A re-run that has been asked for: the head it was asked for at.\n *\n * The head is the whole record, because the head is what the cap is scoped to.\n * A branch that moved has different code, a different CI run and a re-run of\n * its own to earn.\n */\nexport const Rerun = Schema.Struct({\n head: Schema.String\n})\nexport type Rerun = typeof Rerun.Type\n\n/**\n * The head a re-run was last asked for at on this pull request, or null where\n * none has been.\n *\n * A record this version cannot read is one another version of it wrote. Reading\n * it again as nothing costs a flaky pull request one extra re-run, where failing\n * here would cost the command outright.\n */\nexport const rerunFor = Effect.fn(\"rerun.rerunFor\")(function* (repo: string, number: number) {\n const store = yield* storeFor(\"reruns\", Rerun)\n const rerun = yield* Effect.orElseSucceed(store.get(prKey(repo, number)), () => Option.none<Rerun>())\n return Option.getOrNull(rerun)?.head ?? null\n})\n\n/** Writes down that a re-run was asked for at `head`, which is the only head it caps. */\nexport const recordRerun = Effect.fn(\"rerun.recordRerun\")(function* (repo: string, number: number, head: string) {\n const store = yield* storeFor(\"reruns\", Rerun)\n yield* store.set(prKey(repo, number), { head })\n})\n","import { Console, Effect, Option } from \"effect\"\nimport type { Prompt } from \"effect/unstable/cli\"\n\nimport type { ConfigFile } from \"#adapters/config.ts\"\nimport { read as readConfig, settingsFor } from \"#adapters/config.ts\"\nimport { Paint, ink, plain } from \"#adapters/paint.ts\"\nimport { confirm, pick, width } from \"#adapters/picker.ts\"\nimport { prKey } from \"#adapters/store.ts\"\nimport { cells, rule } from \"#cli/row.ts\"\nimport { asUserError, printTroubles, sweeping, userFacing } from \"#cli/sweep.ts\"\nimport { table, truncate, visible } from \"#cli/table.ts\"\nimport type { Facts } from \"#domain/bucket.ts\"\nimport { group } from \"#domain/bucket.ts\"\nimport type { Offer, Standing } from \"#domain/pick.ts\"\nimport { actionsFor, argvFor } from \"#domain/pick.ts\"\nimport { rerunFor } from \"#domain/rerun.ts\"\nimport { stampedAmong } from \"#domain/stamp.ts\"\n\n/** A title cut this short says nothing, so a row that tight loses the column instead. */\nconst shortest = 12\n\n/** The cursor, the marker and the padding a prompt draws around every row of its list. */\nconst frame = 6\n\n/**\n * How much of the screen a prompt leaves for the row itself.\n *\n * A row that wraps takes the whole list's alignment with it. Nothing is piping\n * into a prompt, so no screen to measure means the writing is going somewhere\n * that does not wrap either.\n *\n * A prompt counts the rows it has to erase from the length of what it drew,\n * and colour is length it never shows, so a coloured row has to be shorter by\n * exactly what the colour costs or the prompt erases a line above itself on\n * every keypress.\n */\nconst screenRoom = (screen: number, paint: Paint): number =>\n screen === 0 ? Number.POSITIVE_INFINITY : screen - frame - (paint === plain ? 0 : ink)\n\n/**\n * One row of the list I pick a pull request from: its bucket, and then the row\n * `dw-mc status` gives it.\n *\n * The cells come from there rather than being built again here, so the list I\n * pick from and the table I read are the same rows with the bucket moved onto\n * each of them. A prompt has no headings to group under, so the bucket is named\n * on every row; the rows are still in the order the buckets are acted on.\n */\nconst cellsOf = ({ placed, stamped }: Standing, room: number, paint: Paint): ReadonlyArray<string> =>\n cells(placed, stamped, room, paint, \"named\")\n\n/**\n * Every tracked PR as something to pick, aligned down the whole list.\n *\n * The title is the one cell worth cutting, and then the one worth dropping. The\n * bucket and what the PR waits on are why I am looking at the list at all, and\n * the pull request is how I know which one I am picking; a commit subject I\n * have half of still tells me which pull request it is, and one cut to nothing\n * tells me less than the room it took. A screen too narrow for all four columns\n * loses the title's column rather than the reason's words.\n */\nconst choicesOf = (\n standings: ReadonlyArray<Standing>,\n screen: number,\n paint: Paint\n): ReadonlyArray<Prompt.SelectChoice<Standing>> => {\n const measured = standings.map((it) => cellsOf(it, Number.POSITIVE_INFINITY, paint))\n const widest = (index: number) => Math.max(...measured.map((row) => visible(row[index] ?? \"\")))\n const room = screenRoom(screen, paint) - (widest(0) + widest(1) + widest(3)) - rule.length * 3\n const told = room >= shortest\n\n const rows = table(\n standings.map((it) => {\n const row = cellsOf(it, told ? room : 0, paint)\n return told ? row : [row[0] ?? \"\", row[1] ?? \"\", row[3] ?? \"\"]\n }),\n rule\n )\n return standings.map((standing, index) => ({\n title: truncate(rows[index] ?? \"\", screenRoom(screen, paint)),\n value: standing\n }))\n}\n\nconst actionChoices = (offers: ReadonlyArray<Offer>): ReadonlyArray<Prompt.SelectChoice<Offer>> =>\n offers.map((offer) => ({ title: offer.title, value: offer }))\n\nconst where = (facts: Facts): string => `${facts.repo}#${facts.number}`\n\n/**\n * The front door: pick a pull request, pick what to do with it, read the report.\n *\n * It sweeps first, every time, for the reason `dw-mc status` does: a list I\n * pick from is never one I forgot to refresh. What the sweep could not read is\n * said before the prompt opens, so a pull request missing from the list has its\n * explanation above it rather than after I have chosen.\n *\n * The picker runs nothing of its own. The action I choose is dispatched as the\n * arguments I would have typed, through the same parser and into the same\n * command, so there is one implementation of every action and the picker is\n * only a way of reaching it without remembering the flags.\n *\n * Walking away at any prompt is an answer rather than a failure, and it leaves\n * nothing behind: nothing has been dispatched until every question is answered.\n * An action that carries its own question is asked it here, between the choice\n * and the dispatch, because the picker is where an action costs one keystroke\n * and a merge must never cost only that (ADR 0008).\n */\nexport const picker = <E, R>(dispatch: (argv: ReadonlyArray<string>) => Effect.Effect<void, E, R>) =>\n Effect.fn(\"pick\")(\n function* () {\n const report = yield* sweeping\n\n if (report.repos.length === 0) {\n yield* Console.log(\"No repositories registered. Run dw-mc init inside a repository to register it.\")\n return\n }\n\n const stamped = yield* stampedAmong(report.facts)\n const file: ConfigFile = Option.getOrElse(yield* readConfig, (): ConfigFile => ({}))\n const standings = yield* Effect.forEach(\n group(report.facts).flatMap((grouped) => grouped.placed),\n Effect.fnUntraced(function* (placed) {\n return {\n placed,\n stamped: stamped.has(prKey(placed.facts.repo, placed.facts.number)),\n rebasing: settingsFor(file, placed.facts.repo).rebase.enabled,\n rerunAt: yield* rerunFor(placed.facts.repo, placed.facts.number)\n }\n })\n )\n\n yield* printTroubles(report.troubles)\n if (standings.length === 0) {\n yield* Console.log(\"No open pull requests.\")\n return\n }\n\n const chosen = yield* pick(\"Which pull request?\", choicesOf(standings, yield* width, yield* Paint))\n if (Option.isNone(chosen)) {\n return\n }\n\n const facts = chosen.value.placed.facts\n const offer = yield* pick(`What do I do with ${where(facts)}?`, actionChoices(actionsFor(chosen.value)))\n if (Option.isNone(offer)) {\n return\n }\n\n const question = offer.value.confirm\n if (question !== undefined && !(yield* confirm(question))) {\n yield* Console.log(`Nothing done to ${where(facts)}.`)\n return\n }\n\n yield* dispatch(argvFor(offer.value.action, facts))\n },\n Effect.catchTag(userFacing, asUserError)\n )\n","import { Console, Effect, Option } from \"effect\"\nimport { Command } from \"effect/unstable/cli\"\n\nimport { rollupState } from \"#adapters/ci.ts\"\nimport type { ConfigFile } from \"#adapters/config.ts\"\nimport { read as readConfig, settingsFor } from \"#adapters/config.ts\"\nimport { openPrs, prView, viewer } from \"#adapters/gh.ts\"\nimport { rebaseOnto } from \"#adapters/git.ts\"\nimport { named, prArgument, reading, refuse } from \"#cli/pr.ts\"\nimport { asUserError, userFacing } from \"#cli/sweep.ts\"\nimport { count } from \"#cli/table.ts\"\nimport { decide, recordConflict, stackOf } from \"#domain/rebase.ts\"\nimport { short } from \"#domain/review.ts\"\n\n/**\n * Brings one branch up to date with its base, with the guards that matter more\n * than the rebase does.\n *\n * This is the heavier of the two writes ADR 0002 admits, and it writes one\n * thing: a push to a branch I author, in the repository the branch is in, with\n * a lease, onto the head this run read. Who opened the pull request and where\n * its branch lives are read from GitHub and checked before anything is cut. No\n * comment, reply, thread resolve, label, review, approval or status, here or\n * anywhere. The merge is a write of its own and lives in `dw-mc merge` alone\n * (ADR 0008).\n *\n * Every guard is read live rather than off the last sweep, because each of them\n * is about the branch as it is now: a sweep from ten minutes ago cannot say\n * whether CI is running, and a rebase decided on that would cancel the run I am\n * waiting on.\n *\n * A conflict is written down against the head it conflicted at, with the files\n * it stopped on, which puts the pull request in Needs me until the branch\n * moves. The files are what makes it something to open, and `dw-mc resolve` is\n * what opens it - said here, because a conflict is where the next step stops\n * being obvious, and never taken here, because a session is opened when I ask\n * for one. Nothing half-finished is left behind either way: the rebase aborts\n * and the worktree it ran in goes with the run.\n *\n * A stack is recognised and never driven. The tool does not understand stacks,\n * so what it has to say about one is where the pull request sits in it.\n */\nexport const rebase = Command.make(\n \"rebase\",\n { pr: prArgument },\n Effect.fn(\"rebase\")(\n function* ({ pr }) {\n const file: ConfigFile = Option.getOrElse(yield* readConfig, (): ConfigFile => ({}))\n const { number, repo } = yield* named(pr, Object.keys(file.repos ?? {}).toSorted())\n const settings = settingsFor(file, repo)\n\n const [view, open, me] = yield* reading(\n `${repo}#${number}`,\n Effect.all([prView(repo, number), openPrs(repo), viewer])\n )\n\n yield* refuse(\n decide({\n repo,\n number,\n base: view.baseRefName,\n enabled: settings.rebase.enabled,\n mine: view.author?.login === me,\n fromFork: view.isCrossRepository,\n listed: open.some((it) => it.number === number),\n checks: rollupState(view.statusCheckRollup, settings.ci.ignore),\n stack: stackOf(number, open)\n })\n )\n\n const where = `${repo}#${number}`\n const done = yield* rebaseOnto(repo, number, view.baseRefName, view.headRefName)\n\n if (done._tag === \"up-to-date\") {\n yield* Console.log(`${where} ${short(view.headRefOid)} already on ${view.baseRefName}`)\n return\n }\n if (done._tag === \"conflicted\") {\n yield* recordConflict(repo, number, view.headRefOid, done.paths)\n yield* Console.log(\n `${where} ${short(view.headRefOid)} the rebase onto ${view.baseRefName} conflicted, ` +\n `so it was aborted and nothing was pushed.`\n )\n if (done.paths.length > 0) {\n yield* Console.log(`It stopped on ${count(done.paths.length, \"file\")}:`)\n yield* Effect.forEach(done.paths, (path) => Console.log(` ${path}`))\n }\n\n // A conflict is where the next step stops being obvious, so the step is\n // on screen as itself. Nothing follows it on its own: the session is\n // opened when I ask for it and never because a rebase stopped.\n yield* Effect.forEach([``, ` dw-mc resolve ${number}`, ``], (line) => Console.log(line))\n yield* Console.log(\n `That opens a session on the conflict, in a worktree of your own. ` +\n `The next sweep puts it in Needs me, and it stays there until the branch moves.`\n )\n return\n }\n\n yield* Console.log(\n `${where} ${short(done.before)} → ${short(done.after)} ` +\n `rebased ${count(done.behind, \"commit\")} of ${view.baseRefName} and pushed with a lease`\n )\n },\n Effect.catchTag([...userFacing, \"GitFailed\"], asUserError)\n )\n).pipe(Command.withDescription(\"Rebase one branch onto its base and push it with a lease\"))\n","import { Console, Effect, Option } from \"effect\"\nimport { Command } from \"effect/unstable/cli\"\n\nimport { failedRuns, rerunFailed, rollupState } from \"#adapters/ci.ts\"\nimport type { ConfigFile } from \"#adapters/config.ts\"\nimport { read as readConfig, settingsFor } from \"#adapters/config.ts\"\nimport { prView, viewer } from \"#adapters/gh.ts\"\nimport { named, prArgument, reading, refuse } from \"#cli/pr.ts\"\nimport { asUserError, userFacing } from \"#cli/sweep.ts\"\nimport { count } from \"#cli/table.ts\"\nimport { flakyReason } from \"#domain/flaky.ts\"\nimport { decide, recordRerun, refusedUnclassified, rerunFor } from \"#domain/rerun.ts\"\nimport { short } from \"#domain/review.ts\"\n\n/**\n * Runs a flaky CI again, once, and never a CI that is mine to fix.\n *\n * This is the lighter of the two writes ADR 0002 admits: `gh run rerun\n * --failed` starts the jobs that failed over on a workflow run of a pull\n * request I author. No comment, reply, thread resolve, label, review, approval\n * or status, here or anywhere. The merge is a write of its own and lives in\n * `dw-mc merge` alone (ADR 0008).\n *\n * Every signal is read live rather than off the last sweep, for the reason the\n * rebase guards are: a verdict from ten minutes ago can be about a head that\n * has gone, and spending CI minutes on that is spending them on nothing.\n *\n * The head is written down before a single run is asked for, because the cap is\n * what keeps this from looping and a cap a crash can lose is no cap. The cost of\n * getting it wrong that way is one re-run I have to ask for again; the other way\n * it is a pull request re-running itself until the minutes run out.\n */\nexport const rerun = Command.make(\n \"rerun\",\n { pr: prArgument },\n Effect.fn(\"rerun\")(\n function* ({ pr }) {\n const file: ConfigFile = Option.getOrElse(yield* readConfig, (): ConfigFile => ({}))\n const { number, repo } = yield* named(pr, Object.keys(file.repos ?? {}).toSorted())\n const settings = settingsFor(file, repo)\n\n const [view, me] = yield* reading(`${repo}#${number}`, Effect.all([prView(repo, number), viewer]))\n\n const unclassified = {\n repo,\n number,\n head: view.headRefOid,\n mine: view.author?.login === me,\n checks: rollupState(view.statusCheckRollup, settings.ci.ignore),\n rerunAt: yield* rerunFor(repo, number),\n runs: failedRuns(view.statusCheckRollup, settings.ci.ignore)\n }\n // Asking the classifier costs a handful of reads of GitHub and up to\n // three job logs, so the guards that cost nothing are asked first: a\n // head that has had its re-run is refused without paying for a verdict\n // about it.\n yield* refuse(refusedUnclassified(unclassified))\n\n const flaky = yield* flakyReason(\n repo,\n number,\n view.statusCheckRollup,\n settings.ci.ignore,\n settings.ci.flaky_patterns\n )\n yield* refuse(decide({ ...unclassified, flaky }))\n\n yield* recordRerun(repo, number, view.headRefOid)\n yield* Effect.forEach(unclassified.runs, (run) => rerunFailed(repo, run))\n\n const where = `${repo}#${number}`\n yield* Console.log(\n `${where} ${short(view.headRefOid)} re-ran the failed jobs of ${count(unclassified.runs.length, \"workflow run\")}`\n )\n yield* Console.log(`It is flaky because ${flaky}.`)\n yield* Console.log(`This head gets no second re-run; if it fails again, the failure is yours.`)\n },\n Effect.catchTag(userFacing, asUserError)\n )\n).pipe(Command.withDescription(\"Run a flaky red CI again, once per head\"))\n","import { Effect, Schema } from \"effect\"\n\nimport type { Branch } from \"#domain/rebase.ts\"\nimport { boundary } from \"#domain/rebase.ts\"\nimport { short } from \"#domain/review.ts\"\n\n/** Everything the resolve guards are allowed to know about a pull request. */\nexport interface Situation extends Branch {\n /** Where the pull request is now. */\n readonly head: string\n /** The head a conflict was recorded at, or null where none was. */\n readonly conflictAt: string | null\n}\n\n/**\n * Why this conflict is not one to open a session on, or null where it is.\n *\n * `rebase.enabled` is not asked. That key exists so a force push is never a\n * surprise, and the only push here is my own from the worktree; a session that\n * writes nothing needs no permission to push.\n *\n * The branch's own guards are the same ones a rebase reads, because they are\n * about the branch rather than about what is done to it: a pull request I did\n * not author, one whose branch lives in a fork and one in a stack are none of\n * this tool's business whichever command asks.\n *\n * What is left is this command's own: a conflict is recorded against the head\n * it happened at, so a branch that has moved past it is one nothing here has\n * tried to rebase yet. Either way the answer is the same command, because a\n * conflict to resolve is one a rebase hit.\n */\nexport const decide = (situation: Situation): string | null => {\n const refused = boundary(situation)\n if (refused !== null) {\n return refused\n }\n const where = `${situation.repo}#${situation.number}`\n if (situation.conflictAt === null) {\n return (\n `${where} has no conflict recorded at ${short(situation.head)}. ` +\n `Run dw-mc rebase ${situation.number}: a conflict to resolve is one a rebase hit.`\n )\n }\n if (situation.conflictAt !== situation.head) {\n return (\n `The conflict on ${where} was recorded at ${short(situation.conflictAt)} and the branch is now at ` +\n `${short(situation.head)}. Run dw-mc rebase ${situation.number} to see what the head it is at hits.`\n )\n }\n return null\n}\n\n/** The conflict a session opens on: where it happened, and what it stopped on. */\nexport const Conflicted = Schema.Struct({\n repo: Schema.String,\n number: Schema.Int,\n head: Schema.String,\n /** The branch the pull request merges into, which is what the replay stopped against. */\n base: Schema.String,\n /** What the pull request is for, which is what its conflicting hunks have to keep meaning. */\n title: Schema.String,\n paths: Schema.Array(Schema.String)\n})\nexport type Conflicted = typeof Conflicted.Type\n\n/** The conflict as the JSON the schema defines, rather than as this file spells it. */\nconst asJson = Schema.encodeEffect(Schema.fromJsonString(Conflicted))\n\n/**\n * The prompt a resolve session opens on: what stopped the replay, and the\n * conflict itself as JSON.\n *\n * The paths go in verbatim rather than described, for the reason a fix\n * session's findings do: a re-description is where a path quietly changes. The\n * title goes in because a hunk is resolved against what the pull request is\n * for, and the base because the two sides of every conflict are the branch and\n * it.\n *\n * The rebase stays mine to finish. The session works the files and stops\n * there: continuing the rebase, committing and pushing are three things I do\n * after reading what it did, and a session that did them would be resolving the\n * conflict for me rather than with me.\n */\nexport const promptFor = (conflicted: Conflicted): Effect.Effect<string, Schema.SchemaError> =>\n Effect.map(asJson(conflicted), (json) =>\n [\n `A dw-mc rebase of ${conflicted.repo}#${conflicted.number} onto ${conflicted.base} stopped on a conflict. ` +\n `You are in a worktree standing on the pull request's commits at ${short(conflicted.head)}, ` +\n `with that rebase in progress and the files below unmerged.`,\n `The pull request is \"${conflicted.title}\". Resolve each file so it keeps meaning that and keeps ` +\n `whatever ${conflicted.base} changed underneath it; where the two cannot both hold, say so and stop.`,\n `Do not run git rebase --continue, do not commit and do not push. I read the resolution and do all three ` +\n `myself.`,\n json\n ].join(\"\\n\\n\")\n )\n","import { Console, Effect, Option } from \"effect\"\nimport { CliError, Command, Flag } from \"effect/unstable/cli\"\n\nimport { steeredSession } from \"#adapters/claude.ts\"\nimport type { ConfigFile } from \"#adapters/config.ts\"\nimport { launcherOf, read as readConfig } from \"#adapters/config.ts\"\nimport { openPrs, prView, viewer } from \"#adapters/gh.ts\"\nimport { rebaseInPlace, standingWorktree } from \"#adapters/git.ts\"\nimport { named, prArgument, reading } from \"#cli/pr.ts\"\nimport { asUserError, userFacing } from \"#cli/sweep.ts\"\nimport { count } from \"#cli/table.ts\"\nimport { conflictFor, stackOf } from \"#domain/rebase.ts\"\nimport type { Situation } from \"#domain/resolve.ts\"\nimport { decide, promptFor } from \"#domain/resolve.ts\"\nimport { short } from \"#domain/review.ts\"\n\nconst printFlag = Flag.Boolean(\"print\").pipe(\n Flag.withDefault(false),\n Flag.withDescription(\"Print the prompt a session would open on, and open none\")\n)\n\n/** The domain's word on a conflict that is not one to open, as the command's own failure. */\nconst allowed = (situation: Situation) => {\n const refused = decide(situation)\n return refused === null ? Effect.void : Effect.fail(new CliError.UserError({ cause: refused }))\n}\n\n/**\n * A session on the conflict that stopped a rebase, in a worktree that is mine.\n *\n * `dw-mc rebase` is untouched by this: it aborts, pushes nothing and leaves no\n * partial state. This is the deliberate step afterwards, and it redoes the\n * rebase itself rather than inheriting a half-finished one - the worktree here\n * is one I asked for and it stands, so a rebase in progress in it is the whole\n * point rather than a broken invariant.\n *\n * The tool resolves nothing. It replays onto the base, shows what the replay\n * stopped on and hands an interactive session what conflicted and what the pull\n * request is for; then it is out of the way. Finishing the rebase, committing\n * and pushing are mine, from the worktree, which is why the worktree outlives\n * the session. Nothing here writes to GitHub.\n *\n * `rerere` is turned on in the clone before the replay, so the resolution I\n * make once is one `git` replays by itself the next time a rebase hits it, with\n * no model involved at all. That is also why a replay can go through with\n * nothing to resolve.\n */\nexport const resolve = Command.make(\n \"resolve\",\n { pr: prArgument, print: printFlag },\n Effect.fn(\"resolve\")(\n function* ({ pr, print }) {\n const file: ConfigFile = Option.getOrElse(yield* readConfig, (): ConfigFile => ({}))\n const { number, repo } = yield* named(pr, Object.keys(file.repos ?? {}).toSorted())\n\n const [view, open, me] = yield* reading(\n `${repo}#${number}`,\n Effect.all([prView(repo, number), openPrs(repo), viewer])\n )\n const conflict = yield* conflictFor(repo, number)\n\n yield* allowed({\n repo,\n number,\n mine: view.author?.login === me,\n fromFork: view.isCrossRepository,\n listed: open.some((it) => it.number === number),\n stack: stackOf(number, open),\n head: view.headRefOid,\n conflictAt: conflict === null ? null : conflict.head\n })\n\n /** The conflict as the prompt takes it, around whichever paths are known by then. */\n const conflicted = (paths: ReadonlyArray<string>) => ({\n repo,\n number,\n head: view.headRefOid,\n base: view.baseRefName,\n title: view.title,\n paths\n })\n\n // The prompt on its own, for the session I already have open. Nothing is\n // cut and no replay is run: the paths are the ones the rebase wrote down,\n // which is everything a prompt has to carry.\n if (print) {\n yield* Console.log(yield* promptFor(conflicted(conflict?.paths ?? [])))\n return\n }\n\n const where = `${repo}#${number}`\n const worktree = yield* standingWorktree(repo, number, view.headRefName, \"rebase\")\n yield* Console.log(\n `${where} ${short(view.headRefOid)} replaying onto ${view.baseRefName} in ${worktree.directory}`\n )\n\n const stopped = yield* rebaseInPlace(worktree.directory, view.baseRefName)\n if (stopped._tag === \"replayed\") {\n yield* Console.log(\n `The replay went through, so there is nothing to resolve: git replayed a resolution you made before, ` +\n `or the conflict is gone.`\n )\n yield* Console.log(`The worktree stands where it replayed, and the push onto ${view.headRefName} is yours:`)\n yield* Effect.forEach([``, ` cd ${worktree.directory}`, ` git push`, ``], (line) => Console.log(line))\n yield* Console.log(`Once you have pushed, dw-mc review ${number} reviews the new head as a new run.`)\n return\n }\n\n yield* Console.log(`It stopped on ${count(stopped.paths.length, \"file\")}:`)\n yield* Effect.forEach(stopped.paths, (path) => Console.log(` ${path}`))\n\n const ended = yield* steeredSession({\n launcher: launcherOf(file),\n directory: worktree.directory,\n prompt: yield* promptFor(conflicted(stopped.paths))\n })\n\n yield* Console.log(ended === 0 ? \"The session is over.\" : `The session ended with ${ended}.`)\n yield* Console.log(\"Nothing was committed or pushed for you; the rebase stands where it stopped.\")\n\n // What is left to do is what is left to run, so it is on screen as\n // itself: the rebase is finished and pushed by me, from the worktree,\n // and a sentence about it is one more thing to translate.\n yield* Effect.forEach([``, ` cd ${worktree.directory}`, ` git rebase --continue`, ` git push`, ``], (line) =>\n Console.log(line)\n )\n yield* Console.log(`Once you have pushed, dw-mc review ${number} reviews the new head as a new run.`)\n },\n Effect.catchTag([...userFacing, \"GitFailed\", \"WorktreeHeld\", \"AgentFailed\"], asUserError)\n )\n).pipe(Command.withDescription(\"Open a session on the conflict that stopped a rebase, in a worktree of my own\"))\n","import { Effect, Terminal } from \"effect\"\n\nimport { capture } from \"#adapters/spawner.ts\"\n\n/** A string as AppleScript spells one, so a quotation mark cannot end it early. */\nconst quoted = (text: string): string => `\"${text.replaceAll(\"\\\\\", \"\\\\\\\\\").replaceAll(`\"`, `\\\\\"`)}\"`\n\n/**\n * Says a foreground run has ended, twice: the bell for the terminal I left, and\n * a desktop notification for the window I went to instead.\n *\n * A review run takes minutes, and the whole point of it running in the\n * foreground is that I go and do something else while it does. Neither half is\n * worth failing a finished run over: `osascript` is macOS's, and a machine\n * without it still finished the review.\n */\nexport const announce = Effect.fn(\"notify.announce\")(function* (title: string, message: string) {\n const terminal = yield* Terminal.Terminal\n yield* Effect.ignore(terminal.display(\"\\u0007\"))\n yield* Effect.ignore(\n capture(\"osascript\", [\"-e\", `display notification ${quoted(message)} with title ${quoted(title)}`])\n )\n})\n","/**\n * What a review run is opened on, and the prompt the tool carries.\n *\n * Where a slash command drives the agent's own review, this is a prompt of the\n * tool's own, so my bar is not one agent's idea of a code review. How a turn is\n * spawned belongs to the Claude Code adapter; which turn it is belongs here.\n */\nimport type { ReviewTurn } from \"#terms/review.ts\"\n\n/**\n * The reviewer persona, derived from Addy Osmani's `code-reviewer` agent\n * (`addyosmani/agent-skills`, MIT, see `NOTICE.md`).\n *\n * The five dimensions, their questions and the four severity words are his. The\n * Markdown report template is not: a run here answers as structured output\n * against a schema, so a template that asks for headings would be a second\n * shape to reconcile. `docs/adr/0006-source-layout.md` puts it in the domain\n * because it is text and a decision about text, with nothing outside to reach.\n */\nconst persona = `You are an experienced staff engineer conducting a thorough code review. Evaluate the\nchange and report actionable, categorised findings.\n\nEvaluate every change across these five dimensions.\n\n1. Correctness. Does the code do what the task says it should? Are edge cases handled - null, empty,\n boundary values, error paths? Do the tests verify the behaviour, and are they testing the right\n things? Are there race conditions, off-by-one errors or state inconsistencies?\n2. Readability. Can another engineer understand this without explanation? Are names descriptive and\n consistent with the project's conventions? Is the control flow straightforward? Is related code\n grouped, with clear boundaries?\n3. Architecture. Does the change follow the existing patterns, or introduce a new one, and is a new\n one justified? Are module boundaries maintained? Is the abstraction level appropriate - neither\n over-engineered nor too coupled? Do dependencies flow in the right direction?\n4. Security. Is input validated at the system boundaries? Are secrets kept out of code, logs and\n version control? Is authorisation checked where it is needed? Are queries parameterised and output\n encoded? Does a new dependency carry known vulnerabilities?\n5. Performance. Any N+1 query patterns? Any unbounded loop or unconstrained fetch? Any synchronous\n work that should be asynchronous? Any missing pagination?\n\nGrade every finding with one of four words, and report the severity each maps to:\n\n- Critical, which blocks the merge - a security hole, a risk of data loss, broken functionality - is\n reported as error.\n- Required, which must be addressed before merge - a missing test, the wrong abstraction, poor error\n handling - is reported as error.\n- Optional, which is worth considering and not required - a simpler design, a useful refactor - is\n reported as warning.\n- Nit, which is minor and the author may ignore, and FYI, which is context rather than a request, are\n reported as info.\n\nWork by these rules. Read the tests first: they say what the change intends and what it covers. Read\nthe task or the pull request description before the code. Every Critical and Required finding names a\nspecific fix in its summary. Where you are uncertain, say so in the summary and say what would settle\nit, rather than guessing.`\n\n/** What a review run is about, as much of it as the prompt needs to say. */\nexport interface Reviewing {\n readonly repo: string\n readonly number: number\n readonly title: string\n /** The branch the pull request targets, which is what the change is measured against. */\n readonly base: string\n /** My own review instructions, passed through untouched, or null. */\n readonly prompt: string | null\n}\n\n/**\n * The prompt a review run with no slash command opens on.\n *\n * It says what to review and how to answer, and nothing about how the answer is\n * validated: the schema arrives beside the prompt, so describing it here would\n * be the same shape written twice.\n *\n * `review.prompt` is a passthrough and goes in first, spelled exactly as the\n * file spells it. A repository with its own instructions gets its review with\n * the persona behind it, and the tool does not try to interpret the value.\n */\nexport const reviewPrompt = (reviewing: Reviewing): string =>\n [\n ...(reviewing.prompt === null ? [] : [reviewing.prompt, \"\"]),\n persona,\n \"\",\n `The change is ${reviewing.repo}#${reviewing.number}, \"${reviewing.title}\".`,\n `This worktree stands at its head. \\`git diff ${reviewing.base}...HEAD\\` is the change under`,\n \"review; read whatever file it names in full where the change needs the context.\",\n \"\",\n \"Answer as structured output. Every finding carries the file it is in as a repository path, the\",\n \"line it is at, its severity and a one-sentence summary. The verdict is clean when there is\",\n \"nothing to report, and findings otherwise.\"\n ].join(\"\\n\")\n\n/**\n * What one review run opens on, decided by what the repository configured.\n *\n * A slash command is the review, so the persona stays out of its way and my own\n * instructions ride beside it. Without one the review is the tool's own, and my\n * instructions go in front of the persona. The effort word follows the command\n * because that is where a slash command takes its arguments; a repository that\n * spells its own arguments out sets `review.effort` to null and keeps the line.\n */\nexport const turnFor = (\n review: { readonly command: string | null; readonly effort: string | null; readonly prompt: string | null },\n about: Reviewing\n): ReviewTurn =>\n review.command === null\n ? { _tag: \"prompt\", text: reviewPrompt({ ...about, prompt: review.prompt }) }\n : {\n _tag: \"command\",\n line: [review.command, review.effort].filter((part) => part !== null).join(\" \"),\n instructions: review.prompt\n }\n","import { Console, DateTime, Effect, Exit, Option, Result, Schema } from \"effect\"\nimport { CliError, Command, Flag } from \"effect/unstable/cli\"\n\nimport type { AgentFailed } from \"#adapters/agent.ts\"\nimport { reviewTurns } from \"#adapters/claude.ts\"\nimport type { ConfigFile, Launcher, Settings } from \"#adapters/config.ts\"\nimport { launcherOf, read as readConfig, settingsFor } from \"#adapters/config.ts\"\nimport { comparedFiles, prView } from \"#adapters/gh.ts\"\nimport { withWorktree } from \"#adapters/git.ts\"\nimport type { Reads } from \"#adapters/heartbeat.ts\"\nimport { beating } from \"#adapters/heartbeat.ts\"\nimport { announce } from \"#adapters/notify.ts\"\nimport { stateDirectory, storeFor, textStoreFor } from \"#adapters/store.ts\"\nimport { lines, summary } from \"#cli/findings.ts\"\nimport { named, prArgument } from \"#cli/pr.ts\"\nimport { asUserError, userFacing } from \"#cli/sweep.ts\"\nimport { count } from \"#cli/table.ts\"\nimport { asMarkdown, jsonSchema, Reported } from \"#domain/findings.ts\"\nimport type { Reviewing } from \"#domain/persona.ts\"\nimport { turnFor } from \"#domain/persona.ts\"\nimport type { Asked, Outcome } from \"#domain/review.ts\"\nimport {\n LastReviewed,\n lastRun,\n latestKey,\n detailOf,\n reportDocument,\n reportedBy,\n reportKey,\n ReviewRun,\n runKey,\n short,\n skippedSince\n} from \"#domain/review.ts\"\nimport type { Effort, ReviewTurn } from \"#terms/review.ts\"\n\n/** What a review run has reached for so far, which is what its heartbeat counts. */\ninterface Doing {\n readonly tools: number\n readonly subagents: number\n}\n\n/** What the heartbeat says a run has got through, while it is still going. */\nconst saying =\n (doing: Doing): Reads =>\n (since) =>\n [\"reviewing\", count(doing.tools, \"tool\"), doing.subagents === 0 ? null : count(doing.subagents, \"subagent\"), since]\n .filter((part) => part !== null)\n .join(\" · \")\n\nconst commandFlag = Flag.String(\"command\").pipe(\n Flag.withDescription(\"The slash command this run opens on, over what the repository configured\"),\n Flag.optional\n)\n\nconst promptFlag = Flag.String(\"prompt\").pipe(\n Flag.withDescription(\"The review instructions this run carries, over what the repository configured\"),\n Flag.optional\n)\n\nconst effortFlag = Flag.Literals(\"effort\", [\"low\", \"medium\", \"high\", \"xhigh\", \"max\"]).pipe(\n Flag.withDescription(\"How much this run spends, over what the repository configured\"),\n Flag.optional\n)\n\nconst modelFlag = Flag.String(\"model\").pipe(\n Flag.withDescription(\"The model this run reads the code on, over what the repository configured\"),\n Flag.optional\n)\n\nconst promptOnlyFlag = Flag.Boolean(\"prompt-only\").pipe(\n Flag.withDefault(false),\n Flag.withDescription(\"Review on the prompt alone, whatever slash command the repository configured\")\n)\n\nconst commandOnlyFlag = Flag.Boolean(\"command-only\").pipe(\n Flag.withDefault(false),\n Flag.withDescription(\"Review on the slash command alone, whatever instructions the repository configured\")\n)\n\nconst forceFlag = Flag.Boolean(\"force\").pipe(\n Flag.withDefault(false),\n Flag.withDescription(\"Review even where the re-run rule would skip it\")\n)\n\n/** A flag that names a value beside the flag that clears it: one of the two, never both. */\nconst opposite = (flag: string, given: Option.Option<string>, only: string) =>\n Option.isSome(given) ? [`--${flag} and --${only} say opposite things. Pass one.`] : []\n\n/** What this run is asked, once the flags have had their say over the file. */\nconst asking = (options: {\n readonly settings: Settings\n readonly command: Option.Option<string>\n readonly prompt: Option.Option<string>\n readonly effort: Option.Option<Effort>\n readonly model: Option.Option<string>\n readonly promptOnly: boolean\n readonly commandOnly: boolean\n}) => {\n const clash = [\n ...(options.promptOnly ? opposite(\"command\", options.command, \"prompt-only\") : []),\n ...(options.commandOnly ? opposite(\"prompt\", options.prompt, \"command-only\") : [])\n ]\n if (clash.length > 0) {\n return Effect.fail(new CliError.UserError({ cause: clash.join(\" \") }))\n }\n\n const { review } = options.settings\n return Effect.succeed({\n command: options.promptOnly ? null : Option.getOrElse(options.command, () => review.command),\n effort: Option.getOrElse(options.effort, () => review.effort),\n prompt: options.commandOnly ? null : Option.getOrElse(options.prompt, () => review.prompt),\n model: Option.getOrElse(options.model, () => review.model)\n })\n}\n\n/**\n * What the re-run rule is asked about, read before anything is cut or spawned:\n * the whole point of the rule is not paying for the run.\n *\n * GitHub is asked what changed only where there is a run to measure from and a\n * different head to measure to. Neither is the rule deciding anything - there is\n * simply nothing to compare - and a comparison GitHub would not answer comes\n * back as nothing known rather than as a failure of the command.\n */\nconst askedOf = Effect.fn(\"review.askedOf\")(function* (repo: string, number: number, head: string) {\n const last = Option.getOrNull(yield* lastRun(repo, number))\n const changed =\n last === null || last.head === head\n ? null\n : Option.getOrNull(yield* Effect.option(comparedFiles(repo, last.head, head)))\n return { last, head, changed } satisfies Asked\n})\n\n/** How a run reads on the line above it: what it opens on, and on which model. */\nconst spending = (turn: ReviewTurn, model: string | null): string =>\n [\n turn._tag === \"command\" ? turn.line : \"the tool's own prompt\",\n turn._tag === \"command\" && turn.instructions !== null ? \"with my own instructions\" : null,\n model === null ? null : `model ${model}`\n ]\n .filter((part) => part !== null)\n .join(\", \")\n\n/** What the review came back with, as far as the adapter itself gets. */\ninterface Reviewed {\n /** The session the run happened in. */\n readonly sessionId: string\n /** What the run said in prose, or null where a schema left it none to say. */\n readonly prose: string | null\n /**\n * The findings as they weighed, or whatever stopped them weighing: a turn\n * that could not report, and a turn that answered in a shape that does not\n * validate, are the same kind of failure of the same run.\n */\n readonly reported: Result.Result<typeof Reported.Type, { readonly message: string }>\n}\n\n/** What is written down about the review. */\ninterface Ran {\n readonly sessionId: string | null\n /** The prose the report document is written from, or null where there is none. */\n readonly prose: string | null\n readonly outcome: Outcome\n}\n\n/**\n * The review of the head in the worktree.\n *\n * Whatever the reporting comes to is a value and not a failure: the review is\n * already worth keeping, and a turn that could not report is recorded as the\n * failure it is rather than lost with it.\n */\nconst reviewOn = Effect.fn(\"review.reviewOn\")(function* (options: {\n readonly launcher: Launcher\n readonly directory: string\n readonly turn: ReviewTurn\n readonly model: string | null\n}) {\n const { directory, launcher, model, turn } = options\n // The count is the command's: a tool reached for moves it on, and the line is\n // reworded from where it got to. Where there is no screen the tools go out one\n // to a line, as they did before there was a heartbeat.\n let doing: Doing = { tools: 0, subagents: 0 }\n const run = yield* beating(saying(doing), (says) =>\n reviewTurns({\n launcher,\n directory,\n turn,\n model,\n jsonSchema,\n onTool: (tool) => {\n doing = { tools: doing.tools + 1, subagents: doing.subagents + (tool === \"Agent\" ? 1 : 0) }\n return says(saying(doing), ` · ${tool}`)\n }\n })\n )\n // Both halves answer `message`, which is all `ranBy` reads: a turn that could\n // not report and a turn that answered in a shape that does not validate are\n // the same kind of failure of the same run.\n const answered: Effect.Effect<unknown, { readonly message: string }> = Result.isFailure(run.findings)\n ? Effect.fail(run.findings.failure)\n : Effect.succeed(run.findings.success)\n const reported = yield* Effect.result(\n Effect.flatMap(answered, (output) => Schema.decodeUnknownEffect(Reported)(output))\n )\n return { sessionId: run.sessionId, prose: run.prose, reported } satisfies Reviewed\n})\n\n/**\n * What the review comes to on disk: the findings it reported, or the failure it\n * reached instead.\n *\n * A run that would not start is as much a failure as a turn that answered in a\n * shape that does not validate, and both are recorded: the head has been tried\n * and nothing was found, which is not the same as nothing being wrong.\n */\nconst ranBy = (got: Result.Result<Reviewed, AgentFailed>): Ran => {\n if (Result.isFailure(got)) {\n return { sessionId: null, prose: null, outcome: { _tag: \"failed\", detail: got.failure.detail } }\n }\n const { prose, reported, sessionId } = got.success\n if (Result.isFailure(reported)) {\n return { sessionId, prose, outcome: { _tag: \"failed\", detail: reported.failure.message } }\n }\n const found = reported.success\n return {\n sessionId,\n // A run held to a schema answers in findings and not in prose, so the report\n // kept beside it is written from what it found.\n prose: prose ?? asMarkdown(found),\n outcome: { _tag: \"reported\", verdict: found.verdict, findings: found.findings }\n }\n}\n\n/**\n * The command's own failure where the review reported nothing.\n *\n * The run is written down either way; what the exit code says is whether the\n * review I asked for is one to trust.\n */\nconst unreported = (run: ReviewRun, number: number) => {\n const detail = detailOf(run)\n return detail === null\n ? Effect.void\n : Effect.fail(\n new CliError.UserError({\n cause:\n `The review ran and its findings did not: ${detail}. ` +\n `Run dw-mc review ${number} --force to run it again.`\n })\n )\n}\n\n/**\n * One review run, started by hand, in the foreground.\n *\n * A model runs here and nowhere else in the tool: there is no watch mode and\n * nothing reviews in the background, because a review costs real money and I am\n * the one who decides to spend it.\n *\n * The run happens in a throwaway worktree of the tool's own clone, so what is\n * reviewed is the pull request's head rather than whatever I have open.\n *\n * What it found is kept against that head, which is what takes the pull request\n * out of Needs review run and what a blocking finding later puts into Needs me.\n *\n * The report is printed as well as kept. A run I waited minutes for should not\n * need a second command to read.\n */\nexport const review = Command.make(\n \"review\",\n {\n pr: prArgument,\n command: commandFlag,\n prompt: promptFlag,\n effort: effortFlag,\n model: modelFlag,\n promptOnly: promptOnlyFlag,\n commandOnly: commandOnlyFlag,\n force: forceFlag\n },\n Effect.fn(\"review\")(\n function* ({ command, commandOnly, effort, force, model, pr, prompt, promptOnly }) {\n const file: ConfigFile = Option.getOrElse(yield* readConfig, (): ConfigFile => ({}))\n const { number, repo } = yield* named(pr, Object.keys(file.repos ?? {}).toSorted())\n const settings = settingsFor(file, repo)\n const launcher = launcherOf(file)\n const asked = yield* asking({ settings, command, prompt, effort, model, promptOnly, commandOnly })\n\n const view = yield* prView(repo, number)\n yield* Console.log(`${repo}#${number} ${view.title}`)\n\n const since = force\n ? null\n : skippedSince(yield* askedOf(repo, number, view.headRefOid), settings.review.docs_only)\n if (since !== null) {\n yield* Console.log(\n ` only documentation changed since ${short(since)}, so this run is skipped. ` +\n `Pass --force to review it anyway.`\n )\n return\n }\n\n const about: Reviewing = {\n repo,\n number,\n title: view.title,\n base: view.baseRefName,\n prompt: asked.prompt\n }\n const turn = turnFor(asked, about)\n\n // The bell and the notification are what let me walk away from a run that\n // takes minutes, so they ring however it ended: a run that gave up while I\n // was elsewhere is the one I most need to hear about.\n yield* Effect.gen(function* () {\n const ran = yield* withWorktree(repo, number, (worktree) =>\n Effect.gen(function* () {\n yield* Console.log(` head ${short(worktree.head)} ${spending(turn, asked.model)}`)\n const got = yield* Effect.result(\n reviewOn({ launcher, directory: worktree.directory, turn, model: asked.model })\n )\n return { head: worktree.head, ran: ranBy(got) }\n })\n )\n\n const ranAt = yield* DateTime.now\n const runs = yield* storeFor(\"runs\", ReviewRun)\n const latest = yield* storeFor(\"runs\", LastReviewed)\n const reports = yield* textStoreFor(\"runs\")\n\n const got = ran.ran\n const run: ReviewRun = {\n repo,\n number,\n head: ran.head,\n command: asked.command,\n effort: asked.command === null ? null : asked.effort,\n sessionId: got.sessionId,\n ranAt,\n outcome: got.outcome\n }\n yield* runs.set(runKey(repo, number, run.head), run)\n yield* latest.set(latestKey(repo, number), { head: run.head })\n yield* reports.set(reportKey(repo, number, run.head), reportDocument(run, view.title, got.prose ?? \"\"))\n\n yield* Console.log(\"\")\n const detail = detailOf(run)\n if (detail !== null) {\n yield* Console.log(` reported nothing: ${detail}`)\n } else {\n const found = reportedBy(run)\n if (found !== null) {\n if (got.prose !== null) {\n yield* Console.log(got.prose)\n yield* Console.log(\"\")\n }\n yield* Console.log(summary(found, settings.stamp.blocks_on))\n for (const line of lines(found)) {\n yield* Console.log(` ${line}`)\n }\n }\n }\n\n yield* Console.log(`Recorded against ${short(ran.head)} in ${yield* stateDirectory}`)\n yield* unreported(run, number)\n }).pipe(\n Effect.onExit((exit) =>\n announce(\"dw-mc review\", `${repo}#${number} ${Exit.isSuccess(exit) ? \"reviewed\" : \"could not be reviewed\"}`)\n )\n )\n },\n // No `AgentFailed` here: the run's own failure is caught where it happens\n // and written down as the run's outcome, so it never reaches this far.\n Effect.catchTag([...userFacing, \"GitFailed\"], asUserError)\n )\n).pipe(Command.withDescription(\"Review one pull request on Claude Code, in a throwaway worktree\"))\n","import { Console, Effect, Option } from \"effect\"\nimport { Command, Flag } from \"effect/unstable/cli\"\n\nimport type { ConfigFile } from \"#adapters/config.ts\"\nimport { read as readConfig } from \"#adapters/config.ts\"\nimport { named, prArgument, swept } from \"#cli/pr.ts\"\nimport { asUserError } from \"#cli/sweep.ts\"\nimport { short } from \"#domain/review.ts\"\nimport { stampOf, withdraw } from \"#domain/stamp.ts\"\n\nconst withdrawFlag = Flag.Boolean(\"withdraw\").pipe(\n Flag.withDefault(false),\n Flag.withDescription(\"Take the stamp off this pull request, until its head changes\")\n)\n\n/**\n * The stamp of one pull request, and the one way to take it off by hand.\n *\n * Printing it is the whole command without `--withdraw`: the mark is computed,\n * so what is worth reading is the reason, which is either what it rests on or\n * the first thing that withholds it.\n *\n * `--withdraw` is where I overrule the computation, and it takes the stamp off\n * the head the facts are about rather than whatever GitHub has moved on to\n * since: the stamp I am withdrawing is the one the table showed me, on code I\n * have read, so the withdrawal is pinned to exactly that head. A head that has\n * moved is a stamp the next sweep computes again anyway.\n *\n * Neither path reaches past this machine at all: the stamp is mine, it is\n * computed from what a sweep already wrote down, and nobody else ever sees it\n * (ADR 0001, ADR 0002).\n */\nexport const stampCommand = Command.make(\n \"stamp\",\n { pr: prArgument, withdraw: withdrawFlag },\n Effect.fn(\"stamp\")(\n function* ({ pr, withdraw: byHand }) {\n const file: ConfigFile = Option.getOrElse(yield* readConfig, (): ConfigFile => ({}))\n const { number, repo } = yield* named(pr, Object.keys(file.repos ?? {}).toSorted())\n\n const facts = yield* swept(repo, number)\n const where = `${repo}#${number} ${short(facts.head)}`\n\n if (byHand) {\n yield* withdraw(repo, number, facts.head)\n yield* Console.log(`${where} stamp withdrawn, until the head changes`)\n return\n }\n\n const stamp = yield* stampOf(facts)\n yield* Console.log(`${where} ${stamp.stamped ? \"stamped\" : `not stamped: ${stamp.reason}`}`)\n },\n Effect.catchTag([\"ConfigMalformed\"], asUserError)\n )\n).pipe(Command.withDescription(\"Print my stamp on one pull request, or withdraw it by hand\"))\n","import { Console, Effect } from \"effect\"\nimport { Command } from \"effect/unstable/cli\"\n\nimport { Paint } from \"#adapters/paint.ts\"\nimport { prKey } from \"#adapters/store.ts\"\nimport { cells, heading, rule, titleWidth } from \"#cli/row.ts\"\nimport { asUserError, printTroubles, sweeping, userFacing } from \"#cli/sweep.ts\"\nimport { table } from \"#cli/table.ts\"\nimport type { Grouped } from \"#domain/bucket.ts\"\nimport { group } from \"#domain/bucket.ts\"\nimport { stampedAmong } from \"#domain/stamp.ts\"\n\n/**\n * Every tracked PR under the bucket it sits in, in the order I act on them.\n *\n * The rows of every bucket are measured together, so the columns line up down\n * the whole table rather than restarting under each heading, and they are ruled\n * apart: three columns of prose run into one another without a rule, and the\n * middle one is a commit subject that can end in anything.\n */\nconst lines = (grouped: ReadonlyArray<Grouped>, stamped: ReadonlySet<string>, paint: Paint): ReadonlyArray<string> => {\n const rows = table(\n grouped.flatMap((it) =>\n it.placed.map((placed) =>\n cells(placed, stamped.has(prKey(placed.facts.repo, placed.facts.number)), titleWidth, paint, \"marker\")\n )\n ),\n rule\n )\n let taken = 0\n return grouped.flatMap((it, index) => {\n const mine = rows.slice(taken, taken + it.placed.length)\n taken += it.placed.length\n return [...(index === 0 ? [] : [\"\"]), heading[it.bucket], ...mine.map((row) => ` ${row}`)]\n })\n}\n\n/**\n * The table of what every tracked PR waits on.\n *\n * It sweeps first, every time: a table I read is never one I forgot to refresh.\n */\nexport const status = Command.make(\n \"status\",\n {},\n Effect.fn(\"status\")(\n function* () {\n const report = yield* sweeping\n\n if (report.repos.length === 0) {\n yield* Console.log(\"No repositories registered. Run dw-mc init inside a repository to register it.\")\n return\n }\n\n const grouped = group(report.facts)\n if (grouped.length === 0) {\n yield* Console.log(\"No open pull requests.\")\n }\n for (const line of lines(grouped, yield* stampedAmong(report.facts), yield* Paint)) {\n yield* Console.log(line)\n }\n yield* printTroubles(report.troubles)\n },\n Effect.catchTag(userFacing, asUserError)\n )\n).pipe(Command.withDescription(\"Show which bucket every tracked pull request sits in, and which ones I have stamped\"))\n","import { Console, Effect, FileSystem, Path } from \"effect\"\nimport { Command, Flag } from \"effect/unstable/cli\"\n\nimport { configDirectory, configPath } from \"#adapters/config.ts\"\nimport { holding } from \"#adapters/git.ts\"\nimport type { Paint } from \"#adapters/paint.ts\"\nimport { Paint as PaintService } from \"#adapters/paint.ts\"\nimport { confirm } from \"#adapters/picker.ts\"\nimport { discard, inventory } from \"#adapters/store.ts\"\nimport { yesFlag } from \"#cli/cleanup.ts\"\nimport { table } from \"#cli/table.ts\"\nimport type { Standing } from \"#domain/cleanup.ts\"\nimport { everything, standing, weight } from \"#domain/cleanup.ts\"\n\nconst configFlag = Flag.Boolean(\"config\").pipe(\n Flag.withDefault(false),\n Flag.withDescription(\"Take the configuration file too, and not only the state\")\n)\n\nconst forceFlag = Flag.Boolean(\"force\").pipe(\n Flag.withDefault(false),\n Flag.withDescription(\"Remove a session worktree that still holds work of mine\")\n)\n\n/** A worktree that still holds something, and what it holds. */\ninterface Held {\n readonly at: Standing\n readonly detail: string\n}\n\nconst block = (heading: string, rows: ReadonlyArray<ReadonlyArray<string>>): ReadonlyArray<string> =>\n rows.length === 0 ? [] : [heading, ...table(rows).map((line) => ` ${line}`), \"\"]\n\nconst removes = (\n state: { readonly directory: string; readonly size: string },\n config: string | undefined,\n paint: Paint\n): ReadonlyArray<string> =>\n block(\"Removes\", [\n [paint.dim(state.directory), state.size, \"every record, report, clone and worktree\"],\n ...(config === undefined ? [] : [[paint.dim(config), \"\", \"the runner and every repository registered\"]])\n ])\n\nconst held = (holds: ReadonlyArray<Held>, paint: Paint): ReadonlyArray<string> =>\n block(\n \"Holds work of mine\",\n holds.map((it) => [paint.dim(it.at.directory), it.detail])\n )\n\n/**\n * Takes the tool's own footprint off this machine, which no package manager\n * does.\n *\n * Removing the package removes the binary and nothing else - verified by\n * running it: `pnpm remove` runs no `uninstall` script of any name, so a tool\n * that writes outside its own directory has to say goodbye itself. This is that\n * goodbye, and the one step it cannot take is printed rather than pretended.\n *\n * The state goes in full, because every byte of it is the tool's own record of\n * what it read. The configuration file is the one thing I decided rather than\n * the tool, and it is small, readable and possibly in my dotfiles, so it stays\n * unless `--config` asks for it.\n *\n * A worktree that a fix or resolve session left standing is asked what it still\n * holds before anything is removed, and one holding uncommitted changes or a\n * commit the pull request's head does not have stops the whole command. That is\n * the one thing here no reflog of mine brings back.\n */\nexport const uninstall = Command.make(\n \"uninstall\",\n { config: configFlag, force: forceFlag, yes: yesFlag },\n Effect.fn(\"uninstall\")(function* ({ config: alsoConfig, force, yes }) {\n const fs = yield* FileSystem.FileSystem\n const path = yield* Path.Path\n const paint = yield* PaintService\n\n const found = yield* inventory\n const file = yield* configPath\n const configured = yield* fs.exists(file)\n\n const holds: ReadonlyArray<Held> = yield* Effect.forEach(standing(found), (at) =>\n Effect.map(holding(at.repo, at.number, at.session), (holding_) =>\n holding_._tag === \"held\" ? [{ at, detail: holding_.detail }] : []\n )\n ).pipe(Effect.map((found_) => found_.flat()))\n\n yield* Effect.forEach(\n removes(\n { directory: found.directory, size: weight(everything(found)) },\n alsoConfig && configured ? file : undefined,\n paint\n ),\n (line) => Console.log(line)\n )\n\n if (holds.length > 0) {\n yield* Effect.forEach(held(holds, paint), (line) => Console.log(line))\n if (!force) {\n yield* Console.log(\"Nothing was removed. Push that work or drop it, or run this again with --force.\")\n return\n }\n }\n\n if (!yes && !(yield* confirm(\"Remove it all?\"))) {\n yield* Console.log(\"Nothing was removed.\")\n return\n }\n\n yield* discard(found.directory)\n if (alsoConfig) {\n yield* discard(yield* configDirectory)\n }\n\n yield* Console.log(`Removed ${found.directory}${alsoConfig ? ` and ${path.dirname(file)}` : \"\"}.`)\n if (!alsoConfig && configured) {\n yield* Console.log(`The configuration file stays at ${file}. Run this again with --config to take it too.`)\n }\n yield* Console.log(\"Run pnpm remove -g dw-mc to take the binary, which is all that is left.\")\n })\n).pipe(Command.withDescription(\"Take everything this tool wrote off the machine\"))\n","import { Command } from \"effect/unstable/cli\"\n\nimport { cleanup } from \"#cli/cleanup.ts\"\nimport { comments } from \"#cli/comments.ts\"\nimport { findings } from \"#cli/findings.ts\"\nimport { fix } from \"#cli/fix.ts\"\nimport { init } from \"#cli/init.ts\"\nimport { merge } from \"#cli/merge.ts\"\nimport { picker } from \"#cli/pick.ts\"\nimport { rebase } from \"#cli/rebase.ts\"\nimport { rerun } from \"#cli/rerun.ts\"\nimport { resolve } from \"#cli/resolve.ts\"\nimport { review } from \"#cli/review.ts\"\nimport { stampCommand } from \"#cli/stamp.ts\"\nimport { status } from \"#cli/status.ts\"\nimport { sweepCommand } from \"#cli/sweep.ts\"\nimport { uninstall } from \"#cli/uninstall.ts\"\n\ndeclare const __VERSION__: string | undefined\n\n/**\n * The version the CLI reports: the build stamps it in from `package.json`.\n *\n * Running from source leaves the constant undeclared rather than undefined, so\n * the check has to be `typeof` and the fallback is what a test reads.\n */\nexport const version: string = typeof __VERSION__ === \"string\" ? __VERSION__ : \"0.0.0\"\n\n/** Where the project lives, printed beside the version in the header. */\nexport const projectUrl = \"github.com/dominikwozniak/dw-mc\"\n\nconst subcommands = [\n init,\n review,\n comments,\n findings,\n fix,\n rebase,\n rerun,\n resolve,\n merge,\n sweepCommand,\n status,\n stampCommand,\n cleanup,\n uninstall\n] as const\n\n/**\n * The same subcommands under a root that opens no picker, which is what the\n * picker dispatches into.\n *\n * It exists so that what the picker runs is the command I would have typed,\n * parsed by the parser that would have parsed it. Dispatching into `dwMc`\n * itself would be the command referring to its own definition, and a picker\n * that reached its own root with no arguments would open a second picker.\n */\nconst dispatcher = Command.make(\"dw-mc\").pipe(Command.withSubcommands(subcommands))\n\nexport const dwMc = Command.make(\"dw-mc\", {}, picker(Command.runWith(dispatcher, { version }))).pipe(\n Command.withDescription(\"Keeps the state of my open pull requests on disk and shows what every PR waits on\"),\n Command.withSubcommands(subcommands)\n)\n","import type { Config, Stdio } from \"effect\"\nimport { Effect, Layer } from \"effect\"\nimport type { HelpDoc } from \"effect/unstable/cli\"\nimport { CliConfig, CliOutput, GlobalFlag } from \"effect/unstable/cli\"\n\nimport { paintFor, screened } from \"#adapters/paint.ts\"\nimport { projectUrl, version } from \"#cli/cli.ts\"\n\n/** The tool's name, drawn. Plain ASCII, so a pipe and a paste show one picture. */\nconst logo = [\n \" _\",\n \" __| |__ __ _ __ ___ ___\",\n \" / _` |\\\\ \\\\ /\\\\ / / ___ | '_ ` _ \\\\ / __|\",\n \"| (_| | \\\\ V V / |___| | | | | | || (__\",\n \" \\\\__,_| \\\\_/\\\\_/ |_| |_| |_| \\\\___|\"\n]\n\n/** What stands beside the logo, row by row: what this is, and where it lives. */\nconst beside = [\"\", `mission control ${version}`, projectUrl, \"\"]\n\nconst gap = \" \"\n\n/** The logo, with the tool's name and home set beside it. */\nconst header = (colors: boolean): string => {\n const width = Math.max(...logo.map((line) => line.length))\n const paint = paintFor(colors)\n return logo\n .map((line, index) => {\n const meta = beside[index] ?? \"\"\n // A row with nothing beside it keeps its own width, so no line of the\n // header ends in padding a terminal would still be colouring.\n return meta === \"\" ? paint.cyan(line) : `${paint.cyan(line.padEnd(width))}${gap}${paint.dim(meta)}`\n })\n .join(\"\\n\")\n}\n\n/**\n * The formatter for the two screens the tool introduces itself on, with the\n * header above what the default formatter draws.\n *\n * The root command is the one whose help document lists subcommands, which is\n * what keeps the header off `dw-mc status --help`.\n */\nconst formatter = (colors: boolean): CliOutput.Formatter => {\n const inner = CliOutput.defaultFormatter({ colors })\n const drawn = header(colors)\n return {\n formatHelpDoc: (doc: HelpDoc.HelpDoc) =>\n doc.subcommands === undefined ? inner.formatHelpDoc(doc) : `${drawn}\\n\\n${inner.formatHelpDoc(doc)}`,\n formatVersion: (name: string, printed: string) => `${drawn}\\n\\n${inner.formatVersion(name, printed)}`,\n formatCliError: inner.formatCliError,\n formatError: inner.formatError,\n formatErrors: inner.formatErrors\n }\n}\n\n/**\n * The header, as the layer the entry point provides for the whole CLI.\n *\n * It replaces the formatter under `--help` and `--version` rather than for the\n * run, because a failed parse prints the help screen through the same\n * formatter: decorating that one would bury the error under a logo.\n */\nexport const layer: Layer.Layer<never, Config.ConfigError, Stdio.Stdio> = Layer.unwrap(\n Effect.map(screened, (colors) => {\n const introducing = Effect.provideService(CliOutput.Formatter, formatter(colors))\n return CliConfig.layer({\n builtIns: CliConfig.defaults.builtIns.map((builtIn) =>\n builtIn === GlobalFlag.Help || builtIn === GlobalFlag.Version\n ? GlobalFlag.Action({ flag: builtIn.flag, run: (value, context) => introducing(builtIn.run(value, context)) })\n : builtIn\n )\n })\n })\n)\n","#!/usr/bin/env node\n// oxlint-disable effecttsgo/strict-effect-provide -- the rule exempts entry points, and this file is the one\nimport { NodeRuntime, NodeServices } from \"@effect/platform-node\"\nimport { Effect, Layer } from \"effect\"\nimport { Command } from \"effect/unstable/cli\"\n\nimport { ConfigStore } from \"#adapters/config.ts\"\nimport * as Paint from \"#adapters/paint.ts\"\nimport * as Store from \"#adapters/store.ts\"\nimport { dwMc, version } from \"#cli/cli.ts\"\nimport * as Header from \"#cli/header.ts\"\n\n// Both stores are built here, for the whole CLI rather than for `init` alone:\n// the filesystem store makes its directory as its layer is built, so any run of\n// dw-mc leaves the state and configuration directories behind it.\ndwMc.pipe(\n Command.run({ version }),\n Effect.provide(\n Layer.provideMerge(Layer.mergeAll(ConfigStore.layer, Store.layer, Header.layer, Paint.layer), NodeServices.layer)\n ),\n NodeRuntime.runMain\n)\n"],"mappings":"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"}
|