# Bulwark Scaffold

> Bulwark Scaffold

- Skill: `qball-inc/bulwark-scaffold` (Agent Skill)
- Install (CLI): `npx skillmds@latest add qball-inc/bulwark-scaffold`
- Raw SKILL.md: https://api.skillmd.com/api/skills/qball-inc/bulwark-scaffold/raw
- Safety review: WARNING
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- Author: QBall-Inc (https://skillmd.com/u/qball-inc)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/qball-inc/bulwark-scaffold

---


# Bulwark Scaffold

Initialize Bulwark infrastructure in a project by generating Justfile templates, creating the logs directory structure, and optionally configuring hooks.

---

## Mandatory Execution Checklist (BINDING)

**Every item below is mandatory. No deviations. No substitutions. No skipping. Skipping items violates SC1-SC3 (Skill Compliance Rules in Rules.md).**

You are the orchestrator. Follow every item in order. Do NOT return to the user until all applicable items are checked.

- [ ] **Step 1 — Parse arguments**: `--force`, `--no-hooks`, `--with-permission-hook`, `--dry-run`, `--lang=<...>` extracted from `$ARGUMENTS`
- [ ] **Step 2 — Detect project language**: If `--lang` not supplied, project files inspected (`package.json`, `pyproject.toml`, `Cargo.toml`, `go.mod`, etc.); defaults to `generic` when no signal found
- [ ] **Step 3 — `just` runtime check**: `command -v just` runs; if missing, follow Step 3 install path
- [ ] **Step 3.5a — `bun` runtime check**: `bash <resolved-installer-path> --verify` invoked (NOT `command -v bun` — that bypasses version check); follow Step 3.5 install path if exit non-zero
- [ ] **Step 3.5b — Installer copy**: `scripts/install-bun.sh` copied to `${CLAUDE_PROJECT_DIR}/scripts/` so Justfile recipes can reference it project-relatively
- [ ] **Step 3.6 — Existing Justfile check**: If a Justfile is already present, abort unless `--force` (with backup created)
- [ ] **Step 4 — Dry-run check**: If `--dry-run`, preview the planned writes and exit without modifying disk
- [ ] **Step 5 — Justfile generation**: Template emitted from `lib/templates/justfile-<lang>.just`; bun + eval recipes (`install-bun`, `verify-bun`, `eval-skill`, `eval-grade`, `eval`) confirmed present in output
- [ ] **Step 6 — `logs/` subdirectories**: `diagnostics/`, `validations/`, `debug-reports/` created
- [ ] **Step 7 — `.gitignore`**: Bulwark log patterns appended idempotently
- [ ] **Step 8 — Hooks**: If plugin-level hooks active, SKIP `.claude/settings.json` hook injection (anti-duplication)
- [ ] **Step 8a — Permission hook (opt-in)**: ONLY if `--with-permission-hook` — confirm trust (AskUserQuestion), merge `PreToolUse` entry into `.claude/settings.json`, copy `bulwark-permission-hook.sh`, record choice. Default (no flag) = NOT installed
- [ ] **Step 9 — Scaffold log**: `logs/scaffold-{ts}.yaml` written with top-level `reviewed_files: [...]` (Stop-hook contract)
- [ ] **Step 10 — Report results**: User-facing summary emitted listing files written, installer outcomes, and any skipped steps

---

## Usage

```
/bulwark-scaffold [options]
```

**Options:**
- `--force` - Overwrite existing Justfile (creates backup)
- `--no-hooks` - Skip hook configuration (hooks are generated by default)
- `--with-permission-hook` - Also install the opt-in PreToolUse permission-bypass hook (auto-approves Bulwark's own bundled-asset reads/edits/scripts). Default: NOT installed
- `--dry-run` - Show what would be generated without writing files
- `--lang=<node|python|rust|go|kotlin|swift|shell|generic>` - Override language detection

**Examples:**
- `/bulwark-scaffold` - Full scaffold with Justfile + logs/ + hooks
- `/bulwark-scaffold --force` - Overwrite existing Justfile
- `/bulwark-scaffold --no-hooks` - Skip hook configuration
- `/bulwark-scaffold --with-permission-hook` - Scaffold + install the opt-in permission-bypass hook
- `/bulwark-scaffold --dry-run` - Preview changes

---

## Execution Steps

### Step 1: Parse Arguments

Extract options from `$ARGUMENTS`:
- `--force` → FORCE_OVERWRITE=true
- `--no-hooks` → SKIP_HOOKS=true
- `--with-permission-hook` → WITH_PERMISSION_HOOK=true (default: false)
- `--dry-run` → DRY_RUN=true
- `--lang=X` → LANG_OVERRIDE=X

### Step 2: Detect Project Language

If LANG_OVERRIDE is set, use that. Otherwise, search from current directory for manifest files in this order (first match wins):

1. `package.json` → **node**
2. `pyproject.toml` OR `requirements.txt` OR `setup.py` → **python**
3. `Cargo.toml` → **rust**
4. `go.mod` → **go**
5. `build.gradle` OR `build.gradle.kts` OR any `*.gradle.kts` file → **kotlin**
6. `Package.swift` → **swift**
7. No manifest above AND at least one `*.sh` file at project root or in `scripts/` → **shell**
8. None of the above → **fallthrough** (do NOT silently default to generic; see Step 5)

Store result in DETECTED_LANG variable. If fallthrough, DETECTED_LANG=`fallthrough` (a marker value, not a template name).

### Step 3: Verify `just` runtime

**Pre-flight: Verify `just` is installed.**

```
Run: command -v just

IF just is NOT found:
    Delegate the install attempt to scripts/install-just.sh — it detects the
    platform (macOS/WSL/Debian/RHEL/Windows) and uses the right package
    manager (brew/cargo/apt/dnf/winget/scoop) with cargo as primary for
    Linux. Does NOT use curl|bash.

    Locate the installer:
      - Plugin install: ${CLAUDE_PLUGIN_ROOT}/scripts/install-just.sh
      - Local dev:      ${CLAUDE_PROJECT_DIR}/scripts/install-just.sh

    Ask user: "just is required for the scaffolded Justfile. Install it now via scripts/install-just.sh (recommended) or skip Justfile generation?"
      - If install: Run `bash <installer-path>`
        - Exit 0: just installed → continue
        - Exit non-zero: installer already printed actionable per-platform
          instructions → set SKIP_JUSTFILE=true (do not re-print)
      - If skip: Set SKIP_JUSTFILE=true

    If the installer script cannot be located (rare — repo corruption):
        Print: "Could not find scripts/install-just.sh. Install just manually:"
        Print: "  macOS:   brew install just"
        Print: "  Linux:   cargo install just  (requires Rust: https://rustup.rs)"
        Print: "  Windows: winget install Casey.Just"
        Print: "  All:     https://just.systems/man/en/chapter_4.html"
        Set SKIP_JUSTFILE=true
```

### Step 3.5: Verify bun runtime (eval framework + generated TS scripts)

**Pre-flight: Verify `bun` is installed (required for create-skill eval framework + generated archetype scripts).**

```
1. Locate the source installer (prefer plugin install path; fall back to local-dev):
     - Plugin install: ${CLAUDE_PLUGIN_ROOT}/scripts/install-bun.sh
     - Local dev:      ${CLAUDE_PROJECT_DIR}/scripts/install-bun.sh
   IF neither path exists (rare — repo corruption):
       Print: "Could not find scripts/install-bun.sh. Install bun manually:"
       Print: "  macOS:   brew install oven-sh/bun/bun"
       Print: "  Linux:   curl -fsSL https://bun.sh/install | bash"
       Print: "  Windows: powershell -c \"irm bun.sh/install.ps1 | iex\""
       Print: "  Docs:    https://bun.sh/docs/installation"
       Set SKIP_BUN_FEATURES=true
       Skip the rest of Step 3.5

2. Copy the installer into the project so the Justfile recipes can reference
   it project-relatively (the templates use `./scripts/install-bun.sh`):
     mkdir -p ${CLAUDE_PROJECT_DIR}/scripts
     cp <resolved-installer-path> ${CLAUDE_PROJECT_DIR}/scripts/install-bun.sh
     chmod +x ${CLAUDE_PROJECT_DIR}/scripts/install-bun.sh
   (Idempotent — overwriting any prior copy ensures the project mirrors the
   currently-installed plugin version.)

3. Run the verify probe with the project-local path (NOT `command -v bun`,
   which would bypass version checks performed by the installer):
     bash ${CLAUDE_PROJECT_DIR}/scripts/install-bun.sh --verify

4. IF bun is NOT found OR version < 1.0 (verify exit non-zero):
     Ask user: "bun is required for the create-skill eval framework and generated TS scripts. Install it now via scripts/install-bun.sh (recommended) or skip eval-framework features?"
       - If install: Run `bash ${CLAUDE_PROJECT_DIR}/scripts/install-bun.sh`
         - Exit 0: bun installed → continue
         - Exit non-zero: installer already printed actionable per-platform
           instructions → set SKIP_BUN_FEATURES=true (do not re-print)
       - If skip: Set SKIP_BUN_FEATURES=true; user can install later via `just install-bun`

5. IF bun is found (verify exit 0):
     Continue (no install needed; idempotent verify).
```

When `bun` is verified or installed AND the templates have been emitted, the recipes `just install-bun`, `just verify-bun`, `just eval-skill`, `just eval-grade`, and `just eval` are runnable. The first two delegate to the project-local `scripts/install-bun.sh` (copied in step 2); the eval recipes run TS scripts under `skills/create-skill/scripts/` via bun. When `SKIP_BUN_FEATURES=true`, the scaffold continues but the user is informed those features will fail until `just install-bun` runs successfully.

The `bun` check is parallel to the `just` check (Step 3) — both are runtime installers; both follow the same locate-then-execute pattern; both have actionable manual fallbacks.

### Step 3.6: Check for existing Justfile

```
IF Justfile exists AND NOT FORCE_OVERWRITE:
    Print: "Justfile already exists. Use --force to overwrite (creates backup)."
    Set SKIP_JUSTFILE=true
ELSE IF Justfile exists AND FORCE_OVERWRITE:
    Create backup: Justfile.backup-{YYYYMMDD-HHMMSS}
    Set SKIP_JUSTFILE=false
ELSE:
    Set SKIP_JUSTFILE=false
```

### Step 4: Dry Run Check

If DRY_RUN is true, display preview and exit:

```
## Scaffold Preview (dry-run)

**Language detected:** {DETECTED_LANG} ({manifest file or "no manifest"})

**Would create:**
- Justfile (from `lib/templates/justfile-{RESOLVED_TEMPLATE}.just` — substitute the actual filename, e.g., `justfile-go.just`, NOT the interpolation placeholder)
- logs/
- logs/diagnostics/
- logs/validations/
- logs/debug-reports/
{IF NOT SKIP_HOOKS}
- .claude/settings.json (hooks configuration)
- .claude/skills/governance-protocol/SKILL.md
- scripts/hooks/inject-protocol.sh
- scripts/hooks/enforce-quality.sh
- scripts/hooks/suggest-pipeline-stop.sh
{ENDIF}
{IF WITH_PERMISSION_HOOK}
- scripts/hooks/bulwark-permission-hook.sh
- .claude/settings.json (PreToolUse permission-bypass entry — merged independently of --no-hooks)
{ENDIF}

**Would update:**
- .gitignore (add Bulwark patterns)

Run without --dry-run to apply changes.
```

Then STOP execution.

### Step 5: Generate Justfile

If NOT SKIP_JUSTFILE:

**Template selection:**

If DETECTED_LANG is one of `node|python|rust|go|kotlin|swift|shell`:
- TEMPLATE_NAME = `justfile-{DETECTED_LANG}.just`

If DETECTED_LANG is `fallthrough` (no manifest detected, no --lang override):
- Do NOT silently copy `justfile-generic.just`. The generic template is now
  fail-loudly (every recipe exits 1 with an "unconfigured" error), so handing
  it to the user without explicit consent produces a broken-looking Justfile.
- Prompt the user:
  ```
  No language manifest detected and no --lang override provided.

  Choose a language template:
    1) node     - eslint + tsc + vitest recipes
    2) python   - ruff + mypy + pytest recipes
    3) rust     - cargo fmt + clippy + test recipes
    4) go       - go vet + golangci-lint + gofmt recipes
    5) kotlin   - ktlint + detekt recipes
    6) swift    - swiftlint + swift-format recipes
    7) shell    - shellcheck + shfmt recipes
    8) generic  - placeholder Justfile that FAILS LOUDLY on every recipe
                  (use only if you will hand-edit it immediately)
    9) cancel   - skip Justfile generation

  Select [1-9]:
  ```
- Record the user's choice in DETECTED_LANG. If option 9, set SKIP_JUSTFILE=true.
- If option 8 (generic), print a warning before copying:
  ```
  WARNING: The generic Justfile fails every recipe with exit 1 by design.
  You must either (a) pick a language and re-run scaffold, or
  (b) replace the `_not_configured` recipe calls with real tool invocations
  before running `just <recipe>`.
  ```
- Then TEMPLATE_NAME = `justfile-{DETECTED_LANG}.just` (with the user's choice).

**Copy template:**

1. Locate template: `${CLAUDE_PLUGIN_ROOT}/lib/templates/{TEMPLATE_NAME}`
   - For local development: `${CLAUDE_PROJECT_DIR}/lib/templates/{TEMPLATE_NAME}`
2. Copy template content to `Justfile` in project root

### Step 6: Create logs/ Directory Structure

```bash
mkdir -p logs/diagnostics logs/validations logs/debug-reports
touch logs/.gitkeep logs/diagnostics/.gitkeep logs/validations/.gitkeep logs/debug-reports/.gitkeep
```

### Step 7: Update .gitignore

Check if `.gitignore` exists and contains `# Bulwark logs` marker:
- If marker exists: Skip (already configured)
- If marker does not exist: Append the following patterns

**Patterns to add:**
```gitignore

# Bulwark logs
logs/*.yaml
logs/*.log
logs/diagnostics/*.yaml
logs/validations/*.yaml
logs/debug-reports/*.yaml
!logs/.gitkeep
!logs/*/.gitkeep
```

### Step 8: Generate Hook Configuration (Default)

UNLESS SKIP_HOOKS is true:

**Pre-flight: Check for existing Bulwark hooks to prevent duplication.**

Plugin hooks (`hooks/hooks.json`) and settings hooks (`.claude/settings.json`) both fire at runtime. If hooks already exist in any location, adding them again causes double execution.

**Check these locations for existing Bulwark hooks** (search for `enforce-quality` or `inject-protocol`):

| Location | Scope | Notes |
|----------|-------|-------|
| `./hooks/hooks.json` | Project plugin | Bulwark installed as project-level plugin |
| `~/.claude/plugins/*/hooks/hooks.json` | User plugin | Bulwark installed at user level via `/install` |
| `.claude/settings.json` | Project settings | Previously scaffolded |
| `.claude/settings.local.json` | Project local | User-specific project overrides |
| `~/.claude/settings.json` | User settings | Global user hooks |
| `~/.claude/settings.local.json` | User local | Global user overrides |

```
HOOKS_FOUND_IN = []

FOR each location above:
    IF file exists AND contains "enforce-quality" OR "inject-protocol":
        Append location to HOOKS_FOUND_IN

IF HOOKS_FOUND_IN is not empty:
    Print: "Bulwark hooks already present in: {HOOKS_FOUND_IN}. Skipping hook generation to prevent duplication."
    Set SKIP_HOOKS=true
```

**If no existing hooks found**, proceed with hook generation:

Check if `.claude/settings.json` exists:
- If exists: Merge hooks into existing configuration (preserve other settings)
- If not exists: Create new file

**Hook configuration to add:**
```json
{
  "hooks": {
    "SessionStart": [
      {
        "hooks": [
          {
            "type": "command",
            "command": "${CLAUDE_PROJECT_DIR}/scripts/hooks/inject-protocol.sh",
            "timeout": 5
          }
        ]
      }
    ],
    "PostToolUse": [
      {
        "matcher": "Write|Edit",
        "hooks": [
          {
            "type": "command",
            "command": "${CLAUDE_PROJECT_DIR}/scripts/hooks/enforce-quality.sh",
            "timeout": 60
          }
        ]
      }
    ],
    "Stop": [
      {
        "hooks": [
          {
            "type": "command",
            "command": "${CLAUDE_PROJECT_DIR}/scripts/hooks/suggest-pipeline-stop.sh",
            "timeout": 30
          }
        ]
      }
    ]
  }
}
```

**Note:** SessionStart has no matcher, so it fires for all session events (start, resume, clear, compact). The `Stop` hook fires once at Claude turn end and emits a single consolidated pipeline-suggestion block per turn (mirrors the plugin's own `hooks/hooks.json`). Do NOT install the deprecated `suggest-pipeline.sh` — it emits per-edit blocks and causes the hook storm P10.1 fixes.

**Also copy required files** (only if hooks were generated above):
1. Copy `scripts/hooks/inject-protocol.sh` to `${CLAUDE_PROJECT_DIR}/scripts/hooks/`
2. Copy `scripts/hooks/enforce-quality.sh` to `${CLAUDE_PROJECT_DIR}/scripts/hooks/`
3. Copy `scripts/hooks/suggest-pipeline-stop.sh` to `${CLAUDE_PROJECT_DIR}/scripts/hooks/`
4. Copy `skills/governance-protocol/SKILL.md` to `${CLAUDE_PROJECT_DIR}/.claude/skills/governance-protocol/SKILL.md`

Create parent directories as needed (`mkdir -p`).

### Step 8a: Optional Permission-Bypass Hook (opt-in)

ONLY if `WITH_PERMISSION_HOOK` is true (the `--with-permission-hook` flag). Default (no flag) → **SKIP this entire step**; the permission hook is NOT installed and nothing below runs. Set `PERMISSION_HOOK=not_requested` and continue to Step 9.

This installs `bulwark-permission-hook.sh` as a **project-scope** `PreToolUse` hook that auto-approves Read/Edit/Bash operations on **Bulwark's own bundled assets** — skipping the per-file permission prompts CC raises on plugin-bundled files the user already trusted at install. It is a scoped workaround for upstream CC permission bugs (retire when `#29285` lands; see `docs/reference/hooks.md`).

**Runs independently of `SKIP_HOOKS`** — a user may pass `--no-hooks --with-permission-hook` (no governance hooks, but yes permission-bypass). If Step 8 was skipped and `.claude/settings.json` does not exist, create it here.

**1. Confirm the trust decision (REQUIRED — security-sensitive).** Installing a permission-bypass hook is a trust decision, so confirm explicitly even though the flag was passed. Use **AskUserQuestion**:

> **Install the Bulwark permission-bypass hook?**
> It auto-approves Read/Edit/Bash on Bulwark's **own bundled assets** (under the plugin cache root / `${CLAUDE_PLUGIN_ROOT}`), so you stop seeing permission prompts for them. Everything else still prompts as normal. **Writes are never auto-approved.** Path-traversal that escapes the plugin root is denied. Requires trusting Bulwark at install level.
>
> Options: **Install** / **Skip**

If the user picks **Skip** (or declines): do NOT install; set `PERMISSION_HOOK=declined`; continue to Step 9.

**2. On Install:**

  **a. Idempotency check.** If `.claude/settings.json` already registers `bulwark-permission-hook.sh` under `PreToolUse`, skip re-adding (set `PERMISSION_HOOK=already_present`) and proceed to step (c) to ensure the script copy exists.

  **b. Merge the `PreToolUse` entry** into `.claude/settings.json` (create the file and/or the `hooks` key if absent; preserve all existing settings — same merge discipline as Step 8):

  ```json
  {
    "hooks": {
      "PreToolUse": [
        {
          "matcher": "Read|Edit|Bash",
          "hooks": [
            {
              "type": "command",
              "command": "${CLAUDE_PROJECT_DIR}/scripts/hooks/bulwark-permission-hook.sh",
              "timeout": 5
            }
          ]
        }
      ]
    }
  }
  ```

  **c. Copy the hook script:** copy `scripts/hooks/bulwark-permission-hook.sh` to `${CLAUDE_PROJECT_DIR}/scripts/hooks/` and ensure it is executable (`chmod +x`). Create parent dirs with `mkdir -p`.

  **d.** Set `PERMISSION_HOOK=installed`.

**Why there is no env-var gate in the project entry:** the hook self-gates on `$CLAUDE_PLUGIN_OPTION_ENABLE_PERMISSION_BYPASS`, but project-scope installs do not set that var — the script treats an **unset** gate as **active** (`bulwark-permission-hook.sh` opt-in gate), so the entry's presence in `settings.json` IS the opt-in. (The env-var gate only matters for the plugin-level install, which defaults it to `false`.)

**3. Record the choice** in the scaffold log (Step 9): add `scripts/hooks/bulwark-permission-hook.sh` to `reviewed_files` when copied, and set `actions.permission_hook.action` to the `PERMISSION_HOOK` value.

### Step 9: Write Scaffold Log

Write to `logs/scaffold-{YYYYMMDD-HHMMSS}.yaml`:

```yaml
# Top-level — required for Stop-hook per-file pipeline-recursion suppression.
# List every script/.sh file generated or copied during scaffolding (e.g. the
# project copy of install-bun.sh, any hook scripts copied to scripts/hooks/).
# Paths relative to ${CLAUDE_PROJECT_DIR}. Empty list `[]` if no script files
# were touched. Missing field disables suppression for this log (strict mode).
reviewed_files:
  - scripts/install-bun.sh
  - scripts/hooks/enforce-quality.sh

metadata:
  timestamp: {ISO-8601}
  action: scaffold
  invocation: "/bulwark-scaffold {args}"

detection:
  # language must be one of: node | python | rust | go | kotlin | swift | shell | generic | fallthrough
  language: {DETECTED_LANG}
  manifest: {manifest path or null}
  override: {LANG_OVERRIDE or null}
  user_selected: {true|false}  # true if user picked a language at the fallthrough prompt

actions:
  justfile:
    action: created|skipped|overwritten
    backup: {backup path or null}
    # template filename resolves to one of:
    #   justfile-node.just | justfile-python.just | justfile-rust.just
    #   justfile-go.just | justfile-kotlin.just | justfile-swift.just
    #   justfile-shell.just | justfile-generic.just
    template: lib/templates/justfile-{RESOLVED_TEMPLATE}.just
  logs_directory:
    created: true
    subdirectories: [diagnostics, validations, debug-reports]
  gitignore:
    action: created|updated|skipped
    patterns_added: 7
  hooks:
    action: created|merged|skipped
    path: .claude/settings.json
    skipped_reason: {reason if skipped}
  permission_hook:
    # --with-permission-hook opt-in (Step 8a). not_requested when the flag is absent.
    action: installed|declined|already_present|not_requested
    path: .claude/settings.json
    script: scripts/hooks/bulwark-permission-hook.sh

summary: |
  Scaffold complete for {DETECTED_LANG} project.
```

### Step 10: Report Results

Present summary to user:

```
## Scaffold Complete

**Language:** {DETECTED_LANG}
**Justfile:** {created|skipped|overwritten (backup: path)}
**logs/:** Created with subdirectories (diagnostics, validations, debug-reports)
**.gitignore:** {updated|created|unchanged}
**Hooks:** {created|merged|skipped (--no-hooks)}
**Permission hook:** {installed|declined|already present|not installed}
**Governance:** {installed|skipped} - Protocol injected at session start

Run `just` to see available recipes:
- `just typecheck` - Run type checker
- `just lint` - Run linter
- `just build` - Build project
- `just test` - Run tests
- `just ci` - Run all quality checks
- `just fix` - Auto-fix issues
```

---

## Error Handling

| Scenario | Action |
|----------|--------|
| Cannot detect language | Prompt user to pick from the Step 5 menu. Do NOT silently default to generic — it produces a fail-loudly Justfile by design. |
| Invalid --lang value | Print: "Invalid --lang. Use one of: node, python, rust, go, kotlin, swift, shell, generic" and exit. |
| Template file missing | Print: "Template not found at {path}. Bulwark installation may be corrupted." |
| Cannot create logs/ | Print: "Cannot create logs/ directory. Check permissions." |
| .gitignore write fails | Print warning, continue with other operations |
| Justfile backup fails | Print error, abort Justfile generation |

---

## Diagnostic Output

Write diagnostic log to `logs/diagnostics/bulwark-scaffold-{timestamp}.yaml`:

```yaml
# Top-level — mirror the same list emitted in the scaffold report (Stop hook contract).
reviewed_files:
  - scripts/install-bun.sh
  - scripts/hooks/enforce-quality.sh

skill: bulwark-scaffold
timestamp: {ISO-8601}
invocation: "{full command}"
inputs:
  force: {true|false}
  no_hooks: {true|false}
  with_permission_hook: {true|false}
  dry_run: {true|false}
  lang_override: {value or null}
detection:
  language: {detected}
  manifest_path: {path or null}
outputs:
  justfile_created: {true|false}
  logs_created: {true|false}
  gitignore_updated: {true|false}
  hooks_configured: {true|false}
  permission_hook_configured: {true|false}
errors: []
```

