⚠️ Malware / Trojaner / VirenAndroid-Malware blockiert Google Play per VPN-Trick(24.08.2026 um 13:00 Uhr)
🪟 Windows TippsWindows 11: Falsche Defender-Warnungen und kaputte Mauszeiger(31.08.2026 um 11:58 Uhr)
🕵️ SicherheitslückenDropbox-Hack: Tausende Konten kompromittiert(02.09.2026 um 11:02 Uhr)
⚠️ Malware / Trojaner / VirenAtombomben-Frage trickst KI-Malware-Scanner aus(02.09.2026 um 12:46 Uhr)
🪟 Windows TippsMehr Sicherheit in Windows 11(03.09.2026 um 11:51 Uhr)
⚠️ Malware / Trojaner / VirenNeue Android-Malware schreit Sie an, wenn Sie nicht zahlen(11.09.2026 um 10:33 Uhr)
🐧 Linux TippsMehrere Probleme in freerdp2 (Fedora)(11.09.2026 um 23:24 Uhr)
🐧 Linux TippsMehrere Probleme in kamailio (Debian)(11.09.2026 um 23:28 Uhr)
🐧 Linux TippsAusführen beliebiger Kommandos in dokuwiki (Fedora)(11.09.2026 um 23:28 Uhr)
🐧 Linux TippsAusführen beliebiger Kommandos in python-asteval (Fedora)(11.09.2026 um 23:28 Uhr)
⚠️ Malware / Trojaner / VirenAndroid-Malware blockiert Google Play per VPN-Trick(24.08.2026 um 13:00 Uhr)
🪟 Windows TippsWindows 11: Falsche Defender-Warnungen und kaputte Mauszeiger(31.08.2026 um 11:58 Uhr)
🕵️ SicherheitslückenDropbox-Hack: Tausende Konten kompromittiert(02.09.2026 um 11:02 Uhr)
⚠️ Malware / Trojaner / VirenAtombomben-Frage trickst KI-Malware-Scanner aus(02.09.2026 um 12:46 Uhr)
🪟 Windows TippsMehr Sicherheit in Windows 11(03.09.2026 um 11:51 Uhr)
⚠️ Malware / Trojaner / VirenNeue Android-Malware schreit Sie an, wenn Sie nicht zahlen(11.09.2026 um 10:33 Uhr)
🐧 Linux TippsMehrere Probleme in freerdp2 (Fedora)(11.09.2026 um 23:24 Uhr)
🐧 Linux TippsMehrere Probleme in kamailio (Debian)(11.09.2026 um 23:28 Uhr)
🐧 Linux TippsAusführen beliebiger Kommandos in dokuwiki (Fedora)(11.09.2026 um 23:28 Uhr)
🐧 Linux TippsAusführen beliebiger Kommandos in python-asteval (Fedora)(11.09.2026 um 23:28 Uhr)

🔧 Programmierung 🕛 vor 2 Monaten 14 Min Lesezeit
0

Building M31A: A Terminal-Native AI Coding Agent That Ships, Not Just Suggests

↗ Quelle (dev.to)
🗣️ Stimme:
📑 Inhaltsübersicht

Most AI coding assistants are glorified autocomplete on steroids. They suggest code, maybe write a function or two, but leave you holding the bag when it comes to testing, verification, and actually shipping the changes.



M31A (M31 Autonomous) takes a different approach. It's a terminal-based AI coding agent written in Go that owns a six-phase workflow end-to-end: Initialize → Discuss → Plan → Execute → Verify → Ship. Every run ends with a verified git commit and a learning ledger entry. One static binary, zero telemetry, any POSIX shell.



In this post, I'll walk you through the architecture, design decisions, and technical highlights of this open-source project.






The Problem: AI Assistants That Don't Finish the Job



Here's the typical workflow with most AI coding tools:




  1. Ask the AI to write some code

  2. Copy-paste the suggestion into your editor

  3. Run tests manually

  4. Debug the inevitable issues

  5. Repeat until it works

  6. Commit the changes yourself



The AI "helped" with step 1, but you're still doing 80% of the work. And if something breaks three commits later? Good luck figuring out what the AI actually changed.



M31A flips this model. Instead of being a suggestion engine, it's an autonomous agent that:




  • Asks clarifying questions before planning

  • Generates a structured implementation plan

  • Executes tasks with proper dependency resolution

  • Runs verification (tests, syntax checks)

  • Commits verified changes to git

  • Records what it learned for future sessions






Architecture at a Glance



M31A is built with a clean six-layer architecture:




CODE
┌─────────────────────────────────────────────────────────────┐
│ TUI Layer (Bubble Tea) │
│ 29 screens, keyboard/mouse handling, streaming display │
└─────────────────────────────────────────────────────────────┘

┌─────────────────────────────────────────────────────────────┐
│ Workflow Engine │
│ Six-phase orchestration, LLM streaming, plan parsing │
└─────────────────────────────────────────────────────────────┘

┌─────────────────┼─────────────────┐
↓ ↓ ↓
┌──────────────┐ ┌──────────────┐ ┌────────────────────┐
│ Providers │ │ Tools │ │ Domain Packages │
│ OpenRouter │ │ Bash │ │ session, ledger │
│ Zen │ │ FileRead │ │ rollback, bisect │
│ Fallback │ │ FileWrite │ │ taskrunner │
│ │ │ Glob, Grep │ │ keychain │
└──────────────┘ └──────────────┘ └────────────────────┘
↓ ↓ ↓
┌─────────────────────────────────────────────────────────────┐
│ Infrastructure Layer │
│ git, config, tokens, codeintel, fileutil, logging │
└─────────────────────────────────────────────────────────────┘






The key insight? Separation of concerns at every level. The TUI doesn't know about LLM APIs. The workflow engine doesn't know about terminal rendering. The tools don't know about workflow phases.






The Six-Phase Workflow Engine



The heart of M31A is the workflow engine, implemented in internal/workflow/engine.go. Let's break down each phase:






Phase 1: Initialize



The agent detects your project type (Go, Python, Node, etc.), initializes git if needed, and creates a .m31a/ planning directory with:





  • PROJECT.md — project metadata


  • STATE.md — current workflow state


  • TASKS.md — task list (populated later)




CODE
// From internal/workflow/initialize.go
func (e *Engine) runInitialize(ctx context.Context) error {
// Detect project type, framework, language
project := e.detectProject()

// Initialize git repo if needed
if !e.git.IsRepository() {
e.git.Init()
}

// Create planning directory
os.MkdirAll(e.planningDir, 0755)

// Write PROJECT.md, STATE.md
e.writeProjectState(project)
}









Phase 2: Discuss



Before jumping into code, the agent asks clarifying questions via LLM streaming. This prevents the classic "I built exactly what you asked for, but not what you wanted" problem.



The discuss phase uses embedded prompt templates (loaded via //go:embed prompts/*.md) to guide the LLM toward asking useful questions about scope, constraints, and edge cases.






Phase 3: Plan



The agent generates a structured implementation plan in markdown format. A custom parser (internal/workflow/plan_parser.go) extracts:




  • Task titles and descriptions

  • Dependencies between tasks

  • Files that will be modified

  • Review notes and questions




CODE
// From internal/workflow/plan_parser.go
type Plan struct {
Title string
Tasks []Task
Questions []string
Notes string
}

type Task struct {
ID int
Action string
Description string
Files []string
Dependencies []int
}






The plan parser supports refinement with retry logic (max 3 retries, max 5 refinements) and classifies prompt complexity: trivial → simple → moderate → complex.






Phase 4: Execute



This is where the rubber meets the road. The task runner (pkg/taskrunner/runner.go) uses Kahn's algorithm for topological sorting to determine execution order:




CODE
// From pkg/taskrunner/runner.go
func (r *Runner) Schedule() ([][]int, error) {
// Build adjacency list and in-degree count
inDegree := make(map[int]int)
dependents := make(map[int][]int)

for _, t := range r.tasks {
for _, dep := range t.Dependencies {
inDegree[t.ID]++
dependents[dep] = append(dependents[dep], t.ID)
}
}

// Find all tasks with no dependencies
var queue []int
for _, t := range r.tasks {
if inDegree[t.ID] == 0 {
queue = append(queue, t.ID)
}
}

// Process tasks in topological order
var groups [][]int
for len(queue) > 0 {
groups = append(groups, queue)
var next []int
for _, id := range queue {
for _, dep := range dependents[id] {
inDegree[dep]--
if inDegree[dep] == 0 {
next = append(next, dep)
}
}
}
queue = next
}

return groups, nil
}






Tasks within a group can run with bounded parallelism (default: 4 concurrent tasks via semaphore). The executor includes a self-heal loop that retries recoverable failures up to 2 times.






Phase 5: Verify



The agent runs verification checks:




  • File existence validation

  • Syntax checking (language-specific)

  • Test execution

  • Smart file truncation for LLM context



If verification fails, the agent can rollback the commit chain using git-bisect integration.






Phase 6: Ship



The final phase:




  1. Creates a git commit with all verified changes

  2. Writes a ledger entry (cross-session learning record)

  3. Archives the session

  4. Generates a demonstration summary






Provider System: Multi-LLM with Automatic Fallback



M31A supports two LLM providers out of the box:





  • OpenRouter — primary gateway with access to Claude, GPT-4, etc.


  • Zen — secondary provider (OpenCode Zen)



The provider layer (internal/provider/) includes some clever engineering:






Automatic Fallback



When a provider degrades (429 rate limit, 503 service unavailable), M31A automatically switches to a healthy provider. The fallback logic uses parallel health checks to minimize latency:




CODE
// From internal/provider/fallback.go
func FindFallbackProvider(registry *Registry, current string) (string, *FallbackEvent, error) {
// Collect candidate providers
candidates := registry.ListAll()

// Parallel health checks (10s timeout)
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()

ch := make(chan result, len(candidates))
for _, c := range candidates {
go func(c candidate) {
status := c.provider.HealthCheck(ctx)
ch <- result{name: c.name, status: status}
}(c)
}

// Return first healthy provider in priority order
for i := 0; i < len(candidates); i++ {
r := <-ch
if r.status.Status == "live" || r.status.Status == "slow" {
registry.TrySetActive(r.name)
return r.name, &FallbackEvent{...}, nil
}
}
}









Model Arbitrage



M31A includes a model arbitrage system (pkg/arbitrage/) that automatically switches to the cheapest model that meets the task's capability threshold:




CODE
// From pkg/arbitrage/arbitrage.go
func (s *Scorer) Score(task Task) (ComplexityLevel, int) {
level := classifyText(task.Action, task.Description)

// Boost complexity when task touches many files
if len(task.Files) > 3 {
level = boostLevel(level, 1)
}

// Boost when task has many dependencies
if len(task.Dependencies) > 3 {
level = boostLevel(level, 1)
}

input, output := s.EstimateTokens(level, task)
return level, input + output
}






The scorer uses keyword analysis to classify tasks as simple, moderate, or complex, then recommends the cheapest model that can handle that complexity level.






Tool System: Deliberately Small, Aggressively Sandboxed



M31A ships with 5 core tools:





  1. Bash — shell command execution


  2. FileRead — read files with size limits (50MB max)


  3. FileWrite — atomic file writes (temp + rename)


  4. Glob — file pattern matching (doublestar, 1000 result limit)


  5. Grep — content search (ripgrep when available, pure-Go fallback)



The tool surface area is intentionally small. Each tool is aggressively sandboxed with:






Permission Gating



Every tool call is gated by a permission modal with configurable timeout (default 300s):




CODE
// From internal/tools/permissions.go
type PermissionMode string

const (
ModeAsk PermissionMode = "ask"
ModeAllowAll PermissionMode = "allow_all"
ModeDenyAll PermissionMode = "deny_all"
)

func (d *Dispatcher) RequestPermission(ctx context.Context, tool Tool, input ToolInput) error {
if d.mode == ModeAllowAll {
return nil
}

// Send permission request to TUI
ch := make(chan PermissionResponse)
d.emitter.Emit(PermissionRequestMsg{...})

// Wait for user response with timeout
select {
case resp := <-ch:
if !resp.Approved {
return ErrPermissionDenied
}
case <-time.After(d.timeout):
return ErrPermissionTimeout
}
}









Security Guards





  • Path traversal guards: symlink resolution + workDir prefix check


  • Output capping: MaxToolOutputChars (10,000) / BashOutputLimit (50,000)


  • SSRF protection: DNS pinning, TOCTOU prevention, redirect checking (WebFetch)


  • Process lifecycle: SIGINT/SIGKILL grace period, pipe cleanup






Risk Levels



Each tool declares its risk level:




CODE
type RiskLevel string

const (
RiskSafe RiskLevel = "safe"
RiskMedium RiskLevel = "medium"
RiskDangerous RiskLevel = "dangerous"
RiskDestructive RiskLevel = "destructive"
)






Bash is dangerous, FileWrite is medium, FileRead is safe. The permission system uses these levels to determine whether to prompt the user.






Cross-Session Learning Ledger



One of M31A's most interesting features is the cross-session learning ledger (pkg/ledger/). Every session writes a structured record to a markdown file:




CODE
| Session | Model | Tasks | Failed | Cost | Duration | Framework |
|---------|-------|-------|--------|------|----------|-----------|
| a1b2c3d4 | claude-3.5-sonnet | 5 | 1 | $0.12 | 8min | react |
| e5f6g7h8 | gpt-4-turbo | 3 | 0 | $0.08 | 4min | go |






The ledger tracks:




  • Session ID and timestamp

  • Model and provider used

  • Task count and failures

  • Cost estimate

  • Duration

  • Project type and framework

  • Goal keywords (with stop-word filtering)



Over time, the agent can query the ledger to learn from past sessions:




CODE
// From pkg/ledger/ledger.go
type LedgerStats struct {
TotalSessions int
AvgTaskCount float64
AvgCost float64
AvgDurationMinutes float64
TotalFailedTasks int
TopFailures []string
TopFrameworks []string
ByProjectType map[string]int
}






This creates a feedback loop where the agent gets sharper over time, learning which frameworks are common, what types of tasks fail, and how long things typically take.






AutoDream: Context Window Consolidation



Long conversations blow the context window. M31A solves this with AutoDream (pkg/autodream/), an automatic context consolidation system:




CODE
// From pkg/autodream/autodream.go
func (c *Consolidator) Consolidate() (ConsolidationResult, error) {
// Protect system prompts and recent messages
protected := c.protectedIndices()
candidates := c.candidateIndices(protected)

// Summarize oldest 50% of non-protected messages
midpoint := len(candidates) / 2
toCompress := candidates[:midpoint]

// Build summary prompt
summary := c.summarize(toCompress)

// Replace old messages with summary
c.messages = c.replaceWithSummary(toCompress, summary)

return ConsolidationResult{
MessagesRemoved: len(toCompress),
TokensSaved: c.estimateTokensSaved(toCompress, summary),
}
}






AutoDream triggers at 60% context usage by default. It uses role-sampled summarization (system prompts are never compressed) and preserves recent messages for continuity.






TUI: 29 Screens Built with Bubble Tea



The terminal UI is built with

  • Documentation:






  • - Research: https://github.com/eshanized/M31A/blob/master/RESEARCH.md



    Thanks to the Bubble Tea, Lip Gloss, and Glamour teams for making terminal UIs enjoyable to build. And thanks to everyone who has tried M31A and reported bugs — your feedback makes it better.

    Vollständiger Original-Bericht
    Ausführliche Details, Code-Beispiele & Hersteller-Stellungnahme auf dev.to.
    ↗ Original-Artikel auf dev.to lesen
    Wie bewertest du diesen Beitrag?
    1 Klick Feedback
    Teilen mit Netzwerk & Team:

    Community-Analysen & Experten-Meinungen 0

    Verfasse deine eigene Analyse, teile Workarounds oder diskutiere diesen Vorfall im Blog.
    Noch keine Community-Analyse verfasst. Markiere einen Textabschnitt oder klicke oben auf Eigene Analyse verfassen“!
    Community Pulse: Relevanz-Einschätzung
    1 Klick Experten-Votum
    🔴 Akute Relevanz 0%
    🟡 In Evaluierung 0%
    🟢 Keine Auswirkung 0%
    Spannende Innovation 0%
    Verwandte Story-Cluster & Quellen (Vektor-KI)
    Port 8095 Engine
    1 Quelle
    Stealing AI Reasoning Traces
    1 Quelle
    AIs as Modern Genies
    1 Quelle
    Bitcoin: KI-Hacker räumen Millionen ab! Wird Künstliche Intelligenz zum Problem? - ftd.de
    Ähnliche Beiträge
    🔍 Verwandte News

    Auch interessante Nachrichten Building M31A: A Terminal-Native AI Coding Agent That Ships, Not Just Suggests

    Thematisch verwandte Begriffe: Building, M31A, TerminalNative, Coding · 6 Treffer

    Laden...

    Videos werden geladen ...

    Laden...

    Beiträge werden geladen ...

    Laden...

    Videos werden geladen ...

    Laden...

    Beiträge werden geladen ...

    Laden...

    Videos werden geladen ...

    Laden...

    Beiträge werden geladen ...

    Laden...

    Videos werden geladen ...

    Laden...

    Beiträge werden geladen ...

    Laden...

    Videos werden geladen ...