@descent-vtt/spec-harness 0.19.0 → 0.21.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/.claude-plugin/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +1 -1
- package/CHANGELOG.md +325 -0
- package/README.md +39 -18
- package/dist/briefs.d.ts +45 -0
- package/dist/briefs.js +73 -0
- package/dist/briefs.js.map +1 -1
- package/dist/cli.d.ts +1 -1
- package/dist/cli.js +308 -142
- package/dist/cli.js.map +1 -1
- package/dist/commands.js +7 -2
- package/dist/commands.js.map +1 -1
- package/dist/config.d.ts +13 -1
- package/dist/config.js +14 -3
- package/dist/config.js.map +1 -1
- package/dist/configure.d.ts +94 -3
- package/dist/configure.js +204 -30
- package/dist/configure.js.map +1 -1
- package/dist/fs.d.ts +104 -1
- package/dist/fs.js +383 -11
- package/dist/fs.js.map +1 -1
- package/dist/git.d.ts +88 -2
- package/dist/git.js +155 -8
- package/dist/git.js.map +1 -1
- package/dist/guard.d.ts +20 -1
- package/dist/guard.js +31 -0
- package/dist/guard.js.map +1 -1
- package/dist/hooks.d.ts +59 -0
- package/dist/hooks.js +76 -3
- package/dist/hooks.js.map +1 -1
- package/dist/round.d.ts +27 -1
- package/dist/round.js +75 -17
- package/dist/round.js.map +1 -1
- package/dist/sandbox.js +4 -3
- package/dist/sandbox.js.map +1 -1
- package/dist/server.d.ts +25 -1
- package/dist/server.js +230 -28
- package/dist/server.js.map +1 -1
- package/dist/setup.js +40 -10
- package/dist/setup.js.map +1 -1
- package/dist/siblings.d.ts +29 -6
- package/dist/siblings.js +152 -39
- package/dist/siblings.js.map +1 -1
- package/dist/usage.d.ts +42 -7
- package/dist/usage.js +65 -6
- package/dist/usage.js.map +1 -1
- package/dist/workspace.d.ts +45 -3
- package/dist/workspace.js +143 -13
- package/dist/workspace.js.map +1 -1
- package/package.json +3 -3
package/dist/usage.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"usage.js","sourceRoot":"","sources":["../src/usage.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * What a command line that parsed has still to be before it runs.\n *\n * The parser says what was typed. Whether it names anything was left to each\n * command, and most never asked: an option another command reads was taken\n * and did nothing, an argument no command asked for was dropped, and an\n * option given an empty value, as `--root \"$DIR\"` is where the variable is\n * not set, was read as the option left out. Each run then answered a\n * question nobody had put, exit 0, where a tool never falls back to its\n * defaults and reports clean (spec-core ADR-0005).\n *\n * Pure: the command line as it was read, and the line that refuses it, or\n * `null`. The one thing only the disk knows, whether `--root` is a\n * directory, is asked of the caller.\n */\n\nimport { resolve } from 'node:path';\n\nimport { sameId } from './branch.js';\nimport { literal } from './literal.js';\n\n/** A command line as the parser read it; what `parseOptions` returns is one. */\nexport interface CommandLine {\n readonly command: string | undefined;\n readonly positionals: readonly string[];\n /** The long name of every option on it, in the order given. */\n readonly given: readonly string[];\n /** Every value of each option that takes one, by its long name, in the order given; the fields below hold the last. */\n readonly written: Readonly<Record<string, readonly string[]>>;\n readonly brief: string | undefined;\n readonly base: string | undefined;\n readonly root: string | undefined;\n readonly paths: readonly string[];\n readonly reason: string | undefined;\n readonly options: readonly string[];\n readonly recommend: string | undefined;\n readonly show: string | undefined;\n readonly id: string | undefined;\n}\n\n/** Whether a value names nothing: empty, or only space. */\nexport function isBlank(value: string): boolean {\n return value.trim() === '';\n}\n\ninterface Valued {\n readonly of: (line: CommandLine) => readonly string[];\n /** What is said of a value that names nothing, and what to do instead. */\n readonly says: string;\n}\n\n// Every value an option that takes one was given, so that an empty one is\n// refused wherever it stands: `--root \"\" --root .` names one directory twice\n// as the root is read, and the empty value is still the unset variable it\n// most often is. An option left out has no value to refuse, and a value that\n// names something is passed over as none is: the empty list is equivalent\n// to its mutant.\nconst every =\n (option: string) =>\n (line: CommandLine): readonly string[] =>\n line.written[option] ?? [];\n\n/**\n * The options that take a value, and what an empty one fails to name.\n * `--format` and `--at` are not here: each takes one of a few words, and the\n * parser refuses any other, the empty one with them. Nor is `--note`, which\n * `rule` refuses by name itself.\n */\nconst VALUED = literal({\n brief: { of: every('brief'), says: \"names no brief; give it a brief's id, or leave it out for the one SPEC_BRIEF or the branch names\" },\n base: { of: every('base'), says: 'names no commit; give it a branch or a commit, or leave it out for the configured base' },\n root: { of: every('root'), says: 'names no directory; give it one, or leave it out to run in the current directory' },\n path: { of: (line) => line.paths, says: 'names no file; give it the path of a file' },\n reason: { of: every('reason'), says: 'gives no reason; say why the round cannot be done without the file' },\n option: { of: (line) => line.options, says: 'names no choice; give it \"<label>: <cost>\", or leave it out' },\n recommend: { of: every('recommend'), says: 'recommends nothing; say which option and why, or leave it out' },\n show: { of: every('show'), says: 'names no escalation; give it an id, as spec-harness escalate --list prints them' },\n id: { of: every('id'), says: \"names no probe; give it a probe's id, or leave it out to run every probe\" },\n}) satisfies Record<string, Valued>;\n\n/**\n * Whether two values of an option that takes one ask for one thing, as the\n * option reads them: a brief by its id as spec-brief's names are matched,\n * space around it dropped and `12` the brief `012` (ADR-0004); a root by the\n * directory it names from `cwd`, the one an empty root names on the hook's\n * line among them; any other as it is written, since each is read so, a\n * format, a probe's position, an id or text.\n */\nfunction asksOne(option: string, first: string, other: string, cwd: string): boolean {\n if (option === 'brief') return sameId(first.trim(), other.trim());\n if (option === 'root') return resolve(cwd, first) === resolve(cwd, other);\n return first === other;\n}\n\n/**\n * The first option that takes one value and was given two that read as two,\n * named with both as they were written; `null` where none was. The parser\n * keeps the last, and the earlier went unread with no word of it:\n * `doctor --root nowhere --root .` ran in `.`, and `--brief 404 --brief 001`\n * was brief 001 (spec-core ADR-0005). The same value given twice, however it\n * is spelled, is the option once, and the last is the one read. `cwd` is\n * where a relative root is read from. A list, `--path` and `--option`, has\n * every value it is given.\n */\nexport function twoValues(line: Pick<CommandLine, 'written'>, cwd: string): string | null {\n for (const [option, values] of Object.entries(line.written)) {\n const first = values[0]!;\n const other = values.find((value) => !asksOne(option, first, value, cwd));\n if (other !== undefined) return `--${option} is given twice, as ${JSON.stringify(first)} and as ${JSON.stringify(other)}, and one of the two would go unread`;\n }\n return null;\n}\n\nexport type ValuedOption = keyof typeof VALUED;\n\n/**\n * The line for a value that names nothing. `subject` is the input as its\n * caller knows it: `--brief` on a command line, `\"brief\"` in a tool call. The\n * value is shown as JSON, where a tab or a line break in it can be seen.\n */\nexport function emptyValue(subject: string, option: ValuedOption, value: string): string {\n return `${subject} is ${JSON.stringify(value)}, which ${VALUED[option].says}`;\n}\n\ninterface Shape {\n /** The options the command reads, by their long names. */\n readonly options: readonly string[];\n /** The most arguments it takes, with that said for the refusal of one more; `null` where it takes any number. */\n readonly limit: { readonly most: number; readonly takes: string } | null;\n /**\n * What an argument of it is, and what to do about an empty one; absent\n * where it takes none, or says so itself. `option` is the option that\n * names the same thing, where there is one: the two together are refused.\n * Only a brief is named both ways.\n */\n readonly argument?: { readonly role: string; readonly instead: string; readonly option?: 'brief' };\n /** The options whose empty value the command says in its own report. */\n readonly reports?: readonly string[];\n /**\n * The options that make the command another one, each with the only\n * options it reads beside it. The first on the list that is given decides,\n * as the command itself decides.\n */\n readonly modes?: readonly { readonly option: string; readonly beside: readonly string[] }[];\n}\n\nconst NO_ARGUMENT = literal({ limit: { most: 0, takes: 'no argument' } });\nconst ONE_BRIEF = literal({\n limit: { most: 1, takes: \"one argument at most, a brief's id\" },\n argument: { role: 'the brief', instead: \"give it a brief's id, or leave it out for the one --brief, SPEC_BRIEF or the branch names\", option: 'brief' },\n});\n\n/**\n * Every command, with what it reads of a command line. An option is listed\n * for a command only where the command's answer depends on it: `init`\n * chooses the base itself and the server is told the brief by each call, so\n * neither takes `--base` or `--brief`, and taking them would tell someone\n * the base or the brief had been set.\n */\nconst SHAPE = literal({\n context: { options: ['brief', 'base', 'root', 'format'], ...ONE_BRIEF },\n guard: {\n options: ['brief', 'base', 'root', 'format', 'strict'],\n limit: null,\n argument: { role: 'a path', instead: 'give it the paths the round would write' },\n },\n hook: { options: ['brief', 'base', 'root'], limit: { most: 1, takes: 'one argument, \"claude\" or \"git\"' } },\n // `--list` and `--show` print what waits and raise nothing: neither reads\n // the parts of a request, nor a brief, since what waits is every round's.\n escalate: {\n options: ['brief', 'root', 'format', 'path', 'reason', 'option', 'recommend', 'list', 'show'],\n ...NO_ARGUMENT,\n modes: [\n { option: 'list', beside: ['root', 'format'] },\n { option: 'show', beside: ['root', 'format'] },\n ],\n },\n rule: {\n options: ['root', 'format', 'allow', 'deny', 'note'],\n limit: { most: 1, takes: \"one argument, the escalation's id\" },\n argument: { role: 'the escalation', instead: 'give it an id, as spec-harness escalate --list prints them' },\n },\n rulings: { options: ['brief', 'base', 'root', 'format'], ...ONE_BRIEF },\n audit: { options: ['brief', 'base', 'root', 'format', 'strict'], ...ONE_BRIEF },\n probe: { options: ['brief', 'base', 'root', 'format', 'id', 'at'], ...ONE_BRIEF },\n premises: { options: ['brief', 'root', 'format', 'strict'], ...NO_ARGUMENT },\n init: { options: ['root', 'format', 'write', 'git-hook'], ...NO_ARGUMENT },\n mcp: { options: ['root'], ...NO_ARGUMENT },\n // A diagnosis says what is wrong with what it was given, where a command\n // that acts on it stops: a brief or a base that names nothing is a line of\n // doctor's report.\n doctor: { options: ['brief', 'base', 'root', 'format', 'strict'], ...NO_ARGUMENT, reports: ['brief', 'base'] },\n}) satisfies Record<string, Shape>;\n\n/** The options every command answers instead of running, which no shape lists. */\nconst ANSWERS: readonly string[] = ['help', 'version'];\n\n/** Every command there is: the command line dispatches on these names and no other. */\nexport type Command = keyof typeof SHAPE;\n\n// A map, since a command is a word a person types and an object answers to\n// more names than it was given: `constructor` has no shape.\nconst SHAPES: ReadonlyMap<string, Shape> = new Map(Object.entries(SHAPE));\n\nfunction listed(options: readonly string[]): string {\n const names = options.map((option) => `--${option}`);\n // Every command reads at least `--root`, so a list is never empty.\n if (names.length === 1) return `its only option is ${names[0] as string}`;\n return `its options are ${names.slice(0, -1).join(', ')} and ${names.at(-1)!}`;\n}\n\n/**\n * Whether a line is Claude Code's hook, by its command and the argument\n * after it. It is exempt from every refusal here, and read as each release\n * since 0.1.0 read it: the line lives in settings files and in the plugin of\n * every release, which this one cannot rewrite, and exit 2 from it holds\n * every write of a session, the write that would fix the line among them.\n * A line the parser cannot read is asked the same, and the hook says it\n * with exit 1 (cli.ts).\n */\nexport function isClaudeHook(command: string | undefined, argument: string | undefined): boolean {\n return command === 'hook' && argument === 'claude';\n}\n\n/** The words for a `--root` that is a file, or is not there, wherever it is said. */\nexport function noDirectory(root: string): string {\n return `--root is \"${root}\", which is not a directory`;\n}\n\n/** What is wrong with the line of Claude Code's hook, and what mends it. */\nexport interface LineFault {\n readonly wrong: string;\n readonly mend: string;\n}\n\n/**\n * What Claude Code's hook does with its own line, as far as the line decides\n * it: it guards, in the root the line gives it where it gives one and under\n * the brief it names where it names one; or the guard is off, for a fault\n * the hook says with exit 1 and holds no write for; or every write is held,\n * exit 2, for a brief the line names that is not one (ADR-0005). The hook\n * reads its line through the functions below, and `doctor` reads a line it\n * finds in Claude Code's settings through the same ones, so what it says of\n * a line is what the hook does with it.\n */\nexport type HookLine = Guards | GuardOff | WritesHeld;\n\nexport interface Guards {\n readonly state: 'guards';\n readonly root: string | undefined;\n /**\n * The brief the line names with `--brief`, as written. Whether it is a\n * brief is spec-brief's to say, which the caller asks. Absent where the\n * line names none.\n */\n readonly brief?: string;\n /**\n * The work tree the root is in, where that is not the project's own and\n * holds it: the hook opens the work tree its root is in, so a write to a\n * file of the project is checked there, by that work tree's briefs, under\n * the path the file has in it. Only git can tell, so the caller asks it.\n * Absent where the line gives no root, or one in the project's work tree.\n */\n readonly holder?: OtherTree & { readonly path: string };\n}\n\n/** A work tree the root on the hook's line is in that is not the project's own. */\nexport interface OtherTree {\n /** Its top, as git names it. */\n readonly top: string;\n /** Whether it is a work tree of the project's own repository, linked or main, where it is otherwise another repository's. */\n readonly ours: boolean;\n}\n\n/** How such a work tree is named: by its top, and whose it is. */\nexport function namedTree({ top, ours }: OtherTree): string {\n return `${top}, ${ours ? 'another work tree of this repository' : 'the work tree of another repository'}`;\n}\n\nexport interface GuardOff {\n readonly state: 'off';\n readonly fault: LineFault;\n /**\n * Set where the hook says nothing of the fault and exits 0, so that no\n * run of it fails: a root outside any work tree, or in a work tree that\n * does not hold the project. Every other fault it says, with exit 1.\n */\n readonly silent?: true;\n /**\n * What goes unchecked, where that is not every write: under a root in a\n * work tree that does not hold the project, the hook checks a write to a\n * file of that work tree, and none to a file of the project.\n */\n readonly unchecked?: string;\n}\n\nexport interface WritesHeld {\n readonly state: 'held';\n /** What is said of the name, by what named the brief (workspace.ts). */\n readonly wrong: string;\n}\n\n/**\n * A line under which the round has no brief to be checked against, since\n * what names one names a brief spec-brief does not know, or one it has\n * archived: the hook holds the write, exit 2, and every write after it until\n * the name is mended (ADR-0004). `wrong` is what is said of the name.\n */\nexport function heldHookLine(wrong: string): WritesHeld {\n return { state: 'held', wrong };\n}\n\n/**\n * A line the parser refused: an option no command has, an option left\n * without its value, a `--format` or an `--at` that is none, `--help` beside\n * `--version`; or a line it read with two values for an option that takes\n * one, of which it cannot tell the one meant ({@link twoValues}). The\n * parser's words are kept, on one line: what node says of an option that may\n * have lost its value is three.\n */\nexport function unreadHookLine(said: string): GuardOff {\n return { state: 'off', fault: { wrong: `the hook's line cannot be read (${said.split('\\n').join(' ')})`, mend: 'mend it in Claude Code\\'s settings, where init writes \"hook claude\" and nothing after it' } };\n}\n\n/**\n * A line that parsed, by the root it gives. An empty root is the directory\n * the hook was started in: git starts there when it is handed no directory,\n * so the hook has guarded there for an empty root in every release. A root\n * that is a file, or is not there, is one git starts in neither of, which\n * read as a session outside a work tree, exit 0 and no word, and the guard\n * someone had configured was off.\n */\nexport function readHookLine(root: string | undefined, cwd: string, isDirectory: (path: string) => boolean): Guards | GuardOff {\n const given = root === '' ? cwd : root;\n if (given !== undefined && !isDirectory(given)) {\n return { state: 'off', fault: { wrong: noDirectory(given), mend: \"give the hook's line a directory that exists, or leave --root out for the project Claude Code names\" } };\n }\n return { state: 'guards', root: given };\n}\n\n/**\n * A line whose root is a directory outside any git work tree. The hook\n * reads that as a session outside one, where it has nothing to guard: it\n * checks no write and says nothing, exit 0 (ADR-0005), and that stays. Only\n * git can tell such a root, so the caller asks it. A line that carries one\n * is a guard someone set up that checks nothing, and `doctor` says it as\n * off.\n */\nexport function outsideHookLine(root: string): GuardOff {\n return {\n state: 'off',\n silent: true,\n fault: {\n wrong: `--root is \"${root}\", a directory outside any git work tree, which the hook reads as a session outside one, exit 0 and no word`,\n mend: \"give the hook's line a directory in the work tree, or leave --root out for the project Claude Code names\",\n },\n };\n}\n\n/**\n * A line whose root is in a git work tree that does not hold the project:\n * another repository beside it, or a work tree inside it, a repository of\n * its own, a submodule or a linked worktree. The hook opens the work tree\n * its root is in and decides by that work tree's briefs. A file of the\n * project is outside it, which the hook reads as a file outside the\n * repository: it says nothing, exit 0, as it has in every release, and that\n * stays. So no write to a file of the project is checked under the line,\n * and `doctor` says it as a guard that is off. Only git can tell such a\n * root, so the caller asks it.\n */\nexport function elsewhereHookLine(root: string, tree: OtherTree): GuardOff {\n return {\n state: 'off',\n silent: true,\n unchecked: 'no write to a file of this project is checked',\n fault: {\n wrong: `--root is \"${root}\", a directory in ${namedTree(tree)}, which does not hold this project: the hook checks a write to a file of that work tree, by its briefs, and passes any other with no word, exit 0`,\n mend: \"give the hook's line a directory in this work tree, or leave --root out for the project Claude Code names\",\n },\n };\n}\n\n/**\n * A line that guards under a root in a work tree which holds the project\n * and is not its own: a repository the project is nested in, or the main\n * work tree around a linked one. `path` is the project's top as that work\n * tree names it.\n */\nexport function holderHookLine(line: Guards, tree: OtherTree, path: string): Guards {\n return { ...line, holder: { ...tree, path } };\n}\n\n/**\n * What Claude Code's hook does with the words it is handed, or `null` for\n * words that are not its line: read by the parser the command line reads\n * every line with, and known by its command and the argument after it on a\n * line that parsed and on one that did not, as the command line knows it\n * (cli.ts). `parse` is that parser, which throws its refusal with the words\n * it could still read.\n *\n * A line that guards says which brief it names, where it names one: the hook\n * passes over a `--brief` that is empty, as every release has, and holds\n * every write under one that names no brief, which only spec-brief can tell.\n * A line with two values for an option that takes one is a line the hook\n * cannot read, whichever option it is, read from `cwd` as the hook reads it\n * from where it is started (cli.ts).\n */\nexport function claudeHookLine(\n words: readonly string[],\n parse: (argv: readonly string[]) => CommandLine,\n cwd: string,\n isDirectory: (path: string) => boolean,\n): HookLine | null {\n let line: CommandLine;\n try {\n line = parse(words);\n } catch (error) {\n const refused = error as { readonly message: string; readonly words: readonly string[] };\n return isClaudeHook(refused.words[0], refused.words[1]) ? unreadHookLine(refused.message) : null;\n }\n if (!isClaudeHook(line.command, line.positionals[0])) return null;\n const twice = twoValues(line, cwd);\n if (twice !== null) return unreadHookLine(twice);\n const read = readHookLine(line.root, cwd, isDirectory);\n return read.state === 'guards' && line.brief !== undefined && !isBlank(line.brief) ? { ...read, brief: line.brief } : read;\n}\n\n/**\n * Why the command line is refused, or `null` when the command can run:\n * an option it does not read, alone or beside another, an argument it does\n * not take, a brief named twice, a value or an argument that names nothing,\n * a `--root` that is no directory. Asked once a command is known, and before\n * `--help` or `--version` answers: a line that is wrong is refused with\n * either on it, as one the parser refuses always was (spec-core ADR-0005).\n * A command with no shape is none, which its caller says.\n */\nexport function refusal(line: CommandLine, isDirectory: (path: string) => boolean): string | null {\n const { command, positionals } = line;\n // No shape is kept under `undefined`, so the first check is the compiler's,\n // and its mutant is equivalent.\n if (command === undefined) return null;\n const shape = SHAPES.get(command);\n if (shape === undefined || isClaudeHook(command, positionals[0])) return null;\n // What the line asks the command, beside the help or the version it may\n // ask for instead, which every command answers.\n const asked = line.given.filter((option) => !ANSWERS.includes(option));\n const stray = asked.find((option) => !shape.options.includes(option));\n if (stray !== undefined) return `${command} does not take --${stray}; ${listed(shape.options)}`;\n // An option the command reads, beside one that leaves it unread:\n // `escalate --list --path src/a.ts` printed the list, exit 0, and the\n // request it also spelled out was never raised.\n const mode = shape.modes?.find((candidate) => asked.includes(candidate.option));\n if (mode !== undefined) {\n const unread = asked.find((option) => option !== mode.option && !mode.beside.includes(option));\n if (unread !== undefined) return `${command} --${mode.option} does not take --${unread}; beside --${mode.option} ${listed(mode.beside)}`;\n }\n if (shape.limit !== null && positionals.length > shape.limit.most) {\n return `${command} takes ${shape.limit.takes}, not ${positionals.map((argument) => JSON.stringify(argument)).join(', ')}`;\n }\n // One thing named two ways, whatever the two hold: the argument won with\n // no word, so `audit 012 --brief 013` audited 012 for someone who had\n // also typed 013.\n const { argument } = shape;\n if (argument?.option !== undefined && positionals.length > 0 && line.given.includes(argument.option)) {\n return `${command} was given ${argument.role} twice, as its argument ${JSON.stringify(positionals[0])} and as --${argument.option} ${JSON.stringify(line[argument.option])}; give one of them`;\n }\n for (const [option, valued] of Object.entries(VALUED) as [ValuedOption, Valued][]) {\n if (shape.reports?.includes(option)) continue;\n const empty = valued.of(line).find(isBlank);\n if (empty !== undefined) return emptyValue(`--${option}`, option, empty);\n }\n const empty = positionals.find(isBlank);\n if (shape.argument !== undefined && empty !== undefined) {\n return `${command} was given ${JSON.stringify(empty)} as ${shape.argument.role}, which names none; ${shape.argument.instead}`;\n }\n // An empty one was refused above, so this is a path that was meant: a file,\n // or a directory that is not there. git starts in neither, and the run\n // ended saying the path was outside a work tree, which named neither the\n // option nor what was wrong with what it was given.\n if (line.root !== undefined && !isDirectory(line.root)) {\n return `${noDirectory(line.root)}; give it one that exists, or leave it out to run in the current directory`;\n }\n return null;\n}\n"]}
|
|
1
|
+
{"version":3,"file":"usage.js","sourceRoot":"","sources":["../src/usage.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * What a command line that parsed has still to be before it runs.\n *\n * The parser says what was typed. Whether it names anything was left to each\n * command, and most never asked: an option another command reads was taken\n * and did nothing, an argument no command asked for was dropped, and an\n * option given an empty value, as `--root \"$DIR\"` is where the variable is\n * not set, was read as the option left out. Each run then answered a\n * question nobody had put, exit 0, where a tool never falls back to its\n * defaults and reports clean (spec-core ADR-0005).\n *\n * Pure: the command line as it was read, and the line that refuses it, or\n * `null`. The one thing only the disk knows, whether `--root` is a\n * directory, is asked of the caller.\n */\n\nimport { resolve } from 'node:path';\n\nimport { sameId } from './branch.js';\nimport { literal } from './literal.js';\n\n/** A command line as the parser read it; what `parseOptions` returns is one. */\nexport interface CommandLine {\n readonly command: string | undefined;\n readonly positionals: readonly string[];\n /** The long name of every option on it, in the order given. */\n readonly given: readonly string[];\n /** Every value of each option that takes one, by its long name, in the order given; the fields below hold the last. */\n readonly written: Readonly<Record<string, readonly string[]>>;\n readonly brief: string | undefined;\n readonly base: string | undefined;\n readonly root: string | undefined;\n readonly paths: readonly string[];\n readonly reason: string | undefined;\n readonly options: readonly string[];\n readonly recommend: string | undefined;\n readonly show: string | undefined;\n readonly id: string | undefined;\n /** Whether the line asks for the help, or for the version, which every command answers in place of running. */\n readonly help: boolean;\n readonly version: boolean;\n}\n\n/** Whether a value names nothing: empty, or only space. */\nexport function isBlank(value: string): boolean {\n return value.trim() === '';\n}\n\ninterface Valued {\n readonly of: (line: CommandLine) => readonly string[];\n /** What is said of a value that names nothing, and what to do instead. */\n readonly says: string;\n}\n\n// Every value an option that takes one was given, so that an empty one is\n// refused wherever it stands: `--root \"\" --root .` names one directory twice\n// as the root is read, and the empty value is still the unset variable it\n// most often is. An option left out has no value to refuse, and a value that\n// names something is passed over as none is: the empty list is equivalent\n// to its mutant.\nconst every =\n (option: string) =>\n (line: CommandLine): readonly string[] =>\n line.written[option] ?? [];\n\n/**\n * The options that take a value, and what an empty one fails to name.\n * `--format` and `--at` are not here: each takes one of a few words, and the\n * parser refuses any other, the empty one with them. Nor is `--note`, which\n * `rule` refuses by name itself.\n */\nconst VALUED = literal({\n brief: { of: every('brief'), says: \"names no brief; give it a brief's id, or leave it out for the one SPEC_BRIEF or the branch names\" },\n base: { of: every('base'), says: 'names no commit; give it a branch or a commit, or leave it out for the configured base' },\n root: { of: every('root'), says: 'names no directory; give it one, or leave it out to run in the current directory' },\n path: { of: (line) => line.paths, says: 'names no file; give it the path of a file' },\n reason: { of: every('reason'), says: 'gives no reason; say why the round cannot be done without the file' },\n option: { of: (line) => line.options, says: 'names no choice; give it \"<label>: <cost>\", or leave it out' },\n recommend: { of: every('recommend'), says: 'recommends nothing; say which option and why, or leave it out' },\n show: { of: every('show'), says: 'names no escalation; give it an id, as spec-harness escalate --list prints them' },\n id: { of: every('id'), says: \"names no probe; give it a probe's id, or leave it out to run every probe\" },\n}) satisfies Record<string, Valued>;\n\n/**\n * A resolved path with its drive letter in one case. On Windows a drive\n * letter names its volume in either case, `D:\\` and `d:\\` alike (Microsoft's\n * Naming Files, Paths, and Namespaces, Naming Conventions, read 2026-10-19),\n * where Node's `resolve` keeps the case it was written in (spec-core\n * ADR-0005, amended 2026-10-19). The rest of the path is left as it is: NTFS\n * can be made to tell names apart by case, so whether two names are one\n * directory is the disk's to say. A path resolved elsewhere starts with `/`,\n * and has no drive letter to fold.\n */\nexport function foldDrive(path: string): string {\n return /^[a-z]:/.test(path) ? `${path[0]!.toUpperCase()}${path.slice(1)}` : path;\n}\n\n/**\n * Whether two values of an option that takes one ask for one thing, as the\n * option reads them: a brief by its id as spec-brief's names are matched,\n * space around it dropped and `12` the brief `012` (ADR-0004); a root by the\n * directory it names from `cwd`, the one an empty root names on the hook's\n * line among them, with a drive letter in either case; any other as it is\n * written, since each is read so, a format, a probe's position, an id or\n * text.\n */\nfunction asksOne(option: string, first: string, other: string, cwd: string): boolean {\n if (option === 'brief') return sameId(first.trim(), other.trim());\n if (option === 'root') return foldDrive(resolve(cwd, first)) === foldDrive(resolve(cwd, other));\n return first === other;\n}\n\n/**\n * The first option that takes one value and was given two that read as two,\n * named with both as they were written; `null` where none was. The parser\n * keeps the last, and the earlier went unread with no word of it:\n * `doctor --root nowhere --root .` ran in `.`, and `--brief 404 --brief 001`\n * was brief 001 (spec-core ADR-0005). The same value given twice, however it\n * is spelled, is the option once, and the last is the one read. `cwd` is\n * where a relative root is read from. A list, `--path` and `--option`, has\n * every value it is given.\n */\nexport function twoValues(line: Pick<CommandLine, 'written'>, cwd: string): string | null {\n for (const [option, values] of Object.entries(line.written)) {\n const first = values[0]!;\n const other = values.find((value) => !asksOne(option, first, value, cwd));\n if (other !== undefined) return `--${option} is given twice, as ${JSON.stringify(first)} and as ${JSON.stringify(other)}, and one of the two would go unread`;\n }\n return null;\n}\n\nexport type ValuedOption = keyof typeof VALUED;\n\n/**\n * The line for a value that names nothing. `subject` is the input as its\n * caller knows it: `--brief` on a command line, `\"brief\"` in a tool call. The\n * value is shown as JSON, where a tab or a line break in it can be seen.\n */\nexport function emptyValue(subject: string, option: ValuedOption, value: string): string {\n return `${subject} is ${JSON.stringify(value)}, which ${VALUED[option].says}`;\n}\n\ninterface Shape {\n /** The options the command reads, by their long names. */\n readonly options: readonly string[];\n /** The most arguments it takes, with that said for the refusal of one more; `null` where it takes any number. */\n readonly limit: { readonly most: number; readonly takes: string } | null;\n /**\n * What an argument of it is, and what to do about an empty one; absent\n * where it takes none, or says so itself. `option` is the option that\n * names the same thing, where there is one: the two together are refused.\n * Only a brief is named both ways.\n */\n readonly argument?: { readonly role: string; readonly instead: string; readonly option?: 'brief' };\n /** The options whose empty value the command says in its own report. */\n readonly reports?: readonly string[];\n /**\n * The options that make the command another one, each with the only\n * options it reads beside it. The first on the list that is given decides,\n * as the command itself decides.\n */\n readonly modes?: readonly { readonly option: string; readonly beside: readonly string[] }[];\n}\n\nconst NO_ARGUMENT = literal({ limit: { most: 0, takes: 'no argument' } });\nconst ONE_BRIEF = literal({\n limit: { most: 1, takes: \"one argument at most, a brief's id\" },\n argument: { role: 'the brief', instead: \"give it a brief's id, or leave it out for the one --brief, SPEC_BRIEF or the branch names\", option: 'brief' },\n});\n\n/**\n * Every command, with what it reads of a command line. An option is listed\n * for a command only where the command's answer depends on it: `init`\n * chooses the base itself and the server is told the brief by each call, so\n * neither takes `--base` or `--brief`, and taking them would tell someone\n * the base or the brief had been set.\n */\nconst SHAPE = literal({\n context: { options: ['brief', 'base', 'root', 'format'], ...ONE_BRIEF },\n guard: {\n options: ['brief', 'base', 'root', 'format', 'strict'],\n limit: null,\n argument: { role: 'a path', instead: 'give it the paths the round would write' },\n },\n hook: { options: ['brief', 'base', 'root'], limit: { most: 1, takes: 'one argument, \"claude\" or \"git\"' } },\n // `--list` and `--show` print what waits and raise nothing: neither reads\n // the parts of a request, nor a brief, since what waits is every round's.\n escalate: {\n options: ['brief', 'root', 'format', 'path', 'reason', 'option', 'recommend', 'list', 'show'],\n ...NO_ARGUMENT,\n modes: [\n { option: 'list', beside: ['root', 'format'] },\n { option: 'show', beside: ['root', 'format'] },\n ],\n },\n rule: {\n options: ['root', 'format', 'allow', 'deny', 'note'],\n limit: { most: 1, takes: \"one argument, the escalation's id\" },\n argument: { role: 'the escalation', instead: 'give it an id, as spec-harness escalate --list prints them' },\n },\n rulings: { options: ['brief', 'base', 'root', 'format'], ...ONE_BRIEF },\n audit: { options: ['brief', 'base', 'root', 'format', 'strict'], ...ONE_BRIEF },\n probe: { options: ['brief', 'base', 'root', 'format', 'id', 'at'], ...ONE_BRIEF },\n premises: { options: ['brief', 'root', 'format', 'strict'], ...NO_ARGUMENT },\n init: { options: ['root', 'format', 'write', 'git-hook', 'block-on-failure'], ...NO_ARGUMENT },\n mcp: { options: ['root'], ...NO_ARGUMENT },\n // A diagnosis says what is wrong with what it was given, where a command\n // that acts on it stops: a brief or a base that names nothing is a line of\n // doctor's report.\n doctor: { options: ['brief', 'base', 'root', 'format', 'strict'], ...NO_ARGUMENT, reports: ['brief', 'base'] },\n}) satisfies Record<string, Shape>;\n\n/** The options every command answers instead of running, which no shape lists. */\nconst ANSWERS: readonly string[] = ['help', 'version'];\n\n/** Every command there is: the command line dispatches on these names and no other. */\nexport type Command = keyof typeof SHAPE;\n\n// A map, since a command is a word a person types and an object answers to\n// more names than it was given: `constructor` has no shape.\nconst SHAPES: ReadonlyMap<string, Shape> = new Map(Object.entries(SHAPE));\n\nfunction listed(options: readonly string[]): string {\n const names = options.map((option) => `--${option}`);\n // Every command reads at least `--root`, so a list is never empty.\n if (names.length === 1) return `its only option is ${names[0] as string}`;\n return `its options are ${names.slice(0, -1).join(', ')} and ${names.at(-1)!}`;\n}\n\n/**\n * Whether a line is Claude Code's hook, by its command and the argument\n * after it. It is exempt from every refusal here, and read as each release\n * since 0.1.0 read it: the line lives in settings files and in the plugin of\n * every release, which this one cannot rewrite, and exit 2 from it holds\n * every write of a session, the write that would fix the line among them.\n * A line the parser cannot read is asked the same, and the hook says it\n * to the person with exit 0 (cli.ts).\n */\nexport function isClaudeHook(command: string | undefined, argument: string | undefined): boolean {\n return command === 'hook' && argument === 'claude';\n}\n\n/** The words for a `--root` that is a file, or is not there, wherever it is said. */\nexport function noDirectory(root: string): string {\n return `--root is \"${root}\", which is not a directory`;\n}\n\n/** What is wrong with the line of Claude Code's hook, and what mends it. */\nexport interface LineFault {\n readonly wrong: string;\n readonly mend: string;\n}\n\n/**\n * What Claude Code's hook does with its own line, as far as the line decides\n * it: it guards, in the root the line gives it where it gives one and under\n * the brief it names where it names one; or the guard is off, for a fault\n * the hook says with exit 0 and holds no write for; or every write is held,\n * exit 2, for a brief the line names that is not one (ADR-0005). The hook\n * reads its line through the functions below, and `doctor` reads a line it\n * finds in Claude Code's settings through the same ones, so what it says of\n * a line is what the hook does with it.\n */\nexport type HookLine = Guards | GuardOff | WritesHeld;\n\nexport interface Guards {\n readonly state: 'guards';\n readonly root: string | undefined;\n /**\n * The brief the line names with `--brief`, as written. Whether it is a\n * brief is spec-brief's to say, which the caller asks. Absent where the\n * line names none.\n */\n readonly brief?: string;\n /**\n * The work tree the root is in, where that is not the project's own and\n * holds it: the hook opens the work tree its root is in, so a write to a\n * file of the project is checked there, by that work tree's briefs, under\n * the path the file has in it. Only git can tell, so the caller asks it.\n * Absent where the line gives no root, or one in the project's work tree.\n */\n readonly holder?: OtherTree & { readonly path: string };\n}\n\n/** A work tree the root on the hook's line is in that is not the project's own. */\nexport interface OtherTree {\n /** Its top, as git names it. */\n readonly top: string;\n /** Whether it is a work tree of the project's own repository, linked or main, where it is otherwise another repository's. */\n readonly ours: boolean;\n}\n\n/** How such a work tree is named: by its top, and whose it is. */\nexport function namedTree({ top, ours }: OtherTree): string {\n return `${top}, ${ours ? 'another work tree of this repository' : 'the work tree of another repository'}`;\n}\n\nexport interface GuardOff {\n readonly state: 'off';\n readonly fault: LineFault;\n /**\n * Set where the hook says nothing of the fault: a root outside any work\n * tree, or in a work tree that does not hold the project. Every other\n * fault it says, to the person, and every one with exit 0.\n */\n readonly silent?: true;\n /**\n * What goes unchecked, where that is not every write: under a root in a\n * work tree that does not hold the project, the hook checks a write to a\n * file of that work tree, and none to a file of the project.\n */\n readonly unchecked?: string;\n}\n\nexport interface WritesHeld {\n readonly state: 'held';\n /** What is said of the name, by what named the brief (workspace.ts). */\n readonly wrong: string;\n}\n\n/**\n * A line under which the round has no brief to be checked against, since\n * what names one names a brief spec-brief does not know, or one it has\n * archived: the hook holds the write, exit 2, and every write after it until\n * the name is mended (ADR-0004). `wrong` is what is said of the name.\n */\nexport function heldHookLine(wrong: string): WritesHeld {\n return { state: 'held', wrong };\n}\n\n/**\n * A line the parser refused: an option no command has, an option left\n * without its value, a `--format` or an `--at` that is none, `--help` beside\n * `--version`; or a line it read with two values for an option that takes\n * one, of which it cannot tell the one meant ({@link twoValues}). The\n * parser's words are kept, on one line: what node says of an option that may\n * have lost its value is three.\n */\nexport function unreadHookLine(said: string): GuardOff {\n return { state: 'off', fault: { wrong: `the hook's line cannot be read (${said.split('\\n').join(' ')})`, mend: 'mend it in Claude Code\\'s settings, where init writes \"hook claude\" and nothing after it' } };\n}\n\n/**\n * A line that parsed, by the root it gives. An empty root is the directory\n * the hook was started in: git starts there when it is handed no directory,\n * so the hook has guarded there for an empty root in every release. A root\n * that is a file, or is not there, is one git starts in neither of, which\n * read as a session outside a work tree, exit 0 and no word, and the guard\n * someone had configured was off.\n */\nexport function readHookLine(root: string | undefined, cwd: string, isDirectory: (path: string) => boolean): Guards | GuardOff {\n const given = root === '' ? cwd : root;\n if (given !== undefined && !isDirectory(given)) {\n return { state: 'off', fault: { wrong: noDirectory(given), mend: \"give the hook's line a directory that exists, or leave --root out for the project Claude Code names\" } };\n }\n return { state: 'guards', root: given };\n}\n\n/**\n * What `--help` or `--version` on the hook's line stands beside that the\n * hook would say as it runs, in the words for a line it cannot read; `null`\n * where the line asks for neither, or stands beside nothing the line itself\n * shows to be wrong. Every command answers either before it runs, so the\n * hook printed the help over a root that is not a directory, exit 0, and\n * said nothing of it (ADR-0005). A fault only git or spec-brief can tell\n * stays unsaid: neither is asked before an answer that runs nothing.\n */\nexport function answerBeside(line: Pick<CommandLine, 'help' | 'version' | 'root'>, cwd: string, isDirectory: (path: string) => boolean): string | null {\n if (!line.help && !line.version) return null;\n const read = readHookLine(line.root, cwd, isDirectory);\n if (read.state === 'guards') return null;\n const asked = line.help ? 'help' : 'version';\n return `--${asked} is given beside a fault, ${read.fault.wrong}, and the ${asked} would be printed in its place`;\n}\n\n/**\n * A line of the hook's that asks for the help or the version, which the hook\n * prints, exit 0, in place of reading its question: no write is checked\n * under it, and Claude Code, by its hooks reference, writes what a hook\n * that exits 0 prints to its debug log, so nothing says the guard is off\n * (ADR-0005). The hook says nothing of it, since a person who asks the hook\n * for its help at a terminal is answered the same way.\n */\nexport function answeringHookLine(asked: 'help' | 'version'): GuardOff {\n return {\n state: 'off',\n silent: true,\n fault: {\n wrong: `the line asks for the ${asked}, which the hook prints, exit 0, in place of checking the write`,\n mend: 'take that option off the line, where init writes \"hook claude\" and nothing after it',\n },\n };\n}\n\n/**\n * A line whose root is a directory outside any git work tree. The hook\n * reads that as a session outside one, where it has nothing to guard: it\n * checks no write and says nothing, exit 0 (ADR-0005), and that stays. Only\n * git can tell such a root, so the caller asks it. A line that carries one\n * is a guard someone set up that checks nothing, and `doctor` says it as\n * off.\n */\nexport function outsideHookLine(root: string): GuardOff {\n return {\n state: 'off',\n silent: true,\n fault: {\n wrong: `--root is \"${root}\", a directory outside any git work tree, which the hook reads as a session outside one, exit 0 and no word`,\n mend: \"give the hook's line a directory in the work tree, or leave --root out for the project Claude Code names\",\n },\n };\n}\n\n/**\n * A line whose root is in a git work tree that does not hold the project:\n * another repository beside it, or a work tree inside it, a repository of\n * its own, a submodule or a linked worktree. The hook opens the work tree\n * its root is in and decides by that work tree's briefs. A file of the\n * project is outside it, which the hook reads as a file outside the\n * repository: it says nothing, exit 0, as it has in every release, and that\n * stays. So no write to a file of the project is checked under the line,\n * and `doctor` says it as a guard that is off. Only git can tell such a\n * root, so the caller asks it.\n */\nexport function elsewhereHookLine(root: string, tree: OtherTree): GuardOff {\n return {\n state: 'off',\n silent: true,\n unchecked: 'no write to a file of this project is checked',\n fault: {\n wrong: `--root is \"${root}\", a directory in ${namedTree(tree)}, which does not hold this project: the hook checks a write to a file of that work tree, by its briefs, and passes any other with no word, exit 0`,\n mend: \"give the hook's line a directory in this work tree, or leave --root out for the project Claude Code names\",\n },\n };\n}\n\n/**\n * A line that guards under a root in a work tree which holds the project\n * and is not its own: a repository the project is nested in, or the main\n * work tree around a linked one. `path` is the project's top as that work\n * tree names it.\n */\nexport function holderHookLine(line: Guards, tree: OtherTree, path: string): Guards {\n return { ...line, holder: { ...tree, path } };\n}\n\n/**\n * What Claude Code's hook does with the words it is handed, or `null` for\n * words that are not its line: read by the parser the command line reads\n * every line with, and known by its command and the argument after it on a\n * line that parsed and on one that did not, as the command line knows it\n * (cli.ts). `parse` is that parser, which throws its refusal with the words\n * it could still read.\n *\n * A line that guards says which brief it names, where it names one: the hook\n * passes over a `--brief` that is empty, as every release has, and holds\n * every write under one that names no brief, which only spec-brief can tell.\n * A line with two values for an option that takes one is a line the hook\n * cannot read, whichever option it is, read from `cwd` as the hook reads it\n * from where it is started (cli.ts). So is one with the help or the version\n * beside a root that is not a directory; and with either beside nothing it\n * can see is wrong, the line checks no write, whatever else it holds.\n */\nexport function claudeHookLine(\n words: readonly string[],\n parse: (argv: readonly string[]) => CommandLine,\n cwd: string,\n isDirectory: (path: string) => boolean,\n): HookLine | null {\n let line: CommandLine;\n try {\n line = parse(words);\n } catch (error) {\n const refused = error as { readonly message: string; readonly words: readonly string[] };\n return isClaudeHook(refused.words[0], refused.words[1]) ? unreadHookLine(refused.message) : null;\n }\n if (!isClaudeHook(line.command, line.positionals[0])) return null;\n const twice = twoValues(line, cwd);\n if (twice !== null) return unreadHookLine(twice);\n const beside = answerBeside(line, cwd, isDirectory);\n if (beside !== null) return unreadHookLine(beside);\n if (line.help) return answeringHookLine('help');\n if (line.version) return answeringHookLine('version');\n const read = readHookLine(line.root, cwd, isDirectory);\n return read.state === 'guards' && line.brief !== undefined && !isBlank(line.brief) ? { ...read, brief: line.brief } : read;\n}\n\n/**\n * Why the command line is refused, or `null` when the command can run:\n * an option it does not read, alone or beside another, an argument it does\n * not take, a brief named twice, a value or an argument that names nothing,\n * a `--root` that is no directory. Asked once a command is known, and before\n * `--help` or `--version` answers: a line that is wrong is refused with\n * either on it, as one the parser refuses always was (spec-core ADR-0005).\n * A command with no shape is none, which its caller says.\n */\nexport function refusal(line: CommandLine, isDirectory: (path: string) => boolean): string | null {\n const { command, positionals } = line;\n // No shape is kept under `undefined`, so the first check is the compiler's,\n // and its mutant is equivalent.\n if (command === undefined) return null;\n const shape = SHAPES.get(command);\n if (shape === undefined || isClaudeHook(command, positionals[0])) return null;\n // What the line asks the command, beside the help or the version it may\n // ask for instead, which every command answers.\n const asked = line.given.filter((option) => !ANSWERS.includes(option));\n const stray = asked.find((option) => !shape.options.includes(option));\n if (stray !== undefined) return `${command} does not take --${stray}; ${listed(shape.options)}`;\n // An option the command reads, beside one that leaves it unread:\n // `escalate --list --path src/a.ts` printed the list, exit 0, and the\n // request it also spelled out was never raised.\n const mode = shape.modes?.find((candidate) => asked.includes(candidate.option));\n if (mode !== undefined) {\n const unread = asked.find((option) => option !== mode.option && !mode.beside.includes(option));\n if (unread !== undefined) return `${command} --${mode.option} does not take --${unread}; beside --${mode.option} ${listed(mode.beside)}`;\n }\n if (shape.limit !== null && positionals.length > shape.limit.most) {\n return `${command} takes ${shape.limit.takes}, not ${positionals.map((argument) => JSON.stringify(argument)).join(', ')}`;\n }\n // One thing named two ways, whatever the two hold: the argument won with\n // no word, so `audit 012 --brief 013` audited 012 for someone who had\n // also typed 013.\n const { argument } = shape;\n if (argument?.option !== undefined && positionals.length > 0 && line.given.includes(argument.option)) {\n return `${command} was given ${argument.role} twice, as its argument ${JSON.stringify(positionals[0])} and as --${argument.option} ${JSON.stringify(line[argument.option])}; give one of them`;\n }\n for (const [option, valued] of Object.entries(VALUED) as [ValuedOption, Valued][]) {\n if (shape.reports?.includes(option)) continue;\n const empty = valued.of(line).find(isBlank);\n if (empty !== undefined) return emptyValue(`--${option}`, option, empty);\n }\n const empty = positionals.find(isBlank);\n if (shape.argument !== undefined && empty !== undefined) {\n return `${command} was given ${JSON.stringify(empty)} as ${shape.argument.role}, which names none; ${shape.argument.instead}`;\n }\n // An empty one was refused above, so this is a path that was meant: a file,\n // or a directory that is not there. git starts in neither, and the run\n // ended saying the path was outside a work tree, which named neither the\n // option nor what was wrong with what it was given.\n if (line.root !== undefined && !isDirectory(line.root)) {\n return `${noDirectory(line.root)}; give it one that exists, or leave it out to run in the current directory`;\n }\n return null;\n}\n"]}
|
package/dist/workspace.d.ts
CHANGED
|
@@ -60,6 +60,8 @@ export interface Options {
|
|
|
60
60
|
readonly write: boolean;
|
|
61
61
|
/** init: also install git's pre-commit hook. */
|
|
62
62
|
readonly gitHook: boolean;
|
|
63
|
+
/** init: `onFailure: "block"` on the hook entries it writes (ADR-0012). */
|
|
64
|
+
readonly blockOnFailure: boolean;
|
|
63
65
|
/** escalate: the files the round needs, the reason, the options and the recommendation. */
|
|
64
66
|
readonly paths: readonly string[];
|
|
65
67
|
readonly reason: string | undefined;
|
|
@@ -112,9 +114,10 @@ export declare function openWorkspace(options: Pick<Options, 'root'>, io: CliIO,
|
|
|
112
114
|
/** The installs a work tree's siblings are looked for in after its own, given its top and its common directory (siblings.ts). */
|
|
113
115
|
export type Installs = (root: string, commonDir: string) => readonly string[];
|
|
114
116
|
/**
|
|
115
|
-
*
|
|
116
|
-
*
|
|
117
|
-
*
|
|
117
|
+
* Every command: a linked worktree with no install of its own is checked
|
|
118
|
+
* with the main work tree's, where `git worktree add` leaves the project's
|
|
119
|
+
* `node_modules`, as git's hook and doctor were, and as the hook is with the
|
|
120
|
+
* project's (ADR-0005).
|
|
118
121
|
*/
|
|
119
122
|
export declare const MAIN_INSTALL: Installs;
|
|
120
123
|
/** The rest of opening a work tree, once it is found: its common directory, its configuration and its branch. */
|
|
@@ -155,3 +158,42 @@ export declare function describeActive(active: ActiveBrief, argument?: string):
|
|
|
155
158
|
problem: string;
|
|
156
159
|
};
|
|
157
160
|
export declare function json(command: string, body: Record<string, unknown>): string;
|
|
161
|
+
/**
|
|
162
|
+
* Whether a work tree other than the one whose top is `top` may hold `cwd`:
|
|
163
|
+
* `cwd` is outside it, or a `.git` stands in a directory between the two,
|
|
164
|
+
* where a linked worktree, a submodule or a repository of its own starts.
|
|
165
|
+
* Where neither is so, `cwd` is in that work tree, and git is asked nothing
|
|
166
|
+
* more: a session works at its project's top, or below it, for nearly every
|
|
167
|
+
* write. Answering that one may is what git is then asked, so the mutant
|
|
168
|
+
* that always answers so is equivalent, at the cost of a process, and so
|
|
169
|
+
* is each that only answers so more often: a slice one short reaches the
|
|
170
|
+
* top itself, which holds a `.git`, and a part named in place of none is
|
|
171
|
+
* a directory no top holds.
|
|
172
|
+
*/
|
|
173
|
+
export declare function mayBeElsewhere(cwd: string, top: string): boolean;
|
|
174
|
+
/**
|
|
175
|
+
* The work tree that holds `cwd`, where it is another work tree of the
|
|
176
|
+
* repository the work tree `named` is one of, as the hook's root names it, by the git directory the two share;
|
|
177
|
+
* `null` where `cwd` is in the root's own, in another repository, or in
|
|
178
|
+
* none (ADR-0005).
|
|
179
|
+
*/
|
|
180
|
+
export declare function otherTree(named: Workspace, cwd: string): Promise<FoundTree | null>;
|
|
181
|
+
/**
|
|
182
|
+
* The work trees a write is decided in, each with the paths it decides, for
|
|
183
|
+
* the hook and for the server's `check_path` (ADR-0005). The repository is
|
|
184
|
+
* the one the hook's root names, as before. Each path is decided by the
|
|
185
|
+
* innermost work tree of that repository that holds it, by its branch, its
|
|
186
|
+
* briefs and its configuration, read from its top, with its siblings looked
|
|
187
|
+
* for in its install and then the project's: there a session works in a
|
|
188
|
+
* worktree while Claude Code names the project it started in, or writes from
|
|
189
|
+
* one into another, and the hook decided by the project's branch, which
|
|
190
|
+
* named no brief, so a protected write passed with no word. A path in no
|
|
191
|
+
* other work tree of the repository is decided by the work tree the root
|
|
192
|
+
* names, as before: a write to the project's own file from a worktree is
|
|
193
|
+
* refused where it was, and a path in another repository, or in none, is
|
|
194
|
+
* read as it was. `cwd` is what a relative path is read from.
|
|
195
|
+
*/
|
|
196
|
+
export declare function writesBy(named: Workspace, cwd: string | null, paths: readonly string[], io: CliIO): Promise<{
|
|
197
|
+
readonly workspace: Workspace;
|
|
198
|
+
readonly paths: readonly string[];
|
|
199
|
+
}[]>;
|
package/dist/workspace.js
CHANGED
|
@@ -3,10 +3,11 @@
|
|
|
3
3
|
* runs in, and which brief the round is working on.
|
|
4
4
|
*/
|
|
5
5
|
import { readFileSync } from 'node:fs';
|
|
6
|
+
import { dirname, join } from 'node:path';
|
|
6
7
|
import { parseArgs } from 'node:util';
|
|
7
8
|
import { findActive } from './briefs.js';
|
|
8
9
|
import { briefIdFromBranch, ciBranch } from './branch.js';
|
|
9
|
-
import { holdsGit, isTop, loadConfig, mainWorkTree } from './fs.js';
|
|
10
|
+
import { holdsGit, isTop, loadConfig, mainWorkTree, reachedBy, realSpelling, repositoryPath } from './fs.js';
|
|
10
11
|
import { commonDirectory, currentBranch, workTree, workTreeRoot } from './git.js';
|
|
11
12
|
import { createSiblings } from './siblings.js';
|
|
12
13
|
export const EXIT_OK = 0;
|
|
@@ -48,6 +49,7 @@ export function parseOptions(argv) {
|
|
|
48
49
|
strict: { type: 'boolean' },
|
|
49
50
|
write: { type: 'boolean' },
|
|
50
51
|
'git-hook': { type: 'boolean' },
|
|
52
|
+
'block-on-failure': { type: 'boolean' },
|
|
51
53
|
path: { type: 'string', multiple: true },
|
|
52
54
|
reason: { type: 'string' },
|
|
53
55
|
option: { type: 'string', multiple: true },
|
|
@@ -119,6 +121,7 @@ export function parseOptions(argv) {
|
|
|
119
121
|
version: values.version === true,
|
|
120
122
|
write: values.write === true,
|
|
121
123
|
gitHook: values['git-hook'] === true,
|
|
124
|
+
blockOnFailure: values['block-on-failure'] === true,
|
|
122
125
|
paths: values.path ?? [],
|
|
123
126
|
reason: values.reason,
|
|
124
127
|
options: values.option ?? [],
|
|
@@ -159,7 +162,7 @@ export async function foundCommonDir(found) {
|
|
|
159
162
|
// the mutant that always asks again is equivalent, at a process's cost.
|
|
160
163
|
return found.tree === 'unread' ? commonDirectory(found.root) : found.tree.commonDir;
|
|
161
164
|
}
|
|
162
|
-
export async function openWorkspace(options, io, installs =
|
|
165
|
+
export async function openWorkspace(options, io, installs = MAIN_INSTALL) {
|
|
163
166
|
const cwd = options.root ?? io.cwd;
|
|
164
167
|
const found = await findTree(cwd);
|
|
165
168
|
if (found === null)
|
|
@@ -167,16 +170,10 @@ export async function openWorkspace(options, io, installs = NO_INSTALLS) {
|
|
|
167
170
|
return openFound(found, io, installs);
|
|
168
171
|
}
|
|
169
172
|
/**
|
|
170
|
-
* Every command
|
|
171
|
-
*
|
|
172
|
-
*
|
|
173
|
-
*
|
|
174
|
-
*/
|
|
175
|
-
const NO_INSTALLS = () => [];
|
|
176
|
-
/**
|
|
177
|
-
* git's hook and doctor: a linked worktree with no install of its own is
|
|
178
|
-
* checked with the main work tree's, where `git worktree add` leaves the
|
|
179
|
-
* project's `node_modules` (ADR-0005).
|
|
173
|
+
* Every command: a linked worktree with no install of its own is checked
|
|
174
|
+
* with the main work tree's, where `git worktree add` leaves the project's
|
|
175
|
+
* `node_modules`, as git's hook and doctor were, and as the hook is with the
|
|
176
|
+
* project's (ADR-0005).
|
|
180
177
|
*/
|
|
181
178
|
export const MAIN_INSTALL = (root, commonDir) => {
|
|
182
179
|
const main = mainWorkTree(commonDir);
|
|
@@ -185,7 +182,7 @@ export const MAIN_INSTALL = (root, commonDir) => {
|
|
|
185
182
|
return main === null || main === root ? [] : [main];
|
|
186
183
|
};
|
|
187
184
|
/** The rest of opening a work tree, once it is found: its common directory, its configuration and its branch. */
|
|
188
|
-
export async function openFound(found, io, installs =
|
|
185
|
+
export async function openFound(found, io, installs = MAIN_INSTALL) {
|
|
189
186
|
const { cwd, root, tree, early } = found;
|
|
190
187
|
// The common directory and the branch are asked of the root. What git
|
|
191
188
|
// said from `cwd` is that answer where `cwd` is the root, the same
|
|
@@ -278,4 +275,137 @@ export function describeActive(active, argument = 'the argument') {
|
|
|
278
275
|
export function json(command, body) {
|
|
279
276
|
return `${JSON.stringify({ tool: 'spec-harness', version: version(), schemaVersion: JSON_SCHEMA_VERSION, command, ...body }, null, 2)}\n`;
|
|
280
277
|
}
|
|
278
|
+
/**
|
|
279
|
+
* Whether a work tree other than the one whose top is `top` may hold `cwd`:
|
|
280
|
+
* `cwd` is outside it, or a `.git` stands in a directory between the two,
|
|
281
|
+
* where a linked worktree, a submodule or a repository of its own starts.
|
|
282
|
+
* Where neither is so, `cwd` is in that work tree, and git is asked nothing
|
|
283
|
+
* more: a session works at its project's top, or below it, for nearly every
|
|
284
|
+
* write. Answering that one may is what git is then asked, so the mutant
|
|
285
|
+
* that always answers so is equivalent, at the cost of a process, and so
|
|
286
|
+
* is each that only answers so more often: a slice one short reaches the
|
|
287
|
+
* top itself, which holds a `.git`, and a part named in place of none is
|
|
288
|
+
* a directory no top holds.
|
|
289
|
+
*/
|
|
290
|
+
export function mayBeElsewhere(cwd, top) {
|
|
291
|
+
const path = repositoryPath(cwd, top, cwd);
|
|
292
|
+
if (path === null)
|
|
293
|
+
return true;
|
|
294
|
+
const parts = path === '' ? [] : path.split('/');
|
|
295
|
+
return parts.some((_, index) => holdsGit(join(top, ...parts.slice(0, index + 1))));
|
|
296
|
+
}
|
|
297
|
+
/**
|
|
298
|
+
* The work tree that holds `cwd`, where it is another work tree of the
|
|
299
|
+
* repository the work tree `named` is one of, as the hook's root names it, by the git directory the two share;
|
|
300
|
+
* `null` where `cwd` is in the root's own, in another repository, or in
|
|
301
|
+
* none (ADR-0005).
|
|
302
|
+
*/
|
|
303
|
+
export async function otherTree(named, cwd) {
|
|
304
|
+
// Asked anyway, git names the root's own top for a directory in its work
|
|
305
|
+
// tree, and none for one not made, either of which answers `null` below:
|
|
306
|
+
// the mutant that always asks is equivalent, at a process's cost.
|
|
307
|
+
if (!mayBeElsewhere(cwd, named.root))
|
|
308
|
+
return null;
|
|
309
|
+
const found = await findTree(cwd);
|
|
310
|
+
// The project's own work tree, found again, would decide every path the
|
|
311
|
+
// same way once more: the check saves reading its configuration twice, and
|
|
312
|
+
// its mutant is equivalent.
|
|
313
|
+
if (found === null || found.root === named.root)
|
|
314
|
+
return null;
|
|
315
|
+
const common = await foundCommonDir(found);
|
|
316
|
+
// git names a common directory wherever it names a top, so `null` is
|
|
317
|
+
// there for the type.
|
|
318
|
+
if (common === null || realSpelling(common) !== realSpelling(named.commonDir))
|
|
319
|
+
return null;
|
|
320
|
+
return found;
|
|
321
|
+
}
|
|
322
|
+
/**
|
|
323
|
+
* The top of the nearest work tree, of any repository, above a directory or
|
|
324
|
+
* a file: the nearest directory above it that holds a `.git`, found with no
|
|
325
|
+
* process; `null` where none does. A path not yet written has the
|
|
326
|
+
* directories it would be written in looked through, those not made yet
|
|
327
|
+
* among them.
|
|
328
|
+
*/
|
|
329
|
+
function topAbove(path) {
|
|
330
|
+
for (let directory = dirname(path);; directory = dirname(directory)) {
|
|
331
|
+
if (holdsGit(directory))
|
|
332
|
+
return directory;
|
|
333
|
+
// Only the top of the filesystem is its own parent.
|
|
334
|
+
if (dirname(directory) === directory)
|
|
335
|
+
return null;
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
/**
|
|
339
|
+
* The innermost work tree of the repository the work tree `named` is one of
|
|
340
|
+
* that holds a file, given by its real spelling, where it is not `named`
|
|
341
|
+
* itself: a linked worktree or the main one, inside the project, beside it,
|
|
342
|
+
* or around it (ADR-0005). The
|
|
343
|
+
* directories above the path are looked through, with no process, for each
|
|
344
|
+
* that holds a `.git`; git is asked of each that is not `named`'s top, and
|
|
345
|
+
* one that git names as no work tree of this repository's, a submodule's, a
|
|
346
|
+
* clone's inside the tree, or a `.git` that stands in no work tree's top, is
|
|
347
|
+
* passed by for the next above it. `null` where the first of this repository
|
|
348
|
+
* met is `named`, or none is: the path is decided by `named`, as before, a
|
|
349
|
+
* path in no work tree of the repository among them.
|
|
350
|
+
*/
|
|
351
|
+
async function treeHolding(named, spelled) {
|
|
352
|
+
const own = realSpelling(named.root);
|
|
353
|
+
for (let top = topAbove(spelled); top !== null; top = topAbove(top)) {
|
|
354
|
+
if (top === own)
|
|
355
|
+
return null;
|
|
356
|
+
const tree = await otherTree(named, top);
|
|
357
|
+
// git names a work tree whose top is not this directory where it reads
|
|
358
|
+
// the `.git` here as no repository's, an empty directory say, and names
|
|
359
|
+
// the one around it: the walk meets that one's top next, past any `.git`
|
|
360
|
+
// between that git reads as none too, and takes it. So the mutant that
|
|
361
|
+
// takes what git names here is equivalent for every work tree git makes;
|
|
362
|
+
// the check keeps a `core.worktree` set by hand to name another
|
|
363
|
+
// directory from deciding a path that directory does not hold.
|
|
364
|
+
if (tree !== null && realSpelling(tree.root) === top)
|
|
365
|
+
return tree;
|
|
366
|
+
}
|
|
367
|
+
return null;
|
|
368
|
+
}
|
|
369
|
+
/**
|
|
370
|
+
* The work trees a write is decided in, each with the paths it decides, for
|
|
371
|
+
* the hook and for the server's `check_path` (ADR-0005). The repository is
|
|
372
|
+
* the one the hook's root names, as before. Each path is decided by the
|
|
373
|
+
* innermost work tree of that repository that holds it, by its branch, its
|
|
374
|
+
* briefs and its configuration, read from its top, with its siblings looked
|
|
375
|
+
* for in its install and then the project's: there a session works in a
|
|
376
|
+
* worktree while Claude Code names the project it started in, or writes from
|
|
377
|
+
* one into another, and the hook decided by the project's branch, which
|
|
378
|
+
* named no brief, so a protected write passed with no word. A path in no
|
|
379
|
+
* other work tree of the repository is decided by the work tree the root
|
|
380
|
+
* names, as before: a write to the project's own file from a worktree is
|
|
381
|
+
* refused where it was, and a path in another repository, or in none, is
|
|
382
|
+
* read as it was. `cwd` is what a relative path is read from.
|
|
383
|
+
*/
|
|
384
|
+
export async function writesBy(named, cwd, paths, io) {
|
|
385
|
+
const from = cwd ?? io.cwd;
|
|
386
|
+
const trees = [];
|
|
387
|
+
// The parts in the order their work trees are first met, each with its
|
|
388
|
+
// paths in the order they were named.
|
|
389
|
+
for (const path of paths) {
|
|
390
|
+
// Each file the path reaches, as it reads and as the system writes it,
|
|
391
|
+
// by the work tree that holds that file: a link can lead from one work
|
|
392
|
+
// tree into another, and each decides the file it holds.
|
|
393
|
+
const held = [];
|
|
394
|
+
for (const spelled of reachedBy(path, from)) {
|
|
395
|
+
const tree = await treeHolding(named, spelled);
|
|
396
|
+
if (!held.some((other) => other?.root === tree?.root))
|
|
397
|
+
held.push(tree);
|
|
398
|
+
}
|
|
399
|
+
for (const tree of held) {
|
|
400
|
+
const part = trees.find((other) => other.tree?.root === tree?.root);
|
|
401
|
+
if (part === undefined)
|
|
402
|
+
trees.push({ tree, paths: [path] });
|
|
403
|
+
else
|
|
404
|
+
part.paths.push(path);
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
// The root's own last, as it was decided after the work tree Claude works in.
|
|
408
|
+
const ordered = [...trees.filter((part) => part.tree !== null), ...trees.filter((part) => part.tree === null)];
|
|
409
|
+
return Promise.all(ordered.map(async ({ tree, paths: held }) => ({ workspace: tree === null ? named : await openFound(tree, io, () => [named.root]), paths: held })));
|
|
410
|
+
}
|
|
281
411
|
//# sourceMappingURL=workspace.js.map
|
package/dist/workspace.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"workspace.js","sourceRoot":"","sources":["../src/workspace.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * What every command shares: exit codes, options, the repository a command\n * runs in, and which brief the round is working on.\n */\n\nimport { readFileSync } from 'node:fs';\nimport { parseArgs, type ParseArgsOptionsConfig } from 'node:util';\n\nimport { findActive, type ActiveBrief, type ActiveSource } from './briefs.js';\nimport { briefIdFromBranch, ciBranch } from './branch.js';\nimport type { HarnessConfig } from './config.js';\nimport { holdsGit, isTop, loadConfig, mainWorkTree } from './fs.js';\nimport { commonDirectory, currentBranch, workTree, workTreeRoot, type WorkTree } from './git.js';\nimport type { FindingFormat } from './formats.js';\nimport { createSiblings, type Siblings } from './siblings.js';\nimport type { BriefRow } from './types.js';\n\nexport const EXIT_OK = 0;\nexport const EXIT_FAILED = 1;\nexport const EXIT_ERROR = 2;\n\n/** The version of the JSON documents this tool prints. Bumped when a field changes meaning. */\nexport const JSON_SCHEMA_VERSION = 1;\n\nexport class UsageError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'UsageError';\n }\n}\n\n/**\n * A command line the parser refused, with the command and the arguments on\n * it. Whose line it was decides what the refusal is: Claude Code's hook says\n * it and holds no write for it, where every other command stops (cli.ts).\n */\nexport class UnreadableLine extends UsageError {\n readonly words: readonly string[];\n\n constructor(message: string, words: readonly string[]) {\n super(message);\n this.words = words;\n }\n}\n\nexport interface CliIO {\n readonly stdout: { write(text: string): unknown };\n readonly stderr: { write(text: string): unknown };\n readonly stdin?: () => Promise<string>;\n readonly cwd: string;\n readonly env: Readonly<Record<string, string | undefined>>;\n}\n\nexport function version(): string {\n // A Buffer parses as its text, so the encoding is equivalent to its mutant.\n const manifest: { version: string } = JSON.parse(readFileSync(new URL('../package.json', import.meta.url), 'utf8'));\n return manifest.version;\n}\n\nexport interface Options {\n readonly command: string | undefined;\n readonly positionals: readonly string[];\n /** The long name of every option given, in the order given: what tells an option left out from one a command does not read. */\n readonly given: readonly string[];\n /**\n * Every value given to each option that takes one value, by its long name,\n * in the order given. The fields below hold the last of them, which is\n * what the parser keeps.\n */\n readonly written: Readonly<Record<string, readonly string[]>>;\n readonly brief: string | undefined;\n readonly base: string | undefined;\n readonly root: string | undefined;\n /** `gitlab`, `sarif` and `github` only for `audit` and `premises`, whose findings have places. */\n readonly format: 'pretty' | 'json' | FindingFormat;\n readonly strict: boolean;\n readonly help: boolean;\n readonly version: boolean;\n /** init: apply the plan instead of printing it. */\n readonly write: boolean;\n /** init: also install git's pre-commit hook. */\n readonly gitHook: boolean;\n /** escalate: the files the round needs, the reason, the options and the recommendation. */\n readonly paths: readonly string[];\n readonly reason: string | undefined;\n readonly options: readonly string[];\n readonly recommend: string | undefined;\n readonly list: boolean;\n readonly show: string | undefined;\n /** rule: the decision and what it allows. */\n readonly allow: boolean;\n readonly deny: boolean;\n readonly note: string | undefined;\n /** probe: which probe, and at which commit. */\n readonly id: string | undefined;\n readonly at: 'base' | 'head' | 'both' | undefined;\n}\n\nexport function parseOptions(argv: readonly string[]): Options {\n // `satisfies`, where a const assertion would read the same to the compiler:\n // the sweep mutates nothing that stands under an `as`, the table with it.\n const options = {\n brief: { type: 'string' },\n base: { type: 'string' },\n root: { type: 'string' },\n format: { type: 'string' },\n strict: { type: 'boolean' },\n write: { type: 'boolean' },\n 'git-hook': { type: 'boolean' },\n path: { type: 'string', multiple: true },\n reason: { type: 'string' },\n option: { type: 'string', multiple: true },\n recommend: { type: 'string' },\n list: { type: 'boolean' },\n show: { type: 'string' },\n allow: { type: 'boolean' },\n deny: { type: 'boolean' },\n note: { type: 'string' },\n id: { type: 'string' },\n at: { type: 'string' },\n help: { type: 'boolean', short: 'h' },\n version: { type: 'boolean', short: 'v' },\n } satisfies ParseArgsOptionsConfig;\n let parsed;\n try {\n parsed = parseArgs({ args: [...argv], allowPositionals: true, strict: true, options, tokens: true });\n } catch (error) {\n // Read again with nothing refused, for whose line it was and nothing\n // else: an option no command has is passed over, one left without its\n // value takes none, and no value read this way is used. The same table,\n // so that a value is not taken for the command it stands before.\n throw new UnreadableLine((error as Error).message, parseArgs({ args: [...argv], strict: false, options }).positionals);\n }\n const { values, positionals, tokens } = parsed;\n // The parser keeps the last value of an option given twice, and the earlier\n // went unread with no word of it: `--root nowhere --root .` ran in `.`. So\n // every value is kept, for the refusal of two (usage.ts). A list keeps\n // every value already.\n const table: ParseArgsOptionsConfig = options;\n const written: Record<string, string[]> = {};\n for (const token of tokens) {\n if (token.kind === 'option' && token.value !== undefined && table[token.name]!.multiple !== true) (written[token.name] ??= []).push(token.value);\n }\n // Two answers asked for at once: the version was printed, and the help went\n // unread. A line that is wrong, whoever's it is (spec-core ADR-0005). Only\n // an option's token has a name, so the test of its kind is the compiler's,\n // and its mutant is equivalent.\n const answers = tokens.flatMap((token) => (token.kind === 'option' && (token.name === 'help' || token.name === 'version') ? [token] : []));\n const other = answers.find((token) => token.name !== answers[0]!.name);\n if (other !== undefined) throw new UnreadableLine(`${answers[0]!.rawName} is given beside ${other.rawName}, and one of the two answers would go unread; give one of them`, positionals);\n const asked = values.format ?? 'pretty';\n const format = asked as Options['format'];\n const formats: readonly Options['format'][] = ['pretty', 'json', 'gitlab', 'sarif', 'github'];\n if (!formats.includes(format)) throw new UnreadableLine(`--format must be pretty, json, gitlab, sarif or github, not \"${format}\"`, positionals);\n const command = positionals[0];\n // Only the commands whose findings have places print them for a forge.\n if (command !== undefined && command !== 'audit' && command !== 'premises' && format !== 'pretty' && format !== 'json') {\n throw new UnreadableLine(`--format ${format} is for audit and premises; ${command} prints pretty or json`, positionals);\n }\n const at = values.at;\n if (at !== undefined && at !== 'base' && at !== 'head' && at !== 'both') throw new UnreadableLine(`--at must be base, head or both, not \"${at}\"`, positionals);\n return {\n command,\n positionals: positionals.slice(1),\n given: Object.keys(values),\n written,\n brief: values.brief,\n base: values.base,\n root: values.root,\n format,\n strict: values.strict === true,\n help: values.help === true,\n version: values.version === true,\n write: values.write === true,\n gitHook: values['git-hook'] === true,\n paths: values.path ?? [],\n reason: values.reason,\n options: values.option ?? [],\n recommend: values.recommend,\n list: values.list === true,\n show: values.show,\n allow: values.allow === true,\n deny: values.deny === true,\n note: values.note,\n id: values.id,\n at,\n };\n}\n\n/** Everything a command needs about the repository it runs in. */\nexport interface Workspace {\n readonly root: string;\n readonly commonDir: string;\n readonly config: HarnessConfig;\n readonly siblings: Siblings;\n /** The branch checked out, or on a detached head the one the forge's CI names; `null` when neither names one. */\n readonly branch: string | null;\n /** The CI variable that named the branch, on a detached head; absent when git named it. */\n readonly branchSource?: string | undefined;\n readonly cwd: string;\n}\n\n/**\n * A work tree as the first wave of opening it found it: the directory it\n * was asked from, its top, what one start of git said from there, and the\n * branch where it was asked beside them.\n */\nexport interface FoundTree {\n readonly cwd: string;\n readonly root: string;\n readonly tree: WorkTree | 'unread';\n readonly early: string | null | undefined;\n}\n\n/** The work tree that holds `cwd`, or `null` where none does. */\nexport async function findTree(cwd: string): Promise<FoundTree | null> {\n // Each git command is a process, a few milliseconds on Linux and a few\n // hundred on a Windows workstation, and every command opens a work tree,\n // the hook before every write among them. So the top and the common\n // directory are asked for in one start of git, and where a `.git` stands\n // in the directory, as it does at the top of a work tree, the branch is\n // asked for beside them and not after. Anywhere else the branch waits, as\n // it did: below a top it would be asked twice, and in no work tree for\n // nothing.\n const [tree, early] = await Promise.all([workTree(cwd), holdsGit(cwd) ? currentBranch(cwd) : undefined]);\n if (tree === null) return null;\n const root = tree === 'unread' ? await workTreeRoot(cwd) : tree.root;\n return root === null ? null : { cwd, root, tree, early };\n}\n\n/**\n * The git directory a found work tree's repository keeps, as git names it\n * from the directory it was found from: one repository's work trees, linked\n * and main, share it, and no two repositories do. Asked again only where\n * the one start of git could not be read.\n */\nexport async function foundCommonDir(found: FoundTree): Promise<string | null> {\n // Asked again from the top, git names the directory the one start named:\n // the mutant that always asks again is equivalent, at a process's cost.\n return found.tree === 'unread' ? commonDirectory(found.root) : found.tree.commonDir;\n}\n\nexport async function openWorkspace(options: Pick<Options, 'root'>, io: CliIO, installs: Installs = NO_INSTALLS): Promise<Workspace> {\n const cwd = options.root ?? io.cwd;\n const found = await findTree(cwd);\n if (found === null) throw new UsageError(`${cwd} is not inside a git work tree; spec-harness measures rounds by their commits`);\n return openFound(found, io, installs);\n}\n\n/** The installs a work tree's siblings are looked for in after its own, given its top and its common directory (siblings.ts). */\nexport type Installs = (root: string, commonDir: string) => readonly string[];\n\n/**\n * Every command but the hooks and doctor: a work tree is checked with its\n * own install, as it was. A name put on the list is a directory relative to\n * where the command runs, which holds no install, and no list at all is\n * the siblings' own default of none, so those mutants are equivalent.\n */\nconst NO_INSTALLS: Installs = () => [];\n\n/**\n * git's hook and doctor: a linked worktree with no install of its own is\n * checked with the main work tree's, where `git worktree add` leaves the\n * project's `node_modules` (ADR-0005).\n */\nexport const MAIN_INSTALL: Installs = (root, commonDir) => {\n const main = mainWorkTree(commonDir);\n // The main work tree's own install is the one looked in first: the mutant\n // that keeps it here as well looks in it twice and finds what it found.\n return main === null || main === root ? [] : [main];\n};\n\n/** The rest of opening a work tree, once it is found: its common directory, its configuration and its branch. */\nexport async function openFound(found: FoundTree, io: CliIO, installs: Installs = NO_INSTALLS): Promise<Workspace> {\n const { cwd, root, tree, early } = found;\n // The common directory and the branch are asked of the root. What git\n // said from `cwd` is that answer where `cwd` is the root, the same\n // directory asked the same thing, and nowhere else. Measured with git\n // 2.43 and 2.55: started in a work tree that `core.worktree` names, away\n // from its repository, git finds no repository, so asked of that root it\n // names no branch, where from the repository it names one. So elsewhere\n // the two are asked for from the root, as they were, at once. Only the\n // configuration can fail, so what is thrown is what it threw.\n const here = tree !== 'unread' && isTop(cwd, root) ? tree : null;\n const [common, { config }, checkedOut] = await Promise.all([here === null ? commonDirectory(root) : here.commonDir, loadConfig(root), here === null || early === undefined ? currentBranch(root) : early]);\n // Asked from the top of its own work tree, git names the common\n // directory. From a root that holds no repository, the work tree that\n // `core.worktree` names away from its own, it names none, and the\n // directory a repository would have there stands in, as it always has.\n const commonDir = common ?? `${root}/.git`;\n // A branch checked out is the branch; only a head on none, as CI checks\n // out, asks the forge which branch the run builds.\n const ci = checkedOut === null ? ciBranch(io.env) : null;\n const named = ci === null ? { branch: checkedOut } : { branch: ci.branch, branchSource: ci.source };\n return { root, commonDir, config, siblings: createSiblings(root, config, installs(root, commonDir)), ...named, cwd: io.cwd };\n}\n\n/**\n * What one of several things asked at once answered, or what it threw,\n * thrown from here: the things are asked through `Promise.allSettled`, and\n * each is read with this where it was awaited while they were asked one\n * after another. So a run in which one of them fails still fails on the\n * first that failed in that order, whichever failed first by the clock,\n * and says what it said.\n */\nexport function answerOf<T>(asked: PromiseSettledResult<T>): T {\n if (asked.status === 'rejected') throw asked.reason;\n return asked.value;\n}\n\n/** The id the flag, the environment or the branch names, without asking spec-brief anything. */\nexport function namedId(workspace: Workspace, options: Pick<Options, 'brief'>, env: CliIO['env']): string | null {\n if (options.brief !== undefined && options.brief.trim() !== '') return options.brief.trim();\n const environment = env['SPEC_BRIEF'];\n if (environment !== undefined && environment.trim() !== '') return environment.trim();\n return briefIdFromBranch(workspace.config.branches, workspace.branch);\n}\n\nexport async function activeBrief(workspace: Workspace, options: Pick<Options, 'brief'>, env: CliIO['env']): Promise<ActiveBrief> {\n // Nothing names a brief, so spec-brief is not asked (ADR-0004): the answer\n // is none whatever the briefs are, and so the empty list is equivalent to\n // its mutant.\n if (namedId(workspace, options, env) === null) {\n return findActive([], {});\n }\n const briefs = await workspace.siblings.briefs();\n const fromBranch = briefIdFromBranch(workspace.config.branches, workspace.branch);\n return findActive(briefs, { flag: options.brief, environment: env['SPEC_BRIEF'], branch: fromBranch });\n}\n\n/**\n * What named a brief, as its caller knows it. An argument is the command\n * line's by default, the id after the command; a tool call's is its `brief`,\n * which the server says as it says every argument, by its name in quotes.\n */\nfunction namedBy(source: ActiveSource, argument: string): string {\n return source === 'argument' ? argument : `the ${source}`;\n}\n\n/** What is said of a name that names no brief spec-brief knows, by what named it. */\nexport function namesNoBrief(active: Extract<ActiveBrief, { kind: 'unknown' }>, argument = 'the argument'): string {\n return `${namedBy(active.source, argument)} names brief ${active.id}, and spec-brief knows no such brief`;\n}\n\n/**\n * The brief a command acts on, or why there is none: a note when nothing names\n * one, which a command that needs a brief refuses with, or a problem when what\n * names one is wrong. `argument` is what the caller calls an argument that\n * named it.\n */\nexport function describeActive(\n active: ActiveBrief,\n argument = 'the argument',\n): { brief: BriefRow; note: undefined; problem: null } | { brief: null; note: string; problem: null } | { brief: null; note: undefined; problem: string } {\n switch (active.kind) {\n case 'found':\n return { brief: active.brief, note: undefined, problem: null };\n case 'none':\n return { brief: null, note: active.reason, problem: null };\n case 'unknown':\n return { brief: null, note: undefined, problem: namesNoBrief(active, argument) };\n case 'archived':\n return {\n brief: null,\n note: undefined,\n problem: `${namedBy(active.source, argument)} names brief ${active.brief.id}, which is archived; a closed round writes nothing`,\n };\n }\n}\n\nexport function json(command: string, body: Record<string, unknown>): string {\n return `${JSON.stringify({ tool: 'spec-harness', version: version(), schemaVersion: JSON_SCHEMA_VERSION, command, ...body }, null, 2)}\\n`;\n}\n\n"]}
|
|
1
|
+
{"version":3,"file":"workspace.js","sourceRoot":"","sources":["../src/workspace.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * What every command shares: exit codes, options, the repository a command\n * runs in, and which brief the round is working on.\n */\n\nimport { readFileSync } from 'node:fs';\nimport { dirname, join } from 'node:path';\nimport { parseArgs, type ParseArgsOptionsConfig } from 'node:util';\n\nimport { findActive, type ActiveBrief, type ActiveSource } from './briefs.js';\nimport { briefIdFromBranch, ciBranch } from './branch.js';\nimport type { HarnessConfig } from './config.js';\nimport { holdsGit, isTop, loadConfig, mainWorkTree, reachedBy, realSpelling, repositoryPath } from './fs.js';\nimport { commonDirectory, currentBranch, workTree, workTreeRoot, type WorkTree } from './git.js';\nimport type { FindingFormat } from './formats.js';\nimport { createSiblings, type Siblings } from './siblings.js';\nimport type { BriefRow } from './types.js';\n\nexport const EXIT_OK = 0;\nexport const EXIT_FAILED = 1;\nexport const EXIT_ERROR = 2;\n\n/** The version of the JSON documents this tool prints. Bumped when a field changes meaning. */\nexport const JSON_SCHEMA_VERSION = 1;\n\nexport class UsageError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'UsageError';\n }\n}\n\n/**\n * A command line the parser refused, with the command and the arguments on\n * it. Whose line it was decides what the refusal is: Claude Code's hook says\n * it and holds no write for it, where every other command stops (cli.ts).\n */\nexport class UnreadableLine extends UsageError {\n readonly words: readonly string[];\n\n constructor(message: string, words: readonly string[]) {\n super(message);\n this.words = words;\n }\n}\n\nexport interface CliIO {\n readonly stdout: { write(text: string): unknown };\n readonly stderr: { write(text: string): unknown };\n readonly stdin?: () => Promise<string>;\n readonly cwd: string;\n readonly env: Readonly<Record<string, string | undefined>>;\n}\n\nexport function version(): string {\n // A Buffer parses as its text, so the encoding is equivalent to its mutant.\n const manifest: { version: string } = JSON.parse(readFileSync(new URL('../package.json', import.meta.url), 'utf8'));\n return manifest.version;\n}\n\nexport interface Options {\n readonly command: string | undefined;\n readonly positionals: readonly string[];\n /** The long name of every option given, in the order given: what tells an option left out from one a command does not read. */\n readonly given: readonly string[];\n /**\n * Every value given to each option that takes one value, by its long name,\n * in the order given. The fields below hold the last of them, which is\n * what the parser keeps.\n */\n readonly written: Readonly<Record<string, readonly string[]>>;\n readonly brief: string | undefined;\n readonly base: string | undefined;\n readonly root: string | undefined;\n /** `gitlab`, `sarif` and `github` only for `audit` and `premises`, whose findings have places. */\n readonly format: 'pretty' | 'json' | FindingFormat;\n readonly strict: boolean;\n readonly help: boolean;\n readonly version: boolean;\n /** init: apply the plan instead of printing it. */\n readonly write: boolean;\n /** init: also install git's pre-commit hook. */\n readonly gitHook: boolean;\n /** init: `onFailure: \"block\"` on the hook entries it writes (ADR-0012). */\n readonly blockOnFailure: boolean;\n /** escalate: the files the round needs, the reason, the options and the recommendation. */\n readonly paths: readonly string[];\n readonly reason: string | undefined;\n readonly options: readonly string[];\n readonly recommend: string | undefined;\n readonly list: boolean;\n readonly show: string | undefined;\n /** rule: the decision and what it allows. */\n readonly allow: boolean;\n readonly deny: boolean;\n readonly note: string | undefined;\n /** probe: which probe, and at which commit. */\n readonly id: string | undefined;\n readonly at: 'base' | 'head' | 'both' | undefined;\n}\n\nexport function parseOptions(argv: readonly string[]): Options {\n // `satisfies`, where a const assertion would read the same to the compiler:\n // the sweep mutates nothing that stands under an `as`, the table with it.\n const options = {\n brief: { type: 'string' },\n base: { type: 'string' },\n root: { type: 'string' },\n format: { type: 'string' },\n strict: { type: 'boolean' },\n write: { type: 'boolean' },\n 'git-hook': { type: 'boolean' },\n 'block-on-failure': { type: 'boolean' },\n path: { type: 'string', multiple: true },\n reason: { type: 'string' },\n option: { type: 'string', multiple: true },\n recommend: { type: 'string' },\n list: { type: 'boolean' },\n show: { type: 'string' },\n allow: { type: 'boolean' },\n deny: { type: 'boolean' },\n note: { type: 'string' },\n id: { type: 'string' },\n at: { type: 'string' },\n help: { type: 'boolean', short: 'h' },\n version: { type: 'boolean', short: 'v' },\n } satisfies ParseArgsOptionsConfig;\n let parsed;\n try {\n parsed = parseArgs({ args: [...argv], allowPositionals: true, strict: true, options, tokens: true });\n } catch (error) {\n // Read again with nothing refused, for whose line it was and nothing\n // else: an option no command has is passed over, one left without its\n // value takes none, and no value read this way is used. The same table,\n // so that a value is not taken for the command it stands before.\n throw new UnreadableLine((error as Error).message, parseArgs({ args: [...argv], strict: false, options }).positionals);\n }\n const { values, positionals, tokens } = parsed;\n // The parser keeps the last value of an option given twice, and the earlier\n // went unread with no word of it: `--root nowhere --root .` ran in `.`. So\n // every value is kept, for the refusal of two (usage.ts). A list keeps\n // every value already.\n const table: ParseArgsOptionsConfig = options;\n const written: Record<string, string[]> = {};\n for (const token of tokens) {\n if (token.kind === 'option' && token.value !== undefined && table[token.name]!.multiple !== true) (written[token.name] ??= []).push(token.value);\n }\n // Two answers asked for at once: the version was printed, and the help went\n // unread. A line that is wrong, whoever's it is (spec-core ADR-0005). Only\n // an option's token has a name, so the test of its kind is the compiler's,\n // and its mutant is equivalent.\n const answers = tokens.flatMap((token) => (token.kind === 'option' && (token.name === 'help' || token.name === 'version') ? [token] : []));\n const other = answers.find((token) => token.name !== answers[0]!.name);\n if (other !== undefined) throw new UnreadableLine(`${answers[0]!.rawName} is given beside ${other.rawName}, and one of the two answers would go unread; give one of them`, positionals);\n const asked = values.format ?? 'pretty';\n const format = asked as Options['format'];\n const formats: readonly Options['format'][] = ['pretty', 'json', 'gitlab', 'sarif', 'github'];\n if (!formats.includes(format)) throw new UnreadableLine(`--format must be pretty, json, gitlab, sarif or github, not \"${format}\"`, positionals);\n const command = positionals[0];\n // Only the commands whose findings have places print them for a forge.\n if (command !== undefined && command !== 'audit' && command !== 'premises' && format !== 'pretty' && format !== 'json') {\n throw new UnreadableLine(`--format ${format} is for audit and premises; ${command} prints pretty or json`, positionals);\n }\n const at = values.at;\n if (at !== undefined && at !== 'base' && at !== 'head' && at !== 'both') throw new UnreadableLine(`--at must be base, head or both, not \"${at}\"`, positionals);\n return {\n command,\n positionals: positionals.slice(1),\n given: Object.keys(values),\n written,\n brief: values.brief,\n base: values.base,\n root: values.root,\n format,\n strict: values.strict === true,\n help: values.help === true,\n version: values.version === true,\n write: values.write === true,\n gitHook: values['git-hook'] === true,\n blockOnFailure: values['block-on-failure'] === true,\n paths: values.path ?? [],\n reason: values.reason,\n options: values.option ?? [],\n recommend: values.recommend,\n list: values.list === true,\n show: values.show,\n allow: values.allow === true,\n deny: values.deny === true,\n note: values.note,\n id: values.id,\n at,\n };\n}\n\n/** Everything a command needs about the repository it runs in. */\nexport interface Workspace {\n readonly root: string;\n readonly commonDir: string;\n readonly config: HarnessConfig;\n readonly siblings: Siblings;\n /** The branch checked out, or on a detached head the one the forge's CI names; `null` when neither names one. */\n readonly branch: string | null;\n /** The CI variable that named the branch, on a detached head; absent when git named it. */\n readonly branchSource?: string | undefined;\n readonly cwd: string;\n}\n\n/**\n * A work tree as the first wave of opening it found it: the directory it\n * was asked from, its top, what one start of git said from there, and the\n * branch where it was asked beside them.\n */\nexport interface FoundTree {\n readonly cwd: string;\n readonly root: string;\n readonly tree: WorkTree | 'unread';\n readonly early: string | null | undefined;\n}\n\n/** The work tree that holds `cwd`, or `null` where none does. */\nexport async function findTree(cwd: string): Promise<FoundTree | null> {\n // Each git command is a process, a few milliseconds on Linux and a few\n // hundred on a Windows workstation, and every command opens a work tree,\n // the hook before every write among them. So the top and the common\n // directory are asked for in one start of git, and where a `.git` stands\n // in the directory, as it does at the top of a work tree, the branch is\n // asked for beside them and not after. Anywhere else the branch waits, as\n // it did: below a top it would be asked twice, and in no work tree for\n // nothing.\n const [tree, early] = await Promise.all([workTree(cwd), holdsGit(cwd) ? currentBranch(cwd) : undefined]);\n if (tree === null) return null;\n const root = tree === 'unread' ? await workTreeRoot(cwd) : tree.root;\n return root === null ? null : { cwd, root, tree, early };\n}\n\n/**\n * The git directory a found work tree's repository keeps, as git names it\n * from the directory it was found from: one repository's work trees, linked\n * and main, share it, and no two repositories do. Asked again only where\n * the one start of git could not be read.\n */\nexport async function foundCommonDir(found: FoundTree): Promise<string | null> {\n // Asked again from the top, git names the directory the one start named:\n // the mutant that always asks again is equivalent, at a process's cost.\n return found.tree === 'unread' ? commonDirectory(found.root) : found.tree.commonDir;\n}\n\nexport async function openWorkspace(options: Pick<Options, 'root'>, io: CliIO, installs: Installs = MAIN_INSTALL): Promise<Workspace> {\n const cwd = options.root ?? io.cwd;\n const found = await findTree(cwd);\n if (found === null) throw new UsageError(`${cwd} is not inside a git work tree; spec-harness measures rounds by their commits`);\n return openFound(found, io, installs);\n}\n\n/** The installs a work tree's siblings are looked for in after its own, given its top and its common directory (siblings.ts). */\nexport type Installs = (root: string, commonDir: string) => readonly string[];\n\n/**\n * Every command: a linked worktree with no install of its own is checked\n * with the main work tree's, where `git worktree add` leaves the project's\n * `node_modules`, as git's hook and doctor were, and as the hook is with the\n * project's (ADR-0005).\n */\nexport const MAIN_INSTALL: Installs = (root, commonDir) => {\n const main = mainWorkTree(commonDir);\n // The main work tree's own install is the one looked in first: the mutant\n // that keeps it here as well looks in it twice and finds what it found.\n return main === null || main === root ? [] : [main];\n};\n\n/** The rest of opening a work tree, once it is found: its common directory, its configuration and its branch. */\nexport async function openFound(found: FoundTree, io: CliIO, installs: Installs = MAIN_INSTALL): Promise<Workspace> {\n const { cwd, root, tree, early } = found;\n // The common directory and the branch are asked of the root. What git\n // said from `cwd` is that answer where `cwd` is the root, the same\n // directory asked the same thing, and nowhere else. Measured with git\n // 2.43 and 2.55: started in a work tree that `core.worktree` names, away\n // from its repository, git finds no repository, so asked of that root it\n // names no branch, where from the repository it names one. So elsewhere\n // the two are asked for from the root, as they were, at once. Only the\n // configuration can fail, so what is thrown is what it threw.\n const here = tree !== 'unread' && isTop(cwd, root) ? tree : null;\n const [common, { config }, checkedOut] = await Promise.all([here === null ? commonDirectory(root) : here.commonDir, loadConfig(root), here === null || early === undefined ? currentBranch(root) : early]);\n // Asked from the top of its own work tree, git names the common\n // directory. From a root that holds no repository, the work tree that\n // `core.worktree` names away from its own, it names none, and the\n // directory a repository would have there stands in, as it always has.\n const commonDir = common ?? `${root}/.git`;\n // A branch checked out is the branch; only a head on none, as CI checks\n // out, asks the forge which branch the run builds.\n const ci = checkedOut === null ? ciBranch(io.env) : null;\n const named = ci === null ? { branch: checkedOut } : { branch: ci.branch, branchSource: ci.source };\n return { root, commonDir, config, siblings: createSiblings(root, config, installs(root, commonDir)), ...named, cwd: io.cwd };\n}\n\n/**\n * What one of several things asked at once answered, or what it threw,\n * thrown from here: the things are asked through `Promise.allSettled`, and\n * each is read with this where it was awaited while they were asked one\n * after another. So a run in which one of them fails still fails on the\n * first that failed in that order, whichever failed first by the clock,\n * and says what it said.\n */\nexport function answerOf<T>(asked: PromiseSettledResult<T>): T {\n if (asked.status === 'rejected') throw asked.reason;\n return asked.value;\n}\n\n/** The id the flag, the environment or the branch names, without asking spec-brief anything. */\nexport function namedId(workspace: Workspace, options: Pick<Options, 'brief'>, env: CliIO['env']): string | null {\n if (options.brief !== undefined && options.brief.trim() !== '') return options.brief.trim();\n const environment = env['SPEC_BRIEF'];\n if (environment !== undefined && environment.trim() !== '') return environment.trim();\n return briefIdFromBranch(workspace.config.branches, workspace.branch);\n}\n\nexport async function activeBrief(workspace: Workspace, options: Pick<Options, 'brief'>, env: CliIO['env']): Promise<ActiveBrief> {\n // Nothing names a brief, so spec-brief is not asked (ADR-0004): the answer\n // is none whatever the briefs are, and so the empty list is equivalent to\n // its mutant.\n if (namedId(workspace, options, env) === null) {\n return findActive([], {});\n }\n const briefs = await workspace.siblings.briefs();\n const fromBranch = briefIdFromBranch(workspace.config.branches, workspace.branch);\n return findActive(briefs, { flag: options.brief, environment: env['SPEC_BRIEF'], branch: fromBranch });\n}\n\n/**\n * What named a brief, as its caller knows it. An argument is the command\n * line's by default, the id after the command; a tool call's is its `brief`,\n * which the server says as it says every argument, by its name in quotes.\n */\nfunction namedBy(source: ActiveSource, argument: string): string {\n return source === 'argument' ? argument : `the ${source}`;\n}\n\n/** What is said of a name that names no brief spec-brief knows, by what named it. */\nexport function namesNoBrief(active: Extract<ActiveBrief, { kind: 'unknown' }>, argument = 'the argument'): string {\n return `${namedBy(active.source, argument)} names brief ${active.id}, and spec-brief knows no such brief`;\n}\n\n/**\n * The brief a command acts on, or why there is none: a note when nothing names\n * one, which a command that needs a brief refuses with, or a problem when what\n * names one is wrong. `argument` is what the caller calls an argument that\n * named it.\n */\nexport function describeActive(\n active: ActiveBrief,\n argument = 'the argument',\n): { brief: BriefRow; note: undefined; problem: null } | { brief: null; note: string; problem: null } | { brief: null; note: undefined; problem: string } {\n switch (active.kind) {\n case 'found':\n return { brief: active.brief, note: undefined, problem: null };\n case 'none':\n return { brief: null, note: active.reason, problem: null };\n case 'unknown':\n return { brief: null, note: undefined, problem: namesNoBrief(active, argument) };\n case 'archived':\n return {\n brief: null,\n note: undefined,\n problem: `${namedBy(active.source, argument)} names brief ${active.brief.id}, which is archived; a closed round writes nothing`,\n };\n }\n}\n\nexport function json(command: string, body: Record<string, unknown>): string {\n return `${JSON.stringify({ tool: 'spec-harness', version: version(), schemaVersion: JSON_SCHEMA_VERSION, command, ...body }, null, 2)}\\n`;\n}\n\n/**\n * Whether a work tree other than the one whose top is `top` may hold `cwd`:\n * `cwd` is outside it, or a `.git` stands in a directory between the two,\n * where a linked worktree, a submodule or a repository of its own starts.\n * Where neither is so, `cwd` is in that work tree, and git is asked nothing\n * more: a session works at its project's top, or below it, for nearly every\n * write. Answering that one may is what git is then asked, so the mutant\n * that always answers so is equivalent, at the cost of a process, and so\n * is each that only answers so more often: a slice one short reaches the\n * top itself, which holds a `.git`, and a part named in place of none is\n * a directory no top holds.\n */\nexport function mayBeElsewhere(cwd: string, top: string): boolean {\n const path = repositoryPath(cwd, top, cwd);\n if (path === null) return true;\n const parts = path === '' ? [] : path.split('/');\n return parts.some((_, index) => holdsGit(join(top, ...parts.slice(0, index + 1))));\n}\n\n/**\n * The work tree that holds `cwd`, where it is another work tree of the\n * repository the work tree `named` is one of, as the hook's root names it, by the git directory the two share;\n * `null` where `cwd` is in the root's own, in another repository, or in\n * none (ADR-0005).\n */\nexport async function otherTree(named: Workspace, cwd: string): Promise<FoundTree | null> {\n // Asked anyway, git names the root's own top for a directory in its work\n // tree, and none for one not made, either of which answers `null` below:\n // the mutant that always asks is equivalent, at a process's cost.\n if (!mayBeElsewhere(cwd, named.root)) return null;\n const found = await findTree(cwd);\n // The project's own work tree, found again, would decide every path the\n // same way once more: the check saves reading its configuration twice, and\n // its mutant is equivalent.\n if (found === null || found.root === named.root) return null;\n const common = await foundCommonDir(found);\n // git names a common directory wherever it names a top, so `null` is\n // there for the type.\n if (common === null || realSpelling(common) !== realSpelling(named.commonDir)) return null;\n return found;\n}\n\n/**\n * The top of the nearest work tree, of any repository, above a directory or\n * a file: the nearest directory above it that holds a `.git`, found with no\n * process; `null` where none does. A path not yet written has the\n * directories it would be written in looked through, those not made yet\n * among them.\n */\nfunction topAbove(path: string): string | null {\n for (let directory = dirname(path); ; directory = dirname(directory)) {\n if (holdsGit(directory)) return directory;\n // Only the top of the filesystem is its own parent.\n if (dirname(directory) === directory) return null;\n }\n}\n\n/**\n * The innermost work tree of the repository the work tree `named` is one of\n * that holds a file, given by its real spelling, where it is not `named`\n * itself: a linked worktree or the main one, inside the project, beside it,\n * or around it (ADR-0005). The\n * directories above the path are looked through, with no process, for each\n * that holds a `.git`; git is asked of each that is not `named`'s top, and\n * one that git names as no work tree of this repository's, a submodule's, a\n * clone's inside the tree, or a `.git` that stands in no work tree's top, is\n * passed by for the next above it. `null` where the first of this repository\n * met is `named`, or none is: the path is decided by `named`, as before, a\n * path in no work tree of the repository among them.\n */\nasync function treeHolding(named: Workspace, spelled: string): Promise<FoundTree | null> {\n const own = realSpelling(named.root);\n for (let top = topAbove(spelled); top !== null; top = topAbove(top)) {\n if (top === own) return null;\n const tree = await otherTree(named, top);\n // git names a work tree whose top is not this directory where it reads\n // the `.git` here as no repository's, an empty directory say, and names\n // the one around it: the walk meets that one's top next, past any `.git`\n // between that git reads as none too, and takes it. So the mutant that\n // takes what git names here is equivalent for every work tree git makes;\n // the check keeps a `core.worktree` set by hand to name another\n // directory from deciding a path that directory does not hold.\n if (tree !== null && realSpelling(tree.root) === top) return tree;\n }\n return null;\n}\n\n/**\n * The work trees a write is decided in, each with the paths it decides, for\n * the hook and for the server's `check_path` (ADR-0005). The repository is\n * the one the hook's root names, as before. Each path is decided by the\n * innermost work tree of that repository that holds it, by its branch, its\n * briefs and its configuration, read from its top, with its siblings looked\n * for in its install and then the project's: there a session works in a\n * worktree while Claude Code names the project it started in, or writes from\n * one into another, and the hook decided by the project's branch, which\n * named no brief, so a protected write passed with no word. A path in no\n * other work tree of the repository is decided by the work tree the root\n * names, as before: a write to the project's own file from a worktree is\n * refused where it was, and a path in another repository, or in none, is\n * read as it was. `cwd` is what a relative path is read from.\n */\nexport async function writesBy(named: Workspace, cwd: string | null, paths: readonly string[], io: CliIO): Promise<{ readonly workspace: Workspace; readonly paths: readonly string[] }[]> {\n const from = cwd ?? io.cwd;\n const trees: { readonly tree: FoundTree | null; readonly paths: string[] }[] = [];\n // The parts in the order their work trees are first met, each with its\n // paths in the order they were named.\n for (const path of paths) {\n // Each file the path reaches, as it reads and as the system writes it,\n // by the work tree that holds that file: a link can lead from one work\n // tree into another, and each decides the file it holds.\n const held: (FoundTree | null)[] = [];\n for (const spelled of reachedBy(path, from)) {\n const tree = await treeHolding(named, spelled);\n if (!held.some((other) => other?.root === tree?.root)) held.push(tree);\n }\n for (const tree of held) {\n const part = trees.find((other) => other.tree?.root === tree?.root);\n if (part === undefined) trees.push({ tree, paths: [path] });\n else part.paths.push(path);\n }\n }\n // The root's own last, as it was decided after the work tree Claude works in.\n const ordered = [...trees.filter((part) => part.tree !== null), ...trees.filter((part) => part.tree === null)];\n return Promise.all(ordered.map(async ({ tree, paths: held }) => ({ workspace: tree === null ? named : await openFound(tree, io, () => [named.root]), paths: held })));\n}\n"]}
|