main.lua now resolves package.path relative to its own script directory
rather than cwd, so the packaged install at /usr/share/lua/5.1/aish/
finds its siblings regardless of where the user invokes aish from. Dev
mode (luajit main.lua from the repo root) is preserved: arg[0] is
"main.lua" with no "/" so the regex returns nil and _dir falls back to
"./" — identical to the previous behavior.
bin/aish is a POSIX-sh wrapper that execs luajit against $AISH_LIB/main.lua
(default /usr/share/lua/5.1/aish). The AISH_LIB env override lets users
point at a dev checkout without uninstalling the package. Wrapper emits
distinct errors when AISH_LIB is missing or when luajit isn't on PATH so
broken installs surface clearly instead of through a bare sh: not found.
examples/config.lua is the canonical commented reference, shipped at
/usr/share/doc/aish/examples/config.lua. Stripped of the two live MCP
bearer tokens carried by the in-tree config.lua and switched to the
auth_env env-var indirection form; mcp.servers entries are commented
out so a copy-to-~/.config/aish/config.lua produces a working starting
point on first uncomment. HOSSENFELDER URL flagged as maintainer-LAN.
LICENSE: MIT, copyright 2026 Markus Fritsche. README updated to match.
Sonnet review of the changeset (per feedback_reviews_use_sonnet.md +
bugfix-process step 4): no blockers; the two Important findings (USAGE
text still said "luajit main.lua", bin/aish didn't pre-check luajit)
and one Nit (unredacted HOSSENFELDER URL) were folded in before commit.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Wires the project-overlay step around the existing load_config.
Activates only when a trusted .aish.lua is found in/above cwd.
Changes:
- _find_project_config() walks libc.getcwd() up to $HOME, returning
first .aish.lua found. R1 fix folded: proper-prefix check (`dir ==
home OR dir starts with home .. "/"`) avoids the false positive
where /home/user2 matches HOME=/home/user via byte prefix.
- _trust_file_path() resolves via $AISH_TRUST_FILE env override,
else ~/.aish/trusted-projects. Plan-time decision per N3.
- _check_and_maybe_prompt(project_path, history) — calls
history._sha256_file ONCE; routes through history.is_trusted; on
miss prompts via rl.readline; on accept persists via
history.add_trusted. A8 mitigation: if rl.readline fails to load,
decline silently (no io.read fallback that would consume stdin).
- load_config_with_overlay(opts):
* Calls existing load_config; seeds sources={k="user", ...}
* Walks for .aish.lua; if found:
- In opts.prompt mode (-p, R2): skip the prompt entirely;
only PRE-TRUSTED overlays load. Avoids io consuming the
piped stdin that -p will read for context.
- Else: interactive trust check + prompt.
* On accept + successful dofile: shallow-merge top-level keys
ONTO user config; update sources[k]="project" for overlapping.
* R3: embeds sources on cfg._sources for repl.lua's :config
show meta to read. No global.
* Returns (cfg, user_path, project_path | nil).
- main() now calls load_config_with_overlay; on project layer
active, emits the "[aish] project config: <path> (overlaid on
<user>)" status line per A4 (AFTER the user-config status).
E2E verified across 4 scenarios with AISH_TRUST_FILE + isolated HOME:
1. Decline -> overlay skipped; user config active.
2. Accept -> overlay loaded; project_model active; status line
"[aish] project config: ... (overlaid on ...)" visible.
3. Re-startup -> NO prompt (cached via sha); overlay loaded
transparently. R4 single-sha-call confirmed.
4. -p mode with untrusted overlay -> skipped silently; piped
stdin preserved for run_one_shot.
Regression: test_safety 87/87, test_router_model 31/31, repl loads.
Commit #3 lands :config show + HELP next; commit #4 the config
template comment + status -> Implement.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds `aish -p "<text>"` for Unix-pipeline composability:
tail app.log | aish -p "any anomalies?"
aish -p "summarize: $(curl -sS https://...)"
The flag bypasses repl.lua entirely. On invocation:
1. Stdin: when not a TTY, read to EOF and prepend to the prompt as a
fenced block. ffi.libc.isatty(0) gates the read so interactive
`aish -p "..."` (no pipe) doesn't hang.
2. Resolve config.models[config.default_model].
3. Stream broker.chat_stream replies to stdout; finalize with newline.
4. Exit 0 on success, 1 on broker error, 2 on arg / config error.
Behavior NOT in -p mode (kept simple per the issue's "no repl.lua
involvement"):
- No MCP, no tool loop, no Norris, no routing, no memory injection.
- "CMD:" lines in the reply are printed verbatim, NOT executed —
callers can grep / pipe them as they wish.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Phase 0 entry point per PHASE0.md §4, §10.
Resolves the §10 config search:
--config <path> (explicit; failure if not openable, no fallback)
$AISH_CONFIG
~/.config/aish/config.lua
./config.lua
The explicit form now hard-fails instead of silently falling through to
the next candidate — caught in smoke (`--config /nonexistent` was loading
./config.lua).
Pre-pends `./?.lua;./vendor/?.lua` to package.path so `require("dkjson")`
finds vendor/dkjson.lua and project requires resolve from the repo root.
Run from the repo root; cwd-independent resolution lands later.
`--help` prints the usage block. Unrecognized arg exits 2 with a
diagnostic on stderr.
Phase 0 done-criteria (PHASE0.md §2):
✓ shell command execution with framed output
✓ :meta commands (full §5.2 set)
✓ in-memory conversation history with sliding-window eviction
✓ codebase layout matches §4 — every module name stable for Phase 1+
⏳ live AI exchange — structurally wired; live test deferred per
issue #12 (broker endpoint hostname not resolvable from noether)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- README, .gitignore, CLAUDE.md (project conventions)
- docs/PHASE0.md — full Phase 0 manifest (locked substrate)
- 10 root .lua modules + 4 ffi/ bindings, all stubs raising NotImplemented
with module-scoped responsibilities matching the manifest
- config.lua wired to current dirac/hossenfelder endpoints (qwen-coder-7b
snappy/32k + cloud via OpenRouter through hossenfelder)
File names match docs/PHASE0.md §4 exactly. Module bodies fill in across
later phases; the tree shape is locked.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>