Warns when a git add/stash/checkout/restore/reset may sweep dirty files this session did not write (shared checkouts have several agents). A toast for the…

Quick start package for using AILANG with Claude Code and OpenAI Codex.
AILANG is a deterministic programming language designed for AI code synthesis and reasoning.
Install the AILANG CLI before installing either agent integration:
curl -fsSL https://ailang.sunholo.com/install.sh | bash
ailang --version
The plugins invoke the local ailang executable, so it must be available on PATH.
# Step 1: Add the marketplace
/plugin marketplace add sunholo-data/ailang_bootstrap
# Step 2: Install the plugin
/plugin install ailang@sunholo-data/ailang_bootstrap
Or clone and add locally:
git clone https://github.com/sunholo-data/ailang_bootstrap
/plugin marketplace add ./ailang_bootstrap
/plugin install ailang@ailang_bootstrap
Codex requires Node.js 18 or newer for the local MCP server:
node --version
codex plugin marketplace add sunholo-data/ailang_bootstrap --ref stable
codex plugin add ailang@ailang-marketplace
codex plugin list
Start a new Codex session after installation. Alternatively, add the marketplace with the CLI, then install AILANG through /plugins or the Codex desktop Plugins directory.
Codex loads the shared skills and five local MCP tools: ailang_prompt, ailang_check, ailang_run, ailang_builtins, and ailang_eval.
Download a platform-specific release from GitHub Releases:
| Platform | Architecture | Asset |
|---|---|---|
| macOS | Apple Silicon | darwin.arm64.ailang-bootstrap.tar.gz |
| macOS | Intel | darwin.x64.ailang-bootstrap.tar.gz |
| Linux | x64 | linux.x64.ailang-bootstrap.tar.gz |
| Windows | x64 | win32.x64.ailang-bootstrap.zip |
Each release includes the AILANG binary pre-bundled.
| Skill | Purpose |
|---|---|
| ailang | Write, run, and develop with AILANG |
| ailang-inbox | Cross-agent messaging with semantic search, deduplication, and GitHub sync |
| Command | Purpose |
|---|---|
/ailang-prompt | Load syntax teaching prompt (do this first!) |
/ailang-run <file> | Run an AILANG program |
/ailang-check <file> | Type-check without running |
/ailang-new <name> [template] | Create new program from template |
/ailang-repl | Start interactive REPL |
/ailang-builtins [search] | List builtin functions |
/ailang-editor <editor> | Install syntax highlighting |
| Hook | Event | Purpose | |||
|---|---|---|---|---|---|
session_start.sh | SessionStart | Inbox check + brain context | |||
brain_session.sh | SessionStart | Inject relevant brain knowledge | |||
git_guard.sh | PreToolUse(Bash) | Block destructive git ops | |||
microrag_context.sh | PreToolUse(Edit\ | Write\ | Read\ | MultiEdit) | JIT μRAG knowledge injection — surfaces AILANG syntax/builtin docs at tool-call time |
microrag_lint.sh | PostToolUse(Edit\ | Write\ | MultiEdit) | First-use builtin nudge for *.ail edits | |
brain_resolution.sh | PostToolUse(Bash) | Capture command resolutions | |||
observatory_hook.sh | All events | OTEL telemetry for AILANG Observatory |
Disable μRAG: export AILANG_MICRORAG_ENABLED=0. Requires ailang and jq on PATH; degrades silently if either is missing.
μRAG brain corpus auto-bootstrap: install.sh runs ailang micro-rag init && ailang micro-rag bootstrap --scope user --no-embed after the binary lands. This populates the brain DB at ~/.ailang/state/brain.db with the ailang-syntax (~50 frames) and ailang-builtins (~280 frames) namespaces using only resources embedded in the binary — no source repo required. Works on Windows / minimal Docker images / anywhere awk and python3 are absent.
Re-run manually after install.sh to upgrade the corpus to embedding-backed retrieval (requires Ollama):
ailang micro-rag bootstrap --scope user # add embeddings on top of SimHash/FTS
ailang micro-rag bootstrap --scope user --reset # full rebuild (use after AILANG version bump)
A live pane that shows what you're writing as AILANG sees it. After every Edit/Write of a .ail file it runs ailang iface and ailang check and draws the module's functions, their types and effect rows (pure or !{IO,FS}), plus any type errors — keeping the last good signatures on screen beside a broken edit. The status line carries a one-line summary.
/plugin install ailang-lens@ailang-marketplace
/reload-plugins
/ail-lens [file.ail] # open the pane, or analyse any file into it
Mods are an early-access Claude Code feature; where they are not enabled the plugin installs but does nothing. Requires ailang on PATH.
1. Local stdio MCP (auto-installed by this plugin) — wraps the local AILANG CLI for actually running/checking code:
ailang_check - Type-check filesailang_run - Run programsailang_prompt - Get teaching prompt (embedded copy)ailang_builtins - List builtinsailang_eval - Evaluate an expression in the non-interactive REPL2. Remote HTTP MCP at mcp.ailang.sunholo.com — version-locked live docs, stdlib, examples, design docs, benchmarks. Available to any agent harness that supports remote MCP (Claude Desktop, Cursor, Cline, Continue, Claude Code).
{
"mcpServers": {
"ailang-docs": {
"url": "https://mcp.ailang.sunholo.com/mcp/",
"transport": "streamable-http"
}
}
}
Tools include prompt_get, stdlib_modules, stdlib_search, examples_for_concept, limitations_list, effects_catalog, and submit_feedback (for filing AILANG bugs from inside an agent session). Use these to avoid stale embedded knowledge — the snapshot is rebuilt and redeployed on every AILANG release.
See docs/guides/agent-mcp for the full tool catalog.
Once installed, you can:
# CRITICAL: Load syntax before writing code
ailang prompt
# Run AILANG code
ailang run --caps IO --entry main program.ail
# Start interactive REPL
ailang repl
# Type-check without running
ailang check program.ail
module myproject/hello
export func main() -> () ! {IO} {
print("Hello, AILANG!")
}
Run with:
ailang run --caps IO --entry main hello.ail
func - NOT fn, function, or deflet x = 1; let y = 2; x + y=> - NOT : or ->print expects string - Use print(show(42)) for numbersailang_bootstrap/
├── .claude-plugin/
│ ├── plugin.json # Claude Code plugin manifest
│ └── marketplace.json # Skills marketplace
├── .codex-plugin/
│ └── plugin.json # Codex plugin manifest
├── .mcp.json # Codex MCP configuration
├── .claude/commands/ # Slash commands
│ ├── ailang-run.md
│ ├── ailang-check.md
│ ├── ailang-new.md
│ └── ...
├── mcp-server/ # MCP server for AILANG tools
│ ├── server.js
│ └── package.json
├── skills/
│ ├── ailang/ # Main AILANG skill
│ │ ├── SKILL.md
│ │ └── resources/
│ └── ailang-inbox/ # Agent messaging skill
│ └── SKILL.md
├── bin/ # AILANG binary (in releases)
└── .github/workflows/
└── release.yml # Platform-specific release builds
MIT License - see LICENSE
hooks/register.ts 50 lines1import { atom, read, update } from 'claude-code'
2import type { EngineInterface, Register } from 'claude-code'
3
4import { isSweepingGitOp, parsePorcelain, unownedDirty } from './core'
5
6// A visibility warning, not an authority check (M-CLAUDE-CODE-MODS C1): git
7// itself says what is dirty; this session's Edit/Write calls say what it
8// wrote. Bash writes are unknowable, and the warning says it is a heuristic.
9
10const own = atom({ plugin: 'unowned-dirty', key: 'own' } as const, [])
11
12/** Pure: absolute paths under `top` as repo-relative, as porcelain prints them. */
13export function relativeTo(top: string, paths: readonly string[]): Set<string> {
14 const prefix = top.endsWith('/') ? top : `${top}/`
15 return new Set(paths.filter(p => p.startsWith(prefix)).map(p => p.slice(prefix.length)))
16}
17
18async function sweepWarning($: EngineInterface, command: string): Promise<string | undefined> {
19 const cwd = await $.session.cwd()
20 const top = await $.process.run(['git', 'rev-parse', '--show-toplevel'], { cwd, timeoutMs: 10_000 })
21 if (top.exitCode !== 0) return undefined
22 const status = await $.process.run(['git', 'status', '--porcelain'], { cwd, timeoutMs: 10_000 })
23 if (status.exitCode !== 0) return undefined
24
25 const unowned = unownedDirty(parsePorcelain(status.stdout), relativeTo(top.stdout.trim(), await read($, own)))
26 if (unowned.length === 0) return undefined
27 const named = unowned.slice(0, 5).join(', ') + (unowned.length > 5 ? `, +${unowned.length - 5} more` : '')
28 return `[unowned-dirty heuristic] '${command.trim().slice(0, 60)}' may sweep ${unowned.length} dirty file(s) this session did not write: ${named}. Coordinate before stashing or committing others' in-flight work.`
29}
30
31export const register: Register = on => {
32 on('tool.call', async ($, e, next) => {
33 if (e.tool === 'Edit' || e.tool === 'Write') {
34 const ran = await next(e)
35 if (ran.deny === undefined && !ran.isError) {
36 const path = e.file_path.startsWith('/') ? e.file_path : `${await $.session.cwd()}/${e.file_path}`
37 await update($, own, list => (list.includes(path) ? list : [...list, path]))
38 }
39 return ran
40 }
41 if (e.tool !== 'Bash' || !isSweepingGitOp(e.command)) return next(e)
42
43 // Read the tree before the command changes it; never block it.
44 const warning = await sweepWarning($, e.command).catch(() => undefined)
45 if (warning) $.ui.toast(warning)
46 const ran = await next(e)
47 return warning && ran.deny === undefined ? { ...ran, context: [...(ran.context ?? []), warning] } : ran
48 })
49}
50hooks/core.ts 28 lines1// pi-core: .pi/extensions/unowned-dirty.ts DirtyFile parsePorcelain unownedDirty isSweepingGitOp
2// Generated by scripts/check_claude_mods_drift.sh --write from the pi source
3// above. Do not edit here: change pi, then regenerate.
4
5export interface DirtyFile {
6 status: string; // porcelain XY codes, e.g. " M", "M ", "??"
7 path: string;
8}
9
10/** Pure: parse `git status --porcelain` lines into (status, path). */
11export function parsePorcelain(out: string): DirtyFile[] {
12 return out
13 .split("\n")
14 .filter((l) => l.trim() !== "")
15 .map((l) => ({ status: l.slice(0, 2), path: l.slice(3).trim().replace(/^"|"$/g, "") }));
16}
17
18/** Pure: files dirty in the tree that this session did not write. */
19export function unownedDirty(all: DirtyFile[], ownFiles: Set<string>): string[] {
20 return all.map((f) => f.path).filter((p) => !ownFiles.has(p));
21}
22
23/** Pure: does this bash command perform a sweeping git operation? */
24export function isSweepingGitOp(command: string | undefined): boolean {
25 if (!command) return false;
26 return /\bgit (add|stash|checkout|restore|reset)\b/.test(command);
27}
28types/index.d.ts 9 lines1/** Absolute paths this session wrote through Edit or Write. */
2export type OwnFiles = string[]
3
4declare module 'claude-code' {
5 interface PluginState {
6 'unowned-dirty': { own: OwnFiles }
7 }
8}
9