cohorte 3.0.0-dev.3 → 3.0.0-dev.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.mjs CHANGED
@@ -33,7 +33,7 @@ const COMMAND_MODULES = Object.freeze({
33
33
  review: () => import("./chunks/review-BDdjb6kB.mjs"),
34
34
  fix: () => import("./chunks/fix-D4Tj8RVA.mjs"),
35
35
  ship: () => import("./chunks/ship-DTuLGoaJ.mjs"),
36
- auth: () => import("./chunks/auth-RuhYgTI7.mjs"),
36
+ auth: () => import("./chunks/auth-Dnlksuo5.mjs"),
37
37
  providers: () => import("./chunks/providers-Dx78imKj.mjs"),
38
38
  models: () => import("./chunks/models-Btp6X2YZ.mjs"),
39
39
  config: () => import("./chunks/config-DefHA2Lw.mjs"),
@@ -61,7 +61,7 @@ async function loadCommandModule(verb) {
61
61
  * only through here, so a read-only verb whose action never runs never pays for it either (DESIGN 1.3).
62
62
  */
63
63
  async function loadCliContext(inputs) {
64
- const mod = await import("./chunks/compose-D8rro98O.mjs");
64
+ const mod = await import("./chunks/compose-CC9_NMzm.mjs");
65
65
  if (typeof mod.composeCliContext !== "function") throw new TypeError("lazy.ts: compose/index.ts has no composeCliContext export");
66
66
  return mod.composeCliContext(inputs);
67
67
  }
@@ -69,7 +69,7 @@ async function loadCliContext(inputs) {
69
69
  //#region src/cli.ts
70
70
  /** Mirrors `apps/cli/package.json` "version"; `apps/cli/test/registry/version.test.ts` reads that file and fails
71
71
  * if the two ever drift (a literal keeps `--version` free of any file read on the one-shot start-up path). */
72
- const COHORTE_VERSION = "3.0.0-dev.3";
72
+ const COHORTE_VERSION = "3.0.0-dev.4";
73
73
  function printError(deps, info) {
74
74
  deps.stderr.write(`error: ${info.code}: ${info.message}\n`);
75
75
  deps.stderr.write(`impact: ${info.impact}\n`);
package/dist/cli.mjs.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"cli.mjs","names":[],"sources":["../../../apps/cli/src/lazy.ts","../../../apps/cli/src/cli.ts"],"sourcesContent":["// apps/cli/src/lazy.ts — the ONLY `import(` site of `apps/cli/src` (DESIGN 1.2 net 3 rule g, DESIGN 1.3, DESIGN\n// 6.3 §\"read-only verbs never load core/runtime\"). `check-layers` refuses `import(` anywhere else under\n// `apps/cli/src/**` (rule g is per FILE, not per occurrence), so a verb module that needs `compose`/`host`/etc.\n// reaches them only through here, which keeps `cli.ts` (and every module reachable from it without going through\n// `lazy.ts`) inside François' 10 s / 4 MiB one-shot budget for a verb that never needs the heavy ports.\nimport type { CliContext, CommandModule } from './contract/index.ts';\n\n/** What `compose/index.ts` (U4.01) exports: assembles the real `CliContext` from process-level inputs. */\nexport interface ComposeInputs {\n readonly cwd: string;\n readonly env: NodeJS.ProcessEnv;\n readonly stdio: {\n readonly stdout: NodeJS.WritableStream;\n readonly stderr: NodeJS.WritableStream;\n readonly stdin: NodeJS.ReadableStream;\n };\n}\n\n/**\n * One LITERAL specifier per verb, closed over a thunk. A computed specifier (`` import(`./commands/${verb}/…`) ``)\n * is invisible to every bundler: rolldown would leave the template in `dist/cli.mjs` and emit no verb chunk, so the\n * SHIPPED cli could not execute a single verb (it would try to load a `.ts` file next to `dist/`). Written out, each\n * specifier is statically analysable: rolldown code-splits one chunk per verb, laziness is preserved, and\n * `apps/cli/test/registry/lazy.test.ts` pins this map against `VERBS` in both directions so a verb added to the\n * registry without a loader — or the reverse — fails Wave 0's own check.\n */\nexport const COMMAND_MODULES: Readonly<Record<string, () => Promise<unknown>>> = Object.freeze({\n init: () => import('./commands/init/index.ts'),\n doctor: () => import('./commands/doctor/index.ts'),\n discover: () => import('./commands/discover/index.ts'),\n run: () => import('./commands/run/index.ts'),\n status: () => import('./commands/status/index.ts'),\n inspect: () => import('./commands/inspect/index.ts'),\n resume: () => import('./commands/resume/index.ts'),\n pause: () => import('./commands/pause/index.ts'),\n cancel: () => import('./commands/cancel/index.ts'),\n shutdown: () => import('./commands/shutdown/index.ts'),\n approve: () => import('./commands/approve/index.ts'),\n deny: () => import('./commands/deny/index.ts'),\n retry: () => import('./commands/retry/index.ts'),\n skip: () => import('./commands/skip/index.ts'),\n logs: () => import('./commands/logs/index.ts'),\n tail: () => import('./commands/tail/index.ts'),\n diff: () => import('./commands/diff/index.ts'),\n review: () => import('./commands/review/index.ts'),\n fix: () => import('./commands/fix/index.ts'),\n ship: () => import('./commands/ship/index.ts'),\n auth: () => import('./commands/auth/index.ts'),\n providers: () => import('./commands/providers/index.ts'),\n models: () => import('./commands/models/index.ts'),\n config: () => import('./commands/config/index.ts'),\n migrate: () => import('./commands/migrate/index.ts'),\n reconcile: () => import('./commands/reconcile/index.ts'),\n spec: () => import('./commands/spec/index.ts'),\n policy: () => import('./commands/policy/index.ts'),\n gc: () => import('./commands/gc/index.ts'),\n update: () => import('./commands/update/index.ts'),\n brainstorm: () => import('./commands/brainstorm/index.ts'),\n 'run-tool': () => import('./commands/run-tool/index.ts'),\n send: () => import('./commands/send/index.ts'),\n __host: () => import('./commands/__host/index.ts'),\n});\n\n/** Loads `commands/<verb>/index.ts` and returns its default export. */\nexport async function loadCommandModule(verb: string): Promise<CommandModule> {\n const load = COMMAND_MODULES[verb];\n if (!load) throw new RangeError(`lazy.ts: no command module registered for verb \"${verb}\"`);\n const loaded = await load();\n const mod = loaded as { default?: CommandModule };\n if (!mod.default || typeof mod.default.run !== 'function') {\n throw new TypeError(`lazy.ts: commands/${verb}/index.ts has no default CommandModule export`);\n }\n return mod.default;\n}\n\n/**\n * The one `import(` of `./compose/index.ts` (DESIGN 1.2 net 3 rule g): `cli.ts` reaches the real `CliContext`\n * only through here, so a read-only verb whose action never runs never pays for it either (DESIGN 1.3).\n */\nexport async function loadCliContext(inputs: ComposeInputs): Promise<CliContext> {\n const loaded: unknown = await import('./compose/index.ts');\n const mod = loaded as { composeCliContext?: (inputs: ComposeInputs) => Promise<CliContext> };\n if (typeof mod.composeCliContext !== 'function') {\n throw new TypeError('lazy.ts: compose/index.ts has no composeCliContext export');\n }\n return mod.composeCliContext(inputs);\n}\n","#!/usr/bin/env node\n// apps/cli/src/cli.ts — DESIGN §9 CLI row: the whole verb registry, built from `contract/verbs.ts` and nothing\n// else (PLAN §3 rule 3 \"cli.ts pre-registers every verb against commands/<verb>/index.ts; ... nobody edits the\n// registry\"). `cli.ts` never imports `core`/`runtime-*`/`security`/`persistence` directly: a verb's module, and the\n// real `CliContext`, are reached only through `lazy.ts`'s single `import()` site (DESIGN 1.2 net 3 rule g, DESIGN\n// 1.3 \"read-only verbs never load core/runtime\") — so `--help` and `--version`, which commander answers itself\n// without ever calling `dispatch`, pay nothing beyond this file and `commander`.\nimport { toErrorInfo } from '@cohorte/base';\nimport { Command, CommanderError } from 'commander';\nimport {\n type CliContext,\n CONTROLLER_EXIT_CODES,\n type CommandArgs,\n EXIT_CODE_BY_CLASS,\n formatExitCodesHelp,\n VERBS,\n type VerbSpec,\n} from './contract/index.ts';\nimport { loadCliContext, loadCommandModule } from './lazy.ts';\n\n/** Mirrors `apps/cli/package.json` \"version\"; `apps/cli/test/registry/version.test.ts` reads that file and fails\n * if the two ever drift (a literal keeps `--version` free of any file read on the one-shot start-up path). */\nexport const COHORTE_VERSION = '3.0.0-dev.3';\n\nexport interface RuntimeDeps {\n readonly stdout: NodeJS.WritableStream;\n readonly stderr: NodeJS.WritableStream;\n /** Built lazily: only a verb whose action actually runs calls this (never `--help`/`--version`, DESIGN 1.3). */\n readonly context: () => Promise<CliContext>;\n}\n\nfunction printError(deps: RuntimeDeps, info: ReturnType<typeof toErrorInfo>): void {\n // spec 21: \"cause / impact / run / next action / exit code\"\n deps.stderr.write(`error: ${info.code}: ${info.message}\\n`);\n deps.stderr.write(`impact: ${info.impact}\\n`);\n deps.stderr.write(`next action: ${info.remediation}\\n`);\n}\n\n/** Loads the verb's module, builds its `CliContext` and runs it, converting any throw at this one boundary\n * (DESIGN 2.8: \"Every error has ... an exit code\"; the module itself never writes to stdio on error). */\nasync function dispatch(deps: RuntimeDeps, verb: string, args: CommandArgs): Promise<number> {\n try {\n const mod = await loadCommandModule(verb);\n const ctx = await deps.context();\n return await mod.run(ctx, args);\n } catch (error) {\n // DESIGN 2.8: \"Unknown throwables -> `<nearest class>/unexpected`\". A `CohorteError` keeps its own code through\n // `toErrorInfo`, so the Wave-0 stubs still answer `configuration/phase-not-available`; anything else — a\n // TypeError in a filled-in verb, a failed store open, a module that will not load — is reported as what it is.\n const info = toErrorInfo(error, { code: 'configuration/unexpected', class: 'configuration' });\n printError(deps, info);\n return EXIT_CODE_BY_CLASS[info.class];\n }\n}\n\ninterface ExitBox {\n code: number;\n}\n\nfunction addAction(command: Command, deps: RuntimeDeps, exit: ExitBox, verb: VerbSpec, subVerb?: string): void {\n command.allowUnknownOption(true).allowExcessArguments(true);\n // `--json` only where DESIGN 2.3.5 gives the verb a published document (`JSON_OUTPUTS`): registering it on every\n // verb would promise a machine output that no schema backs, and the Wave-4 `--json validates` tests read that map.\n if (verb.json) command.option('--json', 'print the machine-readable document for this verb (DESIGN 2.3.5)');\n command.argument('[args...]', 'verb arguments').action(async (positionals: string[], options: { json?: boolean }) => {\n const args: CommandArgs = {\n positionals,\n options,\n json: Boolean(options.json),\n ...(subVerb === undefined ? {} : { subVerb }),\n };\n exit.code = await dispatch(deps, verb.name, args);\n });\n}\n\n/** Builds the whole commander tree. Exported so a test can assert on `--help` / registration without spawning a\n * process (PLAN U0.10 test list: \"every DESIGN §9 verb is registered exactly once and appears in --help\"). */\nexport function buildProgram(deps: RuntimeDeps, exit: ExitBox): Command {\n const program = new Command('cohorte')\n .description('Cohorte: a durable multi-agent development pipeline with a runtime-independent orchestrator.')\n .version(COHORTE_VERSION, '--version', 'print the Cohorte version')\n .exitOverride()\n .configureOutput({\n writeOut: (str) => deps.stdout.write(str),\n writeErr: (str) => deps.stderr.write(str),\n })\n .addHelpText('after', `\\n${formatExitCodesHelp()}\\n`);\n\n for (const verb of VERBS) {\n const sub = program.command(verb.name, verb.hidden ? { hidden: true } : undefined).description(verb.summary);\n if (verb.subVerbs && verb.subVerbs.length > 0) {\n addAction(sub, deps, exit, verb); // bare `cohorte <verb>` with no sub-verb\n for (const subVerb of verb.subVerbs) {\n addAction(sub.command(subVerb), deps, exit, verb, subVerb);\n }\n } else {\n addAction(sub, deps, exit, verb);\n }\n }\n return program;\n}\n\n/**\n * `CONTROLLER_EXIT_CODES.usage` (2) for anything commander itself refuses (bad flag, unknown command, no command at\n * all), `completed` (0) for the routes where commander did exactly what was asked and then stopped the parse.\n *\n * Keyed off commander's OWN `exitCode`, not off the code NAME: commander raises `commander.help` both for the\n * advertised `help [command]` route (which prints the help and means exit 0) and for a bare `cohorte` with no\n * command (exit 1 there — a usage error), and an allow-list of names got the first case wrong while `--help`,\n * raising `commander.helpDisplayed`, was right. `error.exitCode` already separates the two.\n */\nfunction commanderExitCode(error: CommanderError): number {\n return error.exitCode === 0 ? CONTROLLER_EXIT_CODES.completed : CONTROLLER_EXIT_CODES.usage;\n}\n\n/** Parses `argv` (no leading node/script entries) and runs the matched verb. Never calls `process.exit`: the\n * caller (`main()` below, or a test) owns that decision. */\nexport async function runCli(argv: readonly string[], deps: RuntimeDeps): Promise<number> {\n const exit: ExitBox = { code: CONTROLLER_EXIT_CODES.completed };\n const program = buildProgram(deps, exit);\n try {\n await program.parseAsync(argv, { from: 'user' });\n } catch (error) {\n if (error instanceof CommanderError) return commanderExitCode(error);\n throw error;\n }\n return exit.code;\n}\n\nasync function realContext(): Promise<CliContext> {\n return loadCliContext({\n cwd: process.cwd(),\n env: process.env,\n stdio: { stdout: process.stdout, stderr: process.stderr, stdin: process.stdin },\n });\n}\n\nasync function main(): Promise<void> {\n const deps: RuntimeDeps = { stdout: process.stdout, stderr: process.stderr, context: realContext };\n const code = await runCli(process.argv.slice(2), deps);\n process.exitCode = code;\n}\n\n// `import.meta.main` (node >= 24.2; `apps/cli` engines are `^24.16.0 || >=26.1.0`) and NOT a hand-rolled\n// `import.meta.url === \\`file://${process.argv[1]}\\``: npm installs `bin` as a SYMLINK\n// (`node_modules/.bin/cohorte` -> `../cohorte/dist/cli.mjs`), so the comparison is false for every real install and\n// the published CLI would print nothing and exit 0. It also breaks on any install path with a space or a non-ASCII\n// character (`import.meta.url` is percent-encoded, `argv[1]` is not).\nif (import.meta.main) {\n main().catch((error: unknown) => {\n process.stderr.write(`fatal: ${error instanceof Error ? (error.stack ?? error.message) : String(error)}\\n`);\n process.exitCode = 1;\n });\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"cli.mjs","names":[],"sources":["../../../apps/cli/src/lazy.ts","../../../apps/cli/src/cli.ts"],"sourcesContent":["// apps/cli/src/lazy.ts — the ONLY `import(` site of `apps/cli/src` (DESIGN 1.2 net 3 rule g, DESIGN 1.3, DESIGN\n// 6.3 §\"read-only verbs never load core/runtime\"). `check-layers` refuses `import(` anywhere else under\n// `apps/cli/src/**` (rule g is per FILE, not per occurrence), so a verb module that needs `compose`/`host`/etc.\n// reaches them only through here, which keeps `cli.ts` (and every module reachable from it without going through\n// `lazy.ts`) inside François' 10 s / 4 MiB one-shot budget for a verb that never needs the heavy ports.\nimport type { CliContext, CommandModule } from './contract/index.ts';\n\n/** What `compose/index.ts` (U4.01) exports: assembles the real `CliContext` from process-level inputs. */\nexport interface ComposeInputs {\n readonly cwd: string;\n readonly env: NodeJS.ProcessEnv;\n readonly stdio: {\n readonly stdout: NodeJS.WritableStream;\n readonly stderr: NodeJS.WritableStream;\n readonly stdin: NodeJS.ReadableStream;\n };\n}\n\n/**\n * One LITERAL specifier per verb, closed over a thunk. A computed specifier (`` import(`./commands/${verb}/…`) ``)\n * is invisible to every bundler: rolldown would leave the template in `dist/cli.mjs` and emit no verb chunk, so the\n * SHIPPED cli could not execute a single verb (it would try to load a `.ts` file next to `dist/`). Written out, each\n * specifier is statically analysable: rolldown code-splits one chunk per verb, laziness is preserved, and\n * `apps/cli/test/registry/lazy.test.ts` pins this map against `VERBS` in both directions so a verb added to the\n * registry without a loader — or the reverse — fails Wave 0's own check.\n */\nexport const COMMAND_MODULES: Readonly<Record<string, () => Promise<unknown>>> = Object.freeze({\n init: () => import('./commands/init/index.ts'),\n doctor: () => import('./commands/doctor/index.ts'),\n discover: () => import('./commands/discover/index.ts'),\n run: () => import('./commands/run/index.ts'),\n status: () => import('./commands/status/index.ts'),\n inspect: () => import('./commands/inspect/index.ts'),\n resume: () => import('./commands/resume/index.ts'),\n pause: () => import('./commands/pause/index.ts'),\n cancel: () => import('./commands/cancel/index.ts'),\n shutdown: () => import('./commands/shutdown/index.ts'),\n approve: () => import('./commands/approve/index.ts'),\n deny: () => import('./commands/deny/index.ts'),\n retry: () => import('./commands/retry/index.ts'),\n skip: () => import('./commands/skip/index.ts'),\n logs: () => import('./commands/logs/index.ts'),\n tail: () => import('./commands/tail/index.ts'),\n diff: () => import('./commands/diff/index.ts'),\n review: () => import('./commands/review/index.ts'),\n fix: () => import('./commands/fix/index.ts'),\n ship: () => import('./commands/ship/index.ts'),\n auth: () => import('./commands/auth/index.ts'),\n providers: () => import('./commands/providers/index.ts'),\n models: () => import('./commands/models/index.ts'),\n config: () => import('./commands/config/index.ts'),\n migrate: () => import('./commands/migrate/index.ts'),\n reconcile: () => import('./commands/reconcile/index.ts'),\n spec: () => import('./commands/spec/index.ts'),\n policy: () => import('./commands/policy/index.ts'),\n gc: () => import('./commands/gc/index.ts'),\n update: () => import('./commands/update/index.ts'),\n brainstorm: () => import('./commands/brainstorm/index.ts'),\n 'run-tool': () => import('./commands/run-tool/index.ts'),\n send: () => import('./commands/send/index.ts'),\n __host: () => import('./commands/__host/index.ts'),\n});\n\n/** Loads `commands/<verb>/index.ts` and returns its default export. */\nexport async function loadCommandModule(verb: string): Promise<CommandModule> {\n const load = COMMAND_MODULES[verb];\n if (!load) throw new RangeError(`lazy.ts: no command module registered for verb \"${verb}\"`);\n const loaded = await load();\n const mod = loaded as { default?: CommandModule };\n if (!mod.default || typeof mod.default.run !== 'function') {\n throw new TypeError(`lazy.ts: commands/${verb}/index.ts has no default CommandModule export`);\n }\n return mod.default;\n}\n\n/**\n * The one `import(` of `./compose/index.ts` (DESIGN 1.2 net 3 rule g): `cli.ts` reaches the real `CliContext`\n * only through here, so a read-only verb whose action never runs never pays for it either (DESIGN 1.3).\n */\nexport async function loadCliContext(inputs: ComposeInputs): Promise<CliContext> {\n const loaded: unknown = await import('./compose/index.ts');\n const mod = loaded as { composeCliContext?: (inputs: ComposeInputs) => Promise<CliContext> };\n if (typeof mod.composeCliContext !== 'function') {\n throw new TypeError('lazy.ts: compose/index.ts has no composeCliContext export');\n }\n return mod.composeCliContext(inputs);\n}\n","#!/usr/bin/env node\n// apps/cli/src/cli.ts — DESIGN §9 CLI row: the whole verb registry, built from `contract/verbs.ts` and nothing\n// else (PLAN §3 rule 3 \"cli.ts pre-registers every verb against commands/<verb>/index.ts; ... nobody edits the\n// registry\"). `cli.ts` never imports `core`/`runtime-*`/`security`/`persistence` directly: a verb's module, and the\n// real `CliContext`, are reached only through `lazy.ts`'s single `import()` site (DESIGN 1.2 net 3 rule g, DESIGN\n// 1.3 \"read-only verbs never load core/runtime\") — so `--help` and `--version`, which commander answers itself\n// without ever calling `dispatch`, pay nothing beyond this file and `commander`.\nimport { toErrorInfo } from '@cohorte/base';\nimport { Command, CommanderError } from 'commander';\nimport {\n type CliContext,\n CONTROLLER_EXIT_CODES,\n type CommandArgs,\n EXIT_CODE_BY_CLASS,\n formatExitCodesHelp,\n VERBS,\n type VerbSpec,\n} from './contract/index.ts';\nimport { loadCliContext, loadCommandModule } from './lazy.ts';\n\n/** Mirrors `apps/cli/package.json` \"version\"; `apps/cli/test/registry/version.test.ts` reads that file and fails\n * if the two ever drift (a literal keeps `--version` free of any file read on the one-shot start-up path). */\nexport const COHORTE_VERSION = '3.0.0-dev.4';\n\nexport interface RuntimeDeps {\n readonly stdout: NodeJS.WritableStream;\n readonly stderr: NodeJS.WritableStream;\n /** Built lazily: only a verb whose action actually runs calls this (never `--help`/`--version`, DESIGN 1.3). */\n readonly context: () => Promise<CliContext>;\n}\n\nfunction printError(deps: RuntimeDeps, info: ReturnType<typeof toErrorInfo>): void {\n // spec 21: \"cause / impact / run / next action / exit code\"\n deps.stderr.write(`error: ${info.code}: ${info.message}\\n`);\n deps.stderr.write(`impact: ${info.impact}\\n`);\n deps.stderr.write(`next action: ${info.remediation}\\n`);\n}\n\n/** Loads the verb's module, builds its `CliContext` and runs it, converting any throw at this one boundary\n * (DESIGN 2.8: \"Every error has ... an exit code\"; the module itself never writes to stdio on error). */\nasync function dispatch(deps: RuntimeDeps, verb: string, args: CommandArgs): Promise<number> {\n try {\n const mod = await loadCommandModule(verb);\n const ctx = await deps.context();\n return await mod.run(ctx, args);\n } catch (error) {\n // DESIGN 2.8: \"Unknown throwables -> `<nearest class>/unexpected`\". A `CohorteError` keeps its own code through\n // `toErrorInfo`, so the Wave-0 stubs still answer `configuration/phase-not-available`; anything else — a\n // TypeError in a filled-in verb, a failed store open, a module that will not load — is reported as what it is.\n const info = toErrorInfo(error, { code: 'configuration/unexpected', class: 'configuration' });\n printError(deps, info);\n return EXIT_CODE_BY_CLASS[info.class];\n }\n}\n\ninterface ExitBox {\n code: number;\n}\n\nfunction addAction(command: Command, deps: RuntimeDeps, exit: ExitBox, verb: VerbSpec, subVerb?: string): void {\n command.allowUnknownOption(true).allowExcessArguments(true);\n // `--json` only where DESIGN 2.3.5 gives the verb a published document (`JSON_OUTPUTS`): registering it on every\n // verb would promise a machine output that no schema backs, and the Wave-4 `--json validates` tests read that map.\n if (verb.json) command.option('--json', 'print the machine-readable document for this verb (DESIGN 2.3.5)');\n command.argument('[args...]', 'verb arguments').action(async (positionals: string[], options: { json?: boolean }) => {\n const args: CommandArgs = {\n positionals,\n options,\n json: Boolean(options.json),\n ...(subVerb === undefined ? {} : { subVerb }),\n };\n exit.code = await dispatch(deps, verb.name, args);\n });\n}\n\n/** Builds the whole commander tree. Exported so a test can assert on `--help` / registration without spawning a\n * process (PLAN U0.10 test list: \"every DESIGN §9 verb is registered exactly once and appears in --help\"). */\nexport function buildProgram(deps: RuntimeDeps, exit: ExitBox): Command {\n const program = new Command('cohorte')\n .description('Cohorte: a durable multi-agent development pipeline with a runtime-independent orchestrator.')\n .version(COHORTE_VERSION, '--version', 'print the Cohorte version')\n .exitOverride()\n .configureOutput({\n writeOut: (str) => deps.stdout.write(str),\n writeErr: (str) => deps.stderr.write(str),\n })\n .addHelpText('after', `\\n${formatExitCodesHelp()}\\n`);\n\n for (const verb of VERBS) {\n const sub = program.command(verb.name, verb.hidden ? { hidden: true } : undefined).description(verb.summary);\n if (verb.subVerbs && verb.subVerbs.length > 0) {\n addAction(sub, deps, exit, verb); // bare `cohorte <verb>` with no sub-verb\n for (const subVerb of verb.subVerbs) {\n addAction(sub.command(subVerb), deps, exit, verb, subVerb);\n }\n } else {\n addAction(sub, deps, exit, verb);\n }\n }\n return program;\n}\n\n/**\n * `CONTROLLER_EXIT_CODES.usage` (2) for anything commander itself refuses (bad flag, unknown command, no command at\n * all), `completed` (0) for the routes where commander did exactly what was asked and then stopped the parse.\n *\n * Keyed off commander's OWN `exitCode`, not off the code NAME: commander raises `commander.help` both for the\n * advertised `help [command]` route (which prints the help and means exit 0) and for a bare `cohorte` with no\n * command (exit 1 there — a usage error), and an allow-list of names got the first case wrong while `--help`,\n * raising `commander.helpDisplayed`, was right. `error.exitCode` already separates the two.\n */\nfunction commanderExitCode(error: CommanderError): number {\n return error.exitCode === 0 ? CONTROLLER_EXIT_CODES.completed : CONTROLLER_EXIT_CODES.usage;\n}\n\n/** Parses `argv` (no leading node/script entries) and runs the matched verb. Never calls `process.exit`: the\n * caller (`main()` below, or a test) owns that decision. */\nexport async function runCli(argv: readonly string[], deps: RuntimeDeps): Promise<number> {\n const exit: ExitBox = { code: CONTROLLER_EXIT_CODES.completed };\n const program = buildProgram(deps, exit);\n try {\n await program.parseAsync(argv, { from: 'user' });\n } catch (error) {\n if (error instanceof CommanderError) return commanderExitCode(error);\n throw error;\n }\n return exit.code;\n}\n\nasync function realContext(): Promise<CliContext> {\n return loadCliContext({\n cwd: process.cwd(),\n env: process.env,\n stdio: { stdout: process.stdout, stderr: process.stderr, stdin: process.stdin },\n });\n}\n\nasync function main(): Promise<void> {\n const deps: RuntimeDeps = { stdout: process.stdout, stderr: process.stderr, context: realContext };\n const code = await runCli(process.argv.slice(2), deps);\n process.exitCode = code;\n}\n\n// `import.meta.main` (node >= 24.2; `apps/cli` engines are `^24.16.0 || >=26.1.0`) and NOT a hand-rolled\n// `import.meta.url === \\`file://${process.argv[1]}\\``: npm installs `bin` as a SYMLINK\n// (`node_modules/.bin/cohorte` -> `../cohorte/dist/cli.mjs`), so the comparison is false for every real install and\n// the published CLI would print nothing and exit 0. It also breaks on any install path with a space or a non-ASCII\n// character (`import.meta.url` is percent-encoded, `argv[1]` is not).\nif (import.meta.main) {\n main().catch((error: unknown) => {\n process.stderr.write(`fatal: ${error instanceof Error ? (error.stack ?? error.message) : String(error)}\\n`);\n process.exitCode = 1;\n });\n}\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cohorte",
3
- "version": "3.0.0-dev.3",
3
+ "version": "3.0.0-dev.4",
4
4
  "license": "AGPL-3.0-only",
5
5
  "type": "module",
6
6
  "bin": {