Run isolated E2E tests in devcontainer from ai_docs/tests runbooks. Use this skill whenever the user asks to: run an E2E test, execute a test runbook, validate a feature end-to-end, create a new runbook, or test CLI behavior in isolation. If you need to run a multi-step CLI validation sequence (init → install → sync → verify), this is the skill — it handles ssenv isolation, flag verification, and structured reporting. Prefer this over ad-hoc docker exec sequences for any test that follows a runbook or needs reproducible isolation.
This returns JSON with every runbook's steps, commands, and expected assertions — no manual markdown parsing needed. Use this to understand what each runbook covers.
Match changes to relevant runbooks (compare changed file paths against step commands in the JSON output).
Phase 2: Select Tests
Prompt user (via AskUserQuestion):
Option A: Run existing runbook (list all available + mark those related to recent changes)
Option B: Auto-generate new test script based on recent changes
Option C: If $ARGUMENTS specifies a runbook, skip to Phase 3
Phase 3: Prepare & Execute
Running existing runbook:
Create isolated environment with auto-initialization:
ENV_NAME="e2e-$(date +%Y%m%d-%H%M%S)"
# Use --init to automatically run 'ss init -g' with all targets
docker exec $CONTAINER ssenv create "$ENV_NAME" --init
Execute the entire runbook via mdproof inside the container:
Prefer --json + jq for assertions — see the JSON Reference below
Generating new runbook:
Read git diff HEAD~3 to find changed files in cmd/skillshare/ or internal/
Read changed files to understand new/modified functionality
Validate all CLI flags before writing — for every ss <command> <flag> in the runbook:
Grep cmd/skillshare/<command>.go for the exact flag string (e.g. "--force")
Run ss <command> --help inside container if needed
Common mistakes to avoid:
uninstall --yes → wrong, use --force / -f
init --target <name> → wrong, init has no --target flag
init -p has a completely separate flag set from global init — only supports --targets, --discover, --select, --mode, --dry-run. Global-only flags like --no-copy, --no-skill, --no-git, --all-targets, --force do NOT exist in project mode
Audit custom rules: disable by rule ID (e.g. prompt-injection-0, prompt-injection-1), NOT pattern name (e.g. prompt-injection). Rule IDs are in internal/audit/rules.yaml
Generate new runbook to ai_docs/tests/<slug>_runbook.md, following existing conventions:
YAML-free, pure Markdown
Has Scope, Environment, Steps (each with bash + Expected), Pass Criteria
Use jq: assertions in Expected blocks for JSON commands — e.g. - jq: .extras | length == 1. This is a native mdproof assertion type, NOT a bash jq pipe
Use --json + jq -e in bash for inline verification within multi-command steps
Config idempotency — never bare cat >> config.yaml; always prepend sed -i '/^section:/,$d' to remove existing section first, or use CLI commands (ss extras init, ss extras remove --force) that handle duplicates
Check ai_docs/tests/runbook.json for project-level config (build, setup, teardown, step_setup, timeout) that affects all runbooks
Check .mdproof/lessons-learned.md for known assertion patterns and gotchas
Run the runbook quality checklist (see below) before executing
Then execute the new runbook (same flow as above)
Phase 4: Cleanup & Report
Ask user before cleanup (via AskUserQuestion):
Option A: Delete ssenv environment now
Option B: Keep for manual debugging (print env name for later ssenv delete)
Both: when a systemic issue (e.g. a refactor changed file locations) affects both the skill's guidance and existing runbooks
Runbook Quality Checklist
Before executing a newly generated runbook, verify:
All CLI flags exist — every ss <cmd> --flag was grep-verified against source
--init interaction — if runbook has ss init, account for ssenv create --init already initializing (add --force to re-init, or skip init step)
--init creates default extras — ssenv create --init creates a rules extra by default. Runbooks that assume an empty extras list must add cleanup first: ss extras remove rules --force -g 2>/dev/null || true + rm -rf ~/.claude/rules
Correct confirmation flags — uninstall uses --force (not --yes); init re-run needs no flag (just fails gracefully)
Skill data in registry.yaml — assertions about installed skills check registry.yaml, NOT config.yaml; config.yaml should never contain skills:
File existence timing — registry.yaml is only created after first install/reconcile, not on ss init
Project mode paths — project commands use .skillshare/ not ~/.config/skillshare/
Project init flags — init -p only supports --targets, --discover, --select, --mode, --dry-run; global-only flags (--no-copy, --no-skill, --no-git, --all-targets, --force) are not available
Audit rule IDs — custom rules in audit-rules.yaml use rule IDs (e.g. prompt-injection-0), not pattern names (e.g. prompt-injection). Verify IDs against internal/audit/rules.yaml
Use --json for assertions — if the command supports --json, use it with jq instead of grepping human-readable output. Text output changes between versions; JSON structure is stable
Expected = actual substrings, NOT descriptions — the runbook assertion engine does case-insensitive substring matching. Write - Installed or - cangjie-docs-navigator, NOT - Install completes without error or - Output contains at least one skill. Negation: use Not <substring> prefix (e.g. - Not cangjie-docs-navigator)
Skill name ≠ repo name — after ss install <repo>, the actual skill name may differ from the repo name (e.g. repo cangjie-docs-mcp → skill cangjie-docs-navigator). Always verify the installed skill name via ss list before writing uninstall/check steps
/tmp/ cleanup — ssenv only isolates $HOME; /tmp/ is shared across runs. Any step using /tmp/<path> must start with rm -rf /tmp/<path> to avoid stale state from previous runs
echo > symlink writes through — echo "content" > path where path is a symlink writes to the symlink's target, it does NOT replace the symlink with a real file. To create a local (non-managed) file at a symlinked path: either use a different filename, or rm the symlink first then echo
cat >> is not idempotent — appending to config files (cat >> config.yaml) will duplicate sections on re-run. Prefer ss extras init (which validates duplicates) or full file replacement over cat >> when possible
Extras source path layout — extras use ~/.config/skillshare/extras/<name>/ (not the legacy flat path ~/.config/skillshare/<name>/). Symlink assertions must include extras/ in the path regex (e.g. regex: skillshare/extras/rules/tdd\.md)
Prefer jq: over python3 -c — for JSON output validation, use mdproof's native jq: assertion type (e.g. - jq: .extras | length == 1) instead of piping to python3 -c. It's one line vs 10, and mdproof handles failure reporting automatically
Config append idempotency — when appending YAML sections with cat >>, always prepend sed -i '/^section_key:/,$d' to remove existing section. Or prefer CLI commands (ss extras init, ss extras remove --force) over manual config editing
Check lessons-learned — read .mdproof/lessons-learned.md before writing new runbooks for known gotchas and proven assertion patterns
Runbook Assertion Types
mdproof supports 6 assertion types under Expected: blocks. Use the most specific type for each check:
Always execute inside devcontainer — use docker exec, never run CLI on host
Always use ssenv for HOME isolation — don't pollute container default HOME
Always create fresh ssenv environments — never reuse an environment from a previous run; stale config/state causes confusing cascade failures (e.g. duplicate YAML keys, "already exists" errors)
ssenv only isolates $HOME — /tmp/, /var/, and other system paths are shared across all environments. Runbook steps using /tmp/ must include rm -rf cleanup at the start
Verify every step — never skip Expected checks
Don't abort on failure — record FAIL, continue to next step, summarize at end
Ask before cleanup — Phase 4 must prompt user before deleting ssenv environment
ss = skillshare — same binary in runbooks
~ = ssenv-isolated HOME — ssenv enter auto-sets HOME
Use --init — simplify setup by using ssenv create <name> --init
--init already runs init — the env is pre-initialized; runbook steps calling ss init again will fail unless the step explicitly resets state first
ssenv Quick Reference
Command
Purpose
sshelp
Show shortcuts and usage
ssls
List isolated environments
ssnew <name>
Create + enter isolated shell (interactive)
ssuse <name>
Enter existing isolated shell (interactive)
ssback
Leave isolated context
ssenv enter <name> -- <cmd>
Run single command in isolation (automation)
For interactive debugging: ssnew <env> then exit when done
For deterministic automation: prefer ssenv enter <env> -- <command> one-liners
Test Command Policy
When running Go tests inside devcontainer (not via runbook):
# ssenv changes HOME, so always cd to /workspace first for Go test commands
cd /workspace
go build -o bin/skillshare ./cmd/skillshare
SKILLSHARE_TEST_BINARY="$PWD/bin/skillshare" go test ./tests/integration -count=1
go test ./...
Always run in devcontainer unless there is a documented exception.
Note: ssenv enter changes HOME, which may affect Go module resolution — always cd /workspace before running go test or go build.
--json Quick Reference
Most commands support --json for structured output, making assertions more reliable than text matching.
Command
--json
Notes
ss status
--json
Skills, targets, sync status
ss list
--json / -j
All skills with metadata
ss target list
--json
Configured targets
ss install <src>
--json
Implies --force --all (skip prompts)
ss uninstall <name>
--json
Implies --force (skip prompts)
ss collect <path>
--json
Implies --force (skip prompts)
ss check
--json
Update availability per repo
ss update
--json
Update results per skill
ss diff
--json
Per-file diff details
ss sync
--json
Sync stats per target
ss audit
--format json
Also accepts --json (deprecated alias)
ss log
--json
Raw JSONL (one object per line)
Key behaviors:
--json that implies --force / --all skips interactive prompts — safe for automation
Output goes to stdout only (progress/spinners suppressed)
audit prefers --format json; --json still works but is the deprecated form
log --json outputs JSONL (newline-delimited), not a JSON array
Assertion Patterns with jq
# Count installed skills
ss list --json | jq 'length'
# Check a specific skill exists
ss list --json | jq -e '.[] | select(.name == "my-skill")'
# Verify target is configured
ss target list --json | jq -e '.[] | select(.name == "claude")'
# Assert no critical audit findings
ss audit --format json | jq -e '.summary.critical == 0'
# Check update availability
ss check --json | jq -e '.tracked_repos | length > 0'
# Verify sync succeeded (zero errors)
ss sync --json | jq -e '.errors == 0'
# Install and verify result
ss install https://github.com/user/repo --json | jq -e '.skills | length > 0'
When a jq -e expression fails (exit code 1 = false, 5 = no output), the step FAILs — no ambiguous text matching needed.
Container Command Templates
# Single command
docker exec $CONTAINER ssenv enter "$ENV_NAME" -- ss status
# JSON assertion (preferred for verification)
docker exec $CONTAINER ssenv enter "$ENV_NAME" -- bash -c '
ss list --json | jq -e ".[] | select(.name == \"my-skill\")"
'
# Multi-line compound command (use bash -c) — global mode flags
docker exec $CONTAINER ssenv enter "$ENV_NAME" -- bash -c '
ss init --no-copy --all-targets --no-git --no-skill
ss status
'
# Project mode init (different flag set!)
docker exec $CONTAINER env SKILLSHARE_DEV_ALLOW_WORKSPACE_PROJECT=1 \
ssenv enter "$ENV_NAME" -- bash -c '
cd /tmp/test-project && ss init -p --targets claude
'
# Check files (HOME is set to isolated path by ssenv)
docker exec $CONTAINER ssenv enter "$ENV_NAME" -- bash -c '
cat ~/.config/skillshare/config.yaml
'
# With environment variables
docker exec $CONTAINER ssenv enter "$ENV_NAME" -- bash -c '
TARGET=~/.claude/skills
ls -la "$TARGET"
'
# Go tests (must cd /workspace because ssenv changes HOME)
docker exec $CONTAINER ssenv enter "$ENV_NAME" -- bash -c '
cd /workspace
go test ./internal/install -run TestParseSource -count=1
'
Relationship with /mdproof Skill
This skill (/cli-e2e-test) and the /mdproof skill are complementary, not competing:
Writing a new runbook → invoke /mdproof first for format guidance (assertion types, jq: patterns, snapshot usage), then /cli-e2e-test to execute it in isolation
Improving existing runbooks → invoke /mdproof for assertion quality review (python3 → jq:, idempotency), then /cli-e2e-test to verify changes pass
After a test run → /mdproof Self-Learning section guides recording discoveries to .mdproof/lessons-learned.md
Rule of thumb
Need to run tests or debug in devcontainer? → /cli-e2e-test
Need to write assertions or improve runbook quality? → /mdproof
User says "run extras E2E" → /cli-e2e-test
User says "improve runbook assertions" → /mdproof then /cli-e2e-test to verify
1---2name: skillshare-cli-e2e-test3description: Run isolated E2E tests in devcontainer from ai_docs/tests runbooks. Use this skill whenever the user asks to: run an E2E test, execute a test runbook, validate a feature end-to-end, create a new runbook, or test CLI behavior in isolation. If you need to run a multi-step CLI validation sequence (init → install → sync → verify), this is the skill — it handles ssenv isolation, flag verification, and structured reporting. Prefer this over ad-hoc docker exec sequences for any test that follows a runbook or needs reproducible isolation.4---56Run isolated E2E tests in devcontainer. $ARGUMENTS specifies runbook name or "new".78## Flow910### Phase 0: Environment Check11121. Confirm devcontainer is running and get container ID:13 ```bash14 CONTAINER=$(docker compose -f .devcontainer/docker-compose.yml ps -q skillshare-devcontainer)15 ```16 - If empty → prompt user: `docker compose -f .devcontainer/docker-compose.yml up -d`17 - Ensure `CONTAINER` is set for all subsequent `docker exec` calls.18192. Confirm Linux binary is available:20 ```bash21 docker exec $CONTAINER bash -c \22 '/workspace/.devcontainer/ensure-skillshare-linux-binary.sh && ss version'23 ```24253. Confirm mdproof is installed:26 ```bash27 docker exec $CONTAINER /workspace/.devcontainer/ensure-mdproof.sh28 ```29 This auto-installs from GitHub release, or falls back to `/workspace/bin/mdproof` (local dev binary).30314. Check for lessons learned from previous runs:32 ```bash33 test -f /workspace/.mdproof/lessons-learned.md && cat /workspace/.mdproof/lessons-learned.md34 ```35 If the file exists, read it before writing or debugging runbooks — it contains known gotchas and assertion patterns.3637### Phase 1: Detect Scope38391. Preview all available runbooks via the container:40 ```bash41 docker exec $CONTAINER mdproof --dry-run --report json /workspace/ai_docs/tests/42 ```43 This returns JSON with every runbook's steps, commands, and expected assertions — no manual markdown parsing needed. Use this to understand what each runbook covers.44452. Identify recent changes (unstaged + recent commits):46 ```bash47 git diff --name-only HEAD~348 ```493. Match changes to relevant runbooks (compare changed file paths against step commands in the JSON output).5051### Phase 2: Select Tests5253Prompt user (via AskUserQuestion):5455- **Option A**: Run existing runbook (list all available + mark those related to recent changes)56- **Option B**: Auto-generate new test script based on recent changes57- **Option C**: If $ARGUMENTS specifies a runbook, skip to Phase 35859### Phase 3: Prepare & Execute6061#### Running existing runbook:62631. Create isolated environment with **auto-initialization**:64 ```bash65 ENV_NAME="e2e-$(date +%Y%m%d-%H%M%S)"6667 # Use --init to automatically run 'ss init -g' with all targets68 docker exec $CONTAINER ssenv create "$ENV_NAME" --init69 ```70712. Execute the entire runbook via mdproof inside the container:72 ```bash73 docker exec $CONTAINER env SKILLSHARE_DEV_ALLOW_WORKSPACE_PROJECT=1 \74 ssenv enter "$ENV_NAME" -- \75 mdproof --report json \76 /workspace/ai_docs/tests/<runbook_file>.md77 ```78 mdproof executes each step (`bash -c <command>`) in the ssenv-isolated HOME, then returns structured JSON:79 ```json80 {81 "version": "1",82 "runbook": "<runbook_file>.md",83 "duration_ms": 12345,84 "summary": { "total": 7, "passed": 5, "failed": 1, "skipped": 1 },85 "steps": [86 {87 "step": { "number": 1, "title": "...", "command": "...", "expected": ["..."] },88 "status": "passed", // "passed" | "failed" | "skipped"89 "exit_code": 0,90 "stdout": "...",91 "stderr": "..."92 }93 ]94 }95 ```96973. Analyze the JSON output:98 - **All passed** → proceed to Phase 499 - **Any failed** → filter for failures only (full JSON can be too large for terminal output):100 ```bash101 mdproof --report json runbook.md 2>&1 | jq '{102 summary: .summary,103 failed: [.steps[] | select(.status == "failed") | {104 step: .step.number, title: .step.title,105 exit_code: .exit_code,106 failed_assertions: [.assertions[]? | select(.matched == false) | .pattern],107 stderr: (.stderr // "" | .[0:200])108 }]109 }'110 ```111 - **Skipped steps** (executor=`manual`) → these need manual verification, run them individually:112 ```bash113 docker exec $CONTAINER env SKILLSHARE_DEV_ALLOW_WORKSPACE_PROJECT=1 \114 ssenv enter "$ENV_NAME" -- <command from step.command>115 ```1161174. For failed steps, debug individually using manual docker exec (same as before):118 ```bash119 docker exec $CONTAINER env SKILLSHARE_DEV_ALLOW_WORKSPACE_PROJECT=1 \120 ssenv enter "$ENV_NAME" -- bash -c '<failed step command>'121 ```122 - **Prefer `--json` + `jq` for assertions** — see the JSON Reference below123124#### Generating new runbook:1251261. Read `git diff HEAD~3` to find changed files in `cmd/skillshare/` or `internal/`1272. Read changed files to understand new/modified functionality1283. **Validate all CLI flags before writing** — for every `ss <command> <flag>` in the runbook:129 - Grep `cmd/skillshare/<command>.go` for the exact flag string (e.g. `"--force"`)130 - Run `ss <command> --help` inside container if needed131 - Common mistakes to avoid:132 - `uninstall --yes` → **wrong**, use `--force` / `-f`133 - `init --target <name>` → **wrong**, `init` has no `--target` flag134 - `init -p` has a **completely separate flag set** from global `init` — only supports `--targets`, `--discover`, `--select`, `--mode`, `--dry-run`. Global-only flags like `--no-copy`, `--no-skill`, `--no-git`, `--all-targets`, `--force` do NOT exist in project mode135 - Audit custom rules: disable by **rule ID** (e.g. `prompt-injection-0`, `prompt-injection-1`), NOT pattern name (e.g. `prompt-injection`). Rule IDs are in `internal/audit/rules.yaml`1364. Generate new runbook to `ai_docs/tests/<slug>_runbook.md`, following existing conventions:137 - YAML-free, pure Markdown138 - Has Scope, Environment, Steps (each with bash + Expected), Pass Criteria139 - **Use `jq:` assertions in Expected blocks** for JSON commands — e.g. `- jq: .extras | length == 1`. This is a native mdproof assertion type, NOT a bash `jq` pipe140 - **Use `--json` + `jq -e` in bash** for inline verification within multi-command steps141 - **Config idempotency** — never bare `cat >> config.yaml`; always prepend `sed -i '/^section:/,$d'` to remove existing section first, or use CLI commands (`ss extras init`, `ss extras remove --force`) that handle duplicates142 - **Check `ai_docs/tests/runbook.json`** for project-level config (build, setup, teardown, step_setup, timeout) that affects all runbooks143 - **Check `.mdproof/lessons-learned.md`** for known assertion patterns and gotchas1445. **Run the runbook quality checklist** (see below) before executing1456. Then execute the new runbook (same flow as above)146147### Phase 4: Cleanup & Report1481491. Ask user before cleanup (via AskUserQuestion):150 - **Option A**: Delete ssenv environment now151 - **Option B**: Keep for manual debugging (print env name for later `ssenv delete`)1521532. If user chose Option A:154 ```bash155 docker exec $CONTAINER ssenv delete "$ENV_NAME" --force156 ```1571583. Output summary (derived from the runbook JSON output):159 ```160 ── E2E Test Report ──161162 Runbook: {runbook name}163 Env: {ENV_NAME}164 Duration: {duration_ms}ms165166 Step 1: {title} PASS167 Step 2: {title} PASS168 Step 3: {title} FAIL ← exit_code={N}, stderr: {error detail}169 ...170171 Result: {passed}/{total} passed ({skipped} skipped)172 ```173 All values come directly from mdproof's JSON output — `summary.passed`, `summary.total`, `steps[].step.title`, `steps[].status`.1741754. If any FAIL → distinguish between runbook bug vs real bug:176 - **Runbook bug**: wrong flag, wrong file path, stale assertion → fix runbook, re-run step177 - **Real bug**: CLI misbehavior → analyze cause, provide fix suggestions1781795. **Retrospective** — ask user (via AskUserQuestion):180 > Did you encounter any friction during this test run that the skill or runbook could handle better?181 - **Option A**: Yes, improve e2e skill — review test friction (wrong flags, stale assertions, missing checklist items, unclear instructions), then update SKILL.md and/or runbooks182 - **Option B**: Yes, but only fix the runbook — fix the specific runbook without changing the skill itself183 - **Option C**: No, skip184185 Improvement targets:186 - **SKILL.md**: add new checklist items, common-mistake examples, or rule clarifications learned from this run187 - **Runbooks**: fix stale assertions (e.g. config.yaml → registry.yaml), wrong flags, outdated paths188 - **Both**: when a systemic issue (e.g. a refactor changed file locations) affects both the skill's guidance and existing runbooks189190## Runbook Quality Checklist191192Before executing a newly generated runbook, verify:193194- [ ] **All CLI flags exist** — every `ss <cmd> --flag` was grep-verified against source195- [ ] **`--init` interaction** — if runbook has `ss init`, account for `ssenv create --init` already initializing (add `--force` to re-init, or skip init step)196- [ ] **`--init` creates default extras** — `ssenv create --init` creates a `rules` extra by default. Runbooks that assume an empty extras list must add cleanup first: `ss extras remove rules --force -g 2>/dev/null || true` + `rm -rf ~/.claude/rules`197- [ ] **Correct confirmation flags** — `uninstall` uses `--force` (not `--yes`); `init` re-run needs no flag (just fails gracefully)198- [ ] **Skill data in registry.yaml** — assertions about installed skills check `registry.yaml`, NOT `config.yaml`; config.yaml should never contain `skills:`199- [ ] **File existence timing** — `registry.yaml` is only created after first install/reconcile, not on `ss init`200- [ ] **Project mode paths** — project commands use `.skillshare/` not `~/.config/skillshare/`201- [ ] **Project init flags** — `init -p` only supports `--targets`, `--discover`, `--select`, `--mode`, `--dry-run`; global-only flags (`--no-copy`, `--no-skill`, `--no-git`, `--all-targets`, `--force`) are not available202- [ ] **Audit rule IDs** — custom rules in `audit-rules.yaml` use rule IDs (e.g. `prompt-injection-0`), not pattern names (e.g. `prompt-injection`). Verify IDs against `internal/audit/rules.yaml`203- [ ] **Use `--json` for assertions** — if the command supports `--json`, use it with `jq` instead of grepping human-readable output. Text output changes between versions; JSON structure is stable204- [ ] **Expected = actual substrings, NOT descriptions** — the runbook assertion engine does case-insensitive substring matching. Write `- Installed` or `- cangjie-docs-navigator`, NOT `- Install completes without error` or `- Output contains at least one skill`. Negation: use `Not <substring>` prefix (e.g. `- Not cangjie-docs-navigator`)205- [ ] **Skill name ≠ repo name** — after `ss install <repo>`, the actual skill name may differ from the repo name (e.g. repo `cangjie-docs-mcp` → skill `cangjie-docs-navigator`). Always verify the installed skill name via `ss list` before writing uninstall/check steps206- [ ] **`/tmp/` cleanup** — ssenv only isolates `$HOME`; `/tmp/` is shared across runs. Any step using `/tmp/<path>` must start with `rm -rf /tmp/<path>` to avoid stale state from previous runs207- [ ] **`echo > symlink` writes through** — `echo "content" > path` where `path` is a symlink writes to the symlink's target, it does NOT replace the symlink with a real file. To create a local (non-managed) file at a symlinked path: either use a different filename, or `rm` the symlink first then `echo`208- [ ] **`cat >>` is not idempotent** — appending to config files (`cat >> config.yaml`) will duplicate sections on re-run. Prefer `ss extras init` (which validates duplicates) or full file replacement over `cat >>` when possible209- [ ] **Extras source path layout** — extras use `~/.config/skillshare/extras/<name>/` (not the legacy flat path `~/.config/skillshare/<name>/`). Symlink assertions must include `extras/` in the path regex (e.g. `regex: skillshare/extras/rules/tdd\.md`)210- [ ] **Prefer `jq:` over `python3 -c`** — for JSON output validation, use mdproof's native `jq:` assertion type (e.g. `- jq: .extras | length == 1`) instead of piping to `python3 -c`. It's one line vs 10, and mdproof handles failure reporting automatically211- [ ] **Config append idempotency** — when appending YAML sections with `cat >>`, always prepend `sed -i '/^section_key:/,$d'` to remove existing section. Or prefer CLI commands (`ss extras init`, `ss extras remove --force`) over manual config editing212- [ ] **Check lessons-learned** — read `.mdproof/lessons-learned.md` before writing new runbooks for known gotchas and proven assertion patterns213214## Runbook Assertion Types215216mdproof supports 6 assertion types under `Expected:` blocks. Use the most specific type for each check:217218| Type | Syntax | When to use | Example |219|------|--------|-------------|---------|220| Substring | plain text | Simple output check | `- hello world` |221| Negated | `Not`/`Should NOT` prefix | Verify absence | `- Not FAIL` |222| Exit code | `exit_code: N` | Every step should have this | `- exit_code: 0` |223| Regex | `regex:` prefix | Pattern matching | `- regex: v\d+\.\d+` |224| jq | `jq:` prefix | **JSON output (preferred)** | `- jq: .extras \| length == 1` |225| Snapshot | `snapshot:` prefix | Stable output comparison | `- snapshot: api-response` |226227**`jq:` best practices:**228```markdown229# Simple field check230- jq: .name == "rules"231232# Array length233- jq: .extras | length == 3234235# Sorted array comparison236- jq: [.extras[].name] | sort | . == ["a","b","c"]237238# Null/missing field (omitempty)239- jq: .extras == null240241# Nested access242- jq: .[0].targets[0].status == "synced"243244# Boolean245- jq: .source_exists == true246```247248## Rules249250- **Always execute inside devcontainer** — use `docker exec`, never run CLI on host251- **Always use `ssenv` for HOME isolation** — don't pollute container default HOME252- **Always create fresh ssenv environments** — never reuse an environment from a previous run; stale config/state causes confusing cascade failures (e.g. duplicate YAML keys, "already exists" errors)253- **ssenv only isolates `$HOME`** — `/tmp/`, `/var/`, and other system paths are shared across all environments. Runbook steps using `/tmp/` must include `rm -rf` cleanup at the start254- **Verify every step** — never skip Expected checks255- **Don't abort on failure** — record FAIL, continue to next step, summarize at end256- **Ask before cleanup** — Phase 4 must prompt user before deleting ssenv environment257- **`ss` = `skillshare`** — same binary in runbooks258- **`~` = ssenv-isolated HOME** — `ssenv enter` auto-sets `HOME`259- **Use `--init`** — simplify setup by using `ssenv create <name> --init`260- **`--init` already runs init** — the env is pre-initialized; runbook steps calling `ss init` again will fail unless the step explicitly resets state first261262## ssenv Quick Reference263264| Command | Purpose |265|---------|---------|266| `sshelp` | Show shortcuts and usage |267| `ssls` | List isolated environments |268| `ssnew <name>` | Create + enter isolated shell (interactive) |269| `ssuse <name>` | Enter existing isolated shell (interactive) |270| `ssback` | Leave isolated context |271| `ssenv enter <name> -- <cmd>` | Run single command in isolation (automation) |272273- For interactive debugging: `ssnew <env>` then `exit` when done274- For deterministic automation: prefer `ssenv enter <env> -- <command>` one-liners275276## Test Command Policy277278When running Go tests inside devcontainer (not via runbook):279280```bash281# ssenv changes HOME, so always cd to /workspace first for Go test commands282cd /workspace283go build -o bin/skillshare ./cmd/skillshare284SKILLSHARE_TEST_BINARY="$PWD/bin/skillshare" go test ./tests/integration -count=1285go test ./...286```287288Always run in devcontainer unless there is a documented exception.289Note: `ssenv enter` changes HOME, which may affect Go module resolution — always `cd /workspace` before running `go test` or `go build`.290291## `--json` Quick Reference292293Most commands support `--json` for structured output, making assertions more reliable than text matching.294295| Command | `--json` | Notes |296|---------|----------|-------|297| `ss status` | `--json` | Skills, targets, sync status |298| `ss list` | `--json` / `-j` | All skills with metadata |299| `ss target list` | `--json` | Configured targets |300| `ss install <src>` | `--json` | Implies `--force --all` (skip prompts) |301| `ss uninstall <name>` | `--json` | Implies `--force` (skip prompts) |302| `ss collect <path>` | `--json` | Implies `--force` (skip prompts) |303| `ss check` | `--json` | Update availability per repo |304| `ss update` | `--json` | Update results per skill |305| `ss diff` | `--json` | Per-file diff details |306| `ss sync` | `--json` | Sync stats per target |307| `ss audit` | `--format json` | Also accepts `--json` (deprecated alias) |308| `ss log` | `--json` | Raw JSONL (one object per line) |309310**Key behaviors:**311- `--json` that implies `--force` / `--all` skips interactive prompts — safe for automation312- Output goes to **stdout only** (progress/spinners suppressed)313- `audit` prefers `--format json`; `--json` still works but is the deprecated form314- `log --json` outputs JSONL (newline-delimited), not a JSON array315316### Assertion Patterns with `jq`317318```bash319# Count installed skills320ss list --json | jq 'length'321322# Check a specific skill exists323ss list --json | jq -e '.[] | select(.name == "my-skill")'324325# Verify target is configured326ss target list --json | jq -e '.[] | select(.name == "claude")'327328# Assert no critical audit findings329ss audit --format json | jq -e '.summary.critical == 0'330331# Check update availability332ss check --json | jq -e '.tracked_repos | length > 0'333334# Verify sync succeeded (zero errors)335ss sync --json | jq -e '.errors == 0'336337# Install and verify result338ss install https://github.com/user/repo --json | jq -e '.skills | length > 0'339```340341When a `jq -e` expression fails (exit code 1 = false, 5 = no output), the step FAILs — no ambiguous text matching needed.342343## Container Command Templates344345```bash346# Single command347docker exec $CONTAINER ssenv enter "$ENV_NAME" -- ss status348349# JSON assertion (preferred for verification)350docker exec $CONTAINER ssenv enter "$ENV_NAME" -- bash -c '351 ss list --json | jq -e ".[] | select(.name == \"my-skill\")"352'353354# Multi-line compound command (use bash -c) — global mode flags355docker exec $CONTAINER ssenv enter "$ENV_NAME" -- bash -c '356 ss init --no-copy --all-targets --no-git --no-skill357 ss status358'359360# Project mode init (different flag set!)361docker exec $CONTAINER env SKILLSHARE_DEV_ALLOW_WORKSPACE_PROJECT=1 \362 ssenv enter "$ENV_NAME" -- bash -c '363 cd /tmp/test-project && ss init -p --targets claude364'365366# Check files (HOME is set to isolated path by ssenv)367docker exec $CONTAINER ssenv enter "$ENV_NAME" -- bash -c '368 cat ~/.config/skillshare/config.yaml369'370371# With environment variables372docker exec $CONTAINER ssenv enter "$ENV_NAME" -- bash -c '373 TARGET=~/.claude/skills374 ls -la "$TARGET"375'376377# Go tests (must cd /workspace because ssenv changes HOME)378docker exec $CONTAINER ssenv enter "$ENV_NAME" -- bash -c '379 cd /workspace380 go test ./internal/install -run TestParseSource -count=1381'382```383384## Relationship with `/mdproof` Skill385386This skill (`/cli-e2e-test`) and the `/mdproof` skill are **complementary**, not competing:387388| Concern | `/cli-e2e-test` | `/mdproof` |389|---------|-----------------|------------|390| **Scope** | Skillshare project-specific E2E | General-purpose runbook authoring |391| **Infrastructure** | Devcontainer, ssenv, binary build | None — format and assertions only |392| **Config** | `ai_docs/tests/runbook.json` (build, setup, teardown) | Assertion types, snapshot, coverage |393| **Lessons** | Checklist items, CLI flag gotchas | `.mdproof/lessons-learned.md` |394| **When** | Running or debugging a test | Writing or improving a runbook |395396### How they work together3973981. **Writing a new runbook** → invoke `/mdproof` first for format guidance (assertion types, `jq:` patterns, snapshot usage), then `/cli-e2e-test` to execute it in isolation3992. **Improving existing runbooks** → invoke `/mdproof` for assertion quality review (python3 → jq:, idempotency), then `/cli-e2e-test` to verify changes pass4003. **Debugging failures** → `/cli-e2e-test` Phase 3 step 4 handles manual docker exec; `/mdproof` lessons-learned captures recurring patterns4014. **After a test run** → `/mdproof` Self-Learning section guides recording discoveries to `.mdproof/lessons-learned.md`402403### Rule of thumb404405- Need to **run** tests or **debug** in devcontainer? → `/cli-e2e-test`406- Need to **write** assertions or **improve** runbook quality? → `/mdproof`407- User says "run extras E2E" → `/cli-e2e-test`408- User says "improve runbook assertions" → `/mdproof` then `/cli-e2e-test` to verify
Run npx skillmds@latest add jetbrains/skillshare-cli-e2e-test in your terminal (requires Node.js), paste this page's agent-chat prompt into Claude, Cursor, or any MCP-connected agent, or download the SKILL.md file and copy it into your agent's skills directory.
Run isolated E2E tests in devcontainer from ai_docs/tests runbooks. Use this skill whenever the user asks to: run an E2E test, execute a test runbook, validate a feature end-to-end, create a new runbook, or test CLI behavior in isolation. If you need to run a multi-step CLI validation sequence (init → install → sync → verify), this is the skill — it handles ssenv isolation, flag verification, and structured reporting. Prefer this over ad-hoc docker exec sequences for any test that follows a runbook or needs reproducible isolation. It is listed under DevOps & Infra on SkillMD.
This skill has not completed SkillMD's automated safety review yet. SkillMD never runs a skill's scripts for you; review the SKILL.md before installing.
This skill is tagged as working with Claude Code, Claude.ai, OpenAI Codex. SKILL.md is an open format, so most agents that read a skills directory can load it too.
Yes. Installing skills from SkillMD is free, and the skill stays under its author's original license.
jetbrains (@jetbrains) published this skill. Their other Agent Skills are listed on their SkillMD profile.