|
| 1 | +--- |
| 2 | +description: "Import existing native permission rules into passthru" |
| 3 | +argument-hint: "[--user-only|--project-only]" |
| 4 | +--- |
| 5 | + |
| 6 | +# /passthru:bootstrap |
| 7 | + |
| 8 | +Convert existing native Claude Code `permissions.allow` entries (from |
| 9 | +`~/.claude/settings.json` and `./.claude/settings{,.local}.json`) into |
| 10 | +passthru rule files, with an interactive preview before anything is |
| 11 | +written. |
| 12 | + |
| 13 | +This is the in-session wrapper around `scripts/bootstrap.sh`. The shell |
| 14 | +script is always available for non-interactive use, but this command is |
| 15 | +the recommended first-run path: it shows exactly what will be imported, |
| 16 | +asks for confirmation, and then verifies the result. |
| 17 | + |
| 18 | +Hand-authored `passthru.json` files are never touched. Imports always |
| 19 | +land in `passthru.imported.json` (separately per scope). Re-running this |
| 20 | +command overwrites the imported files in place. |
| 21 | + |
| 22 | +## What you must do |
| 23 | + |
| 24 | +You are Claude. Drive the workflow below. Surface errors verbatim. Do |
| 25 | +not skip the confirmation step. |
| 26 | + |
| 27 | +### 1. Dry-run to collect the proposed rules |
| 28 | + |
| 29 | +Invoke the bootstrap script WITHOUT `--write`, passing `$ARGUMENTS` |
| 30 | +through so the user's `--user-only` / `--project-only` choice is |
| 31 | +honored: |
| 32 | + |
| 33 | +```bash |
| 34 | +bash ${CLAUDE_PLUGIN_ROOT}/scripts/bootstrap.sh $ARGUMENTS |
| 35 | +``` |
| 36 | + |
| 37 | +Capture stdout, stderr, and the exit code. The script emits: |
| 38 | + |
| 39 | +- `# would write: <path>` comment lines (one per scope in play). |
| 40 | +- A pretty-printed JSON document per scope, of shape |
| 41 | + `{"version":1,"allow":[...],"deny":[]}`. |
| 42 | +- `[WARN] skipping ...` lines on stderr for unsupported native rule |
| 43 | + forms (spaces past the prefix, unusual `WebFetch(...)` forms, etc.). |
| 44 | + These are informational, not fatal. |
| 45 | + |
| 46 | +Non-zero exit means the script itself failed (malformed |
| 47 | +`settings.json`, for example). In that case, surface the error |
| 48 | +verbatim and stop. |
| 49 | + |
| 50 | +### 2. Count the proposed rules |
| 51 | + |
| 52 | +Parse the dry-run output. For each `# would write:` block, count |
| 53 | +`.allow | length` in the JSON document that follows it. Record the |
| 54 | +count per scope (`user` and/or `project`) and the grand total. |
| 55 | + |
| 56 | +### 3. Handle the nothing-to-import case |
| 57 | + |
| 58 | +If the grand total is zero (no scopes have any rules, or `$ARGUMENTS` |
| 59 | +limited the run to a scope with no importable rules), tell the user: |
| 60 | + |
| 61 | +> Nothing to import. Your `settings.json` has no convertible |
| 62 | +> `permissions.allow` entries in the selected scope. |
| 63 | +
|
| 64 | +Then suggest one of these next steps, pick whichever fits the |
| 65 | +situation: |
| 66 | + |
| 67 | +- `/passthru:add <scope> <tool> <pattern> <reason>` to author a rule |
| 68 | + by hand. |
| 69 | +- `/passthru:suggest <hint>` to generalize a rule from a recent tool |
| 70 | + call in the conversation. |
| 71 | + |
| 72 | +Stop there. Do not invoke `--write`. |
| 73 | + |
| 74 | +### 4. Show the proposal |
| 75 | + |
| 76 | +Otherwise, present the proposal in this order: |
| 77 | + |
| 78 | +1. A one-line summary: `Found N importable rule(s): user=<u>, |
| 79 | + project=<p>.` (omit the zero-count scope). |
| 80 | +2. For each scope with rules, show the proposed rules. A compact |
| 81 | + format is fine - list each rule's `tool` field plus a summary of |
| 82 | + the `match` block (or `(namespace rule, no match)` for MCP |
| 83 | + namespace rules). Quote the reason if present. |
| 84 | +3. Explain where they land: |
| 85 | + > This will import N rule(s) from your existing `settings.json` |
| 86 | + > files. Your hand-authored `passthru.json` is never touched; |
| 87 | + > imports go to `passthru.imported.json` (separately per scope). |
| 88 | + > Re-running this command overwrites the imported files in place. |
| 89 | +4. If the dry-run emitted any `[WARN]` lines on stderr, show them |
| 90 | + and explain that those native entries were skipped because the |
| 91 | + converter does not have a safe regex translation for their shape. |
| 92 | + The user can still add them via `/passthru:add` or |
| 93 | + `/passthru:suggest`. |
| 94 | + |
| 95 | +### 5. Ask for confirmation |
| 96 | + |
| 97 | +Ask the user plainly: "Write these rules now? (yes / no)". Wait for a |
| 98 | +clear answer. |
| 99 | + |
| 100 | +- `yes` / `y` / `write` -> proceed to step 6. |
| 101 | +- `no` / `n` / `cancel` -> tell the user nothing was written and |
| 102 | + stop. Remind them they can re-run `/passthru:bootstrap` when they |
| 103 | + are ready, or use `/passthru:add` / `/passthru:suggest` instead. |
| 104 | +- Ambiguous answer -> re-ask once. If still ambiguous, treat as no. |
| 105 | + |
| 106 | +The scope choice is already fixed by whatever the user passed in |
| 107 | +`$ARGUMENTS` (default = both scopes). Do not ask about scope here. |
| 108 | + |
| 109 | +### 6. Write the rules |
| 110 | + |
| 111 | +On confirmation, invoke the script again with `--write` and the same |
| 112 | +`$ARGUMENTS`: |
| 113 | + |
| 114 | +```bash |
| 115 | +bash ${CLAUDE_PLUGIN_ROOT}/scripts/bootstrap.sh --write $ARGUMENTS |
| 116 | +``` |
| 117 | + |
| 118 | +The script backs up any existing `passthru.imported.json`, writes the |
| 119 | +new document, then runs `scripts/verify.sh --quiet`. If the verifier |
| 120 | +fails, the script restores the backup and exits non-zero. Surface |
| 121 | +non-zero output verbatim and stop. |
| 122 | + |
| 123 | +On success the script prints `wrote <path>` per scope. |
| 124 | + |
| 125 | +### 7. Run the verifier once more |
| 126 | + |
| 127 | +Independently re-run the verifier to confirm the imported files parse |
| 128 | +cleanly alongside any existing hand-authored rules: |
| 129 | + |
| 130 | +```bash |
| 131 | +bash ${CLAUDE_PLUGIN_ROOT}/scripts/verify.sh |
| 132 | +``` |
| 133 | + |
| 134 | +If this exits non-zero, show the errors verbatim. The bootstrap |
| 135 | +script has already rolled back on its own verify failure, so an exit |
| 136 | +here means something else (e.g. a pre-existing problem in |
| 137 | +`passthru.json` that the merged view now surfaces). |
| 138 | + |
| 139 | +### 8. Report success |
| 140 | + |
| 141 | +Print a short confirmation: |
| 142 | + |
| 143 | +> Imported N rule(s). Restart Claude Code to pick the new rules up, |
| 144 | +> or wait - the PreToolUse hook re-reads every rule file on every |
| 145 | +> tool call, so the imports take effect on the very next tool call in |
| 146 | +> this session. |
| 147 | +
|
| 148 | +If the dry-run emitted `[WARN]` lines for skipped entries, remind the |
| 149 | +user once more that they can port those manually via `/passthru:add` |
| 150 | +or `/passthru:suggest`. |
| 151 | + |
| 152 | +## Examples |
| 153 | + |
| 154 | +### Both scopes, importable rules in user scope only |
| 155 | + |
| 156 | +Dry-run output: |
| 157 | + |
| 158 | +``` |
| 159 | +# would write: /Users/you/.claude/passthru.imported.json |
| 160 | +{ |
| 161 | + "version": 1, |
| 162 | + "allow": [ |
| 163 | + { "tool": "Bash", "match": { "command": "^ls(\\s|$)" }, "reason": "imported from settings" }, |
| 164 | + { "tool": "Bash", "match": { "command": "^gh api(\\s|$)" }, "reason": "imported from settings" } |
| 165 | + ], |
| 166 | + "deny": [] |
| 167 | +} |
| 168 | +# would write: /Users/you/project/.claude/passthru.imported.json |
| 169 | +{ "version": 1, "allow": [], "deny": [] } |
| 170 | +``` |
| 171 | + |
| 172 | +Present: |
| 173 | + |
| 174 | +> Found 2 importable rule(s): user=2. |
| 175 | +> |
| 176 | +> user scope: |
| 177 | +> - `Bash` match `command: ^ls(\s|$)` - imported from settings |
| 178 | +> - `Bash` match `command: ^gh api(\s|$)` - imported from settings |
| 179 | +> |
| 180 | +> This will import 2 rules from your existing settings.json files. |
| 181 | +> Your hand-authored passthru.json is never touched; imports land in |
| 182 | +> passthru.imported.json. |
| 183 | +> |
| 184 | +> Write these rules now? (yes / no) |
| 185 | +
|
| 186 | +After confirmation and `--write`: |
| 187 | + |
| 188 | +> Imported 2 rule(s). Restart Claude Code (or wait - rules take |
| 189 | +> effect on the next tool call since the hook re-reads every |
| 190 | +> invocation). |
| 191 | +
|
| 192 | +### `--user-only` |
| 193 | + |
| 194 | +``` |
| 195 | +/passthru:bootstrap --user-only |
| 196 | +``` |
| 197 | + |
| 198 | +Skips the project scope entirely. Only writes |
| 199 | +`~/.claude/passthru.imported.json`. Useful when project-scope |
| 200 | +`settings.local.json` has a lot of one-off rules you do not want |
| 201 | +ported plugin-side. |
| 202 | + |
| 203 | +### Nothing to import |
| 204 | + |
| 205 | +``` |
| 206 | +/passthru:bootstrap |
| 207 | +``` |
| 208 | + |
| 209 | +Dry-run emits two empty documents. Respond: |
| 210 | + |
| 211 | +> Nothing to import. Your settings.json has no convertible |
| 212 | +> permissions.allow entries in the selected scope. |
| 213 | +> |
| 214 | +> Next steps: |
| 215 | +> - `/passthru:add user Bash "^gh api " "github api reads"` to add a |
| 216 | +> rule by hand. |
| 217 | +> - `/passthru:suggest gh api` to generalize a rule from a recent |
| 218 | +> tool call. |
| 219 | +
|
| 220 | +Do not invoke `--write`. |
0 commit comments