vouchington-tooling 0.29.0 → 0.31.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (174) hide show
  1. package/README.md +69 -19
  2. package/bin/node-guard.mjs +9 -0
  3. package/bin/vouchington-mcp.mjs +20 -0
  4. package/bin/vouchington.mjs +18 -0
  5. package/dist/agent-blackboard/feedback-outbox-counts.d.mts +5 -0
  6. package/dist/agent-blackboard/feedback-outbox-counts.mjs +11 -0
  7. package/dist/agent-blackboard/feedback-outbox-directory.d.mts +2 -0
  8. package/dist/agent-blackboard/feedback-outbox-directory.mjs +16 -0
  9. package/dist/agent-blackboard/feedback-outbox.mjs +5 -7
  10. package/dist/agent-blackboard/index.d.mts +1 -0
  11. package/dist/agent-blackboard/index.mjs +1 -0
  12. package/dist/api-contract-discovery/header-contract-registry.mjs +2 -0
  13. package/dist/api-contract-discovery/protocol-body-status.d.mts +2 -0
  14. package/dist/api-contract-discovery/protocol-body-status.mjs +4 -0
  15. package/dist/api-contract-discovery/protocol-callback-argument-bindings.d.mts +7 -0
  16. package/dist/api-contract-discovery/protocol-callback-argument-bindings.mjs +33 -0
  17. package/dist/api-contract-discovery/protocol-callback-invocation.d.mts +5 -0
  18. package/dist/api-contract-discovery/protocol-callback-invocation.mjs +103 -0
  19. package/dist/api-contract-discovery/protocol-callback-mutations.d.mts +5 -0
  20. package/dist/api-contract-discovery/protocol-callback-mutations.mjs +42 -0
  21. package/dist/api-contract-discovery/protocol-callback-registration.d.mts +3 -0
  22. package/dist/api-contract-discovery/protocol-callback-registration.mjs +27 -0
  23. package/dist/api-contract-discovery/protocol-callback-values.d.mts +17 -0
  24. package/dist/api-contract-discovery/protocol-callback-values.mjs +114 -0
  25. package/dist/api-contract-discovery/protocol-context-alias.d.mts +3 -0
  26. package/dist/api-contract-discovery/protocol-context-alias.mjs +20 -0
  27. package/dist/api-contract-discovery/protocol-contract-extraction.d.mts +9 -0
  28. package/dist/api-contract-discovery/protocol-contract-extraction.mjs +87 -0
  29. package/dist/api-contract-discovery/protocol-contract-keys.d.mts +1 -0
  30. package/dist/api-contract-discovery/protocol-contract-keys.mjs +8 -0
  31. package/dist/api-contract-discovery/protocol-contract-registry.d.mts +5 -0
  32. package/dist/api-contract-discovery/protocol-contract-registry.mjs +133 -0
  33. package/dist/api-contract-discovery/protocol-executable-path.d.mts +3 -0
  34. package/dist/api-contract-discovery/protocol-executable-path.mjs +64 -0
  35. package/dist/api-contract-discovery/protocol-execution-path.d.mts +3 -0
  36. package/dist/api-contract-discovery/protocol-execution-path.mjs +16 -0
  37. package/dist/api-contract-discovery/protocol-http-association.d.mts +7 -0
  38. package/dist/api-contract-discovery/protocol-http-association.mjs +97 -0
  39. package/dist/api-contract-discovery/protocol-http-body-kinds.d.mts +3 -0
  40. package/dist/api-contract-discovery/protocol-http-body-kinds.mjs +19 -0
  41. package/dist/api-contract-discovery/protocol-http-context-callers.d.mts +6 -0
  42. package/dist/api-contract-discovery/protocol-http-context-callers.mjs +34 -0
  43. package/dist/api-contract-discovery/protocol-http-context.d.mts +9 -0
  44. package/dist/api-contract-discovery/protocol-http-context.mjs +108 -0
  45. package/dist/api-contract-discovery/protocol-http-emission.d.mts +3 -0
  46. package/dist/api-contract-discovery/protocol-http-emission.mjs +76 -0
  47. package/dist/api-contract-discovery/protocol-http-implicit-taint.d.mts +7 -0
  48. package/dist/api-contract-discovery/protocol-http-implicit-taint.mjs +26 -0
  49. package/dist/api-contract-discovery/protocol-http-registration.d.mts +12 -0
  50. package/dist/api-contract-discovery/protocol-http-registration.mjs +53 -0
  51. package/dist/api-contract-discovery/protocol-marker-analysis.d.mts +7 -0
  52. package/dist/api-contract-discovery/protocol-marker-analysis.mjs +50 -0
  53. package/dist/api-contract-discovery/protocol-opaque-callback.d.mts +3 -0
  54. package/dist/api-contract-discovery/protocol-opaque-callback.mjs +107 -0
  55. package/dist/api-contract-discovery/protocol-platform-callbacks.d.mts +5 -0
  56. package/dist/api-contract-discovery/protocol-platform-callbacks.mjs +72 -0
  57. package/dist/api-contract-discovery/protocol-platform-mutation-targets.d.mts +5 -0
  58. package/dist/api-contract-discovery/protocol-platform-mutation-targets.mjs +89 -0
  59. package/dist/api-contract-discovery/protocol-platform-writes.d.mts +3 -0
  60. package/dist/api-contract-discovery/protocol-platform-writes.mjs +92 -0
  61. package/dist/api-contract-discovery/protocol-requested-keys.d.mts +5 -0
  62. package/dist/api-contract-discovery/protocol-requested-keys.mjs +45 -0
  63. package/dist/api-contract-discovery/protocol-sse-callers.d.mts +7 -0
  64. package/dist/api-contract-discovery/protocol-sse-callers.mjs +67 -0
  65. package/dist/api-contract-discovery/protocol-sse-emission.d.mts +11 -0
  66. package/dist/api-contract-discovery/protocol-sse-emission.mjs +61 -0
  67. package/dist/api-contract-discovery/protocol-sse-feasible-status.d.mts +3 -0
  68. package/dist/api-contract-discovery/protocol-sse-feasible-status.mjs +24 -0
  69. package/dist/api-contract-discovery/protocol-sse-raw-writes.d.mts +9 -0
  70. package/dist/api-contract-discovery/protocol-sse-raw-writes.mjs +70 -0
  71. package/dist/api-contract-discovery/protocol-sse-status.d.mts +3 -0
  72. package/dist/api-contract-discovery/protocol-sse-status.mjs +84 -0
  73. package/dist/api-contract-discovery/protocol-sse-write-access.d.mts +9 -0
  74. package/dist/api-contract-discovery/protocol-sse-write-access.mjs +42 -0
  75. package/dist/api-contract-discovery/protocol-sse-write-helpers.d.mts +6 -0
  76. package/dist/api-contract-discovery/protocol-sse-write-helpers.mjs +66 -0
  77. package/dist/api-contract-discovery/protocol-sse-write-mutations.d.mts +9 -0
  78. package/dist/api-contract-discovery/protocol-sse-write-mutations.mjs +36 -0
  79. package/dist/api-contract-discovery/protocol-sse-write-resolution.d.mts +14 -0
  80. package/dist/api-contract-discovery/protocol-sse-write-resolution.mjs +79 -0
  81. package/dist/api-contract-discovery/protocol-status-dominance.d.mts +5 -0
  82. package/dist/api-contract-discovery/protocol-status-dominance.mjs +35 -0
  83. package/dist/api-contract-discovery/protocol-write-receiver.d.mts +9 -0
  84. package/dist/api-contract-discovery/protocol-write-receiver.mjs +79 -0
  85. package/dist/api-contract-discovery/query-contract-registry.mjs +2 -0
  86. package/dist/api-contract-discovery/registered-route-binding-writes.d.mts +3 -0
  87. package/dist/api-contract-discovery/registered-route-binding-writes.mjs +30 -0
  88. package/dist/api-contract-discovery/registered-route-catalog.mjs +11 -7
  89. package/dist/api-contract-discovery/registered-route-factory-returns.d.mts +3 -0
  90. package/dist/api-contract-discovery/registered-route-factory-returns.mjs +29 -0
  91. package/dist/api-contract-discovery/registered-route-handler-analysis.d.mts +5 -1
  92. package/dist/api-contract-discovery/registered-route-handler-analysis.mjs +79 -35
  93. package/dist/api-contract-discovery/registered-route-runtime-parameters.d.mts +3 -0
  94. package/dist/api-contract-discovery/registered-route-runtime-parameters.mjs +5 -0
  95. package/dist/api-contract-discovery/registered-route-terminal-error.d.mts +3 -0
  96. package/dist/api-contract-discovery/registered-route-terminal-error.mjs +98 -0
  97. package/dist/api-contract-discovery/request-contract-registry.mjs +2 -0
  98. package/dist/api-contract-discovery/response-contract-implicit.mjs +28 -10
  99. package/dist/api-contract-discovery/response-contract-registry.mjs +14 -1
  100. package/dist/api-contract-discovery/response-contract-route-analysis.d.mts +1 -1
  101. package/dist/api-contract-discovery/response-contract-route-analysis.mjs +22 -4
  102. package/dist/api-contract-discovery/response-contract-status.d.mts +1 -1
  103. package/dist/api-contract-discovery/response-contract-status.mjs +10 -8
  104. package/dist/api-contract-discovery/response-contract-types.d.mts +7 -0
  105. package/dist/cli/commands/agent-blackboard-flags.d.mts +4 -0
  106. package/dist/cli/commands/agent-blackboard-flags.mjs +34 -0
  107. package/dist/cli/commands/agent-blackboard.mjs +27 -35
  108. package/dist/cli/index.d.mts +2 -0
  109. package/dist/cli/index.mjs +12 -15
  110. package/dist/cli/parse-forwarded-commands.d.mts +0 -3
  111. package/dist/cli/parse-forwarded-commands.mjs +0 -2
  112. package/dist/cli/parse.d.mts +0 -3
  113. package/dist/cli/usage.d.mts +1 -1
  114. package/dist/cli/usage.mjs +2 -6
  115. package/dist/contract-schema/contract-schema-bounded-array.d.mts +1 -3
  116. package/dist/contract-schema/contract-schema-bounded-array.mjs +5 -0
  117. package/dist/contract-schema/contract-schema-object-tuple.mjs +7 -5
  118. package/dist/contract-schema/contract-schema-type-extractor.mjs +13 -0
  119. package/dist/contract-schema/contract-schema-validator.mjs +2 -2
  120. package/dist/index.d.mts +0 -2
  121. package/dist/index.mjs +0 -1
  122. package/dist/mcp-server/dispatch.d.mts +4 -1
  123. package/dist/mcp-server/dispatch.mjs +6 -4
  124. package/dist/mcp-server/handlers-outbox.mjs +2 -7
  125. package/dist/mcp-server/harness.test-helpers.d.mts +1 -0
  126. package/dist/mcp-server/harness.test-helpers.mjs +1 -1
  127. package/dist/mcp-server/main.d.mts +5 -0
  128. package/dist/mcp-server/main.mjs +8 -0
  129. package/dist/mcp-server/run.d.mts +2 -0
  130. package/dist/mcp-server/run.mjs +4 -1
  131. package/dist/mcp-server/sdk.d.mts +11 -2
  132. package/dist/mcp-server/sdk.mjs +37 -5
  133. package/dist/mcp-server/server.mjs +3 -2
  134. package/dist/mcp-server/tools.mjs +3 -2
  135. package/dist/mcp-server/worktree.d.mts +17 -10
  136. package/dist/mcp-server/worktree.mjs +48 -31
  137. package/dist/openapi-document/build-openapi-catalog-sse.d.mts +4 -0
  138. package/dist/openapi-document/build-openapi-catalog-sse.mjs +27 -0
  139. package/dist/openapi-document/build-openapi-document.mjs +5 -2
  140. package/dist/openapi-document/build-openapi-response.d.mts +2 -2
  141. package/dist/openapi-document/build-openapi-response.mjs +33 -3
  142. package/dist/openapi-document/build-openapi-sse-events.d.mts +11 -0
  143. package/dist/openapi-document/build-openapi-sse-events.mjs +46 -0
  144. package/dist/openapi-document/contract-schema-intersection-merge.mjs +11 -11
  145. package/dist/openapi-document/contract-schema-to-openapi.mjs +3 -3
  146. package/dist/openapi-document/openapi-route-helpers.mjs +1 -3
  147. package/dist/openapi-document/openapi-types.d.mts +3 -0
  148. package/dist/openapi-document/operation-types.d.mts +7 -0
  149. package/docs/mcp-server.md +83 -25
  150. package/package.json +11 -9
  151. package/scripts/build.mjs +2 -0
  152. package/skills/blackboard/SKILL.md +66 -4
  153. package/skills/manifest.json +19 -19
  154. package/dist/agent-harness-config/apply.d.mts +0 -4
  155. package/dist/agent-harness-config/apply.mjs +0 -72
  156. package/dist/agent-harness-config/cli.d.mts +0 -10
  157. package/dist/agent-harness-config/cli.mjs +0 -100
  158. package/dist/agent-harness-config/index.d.mts +0 -4
  159. package/dist/agent-harness-config/index.mjs +0 -3
  160. package/dist/agent-harness-config/merge-json.d.mts +0 -5
  161. package/dist/agent-harness-config/merge-json.mjs +0 -62
  162. package/dist/agent-harness-config/merge-toml.d.mts +0 -6
  163. package/dist/agent-harness-config/merge-toml.mjs +0 -132
  164. package/dist/agent-harness-config/paths.d.mts +0 -3
  165. package/dist/agent-harness-config/paths.mjs +0 -117
  166. package/dist/agent-harness-config/policy.d.mts +0 -14
  167. package/dist/agent-harness-config/policy.mjs +0 -51
  168. package/dist/agent-harness-config/prerequisites.d.mts +0 -2
  169. package/dist/agent-harness-config/prerequisites.mjs +0 -67
  170. package/dist/agent-harness-config/toml-value.d.mts +0 -6
  171. package/dist/agent-harness-config/toml-value.mjs +0 -69
  172. package/dist/agent-harness-config/types.d.mts +0 -90
  173. package/dist/agent-harness-config/types.mjs +0 -2
  174. package/docs/agent-harness-config.md +0 -134
@@ -1,4 +1,4 @@
1
- export declare const USAGE = "Usage: vouchington <command> [options]\n\nCommands:\n runner-port-policy Print or validate a runner port policy\n with-host-lock Run a command under a host-wide lock\n agent-harness-config Apply classifier-auto + sandbox keys to agent harnesses\n gha-runtime-audit Audit successful GitHub Actions job runtimes\n require-up-to-date Require HEAD to include a fetched remote branch\n gitleaks-directory-scan Scan a directory with Gitleaks\n ast-grep-examples Run AST-grep rule examples\n ast-grep-pack Print shipped unconditional AST-grep pack paths as JSON\n gha-workspace-policy Check GitHub Actions workspace safety policy\n gha-output Write a collision-safe multiline GITHUB_OUTPUT record\n gha-needs-results Fail if required GitHub Actions job results failed\n download-with-diagnostics Download a URL and report HTTP status on failure\n download-optional-run-artifacts Download optional artifacts from the current run\n host-pressure-diagnostics Print a bounded host memory/OOM/PSI snapshot\n allocate-browser-safe-ports Allocate Fetch-safe localhost ports\n diagnose-port-collision Capture bounded localhost port diagnostics\n prepare-trivy-db Download the Trivy vulnerability database\n gha-artifacts-cleanup Delete classified GitHub Actions artifacts\n http-origin Validate an optional HTTP(S) origin\n vitest-blob-manifest Stamp a vitest-blob-manifest:v1 identity file\n vitest-report-attempt Write or read a Vitest report-attempt marker\n prepare-vitest-reports Validate and select Vitest report JSON files\n pnpm-install Install a pnpm workspace with retry and release-age fail-fast\n check-cache-size Measure a path and decide whether to save a GHA cache\n make-shard-matrix Emit a [1..N] GitHub Actions shard matrix\n load-runner-env Overlay a runner env file onto GITHUB_ENV with injection guards\n clean-workspace Reset a persistent-runner workspace with a fork-PR trust gate\n install-github-release Download a checksum-verified GitHub Release binary\n run-with-timeout Run a command with GNU timeout or a Perl fallback\n lint-links Two-pass lychee: internal links fail, external warn\n materialize-pr-context Dump PR title/body/files/diff/comments and #N crawl\n wait-for-apt-locks Wait until apt/dpkg lock files are free\n install-playwright-chromium-arm64 Install Playwright Chromium from browsers.json\n ghcr-package-retention Delete old GHCR package versions past KEEP_MIN\n harness-admission-lane Compute a GITHUB_RUN_ID admission lane for fleet fan-out\n harness-assert-gates Fail if any named HARNESS_*_ENABLED gate is enabled\n gha-collaborator-trust Classify authors as write-access collaborators or not\n nuget-central-version Validate a Directory.Packages.props PackageVersion delta\n swift-semantic-equal Compare Swift sources ignoring comments and whitespace\n post-review Post one COMMENT review from a staged payload file\n stage-review-payload Validate a review payload file into a staging directory\n retrospective-transcript Format facts from Claude-compatible, Codex, or Grok transcripts\n link-skill Link one packaged skill into an explicit consumer directory\n retrospective-facts Gather immutable facts for a retrospective\n agent-blackboard Probe and journal an Agent Blackboard deployment\n mcp Serve the agent-blackboard journal tools over stdio (MCP)\n\nOptions:\n -h, --help Show this help\n -v, --version Print the package version\n\nrunner-port-policy\n (no args) Print the shipped policy as JSON\n --file <path> Validate and print a policy file\n --reserved <port> Print true if the port is reserved\n\nwith-host-lock\n --name <family>\n [--slots <n>]\n --timeout-seconds <n>\n [--command-timeout-seconds <n>]\n [--failure-diagnostics <absolute-script>]\n [--on-acquire-timeout fail|run-unlocked]\n -- <command> [args...]\n\nagent-harness-config dump\nagent-harness-config check|apply [--global] [--repo PATH]... [--harness claude|codex|grok|cursor]...\n [--home PATH]\n\ngha-runtime-audit\n [--repository owner/name] Default GITHUB_REPOSITORY\n [--branch main]\n [--median-threshold-floor <seconds>] Off unless set. Must be below the ceiling\n [--median-threshold-ceiling <seconds>] Default 360. Flags a five-sample median above this\n --pr-workflow <name|/regex/> Repeatable\n --push-workflow <name|/regex/> Repeatable\n\nrequire-up-to-date --remote <name> --branch <name>\ngitleaks-directory-scan --config <path> [--directory <path>]\nast-grep-examples --rules <directory> --config <path>\nast-grep-pack\ngha-workspace-policy [--root <directory>] [--workflow-directory <directory>] [--action-directory <directory>]\n\ngha-output <name>\ngha-needs-results [label]\ndownload-with-diagnostics <url> <destination> [-- curl-args...]\ndownload-optional-run-artifacts (--name <name>... | --pattern <pattern>) --dir <directory>\nhost-pressure-diagnostics\nallocate-browser-safe-ports <count> [--policy path] [--forbidden-ports path]\ndiagnose-port-collision [--ports \"2200 2216\"] [--output-dir PATH]\nprepare-trivy-db\ngha-artifacts-cleanup run --run-id <id> [--keep-pattern glob] [--delete-pattern glob] [--patterns-file json]\ngha-artifacts-cleanup sweep --older-than-hours <n> [--keep-pattern glob] [--delete-pattern glob] [--patterns-file json]\nhttp-origin [--field NAME] [value]\nvitest-blob-manifest <suite> [reports-directory]\nvitest-report-attempt <write DIRECTORY SUITE|read ROOT>\nprepare-vitest-reports [primary-directory] [fallback-directory] [output-directory]\npnpm-install --runner-lifecycle persistent|ephemeral|ephemeral-full --install-scripts true|false\ncheck-cache-size <path> <max-bytes> <label>\nmake-shard-matrix <total>\nload-runner-env\nclean-workspace\ninstall-github-release --repo owner/name --version X --asset 'name-{platform}.tar.gz' --bin name [--tag-prefix PREFIX] [--expected-sha256 SHA256] [--no-checksum] [--checksums-asset NAME] [--version-flag FLAG] [--bin-dir DIR]\nrun-with-timeout <timeout-seconds> <kill-after-seconds> <command...>\nlint-links [--offline] [--config PATH] [--glob PATTERN] [files...]\nmaterialize-pr-context\nwait-for-apt-locks\ninstall-playwright-chromium-arm64 [name:archive...]\nghcr-package-retention <url-encoded-package>...\nharness-admission-lane <lanes>\nharness-assert-gates <gate>...\ngha-collaborator-trust <owner/repo> [--allow-bot <login>]... < authors.json\nnuget-central-version <trusted-props> <candidate-props> <metadata-json> <output-props>\nswift-semantic-equal <base> <head> <file.swift>\npost-review\nstage-review-payload optional|required <source> <destination>\nretrospective-transcript [--session-id ID] [--jsonl PATH] [--projects-dir PATH] [--codex-sessions-dir PATH] [--grok-sessions-dir PATH]\nlink-skill <name> --source-root <skills-dir> --target-root <consumer-skills-dir> Link a packaged or repository-local skill\nretrospective-facts (--pr NUMBER | --branch NAME | --no-pr) [--repo OWNER/NAME] [--raw]\nagent-blackboard probe\nagent-blackboard journal append --mode interactive|autonomous --source-event-id ID --work-outcome OUTCOME --coverage-status STATUS --session-id UUID --agent NAME --version VERSION --file PATH --repository OWNER/NAME [--repository OWNER/NAME ...] [--parent-session-id UUID] [--timestamp ISO8601] [--coverage-source SOURCE,...] [--dropped-count N] [--outbox-directory PATH]\nagent-blackboard journal entries --session-id UUID\nagent-blackboard journal flush --outbox-directory PATH\nInteractive journal append requires --outbox-directory PATH.\nagent-blackboard snapshot partition --snapshot PATH --checksum SHA256 --counts '{\"sessions\":N,\"entries\":N,\"records\":N,\"bytes\":N}'\nagent-blackboard snapshot cleanup [--snapshot PATH] [--partition-directory PATH --receipt JSON]\nmcp\n (no args) Serve journal_append, journal_entries, outbox_status, outbox_flush,\n session_ensure, snapshot_export, and session_archive over stdio.\n Needs the optional @modelcontextprotocol/sdk peer (and zod).\n Launch it from inside the repository's git worktree.\n";
1
+ export declare const USAGE = "Usage: vouchington <command> [options]\n\nCommands:\n runner-port-policy Print or validate a runner port policy\n with-host-lock Run a command under a host-wide lock\n gha-runtime-audit Audit successful GitHub Actions job runtimes\n require-up-to-date Require HEAD to include a fetched remote branch\n gitleaks-directory-scan Scan a directory with Gitleaks\n ast-grep-examples Run AST-grep rule examples\n ast-grep-pack Print shipped unconditional AST-grep pack paths as JSON\n gha-workspace-policy Check GitHub Actions workspace safety policy\n gha-output Write a collision-safe multiline GITHUB_OUTPUT record\n gha-needs-results Fail if required GitHub Actions job results failed\n download-with-diagnostics Download a URL and report HTTP status on failure\n download-optional-run-artifacts Download optional artifacts from the current run\n host-pressure-diagnostics Print a bounded host memory/OOM/PSI snapshot\n allocate-browser-safe-ports Allocate Fetch-safe localhost ports\n diagnose-port-collision Capture bounded localhost port diagnostics\n prepare-trivy-db Download the Trivy vulnerability database\n gha-artifacts-cleanup Delete classified GitHub Actions artifacts\n http-origin Validate an optional HTTP(S) origin\n vitest-blob-manifest Stamp a vitest-blob-manifest:v1 identity file\n vitest-report-attempt Write or read a Vitest report-attempt marker\n prepare-vitest-reports Validate and select Vitest report JSON files\n pnpm-install Install a pnpm workspace with retry and release-age fail-fast\n check-cache-size Measure a path and decide whether to save a GHA cache\n make-shard-matrix Emit a [1..N] GitHub Actions shard matrix\n load-runner-env Overlay a runner env file onto GITHUB_ENV with injection guards\n clean-workspace Reset a persistent-runner workspace with a fork-PR trust gate\n install-github-release Download a checksum-verified GitHub Release binary\n run-with-timeout Run a command with GNU timeout or a Perl fallback\n lint-links Two-pass lychee: internal links fail, external warn\n materialize-pr-context Dump PR title/body/files/diff/comments and #N crawl\n wait-for-apt-locks Wait until apt/dpkg lock files are free\n install-playwright-chromium-arm64 Install Playwright Chromium from browsers.json\n ghcr-package-retention Delete old GHCR package versions past KEEP_MIN\n harness-admission-lane Compute a GITHUB_RUN_ID admission lane for fleet fan-out\n harness-assert-gates Fail if any named HARNESS_*_ENABLED gate is enabled\n gha-collaborator-trust Classify authors as write-access collaborators or not\n nuget-central-version Validate a Directory.Packages.props PackageVersion delta\n swift-semantic-equal Compare Swift sources ignoring comments and whitespace\n post-review Post one COMMENT review from a staged payload file\n stage-review-payload Validate a review payload file into a staging directory\n retrospective-transcript Format facts from Claude-compatible, Codex, or Grok transcripts\n link-skill Link one packaged skill into an explicit consumer directory\n retrospective-facts Gather immutable facts for a retrospective\n agent-blackboard Probe and journal an Agent Blackboard deployment\n mcp Serve the agent-blackboard journal tools over stdio (MCP)\n\nOptions:\n -h, --help Show this help\n -v, --version Print the package version\n\nrunner-port-policy\n (no args) Print the shipped policy as JSON\n --file <path> Validate and print a policy file\n --reserved <port> Print true if the port is reserved\n\nwith-host-lock\n --name <family>\n [--slots <n>]\n --timeout-seconds <n>\n [--command-timeout-seconds <n>]\n [--failure-diagnostics <absolute-script>]\n [--on-acquire-timeout fail|run-unlocked]\n -- <command> [args...]\n\ngha-runtime-audit\n [--repository owner/name] Default GITHUB_REPOSITORY\n [--branch main]\n [--median-threshold-floor <seconds>] Off unless set. Must be below the ceiling\n [--median-threshold-ceiling <seconds>] Default 360. Flags a five-sample median above this\n --pr-workflow <name|/regex/> Repeatable\n --push-workflow <name|/regex/> Repeatable\n\nrequire-up-to-date --remote <name> --branch <name>\ngitleaks-directory-scan --config <path> [--directory <path>]\nast-grep-examples --rules <directory> --config <path>\nast-grep-pack\ngha-workspace-policy [--root <directory>] [--workflow-directory <directory>] [--action-directory <directory>]\n\ngha-output <name>\ngha-needs-results [label]\ndownload-with-diagnostics <url> <destination> [-- curl-args...]\ndownload-optional-run-artifacts (--name <name>... | --pattern <pattern>) --dir <directory>\nhost-pressure-diagnostics\nallocate-browser-safe-ports <count> [--policy path] [--forbidden-ports path]\ndiagnose-port-collision [--ports \"2200 2216\"] [--output-dir PATH]\nprepare-trivy-db\ngha-artifacts-cleanup run --run-id <id> [--keep-pattern glob] [--delete-pattern glob] [--patterns-file json]\ngha-artifacts-cleanup sweep --older-than-hours <n> [--keep-pattern glob] [--delete-pattern glob] [--patterns-file json]\nhttp-origin [--field NAME] [value]\nvitest-blob-manifest <suite> [reports-directory]\nvitest-report-attempt <write DIRECTORY SUITE|read ROOT>\nprepare-vitest-reports [primary-directory] [fallback-directory] [output-directory]\npnpm-install --runner-lifecycle persistent|ephemeral|ephemeral-full --install-scripts true|false\ncheck-cache-size <path> <max-bytes> <label>\nmake-shard-matrix <total>\nload-runner-env\nclean-workspace\ninstall-github-release --repo owner/name --version X --asset 'name-{platform}.tar.gz' --bin name [--tag-prefix PREFIX] [--expected-sha256 SHA256] [--no-checksum] [--checksums-asset NAME] [--version-flag FLAG] [--bin-dir DIR]\nrun-with-timeout <timeout-seconds> <kill-after-seconds> <command...>\nlint-links [--offline] [--config PATH] [--glob PATTERN] [files...]\nmaterialize-pr-context\nwait-for-apt-locks\ninstall-playwright-chromium-arm64 [name:archive...]\nghcr-package-retention <url-encoded-package>...\nharness-admission-lane <lanes>\nharness-assert-gates <gate>...\ngha-collaborator-trust <owner/repo> [--allow-bot <login>]... < authors.json\nnuget-central-version <trusted-props> <candidate-props> <metadata-json> <output-props>\nswift-semantic-equal <base> <head> <file.swift>\npost-review\nstage-review-payload optional|required <source> <destination>\nretrospective-transcript [--session-id ID] [--jsonl PATH] [--projects-dir PATH] [--codex-sessions-dir PATH] [--grok-sessions-dir PATH]\nlink-skill <name> --source-root <skills-dir> --target-root <consumer-skills-dir> Link a packaged or repository-local skill\nretrospective-facts (--pr NUMBER | --branch NAME | --no-pr) [--repo OWNER/NAME] [--raw]\nagent-blackboard probe\nagent-blackboard journal append --mode interactive|autonomous --source-event-id ID --work-outcome OUTCOME --coverage-status STATUS --session-id UUID --agent NAME --version VERSION --file PATH --repository OWNER/NAME [--repository OWNER/NAME ...] [--parent-session-id UUID] [--timestamp ISO8601] [--coverage-source SOURCE,...] [--dropped-count N] [--outbox-directory PATH]\nagent-blackboard journal entries --session-id UUID\nagent-blackboard journal status --session-id UUID --outbox-directory PATH\nagent-blackboard journal flush --outbox-directory PATH [--session-id UUID]\nInteractive journal append requires --outbox-directory PATH.\nagent-blackboard snapshot partition --snapshot PATH --checksum SHA256 --counts '{\"sessions\":N,\"entries\":N,\"records\":N,\"bytes\":N}'\nagent-blackboard snapshot cleanup [--snapshot PATH] [--partition-directory PATH --receipt JSON]\nmcp\n (no args) Serve journal_append, journal_entries, outbox_status, outbox_flush,\n session_ensure, snapshot_export, and session_archive over stdio.\n Needs the optional @modelcontextprotocol/sdk peer (and zod).\n Launch it from inside the repository's git worktree.\n";
2
2
  export declare function printUsage(stream?: NodeJS.WritableStream): void;
3
3
  export declare function commandNames(usage?: string): string[];
4
4
  export declare function commandUsage(command: string, usage?: string): string;
@@ -3,7 +3,6 @@ export const USAGE = `Usage: vouchington <command> [options]
3
3
  Commands:
4
4
  runner-port-policy Print or validate a runner port policy
5
5
  with-host-lock Run a command under a host-wide lock
6
- agent-harness-config Apply classifier-auto + sandbox keys to agent harnesses
7
6
  gha-runtime-audit Audit successful GitHub Actions job runtimes
8
7
  require-up-to-date Require HEAD to include a fetched remote branch
9
8
  gitleaks-directory-scan Scan a directory with Gitleaks
@@ -66,10 +65,6 @@ with-host-lock
66
65
  [--on-acquire-timeout fail|run-unlocked]
67
66
  -- <command> [args...]
68
67
 
69
- agent-harness-config dump
70
- agent-harness-config check|apply [--global] [--repo PATH]... [--harness claude|codex|grok|cursor]...
71
- [--home PATH]
72
-
73
68
  gha-runtime-audit
74
69
  [--repository owner/name] Default GITHUB_REPOSITORY
75
70
  [--branch main]
@@ -123,7 +118,8 @@ retrospective-facts (--pr NUMBER | --branch NAME | --no-pr) [--repo OWNER/NAME]
123
118
  agent-blackboard probe
124
119
  agent-blackboard journal append --mode interactive|autonomous --source-event-id ID --work-outcome OUTCOME --coverage-status STATUS --session-id UUID --agent NAME --version VERSION --file PATH --repository OWNER/NAME [--repository OWNER/NAME ...] [--parent-session-id UUID] [--timestamp ISO8601] [--coverage-source SOURCE,...] [--dropped-count N] [--outbox-directory PATH]
125
120
  agent-blackboard journal entries --session-id UUID
126
- agent-blackboard journal flush --outbox-directory PATH
121
+ agent-blackboard journal status --session-id UUID --outbox-directory PATH
122
+ agent-blackboard journal flush --outbox-directory PATH [--session-id UUID]
127
123
  Interactive journal append requires --outbox-directory PATH.
128
124
  agent-blackboard snapshot partition --snapshot PATH --checksum SHA256 --counts '{"sessions":N,"entries":N,"records":N,"bytes":N}'
129
125
  agent-blackboard snapshot cleanup [--snapshot PATH] [--partition-directory PATH --receipt JSON]
@@ -9,7 +9,5 @@ export type ExtractionContext = {
9
9
  options: ExtractContractSchemaOptions;
10
10
  };
11
11
  type SchemaForType = (type: ts.Type, context: ExtractionContext) => ContractSchemaNode;
12
- export declare function boundedArraySchema(type: ts.Type, context: ExtractionContext, schemaForType: SchemaForType): Extract<ContractSchemaNode, {
13
- type: 'array';
14
- }> | undefined;
12
+ export declare function boundedArraySchema(type: ts.Type, context: ExtractionContext, schemaForType: SchemaForType): ContractSchemaNode | undefined;
15
13
  export {};
@@ -16,6 +16,11 @@ export function boundedArraySchema(type, context, schemaForType) {
16
16
  if (!(uniqueItemsType.flags & ts.TypeFlags.BooleanLiteral)) {
17
17
  throw unsupportedType(type, context.checker, `${alias} uniqueness must be literal`);
18
18
  }
19
+ if (itemType.flags & ts.TypeFlags.Never) {
20
+ if (minItems !== 0)
21
+ throw unsupportedType(type, context.checker, `${alias} has impossible never element bounds`);
22
+ return { type: 'tuple', items: [], optionalItems: 0 };
23
+ }
19
24
  return {
20
25
  type: 'array',
21
26
  items: schemaForType(itemType, context),
@@ -1,7 +1,7 @@
1
1
  import ts from './typescript-api.mjs';
2
2
  import { compareSymbols } from './contract-schema-type-utils.mjs';
3
3
  export function objectSchema(type, context, schemaForType) {
4
- const properties = {};
4
+ const properties = new Map();
5
5
  for (const property of context.checker.getPropertiesOfType(type).toSorted(compareSymbols)) {
6
6
  const declaration = property.valueDeclaration;
7
7
  /* v8 ignore start */
@@ -12,10 +12,10 @@ export function objectSchema(type, context, schemaForType) {
12
12
  throw new Error(`Property "${property.name}" has no type`);
13
13
  /* v8 ignore stop */
14
14
  try {
15
- properties[property.name] = {
15
+ properties.set(property.name, {
16
16
  required: !(property.flags & ts.SymbolFlags.Optional),
17
17
  schema: schemaForType(propertyType, context),
18
- };
18
+ });
19
19
  }
20
20
  catch (error) {
21
21
  throw new Error(`Property "${property.name}": ${error.message}`, { cause: error });
@@ -24,8 +24,10 @@ export function objectSchema(type, context, schemaForType) {
24
24
  const stringIndex = context.checker.getIndexInfoOfType(type, ts.IndexKind.String);
25
25
  return {
26
26
  type: 'object',
27
- properties,
28
- additionalProperties: stringIndex ? schemaForType(stringIndex.type, context) : false,
27
+ properties: Object.fromEntries(properties),
28
+ additionalProperties: stringIndex && !(stringIndex.type.flags & ts.TypeFlags.Never)
29
+ ? schemaForType(stringIndex.type, context)
30
+ : false,
29
31
  };
30
32
  }
31
33
  /* v8 ignore start -- checker internal used only for compiler-synthesized properties */
@@ -80,6 +80,17 @@ function schemaForType(type, context) {
80
80
  return buildUnion();
81
81
  }
82
82
  if (type.isIntersection()) {
83
+ const index = checker.getIndexTypeOfType(type, ts.IndexKind.String);
84
+ const properties = checker.getPropertiesOfType(type);
85
+ if (index && index.flags & ts.TypeFlags.Never && properties.length > 0) {
86
+ if (properties.some((property) => !(property.flags & ts.SymbolFlags.Optional)))
87
+ throw unsupportedType(type, checker, 'never string index conflicts with named properties');
88
+ if (checker.getSignaturesOfType(type, ts.SignatureKind.Call).length > 0)
89
+ throw unsupportedType(type, checker, 'callable types are not supported');
90
+ if (checker.getSignaturesOfType(type, ts.SignatureKind.Construct).length > 0)
91
+ throw unsupportedType(type, checker, 'constructable types are not supported');
92
+ return { type: 'object', properties: {}, additionalProperties: false };
93
+ }
83
94
  return {
84
95
  type: 'intersection',
85
96
  variants: distinctNodes(type.types.map((variant) => schemaForType(variant, context))),
@@ -104,6 +115,8 @@ function schemaForType(type, context) {
104
115
  /* v8 ignore next */
105
116
  if (!typeArguments[0])
106
117
  throw unsupportedType(type, checker, 'array element type is missing');
118
+ if (typeArguments[0].flags & ts.TypeFlags.Never)
119
+ return { type: 'tuple', items: [], optionalItems: 0 };
107
120
  return { type: 'array', items: schemaForType(typeArguments[0], context) };
108
121
  }
109
122
  if (checker.getSignaturesOfType(type, ts.SignatureKind.Call).length > 0) {
@@ -66,7 +66,7 @@ function validateObject(node, value, path, context, checkUnexpected) {
66
66
  if (!isObject(value))
67
67
  return requireType(false, 'object', value, path, context);
68
68
  for (const [key, property] of Object.entries(node.properties)) {
69
- if (!(key in value)) {
69
+ if (!Object.hasOwn(value, key)) {
70
70
  if (property.required) {
71
71
  addIssue(context, propertyPath(path, key), 'missing-required', 'Required field is missing');
72
72
  }
@@ -77,7 +77,7 @@ function validateObject(node, value, path, context, checkUnexpected) {
77
77
  if (!checkUnexpected)
78
78
  return;
79
79
  for (const [key, nested] of Object.entries(value)) {
80
- if (key in node.properties)
80
+ if (Object.hasOwn(node.properties, key))
81
81
  continue;
82
82
  if (node.additionalProperties === false) {
83
83
  addUnexpectedIssue(context, propertyPath(path, key));
package/dist/index.d.mts CHANGED
@@ -15,8 +15,6 @@ export type { EphemeralListenerOptions, RunnerPortPolicy } from './runner-port-p
15
15
  export { extractAlterTableAddColumnLocations, extractCreateIndexMetadata, extractCreateTableMetadata, extractDefaultFunction, extractDropIndexMetadata, extractFuncCallArgColumnNames, extractMigrationConstraintMetadata, initSqlAst, lineOfUtf8ByteOffset, MissingSqlAstParserError, parseSql, } from './sql-ast/index.mts';
16
16
  export type { ForeignKey, SqlCreateIndexMetadata, SqlCreateTableColumn, SqlCreateTableMetadata, SqlDropIndexMetadata, SqlIndexParam, SqlMigrationConstraintMetadata, } from './sql-ast/index.mts';
17
17
  export { dollarQuoteEnd, lineOf, maskSqlQuotedText, readDollarQuoteDelimiter, readStringLiteral, splitSqlStatements, sqlFragments, stripSqlComments, } from './sql-scanner/index.mts';
18
- export { applyHarnessConfig, checkHarnessConfig, dumpHarnessPolicy, planHarnessConfig, DEFAULT_EXTRA_WRITABLE_ROOTS, } from './agent-harness-config/index.mts';
19
- export type { ApplyTarget, FilePlan, FileResult, HarnessApplyResult, HarnessCheckResult, HarnessConfigOptions, HarnessId, HarnessPlan, HarnessPolicyDump, JsonPatch, KeyDrift, TomlPatch, TomlValue, } from './agent-harness-config/index.mts';
20
18
  export { auditCiJobRuntime, parseWorkflowNameMatch } from './gha-runtime-audit/index.mts';
21
19
  export type { GhApiExecutor, RuntimeAuditOptions, RuntimeAuditResult, RuntimeAuditWorkflowFilter, RuntimeJobResult, RuntimeSample, } from './gha-runtime-audit/index.mts';
22
20
  export { checkGhaWorkspacePolicy } from './gha-workspace-policy/index.mts';
package/dist/index.mjs CHANGED
@@ -8,7 +8,6 @@ export { buildSessionFrictionReport, classifyFrictionObservation, FRICTION_LOG_M
8
8
  export { EphemeralListenerAttemptsExhaustedError, isRunnerReservedPort, listenOnRunnerUnreservedEphemeralPort, loadRunnerPortPolicy, runnerPortPolicy, validateRunnerPortPolicy, } from './runner-port-policy/index.mjs';
9
9
  export { extractAlterTableAddColumnLocations, extractCreateIndexMetadata, extractCreateTableMetadata, extractDefaultFunction, extractDropIndexMetadata, extractFuncCallArgColumnNames, extractMigrationConstraintMetadata, initSqlAst, lineOfUtf8ByteOffset, MissingSqlAstParserError, parseSql, } from './sql-ast/index.mjs';
10
10
  export { dollarQuoteEnd, lineOf, maskSqlQuotedText, readDollarQuoteDelimiter, readStringLiteral, splitSqlStatements, sqlFragments, stripSqlComments, } from './sql-scanner/index.mjs';
11
- export { applyHarnessConfig, checkHarnessConfig, dumpHarnessPolicy, planHarnessConfig, DEFAULT_EXTRA_WRITABLE_ROOTS, } from './agent-harness-config/index.mjs';
12
11
  export { auditCiJobRuntime, parseWorkflowNameMatch } from './gha-runtime-audit/index.mjs';
13
12
  export { checkGhaWorkspacePolicy } from './gha-workspace-policy/index.mjs';
14
13
  export { requireUpToDate } from './require-up-to-date/index.mjs';
@@ -1,9 +1,12 @@
1
1
  import type { BlackboardClientDependencies } from '../agent-blackboard/client.mts';
2
2
  import type { RunTextCommand } from '../gh-cli/exec.mts';
3
3
  export type ServerEnvironment = {
4
- launchRoot: string;
4
+ /** The launch directory's worktree top level, or undefined when it is not inside one. */
5
+ launchRoot: string | undefined;
5
6
  env: NodeJS.ProcessEnv;
6
7
  runGit: RunTextCommand;
8
+ /** Test seam: where `agent-blackboard` resolves from. Defaults to this package's own module. */
9
+ resolveFrom?: string | URL;
7
10
  /** Test seam: replaces how the consumer's `agent-blackboard` client is loaded. */
8
11
  blackboard?: BlackboardClientDependencies;
9
12
  };
@@ -1,4 +1,3 @@
1
- import { join } from 'node:path';
2
1
  import { FeedbackDeliveryError } from '../agent-blackboard/index.mjs';
3
2
  import { optionalString, rejectUnknown, requireObject } from './args.mjs';
4
3
  import { readSessionId } from './context.mjs';
@@ -23,7 +22,7 @@ const DIAGNOSTIC_HINTS = {
23
22
  'authentication-rejected': 'the blackboard rejected AGENT_BLACKBOARD_TOKEN',
24
23
  'blackboard-unavailable': 'the blackboard could not be reached or returned an error',
25
24
  'configuration-invalid': 'AGENT_BLACKBOARD_URL or AGENT_BLACKBOARD_TOKEN is missing or invalid',
26
- 'client-unavailable': 'agent-blackboard is not installed in the worktree',
25
+ 'client-unavailable': 'agent-blackboard is not installed next to vouchington-tooling',
27
26
  'readback-unconfirmed': 'the write could not be confirmed; retry the same call (same sourceEventId and content)',
28
27
  'delivery-timeout': 'delivery timed out; retry the same call (same sourceEventId and content)',
29
28
  };
@@ -52,8 +51,11 @@ export async function callTool(name, rawArguments, environment) {
52
51
  sessionId,
53
52
  worktree,
54
53
  env: environment.env,
55
- // The client resolves from the validated worktree, never from the server's own location.
56
- dependencies: { ...environment.blackboard, resolveFrom: join(worktree, 'package.json') },
54
+ // The client resolves from this package's install, like the SDK, never from the worktree.
55
+ dependencies: {
56
+ ...environment.blackboard,
57
+ resolveFrom: environment.resolveFrom ?? import.meta.url,
58
+ },
57
59
  });
58
60
  return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
59
61
  }
@@ -1,16 +1,11 @@
1
- import { flushFeedbackOutbox, readFeedbackOutbox } from '../agent-blackboard/index.mjs';
1
+ import { flushFeedbackOutbox, feedbackOutboxCounts } from '../agent-blackboard/index.mjs';
2
2
  import { outboxDirectory } from './context.mjs';
3
3
  /**
4
4
  * The outbox belongs to the worktree, not to one session, so two counts are reported. `pendingCount`
5
5
  * is the caller's session only; `worktreePendingCount` is every session's records.
6
6
  */
7
7
  export function outboxCounts(worktree, sessionId) {
8
- // A read that never creates the directory, so asking about an empty outbox leaves no trace.
9
- const records = readFeedbackOutbox(outboxDirectory(worktree));
10
- return {
11
- pendingCount: records.filter((record) => record.identity.sessionId === sessionId).length,
12
- worktreePendingCount: records.length,
13
- };
8
+ return feedbackOutboxCounts(outboxDirectory(worktree), sessionId);
14
9
  }
15
10
  function sessionStatus(pendingCount) {
16
11
  return pendingCount === 0 ? 'empty' : 'pending';
@@ -11,6 +11,7 @@ export type Harness = {
11
11
  /** Calls tools against a repository fixture with the blackboard client faked. */
12
12
  export declare function harness(fixture: RepoFixture, options?: FakeOptions & {
13
13
  env?: NodeJS.ProcessEnv;
14
+ launchRoot?: string | undefined;
14
15
  }): Harness;
15
16
  export declare function textOf(result: ToolCallResult): string;
16
17
  export declare function jsonOf(result: ToolCallResult): Record<string, unknown>;
@@ -20,7 +20,7 @@ export function outboxPath(worktree) {
20
20
  export function harness(fixture, options = {}) {
21
21
  const fake = fakeBlackboard(options);
22
22
  const environment = {
23
- launchRoot: fixture.main,
23
+ launchRoot: 'launchRoot' in options ? options.launchRoot : fixture.main,
24
24
  env: options.env ?? BLACKBOARD_ENV,
25
25
  runGit: runIsolatedGit,
26
26
  blackboard: fake.dependencies,
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Entry of `bin/vouchington-mcp.mjs`: starts the server without parsing CLI arguments and records
3
+ * the exit code. The open stdin keeps the process alive after a successful start.
4
+ */
5
+ export declare function runMcpMain(): Promise<void>;
@@ -0,0 +1,8 @@
1
+ import { runMcpCommand } from '../cli/commands/mcp.mjs';
2
+ /**
3
+ * Entry of `bin/vouchington-mcp.mjs`: starts the server without parsing CLI arguments and records
4
+ * the exit code. The open stdin keeps the process alive after a successful start.
5
+ */
6
+ export async function runMcpMain() {
7
+ process.exitCode = await runMcpCommand([]);
8
+ }
@@ -10,6 +10,8 @@ export type RunMcpServerOptions = {
10
10
  runGit?: RunTextCommand;
11
11
  /** Defaults to stdio. Tests pass one half of an in-memory pair. */
12
12
  transport?: Transport;
13
+ /** Test seam: where the startup check looks for `agent-blackboard`. Defaults to this module. */
14
+ resolveFrom?: string | URL;
13
15
  /** Test seam: replaces how the consumer's `agent-blackboard` client is loaded. */
14
16
  blackboard?: BlackboardClientDependencies;
15
17
  };
@@ -1,4 +1,4 @@
1
- import { loadMcpSdk } from './sdk.mjs';
1
+ import { assertBlackboardInstalled, loadMcpSdk } from './sdk.mjs';
2
2
  import { createMcpServer } from './server.mjs';
3
3
  import { launchWorktreeRoot, runIsolatedGit } from './worktree.mjs';
4
4
  /**
@@ -8,11 +8,14 @@ import { launchWorktreeRoot, runIsolatedGit } from './worktree.mjs';
8
8
  export async function runMcpServer(options) {
9
9
  const runGit = options.runGit ?? runIsolatedGit;
10
10
  const sdk = await loadMcpSdk(options.importSdk);
11
+ if (options.blackboard === undefined)
12
+ assertBlackboardInstalled(options.resolveFrom);
11
13
  const launchRoot = await launchWorktreeRoot(options.cwd, runGit);
12
14
  const environment = {
13
15
  launchRoot,
14
16
  env: options.env,
15
17
  runGit,
18
+ ...(options.resolveFrom === undefined ? {} : { resolveFrom: options.resolveFrom }),
16
19
  ...(options.blackboard === undefined ? {} : { blackboard: options.blackboard }),
17
20
  };
18
21
  const server = createMcpServer(sdk, environment, options.version);
@@ -9,9 +9,18 @@ export type McpSdk = {
9
9
  ListToolsRequestSchema: typeof ListToolsRequestSchema;
10
10
  };
11
11
  export type SdkImporter = (specifier: string) => Promise<unknown>;
12
- export declare const MISSING_SDK_MESSAGE: string;
12
+ /**
13
+ * The directory the server was installed into: the parent of the outermost `node_modules` above
14
+ * `from`, which is where `pnpm add` must run so the peers resolve from this package. Outside any
15
+ * `node_modules` (a source checkout) it is the module's own directory.
16
+ */
17
+ export declare function installDirectory(from?: string | URL): string;
18
+ export declare function missingSdkMessage(directory: string): string;
13
19
  /**
14
20
  * Imports the SDK lazily. A static import would make every `vouchington` command fail on a
15
21
  * consumer that never installed the optional peer.
16
22
  */
17
- export declare function loadMcpSdk(importSdk?: SdkImporter): Promise<McpSdk>;
23
+ export declare function loadMcpSdk(importSdk?: SdkImporter, directory?: string): Promise<McpSdk>;
24
+ export declare function missingBlackboardMessage(directory: string): string;
25
+ /** Fails fast at startup when `agent-blackboard` cannot be resolved from this package's install. */
26
+ export declare function assertBlackboardInstalled(resolveFrom?: string | URL): void;
@@ -6,17 +6,34 @@ var __rewriteRelativeImportExtension = (this && this.__rewriteRelativeImportExte
6
6
  }
7
7
  return path;
8
8
  };
9
+ import { createRequire } from 'node:module';
10
+ import { dirname, sep } from 'node:path';
11
+ import { fileURLToPath } from 'node:url';
9
12
  import { isMissingModuleError } from '../agent-blackboard/client.mjs';
10
13
  const SDK_PACKAGE = '@modelcontextprotocol/sdk';
11
- export const MISSING_SDK_MESSAGE = `vouchington mcp needs ${SDK_PACKAGE} (and its zod peer), which is an optional peer ` +
12
- `dependency of vouchington-tooling and is not installed. Install it next to ` +
13
- `vouchington-tooling: pnpm add -D ${SDK_PACKAGE} zod`;
14
+ /**
15
+ * The directory the server was installed into: the parent of the outermost `node_modules` above
16
+ * `from`, which is where `pnpm add` must run so the peers resolve from this package. Outside any
17
+ * `node_modules` (a source checkout) it is the module's own directory.
18
+ */
19
+ export function installDirectory(from = import.meta.url) {
20
+ const file = typeof from === 'string' && !from.startsWith('file:') ? from : fileURLToPath(from);
21
+ const marker = `${sep}node_modules${sep}`;
22
+ const index = file.indexOf(marker);
23
+ return index === -1 ? dirname(file) : file.slice(0, index);
24
+ }
25
+ export function missingSdkMessage(directory) {
26
+ return (`vouchington mcp needs ${SDK_PACKAGE} (and its zod peer), which is an optional peer ` +
27
+ `dependency of vouchington-tooling and is not installed. Install it in the directory ` +
28
+ `vouchington-tooling is installed in, not in a project: cd ${directory} && ` +
29
+ `pnpm add ${SDK_PACKAGE} zod`);
30
+ }
14
31
  const defaultImporter = (specifier) => import(__rewriteRelativeImportExtension(specifier));
15
32
  /**
16
33
  * Imports the SDK lazily. A static import would make every `vouchington` command fail on a
17
34
  * consumer that never installed the optional peer.
18
35
  */
19
- export async function loadMcpSdk(importSdk = defaultImporter) {
36
+ export async function loadMcpSdk(importSdk = defaultImporter, directory = installDirectory()) {
20
37
  try {
21
38
  const [server, stdio, types] = await Promise.all([
22
39
  importSdk(`${SDK_PACKAGE}/server/mcp.js`),
@@ -27,7 +44,22 @@ export async function loadMcpSdk(importSdk = defaultImporter) {
27
44
  }
28
45
  catch (error) {
29
46
  if (isMissingModuleError(error))
30
- throw new Error(MISSING_SDK_MESSAGE, { cause: error });
47
+ throw new Error(missingSdkMessage(directory), { cause: error });
31
48
  throw error;
32
49
  }
33
50
  }
51
+ export function missingBlackboardMessage(directory) {
52
+ return ('vouchington mcp needs agent-blackboard, which is an optional peer dependency of ' +
53
+ 'vouchington-tooling and is not installed. Install it in the directory vouchington-tooling ' +
54
+ `is installed in, not in a project: cd ${directory} && pnpm add agent-blackboard`);
55
+ }
56
+ /** Fails fast at startup when `agent-blackboard` cannot be resolved from this package's install. */
57
+ export function assertBlackboardInstalled(resolveFrom = import.meta.url) {
58
+ try {
59
+ createRequire(resolveFrom).resolve('agent-blackboard');
60
+ }
61
+ catch (error) {
62
+ // `resolve` only fails when the package cannot be found from `resolveFrom`.
63
+ throw new Error(missingBlackboardMessage(installDirectory(resolveFrom)), { cause: error });
64
+ }
65
+ }
@@ -2,8 +2,9 @@ import { callTool } from './dispatch.mjs';
2
2
  import { TOOLS } from './tools.mjs';
3
3
  export const SERVER_NAME = 'vouchington-tooling';
4
4
  const INSTRUCTIONS = 'Journals agent-blackboard feedback for a vouchington consumer. Every tool takes an explicit ' +
5
- 'sessionId and an optional worktree (an absolute path listed by `git worktree list` for the ' +
6
- 'repository this server was launched from). Hosts prefix the tool names differently: Claude ' +
5
+ 'sessionId and an optional worktree (the absolute path of a git worktree top level; it ' +
6
+ 'defaults to the launch directory worktree and is required when the server was launched ' +
7
+ 'outside one). Hosts prefix the tool names differently: Claude ' +
7
8
  'Code mcp__vouchington-tooling__<tool>, Codex mcp__vouchington_tooling__<tool>, Grok ' +
8
9
  'vouchington-tooling__<tool> (through search_tool and use_tool), Cursor the vouchington-tooling ' +
9
10
  'namespace (through GetDynamicTools and CallDynamicTool). Search for journal_append before ' +
@@ -7,8 +7,9 @@ const SESSION_ID = {
7
7
  };
8
8
  const WORKTREE = {
9
9
  type: 'string',
10
- description: 'Absolute path of a worktree listed by `git worktree list` for the repository the server ' +
11
- 'was launched from. Defaults to the launch worktree.',
10
+ description: 'Absolute path of the top level of a git worktree (`git rev-parse --show-toplevel`), not a ' +
11
+ 'subdirectory. Defaults to the launch directory worktree; required when the server was ' +
12
+ 'launched outside a git worktree.',
12
13
  };
13
14
  const PARENT_SESSION_ID = {
14
15
  type: ['string', 'null'],
@@ -1,18 +1,25 @@
1
1
  import { type RunTextCommand } from '../gh-cli/exec.mts';
2
- /** Runs git without inherited `GIT_*` variables so `GIT_DIR` cannot redirect the check. */
2
+ /**
3
+ * The environment for git: no inherited `GIT_*` variables, so `GIT_DIR` cannot redirect the check,
4
+ * and the C locale, so git's diagnostics are not translated and can be classified reliably.
5
+ */
6
+ export declare function isolatedGitEnv(): NodeJS.ProcessEnv;
3
7
  export declare const runIsolatedGit: RunTextCommand;
4
- /** The worktree root containing `cwd`, or a message that says how to fix the launch directory. */
5
- export declare function launchWorktreeRoot(cwd: string, runGit: RunTextCommand): Promise<string>;
6
- /** Paths of the non-bare entries in `git worktree list --porcelain` output. */
7
- export declare function parseWorktreeList(porcelain: string): string[];
8
8
  /**
9
- * Resolves the worktree a tool call may act on. The list is read from git on every call, so
10
- * worktrees created after the server started are accepted and removed ones are not. The
11
- * comparison is exact on real paths: a subdirectory of a worktree, or an unrelated repository,
12
- * is rejected.
9
+ * The worktree root containing `cwd`, or undefined when git reports `cwd` is not inside one (so
10
+ * the server may be launched from anywhere). Any other git failure, such as a missing git binary
11
+ * or an unsafe repository, is thrown with the cause rather than reported as "outside a worktree".
12
+ */
13
+ export declare function launchWorktreeRoot(cwd: string, runGit: RunTextCommand): Promise<string | undefined>;
14
+ /**
15
+ * Resolves the worktree a tool call may act on. It must be an absolute path that, after
16
+ * resolving symlinks, is exactly the top level of a git worktree (of any repository on the
17
+ * machine). A subdirectory, a relative or nonexistent path, and a non-git directory are rejected.
18
+ * Git is asked on every call, so worktrees created after the server started are accepted and
19
+ * removed ones are not. Without `requested`, the launch worktree is used when there is one.
13
20
  */
14
21
  export declare function resolveWorktree(input: {
15
- launchRoot: string;
22
+ launchRoot: string | undefined;
16
23
  requested?: string | undefined;
17
24
  runGit: RunTextCommand;
18
25
  }): Promise<string>;
@@ -5,8 +5,15 @@ import { promisify } from 'node:util';
5
5
  import { createCommandRunner } from '../gh-cli/exec.mjs';
6
6
  import { gitEnv } from '../shared-context/index.mjs';
7
7
  const execFileAsync = promisify(execFile);
8
- /** Runs git without inherited `GIT_*` variables so `GIT_DIR` cannot redirect the check. */
9
- export const runIsolatedGit = createCommandRunner('git', (command, args) => execFileAsync(command, args, { env: gitEnv() }));
8
+ /**
9
+ * The environment for git: no inherited `GIT_*` variables, so `GIT_DIR` cannot redirect the check,
10
+ * and the C locale, so git's diagnostics are not translated and can be classified reliably.
11
+ */
12
+ export function isolatedGitEnv() {
13
+ const { LANGUAGE: _language, ...env } = gitEnv();
14
+ return { ...env, LC_ALL: 'C' };
15
+ }
16
+ export const runIsolatedGit = createCommandRunner('git', (command, args) => execFileAsync(command, args, { env: isolatedGitEnv() }));
10
17
  async function canonical(path) {
11
18
  try {
12
19
  return await realpath(path);
@@ -15,44 +22,54 @@ async function canonical(path) {
15
22
  return undefined;
16
23
  }
17
24
  }
18
- /** The worktree root containing `cwd`, or a message that says how to fix the launch directory. */
25
+ /**
26
+ * git's two "no worktree here" outcomes: outside any repository, and inside a `.git` directory.
27
+ * The messages are English because `isolatedGitEnv` forces the C locale.
28
+ */
29
+ function isNotAGitRepository(error) {
30
+ if (typeof error !== 'object' || error === null)
31
+ return false;
32
+ const { stderr, message } = error;
33
+ return [stderr, message].some((text) => typeof text === 'string' && /not a git repository|must be run in a work tree/i.test(text));
34
+ }
35
+ /**
36
+ * The worktree root containing `cwd`, or undefined when git reports `cwd` is not inside one (so
37
+ * the server may be launched from anywhere). Any other git failure, such as a missing git binary
38
+ * or an unsafe repository, is thrown with the cause rather than reported as "outside a worktree".
39
+ */
19
40
  export async function launchWorktreeRoot(cwd, runGit) {
41
+ let output;
20
42
  try {
21
- return await realpath((await runGit(['-C', cwd, 'rev-parse', '--show-toplevel'])).trim());
43
+ output = await runGit(['-C', cwd, 'rev-parse', '--show-toplevel']);
22
44
  }
23
45
  catch (error) {
24
- throw new Error(`vouchington mcp must be launched inside a git worktree; ${cwd} is not one (${errorText(error)})`);
46
+ if (isNotAGitRepository(error))
47
+ return undefined;
48
+ const reason = error instanceof Error ? error.message : String(error);
49
+ throw new Error(`git could not determine the worktree of ${cwd}: ${reason}. Check that git is installed and ` +
50
+ 'on PATH, that the directory exists, and that the repository is readable by this user ' +
51
+ '(see safe.directory).', { cause: error });
25
52
  }
26
- }
27
- /** Paths of the non-bare entries in `git worktree list --porcelain` output. */
28
- export function parseWorktreeList(porcelain) {
29
- const paths = [];
30
- for (const block of porcelain.split(/\r?\n\r?\n/)) {
31
- const lines = block.split(/\r?\n/);
32
- const first = lines.find((line) => line.startsWith('worktree '));
33
- if (first !== undefined && !lines.includes('bare'))
34
- paths.push(first.slice('worktree '.length));
35
- }
36
- return paths;
53
+ return realpath(output.trim());
37
54
  }
38
55
  /**
39
- * Resolves the worktree a tool call may act on. The list is read from git on every call, so
40
- * worktrees created after the server started are accepted and removed ones are not. The
41
- * comparison is exact on real paths: a subdirectory of a worktree, or an unrelated repository,
42
- * is rejected.
56
+ * Resolves the worktree a tool call may act on. It must be an absolute path that, after
57
+ * resolving symlinks, is exactly the top level of a git worktree (of any repository on the
58
+ * machine). A subdirectory, a relative or nonexistent path, and a non-git directory are rejected.
59
+ * Git is asked on every call, so worktrees created after the server started are accepted and
60
+ * removed ones are not. Without `requested`, the launch worktree is used when there is one.
43
61
  */
44
62
  export async function resolveWorktree(input) {
45
63
  const { launchRoot, requested, runGit } = input;
46
- if (requested !== undefined && !isAbsolute(requested))
47
- throw new Error('worktree must be an absolute path to a git worktree of the launch repository');
48
- const target = requested === undefined ? launchRoot : await canonical(requested);
49
- const listing = await runGit(['-C', launchRoot, 'worktree', 'list', '--porcelain']);
50
- const known = (await Promise.all(parseWorktreeList(listing).map(canonical))).filter((path) => path !== undefined);
51
- if (target !== undefined && known.includes(target))
64
+ if (requested === undefined && launchRoot === undefined)
65
+ throw new Error('worktree is required: this server was launched outside a git worktree, so pass ' +
66
+ 'worktree as the absolute path of the git worktree to act on');
67
+ const path = requested ?? launchRoot;
68
+ if (!isAbsolute(path))
69
+ throw new Error('worktree must be an absolute path to a git worktree');
70
+ const target = await canonical(path);
71
+ if (target !== undefined && (await launchWorktreeRoot(target, runGit)) === target)
52
72
  return target;
53
- throw new Error(`worktree ${requested ?? launchRoot} is not a worktree of the repository this server was ` +
54
- `launched from (${launchRoot}). Registered worktrees: ${known.join(', ')}`);
55
- }
56
- function errorText(error) {
57
- return error instanceof Error ? error.message : String(error);
73
+ throw new Error(`worktree ${path} is not the top level of a git worktree: it must exist and be the ` +
74
+ 'directory `git rev-parse --show-toplevel` reports, not a subdirectory of it');
58
75
  }
@@ -0,0 +1,4 @@
1
+ import type { buildOperationResponse } from './build-openapi-response.mts';
2
+ import type { RegisteredRoute, ResponseContract } from './operation-types.mts';
3
+ /** Keeps a cataloged stream unavailable until its extracted variants describe SSE. */
4
+ export declare function retainCatalogSseResponse(kind: RegisteredRoute['kind'] | undefined, variants: readonly ResponseContract[], rendered: ReturnType<typeof buildOperationResponse>): ReturnType<typeof buildOperationResponse>;