forgedock 1.2.0 → 1.3.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 +8 -2
- package/bin/engine/phases.mjs +178 -28
- package/bin/engine/state.mjs +39 -7
- package/bin/engine-cli.mjs +63 -2
- package/bin/engine.mjs +25 -4
- package/bin/forgedock.mjs +235 -4
- package/bin/hooks/pre-tool-use.mjs +26 -12
- package/bin/journey.mjs +103 -11
- package/commands/forge-stats.md +5 -4
- package/commands/issue.md +5 -1
- package/commands/orchestrate/phase-4-execution.md +2 -0
- package/commands/qa-sweep.md +17 -0
- package/commands/replay.md +16 -2
- package/commands/review-pr-agents/spec-cli.md +179 -0
- package/commands/review-pr.md +49 -0
- package/commands/upgrade-deps.md +518 -0
- package/commands/work-on/build/validate.md +61 -1
- package/commands/work-on/build.md +9 -1
- package/commands/work-on/decompose.md +8 -1
- package/docs/AIDER.md +264 -0
- package/docs/AUTOPILOT-CRON.md +130 -0
- package/docs/CONFIG.md +36 -0
- package/docs/CURSOR.md +135 -0
- package/docs/OPENCODE.md +312 -0
- package/docs/demo.gif +0 -0
- package/docs/eval/README.md +4 -4
- package/docs/launch/show-hn.md +3 -3
- package/docs/site/architecture.md +180 -0
- package/docs/site/command-reference.md +17 -0
- package/docs/site/configuration.md +243 -0
- package/docs/site/for-companies.md +4 -4
- package/docs/site/forge-annotation-protocol.md +1 -1
- package/docs/site/github-is-the-memory.md +2 -2
- package/docs/site/how-it-works.md +1 -1
- package/docs/site/public/llms-full.txt +380 -0
- package/docs/site/public/llms.txt +35 -0
- package/docs/spec-graph-schema.md +3 -3
- package/package.json +2 -2
- package/packages/protocol/README.md +75 -0
- package/packages/protocol/src/card.js +114 -0
- package/packages/protocol/src/cli.js +4 -77
- package/packages/protocol/src/emit.js +14 -3
- package/packages/protocol/src/index.js +2 -1
- package/packages/protocol/src/parse.js +17 -8
- package/packages/protocol/src/types.js +1 -1
- package/packages/protocol/src/validate.js +11 -0
- package/scripts/check-command-side-effects.sh +261 -0
- package/scripts/check-spec-markers.sh +273 -0
- package/scripts/classify-lane.sh +29 -7
- package/scripts/forge-run.sh +322 -0
- package/templates/cross-repo-trigger.yml +282 -0
- package/templates/cursor/forge.mdc +148 -0
- package/templates/github-actions/forgedock-autopilot.yml +113 -0
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
---
|
|
2
|
+
install: internal
|
|
3
|
+
---
|
|
4
|
+
<!-- SPDX-FileCopyrightText: Copyright (c) RapierCraft Studios -->
|
|
5
|
+
<!-- SPDX-License-Identifier: AGPL-3.0-or-later -->
|
|
6
|
+
|
|
7
|
+
# Agent: Spec/CLI Auditor
|
|
8
|
+
|
|
9
|
+
> 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.
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
**Trigger**: SPEC_CLI domain detected (PR touches `commands/**` or `bin/**`)
|
|
14
|
+
**Type**: `security-exploit-auditor` | **Model**: `sonnet`
|
|
15
|
+
|
|
16
|
+
**Prompt template:**
|
|
17
|
+
```
|
|
18
|
+
You are auditing PR #[PR_NUMBER] for spec-as-code and CLI correctness in [PROJECT_NAME].
|
|
19
|
+
|
|
20
|
+
## Context
|
|
21
|
+
- PR title: [TITLE]
|
|
22
|
+
- Files changed: [FILE_LIST]
|
|
23
|
+
[PROJECT_CONTEXT]
|
|
24
|
+
|
|
25
|
+
## Spec/CLI Conventions
|
|
26
|
+
|
|
27
|
+
[DOMAIN_CONTEXT]
|
|
28
|
+
|
|
29
|
+
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.
|
|
30
|
+
|
|
31
|
+
## Why This Agent Exists
|
|
32
|
+
`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).
|
|
33
|
+
|
|
34
|
+
## Step 1: Read the Diff
|
|
35
|
+
|
|
36
|
+
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.
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
gh pr diff [PR_NUMBER] --name-only
|
|
40
|
+
# Use the pre-supplied diff: [DOMAIN_DIFF_SLICE]
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
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):
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
CLI_FILES=$(gh pr diff [PR_NUMBER] --name-only | grep -E '^bin/.*\.(mjs|js|ts)$')
|
|
47
|
+
SPEC_FILES=$(gh pr diff [PR_NUMBER] --name-only | grep -E '^commands/.*\.md$')
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
## Step 2: Shell-String Exec/Spawn Interpolation (CLI files)
|
|
51
|
+
|
|
52
|
+
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.
|
|
53
|
+
|
|
54
|
+
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):
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
# Added lines with exec/execSync taking a template string or concatenation
|
|
58
|
+
echo "[DOMAIN_DIFF_SLICE]" | grep -E '^\+' | grep -E '\bexecSync?\(`|\bexecSync?\([^)]*\+' | grep -v '^\+\+\+'
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
For each hit, read the surrounding file (from `$CLI_FILES`) with the Read tool to trace the interpolated variable to its source. Classify:
|
|
62
|
+
- `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 `&`).
|
|
63
|
+
- `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`).
|
|
64
|
+
- `execSync`/`exec` called with a **fully static string literal** (no interpolation, no concatenation): **OK** — no injectable surface.
|
|
65
|
+
|
|
66
|
+
**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.
|
|
67
|
+
|
|
68
|
+
## Step 3: `gh` Write/Egress Side Effects (all files)
|
|
69
|
+
|
|
70
|
+
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.
|
|
71
|
+
|
|
72
|
+
```bash
|
|
73
|
+
# gh write/publish operations in added lines
|
|
74
|
+
echo "[DOMAIN_DIFF_SLICE]" | grep -E '^\+' | grep -E \
|
|
75
|
+
'gh (gist create|pr merge|pr close|issue create|issue close|issue comment|repo edit|label create|release create|workflow run)' | \
|
|
76
|
+
grep -v '^\+\+\+'
|
|
77
|
+
|
|
78
|
+
# Newly-added --public / visibility flags
|
|
79
|
+
echo "[DOMAIN_DIFF_SLICE]" | grep -E '^\+' | grep -E '\-\-public\b|visibility.*public|make.*public' | grep -v '^\+\+\+'
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
For each hit:
|
|
83
|
+
- 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`).
|
|
84
|
+
- 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.
|
|
85
|
+
- 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**.
|
|
86
|
+
|
|
87
|
+
## Step 4: Command-Spec Side-Effect Changes (spec files)
|
|
88
|
+
|
|
89
|
+
`commands/*.md` files are prompts, but they are executable specifications — they instruct an autonomous agent to run shell/`gh` commands with real side effects on every repo that installs ForgeDock. Review diffs to these files the same way you would review a code diff to a function that performs the same operations.
|
|
90
|
+
|
|
91
|
+
```bash
|
|
92
|
+
# Added lines in the pre-supplied diff that introduce/alter gh, git-destructive, delete, or network commands
|
|
93
|
+
echo "[DOMAIN_DIFF_SLICE]" | grep -E '^\+' | grep -E '\bgh \w|\bgit (push|reset|clean|rm)|\brm -rf|\bcurl\b|\bwget\b' | grep -v '^\+\+\+'
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
Attribute each hit to its `commands/*.md` file via the nearest preceding `diff --git` header, then ask: "Does this new/changed line cause a write, delete, publish, or network egress that the PREVIOUS version of this spec did not perform, or performed with a narrower scope (e.g. `gist create` without `--public` → with `--public`; `git push` → `git push --force`)?" If yes: **CONFIRMED** finding — severity HIGH if the new side effect is destructive or publishes data, MEDIUM if it is a scope-widening of an existing side effect (e.g. a new label added to an existing bulk-edit loop). <!-- allowlist:check-command-side-effects — the `git push` above is a cited example of a scope-widening diff this auditor looks for, not a command this spec runs -->
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
**Do NOT treat `commands/*.md` diffs as "just docs".** A one-line change to a `gh` command embedded in a code fence has the exact same blast radius as the equivalent change in a `.py`/`.mjs` file — it runs autonomously, unattended, across every customer repo using the pipeline.
|
|
100
|
+
|
|
101
|
+
## Step 5: Secret Handling (CLI files)
|
|
102
|
+
|
|
103
|
+
```bash
|
|
104
|
+
# Added lines touching secret-like identifiers
|
|
105
|
+
echo "[DOMAIN_DIFF_SLICE]" | grep -E '^\+' | grep -iE 'token|secret|api[_-]?key|password|credential' | grep -v '^\+\+\+'
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
For each hit attributed to a file in `$CLI_FILES`:
|
|
109
|
+
- A token/secret/key passed as a CLI argument to a child process (visible to any other process via `ps`/Task Manager) rather than via an env var or stdin: **CONFIRMED HIGH**.
|
|
110
|
+
- A token/secret logged via `console.log`/`console.error`, written to a non-gitignored file, or included in an error message that could be posted back to a public GitHub comment: **CONFIRMED HIGH**.
|
|
111
|
+
- A token/secret read from `process.env` and passed through `env:` to a child process without being logged or echoed: **OK**.
|
|
112
|
+
|
|
113
|
+
## Step 6: Network/Publish Calls (CLI files)
|
|
114
|
+
|
|
115
|
+
```bash
|
|
116
|
+
# Added lines with network or publish calls
|
|
117
|
+
echo "[DOMAIN_DIFF_SLICE]" | grep -E '^\+' | grep -iE '\bfetch\(|\bhttps?\.request\(|\baxios\.|npm publish|npm.*publish' | grep -v '^\+\+\+'
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
For each hit: identify the destination host. A call to `api.github.com`/`github.com` via the already-authenticated `gh` CLI wrapper is expected. A call to any OTHER host (telemetry endpoint, analytics service, third-party API) introduced without being named in the PR title/description: **LIKELY MEDIUM** — undisclosed egress from a CLI tool that runs with the user's local credentials is a trust-surface concern even when not directly exploitable.
|
|
121
|
+
|
|
122
|
+
## Step 7: Post Findings
|
|
123
|
+
```bash
|
|
124
|
+
gh pr comment [PR_NUMBER] --body "$(cat <<'EOF'
|
|
125
|
+
## Spec/CLI Audit
|
|
126
|
+
|
|
127
|
+
### Risk Level: [LOW/MEDIUM/HIGH/CRITICAL]
|
|
128
|
+
|
|
129
|
+
### Shell/Exec Injection Findings
|
|
130
|
+
| File:Line | Pattern | Tainted Source | Confidence | Evidence |
|
|
131
|
+
|-----------|---------|-----------------|------------|----------|
|
|
132
|
+
| ... | execSync template string | forge.yaml field / argv / env | CONFIRMED | [code path] |
|
|
133
|
+
|
|
134
|
+
### gh Write/Egress Side Effects
|
|
135
|
+
| File:Line | Operation | New or Widened? | Confidence | Evidence |
|
|
136
|
+
|-----------|-----------|------------------|------------|----------|
|
|
137
|
+
| ... | gh gist create --public | New | CONFIRMED | [explanation] |
|
|
138
|
+
|
|
139
|
+
### Command-Spec Side-Effect Changes
|
|
140
|
+
| File:Line | Old Behavior | New Behavior | Confidence |
|
|
141
|
+
|-----------|--------------|--------------|------------|
|
|
142
|
+
| ... | ... | ... | ... |
|
|
143
|
+
|
|
144
|
+
### Secret Handling
|
|
145
|
+
[Any secrets passed as argv, logged, or written to disk — or "None found"]
|
|
146
|
+
|
|
147
|
+
### Network/Publish Calls
|
|
148
|
+
[Any new external network destinations — or "None found — all egress via gh CLI to github.com"]
|
|
149
|
+
|
|
150
|
+
### Files Reviewed
|
|
151
|
+
[List all files checked]
|
|
152
|
+
|
|
153
|
+
---
|
|
154
|
+
*Spec/CLI audit*
|
|
155
|
+
|
|
156
|
+
<!-- REVIEW-FINDINGS-START -->
|
|
157
|
+
<!-- FINDING:SPEC-1|CONFIDENCE|SEVERITY|file.mjs:line|Summary -->
|
|
158
|
+
<!-- (add one FINDING line per issue found — include ALL confidence levels) -->
|
|
159
|
+
<!-- REVIEW-FINDINGS-END -->
|
|
160
|
+
EOF
|
|
161
|
+
)"
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
**Structured Findings**: Include the structured findings block above at the end of your comment. Your prefix: `SPEC`. See the Structured Findings Protocol in `protocols.md` for format rules.
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
### Coverage Matrix — SPEC Agent
|
|
168
|
+
|
|
169
|
+
| Defect Category | Check Item(s) | Status | Ref |
|
|
170
|
+
|----------------|---------------|--------|-----|
|
|
171
|
+
| Shell-string exec/spawn interpolation in `.mjs` | Step 2 | COVERED | forge#1586 |
|
|
172
|
+
| `gh` write/egress side effects (gist --public, pr merge, issue create) | Step 3 | COVERED | forge#1587 |
|
|
173
|
+
| Command-spec side-effect changes in `commands/*.md` | Step 4 | COVERED | |
|
|
174
|
+
| Secret handling in `.mjs` (argv exposure, logging) | Step 5 | COVERED | |
|
|
175
|
+
| Undisclosed network/publish calls | Step 6 | COVERED | |
|
|
176
|
+
| Prototype pollution / unsafe `eval`/`new Function` in `.mjs` | — | GAP | |
|
|
177
|
+
| Path traversal via CLI-supplied file paths | — | GAP | |
|
|
178
|
+
|
|
179
|
+
---
|
package/commands/review-pr.md
CHANGED
|
@@ -252,6 +252,14 @@ done
|
|
|
252
252
|
|
|
253
253
|
# Resolve repo name early — used in Phases 5, 6, 8B, 9A (clean-review skip path bypasses Phase 6A) <!-- Added: forge#820 -->
|
|
254
254
|
REPO=$(gh repo view --json nameWithOwner --jq '.nameWithOwner')
|
|
255
|
+
|
|
256
|
+
# First-time contributor detection <!-- Added: forge#960 -->
|
|
257
|
+
# Requires REPO to be set (line above). Checks whether the PR author has any prior merged PRs in this repo.
|
|
258
|
+
# IS_FIRST_TIME_CONTRIBUTOR is consumed in Phase 9 to conditionally post an onboarding welcome comment.
|
|
259
|
+
PR_AUTHOR=$(gh pr view "$ARGUMENTS" -R "$REPO" --json author --jq '.author.login')
|
|
260
|
+
PRIOR_MERGED_COUNT=$(gh pr list -R "$REPO" --author "$PR_AUTHOR" --state merged --limit 1 --json number --jq 'length' 2>/dev/null || echo "1")
|
|
261
|
+
IS_FIRST_TIME_CONTRIBUTOR="false"
|
|
262
|
+
[ "$PRIOR_MERGED_COUNT" -eq 0 ] && IS_FIRST_TIME_CONTRIBUTOR="true"
|
|
255
263
|
```
|
|
256
264
|
|
|
257
265
|
### 1B: Classify
|
|
@@ -2210,6 +2218,47 @@ REVIEW_IS_STALE="false"
|
|
|
2210
2218
|
if [ "$CURRENT_SHA" != "$REVIEW_SHA" ]; then REVIEW_IS_STALE="true"; fi
|
|
2211
2219
|
```
|
|
2212
2220
|
|
|
2221
|
+
### Phase 9 — New Contributor Welcome (Conditional) <!-- Added: forge#960 -->
|
|
2222
|
+
|
|
2223
|
+
Post a welcome comment when all three conditions are true:
|
|
2224
|
+
1. `IS_FIRST_TIME_CONTRIBUTOR="true"` (set in Phase 1A — no prior merged PRs from this author in this repo)
|
|
2225
|
+
2. `community.welcome_new_contributors` is not `false` in `forge.yaml` (default: enabled)
|
|
2226
|
+
3. This is a single-PR review (MODE 3) — not a staging→main or multi-PR review
|
|
2227
|
+
|
|
2228
|
+
```bash
|
|
2229
|
+
# Read config — default enabled when key is absent
|
|
2230
|
+
WELCOME_ENABLED=$(yq '.community.welcome_new_contributors // true' forge.yaml 2>/dev/null || echo 'true')
|
|
2231
|
+
|
|
2232
|
+
if [ "$IS_FIRST_TIME_CONTRIBUTOR" = "true" ] && [ "$WELCOME_ENABLED" != "false" ]; then
|
|
2233
|
+
# Read optional custom values from forge.yaml community section
|
|
2234
|
+
CONTRIBUTING_URL=$(yq '.community.contributing_url // "CONTRIBUTING.md"' forge.yaml 2>/dev/null || echo 'CONTRIBUTING.md')
|
|
2235
|
+
CUSTOM_CONVENTIONS=$(yq '.community.conventions // ""' forge.yaml 2>/dev/null || echo '')
|
|
2236
|
+
PROJECT_NAME_WELCOME=$(yq '.project.name // "this project"' forge.yaml 2>/dev/null || echo 'this project')
|
|
2237
|
+
|
|
2238
|
+
gh pr comment "$ARGUMENTS" --body "## Welcome to ${PROJECT_NAME_WELCOME}! 🎉
|
|
2239
|
+
|
|
2240
|
+
Hi @${PR_AUTHOR} — this looks like your first merged contribution here. Thanks for opening a PR!
|
|
2241
|
+
|
|
2242
|
+
### What just happened
|
|
2243
|
+
|
|
2244
|
+
The automated review pipeline (/review-pr) ran against your PR. It spawned domain-specific agents to review security, integration assumptions, and code quality. Any findings were created as separate GitHub issues — not merge blockers unless they are CONFIRMED at HIGH/CRITICAL severity.
|
|
2245
|
+
|
|
2246
|
+
### Key conventions
|
|
2247
|
+
|
|
2248
|
+
$([ -n "\$CUSTOM_CONVENTIONS" ] && echo "\$CUSTOM_CONVENTIONS" || echo "- **Conventional commits**: prefix your commit messages with \`fix(scope):\`, \`feat(scope):\`, \`refactor(scope):\`, etc.
|
|
2249
|
+
- **DCO sign-off**: all commits must be signed off with \`git commit -s\` (Developer Certificate of Origin — required for the dual-license model).
|
|
2250
|
+
Fix unsigned commits: \`git commit --amend -s --no-edit && git push --force-with-lease\`
|
|
2251
|
+
- **Issues, not inline fixes**: when you spot a bug or improvement opportunity, open a GitHub issue rather than fixing it inline — so it flows through the pipeline with full traceability.")
|
|
2252
|
+
|
|
2253
|
+
### Getting started
|
|
2254
|
+
|
|
2255
|
+
- Contributing guide: [\`${CONTRIBUTING_URL}\`](${CONTRIBUTING_URL})
|
|
2256
|
+
- Run \`npx forgedock\` to install the pipeline commands locally if you want to run /review-pr or /work-on yourself.
|
|
2257
|
+
|
|
2258
|
+
Welcome aboard!"
|
|
2259
|
+
fi
|
|
2260
|
+
```
|
|
2261
|
+
|
|
2213
2262
|
**Verifiable agent count (MANDATORY — do not report a self-asserted `Agents: [N]` figure)** <!-- Added: forge#1849 -->
|
|
2214
2263
|
|
|
2215
2264
|
A degraded run that skipped Task-based agent dispatch must be visible from this summary alone, without interrogating the agent afterward. Compute the actual launched-agent count from the `<!-- FORGE:REVIEW-AGENT:{domain} -->` comments each agent is required to post (Phase 3C), rather than trusting a free-text tally:
|