vouchington-tooling 0.43.0 → 0.44.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 +45 -1
- package/dist/cli/commands/fresh-base.d.mts +6 -0
- package/dist/cli/commands/fresh-base.mjs +14 -0
- package/dist/cli/index.mjs +3 -0
- package/dist/cli/parse-fresh-base.d.mts +2 -0
- package/dist/cli/parse-fresh-base.mjs +15 -0
- package/dist/cli/parse.d.mts +5 -0
- package/dist/cli/parse.mjs +3 -0
- package/dist/cli/usage.d.mts +1 -1
- package/dist/cli/usage.mjs +2 -0
- package/dist/fresh-base/classify.d.mts +21 -0
- package/dist/fresh-base/classify.mjs +29 -0
- package/dist/fresh-base/index.d.mts +4 -0
- package/dist/fresh-base/index.mjs +2 -0
- package/dist/fresh-base/run.d.mts +25 -0
- package/dist/fresh-base/run.mjs +66 -0
- package/dist/gh-cli/closing-refs.d.mts +24 -0
- package/dist/gh-cli/closing-refs.mjs +37 -0
- package/dist/gh-cli/index.d.mts +3 -0
- package/dist/gh-cli/index.mjs +2 -0
- package/dist/gh-cli/non-closing-refs.d.mts +11 -0
- package/dist/gh-cli/non-closing-refs.mjs +24 -0
- package/dist/index.d.mts +6 -0
- package/dist/index.mjs +3 -0
- package/dist/pull-request-diff/diff-size.d.mts +29 -0
- package/dist/pull-request-diff/diff-size.mjs +35 -0
- package/dist/pull-request-diff/index.d.mts +2 -0
- package/dist/pull-request-diff/index.mjs +1 -0
- package/dist/retrospective-distill/args.d.mts +13 -0
- package/dist/retrospective-distill/args.mjs +27 -0
- package/dist/retrospective-distill/checkpoint-entry.d.mts +2 -0
- package/dist/retrospective-distill/checkpoint-entry.mjs +11 -0
- package/dist/retrospective-distill/fixtures.test-helpers.d.mts +3 -0
- package/dist/retrospective-distill/fixtures.test-helpers.mjs +21 -0
- package/dist/retrospective-distill/flag-args.d.mts +5 -0
- package/dist/retrospective-distill/flag-args.mjs +31 -0
- package/dist/retrospective-distill/index.d.mts +10 -0
- package/dist/retrospective-distill/index.mjs +6 -0
- package/dist/retrospective-distill/partition-records.d.mts +6 -0
- package/dist/retrospective-distill/partition-records.mjs +49 -0
- package/dist/retrospective-distill/retrospective-entry.d.mts +3 -0
- package/dist/retrospective-distill/retrospective-entry.mjs +4 -0
- package/dist/retrospective-distill/run.d.mts +5 -0
- package/dist/retrospective-distill/run.mjs +28 -0
- package/dist/retrospective-distill/session-types.d.mts +17 -0
- package/dist/retrospective-distill/session-types.mjs +1 -0
- package/dist/retrospective-distill/shape.d.mts +29 -0
- package/dist/retrospective-distill/shape.mjs +111 -0
- package/docs/mcp-server.md +1 -1
- package/package.json +18 -3
- package/skills/agent-workflow/SKILL.md +20 -0
- package/skills/agent-workflow/references/four-refs.md +74 -0
- package/skills/blackboard/SKILL.md +21 -1
- package/skills/github-issue/SKILL.md +6 -0
- package/skills/manifest.json +11 -5
- package/skills/postgres-node-performance-tuning/SKILL.md +2 -0
- package/skills/postgres-node-performance-tuning/references/performance-patterns.md +76 -0
- package/skills/postgres-node-performance-tuning/references/reading-plans.md +111 -0
- package/skills/postgres-schema-design/references/relations.md +18 -0
- package/skills/retrospective-distill/SKILL.md +67 -4
- package/skills/revisit-followups/SKILL.md +17 -0
- package/skills/stacked-prs/SKILL.md +47 -16
- package/skills/stacked-prs/references/drain.md +168 -0
- package/skills/stacked-prs/references/gh-stack.md +143 -0
package/README.md
CHANGED
|
@@ -138,7 +138,13 @@ one, prints `download failed artifact=<name> exit=<n>`, exits non-zero (a downlo
|
|
|
138
138
|
reported as 1 so it cannot be mistaken for absence), and writes no `availability` output.
|
|
139
139
|
|
|
140
140
|
`require-up-to-date` fetches the requested remote branch and fails unless its fetched tip is an
|
|
141
|
-
ancestor of `HEAD`. `
|
|
141
|
+
ancestor of `HEAD`. `fresh-base` refreshes `<remote>/<branch>` (default `origin/main`, 8 s fetch timeout, `--skip-fetch`
|
|
142
|
+
to compare the cached ref) and prints a JSON report classifying the worktree as `up-to-date`,
|
|
143
|
+
`ahead`, `behind`, `diverged`, `dirty` (matches the base but has uncommitted changes),
|
|
144
|
+
`fetch-failed` (a failed refresh outranks every other status), or `unknown`. It prints nothing
|
|
145
|
+
outside a Git repository. Unlike `require-up-to-date` it never fails on staleness; callers decide
|
|
146
|
+
what to do with the status.
|
|
147
|
+
`gitleaks-directory-scan` builds and scans isolated staged-index and current
|
|
142
148
|
nonignored-working-tree mirrors with an explicit config; `--directory` selects the repository root.
|
|
143
149
|
`ast-grep-examples` runs native `ast-grep test`, then validates each scoped rule's `files:` and
|
|
144
150
|
`ignores:` examples with project `languageGlobs` replay from its root `--config`.
|
|
@@ -292,6 +298,7 @@ import {
|
|
|
292
298
|
} from 'vouchington-tooling/dependency-license-policy'
|
|
293
299
|
import { checkGhaWorkspacePolicy } from 'vouchington-tooling/gha-workspace-policy'
|
|
294
300
|
import { requireUpToDate } from 'vouchington-tooling/require-up-to-date'
|
|
301
|
+
import { checkFreshBase, classifyFreshBase } from 'vouchington-tooling/fresh-base'
|
|
295
302
|
import { runGitleaksDirectoryScan } from 'vouchington-tooling/gitleaks-directory-scan'
|
|
296
303
|
import { runAstGrepExamples } from 'vouchington-tooling/ast-grep-examples'
|
|
297
304
|
import { astGrepPackPaths } from 'vouchington-tooling/ast-grep-pack'
|
|
@@ -331,6 +338,30 @@ await processDiffCommand({ executable: 'git', args: ['diff', 'origin/main...HEAD
|
|
|
331
338
|
// Use changedFileCount only after processDiffCommand resolves.
|
|
332
339
|
```
|
|
333
340
|
|
|
341
|
+
`vouchington-tooling/gh-cli` also parses pull request body references.
|
|
342
|
+
`parseClosingIssueReferences` finds GitHub closing keywords (`close`/`closes`/`closed`,
|
|
343
|
+
`fix`/`fixes`/`fixed`, `resolve`/`resolves`/`resolved`) followed by `#N`, `owner/repo#N` or an issue
|
|
344
|
+
URL, deduplicated by key (`#N` or lowercased `owner/repo#N`, see `formatReferenceKey`).
|
|
345
|
+
`parseNonClosingRefs` collects `Ref`, `Refs` and `Part of` keys from text the caller has already
|
|
346
|
+
scoped, such as a "Related issues" section.
|
|
347
|
+
|
|
348
|
+
`vouchington-tooling/pull-request-diff` counts added and deleted lines in unified-diff text
|
|
349
|
+
(`countDiffLineChanges`, excluding `+++`/`---` headers) and compares them with caller-supplied
|
|
350
|
+
`LargeDiffThresholds` through `exceedsLargeDiffThreshold`. `DEFAULT_LARGE_DIFF_THRESHOLDS`
|
|
351
|
+
(5,000 added, 20,000 deleted) is only an example default.
|
|
352
|
+
|
|
353
|
+
```ts
|
|
354
|
+
import {
|
|
355
|
+
countDiffLineChanges,
|
|
356
|
+
exceedsLargeDiffThreshold,
|
|
357
|
+
} from 'vouchington-tooling/pull-request-diff'
|
|
358
|
+
|
|
359
|
+
exceedsLargeDiffThreshold(countDiffLineChanges(diffText), {
|
|
360
|
+
addedLines: 5000,
|
|
361
|
+
deletedLines: 20_000,
|
|
362
|
+
})
|
|
363
|
+
```
|
|
364
|
+
|
|
334
365
|
This breaking minor removes `getDiffAgainstBase`; migrate callers to `processDiffCommand` and pass
|
|
335
366
|
the command they need to run.
|
|
336
367
|
|
|
@@ -700,6 +731,19 @@ reexports, and literal dynamic/require loads. Missing or incomplete facts for a
|
|
|
700
731
|
throw; external specifiers resolve to `null`. The default file inventory limit is 10,000
|
|
701
732
|
files and can be reduced with `maxFiles`.
|
|
702
733
|
|
|
734
|
+
## Retrospective distill classifier
|
|
735
|
+
|
|
736
|
+
`vouchington-tooling/retrospective-distill` exports `classifySession(session, entries, cutoffs)`,
|
|
737
|
+
which assigns a session one of `retrospective`, `entry-type-unresolved`, `checkpoint-only`,
|
|
738
|
+
`journal-only`, `zero-entry-child`, or `zero-entry-root` and decides distillation eligibility
|
|
739
|
+
against caller-supplied `retroCutoff` and `sessionCutoff` timestamps. It deduplicates replayed
|
|
740
|
+
source events, quarantines unknown entry types, invalid feedback envelopes, and conflicting source
|
|
741
|
+
events, and never marks an `entry-type-unresolved` session eligible. `readPartitionRecords(path)`
|
|
742
|
+
reads a local agent-blackboard partition JSONL file; `parseDistillClassifyArgs` and
|
|
743
|
+
`runDistillClassify` wrap both behind `<partition.jsonl> --retro-cutoff <iso> --session-cutoff
|
|
744
|
+
<iso>` and print one tab-separated line per session. The code is the source of truth for the
|
|
745
|
+
taxonomy; the retrospective-distill skill is its human-facing description.
|
|
746
|
+
|
|
703
747
|
## Static schema-allowlist pairs
|
|
704
748
|
|
|
705
749
|
`extractStaticStringPairs(source)` from `pg-schema-snapshot` collects the first two string literals
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { checkFreshBase } from "../../fresh-base/index.mjs";
|
|
2
|
+
/** Prints the JSON report (nothing outside a Git repository). Exits 0 whenever a report is made. */
|
|
3
|
+
export function runFreshBase(options) {
|
|
4
|
+
try {
|
|
5
|
+
const report = checkFreshBase(options);
|
|
6
|
+
if (report !== null)
|
|
7
|
+
process.stdout.write(`${JSON.stringify(report)}\n`);
|
|
8
|
+
return 0;
|
|
9
|
+
}
|
|
10
|
+
catch (error) {
|
|
11
|
+
process.stderr.write(`${error instanceof Error ? error.message : String(error)}\n`);
|
|
12
|
+
return 1;
|
|
13
|
+
}
|
|
14
|
+
}
|
package/dist/cli/index.mjs
CHANGED
|
@@ -21,6 +21,7 @@ import { runRetrospectiveFactsCommand } from "./commands/retrospective-facts.mjs
|
|
|
21
21
|
import { runAgentBlackboardCommand } from "./commands/agent-blackboard.mjs";
|
|
22
22
|
import { runMcpCommand } from "./commands/mcp.mjs";
|
|
23
23
|
import { runWithHostLock } from "./commands/with-host-lock.mjs";
|
|
24
|
+
import { runFreshBase } from "./commands/fresh-base.mjs";
|
|
24
25
|
import { runRequireUpToDate } from "./commands/require-up-to-date.mjs";
|
|
25
26
|
import { runAstGrepExamplesCommand } from "./commands/ast-grep-examples.mjs";
|
|
26
27
|
import { runAstGrepPackCommand } from "./commands/ast-grep-pack.mjs";
|
|
@@ -135,6 +136,8 @@ export function runCli(argv = process.argv) {
|
|
|
135
136
|
return runMcpCommand(parsed.args);
|
|
136
137
|
case 'require-up-to-date':
|
|
137
138
|
return runRequireUpToDate(parsed);
|
|
139
|
+
case 'fresh-base':
|
|
140
|
+
return runFreshBase(parsed);
|
|
138
141
|
case 'gitleaks-directory-scan':
|
|
139
142
|
return runScript('bash', packageScriptPath('scripts/gitleaks-directory-scan.sh'), [
|
|
140
143
|
'--config',
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { parseOptions } from "./parse-direct-options.mjs";
|
|
2
|
+
export function parseFreshBase(args) {
|
|
3
|
+
const skipFetch = args.includes('--skip-fetch');
|
|
4
|
+
const rest = args.filter((arg) => arg !== '--skip-fetch');
|
|
5
|
+
const options = parseOptions(rest, ['remote', 'branch'], 'fresh-base', 'name');
|
|
6
|
+
if ('kind' in options)
|
|
7
|
+
return options;
|
|
8
|
+
const { remote, branch } = options.values;
|
|
9
|
+
return {
|
|
10
|
+
kind: 'fresh-base',
|
|
11
|
+
skipFetch,
|
|
12
|
+
...(remote === undefined ? {} : { remote }),
|
|
13
|
+
...(branch === undefined ? {} : { branch }),
|
|
14
|
+
};
|
|
15
|
+
}
|
package/dist/cli/parse.d.mts
CHANGED
|
@@ -70,6 +70,11 @@ export type ParsedCli = {
|
|
|
70
70
|
kind: 'require-up-to-date';
|
|
71
71
|
remote: string;
|
|
72
72
|
branch: string;
|
|
73
|
+
} | {
|
|
74
|
+
kind: 'fresh-base';
|
|
75
|
+
remote?: string;
|
|
76
|
+
branch?: string;
|
|
77
|
+
skipFetch: boolean;
|
|
73
78
|
} | {
|
|
74
79
|
kind: 'gitleaks-directory-scan';
|
|
75
80
|
config: string;
|
package/dist/cli/parse.mjs
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { parseGhaArtifactsCleanup, } from "./parse-gha-artifacts-cleanup.mjs";
|
|
2
2
|
import { parseGhaRuntimeAudit } from "./parse-gha-runtime-audit.mjs";
|
|
3
3
|
import { parseForwardedCommand } from "./parse-forwarded-commands.mjs";
|
|
4
|
+
import { parseFreshBase } from "./parse-fresh-base.mjs";
|
|
4
5
|
import { commandNames } from "./usage.mjs";
|
|
5
6
|
import { parseAstGrepExamples, parseAstGrepPack, parseGhaWorkspacePolicy, parseGitleaksDirectoryScan, parseHttpOrigin, parseLinkSkill, parseRequireUpToDate, parseRunnerPortPolicy, } from "./parse-options.mjs";
|
|
6
7
|
const SCRIPT_COMMANDS = new Set([
|
|
@@ -50,6 +51,8 @@ export function parseCli(argv) {
|
|
|
50
51
|
return parseLinkSkill(rest);
|
|
51
52
|
if (command === 'require-up-to-date')
|
|
52
53
|
return parseRequireUpToDate(rest);
|
|
54
|
+
if (command === 'fresh-base')
|
|
55
|
+
return parseFreshBase(rest);
|
|
53
56
|
if (command === 'gitleaks-directory-scan')
|
|
54
57
|
return parseGitleaksDirectoryScan(rest);
|
|
55
58
|
if (command === 'ast-grep-examples')
|
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 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 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, 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
|
@@ -5,6 +5,7 @@ Commands:
|
|
|
5
5
|
with-host-lock Run a command under a host-wide lock
|
|
6
6
|
gha-runtime-audit Audit successful GitHub Actions job runtimes
|
|
7
7
|
require-up-to-date Require HEAD to include a fetched remote branch
|
|
8
|
+
fresh-base Classify the worktree against a remote base branch
|
|
8
9
|
gitleaks-directory-scan Scan a directory with Gitleaks
|
|
9
10
|
ast-grep-examples Run AST-grep rule examples
|
|
10
11
|
ast-grep-pack Print shipped unconditional AST-grep pack paths as JSON
|
|
@@ -74,6 +75,7 @@ gha-runtime-audit
|
|
|
74
75
|
--push-workflow <name|/regex/> Repeatable
|
|
75
76
|
|
|
76
77
|
require-up-to-date --remote <name> --branch <name>
|
|
78
|
+
fresh-base [--remote <name>] [--branch <name>] [--skip-fetch]
|
|
77
79
|
gitleaks-directory-scan --config <path> [--directory <path>]
|
|
78
80
|
ast-grep-examples --rules <directory> --config <path>
|
|
79
81
|
ast-grep-pack
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export type FreshBaseFetch = 'fresh' | 'skipped' | 'failed';
|
|
2
|
+
export type FreshBaseTree = 'clean' | 'dirty' | 'unknown';
|
|
3
|
+
export type FreshBaseRelation = 'matches' | 'behind' | 'ahead' | 'diverged' | 'unknown';
|
|
4
|
+
export type FreshBaseStatus = 'up-to-date' | 'ahead' | 'behind' | 'diverged' | 'dirty' | 'fetch-failed' | 'unknown';
|
|
5
|
+
export interface FreshBaseInput {
|
|
6
|
+
/** Commits on HEAD that the base lacks; null when the comparison was unavailable. */
|
|
7
|
+
readonly ahead: number | null;
|
|
8
|
+
/** Commits on the base that HEAD lacks; null when the comparison was unavailable. */
|
|
9
|
+
readonly behind: number | null;
|
|
10
|
+
readonly tree: FreshBaseTree;
|
|
11
|
+
readonly fetch: FreshBaseFetch;
|
|
12
|
+
}
|
|
13
|
+
export interface FreshBaseClassification extends FreshBaseInput {
|
|
14
|
+
readonly status: FreshBaseStatus;
|
|
15
|
+
readonly relation: FreshBaseRelation;
|
|
16
|
+
/** True for every status except `up-to-date`. */
|
|
17
|
+
readonly needsAttention: boolean;
|
|
18
|
+
}
|
|
19
|
+
export declare function relationOf(ahead: number | null, behind: number | null): FreshBaseRelation;
|
|
20
|
+
/** Pure classification of a worktree against its base from already-gathered facts. */
|
|
21
|
+
export declare function classifyFreshBase(input: FreshBaseInput): FreshBaseClassification;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
export function relationOf(ahead, behind) {
|
|
2
|
+
if (ahead === null || behind === null)
|
|
3
|
+
return 'unknown';
|
|
4
|
+
if (ahead === 0 && behind === 0)
|
|
5
|
+
return 'matches';
|
|
6
|
+
if (ahead === 0)
|
|
7
|
+
return 'behind';
|
|
8
|
+
if (behind === 0)
|
|
9
|
+
return 'ahead';
|
|
10
|
+
return 'diverged';
|
|
11
|
+
}
|
|
12
|
+
function statusOf(relation, input) {
|
|
13
|
+
// A failed refresh means a cached "matches" is not evidence of freshness.
|
|
14
|
+
if (input.fetch === 'failed')
|
|
15
|
+
return 'fetch-failed';
|
|
16
|
+
if (relation === 'unknown')
|
|
17
|
+
return 'unknown';
|
|
18
|
+
if (relation !== 'matches')
|
|
19
|
+
return relation;
|
|
20
|
+
if (input.tree === 'dirty')
|
|
21
|
+
return 'dirty';
|
|
22
|
+
return input.tree === 'clean' ? 'up-to-date' : 'unknown';
|
|
23
|
+
}
|
|
24
|
+
/** Pure classification of a worktree against its base from already-gathered facts. */
|
|
25
|
+
export function classifyFreshBase(input) {
|
|
26
|
+
const relation = relationOf(input.ahead, input.behind);
|
|
27
|
+
const status = statusOf(relation, input);
|
|
28
|
+
return { ...input, relation, status, needsAttention: status !== 'up-to-date' };
|
|
29
|
+
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export { classifyFreshBase, relationOf } from './classify.mts';
|
|
2
|
+
export type { FreshBaseClassification, FreshBaseFetch, FreshBaseInput, FreshBaseRelation, FreshBaseStatus, FreshBaseTree, } from './classify.mts';
|
|
3
|
+
export { checkFreshBase } from './run.mts';
|
|
4
|
+
export type { FreshBaseOptions, FreshBaseReport, GitResult, GitRunner } from './run.mts';
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { FreshBaseClassification } from './classify.mts';
|
|
2
|
+
export interface GitResult {
|
|
3
|
+
readonly status: number;
|
|
4
|
+
readonly stdout: string;
|
|
5
|
+
}
|
|
6
|
+
export type GitRunner = (args: readonly string[], cwd?: string, timeoutMs?: number) => GitResult;
|
|
7
|
+
export interface FreshBaseOptions {
|
|
8
|
+
readonly remote?: string;
|
|
9
|
+
readonly branch?: string;
|
|
10
|
+
readonly cwd?: string;
|
|
11
|
+
/** Skip the refresh and compare against the cached remote-tracking ref. */
|
|
12
|
+
readonly skipFetch?: boolean;
|
|
13
|
+
readonly fetchTimeoutMs?: number;
|
|
14
|
+
readonly git?: GitRunner;
|
|
15
|
+
}
|
|
16
|
+
export interface FreshBaseReport extends FreshBaseClassification {
|
|
17
|
+
readonly base: string;
|
|
18
|
+
/** Current branch name, or `detached HEAD`. */
|
|
19
|
+
readonly branch: string;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Refreshes the base, then classifies the worktree against it. Returns null outside a Git
|
|
23
|
+
* repository. Git failures never throw; they surface as `fetch-failed` or `unknown`.
|
|
24
|
+
*/
|
|
25
|
+
export declare function checkFreshBase(options?: FreshBaseOptions): FreshBaseReport | null;
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { spawnSync } from 'node:child_process';
|
|
2
|
+
import { classifyFreshBase } from "./classify.mjs";
|
|
3
|
+
const DEFAULT_FETCH_TIMEOUT_MS = 8000;
|
|
4
|
+
const defaultGit = (args, cwd, timeoutMs) => {
|
|
5
|
+
const result = spawnSync('git', [...args], {
|
|
6
|
+
cwd,
|
|
7
|
+
encoding: 'utf8',
|
|
8
|
+
stdio: ['ignore', 'pipe', 'ignore'],
|
|
9
|
+
...(timeoutMs === undefined ? {} : { timeout: timeoutMs }),
|
|
10
|
+
});
|
|
11
|
+
return { status: result.status ?? 1, stdout: result.stdout ?? '' };
|
|
12
|
+
};
|
|
13
|
+
function validateName(value, option) {
|
|
14
|
+
if (value === '' || value.startsWith('-')) {
|
|
15
|
+
throw new Error(`${option} must be a non-option Git name`);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
function countsOf(git, ref, cwd) {
|
|
19
|
+
// A shallow history can hide the ancestry that connects HEAD and the base.
|
|
20
|
+
const shallow = git(['rev-parse', '--is-shallow-repository'], cwd);
|
|
21
|
+
if (shallow.status !== 0 || shallow.stdout.trim() !== 'false') {
|
|
22
|
+
return { ahead: null, behind: null };
|
|
23
|
+
}
|
|
24
|
+
const counts = git(['rev-list', '--left-right', '--count', `${ref}...HEAD`], cwd);
|
|
25
|
+
const [behind, ahead] = counts.stdout.trim().split(/\s+/u).map(Number);
|
|
26
|
+
if (counts.status !== 0 || !Number.isInteger(behind) || !Number.isInteger(ahead)) {
|
|
27
|
+
return { ahead: null, behind: null };
|
|
28
|
+
}
|
|
29
|
+
return { ahead: ahead, behind: behind };
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Refreshes the base, then classifies the worktree against it. Returns null outside a Git
|
|
33
|
+
* repository. Git failures never throw; they surface as `fetch-failed` or `unknown`.
|
|
34
|
+
*/
|
|
35
|
+
export function checkFreshBase(options = {}) {
|
|
36
|
+
const remote = options.remote ?? 'origin';
|
|
37
|
+
const branch = options.branch ?? 'main';
|
|
38
|
+
validateName(remote, 'remote');
|
|
39
|
+
validateName(branch, 'branch');
|
|
40
|
+
const git = options.git ?? defaultGit;
|
|
41
|
+
const { cwd } = options;
|
|
42
|
+
if (git(['rev-parse', '--git-dir'], cwd).status !== 0)
|
|
43
|
+
return null;
|
|
44
|
+
if (git(['check-ref-format', '--branch', branch], cwd).status !== 0) {
|
|
45
|
+
throw new Error('branch must be a valid Git branch name');
|
|
46
|
+
}
|
|
47
|
+
const ref = `refs/remotes/${remote}/${branch}`;
|
|
48
|
+
let fetch = 'skipped';
|
|
49
|
+
if (options.skipFetch !== true) {
|
|
50
|
+
const timeout = options.fetchTimeoutMs ?? DEFAULT_FETCH_TIMEOUT_MS;
|
|
51
|
+
const fetched = git(['fetch', remote, `+refs/heads/${branch}:${ref}`, '--quiet'], cwd, timeout);
|
|
52
|
+
fetch = fetched.status === 0 ? 'fresh' : 'failed';
|
|
53
|
+
}
|
|
54
|
+
const current = git(['branch', '--show-current'], cwd).stdout.trim();
|
|
55
|
+
const status = git(['status', '--porcelain', '--untracked-files=normal'], cwd);
|
|
56
|
+
let tree = 'unknown';
|
|
57
|
+
if (status.status === 0)
|
|
58
|
+
tree = status.stdout.trim() === '' ? 'clean' : 'dirty';
|
|
59
|
+
const available = git(['rev-parse', '--verify', '--quiet', ref], cwd).status === 0;
|
|
60
|
+
const counts = available ? countsOf(git, ref, cwd) : { ahead: null, behind: null };
|
|
61
|
+
return {
|
|
62
|
+
...classifyFreshBase({ ...counts, tree, fetch }),
|
|
63
|
+
base: `${remote}/${branch}`,
|
|
64
|
+
branch: current === '' ? 'detached HEAD' : current,
|
|
65
|
+
};
|
|
66
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
export type ClosingIssueReference = {
|
|
2
|
+
key: string;
|
|
3
|
+
number: number;
|
|
4
|
+
owner: string | undefined;
|
|
5
|
+
repo: string | undefined;
|
|
6
|
+
};
|
|
7
|
+
/**
|
|
8
|
+
* GitHub closing keywords (close/closes/closed, fix/fixes/fixed, resolve/resolves/resolved,
|
|
9
|
+
* optionally followed by a colon) followed by `#N`, `owner/repo#N` or a
|
|
10
|
+
* `https://github.com/owner/repo/issues/N` URL. Owner and repo are lowercased and references are
|
|
11
|
+
* deduplicated by key, in first-seen order.
|
|
12
|
+
*
|
|
13
|
+
* The body is scanned as raw text: closing keywords inside HTML comments or code spans/fences (for
|
|
14
|
+
* example a PR template example) are reported too. Callers that must ignore them blank those spans
|
|
15
|
+
* out before calling, as `parseNonClosingRefs` callers do with their scoped text.
|
|
16
|
+
*/
|
|
17
|
+
export declare function parseClosingIssueReferences(body: string): ClosingIssueReference[];
|
|
18
|
+
export declare function hasClosingIssueReference(body: string): boolean;
|
|
19
|
+
/** `#N` for a same-repository reference, otherwise lowercased `owner/repo#N`. */
|
|
20
|
+
export declare function formatReferenceKey(ref: {
|
|
21
|
+
number: number;
|
|
22
|
+
owner?: string | undefined;
|
|
23
|
+
repo?: string | undefined;
|
|
24
|
+
}): string;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
const CLOSING_ISSUE_RE = /\b(?:close(?:s|d)?|fix(?:es|ed)?|resolve(?:s|d)?):?[ \t]+(?:(?<repo>[\w.-]+\/[\w.-]+)?#(?<number>\d+)|https?:\/\/github\.com\/(?<urlRepo>[\w.-]+\/[\w.-]+)\/issues\/(?<urlNumber>\d+))\b/gi;
|
|
2
|
+
/**
|
|
3
|
+
* GitHub closing keywords (close/closes/closed, fix/fixes/fixed, resolve/resolves/resolved,
|
|
4
|
+
* optionally followed by a colon) followed by `#N`, `owner/repo#N` or a
|
|
5
|
+
* `https://github.com/owner/repo/issues/N` URL. Owner and repo are lowercased and references are
|
|
6
|
+
* deduplicated by key, in first-seen order.
|
|
7
|
+
*
|
|
8
|
+
* The body is scanned as raw text: closing keywords inside HTML comments or code spans/fences (for
|
|
9
|
+
* example a PR template example) are reported too. Callers that must ignore them blank those spans
|
|
10
|
+
* out before calling, as `parseNonClosingRefs` callers do with their scoped text.
|
|
11
|
+
*/
|
|
12
|
+
export function parseClosingIssueReferences(body) {
|
|
13
|
+
const refs = [];
|
|
14
|
+
const seen = new Set();
|
|
15
|
+
for (const match of body.matchAll(CLOSING_ISSUE_RE)) {
|
|
16
|
+
const number = Number(match.groups?.number ?? match.groups?.urlNumber);
|
|
17
|
+
if (!Number.isSafeInteger(number) || number < 1)
|
|
18
|
+
continue;
|
|
19
|
+
const repoRef = (match.groups?.repo ?? match.groups?.urlRepo)?.toLowerCase();
|
|
20
|
+
const [owner, repo] = repoRef?.split('/') ?? [undefined, undefined];
|
|
21
|
+
const key = formatReferenceKey({ number, owner, repo });
|
|
22
|
+
if (seen.has(key))
|
|
23
|
+
continue;
|
|
24
|
+
seen.add(key);
|
|
25
|
+
refs.push({ key, number, owner, repo });
|
|
26
|
+
}
|
|
27
|
+
return refs;
|
|
28
|
+
}
|
|
29
|
+
export function hasClosingIssueReference(body) {
|
|
30
|
+
return parseClosingIssueReferences(body).length > 0;
|
|
31
|
+
}
|
|
32
|
+
/** `#N` for a same-repository reference, otherwise lowercased `owner/repo#N`. */
|
|
33
|
+
export function formatReferenceKey(ref) {
|
|
34
|
+
return ref.owner !== undefined && ref.repo !== undefined
|
|
35
|
+
? `${ref.owner.toLowerCase()}/${ref.repo.toLowerCase()}#${ref.number}`
|
|
36
|
+
: `#${ref.number}`;
|
|
37
|
+
}
|
package/dist/gh-cli/index.d.mts
CHANGED
|
@@ -6,3 +6,6 @@ export { GITHUB_BODY_MAX_CHARACTERS, validateGitHubBodyLength } from './body-len
|
|
|
6
6
|
export type { GitHubBodyLengthValidation } from './body-length.mts';
|
|
7
7
|
export { assertHeadPushed, buildGhPrCreateArgs, createPullRequest, DetachedHeadError, HeadNotPushedError, HeadOutOfDateError, resolveHeadBranch, } from './pr-create.mts';
|
|
8
8
|
export type { AssertHeadPushedOptions, BuildGhPrCreateArgsOptions, CreatePullRequestExecutors, CreatePullRequestOptions, } from './pr-create.mts';
|
|
9
|
+
export { formatReferenceKey, hasClosingIssueReference, parseClosingIssueReferences, } from './closing-refs.mts';
|
|
10
|
+
export type { ClosingIssueReference } from './closing-refs.mts';
|
|
11
|
+
export { parseNonClosingRefs } from './non-closing-refs.mts';
|
package/dist/gh-cli/index.mjs
CHANGED
|
@@ -2,3 +2,5 @@ export { createCommandRunner, runGh, runGit } from "./exec.mjs";
|
|
|
2
2
|
export { DiffCommandError, processDiffCommand } from "./diff.mjs";
|
|
3
3
|
export { GITHUB_BODY_MAX_CHARACTERS, validateGitHubBodyLength } from "./body-length.mjs";
|
|
4
4
|
export { assertHeadPushed, buildGhPrCreateArgs, createPullRequest, DetachedHeadError, HeadNotPushedError, HeadOutOfDateError, resolveHeadBranch, } from "./pr-create.mjs";
|
|
5
|
+
export { formatReferenceKey, hasClosingIssueReference, parseClosingIssueReferences, } from "./closing-refs.mjs";
|
|
6
|
+
export { parseNonClosingRefs } from "./non-closing-refs.mjs";
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Collects non-closing references (`Ref`, `Refs`, `Part of`, optionally followed by a colon) from
|
|
3
|
+
* `referenceText`. Returns reference keys (`#N` or lowercased `owner/repo#N`, the format
|
|
4
|
+
* `formatReferenceKey` produces) rather than bare numbers, so a `Refs owner/other-repo#42` cannot
|
|
5
|
+
* disposition a same-numbered local `#42`; callers match by key.
|
|
6
|
+
*
|
|
7
|
+
* The caller decides which text counts: pass only the section that carries issue dispositions (for
|
|
8
|
+
* example a "Related issues" section with code, comments and HTML blanked out), so an incidental
|
|
9
|
+
* `Refs #N` elsewhere does not satisfy an audit.
|
|
10
|
+
*/
|
|
11
|
+
export declare function parseNonClosingRefs(referenceText: string): Set<string>;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { formatReferenceKey } from "./closing-refs.mjs";
|
|
2
|
+
const NON_CLOSING_REF_RE = /\b(?:refs?|part\s+of):?[ \t]+(?:(?<ownerRepo>[\w.-]+\/[\w.-]+))?#(?<number>\d+)\b/gi;
|
|
3
|
+
/**
|
|
4
|
+
* Collects non-closing references (`Ref`, `Refs`, `Part of`, optionally followed by a colon) from
|
|
5
|
+
* `referenceText`. Returns reference keys (`#N` or lowercased `owner/repo#N`, the format
|
|
6
|
+
* `formatReferenceKey` produces) rather than bare numbers, so a `Refs owner/other-repo#42` cannot
|
|
7
|
+
* disposition a same-numbered local `#42`; callers match by key.
|
|
8
|
+
*
|
|
9
|
+
* The caller decides which text counts: pass only the section that carries issue dispositions (for
|
|
10
|
+
* example a "Related issues" section with code, comments and HTML blanked out), so an incidental
|
|
11
|
+
* `Refs #N` elsewhere does not satisfy an audit.
|
|
12
|
+
*/
|
|
13
|
+
export function parseNonClosingRefs(referenceText) {
|
|
14
|
+
const keys = new Set();
|
|
15
|
+
for (const match of referenceText.matchAll(NON_CLOSING_REF_RE)) {
|
|
16
|
+
const number = Number(match.groups?.number);
|
|
17
|
+
if (!Number.isSafeInteger(number) || number < 1)
|
|
18
|
+
continue;
|
|
19
|
+
const ownerRepo = match.groups?.ownerRepo?.toLowerCase();
|
|
20
|
+
const [owner, repo] = ownerRepo?.split('/') ?? [undefined, undefined];
|
|
21
|
+
keys.add(formatReferenceKey({ number, owner, repo }));
|
|
22
|
+
}
|
|
23
|
+
return keys;
|
|
24
|
+
}
|
package/dist/index.d.mts
CHANGED
|
@@ -19,6 +19,8 @@ export { auditCiJobRuntime, parseWorkflowNameMatch } from './gha-runtime-audit/i
|
|
|
19
19
|
export type { GhApiExecutor, RuntimeAuditOptions, RuntimeAuditResult, RuntimeAuditWorkflowFilter, RuntimeJobResult, RuntimeSample, } from './gha-runtime-audit/index.mts';
|
|
20
20
|
export { checkGhaWorkspacePolicy } from './gha-workspace-policy/index.mts';
|
|
21
21
|
export type { GhaWorkspacePolicyOptions } from './gha-workspace-policy/index.mts';
|
|
22
|
+
export { checkFreshBase, classifyFreshBase, relationOf } from './fresh-base/index.mts';
|
|
23
|
+
export type { FreshBaseClassification, FreshBaseFetch, FreshBaseInput, FreshBaseOptions, FreshBaseRelation, FreshBaseReport, FreshBaseStatus, FreshBaseTree, GitResult, GitRunner, } from './fresh-base/index.mts';
|
|
22
24
|
export { requireUpToDate } from './require-up-to-date/index.mts';
|
|
23
25
|
export type { GitExecutor, RequireUpToDateOptions } from './require-up-to-date/index.mts';
|
|
24
26
|
export { gitleaksDirectoryScanArguments, runGitleaksDirectoryScan, } from './gitleaks-directory-scan/index.mts';
|
|
@@ -101,3 +103,7 @@ export { GITHUB_BODY_MAX_CHARACTERS, validateGitHubBodyLength } from './gh-cli/i
|
|
|
101
103
|
export type { GitHubBodyLengthValidation } from './gh-cli/index.mts';
|
|
102
104
|
export { assertHeadPushed, buildGhPrCreateArgs, createPullRequest, DetachedHeadError, HeadNotPushedError, HeadOutOfDateError, resolveHeadBranch, } from './gh-cli/index.mts';
|
|
103
105
|
export type { AssertHeadPushedOptions, BuildGhPrCreateArgsOptions, CreatePullRequestExecutors, CreatePullRequestOptions, } from './gh-cli/index.mts';
|
|
106
|
+
export { formatReferenceKey, hasClosingIssueReference, parseClosingIssueReferences, parseNonClosingRefs, } from './gh-cli/index.mts';
|
|
107
|
+
export type { ClosingIssueReference } from './gh-cli/index.mts';
|
|
108
|
+
export { countDiffLineChanges, DEFAULT_LARGE_DIFF_THRESHOLDS, exceedsLargeDiffThreshold, } from './pull-request-diff/index.mts';
|
|
109
|
+
export type { DiffLineChanges, LargeDiffThresholds } from './pull-request-diff/index.mts';
|
package/dist/index.mjs
CHANGED
|
@@ -10,6 +10,7 @@ export { extractCreateTableMetadata, extractDefaultFunction, extractFuncCallArgC
|
|
|
10
10
|
export { dollarQuoteEnd, lineOf, maskSqlQuotedText, readDollarQuoteDelimiter, readStringLiteral, splitSqlStatements, sqlFragments, stripSqlComments, } from "./sql-scanner/index.mjs";
|
|
11
11
|
export { auditCiJobRuntime, parseWorkflowNameMatch } from "./gha-runtime-audit/index.mjs";
|
|
12
12
|
export { checkGhaWorkspacePolicy } from "./gha-workspace-policy/index.mjs";
|
|
13
|
+
export { checkFreshBase, classifyFreshBase, relationOf } from "./fresh-base/index.mjs";
|
|
13
14
|
export { requireUpToDate } from "./require-up-to-date/index.mjs";
|
|
14
15
|
export { gitleaksDirectoryScanArguments, runGitleaksDirectoryScan, } from "./gitleaks-directory-scan/index.mjs";
|
|
15
16
|
export { astGrepExamplesArguments, compareAstGrepCompanions, runAstGrepExamples, } from "./ast-grep-examples/index.mjs";
|
|
@@ -55,3 +56,5 @@ export { createCommandRunner, runGh, runGit } from "./gh-cli/index.mjs";
|
|
|
55
56
|
export { DiffCommandError, processDiffCommand } from "./gh-cli/index.mjs";
|
|
56
57
|
export { GITHUB_BODY_MAX_CHARACTERS, validateGitHubBodyLength } from "./gh-cli/index.mjs";
|
|
57
58
|
export { assertHeadPushed, buildGhPrCreateArgs, createPullRequest, DetachedHeadError, HeadNotPushedError, HeadOutOfDateError, resolveHeadBranch, } from "./gh-cli/index.mjs";
|
|
59
|
+
export { formatReferenceKey, hasClosingIssueReference, parseClosingIssueReferences, parseNonClosingRefs, } from "./gh-cli/index.mjs";
|
|
60
|
+
export { countDiffLineChanges, DEFAULT_LARGE_DIFF_THRESHOLDS, exceedsLargeDiffThreshold, } from "./pull-request-diff/index.mjs";
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
export type DiffLineChanges = {
|
|
2
|
+
added: number;
|
|
3
|
+
deleted: number;
|
|
4
|
+
};
|
|
5
|
+
export type LargeDiffThresholds = {
|
|
6
|
+
/** A diff adding strictly more lines than this is large. */
|
|
7
|
+
addedLines: number;
|
|
8
|
+
/** A diff deleting strictly more lines than this is large. */
|
|
9
|
+
deletedLines: number;
|
|
10
|
+
};
|
|
11
|
+
/**
|
|
12
|
+
* Example thresholds for a "decide whether to split this PR" gate: additions are the trigger for a
|
|
13
|
+
* split, while deletion-heavy retirement work stays coherent at a larger limit. Callers pass their
|
|
14
|
+
* own `LargeDiffThresholds`; these are only a default.
|
|
15
|
+
*/
|
|
16
|
+
export declare const DEFAULT_LARGE_DIFF_THRESHOLDS: Readonly<LargeDiffThresholds>;
|
|
17
|
+
/**
|
|
18
|
+
* Added + removed lines in a unified diff (`git diff <base>...HEAD` output), excluding the
|
|
19
|
+
* `+++`/`---` file-header lines so renamed/moved files don't double-count their path lines.
|
|
20
|
+
*
|
|
21
|
+
* Known limitation: this matches the literal `+++`/`---` prefix, not "is this actually a file
|
|
22
|
+
* header." An added line whose content starts with `++`, or a removed line whose content starts
|
|
23
|
+
* with `--` (a SQL comment, a CLI flag), renders as `+++`/`---` once the diff marker is prepended
|
|
24
|
+
* and is silently excluded from the count. For a coarse "probably needs a split" heuristic with an
|
|
25
|
+
* explicit override, that undercount is acceptable; this is not a full hunk parser.
|
|
26
|
+
*/
|
|
27
|
+
export declare function countDiffLineChanges(diffText: string): DiffLineChanges;
|
|
28
|
+
/** True when either count is strictly greater than its threshold. */
|
|
29
|
+
export declare function exceedsLargeDiffThreshold(changes: DiffLineChanges, thresholds?: LargeDiffThresholds): boolean;
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Example thresholds for a "decide whether to split this PR" gate: additions are the trigger for a
|
|
3
|
+
* split, while deletion-heavy retirement work stays coherent at a larger limit. Callers pass their
|
|
4
|
+
* own `LargeDiffThresholds`; these are only a default.
|
|
5
|
+
*/
|
|
6
|
+
export const DEFAULT_LARGE_DIFF_THRESHOLDS = Object.freeze({
|
|
7
|
+
addedLines: 5000,
|
|
8
|
+
deletedLines: 20_000,
|
|
9
|
+
});
|
|
10
|
+
/**
|
|
11
|
+
* Added + removed lines in a unified diff (`git diff <base>...HEAD` output), excluding the
|
|
12
|
+
* `+++`/`---` file-header lines so renamed/moved files don't double-count their path lines.
|
|
13
|
+
*
|
|
14
|
+
* Known limitation: this matches the literal `+++`/`---` prefix, not "is this actually a file
|
|
15
|
+
* header." An added line whose content starts with `++`, or a removed line whose content starts
|
|
16
|
+
* with `--` (a SQL comment, a CLI flag), renders as `+++`/`---` once the diff marker is prepended
|
|
17
|
+
* and is silently excluded from the count. For a coarse "probably needs a split" heuristic with an
|
|
18
|
+
* explicit override, that undercount is acceptable; this is not a full hunk parser.
|
|
19
|
+
*/
|
|
20
|
+
export function countDiffLineChanges(diffText) {
|
|
21
|
+
const changes = { added: 0, deleted: 0 };
|
|
22
|
+
for (const line of diffText.split('\n')) {
|
|
23
|
+
if (line.startsWith('+++') || line.startsWith('---'))
|
|
24
|
+
continue;
|
|
25
|
+
if (line.startsWith('+'))
|
|
26
|
+
changes.added++;
|
|
27
|
+
else if (line.startsWith('-'))
|
|
28
|
+
changes.deleted++;
|
|
29
|
+
}
|
|
30
|
+
return changes;
|
|
31
|
+
}
|
|
32
|
+
/** True when either count is strictly greater than its threshold. */
|
|
33
|
+
export function exceedsLargeDiffThreshold(changes, thresholds = DEFAULT_LARGE_DIFF_THRESHOLDS) {
|
|
34
|
+
return changes.added > thresholds.addedLines || changes.deleted > thresholds.deletedLines;
|
|
35
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { countDiffLineChanges, DEFAULT_LARGE_DIFF_THRESHOLDS, exceedsLargeDiffThreshold, } from "./diff-size.mjs";
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export type DistillClassifyArgs = {
|
|
2
|
+
partitionPath: string;
|
|
3
|
+
retroCutoff: string;
|
|
4
|
+
sessionCutoff: string;
|
|
5
|
+
};
|
|
6
|
+
/**
|
|
7
|
+
* Parses `<partition.jsonl> --retro-cutoff <iso> --session-cutoff <iso>`.
|
|
8
|
+
*
|
|
9
|
+
* The classifier never computes its own cutoffs: the eligibility rule is evaluated against
|
|
10
|
+
* whatever `retroCutoff`/`sessionCutoff` the caller passes in, so a classifier run always matches
|
|
11
|
+
* the caller's own idea of "eligible" for that same run.
|
|
12
|
+
*/
|
|
13
|
+
export declare function parseDistillClassifyArgs(argv: string[]): DistillClassifyArgs;
|