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

Hooks

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

HookEvent / matcherBehavior
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.

.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.