# ACode

ACode is the built-in headless AI agent runtime bundled with `aws-runtime-bridge`.
It is not installed or uninstalled as an external tool. Runtime configuration and
MCP/Skill synchronization are stored under the bridge runtime home in `.acode/`.

Current scope:

- non-TUI runtime session loop
- structured JSONL events for bridge adapters
- MCP configuration discovery from `.acode/config.json`
- Skill discovery from `.acode/skills/*/SKILL.md`
- Markdown slash command discovery from configured command directories
- Claude Code-inspired tool permission rules and slash-command tool allowlists
- Minimal HookBus lifecycle for runtime extensions and tool-use guards

## Slash commands

ACode can load Markdown slash commands from directories configured in
`.acode/config.json` or passed by the runtime bridge:

```json
{
  "commandDirs": ["/absolute/path/to/commands"],
  "commands": {
    "directories": ["/another/commands/path"]
  }
}
```

Each command is a `.md` file. The command name defaults to the file name, or can
be supplied through frontmatter. The Markdown body is used as a prompt template;
`$ARGUMENTS` is replaced with the text that follows the slash command.

```md
---
name: review
description: Review current changes
allowed-tools: [read_file, edit_file]
argument-hint: <scope>
---

Review $ARGUMENTS and report risks.
```

The current parser intentionally supports only simple scalar frontmatter fields
and comma-separated list values. It does not implement full YAML parsing,
nested objects, or multiline frontmatter values. `allowed-tools` is enforced for
the expanded command turn, so a command can narrow which built-in or MCP tools
the model may call while handling that command.

## Tool permissions

ACode can load Claude Code-style tool permission rules from `.acode/config.json`
or from `ACodeRuntimeConfig.permissions`:

```json
{
  "permissions": {
    "mode": "default",
    "allow": ["read_file", "aws-mcp.*"],
    "deny": ["write_file"]
  }
}
```

Denied rules win before allowed rules. If `allow` is non-empty, every tool call
must match at least one allowed rule. Rules match exposed tool names such as
`write_file`, built-in source names such as `builtin.write_file`, MCP source
names such as `aws-mcp.poll_message`, and simple trailing-wildcard patterns.
Set `mode` to `bypassPermissions` only for trusted non-interactive automation.

## HookBus lifecycle

Runtime integrations can pass hook handlers through `ACodeRuntimeConfig.hooks`.
ACode dispatches these lifecycle events:

- `SessionStart` after config, skills, commands, MCP servers, and built-in tools are loaded
- `UserPromptSubmit` before a user prompt or slash command is submitted
- `PreToolUse` before a built-in or MCP tool call
- `PostToolUse` after a built-in or MCP tool call completes
- `Stop` during runtime shutdown

Hook handlers may return `{ "decision": "deny", "message": "..." }` to block a
prompt or tool call. Hook errors are reported as runtime `error` events and are
otherwise fail-open so extension failures do not crash the session. The
`modify` decision value is reserved for a future prompt/tool-argument mutation
API and is not applied by the current runtime.

## Code symbols (tree-sitter)

ACode bundles tree-sitter for AST-based code analysis, exposed via the
`symbols` built-in tool. Supports 13 languages: TypeScript (incl. TSX),
JavaScript (incl. JSX), Python, Go, Rust, Java, C, C++, C#, Ruby, PHP, Lua,
Bash.

Three modes:
- `definitions` — find symbol declarations (functions, classes, methods, etc.)
- `references` — find symbol usages within a single file
- `outline` — return the full symbol structure of a file

ASTs are cached in-memory (LRU, 100 files) keyed by path + mtime + size.
Grammar WASM files are lazy-loaded on first use of each language.

Grammar WASM files live in `grammars/`. To refresh them after a
`web-tree-sitter` upgrade:

    node scripts/fetch-grammars.mjs

To add a new language:
1. Add an entry to `scripts/fetch-grammars.mjs`
2. Run the script
3. Add extension mapping in `parsers/language-detector.ts`
4. Add query patterns in `parsers/language-queries.ts`
