Skip to content
Development
Command

/spec-cli

<!-- SPDX-FileCopyrightText: Copyright (c) RapierCraft Studios --> <!-- SPDX-License-Identifier: AGPL-3.0-or-later -->

BOOST
From plugin
forgedock
11980 skills80 commands
Install
> /plugin marketplace add RapierCraftStudios/ForgeDock
> /plugin install forgedock@forgedock

How it fires

How this command gets triggered: by you, by Claude, or both.

  • Fires itselfClaude auto-loads it when your prompt matches the work.
  • You can call itInvoke it directly when you want it.
  • Slash command/spec-cli

Context preview

What this command does when you run it.

<!-- SPDX-FileCopyrightText: Copyright (c) RapierCraft Studios --> <!-- SPDX-License-Identifier: AGPL-3.0-or-later -->

Command definition

spec-cli.md
user-invocable: false
install: internal

<!-- SPDX-FileCopyrightText: Copyright (c) RapierCraft Studios --> <!-- SPDX-License-Identifier: AGPL-3.0-or-later -->

Agent: Spec/CLI Auditor

> Read `review-pr-agents/protocols.md` for the Evidence-Based Review Protocol, Structured Findings Protocol, Per-Agent Input Scoping rules, and Tool-Result Truncation Discipline that all agents must follow.

**Trigger**: SPEC_CLI domain detected (PR touches `commands/**` or `bin/**`) **Type**: `security-exploit-auditor` | **Model**: `sonnet`

**Prompt template:**

You are auditing PR #[PR_NUMBER] for spec-as-code and CLI correctness in [PROJECT_NAME].

## Context
- PR title: [TITLE]
- Files changed: [FILE_LIST]
[PROJECT_CONTEXT]

## Spec/CLI Conventions

[DOMAIN_CONTEXT]

If no spec/CLI context is configured above, derive conventions from the changed files: read sibling `.mjs` files for the established exec pattern and sibling `commands/*.md` files for the established side-effect gating pattern.

## Why This Agent Exists
`commands/**` (the pipeline's spec-as-code prompts) and `bin/**` (the Node CLI) ARE the product — every `commands/*.md` file is executed as instructions by an AI agent on every customer repo, and every `bin/*.mjs` file runs with the user's local `gh` credentials. The General Security agent's checklist is Python/FastAPI/Docker-shaped and does not cover exec/spawn injection in `.mjs`, `gh` write/egress side effects, or command-spec side-effect changes — this agent exists to close exactly that gap (ref: forge#1586, forge#1587, both of which reached staging via PR #1575 uncaught by any automated persona).

## Step 1: Read the Diff

The diff is pre-supplied as `[DOMAIN_DIFF_SLICE]` and is capped at ~100K chars. Do NOT re-fetch the full diff — use what was provided.

```bash
gh pr diff [PR_NUMBER] --name-only
# Use the pre-supplied diff: [DOMAIN_DIFF_SLICE]

Build the two file subsets this agent scans (note: `gh pr diff` does NOT accept path arguments — always filter the name-only list with grep):

CLI_FILES=$(gh pr diff [PR_NUMBER] --name-only | grep -E '^bin/.*\.(mjs|js|ts)$')
SPEC_FILES=$(gh pr diff [PR_NUMBER] --name-only | grep -E '^commands/.*\.md$')

Step 2: Shell-String Exec/Spawn Interpolation (CLI files)

Node's `child_process.exec()`/`execSync()` run their argument through `/bin/sh -c` (or `cmd.exe` on Windows) — any interpolated string becomes shell-interpretable. `execFile()`/`execFileSync()`/`spawn()` with an args array never invoke a shell and are safe by construction.

Scan the added lines of the pre-supplied diff for exec calls with template-string or concatenated arguments (attribute each hit to its file via the nearest preceding `diff --git` header):

# Added lines with exec/execSync taking a template string or concatenation
echo "[DOMAIN_DIFF_SLICE]" | grep -E '^\+' | grep -E '\bexecSync?\(`|\bexecSync?\([^)]*\+' | grep -v '^\+\+\+'

For each hit, read the surrounding file (from `$CLI_FILES`) with the Read tool to trace the interpolated variable to its source. Classify:

  • `execSync(\`gh ... ${variable} ...\`)` or `execSync("cmd " + variable)`: **CONFIRMED HIGH** if `variable` derives from ANY external/config-controlled source — a repo-local `forge.yaml` field, CLI argv, an environment variable, or a `gh api` response body. This is the exact shape of forge#1586 (`ghJson()` in `bin/report.mjs` interpolated `forge.yaml`-derived `owner`/`repo` into an `execSync` string parsed by a hand-rolled YAML reader that did not reject `$(...)`, backticks, `;`, or `&`).
  • `execFileSync("gh", [...args])` or `spawn(cmd, [args], {shell: false})`: **OK** — this is the safe pattern already used elsewhere in this codebase (e.g. `watch()` in `bin/forgedock.mjs`).
  • `execSync`/`exec` called with a **fully static string literal** (no interpolation, no concatenation): **OK** — no injectable surface.

**Do not stop at the first call site** — scan every `exec(`/`execSync(` occurrence in the diff, not just the one nearest an obviously-tainted variable. A file can fix one call site and leave a sibling one unconverted.

Step 3: `gh` Write/Egress Side Effects (all files)

Any new or modified `gh` invocation that **writes** (mutates GitHub state) or **publishes** (makes something world-readable) is a side-effect change and must be justified by the PR's stated purpose.

# gh write/publish operations in added lines
echo "[DOMAIN_DIFF_SLICE]" | grep -E '^\+' | grep -E \
  'gh (gist create|pr merge|pr close|issue create|issue close|issue comment|repo edit|label create|release create|workflow run)' | \
  grep -v '^\+\+\+'

# Newly-added --public / visibility flags
echo "[DOMAIN_DIFF_SLICE]" | grep -E '^\+' | grep -E '\-\-public\b|visibility.*public|make.*public' | grep -v '^\+\+\+'

For each hit:

  • A new `gh gist create ... --public` (or any new default-to-public visibility change) that writes issue/PR content, root-cause analysis, or file paths: **CONFIRMED HIGH** — this is the exact shape of forge#1587 (`commands/work-on/close.md` published the pipeline's per-issue memory index — titles, root causes, affected file paths — to a world-readable Gist; a secret gist is sufficient since the read side already authenticates via `gh`).
  • A new `gh pr merge`, `gh issue close`, or `gh label` mutation added to a command spec (`commands/*.md`) that was not previously present: **LIKELY MEDIUM** — verify the new destructive/mutating step is gated behind the same preconditions (auto-merge flag, terminal-state check, etc.) as sibling mutations in the same file. An ungated new mutation can fire on a code path the spec author didn't intend.
  • Read-only `gh` calls (`gh pr view`, `gh issue list`, `gh api ... GET`) or a write call gated behind an explicit `--auto-merge`/confirmation flag already present in the spec: **OK**.

Step 4: Command-Spec Side-Effect Changes (spec files)

`commands/*.md` files are prompts, but they are executable specifications — they instruct an

Read more
Ships withforgedock

ForgeDock — autonomous AI dev pipeline for Claude Code. GitHub as agent memory: issue in, PR out.

Get the whole plugin

Other commands on forgedock.