Skip to content

Internals — config, hooks, LSP, skills


Config (env vars, all optional)

VariableEffectDefault
NIMLANG_TOOLCHAINForces nim or nimony for every call.unset (auto-detect)
NIM_BIN_DIRDirectory holding nim, nimsuggest, nimble.PATH, then ~/Nim/bin
NIMONY_BIN_DIRDirectory holding nimony, nimsem, hastur.PATH, then ~/nimony/bin
NIMLANG_AGGRESSIVETruthy → every tool defaults to terse output.unset (verbose)
AOWLI_BIN_DIRDirectory holding aowli-interp/aowli-dbg (for trace/debug).PATH, then ~/.aowl/bin, then ~/aowli/bin
NIFLENSPath to the optional niflens/aiflens helper.PATH lookup
AOWLCODE_DEFAULT_MODEBaseline aowl mode when no state file exists: off/guided/strict.guided
AOWLCODE_MODE_TTL_HOURSHow long an explicit mode (including off) survives before falling back to the baseline.12
AOWLCODE_NO_MODE_GATE1 → disable the aowl-mode hook and the session banner entirely.unset
AOWLCODE_ALLOW_GENERATED_GREP1 → allow unscoped searches over generated artifact trees.unset
AOWLCODE_NO_SRC_GUARD1 → allow whole-file reads of large sources.unset
AOWLCODE_SERVERpython forces the Python server; nim requires the Nimony one and fails loudly if it cannot be built.auto
AOWLCODE_NO_BUILD1 → never rebuild the Nimony server; run whatever binary is present.unset
AOWLCODE_BUILD_WAITSeconds launch.sh lets a rebuild finish before serving, when the sources are newer than the binary. 0 = never wait.25

Hooks

Stdlib-only Python, fail-open (any error exits 0 rather than blocking).

HookEvent / matcherBehavior
session-banner.pySessionStartInjects ~200 tokens: the active mode plus the redirect table, once per session. Cheaper than the denial messages it prevents; silent when the mode is off.
aowl-mode.pyPreToolUse / Bash|Grep|GlobThe lock, on by default (guided). Denies code archaeology and names the tool to use instead; state re-read per call, so toggling needs no restart. See Aowl mode.
guard-generated-grep.pyPreToolUse / Grep|GlobDenies an unscoped search over a tree containing checked-in nimcache/*.nif or emitted .c, handing back the scoped re-invocation. A single NIF hit can be one 40KB line.
guard-source-read.pyPreToolUse / ReadDenies a whole-file Read of a large .nim, returning a symbol → line-range outline in the same turn so the follow-up read is a window.
guard-nif-read.pyPreToolUse / ReadDenies reading a .nif >15000 bytes; embeds a compact outline of the file in the denial reason (transform-not-block) so the same turn still gets useful structure.
guard-nif-bash.pyPreToolUse / BashDenies cat/head/tail/less/more/bat targeting a .nif >15000 bytes — the shell-side bypass of the Read guard.
trim-build-output.pyPostToolUse / BashFor nimony/hastur/nim c/nimble invocations, strips nifmake:/FAILURE:/niflink noise and surfaces the real diagnostics as additionalContext.
precompact-nudge.pyPreCompact (no matcher)Reminds the agent to run /land first if durable learnings from the session haven't been flushed to memory — compaction discards anything not written down. Emits systemMessage: hookSpecificOutput is valid for PreToolUse/UserPromptSubmit/PostToolUse/PostToolBatch/Stop but not PreCompact, where it fails validation and the output is discarded.

The session banner also warns when the MCP server is running a build older than its sources — see Which copy is running.

Which copy is running

Claude Code does not execute the marketplace checkout. It installs a copy to ~/.claude/plugins/cache/<marketplace>/<plugin>/<version>/ and records the path in ~/.claude/plugins/installed_plugins.json. If a reload fetches a newer version directory but leaves that record on the old one, the session keeps running the old code — visible only as a command or tool that does not exist. doctor reports this directly (plugin.stale); the fix is to repoint the installPath/version entry (or reinstall) and restart, not another reload.

.lsp.json — single dispatching entry

Nim and Nimony share the .nim extension; Claude Code has no documented way to run two servers against the same extension. .lsp.json ships one entry whose command is a dispatcher (scripts/lsp-dispatch.py, stdlib-only):

json
{
  "aowlcode": {
    "command": "python3",
    "args": ["${CLAUDE_PLUGIN_ROOT}/scripts/lsp-dispatch.py"],
    "extensionToLanguage": { ".nim": "nim", ".nims": "nim" },
    "diagnostics": true
  }
}

On launch it applies the same toolchain detection as the MCP server, then execs exactly one real server, piping JSON-RPC through untouched:

DetectedServerInstall
Nim (default)nimlangservernimble install nimlangserver
Nimonyaoughwl/aowl-lspbuild server/, put aowl-lsp on PATH

Overrides: NIMONY_LSP/NIM_LANGSERVER point at server binaries; NIMONY_EXE sets the Nimony compiler the LSP shells out to. Optional enhancement only — every tool, hook, command, and skill works with no LSP installed; "diagnostics": false keeps navigation but suppresses per-edit injection.

Skills (load on demand)

SkillRead it when
token-thriftWorking Nim/Nimony code and want compact diagnostics/NIF without flooding context; prefer recipe tools (explain_failure) over manual multi-call sequences.
repo-mapNavigating a codebase across a session — keep a lazy incremental project map in file-memory, use symbols/api before grep/reads.
nif-formatWorking with .nif artifacts or the phase pipeline — tag vocabulary, .p/.s/.x/.dce suffixes, which tool produces which.
compiler-contractsBuilding tooling ON the toolchain (LSP, formatter, driver) rather than fixing a bug — the contracts the MCP tools normally hide: idetools relative-path rule, exit-code-0-on-error, coordinate bases, NIF decl-vs-use encoding. Pair with raw mode.
debug-loopDebugging the Nimony compiler itself (miscompiles, bad NIF, phase regressions) — the ~/nimony/AGENTS.md workflow.
nim-vs-nimonyBefore writing/compiling/debugging code that might target Nimony — which binary for what, feature-set deltas; do not assume Nim 2 semantics.

Wiring

  • .mcp.json registers the server: python3 ${CLAUDE_PLUGIN_ROOT}/mcp/server.py, server name nimlang, protocol 2024-11-05.
  • Commands live under commands/*.md, namespaced /aowlcode:<name>.
  • Agents under agents/*.md (see Agents).

aoughwl — self-hosted platform for things n stuff. Contact / Support on Discord for access to the private backends.