Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 6 additions & 4 deletions DEV.md

Large diffs are not rendered by default.

14 changes: 10 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ LLM coding agents imitate what their context shows far more reliably than what i
$ pip install llmdojo
```

Activation is two lines of clikernel user config: register the rules in `$XDG_CONFIG_HOME/clikernel/inspectors.py` (`from llmdojo.rules import make_inspector, RuleBlock; inspectors = [make_inspector()]`), and have your startup file print the bootstrap instructions that tell the agent to run the dojo.
Activation is two lines of clikernel user config: register the rules in `$XDG_CONFIG_HOME/clikernel/inspectors.py` (`from llmdojo.rules import make_inspector, RuleBlock; inspectors = [make_inspector()]`), and have your startup file print the bootstrap instructions that tell the agent which docs to read. For ipyai, link `ipyai/startup.py` into `~/.config/ipyai/` (see `ipyai/README.md`): its kernels then carry the same imports and the rules, applied to the model’s cells only, and `ipyaidojo` launches sessions with the bootstrap and the round already in history.

### Start a session

Expand All @@ -38,17 +38,23 @@ $ claudedojo -r
$ codexdojo -r
```

ipyai starts warm the same way. `ipyaidojo` writes the round as an ipyai session of the current directory and runs `ipyai -r` on it (standing arguments from `ipyai_args` in `$XDG_CONFIG_HOME/ipyaidojo/config.toml`, `--sid` prints the id). Its kernels get the round’s imports from `~/.config/ipyai/startup.py`, see `ipyai/README.md`:

``` sh
$ ipyaidojo
```

### Update the templates

The templates ship inside the package - the canonical dialog plus the compiled per-host stores - so *using* fresh templates takes no action beyond updating llmdojo. Maintaining them is one command: when tooling docs or skills change, the baked round’s outputs go stale, and `dojobuild` regenerates them with no model spend - it replays the round’s cells through a fresh kernel, splices the current outputs into the canonical template dialog, and recompiles both stores, all in your checkout for review:
The templates ship inside the package - the packaged dialogs plus the compiled per-host stores - so *using* fresh templates takes no action beyond updating llmdojo. A template is two prompts: a bootstrap (the host’s startup doc reads, captured by playing the bootstrap prompt in that host: `clik_boot.ipynb` for Claude and Codex, `ipyai_boot.ipynb` for ipyai) and the shared round (`dojo_round.ipynb`, captured once from Claude). Maintaining them is one command: when tooling docs or skills change, the baked outputs go stale, and `dojobuild` regenerates them with no model spend - it replays the packaged clikernel dialogs’ cells through a fresh kernel, splices the current outputs in, and recompiles every store from each host’s bootstrap plus the round, all in your checkout for review (the ipyai store replays its own bootstrap and the round through ipyai’s `py`, so `dojobuild` needs ipyai installed and a running rustygate):

``` sh
$ dojobuild
```

Only a change to the round itself - its cells, not their outputs - needs a fresh capture: `claudedojo --capture` plays a scripted round headlessly, `codexdojo --capture` does the same in a Codex child, and `--current` on either stores a clean round an existing session already played. After reviewing a captured dialog, `dojobuild --claude` and `dojobuild --codex` compile it into each store without re-replaying.
Only a change to the round itself - its cells, not their outputs - needs a fresh capture: `claudedojo --capture` plays the two prompts headlessly, `codexdojo --capture` does the same in a Codex child, and `--current` on either stores a clean round an existing session already played; each writes `boot.ipynb` and `round.ipynb` beside its store for review, to copy over the packaged pair. A change to a host’s bootstrap reads is a new bootstrap capture for that host (`ipyaidojo --capture` after playing its prompt in ipyai). After reviewing, `dojobuild --claude`, `--codex`, and `--ipyai` compile one store each without re-replaying the clikernel dialogs.

The bootstrap `doc()` reads (the cells before `dojo_start()`) live in two artifacts: `dojo_data/capture_prompt.md`, the script future captures replay, and the baked round in the canonical dialog itself. To change the set, update both, keeping each new `doc(x)` as its own cell placed after the `doc(clik, pysk, edsk)` one, then run `dojobuild`, which replays the new cell for a true output and recomputes the stored `doced` list. Updating only one leaves the demonstration and the capture script teaching different bootstraps.
The bootstrap reads are named in two places that must agree: the host’s startup text (`claude/startup.txt`, or `ipyaidojo.BOOT_PROMPT` for ipyai, which has no banner) and `dojo_data/capture_prompt.md`, the script future captures replay. To change the set, update both, then recapture the bootstrap.

### State and templates

Expand Down
22 changes: 22 additions & 0 deletions ipyai/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# ipyai integration

The file here wires ipyai's kernels to the llmdojo regime, the way `claude/` does for clikernel. It lives in this repo because it must change in lockstep with the code: the imports and aliases it sets up are the ones the template's `doc()` calls use.

## The file

`startup.py` runs inside every kernel ipyai owns (ipyai executes `~/.config/ipyai/startup.py` at seeding, with `__file__` bound). It imports the standard tooling modules and defines the short aliases the template's `doc()` calls use (`pysk`, `edsk`, `dsk`, `exh`, `rgsk`, `acp`). ipyai's model-facing tool is `py`, not clikernel's `execute`, so there is no `clik` alias here and no printed banner: ipyai's bootstrap prompt names the reads itself, and a session launched by `ipyaidojo` already has the bootstrap and the worked round in its history.

It also installs the session rules (`llmdojo.rules.make_inspector`), applied to the model's cells only: ipyai runs the model's `py` cells with `store_history=False` and the user's typed cells with `True`, so IPython's `pre_run_cell` info tells them apart, and the user's own cells are never inspected. A rule's note prints ahead of the cell's output and so reaches the model inside the tool result; a blocking rule rejects the cell. Doc-state is keyed to the session file: ipyai names the session stem in the kernel's `LLMDOJO_HOST_ID`, and `ipyaidojo` seeds the baked template's doc-state under that key, so a warm session's docs count as read.

## The bootstrap

ipyai's bootstrap is its own dialog, `dojo_data/ipyai_boot.ipynb`: one prompt (`ipyaidojo.BOOT_PROMPT`) whose reply is the doc reads through `py`. It is captured live: in a Python project, start a plain `ipyai`, send that prompt, and once the model has answered run `ipyaidojo --capture` (or `capture_boot()`) from the same directory. The capture takes the reply from the session file, replays its cells for full results (the session file stores tool results truncated), and writes `boot.ipynb` beside the store; copy it over `dojo_data/ipyai_boot.ipynb` after review. The round is the shared `dojo_round.ipynb`.

## Setup

From the repo root:

mkdir -p ~/.config/ipyai
ln -sf "$PWD/ipyai/startup.py" ~/.config/ipyai/

Then `ipyaidojo` starts ipyai with the bootstrap and the round in history, and `dojobuild` rebuilds the ipyai store (`dojo_data/ipyai_store/`) beside the Claude and Codex ones.
32 changes: 32 additions & 0 deletions ipyai/startup.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
from fastcore.utils import *
from pyskills import list_pyskills, doc, xdir
from fastcore.editskill import *
from aidialog.dlgskill import *
from exhash.skill import *
from rgapi.skill import *
from llmdojo.dojo import *
from ipykernel_helper import info_md
import pyskills.skill as pysk, fastcore.editskill as edsk, aidialog.dlgskill as dsk, exhash.skill as exh, rgapi.skill as rgsk, aai_coding.coding_patterns as acp

# The session rules, applied to the model's cells only. ipyai runs the model's `py` cells with store_history=False
# and the user's typed cells with True, so IPython's pre_run_cell info tells them apart; the user's own cells are
# never inspected. A note prints ahead of the cell's output (so it reaches the model inside the tool result);
# a RuleBlock rejects the cell; any other inspector error prints and the cell runs anyway.
from IPython import get_ipython
from IPython.core.error import InputRejected
from llmdojo.rules import make_inspector

class _AIInspect:
def __init__(self, f): self.f, self.src, self.ai = f, '', False
def stash(self, info): self.src, self.ai = info.raw_cell, not info.store_history
def visit(self, tree):
if not self.ai: return tree
try: note = self.f(tree, self.src)
except InputRejected: raise
except Exception as e: note = f'inspector error (cell runs anyway): {e!r}'
if note: print(note, end='')
return tree

_ai_inspect = _AIInspect(make_inspector())
get_ipython().events.register('pre_run_cell', _ai_inspect.stash)
get_ipython().ast_transformers.append(_ai_inspect)
1 change: 1 addition & 0 deletions llmdojo/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

Modules:

- `llmdojo.ipyaidojo`: Start ipyai with a worked tooling session already in its history
- `llmdojo.tmpl`: The template layer shared by claudedojo and codexdojo"""

__version__ = "0.0.4"
25 changes: 23 additions & 2 deletions llmdojo/_modidx.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,14 @@
'git_url': 'https://github.com/AnswerDotAI/llmdojo',
'lib_path': 'llmdojo'},
'syms': { 'llmdojo.claudedojo': { 'llmdojo.claudedojo._blocks': ('claudedojo.html#_blocks', 'llmdojo/claudedojo.py'),
'llmdojo.claudedojo._boots': ('claudedojo.html#_boots', 'llmdojo/claudedojo.py'),
'llmdojo.claudedojo._cells': ('claudedojo.html#_cells', 'llmdojo/claudedojo.py'),
'llmdojo.claudedojo._dealt': ('claudedojo.html#_dealt', 'llmdojo/claudedojo.py'),
'llmdojo.claudedojo._is_err': ('claudedojo.html#_is_err', 'llmdojo/claudedojo.py'),
'llmdojo.claudedojo._is_prompt': ('claudedojo.html#_is_prompt', 'llmdojo/claudedojo.py'),
'llmdojo.claudedojo._load_reg': ('claudedojo.html#_load_reg', 'llmdojo/claudedojo.py'),
'llmdojo.claudedojo._starts': ('claudedojo.html#_starts', 'llmdojo/claudedojo.py'),
'llmdojo.claudedojo._tcells': ('claudedojo.html#_tcells', 'llmdojo/claudedojo.py'),
'llmdojo.claudedojo._vis': ('claudedojo.html#_vis', 'llmdojo/claudedojo.py'),
'llmdojo.claudedojo.append_dojo': ('claudedojo.html#append_dojo', 'llmdojo/claudedojo.py'),
'llmdojo.claudedojo.build_template': ('claudedojo.html#build_template', 'llmdojo/claudedojo.py'),
Expand All @@ -27,7 +30,8 @@
'llmdojo.claudedojo.prep_dojo': ('claudedojo.html#prep_dojo', 'llmdojo/claudedojo.py'),
'llmdojo.claudedojo.save_template': ('claudedojo.html#save_template', 'llmdojo/claudedojo.py'),
'llmdojo.claudedojo.strip_dojo': ('claudedojo.html#strip_dojo', 'llmdojo/claudedojo.py')},
'llmdojo.codexdojo': { 'llmdojo.codexdojo._capture_span': ('codexdojo.html#_capture_span', 'llmdojo/codexdojo.py'),
'llmdojo.codexdojo': { 'llmdojo.codexdojo._boots': ('codexdojo.html#_boots', 'llmdojo/codexdojo.py'),
'llmdojo.codexdojo._capture_span': ('codexdojo.html#_capture_span', 'llmdojo/codexdojo.py'),
'llmdojo.codexdojo._cell': ('codexdojo.html#_cell', 'llmdojo/codexdojo.py'),
'llmdojo.codexdojo._dealt': ('codexdojo.html#_dealt', 'llmdojo/codexdojo.py'),
'llmdojo.codexdojo._is_prompt': ('codexdojo.html#_is_prompt', 'llmdojo/codexdojo.py'),
Expand Down Expand Up @@ -60,19 +64,36 @@
'llmdojo.dojo_data.proj.core': {},
'llmdojo.dojo_data.proj.report': {},
'llmdojo.dojo_data.proj.tmpl': {},
'llmdojo.ipyaidojo': { 'llmdojo.ipyaidojo.build_template': ('ipyaidojo.html#build_template', 'llmdojo/ipyaidojo.py'),
'llmdojo.ipyaidojo.capture_boot': ('ipyaidojo.html#capture_boot', 'llmdojo/ipyaidojo.py'),
'llmdojo.ipyaidojo.ipyai_dialog': ('ipyaidojo.html#ipyai_dialog', 'llmdojo/ipyaidojo.py'),
'llmdojo.ipyaidojo.load_template': ('ipyaidojo.html#load_template', 'llmdojo/ipyaidojo.py'),
'llmdojo.ipyaidojo.main': ('ipyaidojo.html#main', 'llmdojo/ipyaidojo.py'),
'llmdojo.ipyaidojo.prep_dojo': ('ipyaidojo.html#prep_dojo', 'llmdojo/ipyaidojo.py'),
'llmdojo.ipyaidojo.replay_cells': ('ipyaidojo.html#replay_cells', 'llmdojo/ipyaidojo.py'),
'llmdojo.ipyaidojo.template_dialog': ('ipyaidojo.html#template_dialog', 'llmdojo/ipyaidojo.py')},
'llmdojo.rules': {},
'llmdojo.tmpl': { 'llmdojo.tmpl._dojo_v': ('tmpl.html#_dojo_v', 'llmdojo/tmpl.py'),
'llmdojo.tmpl._msg_cells': ('tmpl.html#_msg_cells', 'llmdojo/tmpl.py'),
'llmdojo.tmpl._one_prompt': ('tmpl.html#_one_prompt', 'llmdojo/tmpl.py'),
'llmdojo.tmpl._pmsgs': ('tmpl.html#_pmsgs', 'llmdojo/tmpl.py'),
'llmdojo.tmpl._replay_cells': ('tmpl.html#_replay_cells', 'llmdojo/tmpl.py'),
'llmdojo.tmpl._seed_doced': ('tmpl.html#_seed_doced', 'llmdojo/tmpl.py'),
'llmdojo.tmpl._turns': ('tmpl.html#_turns', 'llmdojo/tmpl.py'),
'llmdojo.tmpl.assemble': ('tmpl.html#assemble', 'llmdojo/tmpl.py'),
'llmdojo.tmpl.boot_gates': ('tmpl.html#boot_gates', 'llmdojo/tmpl.py'),
'llmdojo.tmpl.canon_tmpl': ('tmpl.html#canon_tmpl', 'llmdojo/tmpl.py'),
'llmdojo.tmpl.capture_slice': ('tmpl.html#capture_slice', 'llmdojo/tmpl.py'),
'llmdojo.tmpl.doced_names': ('tmpl.html#doced_names', 'llmdojo/tmpl.py'),
'llmdojo.tmpl.find_cid': ('tmpl.html#find_cid', 'llmdojo/tmpl.py'),
'llmdojo.tmpl.launch_config': ('tmpl.html#launch_config', 'llmdojo/tmpl.py'),
'llmdojo.tmpl.load_reg': ('tmpl.html#load_reg', 'llmdojo/tmpl.py'),
'llmdojo.tmpl.load_store': ('tmpl.html#load_store', 'llmdojo/tmpl.py'),
'llmdojo.tmpl.localized_cells': ('tmpl.html#localized_cells', 'llmdojo/tmpl.py'),
'llmdojo.tmpl.main': ('tmpl.html#main', 'llmdojo/tmpl.py'),
'llmdojo.tmpl.refresh_dialog': ('tmpl.html#refresh_dialog', 'llmdojo/tmpl.py'),
'llmdojo.tmpl.refresh_template': ('tmpl.html#refresh_template', 'llmdojo/tmpl.py'),
'llmdojo.tmpl.round_gates': ('tmpl.html#round_gates', 'llmdojo/tmpl.py'),
'llmdojo.tmpl.save_store': ('tmpl.html#save_store', 'llmdojo/tmpl.py')}}}
'llmdojo.tmpl.save_store': ('tmpl.html#save_store', 'llmdojo/tmpl.py'),
'llmdojo.tmpl.split_template': ('tmpl.html#split_template', 'llmdojo/tmpl.py'),
'llmdojo.tmpl.template_cells': ('tmpl.html#template_cells', 'llmdojo/tmpl.py')}}}
Loading