vouchington-tooling 0.1.3 → 0.1.5
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 +6 -0
- package/dist/cli/index.mjs +4 -0
- package/dist/cli/parse.d.mts +1 -1
- package/dist/cli/parse.mjs +1 -0
- package/dist/cli/usage.d.mts +1 -1
- package/dist/cli/usage.mjs +3 -1
- package/dist/gha-post-review/post.mjs +2 -9
- package/dist/gha-review-payload/index.d.mts +1 -1
- package/dist/gha-review-payload/index.mjs +1 -1
- package/dist/gha-review-payload/payload.d.mts +1 -3
- package/dist/gha-review-payload/payload.mjs +15 -31
- package/dist/gha-review-payload/remap.d.mts +1 -1
- package/dist/gha-review-payload/remap.mjs +7 -16
- package/dist/index.d.mts +1 -1
- package/dist/index.mjs +1 -1
- package/dist/retrospective-transcript/codex-messages.d.mts +8 -0
- package/dist/retrospective-transcript/codex-messages.mjs +66 -0
- package/dist/retrospective-transcript/codex.mjs +9 -4
- package/package.json +2 -2
- package/scripts/gha/download-optional-run-artifacts.sh +126 -0
- package/scripts/gha/install-github-release.sh +17 -4
- package/skills/github-issue/SKILL.md +52 -14
- package/skills/organize-github-issues/SKILL.md +6 -3
- package/skills/retrospective-distill/SKILL.md +3 -1
- package/skills/review-github-issue-taxonomy/SKILL.md +7 -2
- package/skills/revisit-followups/SKILL.md +4 -2
package/README.md
CHANGED
|
@@ -20,6 +20,7 @@ vouchington gha-runtime-audit --pr-workflow CI --push-workflow '/^Main CI \\(.+\
|
|
|
20
20
|
vouchington gha-output name
|
|
21
21
|
vouchington gha-needs-results
|
|
22
22
|
vouchington download-with-diagnostics <url> <destination>
|
|
23
|
+
vouchington download-optional-run-artifacts --pattern 'coverage-*' --dir ./coverage-fallback
|
|
23
24
|
vouchington host-pressure-diagnostics
|
|
24
25
|
vouchington allocate-browser-safe-ports 2 --policy ./policy.json --forbidden-ports ./ports.json
|
|
25
26
|
vouchington diagnose-port-collision --ports "2200 2216"
|
|
@@ -46,6 +47,11 @@ vouchington post-review
|
|
|
46
47
|
vouchington stage-review-payload optional|required <source> <destination>
|
|
47
48
|
```
|
|
48
49
|
|
|
50
|
+
`download-optional-run-artifacts` uses the current Actions run and host. Pattern mode discovers
|
|
51
|
+
non-expired artifacts across the run, keeps the first result for each name (matching `gh run
|
|
52
|
+
download`), and extracts each selected name into its own directory. Ordinary absence is reported as
|
|
53
|
+
`availability=unavailable`; invalid names and cancellation remain hard failures.
|
|
54
|
+
|
|
49
55
|
`retrospective-transcript` discovers Codex and Claude transcripts by default. It also reads a
|
|
50
56
|
Claude-compatible transcript when `CURSOR_SESSION_ID` is set, and Grok's `updates.jsonl` session
|
|
51
57
|
layout when `GROK_SESSION_ID` is set. Use `--grok-sessions-dir` to point discovery at a nondefault
|
package/dist/cli/index.mjs
CHANGED
|
@@ -26,6 +26,10 @@ const SCRIPT_PATHS = {
|
|
|
26
26
|
command: 'bash',
|
|
27
27
|
path: 'scripts/gha/download-with-diagnostics.sh',
|
|
28
28
|
},
|
|
29
|
+
'download-optional-run-artifacts': {
|
|
30
|
+
command: 'bash',
|
|
31
|
+
path: 'scripts/gha/download-optional-run-artifacts.sh',
|
|
32
|
+
},
|
|
29
33
|
'host-pressure-diagnostics': {
|
|
30
34
|
command: 'bash',
|
|
31
35
|
path: 'scripts/gha/host-pressure-diagnostics.sh',
|
package/dist/cli/parse.d.mts
CHANGED
|
@@ -44,5 +44,5 @@ export type ParsedCli = {
|
|
|
44
44
|
kind: 'retrospective-transcript';
|
|
45
45
|
args: string[];
|
|
46
46
|
} | ParsedGhaRuntimeAudit | ParsedGhaArtifactsCleanup;
|
|
47
|
-
export type ScriptCommand = 'gha-output' | 'gha-needs-results' | 'download-with-diagnostics' | 'host-pressure-diagnostics' | 'allocate-browser-safe-ports' | 'diagnose-port-collision' | 'prepare-trivy-db' | 'check-cache-size' | 'make-shard-matrix' | 'load-runner-env' | 'clean-workspace' | 'install-github-release' | 'run-with-timeout' | 'lint-links' | 'materialize-pr-context' | 'wait-for-apt-locks' | 'install-playwright-chromium-arm64' | 'ghcr-package-retention';
|
|
47
|
+
export type ScriptCommand = 'gha-output' | 'gha-needs-results' | 'download-with-diagnostics' | 'download-optional-run-artifacts' | 'host-pressure-diagnostics' | 'allocate-browser-safe-ports' | 'diagnose-port-collision' | 'prepare-trivy-db' | 'check-cache-size' | 'make-shard-matrix' | 'load-runner-env' | 'clean-workspace' | 'install-github-release' | 'run-with-timeout' | 'lint-links' | 'materialize-pr-context' | 'wait-for-apt-locks' | 'install-playwright-chromium-arm64' | 'ghcr-package-retention';
|
|
48
48
|
export declare function parseCli(argv: readonly string[]): ParsedCli;
|
package/dist/cli/parse.mjs
CHANGED
package/dist/cli/usage.d.mts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
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 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 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 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 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\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 --pr-workflow <name|/regex/> Repeatable\n --push-workflow <name|/regex/> Repeatable\n\ngha-output <name>\ngha-needs-results [label]\ndownload-with-diagnostics <url> <destination> [-- curl-args...]\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]\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] [--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>...\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]\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 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 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 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\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 --pr-workflow <name|/regex/> Repeatable\n --push-workflow <name|/regex/> Repeatable\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]\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>...\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]\n";
|
|
2
2
|
export declare function printUsage(stream?: NodeJS.WritableStream): void;
|
package/dist/cli/usage.mjs
CHANGED
|
@@ -7,6 +7,7 @@ Commands:
|
|
|
7
7
|
gha-output Write a collision-safe multiline GITHUB_OUTPUT record
|
|
8
8
|
gha-needs-results Fail if required GitHub Actions job results failed
|
|
9
9
|
download-with-diagnostics Download a URL and report HTTP status on failure
|
|
10
|
+
download-optional-run-artifacts Download optional artifacts from the current run
|
|
10
11
|
host-pressure-diagnostics Print a bounded host memory/OOM/PSI snapshot
|
|
11
12
|
allocate-browser-safe-ports Allocate Fetch-safe localhost ports
|
|
12
13
|
diagnose-port-collision Capture bounded localhost port diagnostics
|
|
@@ -59,6 +60,7 @@ gha-runtime-audit
|
|
|
59
60
|
gha-output <name>
|
|
60
61
|
gha-needs-results [label]
|
|
61
62
|
download-with-diagnostics <url> <destination> [-- curl-args...]
|
|
63
|
+
download-optional-run-artifacts (--name <name> | --pattern <pattern>) --dir <directory>
|
|
62
64
|
host-pressure-diagnostics
|
|
63
65
|
allocate-browser-safe-ports [count] [--policy path] [--forbidden-ports path]
|
|
64
66
|
diagnose-port-collision [--ports "2200 2216"] [--output-dir PATH]
|
|
@@ -72,7 +74,7 @@ check-cache-size <path> <max-bytes> <label>
|
|
|
72
74
|
make-shard-matrix <total>
|
|
73
75
|
load-runner-env
|
|
74
76
|
clean-workspace
|
|
75
|
-
install-github-release --repo owner/name --version X --asset 'name-{platform}.tar.gz' --bin name [--tag-prefix PREFIX] [--no-checksum] [--checksums-asset NAME] [--version-flag FLAG] [--bin-dir DIR]
|
|
77
|
+
install-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]
|
|
76
78
|
run-with-timeout <timeout-seconds> <kill-after-seconds> <command...>
|
|
77
79
|
lint-links [--offline] [--config PATH] [--glob PATTERN] [files...]
|
|
78
80
|
materialize-pr-context
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ReviewPayloadError,
|
|
1
|
+
import { ReviewPayloadError, indexReviewFiles, parseReviewPayload, remapReviewComments, } from '../gha-review-payload/index.mjs';
|
|
2
2
|
export { MAX_REVIEW_COMMENTS as MAX_COMMENTS, MAX_REVIEW_PAYLOAD_BYTES as MAX_PAYLOAD_BYTES, ReviewPayloadError as PostReviewError, } from '../gha-review-payload/index.mjs';
|
|
3
3
|
export function runPostReview(payloadPath, io) {
|
|
4
4
|
try {
|
|
@@ -12,14 +12,7 @@ export function runPostReview(payloadPath, io) {
|
|
|
12
12
|
const first = io.postReview(review);
|
|
13
13
|
if (first.ok)
|
|
14
14
|
return { posted: true };
|
|
15
|
-
|
|
16
|
-
throw new ReviewPayloadError(`GitHub review POST failed (HTTP ${first.status}).`);
|
|
17
|
-
}
|
|
18
|
-
const retry = io.postReview(bodyOnlyReviewFallback(review, first.status));
|
|
19
|
-
if (!retry.ok) {
|
|
20
|
-
throw new ReviewPayloadError(`GitHub review POST retry failed (HTTP ${retry.status}).`);
|
|
21
|
-
}
|
|
22
|
-
return { posted: true };
|
|
15
|
+
throw new ReviewPayloadError(`GitHub review POST failed (HTTP ${first.status}).`);
|
|
23
16
|
}
|
|
24
17
|
finally {
|
|
25
18
|
io.removeFile(payloadPath);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { MAX_REVIEW_COMMENTS, MAX_REVIEW_PAYLOAD_BYTES, ReviewPayloadError,
|
|
1
|
+
export { MAX_REVIEW_COMMENTS, MAX_REVIEW_PAYLOAD_BYTES, ReviewPayloadError, parseReviewPayload, reviewCommentSubject, } from './payload.mts';
|
|
2
2
|
export type { ReviewComment, ReviewSide, SanitizedReview } from './payload.mts';
|
|
3
3
|
export { indexReviewFiles, parsePatchCommentable, parseReviewFilesJson } from './diff.mts';
|
|
4
4
|
export type { CommentableIndex, CommentableLine, LineKind, ReviewFile } from './diff.mts';
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { MAX_REVIEW_COMMENTS, MAX_REVIEW_PAYLOAD_BYTES, ReviewPayloadError,
|
|
1
|
+
export { MAX_REVIEW_COMMENTS, MAX_REVIEW_PAYLOAD_BYTES, ReviewPayloadError, parseReviewPayload, reviewCommentSubject, } from './payload.mjs';
|
|
2
2
|
export { indexReviewFiles, parsePatchCommentable, parseReviewFilesJson } from './diff.mjs';
|
|
3
3
|
export { nearestReviewLine, remapReviewComments, rewriteSnappedSuggestion, snapReviewNote, } from './remap.mjs';
|
|
4
4
|
export { readRegularReviewPayload, stageReviewPayload, writeStagedOutput } from './file.mjs';
|
|
@@ -23,8 +23,6 @@ export type SanitizedReview = {
|
|
|
23
23
|
export declare function reviewCommentSubject(comment: ReviewComment): string;
|
|
24
24
|
/**
|
|
25
25
|
* Parses untrusted JSON into the exact review wire shape accepted by the caller's poster.
|
|
26
|
-
* Unknown fields
|
|
26
|
+
* Unknown fields are ignored, malformed findings fail closed, and the supplied commit id wins.
|
|
27
27
|
*/
|
|
28
28
|
export declare function parseReviewPayload(bytes: Buffer, commitId: string): SanitizedReview;
|
|
29
|
-
/** Builds the body-only fallback used when inline review placement is rejected. */
|
|
30
|
-
export declare function bodyOnlyReviewFallback(review: SanitizedReview, status: number): SanitizedReview;
|
|
@@ -49,7 +49,7 @@ export function reviewCommentSubject(comment) {
|
|
|
49
49
|
}
|
|
50
50
|
/**
|
|
51
51
|
* Parses untrusted JSON into the exact review wire shape accepted by the caller's poster.
|
|
52
|
-
* Unknown fields
|
|
52
|
+
* Unknown fields are ignored, malformed findings fail closed, and the supplied commit id wins.
|
|
53
53
|
*/
|
|
54
54
|
export function parseReviewPayload(bytes, commitId) {
|
|
55
55
|
if (!isCommitId(commitId)) {
|
|
@@ -70,42 +70,26 @@ export function parseReviewPayload(bytes, commitId) {
|
|
|
70
70
|
}
|
|
71
71
|
const record = parsed;
|
|
72
72
|
const body = typeof record.body === 'string' ? record.body : '';
|
|
73
|
-
|
|
74
|
-
|
|
73
|
+
if (record.comments !== undefined && !Array.isArray(record.comments)) {
|
|
74
|
+
throw new ReviewPayloadError('comments must be an array when provided.');
|
|
75
|
+
}
|
|
76
|
+
const rawComments = record.comments ?? [];
|
|
77
|
+
if (rawComments.length > MAX_REVIEW_COMMENTS) {
|
|
78
|
+
throw new ReviewPayloadError(`Payload has more than ${MAX_REVIEW_COMMENTS} inline comments.`);
|
|
79
|
+
}
|
|
80
|
+
const comments = rawComments.map((comment) => {
|
|
75
81
|
const sanitized = sanitizeComment(comment);
|
|
76
|
-
|
|
82
|
+
if (!sanitized)
|
|
83
|
+
throw new ReviewPayloadError('Every finding must be a valid inline comment.');
|
|
84
|
+
return sanitized;
|
|
77
85
|
});
|
|
78
|
-
|
|
79
|
-
const overflow = valid.slice(MAX_REVIEW_COMMENTS);
|
|
80
|
-
const parts = [body];
|
|
81
|
-
if (overflow.length > 0) {
|
|
82
|
-
parts.push(`## Comments over the ${MAX_REVIEW_COMMENTS}-comment cap`, ...overflow.map((comment) => `- ${reviewCommentSubject(comment)}`));
|
|
83
|
-
}
|
|
84
|
-
const reviewBody = parts.filter((part) => part.length > 0).join('\n\n');
|
|
85
|
-
if (reviewBody.length === 0 && kept.length === 0) {
|
|
86
|
+
if (body.length === 0 && comments.length === 0) {
|
|
86
87
|
throw new ReviewPayloadError('Payload has no review body and no valid comments.');
|
|
87
88
|
}
|
|
88
89
|
return {
|
|
89
90
|
event: 'COMMENT',
|
|
90
91
|
commit_id: commitId,
|
|
91
|
-
body:
|
|
92
|
-
comments
|
|
93
|
-
};
|
|
94
|
-
}
|
|
95
|
-
/** Builds the body-only fallback used when inline review placement is rejected. */
|
|
96
|
-
export function bodyOnlyReviewFallback(review, status) {
|
|
97
|
-
const listed = review.comments.length === 0
|
|
98
|
-
? []
|
|
99
|
-
: [
|
|
100
|
-
'## Inline findings not posted',
|
|
101
|
-
`The inline comments were rejected (HTTP ${status}). The findings were:`,
|
|
102
|
-
...review.comments.map((comment) => `- ${reviewCommentSubject(comment)}`),
|
|
103
|
-
];
|
|
104
|
-
const body = [review.body, ...listed].filter((part) => part.length > 0).join('\n\n');
|
|
105
|
-
return {
|
|
106
|
-
event: 'COMMENT',
|
|
107
|
-
commit_id: review.commit_id,
|
|
108
|
-
body: body.length > 0 ? body : `Inline findings not posted (HTTP ${status}).`,
|
|
109
|
-
comments: [],
|
|
92
|
+
body: body.length > 0 ? body : 'Inline findings only.',
|
|
93
|
+
comments,
|
|
110
94
|
};
|
|
111
95
|
}
|
|
@@ -9,5 +9,5 @@ export declare function nearestReviewLine(candidates: Array<{
|
|
|
9
9
|
line: number;
|
|
10
10
|
kind: LineKind;
|
|
11
11
|
} | undefined;
|
|
12
|
-
/** Places
|
|
12
|
+
/** Places every finding on a commentable diff line and fails if any finding cannot be placed. */
|
|
13
13
|
export declare function remapReviewComments(review: SanitizedReview, index: CommentableIndex): SanitizedReview;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { ReviewPayloadError } from './payload.mjs';
|
|
2
2
|
export function snapReviewNote(path, line) {
|
|
3
3
|
return `_Regarding \`${path}:${line}\` (not in the diff hunk; posted on the nearest commentable line)._`;
|
|
4
4
|
}
|
|
@@ -62,28 +62,19 @@ function placeComment(comment, index) {
|
|
|
62
62
|
return withSnap({ ...target, side: alt, line: nearAlt.line }, originalPath, originalLine);
|
|
63
63
|
return null;
|
|
64
64
|
}
|
|
65
|
-
/** Places
|
|
65
|
+
/** Places every finding on a commentable diff line and fails if any finding cannot be placed. */
|
|
66
66
|
export function remapReviewComments(review, index) {
|
|
67
67
|
const kept = [];
|
|
68
|
-
const dropped = [];
|
|
69
68
|
for (const comment of review.comments) {
|
|
70
69
|
const placed = placeComment(comment, index);
|
|
71
|
-
if (placed)
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
70
|
+
if (!placed) {
|
|
71
|
+
throw new ReviewPayloadError(`Inline finding cannot be placed on the pull request diff: ${comment.path}:${comment.line}.`);
|
|
72
|
+
}
|
|
73
|
+
kept.push(placed);
|
|
75
74
|
}
|
|
76
|
-
const extras = dropped.length === 0
|
|
77
|
-
? []
|
|
78
|
-
: [
|
|
79
|
-
'## Inline findings not posted',
|
|
80
|
-
'These comments could not be placed on a diff hunk:',
|
|
81
|
-
...dropped.map((comment) => `- ${reviewCommentSubject(comment)}`),
|
|
82
|
-
];
|
|
83
|
-
const body = [review.body, ...extras].filter((part) => part.length > 0).join('\n\n');
|
|
84
75
|
return {
|
|
85
76
|
...review,
|
|
86
|
-
body: body.length > 0 ? body : 'Inline findings only.',
|
|
77
|
+
body: review.body.length > 0 ? review.body : 'Inline findings only.',
|
|
87
78
|
comments: kept,
|
|
88
79
|
};
|
|
89
80
|
}
|
package/dist/index.d.mts
CHANGED
|
@@ -35,7 +35,7 @@ export { MissingResponseBodyError, readResponseBody, readResponseBodyAsBuffer, R
|
|
|
35
35
|
export type { ReadResponseBodyOptions } from './http-body/index.mts';
|
|
36
36
|
export { parseAstGrepRuleArgs, runAstGrepRule } from './ast-grep-rule/index.mts';
|
|
37
37
|
export type { AstGrepRuleInvocation, RunAstGrepRuleOptions } from './ast-grep-rule/index.mts';
|
|
38
|
-
export {
|
|
38
|
+
export { indexReviewFiles, MAX_REVIEW_COMMENTS, MAX_REVIEW_PAYLOAD_BYTES, nearestReviewLine, parsePatchCommentable, parseReviewFilesJson, parseReviewPayload, readRegularReviewPayload, remapReviewComments, ReviewPayloadError, reviewCommentSubject, rewriteSnappedSuggestion, snapReviewNote, stageReviewPayload, writeStagedOutput, } from './gha-review-payload/index.mts';
|
|
39
39
|
export type { CommentableIndex, CommentableLine, LineKind, PayloadRequirement, ReviewComment, ReviewFile, ReviewSide, SanitizedReview, } from './gha-review-payload/index.mts';
|
|
40
40
|
export { CLAUDE_OIDC_AUDIENCE, createActionsClaudeTokenIo, mintClaudeAppToken, PostReviewError, requireEnv, resolveReviewPostToken, revokeClaudeAppToken, runPostReview, runPostReviewCli, withClaudeAppToken, } from './gha-post-review/index.mts';
|
|
41
41
|
export type { ClaudeTokenIo, PostResult, PostReviewIo, PullFile, ReviewPostToken, } from './gha-post-review/index.mts';
|
package/dist/index.mjs
CHANGED
|
@@ -20,7 +20,7 @@ export { decide, deriveRetryAttempt } from './transient-retry/index.mjs';
|
|
|
20
20
|
export { escapeSpreadsheetFormula, parseCsvRows, streamCsvRows, stripCsvBom } from './csv/index.mjs';
|
|
21
21
|
export { MissingResponseBodyError, readResponseBody, readResponseBodyAsBuffer, ResponseBodyTooLargeError, } from './http-body/index.mjs';
|
|
22
22
|
export { parseAstGrepRuleArgs, runAstGrepRule } from './ast-grep-rule/index.mjs';
|
|
23
|
-
export {
|
|
23
|
+
export { indexReviewFiles, MAX_REVIEW_COMMENTS, MAX_REVIEW_PAYLOAD_BYTES, nearestReviewLine, parsePatchCommentable, parseReviewFilesJson, parseReviewPayload, readRegularReviewPayload, remapReviewComments, ReviewPayloadError, reviewCommentSubject, rewriteSnappedSuggestion, snapReviewNote, stageReviewPayload, writeStagedOutput, } from './gha-review-payload/index.mjs';
|
|
24
24
|
export { CLAUDE_OIDC_AUDIENCE, createActionsClaudeTokenIo, mintClaudeAppToken, PostReviewError, requireEnv, resolveReviewPostToken, revokeClaudeAppToken, runPostReview, runPostReviewCli, withClaudeAppToken, } from './gha-post-review/index.mjs';
|
|
25
25
|
export { nextPageCursorFromLinkHeader, nextPageUrlFromLinkHeader, validatePaginationRequestUrl, } from './http-link-pagination/index.mjs';
|
|
26
26
|
export { cmdDownloadCoverage, cmdDownloadVitestBlobs, cmdUpload, mintPresignedControl, transportObjectKeys, } from './coverage-transport/index.mjs';
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { asRecord } from './shared.mjs';
|
|
2
|
+
function isInjectedBlock(raw) {
|
|
3
|
+
const text = raw.trim();
|
|
4
|
+
// Hosted metadata occupies complete input blocks; inner fields can vary by runtime version.
|
|
5
|
+
const agents = /^# AGENTS\.md instructions for [^\r\n]+(?:\r?\n)+<INSTRUCTIONS(?:\s[^>]*)?>[\s\S]*<\/INSTRUCTIONS>$/.test(text);
|
|
6
|
+
const environment = /^<environment_context(?:\s[^>]*)?>[\s\S]*<\/environment_context>$/.test(text);
|
|
7
|
+
const skill = /^<skill(?:\s[^>]*)?>[\s\S]*<\/skill>$/.test(text);
|
|
8
|
+
return agents || environment || skill;
|
|
9
|
+
}
|
|
10
|
+
function isInjectedUser(payload) {
|
|
11
|
+
if (!Array.isArray(payload.content) || payload.content.length === 0)
|
|
12
|
+
return false;
|
|
13
|
+
return payload.content.map(asRecord).every((item) => {
|
|
14
|
+
return (item?.type === 'input_text' && typeof item.text === 'string' && isInjectedBlock(item.text));
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
function message(record, payload) {
|
|
18
|
+
if (record.type === 'response_item' && payload?.type === 'message') {
|
|
19
|
+
if (payload.role === 'user' || payload.role === 'assistant')
|
|
20
|
+
return { role: payload.role, schema: 'current' };
|
|
21
|
+
}
|
|
22
|
+
else if (record.type === 'event_msg') {
|
|
23
|
+
if (payload?.type === 'user_message')
|
|
24
|
+
return { role: 'user', schema: 'legacy' };
|
|
25
|
+
if (payload?.type === 'agent_message')
|
|
26
|
+
return { role: 'assistant', schema: 'legacy' };
|
|
27
|
+
}
|
|
28
|
+
return undefined;
|
|
29
|
+
}
|
|
30
|
+
function isDuplicate(previous, current) {
|
|
31
|
+
// Hosted rollouts currently emit user as current→legacy and assistant as legacy→current;
|
|
32
|
+
// keep directional to avoid merging inverse-order distinct turns (see pairing tests).
|
|
33
|
+
return ((current.role === 'user' && previous?.schema === 'current' && current.schema === 'legacy') ||
|
|
34
|
+
(current.role === 'assistant' && previous?.schema === 'legacy' && current.schema === 'current'));
|
|
35
|
+
}
|
|
36
|
+
export class CodexMessageCounter {
|
|
37
|
+
previous;
|
|
38
|
+
userPrompts = 0;
|
|
39
|
+
assistantResponses = 0;
|
|
40
|
+
add(record, payload) {
|
|
41
|
+
const current = message(record, payload);
|
|
42
|
+
const injected = current?.schema === 'current' &&
|
|
43
|
+
current.role === 'user' &&
|
|
44
|
+
payload !== undefined &&
|
|
45
|
+
isInjectedUser(payload);
|
|
46
|
+
if (!current) {
|
|
47
|
+
this.previous = undefined;
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
// Injected records are transparent because hosted metadata can split a duplicate pair.
|
|
51
|
+
if (injected)
|
|
52
|
+
return;
|
|
53
|
+
if (this.previous?.role === current.role && isDuplicate(this.previous, current)) {
|
|
54
|
+
this.previous = undefined;
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
if (current.role === 'user')
|
|
58
|
+
this.userPrompts++;
|
|
59
|
+
else
|
|
60
|
+
this.assistantResponses++;
|
|
61
|
+
this.previous = current;
|
|
62
|
+
}
|
|
63
|
+
totals() {
|
|
64
|
+
return [this.userPrompts, this.assistantResponses];
|
|
65
|
+
}
|
|
66
|
+
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { applyCommand, asNumber, asRecord, emptyFacts, emptyTokens, parseLines, } from './shared.mjs';
|
|
2
|
+
import { CodexMessageCounter } from './codex-messages.mjs';
|
|
2
3
|
import { customExecCommands } from './javascript-command.mjs';
|
|
3
4
|
function usage(record) {
|
|
4
5
|
const payload = asRecord(record.payload);
|
|
@@ -83,14 +84,13 @@ function applyRecords(records, facts, subagent, baseline = emptyTokens()) {
|
|
|
83
84
|
let previous = baseline;
|
|
84
85
|
const calls = new Set();
|
|
85
86
|
const failed = new Set();
|
|
87
|
+
const messages = new CodexMessageCounter();
|
|
86
88
|
let anonymousFailures = 0;
|
|
87
89
|
let previousCompaction;
|
|
88
90
|
for (const record of records) {
|
|
89
91
|
const payload = asRecord(record.payload);
|
|
90
|
-
if (!subagent
|
|
91
|
-
|
|
92
|
-
if (!subagent && record.type === 'event_msg' && payload?.type === 'agent_message')
|
|
93
|
-
facts.assistantResponses++;
|
|
92
|
+
if (!subagent)
|
|
93
|
+
messages.add(record, payload);
|
|
94
94
|
const compaction = record.type === 'compacted'
|
|
95
95
|
? 'top-level'
|
|
96
96
|
: record.type === 'event_msg' && payload?.type === 'context_compacted'
|
|
@@ -135,6 +135,11 @@ function applyRecords(records, facts, subagent, baseline = emptyTokens()) {
|
|
|
135
135
|
anonymousFailures++;
|
|
136
136
|
}
|
|
137
137
|
}
|
|
138
|
+
if (!subagent) {
|
|
139
|
+
const [userPrompts, assistantResponses] = messages.totals();
|
|
140
|
+
facts.userPrompts += userPrompts;
|
|
141
|
+
facts.assistantResponses += assistantResponses;
|
|
142
|
+
}
|
|
138
143
|
facts.failedToolCalls += [...failed].filter((id) => calls.has(id)).length + anonymousFailures;
|
|
139
144
|
}
|
|
140
145
|
export function codexChildren(lines, ownerPath = '/root') {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "vouchington-tooling",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.5",
|
|
4
4
|
"description": "Vouchington CLI and extractable tooling libraries.",
|
|
5
5
|
"homepage": "https://github.com/vouchington/vouchington-tooling/tree/main/packages/vouchington-tooling#readme",
|
|
6
6
|
"bugs": {
|
|
@@ -223,7 +223,7 @@
|
|
|
223
223
|
},
|
|
224
224
|
"dependencies": {
|
|
225
225
|
"csv-parse": "7.0.2",
|
|
226
|
-
"csv-stringify": "6.8.
|
|
226
|
+
"csv-stringify": "6.8.3",
|
|
227
227
|
"dockerfile-ast": "0.7.1",
|
|
228
228
|
"picomatch": "4.0.5",
|
|
229
229
|
"yaml": "2.9.0"
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
set -euo pipefail
|
|
3
|
+
|
|
4
|
+
usage() {
|
|
5
|
+
echo 'usage: download-optional-run-artifacts.sh (--name <name> | --pattern <pattern>) --dir <directory>' >&2
|
|
6
|
+
exit 2
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
selector=''
|
|
10
|
+
selector_type=''
|
|
11
|
+
destination=''
|
|
12
|
+
while [ "$#" -gt 0 ]; do
|
|
13
|
+
case "$1" in
|
|
14
|
+
--name|--pattern)
|
|
15
|
+
[ -z "$selector_type" ] && [ "$#" -ge 2 ] || usage
|
|
16
|
+
selector_type="${1#--}"
|
|
17
|
+
selector="$2"
|
|
18
|
+
shift 2
|
|
19
|
+
;;
|
|
20
|
+
--dir)
|
|
21
|
+
[ -z "$destination" ] && [ "$#" -ge 2 ] || usage
|
|
22
|
+
destination="$2"
|
|
23
|
+
shift 2
|
|
24
|
+
;;
|
|
25
|
+
*) usage ;;
|
|
26
|
+
esac
|
|
27
|
+
done
|
|
28
|
+
|
|
29
|
+
[ -n "$selector_type" ] && [ -n "$selector" ] && [ -n "$destination" ] || usage
|
|
30
|
+
[ -n "${GITHUB_OUTPUT:-}" ] || { echo 'GITHUB_OUTPUT must be set' >&2; exit 2; }
|
|
31
|
+
[ -n "${GITHUB_REPOSITORY:-}" ] || { echo 'GITHUB_REPOSITORY must be set' >&2; exit 2; }
|
|
32
|
+
[ -n "${GITHUB_RUN_ID:-}" ] || { echo 'GITHUB_RUN_ID must be set' >&2; exit 2; }
|
|
33
|
+
github_host="${GH_HOST:-${GITHUB_SERVER_URL:-https://github.com}}"
|
|
34
|
+
github_host="${github_host#*://}"
|
|
35
|
+
github_host="${github_host%%/*}"
|
|
36
|
+
[ -n "$github_host" ] || { echo 'GitHub host must be set' >&2; exit 2; }
|
|
37
|
+
repository="$GITHUB_REPOSITORY"
|
|
38
|
+
case "$github_host" in
|
|
39
|
+
github.com|*.ghe.com) ;;
|
|
40
|
+
*)
|
|
41
|
+
if [ -z "${GH_ENTERPRISE_TOKEN:-${GITHUB_ENTERPRISE_TOKEN:-}}" ]; then
|
|
42
|
+
enterprise_token="${GH_TOKEN:-${GITHUB_TOKEN:-}}"
|
|
43
|
+
[ -z "$enterprise_token" ] || export GH_ENTERPRISE_TOKEN="$enterprise_token"
|
|
44
|
+
fi
|
|
45
|
+
;;
|
|
46
|
+
esac
|
|
47
|
+
|
|
48
|
+
validate_artifact_name() {
|
|
49
|
+
case "$1" in
|
|
50
|
+
''|.|..|*/*|*\\*)
|
|
51
|
+
echo '[optional-run-artifacts] invalid artifact name' >&2
|
|
52
|
+
return 2
|
|
53
|
+
;;
|
|
54
|
+
esac
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
download_exact() {
|
|
58
|
+
artifact="$1"
|
|
59
|
+
directory="$2"
|
|
60
|
+
validate_artifact_name "$artifact" || return $?
|
|
61
|
+
artifacts=$(list_artifact_names) || return $?
|
|
62
|
+
found=0
|
|
63
|
+
while IFS= read -r candidate; do
|
|
64
|
+
if [ "$candidate" = "$artifact" ]; then
|
|
65
|
+
found=1
|
|
66
|
+
break
|
|
67
|
+
fi
|
|
68
|
+
done < <(printf '%s\n' "$artifacts")
|
|
69
|
+
[ "$found" -eq 1 ] || return 3
|
|
70
|
+
echo "[optional-run-artifacts] attempt selector=$selector_type" >&2
|
|
71
|
+
GH_HOST="$github_host" gh run download "$GITHUB_RUN_ID" --repo "$repository" --name "$artifact" --dir "$directory"
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
list_artifact_names() {
|
|
75
|
+
GH_HOST="$github_host" gh api \
|
|
76
|
+
--paginate \
|
|
77
|
+
"repos/$GITHUB_REPOSITORY/actions/runs/$GITHUB_RUN_ID/artifacts?per_page=100" \
|
|
78
|
+
--jq '.artifacts[] | select(.expired | not) | .name'
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
download_pattern() {
|
|
82
|
+
artifacts=$(list_artifact_names | awk '!seen[$0]++') || return $?
|
|
83
|
+
selected_artifacts=()
|
|
84
|
+
while IFS= read -r artifact; do
|
|
85
|
+
[ -n "$artifact" ] || continue
|
|
86
|
+
# shellcheck disable=SC2254
|
|
87
|
+
# selector is the caller's intended glob pattern.
|
|
88
|
+
case "$artifact" in
|
|
89
|
+
$selector) ;;
|
|
90
|
+
*) continue ;;
|
|
91
|
+
esac
|
|
92
|
+
validate_artifact_name "$artifact" || return $?
|
|
93
|
+
selected_artifacts+=("$artifact")
|
|
94
|
+
echo "[optional-run-artifacts] selected artifact=$artifact" >&2
|
|
95
|
+
done < <(printf '%s\n' "$artifacts")
|
|
96
|
+
count=${#selected_artifacts[@]}
|
|
97
|
+
echo "[optional-run-artifacts] selection selector=pattern count=$count" >&2
|
|
98
|
+
[ "$count" -gt 0 ] || return 3
|
|
99
|
+
for artifact in "${selected_artifacts[@]}"; do
|
|
100
|
+
echo "[optional-run-artifacts] attempt artifact=$artifact" >&2
|
|
101
|
+
GH_HOST="$github_host" gh run download "$GITHUB_RUN_ID" --repo "$repository" --name "$artifact" --dir "$destination/$artifact" || return $?
|
|
102
|
+
done
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
if [ "$selector_type" = name ]; then
|
|
106
|
+
if download_exact "$selector" "$destination"; then status=0; else status=$?; fi
|
|
107
|
+
else
|
|
108
|
+
if download_pattern; then status=0; else status=$?; fi
|
|
109
|
+
fi
|
|
110
|
+
if [ "$status" -eq 0 ]; then
|
|
111
|
+
echo 'availability=available' >> "$GITHUB_OUTPUT"
|
|
112
|
+
echo "[optional-run-artifacts] result=available selector=$selector_type" >&2
|
|
113
|
+
else
|
|
114
|
+
case "$status" in
|
|
115
|
+
130|143) exit "$status" ;;
|
|
116
|
+
3)
|
|
117
|
+
echo 'availability=unavailable' >> "$GITHUB_OUTPUT"
|
|
118
|
+
echo "::warning::Optional same-run artifact unavailable; continuing with validated fallback (selector=$selector_type exit=$status)" >&2
|
|
119
|
+
echo "[optional-run-artifacts] result=unavailable selector=$selector_type exit=$status" >&2
|
|
120
|
+
;;
|
|
121
|
+
*)
|
|
122
|
+
echo "[optional-run-artifacts] result=error selector=$selector_type exit=$status" >&2
|
|
123
|
+
exit "$status"
|
|
124
|
+
;;
|
|
125
|
+
esac
|
|
126
|
+
fi
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
set -euo pipefail
|
|
3
3
|
|
|
4
4
|
usage() {
|
|
5
|
-
echo "usage: install-github-release.sh --repo owner/name --version VERSION --asset TEMPLATE --bin NAME [--tag-prefix PREFIX] [--strip-components N] [--bin-dir DIR] [--version-flag FLAG] [--checksums-asset NAME] [--no-checksum]" >&2
|
|
5
|
+
echo "usage: install-github-release.sh --repo owner/name --version VERSION --asset TEMPLATE --bin NAME [--tag-prefix PREFIX] [--strip-components N] [--bin-dir DIR] [--version-flag FLAG] [--checksums-asset NAME] [--expected-sha256 SHA256] [--no-checksum]" >&2
|
|
6
6
|
}
|
|
7
7
|
|
|
8
8
|
REPO="${REPO:-}"
|
|
@@ -14,6 +14,7 @@ STRIP_COMPONENTS="${STRIP_COMPONENTS:-1}"
|
|
|
14
14
|
BIN_DIR="${INSTALL_BIN_DIR:-}"
|
|
15
15
|
VERSION_FLAG="${VERSION_FLAG:---version}"
|
|
16
16
|
CHECKSUMS_ASSET="${CHECKSUMS_ASSET:-}"
|
|
17
|
+
EXPECTED_SHA256="${EXPECTED_SHA256:-}"
|
|
17
18
|
NO_CHECKSUM="${NO_CHECKSUM:-0}"
|
|
18
19
|
|
|
19
20
|
if [ -n "${RELEASE_OWNER:-}" ] && [ -n "${RELEASE_REPO:-}" ]; then
|
|
@@ -67,6 +68,11 @@ while [ "${#}" -gt 0 ]; do
|
|
|
67
68
|
CHECKSUMS_ASSET="$2"
|
|
68
69
|
shift 2
|
|
69
70
|
;;
|
|
71
|
+
--expected-sha256)
|
|
72
|
+
[ "${#}" -ge 2 ] || { usage; exit 2; }
|
|
73
|
+
EXPECTED_SHA256="$2"
|
|
74
|
+
shift 2
|
|
75
|
+
;;
|
|
70
76
|
--no-checksum)
|
|
71
77
|
NO_CHECKSUM=1
|
|
72
78
|
shift
|
|
@@ -97,6 +103,11 @@ if ! [[ "$STRIP_COMPONENTS" =~ ^[0-9]+$ ]]; then
|
|
|
97
103
|
exit 2
|
|
98
104
|
fi
|
|
99
105
|
|
|
106
|
+
if [ -n "$EXPECTED_SHA256" ] && ! [[ "$EXPECTED_SHA256" =~ ^[[:xdigit:]]{64}$ ]]; then
|
|
107
|
+
echo "install-github-release.sh: --expected-sha256 must be a 64-character hexadecimal SHA-256 digest" >&2
|
|
108
|
+
exit 2
|
|
109
|
+
fi
|
|
110
|
+
|
|
100
111
|
BIN_DIR="${BIN_DIR:-${RUNNER_TEMP:-${HOME}/.local}/bin}"
|
|
101
112
|
mkdir -p "$BIN_DIR"
|
|
102
113
|
if [ -n "${GITHUB_PATH:-}" ]; then
|
|
@@ -150,10 +161,12 @@ extract_dir="${work}-extract"
|
|
|
150
161
|
rm -rf "$extract_dir"
|
|
151
162
|
mkdir -p "$extract_dir"
|
|
152
163
|
curl -fsSL -o "$archive" "${base_url}/${asset_name}"
|
|
153
|
-
if [ "$NO_CHECKSUM" != 1 ]; then
|
|
154
|
-
if [ -n "$
|
|
164
|
+
if [ -n "$EXPECTED_SHA256" ] || [ "$NO_CHECKSUM" != 1 ]; then
|
|
165
|
+
if [ -n "$EXPECTED_SHA256" ]; then
|
|
166
|
+
expected="$EXPECTED_SHA256"
|
|
167
|
+
elif [ -n "$checksums_name" ]; then
|
|
155
168
|
curl -fsSL -o "${archive}.checksums" "${base_url}/${checksums_name}"
|
|
156
|
-
expected=$(
|
|
169
|
+
expected=$(awk -v file="$asset_name" '$2 == file || $2 == "*" file { print $1 }' "${archive}.checksums")
|
|
157
170
|
rm "${archive}.checksums"
|
|
158
171
|
else
|
|
159
172
|
curl -fsSL -o "${archive}.sha256" "${base_url}/${asset_name}.sha256"
|
|
@@ -8,17 +8,55 @@ description: Search, create, update, link, or assess GitHub issues using the rep
|
|
|
8
8
|
Use for durable follow-ups and pull-request linkage. Read local `AGENTS.md`, `CLAUDE.md`, issue
|
|
9
9
|
templates, and repository-routing policy before making any remote mutation.
|
|
10
10
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
11
|
+
## Mutation authority
|
|
12
|
+
|
|
13
|
+
Caller authorization is required before any issue or taxonomy mutation; credential capability does
|
|
14
|
+
not grant scope. Resolve the authenticated identity, current repository, and requested target without
|
|
15
|
+
owner-prefix or visibility assumptions. Immediately before every write, refetch the exact target and
|
|
16
|
+
require its canonical identity to still match and the repository not to be archived. Issue operations
|
|
17
|
+
also require issues to be enabled and `viewerPermission` of `TRIAGE`, `WRITE`, `MAINTAIN`, or `ADMIN`;
|
|
18
|
+
issue creation additionally requires `viewerCanCreateIssues`. Applying existing metadata to a pull
|
|
19
|
+
request uses the same permission set but does not require issues to be enabled. Creating, changing, or
|
|
20
|
+
deleting taxonomy definitions requires `WRITE`, `MAINTAIN`, or `ADMIN` plus the operation-specific API
|
|
21
|
+
capability. Treat insufficient permission or capability, missing or inaccessible data, identity
|
|
22
|
+
changes, and mismatches as a hard deny that approval cannot override.
|
|
23
|
+
|
|
24
|
+
When an external creation target is denied, never write there. Search for and create or reuse a
|
|
25
|
+
tracking issue in the current repository, or a consumer-selected tracker. Immediately before that
|
|
26
|
+
write, refetch the destination repository and apply the issue-operation gate above. Include the
|
|
27
|
+
intended upstream repository and a copy-ready report so a human can decide whether to file it. Before
|
|
28
|
+
copying details to a less-restricted destination, remove private repository identity, paths, links,
|
|
29
|
+
code, and findings; if redaction would make the report unusable, require explicit destination approval
|
|
30
|
+
or return the draft without mutation. Authorization to file the external issue includes this tracking
|
|
31
|
+
fallback unless the caller opts out; report the reroute explicitly. If no tracker passes, return the
|
|
32
|
+
draft without mutation. Never fall back silently or to an unverified repository.
|
|
33
|
+
|
|
34
|
+
## Issue workflow
|
|
35
|
+
|
|
36
|
+
1. Search open and relevant closed issues in the selected destination; return a likely duplicate
|
|
37
|
+
rather than filing one. Verify paths and current behavior named in the issue.
|
|
38
|
+
2. Before editing, commenting, relating, closing, or otherwise changing state, refetch the issue and
|
|
39
|
+
its current discussion. Verify the requested mutation is still authorized and supported by that
|
|
40
|
+
evidence. Close only when the documented outcome and acceptance evidence show the work is
|
|
41
|
+
resolved; otherwise leave state unchanged and report the gap.
|
|
42
|
+
3. Write a self-contained issue with the problem, desired outcome, ownership boundaries, concrete
|
|
43
|
+
areas, validation, and external context. A discovered blocker does not widen implementation scope.
|
|
44
|
+
4. Fetch the complete live taxonomy. Apply matching existing labels and a selected existing milestone
|
|
45
|
+
without separate approval. Omit a missing optional milestone; a missing required milestone blocks
|
|
46
|
+
the issue, and milestone creation is a separately authorized taxonomy operation. For a
|
|
47
|
+
missing label, use [review-github-issue-taxonomy](../review-github-issue-taxonomy/SKILL.md): obtain
|
|
48
|
+
explicit approval for its exact repository, name, description, and color before creating it.
|
|
49
|
+
Omit a declined optional label; a missing required label blocks the issue.
|
|
50
|
+
5. Refetch the created or updated issue and verify its metadata. Report a partial failure without
|
|
51
|
+
retrying creation. Preserve history and report the action, URL, labels, and milestone.
|
|
52
|
+
6. Link a pull request with a closing reference only when it fully resolves the issue. Keep
|
|
53
|
+
cross-repository references fully qualified; PR creation authority remains separate.
|
|
54
|
+
7. Use native sub-issues only for real hierarchy, blocked-by relationships only for genuine known
|
|
55
|
+
dependencies, and prose links for merely related work. Read every created relationship back.
|
|
56
|
+
|
|
57
|
+
For batch creation, preflight every entry before writing any issue and fail the whole batch when one
|
|
58
|
+
entry is invalid. A partial transport failure stops further creation and reports every issue already
|
|
59
|
+
created; never retry successful entries.
|
|
60
|
+
|
|
61
|
+
This skill supplies no repository allowlist, taxonomy, issue template, credential, or CLI wrapper.
|
|
62
|
+
Consumer wrappers define local defaults and may impose stricter policy, but may not weaken this gate.
|
|
@@ -9,11 +9,14 @@ Use for issue hygiene, priority normalization, and milestone assignment. Read lo
|
|
|
9
9
|
`CLAUDE.md`, and live taxonomy guidance before acting.
|
|
10
10
|
|
|
11
11
|
1. Confirm repository identity and fetch the live labels, milestone descriptions, and all required
|
|
12
|
-
in-scope issue or pull-request evidence
|
|
12
|
+
in-scope issue or pull-request evidence. Before mutating, enforce the operation-specific gate from
|
|
13
|
+
[github-issue](../github-issue/SKILL.md); pull-request metadata does not require issues to be
|
|
14
|
+
enabled.
|
|
13
15
|
2. Classify from the permitted metadata and discussion evidence, not implementation guesses. Keep
|
|
14
16
|
automation, ownership, and provenance labels unless local policy explicitly permits changes.
|
|
15
|
-
3. Apply only existing labels and milestones
|
|
16
|
-
or alter titles unless the caller separately
|
|
17
|
+
3. Apply only existing labels and milestones without requesting separate label approval. Do not
|
|
18
|
+
create taxonomy, close work, rewrite bodies, or alter titles unless the caller separately
|
|
19
|
+
authorizes that scope.
|
|
17
20
|
4. In review mode, report the exact proposed metadata changes without mutating. In apply mode, make
|
|
18
21
|
only necessary, idempotent updates.
|
|
19
22
|
5. Refetch every touched item and verify the requested metadata changed while protected metadata
|
|
@@ -14,7 +14,9 @@ Use when completed retrospectives or journals should become durable follow-up wo
|
|
|
14
14
|
3. Verify each candidate against the current base and search existing issues and open changes before
|
|
15
15
|
drafting. Skip work that is complete, explicitly rejected, or already covered.
|
|
16
16
|
4. Draft self-contained issues with the problem, concrete proposed work, relevant areas, and
|
|
17
|
-
evidence embedded in the body.
|
|
17
|
+
evidence embedded in the body. Route every authorized creation through
|
|
18
|
+
[github-issue](../github-issue/SKILL.md), including its repository gate, label approval, and
|
|
19
|
+
denied-external tracking behavior.
|
|
18
20
|
5. Archive only records that were fully processed under the repository's retention rules; report
|
|
19
21
|
created, updated, skipped, and deferred themes with reasons.
|
|
20
22
|
|
|
@@ -15,8 +15,13 @@ local configuration edits or live taxonomy mutation; read local `AGENTS.md` and
|
|
|
15
15
|
3. Audit aliases, ambiguity, unused labels, missing descriptions, color consistency, milestone
|
|
16
16
|
overlap, delivery gaps, and path-label coverage.
|
|
17
17
|
4. Return exact recommendations with migration impact and separate safe cleanup from decisions that
|
|
18
|
-
require product or scheduling judgment.
|
|
19
|
-
|
|
18
|
+
require product or scheduling judgment. Applying an existing label is not taxonomy creation and
|
|
19
|
+
needs no separate approval from the authorized issue operation.
|
|
20
|
+
5. Before creating a label, stop for explicit approval naming its exact repository, name,
|
|
21
|
+
description, and color. Re-fetch current state and enforce the taxonomy-definition gate from
|
|
22
|
+
[github-issue](../github-issue/SKILL.md): require `WRITE`, `MAINTAIN`, or `ADMIN` and the
|
|
23
|
+
label-specific API capability immediately before the approved mutation. `viewerCanCreateIssues`
|
|
24
|
+
applies only to issue creation. Approval for an issue or another label does not transfer.
|
|
20
25
|
|
|
21
26
|
Do not create or change labels, milestones, issues, or local files for a recommendation-only
|
|
22
27
|
request. This skill does not define a repository's taxonomy or automation file locations; a
|
|
@@ -17,8 +17,10 @@ issue policy, and review-record conventions first.
|
|
|
17
17
|
4. Group related candidates into self-contained issue drafts that preserve the original evidence and
|
|
18
18
|
explain why the work remains needed.
|
|
19
19
|
5. Remain read-only unless the caller or consumer wrapper explicitly authorizes issue creation. If
|
|
20
|
-
authorized,
|
|
21
|
-
|
|
20
|
+
authorized, route every candidate through
|
|
21
|
+
[github-issue](../github-issue/SKILL.md), including its denied-external tracking behavior; do not
|
|
22
|
+
duplicate repository or label authorization. Then clean up temporary collection artifacts and
|
|
23
|
+
report skipped, covered, and created items.
|
|
22
24
|
|
|
23
25
|
Do not assume a review journal format, default lookback, hosting provider, repository, issue
|
|
24
26
|
taxonomy, or confirmation mechanism. Consumer wrappers own those integrations.
|