scaffold-sub-issues-gh — independently scanned and version-tracked by SaferSkills.
SaferSkills independently audited scaffold-sub-issues-gh (Agent Skill) and scored it 87/100 (green). The audit ran 55 deterministic rules across Security, Supply Chain, Maintenance, Transparency, and Community; it found 0 high-severity and 3 lower-severity findings. The full rule-by-rule trace and per-finding evidence are below. Free, methodology-open.
Findings & checks · 3 flagged
The text {match} tells the agent to skip the normal "ask the user first" gate. Used adversarially it removes the human-in-the-loop check before destructive or sensitive actions, turning a normally-gated agent into a fire-and-forget executor.
The text {match} tells the agent to skip the normal "ask the user first" gate. Used adversarially it removes the human-in-the-loop check before destructive or sensitive actions, turning a normally-gated agent into a fire-and-forget executor.
Every scanned point with the score it earned and what moved between them.
First recorded scan — no prior version to compare against.
The primary manifest — the file an agent reads to learn what this artifact does.
Take one or more PLAN-*.md files, parse their sub-tasks, propose a GitHub parent issue (or add children to an existing one) with a managed ## Sub-issues task-list, repo: / status:* / blocks:* / blocked-by:* labels, and an optional ## Validation gate, then create them after the user confirms.
The output of this skill is a parent issue URL (or <owner>/<repo>#<n> ID) you can paste straight into /abc:ship-epic-gh (parallel multi-repo) or /abc:ship-issue-gh (serial single-loop).
This is the GitHub-Issues sibling of /abc:scaffold-sub-issues (which targets Linear). The two are deliberately parallel skills — pick by tracker, not auto-detect. See github-conventions.md in this directory for the label scheme and task-list parsing rules used across the -gh family.
gh issue create call.repo:<name> that doesn't exist, surface it and ask whether to create the label or rename the sub-task. Same for status:* if those don't exist yet.repo:, ask the user — don't fabricate.gh issue create prints the new number on its own; firing several concurrently makes it easy to bind the wrong number to the wrong ST-N in the ST→#<n> map, corrupting the task-list refs and the dependency labels. (2) secondary rate limits — GitHub throttles rapid bursts of content-creating calls with a 403 secondary-rate error. Sequential creation keeps the number↔ST binding unambiguous and the request pace under the threshold.AskUserQuestion.## Sub-issues task-list section, not a summary. Reviewers should be able to read the parent and understand the full context.repo:*, status:*, blocks:*, blocked-by:*. Never auto-add or remove user-authored labels outside these prefixes.$ARGUMENTS has four shapes — detect in order, first match wins.
Path precedence (applies before shapes 2–3). The first token is a plan path — skip to shape 4 — if either:
ls); this is the reliable discriminator and catches the common examples/PLAN-avatar-component.md case (which matches the <owner>/<repo> shape character-for-character) on its own; or.md and does not fully match ^[A-Za-z0-9_.-]+/[A-Za-z0-9_.-]+$ — e.g. a multi-segment path like docs/plans/feature.md. The "does not fully match owner/repo" guard is load-bearing: GitHub permits a repo named owner/notes.md, and that token should still parse as a hub repo (shape 3), not a plan path. A genuine single-segment plan path that happens to be owner/repo-shaped is covered by the ls existing-file check above (it has to exist on disk to be read anyway).Shapes 2 and 3 below are also full-token anchored (^…$) so a token with a trailing /path/... segment can't partial-match.
PLAN-*.md in cwd (newest by mtime wins), fall back to ~/.claude/plans/PLAN-*.md (newest wins). If multiple candidates, ask the user to pick via AskUserQuestion. If none, abort with: "No PLAN-*.md found. Run /abc:plan first."semanticpixel/abc#42) and is not an existing file → existing-parent mode. Treat the first token as the parent issue. Remaining tokens are plan file paths (all required, all must exist). This skill will add child issues to that parent's task-list instead of creating a new one.<owner>/<repo>) and is not an existing file → new-parent mode with explicit hub repo. Remaining tokens are plan file paths. (A repo legitimately named owner/notes.md lands here, not in the path branch — see the precedence note above.)git remote get-url origin — must be a GitHub URL; if not, ask the user for an explicit <owner>/<repo>).Read all selected plan files in full. Concatenate them in the order provided (Phase 1.5 handles conflicts).
Auth pre-flight. Run gh auth status. If not authed for the target host (github.com by default, or a GitHub Enterprise host if the hub repo's URL indicates one), halt with the exact re-auth command. Don't attempt issue creation against unauthed hosts.
`gh` capability. This skill relies on --json body support on gh issue view. No proactive gh --version probe — if a gh issue view ... --json body call fails because the flag is unsupported (very old gh), halt with reason gh-too-old-for-json-body and the upgrade command.
Parse each plan per the canonical grammar in ../plan/plan-format.md — the strict/loose formats, the sub-task block fields, the (none)/(empty)/omitted relations sentinel, and the validation-gate resolution order are all defined there (single-sourced so this skill and the Linear scaffold-sub-issues can't drift). If neither format matches, halt with the message that doc specifies.
GitHub-specific deltas (everything else follows plan-format.md verbatim):
validation: bullet (or a sub-task-level ## Validation) becomes that child's manual-validation gate — the post-merge blocked-verify halt for /abc:ship-issue-gh. A top-level ## Validation section is unattached; Phase 4 asks which sub-issue inherits it.repo:<name> maps to a GitHub repo:<name> label and routes to a same-owner repo by default. A fully-qualified repo:<owner>/<name> routes the child to a different owner's repo (the cross-owner case in Phase 2.2) — preserve the <owner>/ prefix through to label creation and gh issue create --repo.Identical to the Linear sibling's Phase 1.5. Scan for:
ST-N ID with different content.repo:<name> with different scope.For each conflict: AskUserQuestion side-by-side with the newer-by-mtime tagged (Recommended). Bake the choice into the canonical structure for Phase 2 onward.
In all modes:
<owner>/<repo>.<owner>/<repo> arg.git remote get-url origin, parsed to <owner>/<repo>.gh repo view <owner>/<repo> for the hub and for every unique repo:<name> in the plan that names a different repo (cross-repo case). Resolve <name> to <owner>/<name> if the same owner as the hub, else require the plan to use <owner>/<name> explicitly. Halt with a clear message if any repo doesn't exist or isn't accessible.gh label list --repo <owner>/<repo> --json name,color --limit 200 for the hub repo and for every distinct target repo. Compute the missing labels set: labels** referenced by any sub-task — because the parent issue (created in the hub) carries that whole union as its labels (Phase 3's labels: [<deduped union of all sub-task repo: labels>]). So even a repo:analytics-tools` whose children live in a different target repo must exist as a label in the hub repo, or applying the parent's label set fails.repo:<name> label (the one its children carry), plus status:in-progress, status:in-review (the only two status:* labels we manage; pending is absence-of-label, merged/failed map to closed-state).blocks:#<N> and blocked-by:#<N> are created per-edge in Phase 5, not here — their names depend on resolved issue numbers we don't know yet.gh issue view <n> --repo <owner>/<repo> --json number,title,state,body,labels. Capture body; parse any existing <!-- ship-epic:sub-issues:start --> ... <!-- ship-epic:sub-issues:end --> block for the collision check in Phase 3.For each unique repo:<name> referenced, check whether <cwd>/<name>/ exists (ls in cwd). If missing, note for the Phase 7 advisory — don't halt. The user might invoke /abc:ship-issue-gh from a different cwd.
Compose the structure in memory:
Parent:
hub_repo: <owner>/<repo>
title: <plan title> # new-parent mode only
body: |
<full plan markdown, concatenated if multiple>
## Sub-issues
<!-- ship-epic:sub-issues:start -->
- [ ] #<ST-1-placeholder> — <ST-1 title>
- [ ] #<ST-2-placeholder> — <ST-2 title>
- [ ] <owner>/<other-repo>#<ST-3-placeholder> — <ST-3 title>
<!-- ship-epic:sub-issues:end -->
labels: [<deduped union of all sub-task repo: labels>]
Sub-issues (in plan order):
ST-1:
target_repo: <owner>/<repo> # may differ from hub (cross-repo case)
title: <ST-1 title>
body: |
## Scope
<scope text>
## Acceptance criteria
- <bullets>
[optional ## Validation block, only on the chosen gate sub-issue]
---
Parent: <hub-owner>/<hub-repo>#<parent-placeholder>
labels: [repo:<name>]
relations:
blocks: [<ST-IDs>]
blocked by: [<ST-IDs>]
ST-2: …Existing-parent mode collision check. Re-use the parent body fetched in Phase 2. If the parent already has a non-empty ## Sub-issues block (one or more - [ ] / - [x] lines between the fence markers):
AskUserQuestion: "Parent <owner>/<repo>#<n> already has N children in its task-list. Halt (default), Append more (new children land after existing ones), or Show me the existing ones before deciding?"Print a readable preview:
/abc:scaffold-sub-issues-gh — proposed GitHub structure
Mode: new-parent | existing-parent (<owner>/<repo>#<n>) | existing-parent (append)
Hub repo: <owner>/<repo>
Parent: <Title> # new-parent mode only
Labels: repo:web-frontend, repo:analytics-tools
Body: <2000-char preview>
Sub-issues:
[ST-1] Add WidgetRow component to web frontend
Target repo: <owner>/web-frontend
Labels: repo:web-frontend
Blocks: ST-3
Blocked by: (none)
[ST-2] Publish shared WidgetRow types in analytics-tools
Target repo: <owner>/analytics-tools
Labels: repo:analytics-tools
Blocks: (none)
Blocked by: (none)
[ST-3] Wire WidgetRow into dashboard page ← carries ## Validation gate
Target repo: <owner>/web-frontend
Labels: repo:web-frontend
Blocks: (none)
Blocked by: ST-1
Dependency graph:
ST-2 → (independent)
ST-1 → ST-3
Missing labels to create:
in <owner>/web-frontend (hub): status:in-progress, status:in-review, repo:web-frontend, repo:analytics-tools
in <owner>/analytics-tools: status:in-progress, status:in-review, repo:analytics-tools
cwd advisory: <cwd>/analytics-tools/ not found (you'll need to invoke /abc:ship-issue-gh from a cwd containing this subdir)
# Note: the hub repo gets the *union* of all repo:* labels (the parent issue carries them all); each target repo gets only its own repo:<name>.Then ask via AskUserQuestion:
## Validation was attached in Phase 1, or there's a top-level Validation section that needs an owner):## Validation section to trigger /abc:ship-issue-gh's blocked-verify flow?" Default: last UI-touching sub-issue if detectable, else "none".<list>. Create them, or rename the sub-tasks?"If "Edit before creating" — wait for user input, re-render the preview, re-ask.
gh label create <name> --repo <target-repo> --color <hex> --description <text>. Use the color scheme from github-conventions.md. Run per-repo (don't batch across repos — gh label create is per-repo).## Sub-issues block containing placeholder lines (we'll patch in real numbers after Phase 5.3). Pipe the body on stdin (no Write tool — --body-file - reads stdin, stays inside the gh issue create grant): gh issue create --repo <hub> --title <T> --body-file - --label <comma-list> <<'EOF' … body … EOF. Capture the parent's issue number from the URL the command prints.## Scope, ## Acceptance criteria, plus the ## Validation block on the chosen gate sub-issue, plus a Parent: <hub>/<parent-num> trailer.gh issue create --repo <target-repo> --title <T> --body-file - --label "repo:<name>" <<'EOF' … description … EOF (body piped on stdin, same --body-file - pattern as the parent). Wait for the response before issuing the next call. Capture the new number from the URL.<owner>/<repo>#<n> map.gh issue view <parent-num> --repo <hub> --json body), replace the placeholder lines between the fence markers with real - [ ] <ref> — <title> lines (use #<n> for same-repo, <owner>/<repo>#<n> for cross-repo), then pipe the patched body on stdin: gh issue edit <parent-num> --repo <hub> --body-file - <<'EOF' … body … EOF. In append mode, insert new lines after the existing block contents (don't touch the existing lines).blocks: [ST-X, ST-Y]:blocks:#<X-resolved> and blocks:#<Y-resolved> (use the resolved per-repo number; cross-repo blocks uses blocks:<owner>/<repo>#<n> form).gh label create per target repo). These are per-edge — fine to accumulate; they're cheap.gh issue edit <n> --repo <target-repo> --add-label "blocks:#<X>".blocked by: blocked-by:#<N> label on the dependent sub-issue.gh issue view <parent-num> --repo <hub> --json body — confirm the ## Sub-issues block contains every newly created sub-issue's ref, in plan order.gh issue view <n> --repo <target> --json labels — confirm the expected repo:, blocks:*, blocked-by:* set is present.If any check fails, surface the diff between expected and actual to the user and recommend a manual fix; do not auto-retry destructive edits.
✓ Created parent: <owner>/<repo>#<N> "<Title>" # new-parent mode
✓ Added 5 children to <owner>/<repo>#<N> "<Title>" # existing-parent mode
✓ Wired 2 dependency edges
✓ Created 6 new labels (status:in-progress, status:in-review, repo:web-frontend, repo:analytics-tools, blocks:#42, blocked-by:#42)
Sub-issues (plan order — what /abc:ship-issue-gh will walk):
1. <owner>/web-frontend#42 repo:web-frontend
2. <owner>/analytics-tools#7 repo:analytics-tools
3. <owner>/web-frontend#43 repo:web-frontend ← carries ## Validation gate
Next:
→ /abc:ship-issue-gh <owner>/<repo>#<N> (serial single-loop; recommended)
→ /abc:ship-epic-gh <owner>/<repo>#<N> (parallel multi-repo; faster if relations allow)
Parent URL: https://github.com/<owner>/<repo>/issues/<N>If Phase 2.5 flagged any missing cwd subdirectories, repeat the advisory:
⚠ Heads up: <cwd>/<repo-name>/ is missing.
/abc:ship-issue-gh resolves repo: labels against subdirectories of cwd.
Invoke it from a cwd that has each <repo-name>/ as a subdirectory,
or clone the missing repos first./abc:ship-issue-gh to know when an issue is done.AskUserQuestion, recommends newer plan by mtime.repo:other-org/foo and the hub repo's owner is our-org, surface and confirm the cross-org wiring before any gh repo view. Cross-org task-list autolinks work, but the visual is more disruptive — make sure the user intended it.gh repo view honors --repo with full host prefix). All gh calls inherit the same host because --repo carries it. Don't mix hosts in a single invocation — Phase 0 halts if cross-host is implied.--body-file reads should keep us well under, but if a 403 secondary rate is returned, halt and surface — don't retry.~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.