aict — Coreutils for AI Agents
Unix coreutils rebuilt for AI agents — structured XML/JSON output, zero parsing, zero ambiguity.
What is aict?
aict is a single Go binary that reimplements ~22 Unix CLI tools (ls, grep, cat, find, stat, diff, etc.) with structured XML/JSON output designed for AI coding agents.
Every time an AI agent runs ls, grep, or cat, it wastes tokens parsing human-readable output. aict gives you the same tools you already know, but the output is structured. No parsing. No regex. Just data.
What You Get
$ aict ls src/
<ls timestamp="1234567890" total_entries="3">
<file name="main.go" path="src/main.go" absolute="/project/src/main.go"
size_bytes="2048" size_human="2.0 KiB" modified="1234567890" modified_ago_s="3600"
language="go" mime="text/x-go" binary="false"/>
<file name="utils.go" path="src/utils.go" absolute="/project/src/utils.go"
size_bytes="1024" size_human="1.0 KiB" modified="1234567890" modified_ago_s="3600"
language="go" mime="text/x-go" binary="false"/>
<directory name="internal" path="src/internal"/>
</ls>
Every field is labeled. Every path is absolute. Every timestamp is a Unix epoch integer. The agent knows exactly what it is looking at.
Quick Start
# Install
go install github.com/synseqack/aict@latest
# Use it
aict ls src/
aict grep "func" . -r
aict cat main.go
Key Features
| Feature | Detail |
|---|---|
| Structured output | XML, JSON, or plain text for every tool |
| Zero dependencies | Go standard library only (MCP server excepted) |
| Single binary | Drop it anywhere and it works |
| Enriched metadata | Language detection, MIME types, epoch timestamps |
| MCP server | Connect to Claude, Cursor, and other AI assistants |
| Cross-platform | Linux, macOS, Windows (partial) |
Navigate This Site
| Section | What You’ll Find |
|---|---|
| Installation | Build from source, go install, Docker |
| Usage | Commands, flags, environment variables |
| Output Modes | XML, JSON, plain text explained |
| Tools Reference | Documentation for all 22+ tools |
| MCP Server | Connect to Claude, Cursor, etc. |
| XML Schema Reference | Complete output schema for every tool |
| Migration Guide | GNU coreutils → aict mapping |
| Integration Guide | Using aict with AI coding agents |
| Benchmarks | Performance vs GNU coreutils |
| FAQ | Common questions answered |
| Contributing | How to add tools, code style, testing |
Why This Exists
We built AI coding agents that needed to read files, search codebases, and compare directories. Standard CLI tools are designed for humans. Every parsing attempt was brittle.
This gives you the same capabilities, but the output is unambiguous. The agent does not guess. It reads.
Getting Started
This section covers everything you need to start using aict.
- Installation — Build from source or install via
go install - Usage — Basic commands and flags
- Output Modes — XML, JSON, and plain text
Installation
Option 1: Go Install (Recommended)
# Install both binaries
go install github.com/synseqack/aict@latest
go install github.com/synseqack/aict/cmd/mcp@latest
This places aict and aict-mcp in your $GOPATH/bin directory.
Option 2: Build from Source
git clone https://github.com/synseqack/aict
cd aict
go build -o aict .
go build -o aict-mcp ./cmd/mcp
Option 3: Docker
docker build -t aict .
docker run --rm -v "$(pwd)":/work -w /work aict ls .
Verify Installation
aict --help
aict ls .
You should see a list of available commands and an XML directory listing.
Shell Completion
# Bash
source completions/aict.bash
# Zsh
source completions/aict.zsh
Add the appropriate line to your .bashrc or .zshrc for persistent completion.
Usage
Basic Commands
Every aict tool is invoked as a subcommand:
aict <tool> [flags] [arguments]
Examples
# List directory contents
aict ls src/
# Search for a pattern
aict grep "func main" . -r
# Read a file
aict cat main.go
# Find files by name
aict find . -name "*.go"
# Get file metadata
aict stat main.go
# Compare two files
aict diff old.go new.go
# Count lines, words, bytes
aict wc main.go
Global Flags
| Flag | Description |
|---|---|
--xml | Force XML output |
--json | Force JSON output |
--plain | Plain text output (GNU-compatible) |
--help | Show help for any tool |
Environment Variables
| Variable | Description |
|---|---|
AICT_XML=1 | Default to XML output for all tools |
AICT_JSON=1 | Default to JSON output for all tools |
Help
# List all available tools
aict help
# Get help for a specific tool
aict grep --help
Output Modes
Every aict tool supports three output modes.
XML (Default for AI)
XML is the default when AICT_XML=1 is set, or when --xml is passed.
<ls timestamp="1234567890" total_entries="1">
<file name="main.go" path="main.go" absolute="/project/main.go"
size_bytes="2048" size_human="2.0 KiB"
modified="1234567890" modified_ago_s="3600"
language="go" mime="text/x-go" binary="false"/>
</ls>
Why XML? Attributes carry metadata without nesting. A <file size_bytes="2048" language="go"/> is 40 chars; the JSON equivalent is 60+. AI context windows are token-limited — denser encoding means more results per call.
JSON
Pass --json for JSON output. The schema mirrors the XML structure.
{
"timestamp": 1234567890,
"total_entries": 1,
"files": [
{
"name": "main.go",
"path": "main.go",
"absolute": "/project/main.go",
"size_bytes": 2048,
"size_human": "2.0 KiB",
"modified": 1234567890,
"modified_ago_s": 3600,
"language": "go",
"mime": "text/x-go",
"binary": false
}
]
}
Plain Text
Pass --plain for GNU-compatible plain text output. This skips enrichment and outputs text similar to the original Unix tool.
$ aict ls src/ --plain
main.go
utils.go
internal/
Use --plain when:
- You only need content, not metadata
- You want maximum performance (skips language/MIME detection)
- You need compatibility with existing scripts
Error Output
Errors are structured XML in stdout, never stderr:
<ls timestamp="1234567890" total_entries="0">
<error code="2" msg="no such file or directory" path="/nonexistent"/>
</ls>
Exit code is always 0 for structured errors. Non-zero exit codes indicate fatal failures.
Tools Reference
All aict tools are organized into five categories. Every tool supports --xml, --json, and --plain output modes.
File Inspection
| Tool | Description |
|---|---|
cat | File contents with encoding detection |
head / tail | First or last lines of a file |
file | File type detection via magic bytes |
stat | File metadata with all timestamps |
wc | Line, word, and byte counts |
Directory & Search
| Tool | Description |
|---|---|
ls | Directory listings with language and MIME type detection |
find | Filesystem search by name, type, or modification time |
grep | Pattern search with context lines and recursive support |
diff | File and directory comparison |
Path Utilities
| Tool | Description |
|---|---|
realpath | Resolve absolute paths |
basename | Extract filename from path |
dirname | Extract directory from path |
pwd | Print working directory |
Text Processing
| Tool | Description |
|---|---|
sort | Sort lines with options |
uniq | Remove or count duplicate lines |
cut | Extract columns from delimited text |
tr | Translate or delete characters |
System & Environment
| Tool | Description |
|---|---|
env | Environment variables with secret redaction |
system | OS, runtime, and user information |
ps | Running process list |
df | Disk space usage |
du | Directory size analysis |
checksums | MD5, SHA1, and SHA256 hashes |
git | Git status, diff, log, ls-files, blame |
doctor | Self-diagnostic command |
ls — Directory Listing
List directory contents with language detection, MIME types, and structured metadata.
Usage
aict ls [flags] [directory...]
Flags
| Flag | Description |
|---|---|
-l | Long format (default in XML mode) |
-a | Include hidden files |
-A | Include hidden files except . and .. |
-h | Human-readable sizes |
-t | Sort by modification time |
-r | Reverse sort order |
-R | Recursive listing |
XML Output
<ls timestamp="1234567890" total_entries="3" path="src/" absolute="/project/src">
<file name="main.go" path="src/main.go" absolute="/project/src/main.go"
size_bytes="2048" size_human="2.0 KiB"
modified="1234567890" modified_ago_s="3600"
permissions="rw-r--r--" mode="0644"
owner="user" group="group"
mime="text/x-go" language="go"
binary="false" executable="false"/>
<directory name="internal" path="src/internal" absolute="/project/src/internal"
modified="1234567890" modified_ago_s="3600"
permissions="rwxr-xr-x" mode="0755"/>
<symlink name="link" path="src/link" absolute="/project/src/link"
target="main.go" broken="false"/>
</ls>
Plain Text Output
$ aict ls src/ --plain
main.go
utils.go
internal/
cat — File Read
Read file contents with encoding detection, language identification, and line counting.
Usage
aict cat [flags] [file...]
Flags
| Flag | Description |
|---|---|
-n | Show line numbers |
XML Output
<cat timestamp="1234567890" files="1" total_bytes="2048" total_lines="50">
<file path="main.go" absolute="/project/main.go"
size_bytes="2048" size_human="2.0 KiB"
lines="50" encoding="utf-8" language="go"
mime="text/x-go" binary="false"
modified="1234567890" modified_ago_s="3600">
<content><![CDATA[package main...]]></content>
</file>
</cat>
Notes
- Binary files omit
<content>and setbinary="true" - Multi-file concatenation is supported
- Encoding detection: UTF-8, UTF-8-BOM, binary
grep — Pattern Search
Search files for regex patterns with context lines, byte offsets, and language metadata.
Usage
aict grep [flags] <pattern> [path...]
Flags
| Flag | Description |
|---|---|
-r | Recursive directory search |
-n | Show line numbers |
-l | List matching files only |
-i | Case-insensitive search |
-w | Match whole words only |
-A N | Show N lines after match |
-B N | Show N lines before match |
-C N | Show N lines of context |
-c | Count matches per file |
-v | Invert match |
-E | Extended regex |
-F | Fixed string (literal) |
--include | Include files matching glob |
--exclude-dir | Exclude directories matching glob |
XML Output
<grep timestamp="1234567890" pattern="func" recursive="true"
case_sensitive="true" match_type="regex"
searched_files="100" matched_files="3" total_matches="12"
search_root="/project">
<file path="main.go" absolute="/project/main.go" matches="5" language="go">
<match line="10" col="1" offset_bytes="200">
<before>package main</before>
<text>func main() {</text>
<after> fmt.Println("hello")</after>
</match>
</file>
</grep>
Empty Results
<grep timestamp="1234567890" pattern="neverexists" recursive="false"
case_sensitive="true" match_type="regex"
searched_files="0" matched_files="0" total_matches="0"
search_root=".">
</grep>
find — Filesystem Search
Search the filesystem by name, type, modification time, and more.
Usage
aict find [path...] [conditions]
Conditions
| Condition | Description |
|---|---|
-name <pattern> | Match filename (glob) |
| `-type <f | d |
-mtime <N> | Modified N days ago |
-size <N> | File size |
-maxdepth <N> | Maximum directory depth |
-not | Negate next condition |
-o | OR operator |
XML Output
<find timestamp="1234567890" total_results="42" search_root="/project">
<condition type="name" value="*.go"/>
<condition type="maxdepth" value="3"/>
<result path="main.go" absolute="/project/main.go" type="file"
size_bytes="2048" modified="1234567890" modified_ago_s="3600"
language="go" mime="text/x-go" depth="0"/>
</find>
stat — File Metadata
Get detailed file metadata with all timestamps, permissions, ownership, and enrichment.
Usage
aict stat [flags] [file...]
Flags
| Flag | Description |
|---|---|
-L | Follow symlinks |
XML Output
<stat timestamp="1234567890">
<file path="main.go" absolute="/project/main.go"
inode="123456" links="1" device="259,0"
permissions="rw-r--r--" mode_octal="0644"
uid="1000" gid="1000" owner="user" group="group"
size_bytes="2048" size_human="2.0 KiB"
atime="1234567890" atime_ago_s="3600"
mtime="1234567890" mtime_ago_s="3600"
ctime="1234567890" ctime_ago_s="3600"
birth="1234567890" birth_ago_s="7200"
language="go" mime="text/x-go"/>
</stat>
wc — Line/Word/Char/Byte Count
Count lines, words, characters, and bytes with per-file and total statistics.
Usage
aict wc [flags] [file...]
Flags
| Flag | Description |
|---|---|
-l | Lines only |
-w | Words only |
-c | Bytes only |
-m | Characters only |
XML Output
<wc timestamp="1234567890" files="3">
<file path="main.go" absolute="/project/main.go"
lines="50" words="200" chars="1800" bytes="2048" language="go"/>
<file path="utils.go" absolute="/project/utils.go"
lines="30" words="100" chars="900" bytes="1024" language="go"/>
<total lines="80" words="300" chars="2700" bytes="3072"/>
</wc>
diff — File Comparison
Compare files and directories with Myers diff algorithm, structured hunks, and line numbers.
Usage
aict diff [flags] <old> <new>
Flags
| Flag | Description |
|---|---|
-u | Unified diff format |
--label <name> | Custom labels |
-r | Recursive directory comparison |
--ignore-all-space | Ignore whitespace changes |
-q | Brief output |
XML Output
<diff timestamp="1234567890" added_lines="5" removed_lines="3"
changed_hunks="2" identical="false">
<hunk old_start="10" old_count="5" new_start="10" new_count="7">
<context line="10">func example() {</context>
<removed line="11"> old_line();</removed>
<added line="11"> new_line();</added>
<added line="12"> another_new();</added>
<context line="12"> return nil</context>
</hunk>
</diff>
Identical Files
<diff timestamp="1234567890" added_lines="0" removed_lines="0"
changed_hunks="0" identical="true"/>
file
head / tail — Partial File Read
Read the first or last N lines/bytes of a file with enrichment metadata.
Usage
aict head [flags] [file...]
aict tail [flags] [file...]
Flags
| Flag | Description |
|---|---|
-n N | Number of lines (default: 10) |
-c N | Number of bytes |
XML Output
<head timestamp="1234567890" lines_requested="10" lines_returned="10"
file_total_lines="500" bytes_returned="256" file_total_bytes="12800"
truncated="false">
<content><![CDATA[line 1...]]></content>
</head>
Notes
tailsupports-ffor follow mode (not available in XML stream mode)- Language and MIME enrichment applied to returned content
du / df — Disk Usage
Analyze directory sizes and filesystem usage.
du — Directory Usage
aict du [flags] [path...]
Flags
| Flag | Description |
|---|---|
-s | Summary only |
-h | Human-readable sizes |
-a | Show all files, not just directories |
--max-depth N | Maximum depth |
XML Output
<du timestamp="1234567890" total_size_bytes="1048576" total_size_human="1.0 MiB">
<entry path="src/" absolute="/project/src"
size_bytes="524288" size_human="512.0 KiB" depth="0"/>
</du>
df — Filesystem Usage
aict df [flags]
Flags
| Flag | Description |
|---|---|
-h | Human-readable sizes |
XML Output
<df timestamp="1234567890">
<filesystem device="/dev/sda1" mount="/" type="ext4"
size_bytes="107374182400" size_human="100.0 GiB"
used_bytes="53687091200" used_human="50.0 GiB"
avail_bytes="53687091200" avail_human="50.0 GiB"
use_pct="50" inodes_total="6553600" inodes_used="3276800"/>
</df>
Platform Support
df uses syscall.Statfs and is Linux/macOS only. Not available on Windows.
Path Utilities
realpath
Resolve a path to its absolute, canonical form.
aict realpath [path...]
<realpath timestamp="1234567890" path="src/../main.go"
absolute="/project/main.go" exists="true" type="file"/>
basename
Extract the filename and optionally the stem/extension from a path.
aict basename [path...]
<basename timestamp="1234567890" path="/project/main.go"
name="main.go" stem="main" extension=".go"/>
dirname
Extract the parent directory from a path.
aict dirname [path...]
<dirname timestamp="1234567890" path="/project/main.go"
directory="/project"/>
pwd
Print the current working directory with home-relative path.
aict pwd
<pwd timestamp="1234567890" path="/project/src"
home="/home/user" relative_to_home="~/project/src"/>
sort / uniq — Sorting and Deduplication
sort
Sort lines with various options.
aict sort [flags] [file...]
Flags
| Flag | Description |
|---|---|
-n | Numeric sort |
-r | Reverse order |
-k N | Sort by field N |
-t <delim> | Field delimiter |
XML Output
<sort timestamp="1234567890" lines_in="100" lines_out="100"
key="1" order="ascending">
<content><![CDATA[sorted lines...]]></content>
</sort>
uniq
Remove or count duplicate lines.
aict uniq [flags] [file...]
Flags
| Flag | Description |
|---|---|
-c | Prefix lines with count |
-d | Only print duplicates |
-u | Only print unique lines |
XML Output
<uniq timestamp="1234567890" lines_in="100" lines_out="50"
duplicates_removed="50" counted="true">
<entry count="5"><![CDATA[duplicate line]]></entry>
</uniq>
cut / tr — Text Processing
cut
Extract columns from delimited text.
aict cut [flags] [file...]
Flags
| Flag | Description |
|---|---|
-d <delim> | Field delimiter (default: tab) |
-f <fields> | Fields to extract (comma-separated) |
XML Output
<cut timestamp="1234567890" delimiter="," fields="1,3" lines_processed="100">
<content><![CDATA[extracted columns...]]></content>
</cut>
tr
Translate or delete characters.
aict tr [flags] <set1> [set2]
Flags
| Flag | Description |
|---|---|
-d | Delete characters in set1 |
-s | Squeeze repeated characters |
XML Output
<tr timestamp="1234567890" lines_processed="100" bytes_processed="5000">
<content><![CDATA[transformed text...]]></content>
</tr>
env — Environment Variables
List environment variables with automatic secret redaction and PATH parsing.
Usage
aict env [flags]
XML Output
<env timestamp="1234567890" total="42" secrets_redacted="3">
<variable name="HOME" value="/home/user" type="path"/>
<variable name="PATH" type="path_list">
<path_entry path="/usr/bin" exists="true"/>
<path_entry path="/usr/local/bin" exists="true"/>
</variable>
<variable name="API_KEY" present="true" redacted="true" type="secret"/>
</env>
Secret Detection
Variables matching these patterns are automatically redacted:
- Names containing
KEY,SECRET,TOKEN,PASSWORD,DSN - URLs containing
://user:pass@
Redacted variables show present="true" redacted="true" instead of their value.
system — System Information
Combined OS, runtime, and user information.
Usage
aict system
XML Output
<system timestamp="1234567890">
<user uid="1000" gid="1000" username="user" home="/home/user" shell="/bin/bash">
<group name="users" gid="100"/>
<group name="sudo" gid="27"/>
</user>
<os name="linux" arch="amd64" hostname="myhost" kernel="5.15.0" distro="Ubuntu 24.04"/>
<runtime go_version="go1.25" num_cpu="8" go_max_procs="8"/>
</system>
Platform Support
- Linux: Full support including distro detection from
/etc/os-release - macOS: Partial — distro detection unavailable
- Windows: Partial — group lookup and distro detection unavailable
ps — Process List
List running processes with detailed metadata.
Usage
aict ps [flags]
Flags
| Flag | Description |
|---|---|
aux | BSD-style full listing |
-ef | System V-style full listing |
-p <pid> | Show specific PID |
--sort <field> | Sort by field |
XML Output
<ps timestamp="1234567890" total_processes="150">
<process pid="1234" ppid="1" user="root" uid="0"
cpu_pct="0.0" mem_pct="1.2" vsz_kb="12345" rss_kb="8192"
state="S" state_desc="sleeping"
started="1234567890" started_ago_s="86400"
command="/usr/bin/python3" args="python3 server.py"
exe="/usr/bin/python3"/>
</ps>
Platform Support
- Linux: Full support via
/procfilesystem - macOS: Partial — uses
syscall.SysctlRawfallback - Windows: Not available
checksums — Hash Computation
Compute MD5, SHA1, and SHA256 hashes in a single pass.
Usage
aict checksums [flags] [file...]
Flags
| Flag | Description |
|---|---|
-c | Verify against checksum file |
XML Output
<checksums timestamp="1234567890" files="2">
<file path="main.go" absolute="/project/main.go"
size_bytes="2048"
md5="d41d8cd98f00b204e9800998ecf8427e"
sha1="da39a3ee5e6b4b0d3255bfef95601890afd80709"
sha256="e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"/>
</checksums>
Notes
- All three hashes computed in a single pass using
io.MultiWriter - Verification mode (
-c) reads a standard checksum file and validates each entry
git — Git Subcommands
Git operations with structured XML output.
Usage
aict git <subcommand> [flags]
Subcommands
| Subcommand | Description |
|---|---|
status | Working tree status |
diff | Changes between commits/working tree |
log | Commit history |
ls-files | List tracked files |
blame | Line-by-line authorship |
git status
<git timestamp="1234567890" command="status">
<branch name="main" ahead="0" behind="0"/>
<staged path="main.go" absolute="/project/main.go" status="modified"/>
<modified path="utils.go" absolute="/project/utils.go"/>
<untracked path="new.go" absolute="/project/new.go"/>
</git>
git diff
<git timestamp="1234567890" command="diff">
<file path="main.go" absolute="/project/main.go">
<hunk old_start="10" old_count="5" new_start="10" new_count="7">
<added line="11"> new_line();</added>
<removed line="11"> old_line();</removed>
</hunk>
</file>
</git>
git log
<git timestamp="1234567890" command="log" count="10">
<commit hash="abc123" author="user" email="user@example.com"
date="1234567890" message="feat: add new feature"/>
</git>
doctor — Self-Diagnostic
Check that aict is properly installed and functioning on your system.
Usage
aict doctor
XML Output
<doctor timestamp="1234567890" status="ok">
<check name="binary" status="pass" message="aict found in PATH"/>
<check name="platform" status="pass" message="linux/amd64"/>
<check name="tools" status="pass" message="22 tools registered"/>
<check name="mcp" status="pass" message="aict-mcp available"/>
</doctor>
Status Values
| Status | Meaning |
|---|---|
pass | Check succeeded |
warn | Non-critical issue |
fail | Critical issue found |
Run this command if aict isn’t behaving as expected. It verifies the binary location, platform compatibility, tool registration, and MCP server availability.
MCP Server
aict ships a standalone MCP (Model Context Protocol) server binary so AI assistants can call every tool directly — no shell spawning, no output parsing.
Build
go build -o aict-mcp ./cmd/mcp
Configure Claude Desktop
Add to ~/.config/claude/claude_desktop_config.json:
{
"mcpServers": {
"aict": {
"command": "aict-mcp",
"args": []
}
}
}
Configure Cursor
Add to .cursor/mcp.json in your project root:
{
"mcpServers": {
"aict": {
"command": "aict-mcp",
"args": []
}
}
}
Available Tools
Once connected, every aict tool becomes a typed, callable function:
| Tool | Description |
|---|---|
ls | Directory listing |
grep | Pattern search |
cat | File read |
find | Filesystem search |
stat | File metadata |
wc | Line/word/byte count |
diff | File comparison |
git status | Git status |
git diff | Git diff |
git log | Git log |
git ls-files | Git tracked files |
git blame | Git blame |
checksums | Hash computation |
ps | Process list |
env | Environment variables |
system | System info |
doctor | Self-diagnostic |
How It Works
- The MCP server starts and registers all tool specs
- The AI client (Claude, Cursor) discovers available tools
- When the model decides to call a tool, it sends a structured request
- The server runs the tool, captures structured JSON output, and returns it
- The model receives typed data — not raw terminal output
Troubleshooting
| Issue | Fix |
|---|---|
| Server won’t start | Ensure aict-mcp is in your PATH |
| Tools not showing | Restart your AI client after config change |
| Permission errors | Check file permissions on target directories |
XML Schema Reference
Migration Guide
Integration Guide
Benchmarks
Measured on a 3.2 GHz Linux x86-64 host against GNU coreutils.
Results
| Tool | GNU | aict | Ratio | Status |
|---|---|---|---|---|
ls (1,000 files) | ~2ms | ~15ms | 7x | PASS |
grep (100k lines) | ~1ms | ~100ms | 100x | SLOW |
find (deep tree) | ~2ms | ~9ms | 5x | PASS |
cat (100k lines) | ~1ms | ~23ms | 17x | SLOW |
diff (1,000 lines) | ~1ms | ~10ms | 10x | PASS |
Why is aict slower?
aict provides significantly more functionality than GNU coreutils:
- Language Detection — Each file is analyzed for programming language
- MIME Detection — Magic bytes analysis for file type
- Structured Output — XML/JSON structures instead of plain text
- Enriched Metadata — Timestamps, permissions, ownership, etc.
Optimization Tips
- Use
--plainto skip enrichment when you only need content - Use
--includefilters ingrepto reduce files scanned - Use
--maxdepthinfindto limit directory traversal - For maximum speed, use GNU coreutils directly when structured output isn’t needed
The trade-off is intentional: more tokens spent on parsing vs. more semantic information returned.
Run Benchmarks Yourself
go build -o aict .
go run ./benchmarks/bench.go
FAQ
vs ripgrep (rg)?
ripgrep is faster and better for interactive search in terminals. aict grep is slower but returns structured XML/JSON with line numbers, byte offsets, language metadata, and context — all in one response. For AI agents that need to reason about search results, aict grep eliminates a parsing layer entirely.
vs eza / lsd?
eza and lsd are beautiful terminal replacements for humans. aict ls is for machines — the output is XML with absolute paths, MIME types, language tags, binary flags, and epoch timestamps. There is no colour code to strip, no column alignment to guess.
Why XML and not JSON by default?
XML is the default because:
- Attributes carry metadata without nesting — a
<file size_bytes="2048" language="go"/>is 40 chars; the JSON equivalent is 60+ with mandatory quotes and colons. - AI context windows are token-limited; denser encoding means more results per call.
- Structured errors (
<error code="2" msg="no such file"/>) compose naturally into the parent element.
Pass --json any time you want JSON. The schema is identical.
Does it work on Windows?
Partially. ls, cat, stat, wc, find, diff, grep, head, tail, sort, uniq, cut, tr, checksums, realpath, basename, dirname, pwd, env all work. ps and df are Linux/macOS only (they read /proc and use syscall.Statfs).
Can I use it without AICT_XML=1?
Yes. Pass --xml, --json, or --plain per invocation. The env var is a convenience for shells configured for AI pipelines.
Contributing
Thank you for your interest in contributing to aict.
Project Structure
aict/
├── cmd/aict/main.go # Entry point
├── cmd/mcp/ # MCP server implementation
├── internal/ # Shared packages
│ ├── xml/ # Output encoding (XML/JSON/plain)
│ ├── detect/ # Language & MIME detection
│ ├── path/ # Path resolution
│ ├── format/ # Size formatting
│ └── meta/ # Timestamps
└── tools/ # Individual tool implementations
└── <toolname>/
└── <toolname>.go # Tool implementation
Adding a New Tool
- Create a new directory under
tools/<toolname>/ - Implement the tool following the pattern in
CONTRIBUTING.md - Register it in
main.go:import _ "github.com/synseqack/aict/tools/toolname" - Write tests in
<toolname>_test.go
Output Requirements
All tools must:
- Output valid XML with root element named after the tool
- Include
timestampattribute (Unix epoch) - Support
--xml,--json,--plainflags - Support
AICT_XML=1environment variable - Return structured errors via
<error>elements - Use absolute paths in output
- Include human-readable companions for bytes/sizes
Testing
go test ./...
go test ./tools/toolname/
Code Style
- Use
gofmtfor formatting - Avoid external dependencies (stdlib only)
- Use lowercase for error messages (no punctuation)
- Export structs for XML marshaling
Dependencies
ALLOWED — Go standard library only.
FORBIDDEN — no external dependencies (github.com/... imports, go get, cgo).
Exception: the MCP server (cmd/mcp) uses github.com/modelcontextprotocol/go-sdk.