| 1 | --- |
| 2 | name: ELI5 |
| 3 | description: keep it simple pls |
| 4 | keep-coding-instructions: true |
| 5 | --- |
| 6 | |
| 7 | It's been a long day and my brain is fried, talk to me like I'm 5. |
| 8 | |
| 9 | Small words, short sentences, short paragraphs. If you have to use |
| 10 | a big word, explain it right after. Only return what's actually necessary. |
| 11 | |
| 12 | Just tell me what you did, did it work, what do I do now. |
| 13 | |
| 14 | If I have to decide something: 2 options max, the context I need to pick fast, |
| 15 | and which one you'd go with. |
| 16 | |
| 17 | Keep paths and commands exact. I have no brain cells left for the rest. |
AI Coding Agent Skill Collection
Quick Install: Grilling Question Tools
Install or update the question-tool preference for Claude Code, Codex, and OpenCode in one command:
bash -o pipefail -c 'curl -fsSL https://opengist.resetrix.work/weehong/9e381dc89dff4670b7232b910454f056/raw/HEAD/install-question-tools.sh | bash'
Trust check: this runs remote code and downloads remote instructions from
this gist. Read both before running if you do not already trust the publisher
and server. pipefail reports download failures; it is not a security sandbox
or a guarantee against executing partially downloaded shell code.
Quick links: installer | authoritative policy | ELI5 writing style
Download and Inspect Instead
Run in a scratch directory. These downloads alone do not install anything:
base=https://opengist.resetrix.work/weehong/9e381dc89dff4670b7232b910454f056
curl -fsSL "$base/raw/HEAD/install-question-tools.sh" -o install-question-tools.sh
curl -fsSL "$base/raw/HEAD/grilling-question-tools.md" -o grilling-question-tools.md
less install-question-tools.sh grilling-question-tools.md
# After inspection:
bash install-question-tools.sh
By default the installer fetches the policy at HEAD, not the inspected local
copy. To avoid changes between inspection and installation, get a full commit
hash from the gist's Revisions,
replace HEAD in both download URLs with that hash, and run
QUESTION_TOOLS_REVISION=<same-full-40-character-hash> bash install-question-tools.sh.
The revision variable controls the policy download, not the installer download.
Requirements and Scope
- Linux, macOS, or WSL; Bash 3.2+, Python 3.8+, and
curlwith HTTPS support. - Run as your normal user, without
sudo, and with no concurrent installer or editor changing the destination files. Native Windows shells are not supported. - This installs instructions only, not the Matt Pocock skills, tools, plugins, or permissions. Install your desired grilling skills separately.
- Only Matt Pocock grilling flows use this preference. The policy retains the flow's pacing and recommendations, custom answers, unresolved decisions, and text fallback when the native question tool is unavailable or restricted.
- This is not runtime enforcement. Host permissions, mode restrictions, instruction precedence, and agent instruction-loading limits still apply.
Destinations
All three destinations are installed, even if a corresponding CLI is not yet
installed. Export overrides before running the quick command so both shells
inherit them. Empty variables use defaults; paths must be absolute, without
.. components. Spaces are supported.
| Agent | Default file | Override |
|---|---|---|
| Claude Code | ~/.claude/CLAUDE.md |
$CLAUDE_CONFIG_DIR/CLAUDE.md |
| Codex | ~/.codex/AGENTS.md |
$CODEX_HOME/AGENTS.md |
| OpenCode | ~/.config/opencode/AGENTS.md |
$OPENCODE_CONFIG_DIR/AGENTS.md, otherwise $XDG_CONFIG_HOME/opencode/AGENTS.md |
HOME supplies ~. Current OpenCode supports OPENCODE_CONFIG_DIR for global
instructions; older versions may only read the XDG location. For an older
release, leave OPENCODE_CONFIG_DIR unset and use XDG_CONFIG_HOME instead.
See OpenCode instruction loading
and global paths.
OpenCode normally prefers its own global AGENTS.md over the Claude fallback:
creating this file can stop fallback loading of ~/.claude/CLAUDE.md. If you
rely on unrelated instructions in that fallback, explicitly configure them in
OpenCode's instructions setting before installing.
Codex's AGENTS.override.md, if present, can take precedence over AGENTS.md;
review it if the new preference is not being loaded.
Updates and Safety
Rerun the quick command to fetch the latest policy and replace its managed
block. Identical reruns leave file bytes unchanged and create no new backups.
The authoritative policy is grilling-question-tools.md; the installer embeds
its downloaded content rather than maintaining another policy copy.
- Download errors or an invalid/incomplete policy abort before destination edits. Validation checks UTF-8, versioned start/end markers, title, and all seven numbered rules. These are integrity sanity checks, not a signature.
- Every destination is preflighted before any directories, backups, or files are created. Malformed or duplicate managed markers abort the entire run.
- The unique HTML markers are
<!-- BEGIN MATT-POCOCK-GRILLING-QUESTION-TOOLS -->and<!-- END MATT-POCOCK-GRILLING-QUESTION-TOOLS -->. Keep them intact. - Bytes outside an existing managed block are preserved exactly. A first installation appends a separator and block without changing existing bytes.
- Each changed existing file gets a byte-for-byte sibling backup named
<filename>.question-tools-backup-<unique-suffix>(private mode0600). New files need no backup. Paths are printed; previous backups are never replaced. - Symlinked files and ancestor directories are refused without following them. Use physical directory paths and regular files, or integrate the policy manually through your dotfile manager. The installer never unlinks symlinks.
- Replacements are atomic per file, not a transaction across all three files. Staging and backups precede replacements, but disk/permission errors during writing can leave directories/backups or a partial install. Inspect the printed paths, restore from backups if needed, and rerun after fixing the cause. File modes are retained; ownership, ACLs, extended attributes and hard-link relationships are not preserved by replacement. Avoid concurrent writers.
After installation, start fresh Claude Code and Codex sessions, and quit and restart OpenCode. Ask the agent which instruction files it loaded if the preference is missing. To uninstall, remove only the managed block from each destination, or restore a backup if it will not erase newer unrelated edits.
Existing Style
ELI5.mdx
is the original short, plain-language writing style. It is preserved unchanged
and is not installed by this script.
Question Tools for Matt Pocock Grilling Flows
Scope: apply only while running Matt Pocock's grilling flows (grill-me, grilling, grill-with-docs, loop-me, or a skill explicitly delegating its interview to one of these). This changes question delivery, not the flow's research, decision criteria, documentation, or completion requirements. Ordinary coding conversations and unrelated skills keep their own behavior.
- Research what the codebase can answer before asking the user. The main conversation agent owns the interview; subagents return findings and proposed questions to it rather than independently prompting the user.
- Use the host's native interactive question tool when exposed and permitted
in the current session: Claude Code
AskUserQuestion, Codexrequest_user_input, or OpenCodequestion. Follow its actual schema and mode/permission restrictions. Do not invent a tool, change modes merely to unlock one, or bypass a restriction. In Codex,request_user_inputmay be limited to Plan mode; use the fallback when it is unavailable. - Preserve the originating flow's round size and pacing. If it calls for one question at a time, ask one. If it allows a round of independent questions, batch only those that can be answered without knowing another answer. Wait for each answer before formulating dependent follow-ups. Split rounds across sequential tool calls when question, option, or text limits require it; retain every decision rather than silently dropping questions.
- Give enough context to decide, concrete options with tradeoffs, and your recommendation with a short reason when you have one. Mark the recommended option clearly. Keep choices neutral enough to disagree with; never invent a recommendation just to fill the UI. Put context outside the tool if its field limits would lose necessary detail.
- Preserve custom responses: use the tool's built-in free-text/Other facility where available. If it cannot accept a needed custom answer, invite a plain text reply instead. Do not force an open-ended question into false choices or interpret Other as agreement with a listed option.
- Treat skipped, dismissed, empty, or ambiguous answers as unresolved, not consent and not acceptance of the recommendation. Clarify a blocking gap or explicitly defer it with the user's agreement. Track unresolved decisions and keep them out of the confirmed decisions in any spec, ADR, or summary.
- If the native tool is absent, disallowed, fails, or cannot express the question faithfully, ask in plain text and wait. Use the same round rules, numbered questions/options, recommendation and reason, and an explicit invitation to give a custom answer. Resume the native tool when usable.
This is an instruction preference, not runtime enforcement. Higher-priority instructions and host restrictions still apply. Finish only when the original flow's completion criteria are met; changing the UI does not satisfy them.
| 1 | #!/usr/bin/env bash |
| 2 | # Requires Bash 3.2+, Python 3.8+, and curl. No sudo; run one installer at a time. |
| 3 | set -euo pipefail |
| 4 | |
| 5 | # Defining the complete function before calling it also avoids starting an |
| 6 | # installation from a download truncated inside the embedded Python program. |
| 7 | main() { |
| 8 | command -v python3 >/dev/null || { printf '%s\n' 'Python 3.8+ is required.' >&2; return 1; } |
| 9 | command -v curl >/dev/null || { printf '%s\n' 'curl is required.' >&2; return 1; } |
| 10 | python3 - <<'PY' |
| 11 | import os |
| 12 | import re |
| 13 | import stat |
| 14 | import subprocess |
| 15 | import sys |
| 16 | import tempfile |
| 17 | from pathlib import Path |
| 18 | |
| 19 | BEGIN = b'<!-- BEGIN MATT-POCOCK-GRILLING-QUESTION-TOOLS -->' |
| 20 | END = b'<!-- END MATT-POCOCK-GRILLING-QUESTION-TOOLS -->' |
| 21 | TOKEN = b'MATT-POCOCK-GRILLING-QUESTION-TOOLS' |
| 22 | POLICY_BEGIN = '<!-- BEGIN MATT-POCOCK-GRILLING-QUESTION-POLICY v1 -->' |
| 23 | POLICY_END = '<!-- END MATT-POCOCK-GRILLING-QUESTION-POLICY v1 -->' |
| 24 | BASE = 'https://opengist.resetrix.work/weehong/9e381dc89dff4670b7232b910454f056' |
| 25 | |
| 26 | |
| 27 | def check_path(path): |
| 28 | for part in [path] + list(path.parents): |
| 29 | if part.is_symlink(): |
| 30 | raise ValueError('Symlink refused: %s. Use physical directories and a regular instruction file; manage linked files manually.' % part) |
| 31 | if part.exists() and not (part.is_file() if part == path else part.is_dir()): |
| 32 | raise ValueError('Not a regular file/directory: %s' % part) |
| 33 | parent = path.parent |
| 34 | while not parent.exists(): |
| 35 | parent = parent.parent |
| 36 | if not os.access(parent, os.W_OK | os.X_OK): |
| 37 | raise ValueError('Directory is not writable/searchable: %s' % parent) |
| 38 | if path.exists() and not os.access(path, os.R_OK | os.W_OK): |
| 39 | raise ValueError('Instruction file is not readable/writable: %s' % path) |
| 40 | |
| 41 | |
| 42 | def install(): |
| 43 | if sys.version_info < (3, 8): |
| 44 | raise ValueError('Python 3.8+ is required.') |
| 45 | revision = os.environ.get('QUESTION_TOOLS_REVISION') or 'HEAD' |
| 46 | if revision != 'HEAD' and not re.fullmatch(r'[0-9a-fA-F]{40}', revision): |
| 47 | raise ValueError('QUESTION_TOOLS_REVISION must be HEAD or a full 40-character commit hash.') |
| 48 | url = BASE + '/raw/' + revision + '/grilling-question-tools.md' |
| 49 | result = subprocess.run(['curl', '-fsSL', '--proto', '=https', '--proto-redir', '=https', |
| 50 | '--connect-timeout', '15', '--max-time', '60', url], |
| 51 | stdout=subprocess.PIPE, check=True) |
| 52 | policy = result.stdout.decode('utf-8') |
| 53 | lines = policy.splitlines() |
| 54 | if (not lines or lines[0] != POLICY_BEGIN or lines[-1] != POLICY_END |
| 55 | or policy.count(POLICY_BEGIN) != 1 or policy.count(POLICY_END) != 1 |
| 56 | or len(policy) > 32768 or '\x00' in policy |
| 57 | or TOKEN.decode() in policy |
| 58 | or not policy.startswith(POLICY_BEGIN + '\n# Question Tools for Matt Pocock Grilling Flows\n') |
| 59 | or not all('\n%d. ' % i in policy for i in range(1, 8))): |
| 60 | raise ValueError('Downloaded policy is incomplete or invalid; no instruction files changed.') |
| 61 | block = BEGIN + b'\n' + policy.rstrip('\r\n').encode('utf-8') + b'\n' + END |
| 62 | home = os.environ.get('HOME') |
| 63 | if not home or not os.path.isabs(home): |
| 64 | raise ValueError('HOME must be an absolute path.') |
| 65 | directories = [ |
| 66 | os.environ.get('CLAUDE_CONFIG_DIR') or home + '/.claude', |
| 67 | os.environ.get('CODEX_HOME') or home + '/.codex', |
| 68 | os.environ.get('OPENCODE_CONFIG_DIR') or |
| 69 | (os.environ.get('XDG_CONFIG_HOME') or home + '/.config') + '/opencode', |
| 70 | ] |
| 71 | if any(not os.path.isabs(d) or '..' in Path(d).parts for d in directories): |
| 72 | raise ValueError('Config directories must be absolute paths without .. components.') |
| 73 | paths = list(dict.fromkeys(Path(d) / name for d, name in |
| 74 | zip(directories, ['CLAUDE.md', 'AGENTS.md', 'AGENTS.md']))) |
| 75 | changes = [] |
| 76 | # Preflight every destination before creating directories, backups, or files. |
| 77 | for path in paths: |
| 78 | check_path(path) |
| 79 | exists = path.exists() |
| 80 | old = path.read_bytes() if exists else b'' |
| 81 | if TOKEN in old: |
| 82 | if old.count(TOKEN) != 2 or old.count(BEGIN) != 1 or old.count(END) != 1: |
| 83 | raise ValueError('Malformed/duplicate managed markers: %s. Repair them manually before rerunning.' % path) |
| 84 | start, end = old.index(BEGIN), old.index(END) |
| 85 | after = end + len(END) |
| 86 | if (end < start or (start and old[start-1:start] != b'\n') |
| 87 | or old[end-1:end] != b'\n' |
| 88 | or (old[after:] and not old[after:].startswith((b'\n', b'\r\n'))) |
| 89 | or not old[start+len(BEGIN):].startswith((b'\n', b'\r\n'))): |
| 90 | raise ValueError('Malformed managed marker boundaries: %s' % path) |
| 91 | new = old[:start] + block + old[after:] |
| 92 | else: |
| 93 | separator = (b'\n' if old.endswith(b'\n') else b'\n\n') if old else b'' |
| 94 | new = old + separator + block + b'\n' |
| 95 | if new != old: |
| 96 | changes.append((path, exists, old, new)) |
| 97 | else: |
| 98 | print('Unchanged: %s' % path) |
| 99 | |
| 100 | staged = [] |
| 101 | try: |
| 102 | for path, exists, old, new in changes: |
| 103 | check_path(path) |
| 104 | if path.exists() != exists or (exists and path.read_bytes() != old): |
| 105 | raise ValueError('File changed during installation: %s. Retry with other writers stopped.' % path) |
| 106 | path.parent.mkdir(parents=True, exist_ok=True) |
| 107 | mode = stat.S_IMODE(path.stat().st_mode) if exists else 0o600 |
| 108 | fd, temporary = tempfile.mkstemp(prefix='.' + path.name + '.question-tools-', dir=str(path.parent)) |
| 109 | staged.append((path, temporary)) |
| 110 | with os.fdopen(fd, 'wb') as stream: |
| 111 | stream.write(new) |
| 112 | stream.flush() |
| 113 | os.fsync(stream.fileno()) |
| 114 | os.fchmod(stream.fileno(), mode) |
| 115 | if exists: |
| 116 | fd, backup = tempfile.mkstemp(prefix=path.name + '.question-tools-backup-', dir=str(path.parent)) |
| 117 | with os.fdopen(fd, 'wb') as stream: |
| 118 | stream.write(old) |
| 119 | stream.flush() |
| 120 | os.fsync(stream.fileno()) |
| 121 | print('Backup: %s' % backup) |
| 122 | for path, temporary in staged: |
| 123 | os.replace(temporary, path) |
| 124 | print('Updated: %s' % path) |
| 125 | finally: |
| 126 | for _, temporary in staged: |
| 127 | if os.path.exists(temporary): |
| 128 | os.unlink(temporary) |
| 129 | print('Policy source: %s' % url) |
| 130 | print('Start fresh Claude Code/Codex sessions; quit and restart OpenCode.') |
| 131 | |
| 132 | |
| 133 | try: |
| 134 | install() |
| 135 | except (OSError, ValueError, subprocess.SubprocessError) as error: |
| 136 | print('Installation failed: %s' % error, file=sys.stderr) |
| 137 | sys.exit(1) |
| 138 | PY |
| 139 | } |
| 140 | |
| 141 | main "$@" |
| 142 |