From 2e214ad528c664441b4099dd8c5596fba5a68c1e Mon Sep 17 00:00:00 2001 From: Andy Chan <34787960+ndisisnd@users.noreply.github.com> Date: Sun, 9 Aug 2026 00:33:18 +0800 Subject: [PATCH 1/4] =?UTF-8?q?=E2=9C=A8=20feat(codex):=20host-neutral=20b?= =?UTF-8?q?lock,=20ask-step=20abstraction,=20Codex=20state=20resolution=20?= =?UTF-8?q?(WS-A)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Landing 1 of the v1.2 plan: wdym runs correctly under Codex. - Host-neutral emitted signal line; bench updated in the same change - "Present options and stop" ask step replaces hard AskUserQuestion dependence in the comprehensive gate and --init (chosen by tool availability) - State resolution walks cwd to repo root and consults $CODEX_HOME - agents/openai.yaml manifest; allowed-tools dropped; description triggers front-loaded for Codex's skills-list truncation - init/help/commands/heal made host-aware (Codex global-only per D7) Co-Authored-By: Claude Fable 5 --- CHANGELOG.md | 13 +++++ SKILL.md | 29 ++++++----- agents/openai.yaml | 13 +++++ hooks/prompt-detect.py | 101 +++++++++++++++++++++++++++++---------- hooks/telemetry-stats.py | 33 ++++++++++++- refs/commands.md | 10 +++- refs/heal.md | 11 +++-- refs/help.txt | 10 ++-- refs/init.md | 92 ++++++++++++++++++++++++++++------- refs/protocol.md | 65 +++++++++++++++++++++---- tests/detect_bench.py | 59 +++++++++++++++++++++++ 11 files changed, 365 insertions(+), 71 deletions(-) create mode 100644 agents/openai.yaml diff --git a/CHANGELOG.md b/CHANGELOG.md index c5784a7..edc14f8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,19 @@ All notable changes to this project will be documented here. +## 2026-08-09 + +### [11] — wdym runs correctly under Codex, not just accidentally + +- `hooks/prompt-detect.py`: emitted block's signal line is host-neutral ("invoke per the wdym auto-invoke contract" — no `CLAUDE.md`); state resolution walks cwd up to the repo root and consults `$CODEX_HOME`, so repo-local installs are found when the host starts in a subdirectory +- `hooks/telemetry-stats.py`: mirrors the same resolution order so `--status` reads the file the hook writes +- `tests/detect_bench.py`: asserts the new signal line, bans host filenames in the block, adds a subdirectory-launch scope test +- `SKILL.md`: `allowed-tools:` frontmatter removed (not a Codex field); description rewritten with trigger words front-loaded to survive Codex's skills-list truncation; host-portability note added +- `agents/openai.yaml`: new — Codex skill manifest with `display_name`, `short_description`, `policy.allow_implicit_invocation: true` +- `refs/protocol.md`: comprehensive approval gate routed through a new "present options and stop" ask step — `AskUserQuestion` when available, plain-text question + end-of-turn otherwise, chosen by tool availability +- `refs/init.md`: host-resolution table (hook file, trust anchor, command prefix per host), both `--init` questions via the ask step, Codex global-only rule, duplicate-hook collapse +- `refs/commands.md`, `refs/help.txt`, `refs/heal.md`: host-neutral wording; heal knows the Codex untrusted-hook "wired but silent" failure mode + ## 2026-07-30 ### [10] — Principles catch up to modern models; writing prompts no longer read as code diff --git a/SKILL.md b/SKILL.md index 34b4ee3..a38bcca 100644 --- a/SKILL.md +++ b/SKILL.md @@ -1,21 +1,28 @@ --- name: wdym description: > - Prompt rewriter with two activation modes — hooked (fires on every prompt via a - UserPromptSubmit block) or on-demand (invoked via /wdym or when - asked to improve a prompt). Detects prompt type (code, question, text-gen) and - rewrites using 2–3 matched principles; flash mode runs immediately, comprehensive - gates for approval. Manage with /wdym --init, --status, --set-mode, --help. -allowed-tools: - - AskUserQuestion - - Read - - Write - - Edit - - Bash + Rewrite, improve, or enhance a prompt. Runs on /wdym, on any request to + improve/enhance/rewrite a prompt, and on a UserPromptSubmit + block. Detects prompt type (code, question, text-gen) and rewrites using 2–3 + matched principles; flash mode rewrites immediately, comprehensive mode + presents the rewrite and waits for approval. Manage with /wdym --init, + --status, --set-mode, --help. --- # wdym +**Host portability.** wdym runs on any agent host with a `UserPromptSubmit` +hook. Two things vary by host while the skill is running (a third set — where +installed files land — is resolved by `refs/init.md`, and only during `--init`): + +- **Command prefix** — `/wdym` on Claude Code, `$wdym` on Codex. This skill + writes `/wdym` throughout; use the running host's prefix in anything shown to + the user. +- **Asking the user** — the *ask step* in `refs/protocol.md`. Use the + `AskUserQuestion` tool when it is available, otherwise ask in plain text and + end the turn. Choose by whether that tool is available, never by guessing + which host you are on. + **Activation** is set by `pref.json`'s `activation` key and chosen at `/wdym --init`: diff --git a/agents/openai.yaml b/agents/openai.yaml new file mode 100644 index 0000000..4192310 --- /dev/null +++ b/agents/openai.yaml @@ -0,0 +1,13 @@ +# Codex skill metadata. +# +# Codex reads this file for the skills list and for whether the model may invoke +# wdym on its own; Claude Code ignores it. Tool dependencies are deliberately not +# declared: wdym uses only the host's built-in file-read, file-write and shell +# capabilities, and asking the user is handled by the tool-availability check in +# refs/protocol.md rather than by requiring a tool that Codex does not have. + +display_name: wdym +short_description: Rewrite, improve, or enhance a prompt before it runs. + +policy: + allow_implicit_invocation: true diff --git a/hooks/prompt-detect.py b/hooks/prompt-detect.py index eae729f..bd95ba2 100755 --- a/hooks/prompt-detect.py +++ b/hooks/prompt-detect.py @@ -23,10 +23,10 @@ Passthrough prompts (slash command / <=5 words / conversational follow-up) get NO block at all — only a telemetry line. This makes the contract binary: block present => substantive prompt => invoke the wdym skill. The block is a neutral -classification signal; the invoke instruction itself lives in CLAUDE.md (a -trusted, user-installed contract), so an override-shaped imperative in the block -can't be mistaken for a prompt injection and refused. Block absent => respond -normally. +classification signal; the invoke instruction itself lives in the host's own +instruction file (CLAUDE.md on Claude Code, AGENTS.md on Codex) as a trusted, +user-installed contract, so an override-shaped imperative in the block can't be +mistaken for a prompt injection and refused. Block absent => respond normally. Token-efficiency duties absorbed from the skill (each deletes an LLM tool call, i.e. a full-context API round trip): @@ -85,17 +85,66 @@ def is_passthrough(raw: str) -> bool: return False -def telemetry_path(): - """Resolve wdym/telemetry.jsonl at the active install scope: local - .claude/wdym/ overrides global ~/.claude/wdym/. Returns None if neither - install dir exists (the dir is created by --init, never by the hook).""" - candidates = [] +def _cwd_walk_dirs(): + """The CWD and its ancestors, nearest first, stopping at the repo root. + + Only Claude Code sets CLAUDE_PROJECT_DIR. Other hosts (Codex) may start the + session in a subdirectory of the repo, so a repo-local install one or more + levels up would be missed and the run would silently fall through to the + global scope. Walking up fixes that without naming a host: stop at the + directory holding `.git` (the repo root, itself included), and hard-stop at + $HOME or the filesystem root so an unrelated parent install is never picked + up. Depth is capped as a cheap guard against pathological trees.""" + dirs = [] + try: + home = os.path.realpath(os.path.expanduser("~")) + cur = os.path.realpath(os.getcwd()) + except Exception: + return dirs + for _ in range(40): + dirs.append(cur) + if os.path.exists(os.path.join(cur, ".git")): + break + parent = os.path.dirname(cur) + if parent == cur or cur == home: + break + cur = parent + return dirs + + +def state_dirs(): + """wdym state directories in resolution order — nearest scope wins. + + 1. $CLAUDE_PROJECT_DIR/.claude/wdym — Claude Code's repo root, when set. + 2. CWD and ancestors up to the repo root, each `.claude/wdym` — the + host-neutral way to find a repo-local install. + 3. ~/.claude/wdym — the canonical global install, on either host. + 4. $CODEX_HOME/wdym — Codex's config root, when it is set and non-default. + + Order is deduplicated but otherwise preserved, so the same list backs both + the pref file and the telemetry file and the two can never disagree.""" + dirs = [] proj = os.environ.get("CLAUDE_PROJECT_DIR") if proj: - candidates.append(os.path.join(proj, ".claude", "wdym")) - candidates.append(os.path.join(os.getcwd(), ".claude", "wdym")) - candidates.append(os.path.expanduser("~/.claude/wdym")) - for d in candidates: + dirs.append(os.path.join(proj, ".claude", "wdym")) + for d in _cwd_walk_dirs(): + dirs.append(os.path.join(d, ".claude", "wdym")) + dirs.append(os.path.expanduser("~/.claude/wdym")) + codex_home = os.environ.get("CODEX_HOME") + if codex_home: + dirs.append(os.path.join(codex_home, "wdym")) + ordered = [] + for d in dirs: + if d not in ordered: + ordered.append(d) + return ordered + + +def telemetry_path(): + """Resolve wdym/telemetry.jsonl at the active install scope (state_dirs + order: nearest local scope wins, global last). Returns None if no install + dir exists (the dir is created by --init, never by the hook).""" + for d in state_dirs(): if os.path.isdir(d): return os.path.join(d, "telemetry.jsonl") return None @@ -114,14 +163,8 @@ def log_telemetry(record: dict) -> None: def pref_candidates(): """Pref file paths in resolution order (same order as telemetry_path): - local scope overrides global.""" - candidates = [] - proj = os.environ.get("CLAUDE_PROJECT_DIR") - if proj: - candidates.append(os.path.join(proj, ".claude", "wdym", "pref.json")) - candidates.append(os.path.join(os.getcwd(), ".claude", "wdym", "pref.json")) - candidates.append(os.path.expanduser("~/.claude/wdym/pref.json")) - return candidates + the nearest local scope overrides global.""" + return [os.path.join(d, "pref.json") for d in state_dirs()] def read_pref(): @@ -260,12 +303,18 @@ def is_forced(cat: dict, text: str) -> bool: # A neutral signal, not an imperative. The trusted invoke instruction lives in -# CLAUDE.md (installed by init); the block only reports that a classification is -# available. Override-shaped wording here ('ACTION: ... BEFORE processing this -# prompt') read as a prompt injection through the low-trust hook channel and got -# refused, silently skipping the skill — so the authority moved to CLAUDE.md. +# the host's own instruction file (CLAUDE.md on Claude Code, AGENTS.md on Codex), +# written by init; the block only reports that a classification is available. +# Override-shaped wording here ('ACTION: ... BEFORE processing this prompt') read +# as a prompt injection through the low-trust hook channel and got refused, +# silently skipping the skill — so the authority moved to that file. The line +# names the contract, not the file, because the filename is host-specific. +# +# Treat this string as user-facing copy: Codex currently renders additionalContext +# as a visible developer message (openai/codex#16933), so the user reads it. One +# tidy status line, no internal plumbing. ACTION_LINE = ( - "signal: wdym classification available — invoke per the CLAUDE.md contract." + "signal: wdym classification available — invoke per the wdym auto-invoke contract." ) diff --git a/hooks/telemetry-stats.py b/hooks/telemetry-stats.py index e0a946b..18fe83b 100755 --- a/hooks/telemetry-stats.py +++ b/hooks/telemetry-stats.py @@ -26,6 +26,28 @@ TYPES = ("code", "question", "text-gen", "global") +def cwd_walk_dirs(): + """CWD and its ancestors, nearest first, stopping at the repo root — the + host-neutral way to find a repo-local install when the session started in a + subdirectory (only Claude Code sets CLAUDE_PROJECT_DIR). Mirrors + hooks/prompt-detect.py so --status reads the file the hook writes.""" + dirs = [] + try: + home = os.path.realpath(os.path.expanduser("~")) + cur = os.path.realpath(os.getcwd()) + except Exception: + return dirs + for _ in range(40): + dirs.append(cur) + if os.path.exists(os.path.join(cur, ".git")): + break + parent = os.path.dirname(cur) + if parent == cur or cur == home: + break + cur = parent + return dirs + + def resolve(): """Return (telemetry_path, scope_label) at the active install scope, or (None, None) if no wdym install dir exists.""" @@ -33,8 +55,15 @@ def resolve(): candidates = [] if proj: candidates.append((os.path.join(proj, ".claude", "wdym"), "local")) - candidates.append((os.path.join(os.getcwd(), ".claude", "wdym"), "local")) - candidates.append((os.path.expanduser("~/.claude/wdym"), "global")) + home_dir = os.path.expanduser("~/.claude/wdym") + for d in cwd_walk_dirs(): + cand = os.path.join(d, ".claude", "wdym") + if cand != home_dir: # the home dir is the global scope, reported below + candidates.append((cand, "local")) + candidates.append((home_dir, "global")) + codex_home = os.environ.get("CODEX_HOME") + if codex_home: + candidates.append((os.path.join(codex_home, "wdym"), "global")) for d, label in candidates: if os.path.isdir(d): return os.path.join(d, "telemetry.jsonl"), label diff --git a/refs/commands.md b/refs/commands.md index 40132ee..35d7b24 100644 --- a/refs/commands.md +++ b/refs/commands.md @@ -10,6 +10,10 @@ Reached from `refs/protocol.md` Step 0 when the raw prompt carries a `/wdym` command flag. Each command executes and then **terminates** — never continue to Step 1, never enhance a prompt. +**Command prefix:** `/wdym` on Claude Code, `$wdym` on Codex. Recognise either +form on input; use the running host's form in anything shown to the user. This +document writes `/wdym`. + **Self-check ordering:** for `--help`, `--status`, and `--set-mode`, probe the install first (slash commands get no hook block, so the hook's probe didn't run): `ls` the five required files (`refs/categories.json`, `refs/categories.default.json`, @@ -25,7 +29,11 @@ Dispatch on the first flag present, in this order: Run `refs/init.md` end-to-end, then terminate. Do not run the self-check. It asks two questions — scope (local vs. global) and activation (hook vs. on-demand) — writes `pref.json`, wires or unwires the `UserPromptSubmit` hook to match, and -installs the `CLAUDE.md` trust-anchor contract. +installs the trust-anchor contract in the host's instruction file (`CLAUDE.md` on +Claude Code, `AGENTS.md` on Codex — `refs/init.md` resolves which). + +Both questions go through the **ask step** in `refs/protocol.md`, so they work on +a host without an `AskUserQuestion` tool. This is the completion step for any install that delivered only the skill files (`npx skills add`, a manual copy, a dev checkout) — those set up none of the diff --git a/refs/heal.md b/refs/heal.md index b38e386..90c9405 100644 --- a/refs/heal.md +++ b/refs/heal.md @@ -32,11 +32,14 @@ one. - Block present with `verdict: degraded` → hook ran but its config is broken; go to Check 3. -- **No block** (and `activation: hook`) → read the resolved settings file (local - `settings.local.json`, else global `settings.json`) for a - `hooks.UserPromptSubmit` entry whose command contains `prompt-detect.py`: +- **No block** (and `activation: hook`) → read the host's resolved hook file + (`refs/init.md` Step I1 names it per host: Claude Code local + `settings.local.json` else global `settings.json`; Codex `~/.codex/hooks.json`) + for a `UserPromptSubmit` entry whose command contains `prompt-detect.py`: - Entry present **and** script path exists → wired but silent (e.g. `python3` - unavailable). Record `hook silent`; do not repair. + unavailable; on Codex, most often an unapproved hook — it trusts hooks by + file contents, so any reinstall revokes approval and it fails silently). + Record `hook silent`; do not repair. On Codex, hint `/hooks` to approve it. - Entry present **but** script path missing → stale path (skill dir moved). Rewrite the command to `python3 "/hooks/prompt-detect.py"` using the running skill's absolute root (merge rules: `refs/init.md` Step I5). diff --git a/refs/help.txt b/refs/help.txt index 697fb89..5aa03d1 100644 --- a/refs/help.txt +++ b/refs/help.txt @@ -1,4 +1,4 @@ -wdym — prompt rewriter for Claude Code +wdym — prompt rewriter for Claude Code and Codex Applies structured rewrite principles to a prompt to make it sharper and better scoped, then submits the enhanced version. Detects prompt type (code, question, @@ -6,6 +6,9 @@ text-gen) and loads matching principles on top of the universal base. Runs eithe on every prompt (hook) or only when you ask for it (on-demand) — your choice at /wdym --init. +Commands are written "/wdym" below. On Codex, type "$wdym" instead; everything +after the prefix is identical. + COMMANDS ──────────────────────────────────────────────────────────────────── /wdym Rewrite this prompt now @@ -17,8 +20,9 @@ COMMANDS INIT FLAGS (skip the matching question) ──────────────────────────────────────────────────────────────────── - --global Apply to all projects (~/.claude) - --local Apply to this directory only (./.claude) + --global Apply to all projects (user scope) + --local Apply to this directory only (Claude Code + only; Codex is global-scope only) --hook Fire automatically on every prompt --on-demand Fire only when invoked diff --git a/refs/init.md b/refs/init.md index 1632b28..a860ee2 100644 --- a/refs/init.md +++ b/refs/init.md @@ -1,6 +1,6 @@ --- name: Init Protocol -description: Bootstrap/reconfigure the wdym skill — writes pref.json (mode + activation), wires or unwires the UserPromptSubmit hook, and installs the CLAUDE.md trust-anchor contract, at local (this directory) or global (~/.claude) scope +description: Bootstrap/reconfigure the wdym skill — writes pref.json (mode + activation), wires or unwires the UserPromptSubmit hook, and installs the trust-anchor contract in the host's instruction file, at local (this directory) or global (user) scope type: reference --- @@ -31,9 +31,31 @@ prompt. Confirm `SKILL_DIR/hooks/prompt-detect.py` exists before proceeding. If it does not, report the problem and terminate without writing anything. +**Resolve `HOST`** as well, by evidence rather than assumption. Three install +targets differ by host and nothing else does: which file holds the hook, which +instruction file carries the trust anchor, and which prefix the user types. + +| | Claude Code | Codex | +|---|---|---| +| Evidence | `CLAUDE_PROJECT_DIR` set, or `~/.claude/` exists | `CODEX_HOME` set, or `~/.codex/` exists | +| Hook file (global) | `~/.claude/settings.json` | `$CODEX_HOME/hooks.json` (default `~/.codex/hooks.json`) | +| Hook file (local) | `/.claude/settings.local.json` | not supported — global only | +| Trust anchor (global) | `~/.claude/CLAUDE.md` | `$CODEX_HOME/AGENTS.md` (default `~/.codex/AGENTS.md`) | +| Trust anchor (local) | `/CLAUDE.md` | not supported — global only | +| Command prefix | `/wdym` | `$wdym` | + +If both hosts show evidence, ask which to configure using the **ask step** +(`refs/protocol.md`) with one option per host plus "Both". Configure each chosen +host with the same `pref.json`, and report them separately in Step I7. + +Use `HOST`'s prefix in every line shown to the user. This document writes +`/wdym`. + ## Step I2 — Choose install scope -Call `AskUserQuestion` with these options: +Run the **ask step** (`refs/protocol.md` — `AskUserQuestion` when that tool is +available, otherwise the plain-text question shape) with the question "Where +should wdym apply?" and these options: - **Local (this directory)** — installs into `$CLAUDE_PROJECT_DIR/.claude/` (fall back to the current working directory). Applies only in this directory. @@ -43,6 +65,13 @@ Call `AskUserQuestion` with these options: If the user shortcut the choice in their prompt — `--init --global` (or "globally") → Global; `--init --local` → Local — honour it and skip the question. +**Codex is global-scope only.** A repo-scoped Codex hook lives in a committed +file, so every teammate who pulls the repo gets an approval prompt for a tool +they never installed. If `HOST` is Codex and the user asked for local scope, do +not write anything for that host: say so plainly, point at +`$wdym --init --global`, and stop (if Claude Code was also selected, continue for +Claude only). + Resolve the chosen scope into: | Variable | Local scope | Global scope | @@ -51,12 +80,18 @@ Resolve the chosen scope into: | `PREF_PATH` | `BASE_DIR/wdym/pref.json` | `BASE_DIR/wdym/pref.json` | | `SETTINGS_PATH` | `BASE_DIR/settings.local.json` (personal, not committed) | `BASE_DIR/settings.json` (global user settings) | -Writes are allowed **only** under the chosen `BASE_DIR`. Never write to the other -scope's location. +Under Codex, `SETTINGS_PATH` is the Step I1 hook file instead +(`$CODEX_HOME/hooks.json`, default `~/.codex/hooks.json`); `BASE_DIR` and +`PREF_PATH` stay `~/.claude` so one pref file serves both hosts and the two can +never disagree about mode or activation. + +Writes are allowed **only** under the chosen `BASE_DIR` (plus the host's own hook +and instruction files). Never write to the other scope's location. ## Step I3 — Choose activation -Call `AskUserQuestion` with these options: +Run the **ask step** again with the question "When should wdym run?" and these +options: - **Hook (every prompt)** — wires `UserPromptSubmit` so wdym classifies and rewrites every substantive prompt automatically. Passthrough prompts (slash @@ -92,10 +127,11 @@ reads to decide whether to emit anything at all. ## Step I5 — Wire or unwire the UserPromptSubmit hook -Target file: `SETTINGS_PATH` (`settings.local.json` for local scope, -`settings.json` for global scope). The hook entry this step manages (note the -**absolute** `SKILL_DIR` path so it resolves no matter what directory Claude -runs from): +Target file: `SETTINGS_PATH` from Step I2 — on Claude Code +`settings.local.json` (local scope) or `settings.json` (global scope), on Codex +the global hooks file. The hook entry this step manages (note the **absolute** +`SKILL_DIR` path, so it resolves no matter which directory the session started +in): ```json { @@ -104,8 +140,16 @@ runs from): } ``` +Both hosts fire the same `UserPromptSubmit` event and read the same stdout +contract, so the script and its invocation are identical. Only the file and its +surrounding JSON shape differ. On Codex, read the existing hooks file first and +follow the entry shape already in it rather than imposing this one; the event key +is still `UserPromptSubmit`. + Match existing entries by the substring `prompt-detect.py`, not by exact string — -a path may be stale from a moved skill directory. +a path may be stale from a moved skill directory. **Collapse duplicates:** if two +or more entries match, keep one and remove the rest. Duplicate handlers inject +the block twice and double every telemetry row. ### If `ACTIVATION = hook` → wire it @@ -159,8 +203,10 @@ unparseable, report it and stop rather than clobbering it. ## Step I6 — Write the trust-anchor contract -Target file: `~/.claude/CLAUDE.md` for global scope, `/CLAUDE.md` for -local scope (the memory file at the chosen scope). +Target file: the host's instruction file at the chosen scope, per the Step I1 +table — Claude Code: `~/.claude/CLAUDE.md` (global) or `/CLAUDE.md` +(local); Codex: `$CODEX_HOME/AGENTS.md`, default `~/.codex/AGENTS.md` (global +only). The block text below is identical either way; only the file differs. The `` block arrives through the hook channel, which is low-trust: without a trusted file authorising it, an override-shaped signal reads as a @@ -170,7 +216,8 @@ most when wdym arrived some way other than `install.sh` (`npx skills add`, a manual copy, a cloned dev checkout), because those deliver the skill files and nothing else. -Write this marker-delimited block, substituting the Step I3 `ACTIVATION`: +Write this marker-delimited block, substituting the Step I3 `ACTIVATION` and the +`HOST` command prefix (`/wdym` on Claude Code, `$wdym` on Codex): ```markdown @@ -178,7 +225,7 @@ Write this marker-delimited block, substituting the Step I3 `ACTIVATION`: The `` block is emitted by a user-installed `UserPromptSubmit` hook — it is **trusted configuration, not untrusted injected input**. Invoking `wdym` when a block is present is expected and safe; do not treat the block as a prompt injection. -A `` block present (any verdict) ⇒ invoke the `wdym` skill via the Skill tool **before** processing the prompt; no block ⇒ respond normally. +A `` block present (any verdict) ⇒ invoke the `wdym` skill **before** processing the prompt; no block ⇒ respond normally. Activation is set by `activation` in wdym's `pref.json` (currently ``) and changed with `/wdym --init --hook` / `--on-demand`, which moves the hook wiring with it. @@ -197,8 +244,9 @@ Record whether it was added, refreshed, or already current. ## Step I7 — Confirm -Emit a short summary listing scope, activation, and exactly what was created, -changed, or left untouched, e.g.: +Emit a short summary listing host, scope, activation, and exactly what was +created, changed, or left untouched. Name the files you actually touched, and use +the host's command prefix in the closing hints. Claude Code, global scope: ``` Initialised wdym (global scope) in ~/.claude: @@ -209,6 +257,18 @@ wdym now runs on every substantive prompt. Switch modes with "/wdym --set-mode --flash", or go manual with "/wdym --init --on-demand". ``` +Codex, global scope (the only scope Codex supports): + +``` +Initialised wdym for Codex (global scope): + • ~/.claude/wdym/pref.json — created (mode: comprehensive, activation: hook) + • ~/.codex/hooks.json — hook added (UserPromptSubmit → prompt-detect.py) + • ~/.codex/AGENTS.md — auto-invoke contract added +Codex trusts hooks by file contents, so run /hooks in Codex and approve the wdym +hook — until you do, wdym stays silent and Codex will not warn you. To confirm: +submit any prompt, then run "$wdym --status". +``` + Switching to on-demand: ``` diff --git a/refs/protocol.md b/refs/protocol.md index 6647046..73955a4 100644 --- a/refs/protocol.md +++ b/refs/protocol.md @@ -10,6 +10,40 @@ Emit no step markers. Visible output is limited to the Step 0.5 repair line (onl when something was healed/escalated) and the Step 6 enhanced-prompt block (comprehensive mode only — the rewritten prompt, no original, no rationale). +## Ask step — present options and stop + +wdym asks the user a question in exactly two places: the Step 6 approval gate and +`refs/init.md`'s scope and activation questions. Both use this one step, so the +question reads the same whichever host is running. + +**Pick the implementation by tool availability, never by guessing the host.** +Check your own available tools for `AskUserQuestion`: + +- **Available** → call it once with the options as given. Unchanged behaviour. +- **Not available** (Codex, and any host without it) → emit the options as plain + text in the shape below, then **end the turn**. Do not choose an option + yourself, do not act on any option, and do not continue past the step. Read the + user's next message as the answer. + +Plain-text shape — a one-line question, the options numbered in the order given +with a short consequence each, and one closing line: + +``` + + +1.