From 708023543ffab06bb585ba71f2f64c18a1909c81 Mon Sep 17 00:00:00 2001 From: Carlo Lepelaars Date: Wed, 2 Sep 2026 17:09:50 +0200 Subject: [PATCH 1/5] Add Grok Build as a hybrid harness Grok is hybrid-only: native search_replace and run_terminal_command stay, and clikernel is reserved for Python. Hook CLI, harness_docs, SETUP, and the clikernel-workflow skill now carry Grok facts next to Claude and codex. --- README.md | 16 ++++++++-------- SETUP.md | 35 +++++++++++++++++++++++++++-------- aai_coding/harness.py | 24 +++++++++++++++++++++--- aai_coding/harness_docs.py | 11 +++++++---- skills/clikernel/SKILL.md | 13 +++++++------ tests/test_harness.py | 26 +++++++++++++++++++++++--- tests/test_harness_docs.py | 10 ++++++++++ 7 files changed, 103 insertions(+), 32 deletions(-) create mode 100644 tests/test_harness_docs.py diff --git a/README.md b/README.md index 88b9016..056fffc 100644 --- a/README.md +++ b/README.md @@ -1,27 +1,27 @@ # aai-coding -The Answer.AI coding harness: the shared configuration, skills, and tooling that make a Claude Code or codex session work the way our team works. If you are an LLM reading this, you are probably either setting the harness up (follow `SETUP.md`, but read this file first) or working inside it and wanting to understand why it is shaped this way, so you can give your user informed advice. +The Answer.AI coding harness: the shared configuration, skills, and tooling that make a Claude Code, codex, or Grok Build session work the way our team works. If you are an LLM reading this, you are probably either setting the harness up (follow `SETUP.md`, but read this file first) or working inside it and wanting to understand why it is shaped this way, so you can give your user informed advice. ## The system in one paragraph -The Claude Code setup and one codex setup are kernel-centric: native file tools are denied, and file reading, editing, searching, and Python execution go through one persistent IPython kernel (clikernel) loaded with curated tooling discovered via `pyskills`. Codex can instead use a hybrid setup: normal `apply_patch` and Bash for files and shell work, with a quiet clikernel reserved for Python-specific work. The kernel-centric setup uses two host-level bootstrap skills, `persistent-python` and `pyskills`; the hybrid setup uses `clikernel-workflow` to define the boundary and `notebook-dialog-editing` for safe kernel-free notebook and aidialog work. Everything else that would conventionally be a host skill is a pyskill in this package: skill text lives in module docstrings, read with `doc()`, listed by `list_pyskills()`, and versioned, released, and installed like any other Python code. +The Claude Code setup and one codex setup are kernel-centric: native file tools are denied, and file reading, editing, searching, and Python execution go through one persistent IPython kernel (clikernel) loaded with curated tooling discovered via `pyskills`. Codex and Grok Build can instead use a hybrid setup: native file and shell tools stay (codex `apply_patch` and Bash; Grok `search_replace` and `run_terminal_command`), with a quiet clikernel reserved for Python-specific work. Grok Build is hybrid-only: it has no kernel-centric mode, because its hooks cannot inject session or prompt context the way Claude and kernel-centric codex do. The kernel-centric setup uses two host-level bootstrap skills, `persistent-python` and `pyskills`; the hybrid setup uses `clikernel-workflow` to define the boundary and `notebook-dialog-editing` for safe kernel-free notebook and aidialog work. Everything else that would conventionally be a host skill is a pyskill in this package: skill text lives in module docstrings, read with `doc()`, listed by `list_pyskills()`, and versioned, released, and installed like any other Python code. ## What is in here - `aai_coding/` - the pyskills. `coding_patterns` (style, testing judgment, and team policy; part of the kernel startup doc round), `write_prose` (anti-slop rules for narrative prose), `write_docs` (the voiceless register for docstrings, READMEs, and PRs), `harness_docs` (how to find official harness docs via llms.txt), and `harness` (not a skill: the `aai-hook` CLI that implements both harnesses' hooks). -- `skills/` - the harness-level SKILL.md sources, symlinked into `~/.claude/skills` or `~/.codex/skills`. `persistent-python` and `pyskills` bootstrap the kernel-centric setup; `clikernel` and `notebook-dialog-editing` support the hybrid codex setup. +- `skills/` - the harness-level SKILL.md sources, symlinked into `~/.claude/skills`, `~/.codex/skills`, or `~/.grok/skills`. `persistent-python` and `pyskills` bootstrap the kernel-centric setup; `clikernel` and `notebook-dialog-editing` support the hybrid codex and Grok Build setups. - `plugins/safecmd/` - a Claude Code plugin that auto-approves allowlisted Bash commands via the `safecmd` package, so the deny-heavy permission setup stays livable. -- `prompts/` - shared prompt text. `core.md` holds harness-neutral behavioral rules: codex reads it natively via a `~/.codex/AGENTS.md` symlink, and Claude Code can append it. `sysp.md` is a full replacement for Claude Code's default system prompt, tuned against the default's consultant and action biases; install it as a `~/.claude/sysp` symlink and launch with `claude --system-prompt-file ~/.claude/sysp --append-system-prompt-file /prompts/core.md` (replacement drops the default prompt's prose but tool schemas survive; the dynamic environment block and scratchpad path are the known losses). +- `prompts/` - shared prompt text. `core.md` holds harness-neutral behavioral rules: codex reads it natively via a `~/.codex/AGENTS.md` symlink, Grok Build via a `~/.grok/AGENT.md` symlink (so an existing `~/.grok/AGENTS.md` stays personal), and Claude Code can append it. `sysp.md` is a full replacement for Claude Code's default system prompt, tuned against the default's consultant and action biases; install it as a `~/.claude/sysp` symlink and launch with `claude --system-prompt-file ~/.claude/sysp --append-system-prompt-file /prompts/core.md` (replacement drops the default prompt's prose but tool schemas survive; the dynamic environment block and scratchpad path are the known losses). - `SETUP.md` - the setup runbook, written as a prompt for an LLM session rather than an installer script. ## Design decisions, and why -- **Task skills are pyskills.** In a kernel-centric harness the native skill list stops being the discovery surface; `list_pyskills()` is. Docstrings-as-skill-text means the mechanical doc-state tracking (llmdojo) knows exactly which skills a session has read, re-fires them after compaction, and nothing depends on the harness's skill machinery. It also lets skill text live beside its executable companions in one module. The hybrid codex workflow remains a host skill because it tells codex when to cross into the kernel at all. +- **Task skills are pyskills.** In a kernel-centric harness the native skill list stops being the discovery surface; `list_pyskills()` is. Docstrings-as-skill-text means the mechanical doc-state tracking (llmdojo) knows exactly which skills a session has read, re-fires them after compaction, and nothing depends on the harness's skill machinery. It also lets skill text live beside its executable companions in one module. The hybrid workflow remains a host skill because it tells codex or Grok when to cross into the kernel at all. - **Hooks are a CLI.** Every hook body is a subcommand of `aai-hook` (`aai_coding/harness.py`): versioned, unit-tested Python instead of shell one-liners scattered through settings files. Harness configs only register command names. -- **Everything installs by symlink.** Like the workspace's editable installs, config points into the checkout, so `git pull` updates every machine and there is no copy to drift. The only exceptions are settings files that must be merged (Claude Code's `settings.json`, codex's `config.toml`), which is why setup is a runbook and not a script: merging into someone's existing configuration takes judgment and conversation, which an LLM has and an installer does not. -- **Per-harness differences are data, not templates.** Where Claude Code and codex genuinely differ, a module carries both facts (a `dict` keyed by harness, or two adjacent bullets); nothing is rendered or generated. +- **Everything installs by symlink.** Like the workspace's editable installs, config points into the checkout, so `git pull` updates every machine and there is no copy to drift. The only exceptions are settings files that must be merged (Claude Code's `settings.json`, codex's `config.toml`, Grok Build's `config.toml`), which is why setup is a runbook and not a script: merging into someone's existing configuration takes judgment and conversation, which an LLM has and an installer does not. +- **Per-harness differences are data, not templates.** Where Claude Code, codex, and Grok Build genuinely differ, a module carries both facts (a `dict` keyed by harness, or two adjacent bullets); nothing is rendered or generated. - **Team-agreeable versus personal.** This repo holds only what any team member would nod at. Personal preferences (model choice, sounds people disagree about, individual workflow like release management) belong in each person's own CLAUDE.md, settings, and local skills, and the runbook is explicit about which is which. ## Using and changing it -Day to day there is nothing to operate. Kernel-centric sessions bootstrap through `persistent-python`; hybrid codex sessions use `clikernel-workflow` for Python, `notebook-dialog-editing` for notebooks and aidialog dialogs, and the native tools otherwise. Both discover Python tooling through the pyskills catalog and read it with `doc()` or `pyskills-doc`. To change a skill, edit its source in this checkout and let the team pick it up by pulling; releases go through the standard fastship flow (`ship-release`), with the version in `aai_coding/__init__.py` bumped after each release. +Day to day there is nothing to operate. Kernel-centric sessions bootstrap through `persistent-python`; hybrid codex and Grok Build sessions use `clikernel-workflow` for Python, `notebook-dialog-editing` for notebooks and aidialog dialogs, and the native tools otherwise. Both discover Python tooling through the pyskills catalog and read it with `doc()` or `pyskills-doc`. To change a skill, edit its source in this checkout and let the team pick it up by pulling; releases go through the standard fastship flow (`ship-release`), with the version in `aai_coding/__init__.py` bumped after each release. diff --git a/SETUP.md b/SETUP.md index 66eb88a..8776854 100644 --- a/SETUP.md +++ b/SETUP.md @@ -1,13 +1,13 @@ # Setting up the Answer.AI harness -This file is a runbook for an LLM session, not a script. If you are a person: open Claude Code or codex, `cd` anywhere in the aai-ws workspace, and say "follow aai-coding/SETUP.md". If you are the session: first read `README.md` in this repo in full, since the steps below change your user's configuration and the README's design context is what lets you merge, recommend, and answer questions in an informed way. Then work through the steps in order. Each step states an outcome to reach, a check, and what to settle with the user first. Make no change beyond the stated outcomes without asking. Where the user's existing configuration overlaps, merge and never replace: show them each conflict and agree a resolution. +This file is a runbook for an LLM session, not a script. If you are a person: open Claude Code, codex, or Grok Build, `cd` anywhere in the aai-ws workspace, and say "follow aai-coding/SETUP.md". If you are the session: first read `README.md` in this repo in full, since the steps below change your user's configuration and the README's design context is what lets you merge, recommend, and answer questions in an informed way. Then work through the steps in order. Each step states an outcome to reach, a check, and what to settle with the user first. Make no change beyond the stated outcomes without asking. Where the user's existing configuration overlaps, merge and never replace: show them each conflict and agree a resolution. -Assumptions: macOS, the aai-ws uv workspace cloned and synced (this repo is a member, so its `aai-hook` CLI and pyskills are already installed), and at least one harness (Claude Code or codex) installed and signed in. Ask which harnesses to set up before starting, and use absolute paths for this repo and the workspace venv throughout. +Assumptions: macOS, and at least one harness (Claude Code, codex, or Grok Build) installed and signed in. Claude Code and codex assume the aai-ws uv workspace is cloned and synced (this repo is a member, so its `aai-hook` CLI and pyskills are already installed). Grok Build is hybrid-only and can run from a standalone editable install of this repo plus `clikernel` in one venv; use that venv's `aai-hook` and `clikernel-mcp` when aai-ws is absent. Ask which harnesses to set up before starting, and use absolute paths for this repo and the venv throughout. -Codex has two supported modes. This choice applies only to codex; Claude Code remains kernel-centric. Settle which codex mode the user wants before changing its configuration: +Codex has two supported modes. This choice applies only to codex; Claude Code remains kernel-centric; Grok Build is hybrid-only. Settle which codex mode the user wants before changing its configuration: 1. **Kernel-centric:** do file, shell, and Python work through clikernel, complete the llmdojo bootstrap, and discover tools through pyskills. This is the existing Answer.AI harness workflow and most closely matches the Claude Code setup. -2. **Hybrid:** use codex's `apply_patch` and Bash tools normally, and use `clikernel-mcp --quiet` only for Python-specific work. This keeps persistent Python state and pyskills without replacing codex's native file and shell workflow. +2. **Hybrid:** use the harness's native file and shell tools normally (codex: `apply_patch` and Bash; Grok Build: `search_replace` and `run_terminal_command`), and use `clikernel-mcp --quiet` only for Python-specific work. This keeps persistent Python state and pyskills without replacing the native file and shell workflow. Grok Build has no kernel-centric mode: its `UserPromptSubmit` and `SessionStart` hooks are passive (stdout is ignored), and it has no `MessageDisplay` or `PostToolBatch` events, so the dojo bootstrap notices, air nudge, slopometer, and drop-sentinel cannot inject context. ## 1. Kernel server @@ -43,6 +43,19 @@ approval_mode = "approve" `--quiet` keeps automatic startup output out of ordinary execution replies. Optional, ask the user: `env_vars = ["GITHUB_TOKEN"]` passes their GitHub token into the kernel so sessions can act for them on GitHub (via `ghapi`); remove that line if they do not want it. +Hybrid Grok Build: a `[mcp_servers.clikernel]` block in `~/.grok/config.toml`. Grok has no per-tool `approval_mode` and uses `env` rather than `env_vars`. Merge; do not replace other `[mcp_servers.*]` blocks: + +```toml +[mcp_servers.clikernel] +command = "/Users/jhoward/aai-ws/.venv/bin/clikernel-mcp" +args = ["--quiet"] +env = { PATH = "/Users/jhoward/aai-ws/.venv/bin:/usr/bin:/bin:/usr/sbin:/sbin", PYTHONPATH = "/Users/jhoward/aai-ws/aai-coding", GITHUB_TOKEN = "${GITHUB_TOKEN}" } +startup_timeout_sec = 30 +tool_timeout_sec = 3600 +``` + +Change `command` (and the `PATH` prefix) to the venv that has `clikernel`, `rustygate`, and this package installed. Grok does not put that venv on PATH when it spawns the server, and `clikernel-mcp` looks up `rustygate` by name. Set `PYTHONPATH` to this repo so the kernel process can import `aai_coding` and the pyskills entry points resolve. Drop `GITHUB_TOKEN` if they do not want the token in the kernel. `grok mcp add` also writes this shape; prefer editing `config.toml` so the merge is visible. + Check: deferred to step 7, where a kernel round trip must work. Settle first: whether a server named `clikernel` already exists. @@ -61,6 +74,8 @@ Outcome, Claude Code, in `~/.claude/settings.json` under `hooks`: PreToolUse mat Outcome, kernel-centric codex, in `~/.codex/hooks.json`: PostCompact, SessionStart with matcher `compact`, and PreToolUse with matcher `mcp__clikernel__execute` each run `/bin/aai-hook codex-orientation`; UserPromptSubmit runs `/bin/aai-hook codex-prompt-submit`. Hybrid codex does not install `codex-orientation`, since it does not run the dojo; it may still install `codex-prompt-submit`. codex asks the user to trust hooks on the first start after any `hooks.json` change; tell them to expect that prompt. +Outcome, hybrid Grok Build, a new file `~/.grok/hooks/aai.json` (Grok merges every `*.json` in that directory; do not overwrite other hook files). UserPromptSubmit runs `/bin/aai-hook grok-prompt-submit`. Do not install `claude-air`, `claude-slop`, `claude-drop-sentinel`, `claude-session-start`, `claude-block-native-edit`, or `codex-orientation`: Grok cannot inject their stdout, and hybrid Grok does not run the dojo. `grok-prompt-submit` emits the same `hookSpecificOutput` JSON as `codex-prompt-submit` so a future Grok that honors Claude-shaped injection will pick it up; today's Grok treats UserPromptSubmit as passive, so the notices also live in `prompts/core.md` (step 5). Tell them to run `/hooks-trust` only if they later add project-scoped hooks; user-scope `~/.grok/hooks/` is already trusted. + Check: `aai-hook claude-prompt-submit` fed `{"prompt": "test?"}` on stdin prints the question notice. Settle first: every hook the user already has; theirs stay alongside these unless they collide. @@ -77,7 +92,7 @@ Check: the file still parses as JSON after editing. ## 5. Skills, safecmd, and prompts -Outcome: symlinks from `~/.claude/skills/persistent-python` and `~/.claude/skills/pyskills` to `/skills/`. Kernel-centric codex gets the same two skill symlinks. Hybrid codex instead gets `~/.codex/skills/clikernel` pointing to `/skills/clikernel` and `~/.codex/skills/notebook-dialog-editing` pointing to `/skills/notebook-dialog-editing`; the latter teaches Codex to inspect and edit notebooks and aidialog dialogs safely through the shell CLIs without using a kernel. Remove the other mode's codex skill symlinks when switching, since they intentionally prescribe conflicting tool-use policies. Also link `~/.claude/skills/safecmd` to `/plugins/safecmd` and `~/.codex/AGENTS.md` to `/prompts/core.md`. +Outcome: symlinks from `~/.claude/skills/persistent-python` and `~/.claude/skills/pyskills` to `/skills/`. Kernel-centric codex gets the same two skill symlinks. Hybrid codex instead gets `~/.codex/skills/clikernel` pointing to `/skills/clikernel` and `~/.codex/skills/notebook-dialog-editing` pointing to `/skills/notebook-dialog-editing`; the latter teaches Codex to inspect and edit notebooks and aidialog dialogs safely through the shell CLIs without using a kernel. Hybrid Grok Build gets the same two skill directories under `~/.grok/skills/`. Remove the other mode's skill symlinks when switching, since they intentionally prescribe conflicting tool-use policies. Also link `~/.claude/skills/safecmd` to `/plugins/safecmd` and `~/.codex/AGENTS.md` to `/prompts/core.md`. Grok Build already has a personal `~/.grok/AGENTS.md` on most machines: do not replace it. Symlink `~/.grok/AGENT.md` to `/prompts/core.md` instead; Grok loads every recognized rule filename in `~/.grok/`, and `AGENT.md` is a distinct name from `AGENTS.md` even on a case-insensitive disk. safecmd auto-approves allowlisted Bash commands. The `safecmd` package is a workspace member, so it is already installed; its allowlist lives at `~/.config/safecmd/config.ini` and the defaults are fine to start. @@ -85,18 +100,22 @@ Optional, Claude Code: the user might like `/prompts/core.md` appende Optional, codex: the analogue of the full behavioral prompt is `model_instructions_file = "/prompts/codex-sysp.md"` (absolute path) in `~/.codex/config.toml`, replacing codex's built-in instructions entirely; `~/.codex/AGENTS.md` (and so `core.md`) still loads on top, and no symlink is involved since the key points straight into the checkout. Explain the trade to the user before wiring it: the file is the team's edited reconstruction of the built-in instructions, so upstream changes to codex's own prompt stop arriving until the file is revised. +Optional, Grok Build: `grok --rules` (alias `--append-system-prompt`) appends text for one session; `--system-prompt-override` replaces the default prompt entirely. There is no team `grok-sysp.md`. Prefer the `~/.grok/AGENT.md` symlink. Do not ship a reconstructed Grok system prompt: Grok's default prompt is the product, and replacing it drops tool-use guidance the hybrid setup still needs. + Settle first: existing real directories where the symlinks go. ## 6. Optional comforts -The user might find it useful to hear a quiet tone when the harness finishes or asks a question: Notification hooks running `afplay /System/Library/Sounds/Submarine.aiff` on matcher `permission_prompt` and `Pop.aiff` on `idle_prompt`. If they keep shell functions they want available inside harness command shells, `env.BASH_ENV` in settings.json (and `[shell_environment_policy.set]` in codex's config.toml) pointing at their aliases file does that. +The user might find it useful to hear a quiet tone when the harness finishes or asks a question: Notification hooks running `afplay /System/Library/Sounds/Submarine.aiff` on matcher `permission_prompt` and `Pop.aiff` on `idle_prompt`. If they keep shell functions they want available inside harness command shells, `env.BASH_ENV` in settings.json (and `[shell_environment_policy.set]` in codex's config.toml) pointing at their aliases file does that. Grok Build has no equivalent shell-env key; functions they need inside `run_terminal_command` belong in their login shell or in the MCP `env` map. ## 7. Restart and verify wiring -Both harnesses read configuration at startup: ask the user to restart each, accepting codex's hook trust prompt when hooks changed. Then verify a kernel round trip by running `1+1` through clikernel. In the hybrid codex mode, the reply should contain just the result rather than the startup text. +All three harnesses read configuration at startup: ask the user to restart each, accepting codex's hook trust prompt when hooks changed. Then verify a kernel round trip by running `1+1` through clikernel. In hybrid codex or Grok Build, the reply should contain just the result rather than the startup text. If `py` fails with connection refused, the venv is missing `ipykernel`/`ipymini`; install them into that same venv. On Grok, also run `grok inspect` and confirm `clikernel-workflow` and `notebook-dialog-editing` appear under Skills, `~/.grok/AGENT.md` under global rules, the `clikernel` MCP server under MCP, and `aai.json` under Hooks. ## 8. Acceptance In a fresh Claude Code or kernel-centric codex session in any workspace Python project: the bootstrap notice fires; invoking `persistent-python` then running `dojo_start()` completes a clean round; `list_pyskills()` shows the `aai_coding.*` rows; `doc(aai_coding.coding_patterns)` renders. -In a fresh hybrid codex session: `clikernel-workflow` and `notebook-dialog-editing` appear in the available skills; ordinary local text edits use `apply_patch`; notebook and aidialog work can use the shell CLIs without starting a kernel; shell work uses Bash; and clikernel retains Python state across two execution calls. Inside clikernel, `list_pyskills()` shows the `aai_coding.*` rows and `doc(aai_coding.coding_patterns)` renders. When a check fails, fix that step's wiring before moving on, and tell the user what was wrong. +In a fresh hybrid codex session: `clikernel-workflow` and `notebook-dialog-editing` appear in the available skills; ordinary local text edits use `apply_patch`; notebook and aidialog work can use the shell CLIs without starting a kernel; shell work uses Bash; and clikernel retains Python state across two execution calls. Inside clikernel, `list_pyskills()` shows the `aai_coding.*` rows and `doc(aai_coding.coding_patterns)` renders. + +In a fresh hybrid Grok Build session: `clikernel-workflow` and `notebook-dialog-editing` appear in the available skills; ordinary local text edits use `search_replace`; notebook and aidialog work can use the shell CLIs without starting a kernel; shell work uses `run_terminal_command`; and clikernel retains Python state across two execution calls. Inside clikernel, `list_pyskills()` shows the `aai_coding.*` rows and `doc(aai_coding.coding_patterns)` renders. Native file tools stay allowed. When a check fails, fix that step's wiring before moving on, and tell the user what was wrong. diff --git a/aai_coding/harness.py b/aai_coding/harness.py index 4faadda..7e86cec 100644 --- a/aai_coding/harness.py +++ b/aai_coding/harness.py @@ -1,4 +1,4 @@ -"""Hook implementations for the team harness, installed as the `aai-hook` CLI. Each subcommand is registered in a harness's hook config (Claude Code settings.json or codex hooks.json) and reads the hook event's JSON payload from stdin. See SETUP.md for the wiring.""" +"""Hook implementations for the team harness, installed as the `aai-hook` CLI. Each subcommand is registered in a harness's hook config (Claude Code settings.json, codex hooks.json, or Grok Build ~/.grok/hooks) and reads the hook event's JSON payload from stdin. See SETUP.md for the wiring.""" import json, os, re, sys from datetime import datetime from pathlib import Path @@ -38,6 +38,19 @@ def bash_guard_msg(cmd): 'or cancelling that task unless the user explicitly says so.') +def prompt_text(o): + "Prompt string from a Claude, codex, or Grok hook payload" + if p := o.get('prompt'): return p + ti = o.get('tool_input') or o.get('toolInput') or {} + return ti.get('prompt') or o.get('userPrompt') or '' + + +def tool_command(o): + "Shell command from a Claude (`tool_input`) or Grok (`toolInput`) PreToolUse payload" + ti = o.get('tool_input') or o.get('toolInput') or {} + return ti.get('command') or '' + + def prompt_notices(prompt, q_notice=Q_NOTICE): "Notices a submitted prompt earns: question-mark answer-first, read-in-full, bare-approval scope, and BTW side-request" out = [] @@ -99,7 +112,7 @@ def claude_session_start(o): def _prompt_submit(o, q_notice): - ns = prompt_notices(o.get('prompt') or '', q_notice) + ns = prompt_notices(prompt_text(o), q_notice) if ns: print(json.dumps(dict(hookSpecificOutput=dict( hookEventName='UserPromptSubmit', additionalContext='\n'.join(ns))))) @@ -114,9 +127,14 @@ def codex_prompt_submit(o): _prompt_submit(o, Q_NOTICE_CODEX) +def grok_prompt_submit(o): + "Grok Build UserPromptSubmit: same notices as hybrid codex (no Claude display-bug warning)" + _prompt_submit(o, Q_NOTICE_CODEX) + + def claude_bash_guard(o): "PreToolUse(Bash): reject output-truncating pipes" - if m := bash_guard_msg(o.get('tool_input', {}).get('command') or ''): + if m := bash_guard_msg(tool_command(o)): print(m, file=sys.stderr) sys.exit(2) diff --git a/aai_coding/harness_docs.py b/aai_coding/harness_docs.py index 8fd2f6b..d9a6e3c 100644 --- a/aai_coding/harness_docs.py +++ b/aai_coding/harness_docs.py @@ -1,20 +1,23 @@ -r'''Official docs for the tool you are running in (Claude Code or codex): read before answering questions about its behaviour, config, or features. +r'''Official docs for the tool you are running in (Claude Code, codex, or Grok Build): read before answering questions about its behaviour, config, or features. # Harness Docs 1. Work in `clikernel`, with the `exhash` pyskill loaded. Open the docs index in one step, never displaying the raw text: `toc = open_doc(llms_txt())`. 2. Find the page with `toc.links('')`: a few `[n] Title: description` rows are the whole display. Pick the row whose title matches the question. For codex the index is the whole OpenAI developers hub (the codex-scoped llms.txt their docs link to is a 404), so narrow to Codex first: `toc.search('^## Codex')` lists one `Codex — ` row per page with its address token, then `toc.at('').links('')` gives that page's followable row. A bare `toc.links('')` over the hub matches other OpenAI products too. + For Grok Build the published llms.txt is a concatenated dump of the xAI docs hub, not a page index. Narrow immediately: `toc.search('Grok Build')` or `toc.links('build')`. Never display the whole file. The CLI also ships the user guide at `~/.grok/docs/user-guide/`; `grok inspect` and `grok --help` are the local contracts. 3. Open it with `page = toc.open(n)`: fetched, parsed, and `base`-recorded in one step. No URL is copied at any point. Never fetch `llms-full.txt`. Pick the right page from the index instead. 4. Display `page` bare and read the listing: every row ends with the section's size, so the whole-or-sections decision reads straight off it. At most 30,000 characters in total: display the whole `page.text`. A short page read whole gives an overview sections cannot. Larger: do not display it. `page.search('')` shows `token title (count) [size] preview` rows; retrieve matching sections with `at('')`, or `paths(2)` when structure itself is the question. Never display an arbitrary slice. The choice is the whole page or selected sections, nothing between. Fetch shared sections separately when an event-specific section refers to them. -5. Use the official documentation for documented interfaces and normal day-to-day behavior. Some questions the docs do not answer. For codex, dive into its source, which is open. For Claude Code, which ships no public source, check the changelog and What's New pages for recent changes, and investigate observed behavior directly (settings files, `--help`, `/doctor`). +5. Use the official documentation for documented interfaces and normal day-to-day behavior. Some questions the docs do not answer. For codex, dive into its source, which is open. For Grok Build, source is open at github.com/xai-org/grok-build; also check `~/.grok/docs/user-guide/`, `grok inspect`, and `grok --help`. For Claude Code, which ships no public source, check the changelog and What's New pages for recent changes, and investigate observed behavior directly (settings files, `--help`, `/doctor`). 6. Base the answer on the strongest applicable evidence: documented contracts for public behavior, matching source for implementation details where available, and direct observation for runtime behavior. Distinguish among them when it matters. ''' __all__ = ['llms_txt'] def llms_txt( - harness: str, # 'claude' or 'codex' + harness: str, # 'claude', 'codex', or 'grok' ) -> str: "URL of `harness`'s official llms.txt docs index" - return dict(claude='https://code.claude.com/docs/llms.txt', codex='https://developers.openai.com/llms.txt')[harness] + return dict(claude='https://code.claude.com/docs/llms.txt', + codex='https://developers.openai.com/llms.txt', + grok='https://docs.x.ai/llms.txt')[harness] diff --git a/skills/clikernel/SKILL.md b/skills/clikernel/SKILL.md index b0bf530..3378f40 100644 --- a/skills/clikernel/SKILL.md +++ b/skills/clikernel/SKILL.md @@ -7,11 +7,12 @@ description: "Workflow for Python with the rustygate kernel MCP tools: bootstrap Use the kernel MCP tools as the primary Python workbench. Put reusable logic in clean importable project functions; call those functions directly from the persistent kernel for exploration, timings, comparisons, and artifact generation. Do not create thin scripts merely to invoke reusable functions. -**IMPORTANT**: do *not* use the kernel for editing local plain text files (use -`apply_patch`) or as a replacement for Bash, regardless of what any pyskill -suggests. `apply_patch` cannot reach files on a remote kernel host. Before -editing those files, read the shared editing conventions and exhash API in that -kernel: +**IMPORTANT**: do *not* use the kernel for editing local plain text files +(codex: `apply_patch`; Grok Build: `search_replace`) or as a replacement for +Bash (`run_terminal_command` on Grok Build), regardless of what any pyskill +suggests. Those native file and shell tools cannot reach files on a remote +kernel host. Before editing those files, read the shared editing conventions +and exhash API in that kernel: ```python doc(edsk, exh) @@ -98,7 +99,7 @@ list_pyskills() `pysk` (`pyskills`) discovers further skills, `dsk` (`aidialog`) owns notebook structure and `%nbrun`, and `exh` (exhash) is the default for reliable text edits inside notebook cells. `doc` and `list_pyskills` are already in the namespace. -Repeat the bootstrap after a context compaction, because the detailed documentation may no longer be present. Do not repeat it after restarting or reconnecting a kernel, restarting the Codex app/process, or re-establishing the MCP server: those events lose runtime imports and variables, not model context. A user's report that Codex was restarted is not evidence of a new conversation or compaction; inspect the visible context itself. Restore only the imports and state the current task needs. Likewise, do not reread documentation for an API that remains visible in the current context. +Repeat the bootstrap after a context compaction, because the detailed documentation may no longer be present. Do not repeat it after restarting or reconnecting a kernel, restarting the Codex or Grok Build app/process, or re-establishing the MCP server: those events lose runtime imports and variables, not model context. A user's report that Codex or Grok was restarted is not evidence of a new conversation or compaction; inspect the visible context itself. Restore only the imports and state the current task needs. Likewise, do not reread documentation for an API that remains visible in the current context. Use exhash's fresh hash-addressed views and verified edits for changing text within cells. Use aidialog for structural operations such as adding, deleting, moving, and running notebook cells. diff --git a/tests/test_harness.py b/tests/test_harness.py index 0bb7434..de7ae92 100644 --- a/tests/test_harness.py +++ b/tests/test_harness.py @@ -4,7 +4,7 @@ import pytest from shutil import which -from aai_coding.harness import bash_guard_msg, claude_air, claude_drop_sentinel, claude_slop, claude_session_start, codex_orientation, prompt_notices, synthetic_resume +from aai_coding.harness import bash_guard_msg, claude_air, claude_bash_guard, claude_drop_sentinel, claude_slop, claude_session_start, codex_orientation, prompt_notices, prompt_text, synthetic_resume, tool_command def test_bash_guard(): @@ -34,20 +34,40 @@ def kinds(p): return [n.split()[2] for n in prompt_notices(p)] # third word di assert kinds('the btw case is prefix-only') == [] +def test_prompt_text_and_tool_command(): + assert prompt_text(dict(prompt='hi')) == 'hi' + assert prompt_text(dict(toolInput=dict(prompt='from grok'))) == 'from grok' + assert prompt_text(dict(userPrompt='legacy')) == 'legacy' + assert prompt_text({}) == '' + assert tool_command(dict(tool_input=dict(command='ls'))) == 'ls' + assert tool_command(dict(toolInput=dict(command='pwd'))) == 'pwd' + assert tool_command({}) == '' + + def test_prompt_submit(capsys): - from aai_coding.harness import claude_prompt_submit, codex_prompt_submit + from aai_coding.harness import claude_prompt_submit, codex_prompt_submit, grok_prompt_submit prompt = "BTW is the '# also activates the Message.to_parts/ai_output patches' still correct for llmsurgery?" - for f,has_bug in ((claude_prompt_submit,True), (codex_prompt_submit,False)): + for f,has_bug in ((claude_prompt_submit,True), (codex_prompt_submit,False), (grok_prompt_submit,False)): f(dict(prompt=prompt)) out = json.loads(capsys.readouterr().out) ctx = out['hookSpecificOutput']['additionalContext'] assert out['hookSpecificOutput']['hookEventName'] == 'UserPromptSubmit' assert 'question' in ctx and 'side request' in ctx assert ('Claude Code bug' in ctx) is has_bug + grok_prompt_submit(dict(toolInput=dict(prompt='is it done?'))) + ctx = json.loads(capsys.readouterr().out)['hookSpecificOutput']['additionalContext'] + assert 'question' in ctx and 'Claude Code bug' not in ctx codex_prompt_submit(dict(prompt='all good')) assert capsys.readouterr().out == '' +def test_bash_guard_grok_payload(capsys): + claude_bash_guard(dict(toolInput=dict(command='ls'))) + with pytest.raises(SystemExit) as e: claude_bash_guard(dict(toolInput=dict(command='pytest -q | tail -5'))) + assert e.value.code == 2 + assert 'truncat' in capsys.readouterr().err.lower() + + def test_synthetic_resume(tmp_path): t = tmp_path/'t.jsonl' boundary = json.dumps(dict(type='system', subtype='compact_boundary')) diff --git a/tests/test_harness_docs.py b/tests/test_harness_docs.py new file mode 100644 index 0000000..275dc88 --- /dev/null +++ b/tests/test_harness_docs.py @@ -0,0 +1,10 @@ +import pytest + +from aai_coding.harness_docs import llms_txt + + +def test_llms_txt(): + assert llms_txt('claude').endswith('/llms.txt') + assert 'openai.com' in llms_txt('codex') + assert llms_txt('grok') == 'https://docs.x.ai/llms.txt' + with pytest.raises(KeyError): llms_txt('unknown') From 8b257e551d618d0ef28925c1083280245ed25b97 Mon Sep 17 00:00:00 2001 From: Carlo Lepelaars Date: Wed, 2 Sep 2026 17:59:39 +0200 Subject: [PATCH 2/5] Simplify Grok build section for harness and setup --- README.md | 4 ++-- SETUP.md | 39 ++++++++++---------------------------- aai_coding/harness.py | 22 ++------------------- tests/test_harness.py | 26 +++---------------------- tests/test_harness_docs.py | 10 ---------- 5 files changed, 17 insertions(+), 84 deletions(-) delete mode 100644 tests/test_harness_docs.py diff --git a/README.md b/README.md index 056fffc..3dfce71 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,10 @@ # aai-coding -The Answer.AI coding harness: the shared configuration, skills, and tooling that make a Claude Code, codex, or Grok Build session work the way our team works. If you are an LLM reading this, you are probably either setting the harness up (follow `SETUP.md`, but read this file first) or working inside it and wanting to understand why it is shaped this way, so you can give your user informed advice. +The Answer.AI coding harness: the shared configuration, skills, and tooling that make a Claude Code, codex or Grok Build session work the way our team works. If you are an LLM reading this, you are probably either setting the harness up (follow `SETUP.md`, but read this file first) or working inside it and wanting to understand why it is shaped this way, so you can give your user informed advice. ## The system in one paragraph -The Claude Code setup and one codex setup are kernel-centric: native file tools are denied, and file reading, editing, searching, and Python execution go through one persistent IPython kernel (clikernel) loaded with curated tooling discovered via `pyskills`. Codex and Grok Build can instead use a hybrid setup: native file and shell tools stay (codex `apply_patch` and Bash; Grok `search_replace` and `run_terminal_command`), with a quiet clikernel reserved for Python-specific work. Grok Build is hybrid-only: it has no kernel-centric mode, because its hooks cannot inject session or prompt context the way Claude and kernel-centric codex do. The kernel-centric setup uses two host-level bootstrap skills, `persistent-python` and `pyskills`; the hybrid setup uses `clikernel-workflow` to define the boundary and `notebook-dialog-editing` for safe kernel-free notebook and aidialog work. Everything else that would conventionally be a host skill is a pyskill in this package: skill text lives in module docstrings, read with `doc()`, listed by `list_pyskills()`, and versioned, released, and installed like any other Python code. +The Claude Code setup and one codex setup are kernel-centric: native file tools are denied, and file reading, editing, searching, and Python execution go through one persistent IPython kernel (clikernel) loaded with curated tooling discovered via `pyskills`. Codex and Grok Build can instead use a hybrid setup: native file and shell tools stay (codex `apply_patch` and Bash; Grok `search_replace` and `run_terminal_command`), with a quiet clikernel reserved for Python-specific work. Grok Build is hybrid-only. The kernel-centric setup uses two host-level bootstrap skills, `persistent-python` and `pyskills`; the hybrid setup uses `clikernel-workflow` to define the boundary and `notebook-dialog-editing` for safe kernel-free notebook and aidialog work. Everything else that would conventionally be a host skill is a pyskill in this package: skill text lives in module docstrings, read with `doc()`, listed by `list_pyskills()`, and versioned, released, and installed like any other Python code. ## What is in here diff --git a/SETUP.md b/SETUP.md index 8776854..03daf45 100644 --- a/SETUP.md +++ b/SETUP.md @@ -1,19 +1,19 @@ # Setting up the Answer.AI harness -This file is a runbook for an LLM session, not a script. If you are a person: open Claude Code, codex, or Grok Build, `cd` anywhere in the aai-ws workspace, and say "follow aai-coding/SETUP.md". If you are the session: first read `README.md` in this repo in full, since the steps below change your user's configuration and the README's design context is what lets you merge, recommend, and answer questions in an informed way. Then work through the steps in order. Each step states an outcome to reach, a check, and what to settle with the user first. Make no change beyond the stated outcomes without asking. Where the user's existing configuration overlaps, merge and never replace: show them each conflict and agree a resolution. +This file is a runbook for an LLM session, not a script. If you are a person: open Claude Code or codex, `cd` anywhere in the aai-ws workspace, and say "follow aai-coding/SETUP.md". If you are the session: first read `README.md` in this repo in full, since the steps below change your user's configuration and the README's design context is what lets you merge, recommend, and answer questions in an informed way. Then work through the steps in order. Each step states an outcome to reach, a check, and what to settle with the user first. Make no change beyond the stated outcomes without asking. Where the user's existing configuration overlaps, merge and never replace: show them each conflict and agree a resolution. -Assumptions: macOS, and at least one harness (Claude Code, codex, or Grok Build) installed and signed in. Claude Code and codex assume the aai-ws uv workspace is cloned and synced (this repo is a member, so its `aai-hook` CLI and pyskills are already installed). Grok Build is hybrid-only and can run from a standalone editable install of this repo plus `clikernel` in one venv; use that venv's `aai-hook` and `clikernel-mcp` when aai-ws is absent. Ask which harnesses to set up before starting, and use absolute paths for this repo and the venv throughout. +Assumptions: macOS, the aai-ws uv workspace cloned and synced (this repo is a member, so its `aai-hook` CLI and pyskills are already installed), and at least one harness (Claude Code, codex, or Grok Build) installed and signed in. Ask which harnesses to set up before starting, and use absolute paths for this repo and the workspace venv throughout. Codex has two supported modes. This choice applies only to codex; Claude Code remains kernel-centric; Grok Build is hybrid-only. Settle which codex mode the user wants before changing its configuration: 1. **Kernel-centric:** do file, shell, and Python work through clikernel, complete the llmdojo bootstrap, and discover tools through pyskills. This is the existing Answer.AI harness workflow and most closely matches the Claude Code setup. -2. **Hybrid:** use the harness's native file and shell tools normally (codex: `apply_patch` and Bash; Grok Build: `search_replace` and `run_terminal_command`), and use `clikernel-mcp --quiet` only for Python-specific work. This keeps persistent Python state and pyskills without replacing the native file and shell workflow. Grok Build has no kernel-centric mode: its `UserPromptSubmit` and `SessionStart` hooks are passive (stdout is ignored), and it has no `MessageDisplay` or `PostToolBatch` events, so the dojo bootstrap notices, air nudge, slopometer, and drop-sentinel cannot inject context. +2. **Hybrid:** use the native file and shell tools normally (codex: `apply_patch` and Bash; Grok Build: `search_replace` and `run_terminal_command`), and use `clikernel-mcp --quiet` only for Python-specific work. This keeps persistent Python state and pyskills without replacing the native file and shell workflow. ## 1. Kernel server Outcome: the clikernel MCP server is registered. Claude Code: a user-scope server named `clikernel` running `/bin/clikernel-mcp`. Kernel-centric codex: a `[mcp_servers.clikernel]` block in `~/.codex/config.toml` with `command` set to that binary, `startup_timeout_sec = 30`, `tool_timeout_sec = 3600`, and `approval_mode = "approve"` for its `execute`, `connect`, `restart`, and `interrupt` tools. -Hybrid codex: use the following exact working configuration, changing the `command` path if the workspace is elsewhere: +Hybrid (codex or Grok Build): the following block in `~/.codex/config.toml` or `~/.grok/config.toml`, changing the `command` path if the workspace is elsewhere. Codex keeps `env_vars`, `omit_tools_from`, and the per-tool `approval_mode` keys. Grok has none of those; its optional token line is `env = { GITHUB_TOKEN = "${GITHUB_TOKEN}" }`. ```toml [mcp_servers.clikernel] @@ -41,20 +41,7 @@ approval_mode = "approve" approval_mode = "approve" ``` -`--quiet` keeps automatic startup output out of ordinary execution replies. Optional, ask the user: `env_vars = ["GITHUB_TOKEN"]` passes their GitHub token into the kernel so sessions can act for them on GitHub (via `ghapi`); remove that line if they do not want it. - -Hybrid Grok Build: a `[mcp_servers.clikernel]` block in `~/.grok/config.toml`. Grok has no per-tool `approval_mode` and uses `env` rather than `env_vars`. Merge; do not replace other `[mcp_servers.*]` blocks: - -```toml -[mcp_servers.clikernel] -command = "/Users/jhoward/aai-ws/.venv/bin/clikernel-mcp" -args = ["--quiet"] -env = { PATH = "/Users/jhoward/aai-ws/.venv/bin:/usr/bin:/bin:/usr/sbin:/sbin", PYTHONPATH = "/Users/jhoward/aai-ws/aai-coding", GITHUB_TOKEN = "${GITHUB_TOKEN}" } -startup_timeout_sec = 30 -tool_timeout_sec = 3600 -``` - -Change `command` (and the `PATH` prefix) to the venv that has `clikernel`, `rustygate`, and this package installed. Grok does not put that venv on PATH when it spawns the server, and `clikernel-mcp` looks up `rustygate` by name. Set `PYTHONPATH` to this repo so the kernel process can import `aai_coding` and the pyskills entry points resolve. Drop `GITHUB_TOKEN` if they do not want the token in the kernel. `grok mcp add` also writes this shape; prefer editing `config.toml` so the merge is visible. +`--quiet` keeps automatic startup output out of ordinary execution replies. Optional, ask the user: `env_vars` (Grok: `env`) passes their GitHub token into the kernel so sessions can act for them on GitHub (via `ghapi`); remove that line if they do not want it. Check: deferred to step 7, where a kernel round trip must work. @@ -72,9 +59,7 @@ Settle first: existing non-symlink files at those paths. Outcome, Claude Code, in `~/.claude/settings.json` under `hooks`: PreToolUse matcher `Write|Edit|NotebookEdit` runs `aai-hook claude-block-native-edit`; PreToolUse matcher `Bash` runs `aai-hook claude-bash-guard`; UserPromptSubmit runs `aai-hook claude-prompt-submit`; SessionStart runs `aai-hook claude-session-start`; UserPromptSubmit, MessageDisplay, and PostToolBatch each also run `aai-hook claude-air` (the come-up-for-air nudge: after 8 tool-call rounds with no text response of 100+ chars, it injects a reminder to surface and reassess, repeating every 5 further rounds). The air nudge is Claude-only: codex has no message-level hook event, so it cannot observe the "text happened" reset condition - the codex-shaped substitute is a sentence in AGENTS.md; revisit if codex grows one. PostToolBatch and Stop also each run `aai-hook claude-drop-sentinel`, a Python port of podlayer/message-drop-sentinel (MIT): it detects the thinking-sandwich message-drop platform bug from the transcript scar (two adjacent thinking blocks) and tells the agent its text was probably eaten: restate it in the turn-final message, or say it now and end the turn if the user needs it immediately. Retire the sentinel entries when the upstream bug is fixed (re-test recipe and issue links in that repo's README). UserPromptSubmit and MessageDisplay also each run `aai-hook claude-slop`: MessageDisplay buffers each displayed assistant message, and at the next prompt the hook scores the previous turn's final message with the `slopometer` CLI, injecting the flagged patterns as context. A prompt that is a bare `;` means the user did not understand the previous reply, and the hook injects an instruction to restate it in plain English. Bare `aai-hook` resolves because the user's shell profile puts the workspace venv on PATH; if it does not, use the absolute venv path. -Outcome, kernel-centric codex, in `~/.codex/hooks.json`: PostCompact, SessionStart with matcher `compact`, and PreToolUse with matcher `mcp__clikernel__execute` each run `/bin/aai-hook codex-orientation`; UserPromptSubmit runs `/bin/aai-hook codex-prompt-submit`. Hybrid codex does not install `codex-orientation`, since it does not run the dojo; it may still install `codex-prompt-submit`. codex asks the user to trust hooks on the first start after any `hooks.json` change; tell them to expect that prompt. - -Outcome, hybrid Grok Build, a new file `~/.grok/hooks/aai.json` (Grok merges every `*.json` in that directory; do not overwrite other hook files). UserPromptSubmit runs `/bin/aai-hook grok-prompt-submit`. Do not install `claude-air`, `claude-slop`, `claude-drop-sentinel`, `claude-session-start`, `claude-block-native-edit`, or `codex-orientation`: Grok cannot inject their stdout, and hybrid Grok does not run the dojo. `grok-prompt-submit` emits the same `hookSpecificOutput` JSON as `codex-prompt-submit` so a future Grok that honors Claude-shaped injection will pick it up; today's Grok treats UserPromptSubmit as passive, so the notices also live in `prompts/core.md` (step 5). Tell them to run `/hooks-trust` only if they later add project-scoped hooks; user-scope `~/.grok/hooks/` is already trusted. +Outcome, kernel-centric codex, in `~/.codex/hooks.json`: PostCompact, SessionStart with matcher `compact`, and PreToolUse with matcher `mcp__clikernel__execute` each run `/bin/aai-hook codex-orientation`; UserPromptSubmit runs `/bin/aai-hook codex-prompt-submit`. Hybrid (codex or Grok Build) does not install `codex-orientation`, since it does not run the dojo; it may still install `codex-prompt-submit` in `~/.codex/hooks.json` or `~/.grok/hooks/aai.json`. Codex asks the user to trust hooks on the first start after any `hooks.json` change; tell them to expect that prompt. Grok user-scope hooks are already trusted. Check: `aai-hook claude-prompt-submit` fed `{"prompt": "test?"}` on stdin prints the question notice. @@ -92,7 +77,7 @@ Check: the file still parses as JSON after editing. ## 5. Skills, safecmd, and prompts -Outcome: symlinks from `~/.claude/skills/persistent-python` and `~/.claude/skills/pyskills` to `/skills/`. Kernel-centric codex gets the same two skill symlinks. Hybrid codex instead gets `~/.codex/skills/clikernel` pointing to `/skills/clikernel` and `~/.codex/skills/notebook-dialog-editing` pointing to `/skills/notebook-dialog-editing`; the latter teaches Codex to inspect and edit notebooks and aidialog dialogs safely through the shell CLIs without using a kernel. Hybrid Grok Build gets the same two skill directories under `~/.grok/skills/`. Remove the other mode's skill symlinks when switching, since they intentionally prescribe conflicting tool-use policies. Also link `~/.claude/skills/safecmd` to `/plugins/safecmd` and `~/.codex/AGENTS.md` to `/prompts/core.md`. Grok Build already has a personal `~/.grok/AGENTS.md` on most machines: do not replace it. Symlink `~/.grok/AGENT.md` to `/prompts/core.md` instead; Grok loads every recognized rule filename in `~/.grok/`, and `AGENT.md` is a distinct name from `AGENTS.md` even on a case-insensitive disk. +Outcome: symlinks from `~/.claude/skills/persistent-python` and `~/.claude/skills/pyskills` to `/skills/`. Kernel-centric codex gets the same two skill symlinks. Hybrid (codex or Grok Build) instead gets `clikernel` and `notebook-dialog-editing` under `~/.codex/skills/` or `~/.grok/skills/`, pointing at `/skills/`; the latter teaches the harness to inspect and edit notebooks and aidialog dialogs safely through the shell CLIs without using a kernel. Remove the other mode's skill symlinks when switching, since they intentionally prescribe conflicting tool-use policies. Also link `~/.claude/skills/safecmd` to `/plugins/safecmd` and `~/.codex/AGENTS.md` (Grok: `~/.grok/AGENT.md`, so an existing `~/.grok/AGENTS.md` stays personal) to `/prompts/core.md`. safecmd auto-approves allowlisted Bash commands. The `safecmd` package is a workspace member, so it is already installed; its allowlist lives at `~/.config/safecmd/config.ini` and the defaults are fine to start. @@ -100,22 +85,18 @@ Optional, Claude Code: the user might like `/prompts/core.md` appende Optional, codex: the analogue of the full behavioral prompt is `model_instructions_file = "/prompts/codex-sysp.md"` (absolute path) in `~/.codex/config.toml`, replacing codex's built-in instructions entirely; `~/.codex/AGENTS.md` (and so `core.md`) still loads on top, and no symlink is involved since the key points straight into the checkout. Explain the trade to the user before wiring it: the file is the team's edited reconstruction of the built-in instructions, so upstream changes to codex's own prompt stop arriving until the file is revised. -Optional, Grok Build: `grok --rules` (alias `--append-system-prompt`) appends text for one session; `--system-prompt-override` replaces the default prompt entirely. There is no team `grok-sysp.md`. Prefer the `~/.grok/AGENT.md` symlink. Do not ship a reconstructed Grok system prompt: Grok's default prompt is the product, and replacing it drops tool-use guidance the hybrid setup still needs. - Settle first: existing real directories where the symlinks go. ## 6. Optional comforts -The user might find it useful to hear a quiet tone when the harness finishes or asks a question: Notification hooks running `afplay /System/Library/Sounds/Submarine.aiff` on matcher `permission_prompt` and `Pop.aiff` on `idle_prompt`. If they keep shell functions they want available inside harness command shells, `env.BASH_ENV` in settings.json (and `[shell_environment_policy.set]` in codex's config.toml) pointing at their aliases file does that. Grok Build has no equivalent shell-env key; functions they need inside `run_terminal_command` belong in their login shell or in the MCP `env` map. +The user might find it useful to hear a quiet tone when the harness finishes or asks a question: Notification hooks running `afplay /System/Library/Sounds/Submarine.aiff` on matcher `permission_prompt` and `Pop.aiff` on `idle_prompt`. If they keep shell functions they want available inside harness command shells, `env.BASH_ENV` in settings.json (and `[shell_environment_policy.set]` in codex's config.toml) pointing at their aliases file does that. ## 7. Restart and verify wiring -All three harnesses read configuration at startup: ask the user to restart each, accepting codex's hook trust prompt when hooks changed. Then verify a kernel round trip by running `1+1` through clikernel. In hybrid codex or Grok Build, the reply should contain just the result rather than the startup text. If `py` fails with connection refused, the venv is missing `ipykernel`/`ipymini`; install them into that same venv. On Grok, also run `grok inspect` and confirm `clikernel-workflow` and `notebook-dialog-editing` appear under Skills, `~/.grok/AGENT.md` under global rules, the `clikernel` MCP server under MCP, and `aai.json` under Hooks. +All three harnesses read configuration at startup: ask the user to restart each, accepting codex's hook trust prompt when hooks changed. Then verify a kernel round trip by running `1+1` through clikernel. In hybrid mode, the reply should contain just the result rather than the startup text. ## 8. Acceptance In a fresh Claude Code or kernel-centric codex session in any workspace Python project: the bootstrap notice fires; invoking `persistent-python` then running `dojo_start()` completes a clean round; `list_pyskills()` shows the `aai_coding.*` rows; `doc(aai_coding.coding_patterns)` renders. -In a fresh hybrid codex session: `clikernel-workflow` and `notebook-dialog-editing` appear in the available skills; ordinary local text edits use `apply_patch`; notebook and aidialog work can use the shell CLIs without starting a kernel; shell work uses Bash; and clikernel retains Python state across two execution calls. Inside clikernel, `list_pyskills()` shows the `aai_coding.*` rows and `doc(aai_coding.coding_patterns)` renders. - -In a fresh hybrid Grok Build session: `clikernel-workflow` and `notebook-dialog-editing` appear in the available skills; ordinary local text edits use `search_replace`; notebook and aidialog work can use the shell CLIs without starting a kernel; shell work uses `run_terminal_command`; and clikernel retains Python state across two execution calls. Inside clikernel, `list_pyskills()` shows the `aai_coding.*` rows and `doc(aai_coding.coding_patterns)` renders. Native file tools stay allowed. When a check fails, fix that step's wiring before moving on, and tell the user what was wrong. +In a fresh hybrid session (codex or Grok Build): `clikernel-workflow` and `notebook-dialog-editing` appear in the available skills; ordinary local text edits use `apply_patch` (Grok: `search_replace`); notebook and aidialog work can use the shell CLIs without starting a kernel; shell work uses Bash (Grok: `run_terminal_command`); and clikernel retains Python state across two execution calls. Inside clikernel, `list_pyskills()` shows the `aai_coding.*` rows and `doc(aai_coding.coding_patterns)` renders. When a check fails, fix that step's wiring before moving on, and tell the user what was wrong. diff --git a/aai_coding/harness.py b/aai_coding/harness.py index 7e86cec..950956e 100644 --- a/aai_coding/harness.py +++ b/aai_coding/harness.py @@ -38,19 +38,6 @@ def bash_guard_msg(cmd): 'or cancelling that task unless the user explicitly says so.') -def prompt_text(o): - "Prompt string from a Claude, codex, or Grok hook payload" - if p := o.get('prompt'): return p - ti = o.get('tool_input') or o.get('toolInput') or {} - return ti.get('prompt') or o.get('userPrompt') or '' - - -def tool_command(o): - "Shell command from a Claude (`tool_input`) or Grok (`toolInput`) PreToolUse payload" - ti = o.get('tool_input') or o.get('toolInput') or {} - return ti.get('command') or '' - - def prompt_notices(prompt, q_notice=Q_NOTICE): "Notices a submitted prompt earns: question-mark answer-first, read-in-full, bare-approval scope, and BTW side-request" out = [] @@ -112,7 +99,7 @@ def claude_session_start(o): def _prompt_submit(o, q_notice): - ns = prompt_notices(prompt_text(o), q_notice) + ns = prompt_notices(o.get('prompt') or '', q_notice) if ns: print(json.dumps(dict(hookSpecificOutput=dict( hookEventName='UserPromptSubmit', additionalContext='\n'.join(ns))))) @@ -127,14 +114,9 @@ def codex_prompt_submit(o): _prompt_submit(o, Q_NOTICE_CODEX) -def grok_prompt_submit(o): - "Grok Build UserPromptSubmit: same notices as hybrid codex (no Claude display-bug warning)" - _prompt_submit(o, Q_NOTICE_CODEX) - - def claude_bash_guard(o): "PreToolUse(Bash): reject output-truncating pipes" - if m := bash_guard_msg(tool_command(o)): + if m := bash_guard_msg(o.get('tool_input', {}).get('command') or ''): print(m, file=sys.stderr) sys.exit(2) diff --git a/tests/test_harness.py b/tests/test_harness.py index de7ae92..0bb7434 100644 --- a/tests/test_harness.py +++ b/tests/test_harness.py @@ -4,7 +4,7 @@ import pytest from shutil import which -from aai_coding.harness import bash_guard_msg, claude_air, claude_bash_guard, claude_drop_sentinel, claude_slop, claude_session_start, codex_orientation, prompt_notices, prompt_text, synthetic_resume, tool_command +from aai_coding.harness import bash_guard_msg, claude_air, claude_drop_sentinel, claude_slop, claude_session_start, codex_orientation, prompt_notices, synthetic_resume def test_bash_guard(): @@ -34,40 +34,20 @@ def kinds(p): return [n.split()[2] for n in prompt_notices(p)] # third word di assert kinds('the btw case is prefix-only') == [] -def test_prompt_text_and_tool_command(): - assert prompt_text(dict(prompt='hi')) == 'hi' - assert prompt_text(dict(toolInput=dict(prompt='from grok'))) == 'from grok' - assert prompt_text(dict(userPrompt='legacy')) == 'legacy' - assert prompt_text({}) == '' - assert tool_command(dict(tool_input=dict(command='ls'))) == 'ls' - assert tool_command(dict(toolInput=dict(command='pwd'))) == 'pwd' - assert tool_command({}) == '' - - def test_prompt_submit(capsys): - from aai_coding.harness import claude_prompt_submit, codex_prompt_submit, grok_prompt_submit + from aai_coding.harness import claude_prompt_submit, codex_prompt_submit prompt = "BTW is the '# also activates the Message.to_parts/ai_output patches' still correct for llmsurgery?" - for f,has_bug in ((claude_prompt_submit,True), (codex_prompt_submit,False), (grok_prompt_submit,False)): + for f,has_bug in ((claude_prompt_submit,True), (codex_prompt_submit,False)): f(dict(prompt=prompt)) out = json.loads(capsys.readouterr().out) ctx = out['hookSpecificOutput']['additionalContext'] assert out['hookSpecificOutput']['hookEventName'] == 'UserPromptSubmit' assert 'question' in ctx and 'side request' in ctx assert ('Claude Code bug' in ctx) is has_bug - grok_prompt_submit(dict(toolInput=dict(prompt='is it done?'))) - ctx = json.loads(capsys.readouterr().out)['hookSpecificOutput']['additionalContext'] - assert 'question' in ctx and 'Claude Code bug' not in ctx codex_prompt_submit(dict(prompt='all good')) assert capsys.readouterr().out == '' -def test_bash_guard_grok_payload(capsys): - claude_bash_guard(dict(toolInput=dict(command='ls'))) - with pytest.raises(SystemExit) as e: claude_bash_guard(dict(toolInput=dict(command='pytest -q | tail -5'))) - assert e.value.code == 2 - assert 'truncat' in capsys.readouterr().err.lower() - - def test_synthetic_resume(tmp_path): t = tmp_path/'t.jsonl' boundary = json.dumps(dict(type='system', subtype='compact_boundary')) diff --git a/tests/test_harness_docs.py b/tests/test_harness_docs.py deleted file mode 100644 index 275dc88..0000000 --- a/tests/test_harness_docs.py +++ /dev/null @@ -1,10 +0,0 @@ -import pytest - -from aai_coding.harness_docs import llms_txt - - -def test_llms_txt(): - assert llms_txt('claude').endswith('/llms.txt') - assert 'openai.com' in llms_txt('codex') - assert llms_txt('grok') == 'https://docs.x.ai/llms.txt' - with pytest.raises(KeyError): llms_txt('unknown') From 16a1d021e800df2ba1a6a79b8ad103b586de8ad4 Mon Sep 17 00:00:00 2001 From: Carlo Lepelaars Date: Wed, 2 Sep 2026 18:11:23 +0200 Subject: [PATCH 3/5] Simplify prose for Grok Build addition --- README.md | 2 +- SETUP.md | 6 +++--- aai_coding/harness_docs.py | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 3dfce71..a8c2f24 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ The Claude Code setup and one codex setup are kernel-centric: native file tools - `aai_coding/` - the pyskills. `coding_patterns` (style, testing judgment, and team policy; part of the kernel startup doc round), `write_prose` (anti-slop rules for narrative prose), `write_docs` (the voiceless register for docstrings, READMEs, and PRs), `harness_docs` (how to find official harness docs via llms.txt), and `harness` (not a skill: the `aai-hook` CLI that implements both harnesses' hooks). - `skills/` - the harness-level SKILL.md sources, symlinked into `~/.claude/skills`, `~/.codex/skills`, or `~/.grok/skills`. `persistent-python` and `pyskills` bootstrap the kernel-centric setup; `clikernel` and `notebook-dialog-editing` support the hybrid codex and Grok Build setups. - `plugins/safecmd/` - a Claude Code plugin that auto-approves allowlisted Bash commands via the `safecmd` package, so the deny-heavy permission setup stays livable. -- `prompts/` - shared prompt text. `core.md` holds harness-neutral behavioral rules: codex reads it natively via a `~/.codex/AGENTS.md` symlink, Grok Build via a `~/.grok/AGENT.md` symlink (so an existing `~/.grok/AGENTS.md` stays personal), and Claude Code can append it. `sysp.md` is a full replacement for Claude Code's default system prompt, tuned against the default's consultant and action biases; install it as a `~/.claude/sysp` symlink and launch with `claude --system-prompt-file ~/.claude/sysp --append-system-prompt-file /prompts/core.md` (replacement drops the default prompt's prose but tool schemas survive; the dynamic environment block and scratchpad path are the known losses). +- `prompts/` - shared prompt text. `core.md` holds harness-neutral behavioral rules: codex reads it natively via a `~/.codex/AGENTS.md` symlink, Grok Build via a `~/.grok/AGENTS.md` symlink, and Claude Code can append it. `sysp.md` is a full replacement for Claude Code's default system prompt, tuned against the default's consultant and action biases; install it as a `~/.claude/sysp` symlink and launch with `claude --system-prompt-file ~/.claude/sysp --append-system-prompt-file /prompts/core.md` (replacement drops the default prompt's prose but tool schemas survive; the dynamic environment block and scratchpad path are the known losses). - `SETUP.md` - the setup runbook, written as a prompt for an LLM session rather than an installer script. ## Design decisions, and why diff --git a/SETUP.md b/SETUP.md index 03daf45..8a50c95 100644 --- a/SETUP.md +++ b/SETUP.md @@ -13,7 +13,7 @@ Codex has two supported modes. This choice applies only to codex; Claude Code re Outcome: the clikernel MCP server is registered. Claude Code: a user-scope server named `clikernel` running `/bin/clikernel-mcp`. Kernel-centric codex: a `[mcp_servers.clikernel]` block in `~/.codex/config.toml` with `command` set to that binary, `startup_timeout_sec = 30`, `tool_timeout_sec = 3600`, and `approval_mode = "approve"` for its `execute`, `connect`, `restart`, and `interrupt` tools. -Hybrid (codex or Grok Build): the following block in `~/.codex/config.toml` or `~/.grok/config.toml`, changing the `command` path if the workspace is elsewhere. Codex keeps `env_vars`, `omit_tools_from`, and the per-tool `approval_mode` keys. Grok has none of those; its optional token line is `env = { GITHUB_TOKEN = "${GITHUB_TOKEN}" }`. +Hybrid (codex or Grok Build): use the following exact working configuration in `~/.codex/config.toml` or `~/.grok/config.toml`, changing the `command` path if the workspace is elsewhere: ```toml [mcp_servers.clikernel] @@ -41,7 +41,7 @@ approval_mode = "approve" approval_mode = "approve" ``` -`--quiet` keeps automatic startup output out of ordinary execution replies. Optional, ask the user: `env_vars` (Grok: `env`) passes their GitHub token into the kernel so sessions can act for them on GitHub (via `ghapi`); remove that line if they do not want it. +`--quiet` keeps automatic startup output out of ordinary execution replies. Optional, ask the user: `env_vars = ["GITHUB_TOKEN"]` passes their GitHub token into the kernel so sessions can act for them on GitHub (via `ghapi`); remove that line if they do not want it. Check: deferred to step 7, where a kernel round trip must work. @@ -77,7 +77,7 @@ Check: the file still parses as JSON after editing. ## 5. Skills, safecmd, and prompts -Outcome: symlinks from `~/.claude/skills/persistent-python` and `~/.claude/skills/pyskills` to `/skills/`. Kernel-centric codex gets the same two skill symlinks. Hybrid (codex or Grok Build) instead gets `clikernel` and `notebook-dialog-editing` under `~/.codex/skills/` or `~/.grok/skills/`, pointing at `/skills/`; the latter teaches the harness to inspect and edit notebooks and aidialog dialogs safely through the shell CLIs without using a kernel. Remove the other mode's skill symlinks when switching, since they intentionally prescribe conflicting tool-use policies. Also link `~/.claude/skills/safecmd` to `/plugins/safecmd` and `~/.codex/AGENTS.md` (Grok: `~/.grok/AGENT.md`, so an existing `~/.grok/AGENTS.md` stays personal) to `/prompts/core.md`. +Outcome: symlinks from `~/.claude/skills/persistent-python` and `~/.claude/skills/pyskills` to `/skills/`. Kernel-centric codex gets the same two skill symlinks. Hybrid (codex or Grok Build) instead gets `clikernel` and `notebook-dialog-editing` under `~/.codex/skills/` or `~/.grok/skills/`, pointing at `/skills/`; the latter teaches the harness to inspect and edit notebooks and aidialog dialogs safely through the shell CLIs without using a kernel. Remove the other mode's skill symlinks when switching, since they intentionally prescribe conflicting tool-use policies. Also link `~/.claude/skills/safecmd` to `/plugins/safecmd` and `~/.codex/AGENTS.md` or `~/.grok/AGENTS.md` to `/prompts/core.md`. safecmd auto-approves allowlisted Bash commands. The `safecmd` package is a workspace member, so it is already installed; its allowlist lives at `~/.config/safecmd/config.ini` and the defaults are fine to start. diff --git a/aai_coding/harness_docs.py b/aai_coding/harness_docs.py index d9a6e3c..244f53e 100644 --- a/aai_coding/harness_docs.py +++ b/aai_coding/harness_docs.py @@ -8,7 +8,7 @@ For Grok Build the published llms.txt is a concatenated dump of the xAI docs hub, not a page index. Narrow immediately: `toc.search('Grok Build')` or `toc.links('build')`. Never display the whole file. The CLI also ships the user guide at `~/.grok/docs/user-guide/`; `grok inspect` and `grok --help` are the local contracts. 3. Open it with `page = toc.open(n)`: fetched, parsed, and `base`-recorded in one step. No URL is copied at any point. Never fetch `llms-full.txt`. Pick the right page from the index instead. 4. Display `page` bare and read the listing: every row ends with the section's size, so the whole-or-sections decision reads straight off it. At most 30,000 characters in total: display the whole `page.text`. A short page read whole gives an overview sections cannot. Larger: do not display it. `page.search('')` shows `token title (count) [size] preview` rows; retrieve matching sections with `at('')`, or `paths(2)` when structure itself is the question. Never display an arbitrary slice. The choice is the whole page or selected sections, nothing between. Fetch shared sections separately when an event-specific section refers to them. -5. Use the official documentation for documented interfaces and normal day-to-day behavior. Some questions the docs do not answer. For codex, dive into its source, which is open. For Grok Build, source is open at github.com/xai-org/grok-build; also check `~/.grok/docs/user-guide/`, `grok inspect`, and `grok --help`. For Claude Code, which ships no public source, check the changelog and What's New pages for recent changes, and investigate observed behavior directly (settings files, `--help`, `/doctor`). +5. Use the official documentation for documented interfaces and normal day-to-day behavior. Some questions the docs do not answer. For codex or Grok Build, dive into its source, which is open. For Claude Code, which ships no public source, check the changelog and What's New pages for recent changes, and investigate observed behavior directly (settings files, `--help`, `/doctor`). 6. Base the answer on the strongest applicable evidence: documented contracts for public behavior, matching source for implementation details where available, and direct observation for runtime behavior. Distinguish among them when it matters. ''' From 0bb642600f27bdf1d9f47bc63275bd28fd990c39 Mon Sep 17 00:00:00 2001 From: Carlo Lepelaars Date: Wed, 2 Sep 2026 18:13:34 +0200 Subject: [PATCH 4/5] Simplify SETUP.md --- SETUP.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SETUP.md b/SETUP.md index 8a50c95..423235d 100644 --- a/SETUP.md +++ b/SETUP.md @@ -59,7 +59,7 @@ Settle first: existing non-symlink files at those paths. Outcome, Claude Code, in `~/.claude/settings.json` under `hooks`: PreToolUse matcher `Write|Edit|NotebookEdit` runs `aai-hook claude-block-native-edit`; PreToolUse matcher `Bash` runs `aai-hook claude-bash-guard`; UserPromptSubmit runs `aai-hook claude-prompt-submit`; SessionStart runs `aai-hook claude-session-start`; UserPromptSubmit, MessageDisplay, and PostToolBatch each also run `aai-hook claude-air` (the come-up-for-air nudge: after 8 tool-call rounds with no text response of 100+ chars, it injects a reminder to surface and reassess, repeating every 5 further rounds). The air nudge is Claude-only: codex has no message-level hook event, so it cannot observe the "text happened" reset condition - the codex-shaped substitute is a sentence in AGENTS.md; revisit if codex grows one. PostToolBatch and Stop also each run `aai-hook claude-drop-sentinel`, a Python port of podlayer/message-drop-sentinel (MIT): it detects the thinking-sandwich message-drop platform bug from the transcript scar (two adjacent thinking blocks) and tells the agent its text was probably eaten: restate it in the turn-final message, or say it now and end the turn if the user needs it immediately. Retire the sentinel entries when the upstream bug is fixed (re-test recipe and issue links in that repo's README). UserPromptSubmit and MessageDisplay also each run `aai-hook claude-slop`: MessageDisplay buffers each displayed assistant message, and at the next prompt the hook scores the previous turn's final message with the `slopometer` CLI, injecting the flagged patterns as context. A prompt that is a bare `;` means the user did not understand the previous reply, and the hook injects an instruction to restate it in plain English. Bare `aai-hook` resolves because the user's shell profile puts the workspace venv on PATH; if it does not, use the absolute venv path. -Outcome, kernel-centric codex, in `~/.codex/hooks.json`: PostCompact, SessionStart with matcher `compact`, and PreToolUse with matcher `mcp__clikernel__execute` each run `/bin/aai-hook codex-orientation`; UserPromptSubmit runs `/bin/aai-hook codex-prompt-submit`. Hybrid (codex or Grok Build) does not install `codex-orientation`, since it does not run the dojo; it may still install `codex-prompt-submit` in `~/.codex/hooks.json` or `~/.grok/hooks/aai.json`. Codex asks the user to trust hooks on the first start after any `hooks.json` change; tell them to expect that prompt. Grok user-scope hooks are already trusted. +Outcome, kernel-centric codex, in `~/.codex/hooks.json`: PostCompact, SessionStart with matcher `compact`, and PreToolUse with matcher `mcp__clikernel__execute` each run `/bin/aai-hook codex-orientation`; UserPromptSubmit runs `/bin/aai-hook codex-prompt-submit`. Hybrid (codex or Grok Build) does not install `codex-orientation`, since it does not run the dojo; it may still install `codex-prompt-submit` in `~/.codex/hooks.json` or `~/.grok/hooks/aai.json`. codex asks the user to trust hooks on the first start after any `hooks.json` change; tell them to expect that prompt. Check: `aai-hook claude-prompt-submit` fed `{"prompt": "test?"}` on stdin prints the question notice. From 0d2a5a82de0026652c0eabc64cc892e98f106312 Mon Sep 17 00:00:00 2001 From: Carlo Lepelaars Date: Wed, 2 Sep 2026 18:41:05 +0200 Subject: [PATCH 5/5] Fix Grok Build hooks issue --- SETUP.md | 2 +- aai_coding/harness.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/SETUP.md b/SETUP.md index 423235d..dc21b56 100644 --- a/SETUP.md +++ b/SETUP.md @@ -59,7 +59,7 @@ Settle first: existing non-symlink files at those paths. Outcome, Claude Code, in `~/.claude/settings.json` under `hooks`: PreToolUse matcher `Write|Edit|NotebookEdit` runs `aai-hook claude-block-native-edit`; PreToolUse matcher `Bash` runs `aai-hook claude-bash-guard`; UserPromptSubmit runs `aai-hook claude-prompt-submit`; SessionStart runs `aai-hook claude-session-start`; UserPromptSubmit, MessageDisplay, and PostToolBatch each also run `aai-hook claude-air` (the come-up-for-air nudge: after 8 tool-call rounds with no text response of 100+ chars, it injects a reminder to surface and reassess, repeating every 5 further rounds). The air nudge is Claude-only: codex has no message-level hook event, so it cannot observe the "text happened" reset condition - the codex-shaped substitute is a sentence in AGENTS.md; revisit if codex grows one. PostToolBatch and Stop also each run `aai-hook claude-drop-sentinel`, a Python port of podlayer/message-drop-sentinel (MIT): it detects the thinking-sandwich message-drop platform bug from the transcript scar (two adjacent thinking blocks) and tells the agent its text was probably eaten: restate it in the turn-final message, or say it now and end the turn if the user needs it immediately. Retire the sentinel entries when the upstream bug is fixed (re-test recipe and issue links in that repo's README). UserPromptSubmit and MessageDisplay also each run `aai-hook claude-slop`: MessageDisplay buffers each displayed assistant message, and at the next prompt the hook scores the previous turn's final message with the `slopometer` CLI, injecting the flagged patterns as context. A prompt that is a bare `;` means the user did not understand the previous reply, and the hook injects an instruction to restate it in plain English. Bare `aai-hook` resolves because the user's shell profile puts the workspace venv on PATH; if it does not, use the absolute venv path. -Outcome, kernel-centric codex, in `~/.codex/hooks.json`: PostCompact, SessionStart with matcher `compact`, and PreToolUse with matcher `mcp__clikernel__execute` each run `/bin/aai-hook codex-orientation`; UserPromptSubmit runs `/bin/aai-hook codex-prompt-submit`. Hybrid (codex or Grok Build) does not install `codex-orientation`, since it does not run the dojo; it may still install `codex-prompt-submit` in `~/.codex/hooks.json` or `~/.grok/hooks/aai.json`. codex asks the user to trust hooks on the first start after any `hooks.json` change; tell them to expect that prompt. +Outcome, kernel-centric codex, in `~/.codex/hooks.json`: PostCompact, SessionStart with matcher `compact`, and PreToolUse with matcher `mcp__clikernel__execute` each run `/bin/aai-hook codex-orientation`; UserPromptSubmit runs `/bin/aai-hook codex-prompt-submit`. Hybrid codex does not install `codex-orientation`, since it does not run the dojo; it may still install `codex-prompt-submit`. codex asks the user to trust hooks on the first start after any `hooks.json` change; tell them to expect that prompt. Check: `aai-hook claude-prompt-submit` fed `{"prompt": "test?"}` on stdin prints the question notice. diff --git a/aai_coding/harness.py b/aai_coding/harness.py index 950956e..5cf949c 100644 --- a/aai_coding/harness.py +++ b/aai_coding/harness.py @@ -1,4 +1,4 @@ -"""Hook implementations for the team harness, installed as the `aai-hook` CLI. Each subcommand is registered in a harness's hook config (Claude Code settings.json, codex hooks.json, or Grok Build ~/.grok/hooks) and reads the hook event's JSON payload from stdin. See SETUP.md for the wiring.""" +"""Hook implementations for the team harness, installed as the `aai-hook` CLI. Each subcommand is registered in a harness's hook config (Claude Code settings.json, codex and Grok Build hooks.json) and reads the hook event's JSON payload from stdin. See SETUP.md for the wiring.""" import json, os, re, sys from datetime import datetime from pathlib import Path