docs: rewrite README + CLAUDE for handoff to a dedicated session
README is now self-contained for a human reader landing on the repo cold: project value-prop, status, quick-orientation reading order, directory layout, build/runtime deps, run + config invocation, and a pointer to CLAUDE.md for contribution norms. CLAUDE.md is rewritten as the substrate a fresh Claude session needs to pick up Phase 0→1 implementation without prior conversation context: - Reading order (PHASE0.md → README → config.lua) - Phase-loop discipline (8+1 with loopbacks) - Eight invariants from PHASE0.md called out as non-negotiable without manifest amendment - Bottom-up implementation order for Phase 0 (libc → readline → curl → context → executor → router → broker → renderer → repl → main) - Testing approach without a test framework - Open question on JSON library (dkjson recommended; needs §3 amendment) - Ambiguity handling pattern (ask vs log-in-§13 vs stop-and-ask) - Commit style + Co-Authored-By trailer template - Model-class caveat: small Q4 coder models have output variance, validate before exec, confirm_cmd defaults exist for this reason - Push credential note for sessions without ssh-keys-on-Gitea Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,37 +1,213 @@
|
||||
# CLAUDE.md — aish project conventions
|
||||
# CLAUDE.md — aish project handoff
|
||||
|
||||
This file is auto-loaded when any Claude session opens a clone of this repo.
|
||||
You are continuing work on **aish**, an AI-augmented conversational shell
|
||||
implemented in LuaJIT. This file is auto-loaded when a Claude session opens
|
||||
a clone of this repo. Read it once at session start.
|
||||
|
||||
## Source of truth
|
||||
---
|
||||
|
||||
[`docs/PHASE0.md`](docs/PHASE0.md) is the locked substrate. Read it before
|
||||
making non-trivial changes. If a change touches §3 (technology decisions),
|
||||
§4 (directory layout), or §6 (broker contract), the change needs to be
|
||||
reflected in PHASE0.md *and* called out in the commit message.
|
||||
## 1. Read these first, in order
|
||||
|
||||
## Phase loop
|
||||
1. **`docs/PHASE0.md`** — locked substrate. Every architectural decision is
|
||||
here. Do not contradict it without amending it.
|
||||
2. **`README.md`** — human-facing project summary and orientation.
|
||||
3. **`config.lua`** — the runtime model registry. Endpoints reflect a
|
||||
specific LAN; the user may have already adapted yours.
|
||||
|
||||
This project follows the 8(+1) phase loop documented in mfritsche's home
|
||||
canon (`feedback_dev_process.md` in claude-noether memory). Each phase has
|
||||
its own document under `docs/`. Don't skip phases.
|
||||
If a question seems open after reading those three, **ask the user a
|
||||
single focused question** rather than guessing. Cost of asking is one
|
||||
turn; cost of building on a wrong assumption is half a phase.
|
||||
|
||||
Loopbacks per the canon: 3→1, 7→4, any→0.
|
||||
---
|
||||
|
||||
## Module structure invariant
|
||||
## 2. Where you are in the phase loop
|
||||
|
||||
The file names listed in `docs/PHASE0.md` §4 are stable across phases. Later
|
||||
phases fill in module bodies; they do not rename files or restructure the
|
||||
tree. If you find yourself wanting to rename or split a module, that's a
|
||||
PHASE0.md amendment first.
|
||||
aish follows an **8(+1) phase loop**:
|
||||
|
||||
## No C extensions
|
||||
```
|
||||
0 substrate → 1 formulate → 2 analyze → 3 baseline → 4 plan
|
||||
→ 5 review → 6 implement → 7 verify → 8 memory-update
|
||||
(+1 reflect)
|
||||
```
|
||||
|
||||
LuaJIT FFI only. If you reach for a C extension to "make this easier",
|
||||
stop. The FFI bindings under `ffi/` are extended in place.
|
||||
Loopbacks:
|
||||
- `3 → 1` if baseline data invalidates the formulation
|
||||
- `7 → 4` if verification fails — fix the plan, not the substrate
|
||||
- `any → 0` if a substrate fact turns out to be wrong
|
||||
|
||||
## Commit style
|
||||
Don't skip phases. Phase 0 is done (the manifest is locked); your starting
|
||||
position depends on what's already in the tree.
|
||||
|
||||
Short imperative subject, file-scoped. Example: `executor: add cd
|
||||
interception via libc chdir`. Body explains the *why* if non-obvious.
|
||||
Check the state by reading `docs/` and `git log --oneline`. If only
|
||||
PHASE0.md exists in `docs/` and every module raises NotImplemented, **you
|
||||
are at Phase 0 → Phase 1 transition**: the substrate is locked, no module
|
||||
is implemented, your job is to write the Phase 0 implementation per the
|
||||
manifest.
|
||||
|
||||
Co-Authored-By trailers on Claude commits per project canon.
|
||||
If you find phase docs `docs/PHASE1.md`, `docs/PHASE2.md` etc., follow
|
||||
their state.
|
||||
|
||||
---
|
||||
|
||||
## 3. Source-of-truth invariants — do not violate without amending PHASE0.md
|
||||
|
||||
These are not stylistic preferences; they are decisions that downstream
|
||||
phases depend on:
|
||||
|
||||
| Invariant | Where it's locked |
|
||||
|---|---|
|
||||
| LuaJIT 2.x only — no PUC-Rio Lua-only constructs | §3 |
|
||||
| FFI only — no compiled C extensions, no `luarocks` packages | §3 |
|
||||
| Module file names in §4 are stable across phases | §4 |
|
||||
| `/v1/chat/completions` is the broker contract | §6 |
|
||||
| `CMD:` (exact prefix, single space) is the command-extraction marker | §6 |
|
||||
| Config is plain Lua loaded with `dofile`, not JSON/YAML | §3, §10 |
|
||||
| `cd` is intercepted via libc `chdir`, not delegated to `popen` | §7 |
|
||||
| Phase 0 has no disk I/O for history (memory only) | §8 |
|
||||
|
||||
If a Phase N implementation needs to break one of these, **amend
|
||||
PHASE0.md in the same commit and call out the change in the commit
|
||||
message**. Don't silently diverge.
|
||||
|
||||
---
|
||||
|
||||
## 4. Implementation order for Phase 0
|
||||
|
||||
Bottom-up beats top-down for this codebase. Suggested ordering:
|
||||
|
||||
1. `ffi/libc.lua` — `chdir`, `strerror` work; verifiable in isolation.
|
||||
2. `ffi/readline.lua` — `readline()`, `add_history()`, `free()` work; test
|
||||
with a tiny REPL stub before wiring `repl.lua`.
|
||||
3. `ffi/curl.lua` — easy interface, blocking POST with response capture
|
||||
into a Lua string. Test against any local llama-server with a curl
|
||||
one-liner side-by-side.
|
||||
4. `context.lua` — pure data structure, trivial to unit-test.
|
||||
5. `executor.lua` — `popen` wrapper, `cd` interception (uses libc.chdir),
|
||||
`CMD:` line extraction.
|
||||
6. `router.lua` — pure function; classify(line, config) → (kind, payload).
|
||||
7. `broker.lua` — uses `ffi/curl.lua` + JSON encode/decode. **You will
|
||||
need a JSON library** — see §6 below.
|
||||
8. `renderer.lua` — output formatting; trivial.
|
||||
9. `repl.lua` — wires everything via the dispatch table.
|
||||
10. `main.lua` — already mostly there; finalize once `repl.run` exists.
|
||||
|
||||
Don't write all ten in one commit. One commit per module, build the
|
||||
chain by passing through; each commit should leave the tree in a state
|
||||
where `luajit main.lua` either runs further than the previous commit or
|
||||
fails with the next NotImplemented.
|
||||
|
||||
---
|
||||
|
||||
## 5. Testing approach
|
||||
|
||||
There is no test framework dependency by design. Testing is per-module
|
||||
ad-hoc with `luajit -e 'local m = require("module"); ...'` from the repo
|
||||
root, or a smoke `luajit main.lua` after each module lands.
|
||||
|
||||
For broker testing without burning model time: any of the local
|
||||
llama-servers in `config.lua` will respond to a hand-crafted POST. Use
|
||||
`curl -sS http://dirac.fritz.box:8081/v1/chat/completions -d '{...}'` to
|
||||
generate a known-good reference, then compare your FFI output.
|
||||
|
||||
---
|
||||
|
||||
## 6. JSON encode/decode — undecided
|
||||
|
||||
The manifest doesn't pick a JSON library. LuaJIT 2.x has no built-in JSON.
|
||||
Options:
|
||||
|
||||
- **`dkjson`** — pure Lua, single file, vendor it under `vendor/dkjson.lua`.
|
||||
Slow but no dependency.
|
||||
- **`cjson`** — fast, but it's a C extension. Violates §3 (no compiled
|
||||
extensions). Skip unless you amend the manifest.
|
||||
- **Hand-rolled minimal encoder** — viable since the broker payload shape is
|
||||
small and well-defined; ~50 LOC.
|
||||
|
||||
Recommend dkjson vendored. Decide and add a note to PHASE0.md §3 in the
|
||||
same commit.
|
||||
|
||||
---
|
||||
|
||||
## 7. When you hit ambiguity
|
||||
|
||||
The user (mfritsche) prefers being asked over being assumed-about. The
|
||||
phrase "Ask readily — prefer quick question over 3+ guessing attempts"
|
||||
applies. Concrete pattern:
|
||||
|
||||
- If the question affects only the current commit: ask.
|
||||
- If it affects a future phase: log it in `docs/PHASE0.md` §13 (Open
|
||||
Questions) with target phase, keep working.
|
||||
- If it affects multiple existing modules: stop, ask before refactoring.
|
||||
|
||||
Don't suggest pausing the session. Don't suggest "let's pick this up
|
||||
tomorrow". Don't pre-emptively defer work the user hasn't asked you to
|
||||
defer. The user controls pace.
|
||||
|
||||
---
|
||||
|
||||
## 8. Commit style
|
||||
|
||||
Imperative subject, file-scoped where possible. Examples:
|
||||
|
||||
- `executor: implement io.popen wrapper with stderr merge`
|
||||
- `ffi/curl: blocking POST with header list and response buffer`
|
||||
- `repl: wire router → executor → broker dispatch`
|
||||
- `phase0 amendment: vendor dkjson under vendor/`
|
||||
|
||||
Body explains the *why* if non-obvious. Reference PHASE0.md sections by
|
||||
number when relevant.
|
||||
|
||||
Co-Authored-By trailer on Claude-authored commits:
|
||||
|
||||
```
|
||||
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
||||
```
|
||||
|
||||
(or whichever model you actually are; check before substituting.)
|
||||
|
||||
---
|
||||
|
||||
## 9. What aish does NOT do — out of scope, all phases
|
||||
|
||||
These are listed in PHASE0.md §12. Briefly:
|
||||
|
||||
- Does not manage llama.cpp lifecycle (assumed externally running)
|
||||
- Does not implement model inference
|
||||
- Is not a multiplexer (no tmux semantics)
|
||||
- Is not a sandbox (no namespaces, no seccomp)
|
||||
|
||||
If you find yourself implementing any of those, stop — that's a different
|
||||
project.
|
||||
|
||||
---
|
||||
|
||||
## 10. The model serving aish (typically)
|
||||
|
||||
aish targets local llama.cpp endpoints. The committed `config.lua`
|
||||
references the user's home network (`dirac.fritz.box`,
|
||||
`hossenfelder.fritz.box`). The user's other Claude sessions have
|
||||
established that small Q4_K_M models (Qwen2.5-Coder-7B and similar) have
|
||||
**variance issues on code generation tasks** — the same prompt can yield
|
||||
both correct and broken code across rolls. Practical implications for
|
||||
aish:
|
||||
|
||||
- Default `temperature` to `0.2` or lower for code tasks.
|
||||
- Don't assume the model output is correct — validate before exec.
|
||||
- The `confirm_cmd = true` default in `config.shell` is there for this
|
||||
reason; don't disable it without a deliberate UX change.
|
||||
|
||||
This isn't paranoia, it's a measured property of the local model class
|
||||
the user runs.
|
||||
|
||||
---
|
||||
|
||||
## 11. Identity
|
||||
|
||||
If this is a session running on a fleet host other than the user's
|
||||
primary Claude window, your Gitea identity is `claude-<hostname>`. For
|
||||
aish (a non-PR-flow repo), commits as that identity are fine without a PR.
|
||||
If you need to push and lack credentials, use a Gitea Personal Access
|
||||
Token in the URL: `git push https://<user>:<token>@git.reauktion.de/marfrit/aish.git main`.
|
||||
|
||||
The user has marfrit-level credentials available via a separate channel
|
||||
if needed for repo-admin operations.
|
||||
|
||||
@@ -2,35 +2,118 @@
|
||||
|
||||
**aish** — AI-augmented conversational shell.
|
||||
|
||||
A unified REPL backed by language models accessed through a llama.cpp broker.
|
||||
Shell execution, AI inference, context management, and session memory in one
|
||||
terminal interface.
|
||||
A single REPL that interleaves shell command execution and language-model
|
||||
conversation, backed by a llama.cpp HTTP broker. Implementation is LuaJIT
|
||||
2.x with FFI bindings to libcurl, GNU readline, and libc — no C extensions,
|
||||
no build step, one source tree.
|
||||
|
||||
Implementation: LuaJIT 2.x with FFI bindings (libcurl, GNU readline, libc),
|
||||
no compiled extensions, no build step.
|
||||
## Why
|
||||
|
||||
Three flows that currently live in three windows fold into one:
|
||||
|
||||
1. **"Run this command and show me the output"** — fast feedback loop, no
|
||||
copy-paste between terminal and chat.
|
||||
2. **"Explain or write code based on the output we just looked at"** —
|
||||
exec output is automatically injected into the model's context.
|
||||
3. **"Plan and execute a multi-step task with confirmation gates"** —
|
||||
landing in Phase 2 as Chuck Norris autonomous mode.
|
||||
|
||||
aish is not a wrapper around bash. It's a first-class interactive
|
||||
environment where the shell is one of several execution channels.
|
||||
|
||||
## Status
|
||||
|
||||
**Pre-implementation.** Phase 0 manifest only — see
|
||||
[`docs/PHASE0.md`](docs/PHASE0.md) for the full substrate, scope, technology
|
||||
decisions, directory layout, dispatch model, broker contract, execution
|
||||
model, configuration, and planned phase sequence.
|
||||
| Component | State |
|
||||
|---|---|
|
||||
| Repository skeleton | ✅ in this commit |
|
||||
| Phase 0 manifest | ✅ [`docs/PHASE0.md`](docs/PHASE0.md) — locked |
|
||||
| Phase 0 implementation | 🔜 next session |
|
||||
| Phase 1+ | 📋 enumerated in PHASE0.md §11 |
|
||||
|
||||
Every module file currently raises `not implemented (Phase 0 pending)`
|
||||
when called. `luajit main.lua` fails loudly at the first un-implemented
|
||||
function, never silently.
|
||||
|
||||
## Quick orientation
|
||||
|
||||
| Read this | If you want to know |
|
||||
|---|---|
|
||||
| `docs/PHASE0.md` §1–2 | What aish is and what Phase 0 ships |
|
||||
| `docs/PHASE0.md` §3 | Technology decisions (LuaJIT, FFI, readline, libcurl, llama.cpp) |
|
||||
| `docs/PHASE0.md` §4 | Directory layout — these file names are stable across all phases |
|
||||
| `docs/PHASE0.md` §5 | How input is dispatched (meta / shell / AI) |
|
||||
| `docs/PHASE0.md` §6 | Broker contract: `/v1/chat/completions`, `CMD:` extraction |
|
||||
| `docs/PHASE0.md` §10 | Config schema and resolution order |
|
||||
| `docs/PHASE0.md` §11 | Phase sequence (what lands when) |
|
||||
| `docs/PHASE0.md` §13 | Open questions, tracked per phase |
|
||||
| `CLAUDE.md` | Project conventions for AI-assisted contributors |
|
||||
|
||||
## Directory layout
|
||||
|
||||
See `docs/PHASE0.md` §4. File names are stable across phases — later phases
|
||||
fill in module bodies, they do not rename files.
|
||||
```
|
||||
aish/
|
||||
├── main.lua # entry point
|
||||
├── repl.lua # readline loop, dispatch, prompt
|
||||
├── broker.lua # llama.cpp HTTP client
|
||||
├── router.lua # input classifier (meta/shell/AI)
|
||||
├── executor.lua # command exec + CMD: extraction
|
||||
├── context.lua # in-memory turn history
|
||||
├── history.lua # disk persistence (Phase 1+)
|
||||
├── safety.lua # destructive-op gate (Phase 2+)
|
||||
├── renderer.lua # output formatting
|
||||
├── config.lua # default model registry + preferences
|
||||
├── ffi/
|
||||
│ ├── curl.lua # libcurl easy interface
|
||||
│ ├── readline.lua # GNU readline
|
||||
│ ├── pty.lua # forkpty (Phase 1+)
|
||||
│ └── libc.lua # chdir, errno, strerror
|
||||
└── docs/
|
||||
└── PHASE0.md # locked substrate
|
||||
```
|
||||
|
||||
## Build / runtime dependencies
|
||||
|
||||
System packages (Debian / ALARM / Arch names):
|
||||
|
||||
- `luajit` (>= 2.0)
|
||||
- `libcurl4` / `libcurl-openssl-3` runtime
|
||||
- `libreadline8` runtime
|
||||
- `libc6` runtime (always present)
|
||||
|
||||
No compilation, no `luarocks`, no `make`. Just `luajit main.lua`.
|
||||
|
||||
## Running
|
||||
|
||||
Not runnable yet. Once Phase 0 lands:
|
||||
Once Phase 0 ships:
|
||||
|
||||
```sh
|
||||
luajit main.lua [--config <path>]
|
||||
luajit main.lua # uses ~/.config/aish/config.lua
|
||||
luajit main.lua --config ./config.lua # explicit config path
|
||||
AISH_CONFIG=/path/to/config.lua luajit main.lua
|
||||
```
|
||||
|
||||
Config resolution order is documented in `docs/PHASE0.md` §10.
|
||||
|
||||
## Configuration
|
||||
|
||||
`config.lua` is a Lua file returning a single table. The committed
|
||||
`config.lua` in this repo is both the canonical example and the
|
||||
development-fallback config (lowest precedence). Copy it to
|
||||
`~/.config/aish/config.lua` and edit endpoints to your local llama.cpp
|
||||
servers, or point `AISH_CONFIG` at your own.
|
||||
|
||||
The default endpoints assume mfritsche's home network:
|
||||
- `fast` → `dirac.fritz.box:8081` (Qwen2.5-Coder-7B q4 8k ctx)
|
||||
- `deep` → `dirac.fritz.box:8080` (Qwen2.5-Coder-7B q4 32k ctx)
|
||||
- `cloud` → `hossenfelder.fritz.box:8082` (forwards to OpenRouter)
|
||||
|
||||
Replace these with your own llama.cpp endpoints if you're not on that LAN.
|
||||
|
||||
## License
|
||||
|
||||
Not yet selected. Default-private until decided.
|
||||
|
||||
## Project conventions
|
||||
|
||||
This repo follows the 8(+1) phase loop documented in mfritsche's home-project
|
||||
canon (`feedback_dev_process.md` in claude-noether memory). Each phase has its
|
||||
own document under `docs/`. The Phase 0 manifest is the locked substrate.
|
||||
See [`CLAUDE.md`](CLAUDE.md) for contribution conventions, commit style,
|
||||
and the phase-loop discipline this project follows.
|
||||
|
||||
Reference in New Issue
Block a user