aict — Coreutils for AI Agents

Unix coreutils rebuilt for AI agents — structured XML/JSON output, zero parsing, zero ambiguity.

CI Go version Latest release License: MIT


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

FeatureDetail
Structured outputXML, JSON, or plain text for every tool
Zero dependenciesGo standard library only (MCP server excepted)
Single binaryDrop it anywhere and it works
Enriched metadataLanguage detection, MIME types, epoch timestamps
MCP serverConnect to Claude, Cursor, and other AI assistants
Cross-platformLinux, macOS, Windows (partial)
SectionWhat You’ll Find
InstallationBuild from source, go install, Docker
UsageCommands, flags, environment variables
Output ModesXML, JSON, plain text explained
Tools ReferenceDocumentation for all 22+ tools
MCP ServerConnect to Claude, Cursor, etc.
XML Schema ReferenceComplete output schema for every tool
Migration GuideGNU coreutils → aict mapping
Integration GuideUsing aict with AI coding agents
BenchmarksPerformance vs GNU coreutils
FAQCommon questions answered
ContributingHow 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

# 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

FlagDescription
--xmlForce XML output
--jsonForce JSON output
--plainPlain text output (GNU-compatible)
--helpShow help for any tool

Environment Variables

VariableDescription
AICT_XML=1Default to XML output for all tools
AICT_JSON=1Default 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

ToolDescription
catFile contents with encoding detection
head / tailFirst or last lines of a file
fileFile type detection via magic bytes
statFile metadata with all timestamps
wcLine, word, and byte counts
ToolDescription
lsDirectory listings with language and MIME type detection
findFilesystem search by name, type, or modification time
grepPattern search with context lines and recursive support
diffFile and directory comparison

Path Utilities

ToolDescription
realpathResolve absolute paths
basenameExtract filename from path
dirnameExtract directory from path
pwdPrint working directory

Text Processing

ToolDescription
sortSort lines with options
uniqRemove or count duplicate lines
cutExtract columns from delimited text
trTranslate or delete characters

System & Environment

ToolDescription
envEnvironment variables with secret redaction
systemOS, runtime, and user information
psRunning process list
dfDisk space usage
duDirectory size analysis
checksumsMD5, SHA1, and SHA256 hashes
gitGit status, diff, log, ls-files, blame
doctorSelf-diagnostic command

ls — Directory Listing

List directory contents with language detection, MIME types, and structured metadata.

Usage

aict ls [flags] [directory...]

Flags

FlagDescription
-lLong format (default in XML mode)
-aInclude hidden files
-AInclude hidden files except . and ..
-hHuman-readable sizes
-tSort by modification time
-rReverse sort order
-RRecursive 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

FlagDescription
-nShow 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 set binary="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

FlagDescription
-rRecursive directory search
-nShow line numbers
-lList matching files only
-iCase-insensitive search
-wMatch whole words only
-A NShow N lines after match
-B NShow N lines before match
-C NShow N lines of context
-cCount matches per file
-vInvert match
-EExtended regex
-FFixed string (literal)
--includeInclude files matching glob
--exclude-dirExclude 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

ConditionDescription
-name <pattern>Match filename (glob)
`-type <fd
-mtime <N>Modified N days ago
-size <N>File size
-maxdepth <N>Maximum directory depth
-notNegate next condition
-oOR 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

FlagDescription
-LFollow 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

FlagDescription
-lLines only
-wWords only
-cBytes only
-mCharacters 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

FlagDescription
-uUnified diff format
--label <name>Custom labels
-rRecursive directory comparison
--ignore-all-spaceIgnore whitespace changes
-qBrief 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

FlagDescription
-n NNumber of lines (default: 10)
-c NNumber 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

  • tail supports -f for 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

FlagDescription
-sSummary only
-hHuman-readable sizes
-aShow all files, not just directories
--max-depth NMaximum 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

FlagDescription
-hHuman-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

FlagDescription
-nNumeric sort
-rReverse order
-k NSort 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

FlagDescription
-cPrefix lines with count
-dOnly print duplicates
-uOnly 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

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

FlagDescription
-dDelete characters in set1
-sSqueeze 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

FlagDescription
auxBSD-style full listing
-efSystem 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 /proc filesystem
  • macOS: Partial — uses syscall.SysctlRaw fallback
  • Windows: Not available

checksums — Hash Computation

Compute MD5, SHA1, and SHA256 hashes in a single pass.

Usage

aict checksums [flags] [file...]

Flags

FlagDescription
-cVerify 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

SubcommandDescription
statusWorking tree status
diffChanges between commits/working tree
logCommit history
ls-filesList tracked files
blameLine-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

StatusMeaning
passCheck succeeded
warnNon-critical issue
failCritical 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:

ToolDescription
lsDirectory listing
grepPattern search
catFile read
findFilesystem search
statFile metadata
wcLine/word/byte count
diffFile comparison
git statusGit status
git diffGit diff
git logGit log
git ls-filesGit tracked files
git blameGit blame
checksumsHash computation
psProcess list
envEnvironment variables
systemSystem info
doctorSelf-diagnostic

How It Works

  1. The MCP server starts and registers all tool specs
  2. The AI client (Claude, Cursor) discovers available tools
  3. When the model decides to call a tool, it sends a structured request
  4. The server runs the tool, captures structured JSON output, and returns it
  5. The model receives typed data — not raw terminal output

Troubleshooting

IssueFix
Server won’t startEnsure aict-mcp is in your PATH
Tools not showingRestart your AI client after config change
Permission errorsCheck 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

ToolGNUaictRatioStatus
ls (1,000 files)~2ms~15ms7xPASS
grep (100k lines)~1ms~100ms100xSLOW
find (deep tree)~2ms~9ms5xPASS
cat (100k lines)~1ms~23ms17xSLOW
diff (1,000 lines)~1ms~10ms10xPASS

Why is aict slower?

aict provides significantly more functionality than GNU coreutils:

  1. Language Detection — Each file is analyzed for programming language
  2. MIME Detection — Magic bytes analysis for file type
  3. Structured Output — XML/JSON structures instead of plain text
  4. Enriched Metadata — Timestamps, permissions, ownership, etc.

Optimization Tips

  • Use --plain to skip enrichment when you only need content
  • Use --include filters in grep to reduce files scanned
  • Use --maxdepth in find to 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

  1. Create a new directory under tools/<toolname>/
  2. Implement the tool following the pattern in CONTRIBUTING.md
  3. Register it in main.go:
    import _ "github.com/synseqack/aict/tools/toolname"
    
  4. Write tests in <toolname>_test.go

Output Requirements

All tools must:

  • Output valid XML with root element named after the tool
  • Include timestamp attribute (Unix epoch)
  • Support --xml, --json, --plain flags
  • Support AICT_XML=1 environment 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 gofmt for 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.