vouchington-tooling 0.44.0 → 0.46.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/README.md +10 -52
- package/dist/agent-blackboard/client.d.mts +6 -0
- package/dist/agent-blackboard/feedback-audit-source.d.mts +4 -10
- package/dist/agent-blackboard/feedback-audit-source.mjs +7 -18
- package/dist/agent-blackboard/feedback-compose-audit.mjs +6 -14
- package/dist/agent-blackboard/feedback-compose.d.mts +1 -4
- package/dist/agent-blackboard/feedback-compose.mjs +2 -5
- package/dist/agent-blackboard/feedback-fields.mjs +1 -1
- package/dist/agent-blackboard/feedback-identity.mjs +1 -1
- package/dist/agent-blackboard/feedback-journal-audit.d.mts +6 -8
- package/dist/agent-blackboard/feedback-journal-audit.mjs +9 -10
- package/dist/agent-blackboard/feedback-journal-sandbox.d.mts +2 -2
- package/dist/agent-blackboard/feedback-journal-sandbox.mjs +2 -2
- package/dist/agent-blackboard/feedback-outbox-rejected.mjs +1 -1
- package/dist/agent-blackboard/feedback-outbox.mjs +2 -2
- package/dist/agent-blackboard/index.d.mts +1 -1
- package/dist/audit-journal/ci-failures.d.mts +6 -0
- package/dist/{session-friction → audit-journal}/ci-failures.mjs +3 -9
- package/dist/{session-friction → audit-journal}/journal.d.mts +0 -1
- package/dist/{session-friction → audit-journal}/journal.mjs +1 -1
- package/dist/audit-journal/session-id.d.mts +1 -0
- package/dist/{session-friction → audit-journal}/session-id.mjs +0 -5
- package/dist/{session-friction → audit-journal}/text.d.mts +1 -4
- package/dist/{session-friction → audit-journal}/text.mjs +4 -22
- package/dist/audit-journal/types.d.mts +17 -0
- package/dist/cli/commands/gha-runtime-audit.mjs +2 -0
- package/dist/cli/usage.d.mts +1 -1
- package/dist/cli/usage.mjs +1 -1
- package/dist/gh-cli/env.d.mts +9 -0
- package/dist/gh-cli/env.mjs +12 -0
- package/dist/gh-cli/exec.d.mts +7 -4
- package/dist/gh-cli/exec.mjs +7 -5
- package/dist/gh-cli/index.d.mts +1 -0
- package/dist/gh-cli/index.mjs +1 -0
- package/dist/gha-post-review/github.mjs +2 -1
- package/dist/index.d.mts +3 -3
- package/dist/index.mjs +2 -2
- package/dist/mcp-server/context.d.mts +5 -0
- package/dist/mcp-server/dispatch.d.mts +5 -0
- package/dist/mcp-server/dispatch.mjs +8 -1
- package/dist/mcp-server/fake-blackboard.test-helpers.d.mts +2 -0
- package/dist/mcp-server/fake-blackboard.test-helpers.mjs +3 -0
- package/dist/mcp-server/handlers-pr.d.mts +1 -1
- package/dist/mcp-server/handlers-pr.mjs +6 -97
- package/dist/mcp-server/handlers-retrospective-save.d.mts +10 -0
- package/dist/mcp-server/handlers-retrospective-save.mjs +138 -0
- package/dist/mcp-server/handlers-retrospective.d.mts +1 -12
- package/dist/mcp-server/handlers-retrospective.mjs +1 -154
- package/dist/mcp-server/handlers-session.mjs +25 -6
- package/dist/mcp-server/handlers-snapshot-cleanup.d.mts +7 -0
- package/dist/mcp-server/handlers-snapshot-cleanup.mjs +36 -0
- package/dist/mcp-server/harness.test-helpers.d.mts +3 -0
- package/dist/mcp-server/harness.test-helpers.mjs +2 -0
- package/dist/mcp-server/policy-run.d.mts +1 -0
- package/dist/mcp-server/policy-run.mjs +1 -0
- package/dist/mcp-server/pr-agent.d.mts +12 -0
- package/dist/mcp-server/pr-agent.mjs +34 -0
- package/dist/mcp-server/pr-body.d.mts +6 -2
- package/dist/mcp-server/pr-body.mjs +16 -5
- package/dist/mcp-server/pr-defaults.d.mts +3 -0
- package/dist/mcp-server/pr-defaults.mjs +7 -2
- package/dist/mcp-server/pr-gh.mjs +2 -1
- package/dist/mcp-server/pr-input.d.mts +14 -6
- package/dist/mcp-server/pr-input.mjs +22 -5
- package/dist/mcp-server/pr-live.d.mts +4 -1
- package/dist/mcp-server/pr-live.mjs +8 -3
- package/dist/mcp-server/pr-mode.d.mts +7 -0
- package/dist/mcp-server/pr-mode.mjs +34 -0
- package/dist/mcp-server/pr-no-source.d.mts +9 -0
- package/dist/mcp-server/pr-no-source.mjs +22 -0
- package/dist/mcp-server/pr-prepare.d.mts +25 -0
- package/dist/mcp-server/pr-prepare.mjs +124 -0
- package/dist/mcp-server/preview-registry.d.mts +25 -0
- package/dist/mcp-server/preview-registry.mjs +38 -0
- package/dist/mcp-server/retrospective-check.d.mts +3 -0
- package/dist/mcp-server/retrospective-check.mjs +38 -0
- package/dist/mcp-server/retrospective-input.mjs +2 -2
- package/dist/mcp-server/retrospective-preview.d.mts +17 -0
- package/dist/mcp-server/retrospective-preview.mjs +60 -0
- package/dist/mcp-server/server.mjs +1 -0
- package/dist/mcp-server/snapshot-registry.d.mts +25 -0
- package/dist/mcp-server/snapshot-registry.mjs +32 -0
- package/dist/mcp-server/tools-pr.mjs +28 -16
- package/dist/mcp-server/tools-retrospective.mjs +25 -5
- package/dist/mcp-server/tools.mjs +6 -0
- package/dist/{session-friction → private-files}/lock.mjs +3 -2
- package/dist/private-files/log-file.d.mts +3 -0
- package/dist/private-files/log-file.mjs +53 -0
- package/dist/{session-friction → private-files}/utf8.mjs +1 -1
- package/dist/retrospective-facts/exec.mjs +2 -1
- package/docs/mcp-server.md +112 -26
- package/package.json +3 -8
- package/skills/retrospective/SKILL.md +3 -1
- package/skills/retrospective-distill/SKILL.md +5 -0
- package/dist/session-friction/ci-failures.d.mts +0 -13
- package/dist/session-friction/classify.d.mts +0 -2
- package/dist/session-friction/classify.mjs +0 -98
- package/dist/session-friction/command-segments.d.mts +0 -3
- package/dist/session-friction/command-segments.mjs +0 -94
- package/dist/session-friction/coverage-ledger.d.mts +0 -10
- package/dist/session-friction/coverage-ledger.mjs +0 -75
- package/dist/session-friction/directory.d.mts +0 -1
- package/dist/session-friction/directory.mjs +0 -71
- package/dist/session-friction/index.d.mts +0 -6
- package/dist/session-friction/index.mjs +0 -5
- package/dist/session-friction/log-file.d.mts +0 -7
- package/dist/session-friction/log-file.mjs +0 -95
- package/dist/session-friction/log.d.mts +0 -8
- package/dist/session-friction/log.mjs +0 -163
- package/dist/session-friction/normalize.d.mts +0 -1
- package/dist/session-friction/normalize.mjs +0 -81
- package/dist/session-friction/report.d.mts +0 -16
- package/dist/session-friction/report.mjs +0 -91
- package/dist/session-friction/sandbox.d.mts +0 -3
- package/dist/session-friction/sandbox.mjs +0 -43
- package/dist/session-friction/session-id.d.mts +0 -2
- package/dist/session-friction/types.d.mts +0 -80
- /package/dist/{session-friction → audit-journal}/audit-block.d.mts +0 -0
- /package/dist/{session-friction → audit-journal}/audit-block.mjs +0 -0
- /package/dist/{session-friction → audit-journal}/audit-fit.d.mts +0 -0
- /package/dist/{session-friction → audit-journal}/audit-fit.mjs +0 -0
- /package/dist/{session-friction → audit-journal}/types.mjs +0 -0
- /package/dist/{session-friction → private-files}/lock.d.mts +0 -0
- /package/dist/{session-friction → private-files}/utf8.d.mts +0 -0
package/dist/cli/usage.d.mts
CHANGED
|
@@ -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 gha-runtime-audit Audit successful GitHub Actions job runtimes\n require-up-to-date Require HEAD to include a fetched remote branch\n fresh-base Classify the worktree against a remote base 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>\nfresh-base [--remote <name>] [--branch <name>] [--skip-fetch]\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 journal is deprecated and being phased out: agents journal through the mcp server.\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, retrospective_check, retrospective_save,\n pr_validate, pr_create, pr_update,\n 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 fresh-base Classify the worktree against a remote base 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>\nfresh-base [--remote <name>] [--branch <name>] [--skip-fetch]\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 journal is deprecated and being phased out: agents journal through the mcp server.\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, retrospective_check, retrospective_save,\n pr_validate, pr_create, pr_update,\n snapshot_export, snapshot_cleanup, 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;
|
package/dist/cli/usage.mjs
CHANGED
|
@@ -130,7 +130,7 @@ mcp
|
|
|
130
130
|
(no args) Serve journal_append, journal_entries, outbox_status, outbox_flush,
|
|
131
131
|
session_ensure, retrospective_check, retrospective_save,
|
|
132
132
|
pr_validate, pr_create, pr_update,
|
|
133
|
-
snapshot_export, and session_archive over stdio.
|
|
133
|
+
snapshot_export, snapshot_cleanup, and session_archive over stdio.
|
|
134
134
|
Needs the optional @modelcontextprotocol/sdk peer (and zod).
|
|
135
135
|
Launch it from inside the repository's git worktree.
|
|
136
136
|
`;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The environment to spawn `gh` with: `env` minus `CLICOLOR_FORCE`. A non-zero `CLICOLOR_FORCE`
|
|
3
|
+
* makes `gh ... --json` emit ANSI-colored JSON even on a pipe, and `NO_COLOR` does not override
|
|
4
|
+
* it, so callers that `JSON.parse` gh output would throw. Deleting the key (rather than setting
|
|
5
|
+
* it to `0`) restores gh's default and never adds a variable the caller did not have. Every other
|
|
6
|
+
* variable passes through untouched; the input is not mutated. Windows environment variable names
|
|
7
|
+
* are case-insensitive, so the key is matched case-insensitively there.
|
|
8
|
+
*/
|
|
9
|
+
export declare function ghSpawnEnv(env?: NodeJS.ProcessEnv, platform?: NodeJS.Platform): NodeJS.ProcessEnv;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The environment to spawn `gh` with: `env` minus `CLICOLOR_FORCE`. A non-zero `CLICOLOR_FORCE`
|
|
3
|
+
* makes `gh ... --json` emit ANSI-colored JSON even on a pipe, and `NO_COLOR` does not override
|
|
4
|
+
* it, so callers that `JSON.parse` gh output would throw. Deleting the key (rather than setting
|
|
5
|
+
* it to `0`) restores gh's default and never adds a variable the caller did not have. Every other
|
|
6
|
+
* variable passes through untouched; the input is not mutated. Windows environment variable names
|
|
7
|
+
* are case-insensitive, so the key is matched case-insensitively there.
|
|
8
|
+
*/
|
|
9
|
+
export function ghSpawnEnv(env = process.env, platform = process.platform) {
|
|
10
|
+
const isForcedColor = (key) => platform === 'win32' ? key.toUpperCase() === 'CLICOLOR_FORCE' : key === 'CLICOLOR_FORCE';
|
|
11
|
+
return Object.fromEntries(Object.entries(env).filter(([key]) => !isForcedColor(key)));
|
|
12
|
+
}
|
package/dist/gh-cli/exec.d.mts
CHANGED
|
@@ -1,16 +1,19 @@
|
|
|
1
1
|
/** A text-mode command runner: argv in, stdout out. Rejects on a non-zero exit. */
|
|
2
2
|
export type RunTextCommand = (args: string[]) => Promise<string>;
|
|
3
3
|
/** The `child_process.execFile` shape `createCommandRunner` wraps — injectable for tests. */
|
|
4
|
-
export type ExecFileText = (command: string, args: string[]
|
|
4
|
+
export type ExecFileText = (command: string, args: string[], options?: {
|
|
5
|
+
env: NodeJS.ProcessEnv;
|
|
6
|
+
}) => Promise<{
|
|
5
7
|
stdout: string;
|
|
6
8
|
}>;
|
|
7
9
|
/**
|
|
8
10
|
* Builds a `RunTextCommand` bound to a fixed binary. The default `exec` wraps `execFile` via
|
|
9
11
|
* `promisify` so a test can inject a fake without spawning a real process; `runGh`/`runGit` below
|
|
10
|
-
* are this factory applied to the two binaries this module cares about.
|
|
12
|
+
* are this factory applied to the two binaries this module cares about. `buildEnv` maps the
|
|
13
|
+
* parent environment to the one the child is spawned with (identity by default).
|
|
11
14
|
*/
|
|
12
|
-
export declare function createCommandRunner(command: string, exec?: ExecFileText): RunTextCommand;
|
|
13
|
-
/** Runs `gh` and returns its stdout. */
|
|
15
|
+
export declare function createCommandRunner(command: string, exec?: ExecFileText, buildEnv?: (env: NodeJS.ProcessEnv) => NodeJS.ProcessEnv): RunTextCommand;
|
|
16
|
+
/** Runs `gh` and returns its stdout, with `CLICOLOR_FORCE` removed so `--json` output stays plain. */
|
|
14
17
|
export declare const runGh: RunTextCommand;
|
|
15
18
|
/** Runs `git` and returns its stdout. */
|
|
16
19
|
export declare const runGit: RunTextCommand;
|
package/dist/gh-cli/exec.mjs
CHANGED
|
@@ -1,18 +1,20 @@
|
|
|
1
1
|
import { execFile } from 'node:child_process';
|
|
2
2
|
import { promisify } from 'node:util';
|
|
3
|
+
import { ghSpawnEnv } from "./env.mjs";
|
|
3
4
|
const execFileAsync = promisify(execFile);
|
|
4
5
|
/**
|
|
5
6
|
* Builds a `RunTextCommand` bound to a fixed binary. The default `exec` wraps `execFile` via
|
|
6
7
|
* `promisify` so a test can inject a fake without spawning a real process; `runGh`/`runGit` below
|
|
7
|
-
* are this factory applied to the two binaries this module cares about.
|
|
8
|
+
* are this factory applied to the two binaries this module cares about. `buildEnv` maps the
|
|
9
|
+
* parent environment to the one the child is spawned with (identity by default).
|
|
8
10
|
*/
|
|
9
|
-
export function createCommandRunner(command, exec = (cmd, args) => execFileAsync(cmd, args)) {
|
|
11
|
+
export function createCommandRunner(command, exec = (cmd, args, options) => execFileAsync(cmd, args, { ...options, encoding: 'utf8' }), buildEnv = (env) => env) {
|
|
10
12
|
return async (args) => {
|
|
11
|
-
const { stdout } = await exec(command, args);
|
|
13
|
+
const { stdout } = await exec(command, args, { env: buildEnv(process.env) });
|
|
12
14
|
return stdout;
|
|
13
15
|
};
|
|
14
16
|
}
|
|
15
|
-
/** Runs `gh` and returns its stdout. */
|
|
16
|
-
export const runGh = createCommandRunner('gh');
|
|
17
|
+
/** Runs `gh` and returns its stdout, with `CLICOLOR_FORCE` removed so `--json` output stays plain. */
|
|
18
|
+
export const runGh = createCommandRunner('gh', undefined, ghSpawnEnv);
|
|
17
19
|
/** Runs `git` and returns its stdout. */
|
|
18
20
|
export const runGit = createCommandRunner('git');
|
package/dist/gh-cli/index.d.mts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
export { createCommandRunner, runGh, runGit } from './exec.mts';
|
|
2
|
+
export { ghSpawnEnv } from './env.mts';
|
|
2
3
|
export type { ExecFileText, RunTextCommand } from './exec.mts';
|
|
3
4
|
export { DiffCommandError, processDiffCommand } from './diff.mts';
|
|
4
5
|
export type { DiffBlockCallback, DiffCommand } from './diff.mts';
|
package/dist/gh-cli/index.mjs
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
export { createCommandRunner, runGh, runGit } from "./exec.mjs";
|
|
2
|
+
export { ghSpawnEnv } from "./env.mjs";
|
|
2
3
|
export { DiffCommandError, processDiffCommand } from "./diff.mjs";
|
|
3
4
|
export { GITHUB_BODY_MAX_CHARACTERS, validateGitHubBodyLength } from "./body-length.mjs";
|
|
4
5
|
export { assertHeadPushed, buildGhPrCreateArgs, createPullRequest, DetachedHeadError, HeadNotPushedError, HeadOutOfDateError, resolveHeadBranch, } from "./pr-create.mjs";
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { execFileSync } from 'node:child_process';
|
|
2
2
|
import { appendFileSync } from 'node:fs';
|
|
3
|
+
import { ghSpawnEnv } from "../gh-cli/env.mjs";
|
|
3
4
|
import { ReviewPayloadError } from "../gha-review-payload/index.mjs";
|
|
4
5
|
import { readRegularReviewPayload } from "../gha-review-payload/index.mjs";
|
|
5
6
|
import { createActionsClaudeTokenIo, withClaudeAppToken } from "./claude-token.mjs";
|
|
@@ -11,7 +12,7 @@ export function createGhExec(exec = execFileSync) {
|
|
|
11
12
|
return (args, options) => exec('gh', [...args], {
|
|
12
13
|
encoding: 'utf8',
|
|
13
14
|
input: options?.input,
|
|
14
|
-
env: options?.env ?? process.env,
|
|
15
|
+
env: ghSpawnEnv(options?.env ?? process.env),
|
|
15
16
|
}).trim();
|
|
16
17
|
}
|
|
17
18
|
export function writePostedOutput(posted, commentCount, outputPath = process.env.GITHUB_OUTPUT) {
|
package/dist/index.d.mts
CHANGED
|
@@ -8,8 +8,8 @@ export { runRetrospectiveFacts } from './retrospective-facts/index.mts';
|
|
|
8
8
|
export type { CommandExecutor, CommandResult, RetrospectiveFactsOptions, } from './retrospective-facts/index.mts';
|
|
9
9
|
export { appendJournal, assertSessionId, cleanupSnapshotPartitions, partitionSnapshot, probeBlackboard, readJournal, resolveBlackboardConnection, } from './agent-blackboard/index.mts';
|
|
10
10
|
export type { BlackboardConnection, SnapshotChecksum, SnapshotCleanupReceipt, SnapshotCleanupOptions, SnapshotCounts, SnapshotManifest, SnapshotPartition, SnapshotPartitionOptions, SnapshotPartitionResult, SnapshotSelection, } from './agent-blackboard/index.mts';
|
|
11
|
-
export {
|
|
12
|
-
export type {
|
|
11
|
+
export { isConformingCiFailureBlock } from './audit-journal/ci-failures.mts';
|
|
12
|
+
export type { JournalEntry, JournalLoader, JournalLoadResult } from './audit-journal/types.mts';
|
|
13
13
|
export { EphemeralListenerAttemptsExhaustedError, isRunnerReservedPort, listenOnRunnerUnreservedEphemeralPort, loadRunnerPortPolicy, runnerPortPolicy, validateRunnerPortPolicy, } from './runner-port-policy/index.mts';
|
|
14
14
|
export type { EphemeralListenerOptions, RunnerPortPolicy } from './runner-port-policy/index.mts';
|
|
15
15
|
export { extractCreateTableMetadata, extractDefaultFunction, extractFuncCallArgColumnNames, initSqlAst, lineOfUtf8ByteOffset, MissingSqlAstParserError, parseSql, } from './sql-ast/index.mts';
|
|
@@ -95,7 +95,7 @@ export { validateResolvedPinDelta } from './swift-resolved-pin-delta/index.mts';
|
|
|
95
95
|
export type { ResolvedDocument, ResolvedPin, ValidateResolvedPinDeltaOptions, } from './swift-resolved-pin-delta/index.mts';
|
|
96
96
|
export { DEFAULT_MAX_DIAGNOSTIC_REPORTS, DEFAULT_MAX_FORMATTED_DIAGNOSTIC_REPORTS, formatDiagnosticReportSummaries, HARD_MAX_DIAGNOSTIC_REPORTS, readDiagnosticReportSummaries, summarizeDiagnosticReport, } from './vitest-diagnostics/index.mts';
|
|
97
97
|
export type { DiagnosticReportLimitOptions, DiagnosticReportSummary, } from './vitest-diagnostics/index.mts';
|
|
98
|
-
export { createCommandRunner, runGh, runGit } from './gh-cli/index.mts';
|
|
98
|
+
export { createCommandRunner, ghSpawnEnv, runGh, runGit } from './gh-cli/index.mts';
|
|
99
99
|
export type { ExecFileText, RunTextCommand } from './gh-cli/index.mts';
|
|
100
100
|
export { DiffCommandError, processDiffCommand } from './gh-cli/index.mts';
|
|
101
101
|
export type { DiffBlockCallback, DiffCommand } from './gh-cli/index.mts';
|
package/dist/index.mjs
CHANGED
|
@@ -4,7 +4,7 @@ export { HARNESS_IDS, inspectHarnessEnvironment, selectHarnessSession, } from ".
|
|
|
4
4
|
export { codexChildren, codexIdentity, computeTranscriptFacts, formatTranscriptFacts, formatUnavailable, resolveTranscriptFile, runRetrospectiveTranscript, } from "./retrospective-transcript/index.mjs";
|
|
5
5
|
export { runRetrospectiveFacts } from "./retrospective-facts/index.mjs";
|
|
6
6
|
export { appendJournal, assertSessionId, cleanupSnapshotPartitions, partitionSnapshot, probeBlackboard, readJournal, resolveBlackboardConnection, } from "./agent-blackboard/index.mjs";
|
|
7
|
-
export {
|
|
7
|
+
export { isConformingCiFailureBlock } from "./audit-journal/ci-failures.mjs";
|
|
8
8
|
export { EphemeralListenerAttemptsExhaustedError, isRunnerReservedPort, listenOnRunnerUnreservedEphemeralPort, loadRunnerPortPolicy, runnerPortPolicy, validateRunnerPortPolicy, } from "./runner-port-policy/index.mjs";
|
|
9
9
|
export { extractCreateTableMetadata, extractDefaultFunction, extractFuncCallArgColumnNames, 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";
|
|
@@ -52,7 +52,7 @@ export { normalizeSwiftSource } from "./swift-semantic-equal/index.mjs";
|
|
|
52
52
|
export { isSwiftCodeOffset, parseUniqueSwiftBinaryTargetChecksum, } from "./swift-source-offset/index.mjs";
|
|
53
53
|
export { validateResolvedPinDelta } from "./swift-resolved-pin-delta/index.mjs";
|
|
54
54
|
export { DEFAULT_MAX_DIAGNOSTIC_REPORTS, DEFAULT_MAX_FORMATTED_DIAGNOSTIC_REPORTS, formatDiagnosticReportSummaries, HARD_MAX_DIAGNOSTIC_REPORTS, readDiagnosticReportSummaries, summarizeDiagnosticReport, } from "./vitest-diagnostics/index.mjs";
|
|
55
|
-
export { createCommandRunner, runGh, runGit } from "./gh-cli/index.mjs";
|
|
55
|
+
export { createCommandRunner, ghSpawnEnv, runGh, runGit } from "./gh-cli/index.mjs";
|
|
56
56
|
export { DiffCommandError, processDiffCommand } from "./gh-cli/index.mjs";
|
|
57
57
|
export { GITHUB_BODY_MAX_CHARACTERS, validateGitHubBodyLength } from "./gh-cli/index.mjs";
|
|
58
58
|
export { assertHeadPushed, buildGhPrCreateArgs, createPullRequest, DetachedHeadError, HeadNotPushedError, HeadOutOfDateError, resolveHeadBranch, } from "./gh-cli/index.mjs";
|
|
@@ -3,6 +3,7 @@ import type { FeedbackIdentity } from '../agent-blackboard/feedback-types.mts';
|
|
|
3
3
|
import type { RunTextCommand } from '../gh-cli/exec.mts';
|
|
4
4
|
import type { GhRunner } from './pr-gh.mts';
|
|
5
5
|
import type { PolicySpawner } from './policy-spawn.mts';
|
|
6
|
+
import type { SnapshotRegistry } from './snapshot-registry.mts';
|
|
6
7
|
import { type Args } from './args.mts';
|
|
7
8
|
/** What a handler may rely on: every field here was validated before the handler ran. */
|
|
8
9
|
export type ToolContext = {
|
|
@@ -14,10 +15,14 @@ export type ToolContext = {
|
|
|
14
15
|
launchRoot: string | undefined;
|
|
15
16
|
/** Test seam: replaces the child process a policy export runs in. */
|
|
16
17
|
spawnPolicy?: PolicySpawner;
|
|
18
|
+
/** The server's record of its own exports, which `snapshot_cleanup` is bound to. */
|
|
19
|
+
snapshots: SnapshotRegistry;
|
|
17
20
|
/** Runs git with the isolated environment; callers add their own `-C <worktree>`. */
|
|
18
21
|
runGit: RunTextCommand;
|
|
19
22
|
/** Test seam: replaces locating and running gh. */
|
|
20
23
|
runGh?: GhRunner;
|
|
24
|
+
/** The connected MCP client's `clientInfo.name`, once `initialize` has completed. */
|
|
25
|
+
clientName?: () => string | undefined;
|
|
21
26
|
/** Test seam: replaces the operating system user and host name. */
|
|
22
27
|
device?: () => {
|
|
23
28
|
user: string;
|
|
@@ -2,6 +2,7 @@ import type { BlackboardClientDependencies } from '../agent-blackboard/client.mt
|
|
|
2
2
|
import type { RunTextCommand } from '../gh-cli/exec.mts';
|
|
3
3
|
import type { GhRunner } from './pr-gh.mts';
|
|
4
4
|
import type { PolicySpawner } from './policy-spawn.mts';
|
|
5
|
+
import { type SnapshotRegistry } from './snapshot-registry.mts';
|
|
5
6
|
export type ServerEnvironment = {
|
|
6
7
|
/** The launch directory's worktree top level, or undefined when it is not inside one. */
|
|
7
8
|
launchRoot: string | undefined;
|
|
@@ -15,11 +16,15 @@ export type ServerEnvironment = {
|
|
|
15
16
|
spawnPolicy?: PolicySpawner;
|
|
16
17
|
/** Test seam: replaces locating and running gh. */
|
|
17
18
|
runGh?: GhRunner;
|
|
19
|
+
/** The connected MCP client's `clientInfo.name`, once `initialize` has completed. */
|
|
20
|
+
clientName?: () => string | undefined;
|
|
18
21
|
/** Test seam: replaces the operating system user and host name. */
|
|
19
22
|
device?: () => {
|
|
20
23
|
user: string;
|
|
21
24
|
host: string;
|
|
22
25
|
};
|
|
26
|
+
/** Test seam: a fresh registry stands in for a server restart. Defaults to the process's own. */
|
|
27
|
+
snapshots?: SnapshotRegistry;
|
|
23
28
|
};
|
|
24
29
|
export type ToolCallResult = {
|
|
25
30
|
content: Array<{
|
|
@@ -2,12 +2,16 @@ import { FeedbackDeliveryError } from "../agent-blackboard/index.mjs";
|
|
|
2
2
|
import { optionalString, rejectUnknown, requireObject } from "./args.mjs";
|
|
3
3
|
import { readSessionId } from "./context.mjs";
|
|
4
4
|
import { journalAppend, journalEntries } from "./handlers-journal.mjs";
|
|
5
|
-
import {
|
|
5
|
+
import { retrospectiveSave } from "./handlers-retrospective.mjs";
|
|
6
|
+
import { retrospectiveCheck } from "./retrospective-check.mjs";
|
|
6
7
|
import { outboxFlush, outboxStatus } from "./handlers-outbox.mjs";
|
|
8
|
+
import { snapshotCleanup } from "./handlers-snapshot-cleanup.mjs";
|
|
7
9
|
import { sessionArchive, sessionEnsure, snapshotExport } from "./handlers-session.mjs";
|
|
8
10
|
import { handlePrCreate, handlePrUpdate, handlePrValidate } from "./handlers-pr.mjs";
|
|
11
|
+
import { createSnapshotRegistry } from "./snapshot-registry.mjs";
|
|
9
12
|
import { TOOLS } from "./tools.mjs";
|
|
10
13
|
import { resolveWorktree } from "./worktree.mjs";
|
|
14
|
+
const PROCESS_SNAPSHOTS = createSnapshotRegistry();
|
|
11
15
|
const HANDLERS = {
|
|
12
16
|
journal_append: journalAppend,
|
|
13
17
|
journal_entries: journalEntries,
|
|
@@ -20,6 +24,7 @@ const HANDLERS = {
|
|
|
20
24
|
pr_create: handlePrCreate,
|
|
21
25
|
pr_update: handlePrUpdate,
|
|
22
26
|
snapshot_export: snapshotExport,
|
|
27
|
+
snapshot_cleanup: snapshotCleanup,
|
|
23
28
|
session_archive: sessionArchive,
|
|
24
29
|
};
|
|
25
30
|
const DIAGNOSTIC_HINTS = {
|
|
@@ -60,7 +65,9 @@ export async function callTool(name, rawArguments, environment) {
|
|
|
60
65
|
env: environment.env,
|
|
61
66
|
launchRoot: environment.launchRoot,
|
|
62
67
|
runGit: environment.runGit,
|
|
68
|
+
snapshots: environment.snapshots ?? PROCESS_SNAPSHOTS,
|
|
63
69
|
...(environment.runGh === undefined ? {} : { runGh: environment.runGh }),
|
|
70
|
+
...(environment.clientName === undefined ? {} : { clientName: environment.clientName }),
|
|
64
71
|
...(environment.device === undefined ? {} : { device: environment.device }),
|
|
65
72
|
...(environment.spawnPolicy === undefined ? {} : { spawnPolicy: environment.spawnPolicy }),
|
|
66
73
|
// The client resolves from this package's install, like the SDK, never from the worktree.
|
|
@@ -13,6 +13,8 @@ export type FakeOptions = {
|
|
|
13
13
|
omitArchive?: boolean;
|
|
14
14
|
omitSnapshots?: boolean;
|
|
15
15
|
exportResult?: unknown;
|
|
16
|
+
/** Gives the fake client its own capability-aware snapshot cleanup. */
|
|
17
|
+
clientCleanup?: (options: unknown) => Promise<void>;
|
|
16
18
|
/** Makes `ensure` fail for any other identity, and `get` report a session with this one. */
|
|
17
19
|
conflictingSession?: {
|
|
18
20
|
id: string;
|
|
@@ -81,6 +81,9 @@ export function fakeBlackboard(options = {}) {
|
|
|
81
81
|
Sessions,
|
|
82
82
|
Entries,
|
|
83
83
|
...(options.omitSnapshots ? {} : { Snapshots }),
|
|
84
|
+
...(options.clientCleanup === undefined
|
|
85
|
+
? {}
|
|
86
|
+
: { cleanupSnapshotPartitions: options.clientCleanup }),
|
|
84
87
|
};
|
|
85
88
|
return { dependencies: { loadClient: async () => module }, calls, stored };
|
|
86
89
|
}
|
|
@@ -1,115 +1,24 @@
|
|
|
1
|
-
import { hostname, userInfo } from 'node:os';
|
|
2
|
-
import { validateGitHubBodyLength } from "../gh-cli/body-length.mjs";
|
|
3
1
|
import { createPullRequest } from "../gh-cli/pr-create.mjs";
|
|
4
|
-
import { assertPolicyAllowed } from "./context.mjs";
|
|
5
|
-
import { runValidator } from "./policy-run.mjs";
|
|
6
|
-
import { renderPullRequestBody, worktreeLabel } from "./pr-body.mjs";
|
|
7
2
|
import { pullRequestNumber, withBodyFile } from "./pr-body-file.mjs";
|
|
8
3
|
import { pullRequestDefaults } from "./pr-defaults.mjs";
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
12
|
-
import { assertJournalUnchanged, credentials, liveBase, livePullRequest, redactCredentials, } from "./pr-live.mjs";
|
|
13
|
-
import { parseWorktreeList } from "./worktree.mjs";
|
|
14
|
-
function gitIn(context) {
|
|
15
|
-
return (args) => context.runGit(['-C', context.worktree, ...args]);
|
|
16
|
-
}
|
|
17
|
-
function ghIn(context) {
|
|
18
|
-
const run = context.runGh ?? createGhRunner(context.env);
|
|
19
|
-
const env = ghEnvironment(context.env);
|
|
20
|
-
return (args) => run(args, { cwd: context.worktree, env });
|
|
21
|
-
}
|
|
22
|
-
async function currentBranch(git) {
|
|
23
|
-
const branch = (await git(['rev-parse', '--abbrev-ref', 'HEAD'])).trim();
|
|
24
|
-
if (branch === 'HEAD')
|
|
25
|
-
throw new Error('the worktree has a detached HEAD: check out a branch');
|
|
26
|
-
return branch;
|
|
27
|
-
}
|
|
28
|
-
async function label(context, git) {
|
|
29
|
-
const listing = await git(['worktree', 'list', '--porcelain', '-z']);
|
|
30
|
-
return worktreeLabel(context.worktree, parseWorktreeList(listing)[0]);
|
|
31
|
-
}
|
|
4
|
+
import { positiveInteger } from "./pr-input.mjs";
|
|
5
|
+
import { assertJournalUnchanged } from "./pr-live.mjs";
|
|
6
|
+
import { ghIn, gitIn, policyFor, prepare } from "./pr-prepare.mjs";
|
|
32
7
|
function readPr(args, required) {
|
|
33
8
|
if (args.pr === undefined && !required)
|
|
34
9
|
return undefined;
|
|
35
10
|
return positiveInteger(args.pr, 'pr');
|
|
36
11
|
}
|
|
37
|
-
function policyFor(context) {
|
|
38
|
-
return {
|
|
39
|
-
worktree: context.worktree,
|
|
40
|
-
env: context.env,
|
|
41
|
-
...(context.spawnPolicy === undefined ? {} : { spawn: context.spawnPolicy }),
|
|
42
|
-
};
|
|
43
|
-
}
|
|
44
|
-
/** Renders the body, preserves the live journal for an update, and runs the policy validator. */
|
|
45
|
-
async function prepare(args, context, pr) {
|
|
46
|
-
assertPolicyAllowed(context);
|
|
47
|
-
// No server credential may reach the policy or gh, whatever the caller wrote: every field is
|
|
48
|
-
// redacted before anything is rendered or forwarded.
|
|
49
|
-
const secrets = credentials(context.env);
|
|
50
|
-
const given = redactCredentials(readPullRequestInput(args), secrets);
|
|
51
|
-
checkTitleLength(given.title);
|
|
52
|
-
const git = gitIn(context);
|
|
53
|
-
const head = await currentBranch(git);
|
|
54
|
-
// Branch names are refs passed to gh as is: redacting one would name a branch that does not exist.
|
|
55
|
-
if (secrets.some((secret) => head.includes(secret)))
|
|
56
|
-
throw new Error('the worktree branch name contains a server credential: rename the branch');
|
|
57
|
-
if (secrets.some((secret) => args.base?.includes(secret)))
|
|
58
|
-
throw new Error('base contains a server credential: use another base branch');
|
|
59
|
-
const live = pr === undefined ? undefined : await livePullRequest(ghIn(context), pr);
|
|
60
|
-
if (live !== undefined && live.headRefName !== head)
|
|
61
|
-
throw new Error(`pull request ${pr} has head ${live.headRefName}, not the worktree branch ${head}`);
|
|
62
|
-
const base = live === undefined
|
|
63
|
-
? given.base
|
|
64
|
-
: liveBase(pr, live, redactCredentials(args.base, secrets));
|
|
65
|
-
const input = { ...given, base };
|
|
66
|
-
if (head === base)
|
|
67
|
-
throw new Error(`head branch ${head} must differ from base ${base}`);
|
|
68
|
-
const device = (context.device ?? (() => ({ user: userInfo().username, host: hostname() })))();
|
|
69
|
-
let body = renderPullRequestBody(input, {
|
|
70
|
-
sessionId: context.sessionId,
|
|
71
|
-
...device,
|
|
72
|
-
worktree: await label(context, git),
|
|
73
|
-
});
|
|
74
|
-
if (live !== undefined)
|
|
75
|
-
body = preserveShepherdJournal(body, live.body);
|
|
76
|
-
body = redactCredentials(body, secrets);
|
|
77
|
-
const length = validateGitHubBodyLength(body);
|
|
78
|
-
if (!length.ok)
|
|
79
|
-
throw new Error(`the rendered body has ${length.characterCount} characters; GitHub allows ${length.maxCharacterCount}`);
|
|
80
|
-
const report = await runValidator({
|
|
81
|
-
...policyFor(context),
|
|
82
|
-
export: 'validatePullRequest',
|
|
83
|
-
input: {
|
|
84
|
-
title: input.title,
|
|
85
|
-
body,
|
|
86
|
-
base: input.base,
|
|
87
|
-
head,
|
|
88
|
-
mode: input.mode,
|
|
89
|
-
...(pr === undefined ? {} : { pr }),
|
|
90
|
-
},
|
|
91
|
-
});
|
|
92
|
-
const errors = 'errors' in report ? report.errors : [];
|
|
93
|
-
const warnings = 'warnings' in report ? report.warnings : [];
|
|
94
|
-
return {
|
|
95
|
-
input,
|
|
96
|
-
head,
|
|
97
|
-
body,
|
|
98
|
-
errors,
|
|
99
|
-
warnings,
|
|
100
|
-
...(live === undefined ? {} : { liveBody: live.body }),
|
|
101
|
-
};
|
|
102
|
-
}
|
|
103
12
|
function rejectErrors(prepared) {
|
|
104
13
|
if (prepared.errors.length)
|
|
105
14
|
throw new Error(`policy rejected the pull request:\n- ${prepared.errors.join('\n- ')}`);
|
|
106
15
|
}
|
|
107
16
|
export const handlePrValidate = async (args, context) => {
|
|
108
|
-
const { body, errors, warnings } = await prepare(args, context, readPr(args, false));
|
|
17
|
+
const { body, errors, warnings } = await prepare(args, context, 'validate', readPr(args, false));
|
|
109
18
|
return { body, errors, warnings };
|
|
110
19
|
};
|
|
111
20
|
export const handlePrCreate = async (args, context) => {
|
|
112
|
-
const prepared = await prepare(args, context, undefined);
|
|
21
|
+
const prepared = await prepare(args, context, 'create', undefined);
|
|
113
22
|
rejectErrors(prepared);
|
|
114
23
|
const { input, head, body } = prepared;
|
|
115
24
|
const defaults = await pullRequestDefaults(policyFor(context), input);
|
|
@@ -127,7 +36,7 @@ export const handlePrCreate = async (args, context) => {
|
|
|
127
36
|
};
|
|
128
37
|
export const handlePrUpdate = async (args, context) => {
|
|
129
38
|
const pr = readPr(args, true);
|
|
130
|
-
const prepared = await prepare(args, context, pr);
|
|
39
|
+
const prepared = await prepare(args, context, 'update', pr);
|
|
131
40
|
rejectErrors(prepared);
|
|
132
41
|
await assertJournalUnchanged(ghIn(context), pr, prepared.liveBody, prepared.input.base);
|
|
133
42
|
const url = await withBodyFile(prepared.body, async (bodyFile) => (await ghIn(context)([
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { type ToolHandler } from './context.mts';
|
|
2
|
+
/** The default event id, hashed when the session id would push it past the 256-byte limit. */
|
|
3
|
+
export declare function defaultSourceEventId(sessionId: string): string;
|
|
4
|
+
/**
|
|
5
|
+
* Saves the session's retrospective: composes it when `compose` is given, checks the arguments
|
|
6
|
+
* against the document's front matter, runs the checkout's `validateRetrospective` policy (in a
|
|
7
|
+
* child process) and writes nothing if it reports errors, then writes through the same verified
|
|
8
|
+
* path as `journal_append`.
|
|
9
|
+
*/
|
|
10
|
+
export declare const retrospectiveSave: ToolHandler;
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto';
|
|
2
|
+
import { createFeedbackEnvelope, validateFeedbackEnvelope } from "../agent-blackboard/index.mjs";
|
|
3
|
+
import { optionalRecord, optionalString, requiredChoice, requiredString, requiredStringArray, } from "./args.mjs";
|
|
4
|
+
import { assertPolicyAllowed, readIdentity } from "./context.mjs";
|
|
5
|
+
import { MODES, OUTCOMES, readCoverage } from "./handlers-journal.mjs";
|
|
6
|
+
import { runValidator } from "./policy-run.mjs";
|
|
7
|
+
import { previewRegistry } from "./preview-registry.mjs";
|
|
8
|
+
import { deliver, saveFromPreview } from "./retrospective-preview.mjs";
|
|
9
|
+
import { parseRetrospectiveFrontMatter } from "./retrospective-front-matter.mjs";
|
|
10
|
+
import { FEEDBACK_MARKDOWN_MAX_BYTES, redactFeedbackText, } from "../agent-blackboard/feedback-codec.mjs";
|
|
11
|
+
import { composeFromArgs, sensitiveValues } from "./retrospective-input.mjs";
|
|
12
|
+
import { checkFrontMatter, resolveRepositories } from "./retrospective-resolve.mjs";
|
|
13
|
+
function readMarkdownSource(args) {
|
|
14
|
+
const compose = optionalRecord(args, 'compose');
|
|
15
|
+
if ((args.markdown === undefined) === (compose === undefined))
|
|
16
|
+
throw new Error('pass exactly one of markdown or compose');
|
|
17
|
+
return compose === undefined ? { markdown: requiredString(args, 'markdown') } : { compose };
|
|
18
|
+
}
|
|
19
|
+
/** The default event id, hashed when the session id would push it past the 256-byte limit. */
|
|
20
|
+
export function defaultSourceEventId(sessionId) {
|
|
21
|
+
const id = `retrospective-${sessionId}`;
|
|
22
|
+
return Buffer.byteLength(id) <= 256
|
|
23
|
+
? id
|
|
24
|
+
: `retrospective-${createHash('sha256').update(sessionId).digest('hex')}`;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Saves the session's retrospective: composes it when `compose` is given, checks the arguments
|
|
28
|
+
* against the document's front matter, runs the checkout's `validateRetrospective` policy (in a
|
|
29
|
+
* child process) and writes nothing if it reports errors, then writes through the same verified
|
|
30
|
+
* path as `journal_append`.
|
|
31
|
+
*/
|
|
32
|
+
export const retrospectiveSave = async (args, context) => {
|
|
33
|
+
assertPolicyAllowed(context);
|
|
34
|
+
const identity = readIdentity(args, context.sessionId);
|
|
35
|
+
const mode = requiredChoice(args, 'mode', MODES);
|
|
36
|
+
const sourceEventId = optionalString(args, 'sourceEventId') ?? defaultSourceEventId(context.sessionId);
|
|
37
|
+
if (args.previewToken !== undefined)
|
|
38
|
+
return saveFromPreview(args, context, identity, mode, sourceEventId);
|
|
39
|
+
const workOutcome = requiredChoice(args, 'workOutcome', OUTCOMES);
|
|
40
|
+
const feedbackCoverage = readCoverage(args);
|
|
41
|
+
const argRepositories = args.repositories === undefined ? undefined : requiredStringArray(args, 'repositories');
|
|
42
|
+
const category = optionalString(args, 'category');
|
|
43
|
+
if (args.dryRun !== undefined && typeof args.dryRun !== 'boolean')
|
|
44
|
+
throw new Error('dryRun must be a boolean');
|
|
45
|
+
const policy = {
|
|
46
|
+
worktree: context.worktree,
|
|
47
|
+
env: context.env,
|
|
48
|
+
...(context.spawnPolicy === undefined ? {} : { spawn: context.spawnPolicy }),
|
|
49
|
+
};
|
|
50
|
+
const { markdown: given, compose } = readMarkdownSource(args);
|
|
51
|
+
if (compose !== undefined && args.knownSensitiveValues !== undefined)
|
|
52
|
+
throw new Error('with compose, pass knownSensitiveValues inside compose');
|
|
53
|
+
// A markdown save redacts every argument with the same values a compose save would.
|
|
54
|
+
const secrets = sensitiveValues(compose ?? { knownSensitiveValues: args.knownSensitiveValues }, context.env);
|
|
55
|
+
// Everything the policy sees is derived from the redacted document, never the raw input.
|
|
56
|
+
const redactedGiven = given === undefined ? undefined : redactFeedbackText(given, secrets);
|
|
57
|
+
// Reject a document that can never be stored before any policy export runs for it.
|
|
58
|
+
if (redactedGiven !== undefined && Buffer.byteLength(redactedGiven) > FEEDBACK_MARKDOWN_MAX_BYTES)
|
|
59
|
+
throw new Error(`markdown must be at most ${FEEDBACK_MARKDOWN_MAX_BYTES} bytes`);
|
|
60
|
+
const early = redactedGiven === undefined ? undefined : parseRetrospectiveFrontMatter(redactedGiven);
|
|
61
|
+
const repositories = await resolveRepositories(argRepositories, early?.repositories, policy);
|
|
62
|
+
const markdown = redactedGiven ??
|
|
63
|
+
redactFeedbackText(await composeFromArgs(compose, {
|
|
64
|
+
sessionId: context.sessionId,
|
|
65
|
+
repositories,
|
|
66
|
+
workOutcome,
|
|
67
|
+
feedbackCoverage,
|
|
68
|
+
worktree: context.worktree,
|
|
69
|
+
env: context.env,
|
|
70
|
+
dependencies: context.dependencies,
|
|
71
|
+
}), secrets);
|
|
72
|
+
const front = early ?? parseRetrospectiveFrontMatter(markdown);
|
|
73
|
+
checkFrontMatter(front, {
|
|
74
|
+
sessionId: context.sessionId,
|
|
75
|
+
workOutcome,
|
|
76
|
+
// The document was redacted before parsing, so compare it with equally redacted sources.
|
|
77
|
+
feedbackCoverage: {
|
|
78
|
+
...feedbackCoverage,
|
|
79
|
+
sources: feedbackCoverage.sources.map((source) => redactFeedbackText(source, secrets)),
|
|
80
|
+
},
|
|
81
|
+
repositories: argRepositories,
|
|
82
|
+
});
|
|
83
|
+
// Building the envelope first rejects empty or oversized markdown before any policy runs.
|
|
84
|
+
const envelope = createFeedbackEnvelope({
|
|
85
|
+
schemaVersion: 1,
|
|
86
|
+
type: 'retrospective',
|
|
87
|
+
sourceEventId,
|
|
88
|
+
// The server owns the timestamp, as in journal_append.
|
|
89
|
+
timestamp: new Date().toISOString(),
|
|
90
|
+
date: front.date,
|
|
91
|
+
issues: front.issues,
|
|
92
|
+
prs: front.prs,
|
|
93
|
+
repositories,
|
|
94
|
+
markdown,
|
|
95
|
+
workOutcome,
|
|
96
|
+
feedbackCoverage,
|
|
97
|
+
...(category === undefined ? {} : { category }),
|
|
98
|
+
}, { knownSensitiveValues: secrets });
|
|
99
|
+
const redactedFront = parseRetrospectiveFrontMatter(envelope.markdown);
|
|
100
|
+
validateFeedbackEnvelope(envelope);
|
|
101
|
+
const report = await runValidator({
|
|
102
|
+
...policy,
|
|
103
|
+
export: 'validateRetrospective',
|
|
104
|
+
input: {
|
|
105
|
+
sessionId: context.sessionId,
|
|
106
|
+
parentSessionId: identity.parentSessionId,
|
|
107
|
+
mode,
|
|
108
|
+
workOutcome,
|
|
109
|
+
// The envelope's copy, which is redacted like the markdown.
|
|
110
|
+
feedbackCoverage: envelope.feedbackCoverage,
|
|
111
|
+
repositories,
|
|
112
|
+
markdown: envelope.markdown,
|
|
113
|
+
sourceEventId,
|
|
114
|
+
metadata: {
|
|
115
|
+
date: redactedFront.date,
|
|
116
|
+
issues: redactedFront.issues,
|
|
117
|
+
prs: redactedFront.prs,
|
|
118
|
+
description: redactedFront.description,
|
|
119
|
+
},
|
|
120
|
+
},
|
|
121
|
+
});
|
|
122
|
+
if ('errors' in report && report.errors.length)
|
|
123
|
+
throw new Error(`policy rejected the retrospective:\n- ${report.errors.join('\n- ')}`);
|
|
124
|
+
const warnings = 'warnings' in report ? report.warnings : [];
|
|
125
|
+
if (args.dryRun === true) {
|
|
126
|
+
const previewToken = previewRegistry.put({
|
|
127
|
+
sessionId: context.sessionId,
|
|
128
|
+
parentSessionId: identity.parentSessionId,
|
|
129
|
+
mode,
|
|
130
|
+
worktree: context.worktree,
|
|
131
|
+
sourceEventId,
|
|
132
|
+
envelope,
|
|
133
|
+
warnings,
|
|
134
|
+
});
|
|
135
|
+
return { markdown: envelope.markdown, previewToken, policy: { warnings } };
|
|
136
|
+
}
|
|
137
|
+
return { ...(await deliver(envelope, identity, mode, context)), policy: { warnings } };
|
|
138
|
+
};
|
|
@@ -1,12 +1 @@
|
|
|
1
|
-
|
|
2
|
-
/** Whether the session already holds a retrospective, and which (sourceEventId, issues, prs). */
|
|
3
|
-
export declare const retrospectiveCheck: ToolHandler;
|
|
4
|
-
/** The default event id, hashed when the session id would push it past the 256-byte limit. */
|
|
5
|
-
export declare function defaultSourceEventId(sessionId: string): string;
|
|
6
|
-
/**
|
|
7
|
-
* Saves the session's retrospective: composes it when `compose` is given, checks the arguments
|
|
8
|
-
* against the document's front matter, runs the checkout's `validateRetrospective` policy (in a
|
|
9
|
-
* child process) and writes nothing if it reports errors, then writes through the same verified
|
|
10
|
-
* path as `journal_append`.
|
|
11
|
-
*/
|
|
12
|
-
export declare const retrospectiveSave: ToolHandler;
|
|
1
|
+
export { defaultSourceEventId, retrospectiveSave } from './handlers-retrospective-save.mts';
|