stacklok / stacklok/mecatl

epic: agent-config portability — onboard an arriving Claude Code / Codex user, and let them leave as cheaply as they arrived

Open
#410 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

devex enhancement ux
Dominant language
Go
Stars
152
Forks
16
Avg merge
14h 48m
Merged PRs (30d)
536

Description

epic: agent-config portability — onboard an arriving Claude Code / Codex user, and let them leave as cheaply as they arrived

Tracking issue. The work is in the sub-issues; this issue owns the shared map, the boundaries
every sub-issue inherits, and the sequencing.

Why

Codex's desktop app ships one-click "Import other agent setup": scan for a Claude Code
install, preview, cherry-pick ~8 categories, convert (CLAUDE.mdAGENTS.md,
settings.jsonconfig.toml, skills re-homed under .codex/, 30 days of sessions,
subagents → .codex/agents/*.toml), then open a follow-up agent thread for whatever the
importer refused. It works because it attacks the actual adoption blocker: sunk cost. A
team with two quarters of skills, agent defs, rules and permission policy hears "try another
harness" as "re-derive all of that."

Mecatl's position is structurally different and we currently fail to say so: Codex's importer
converts and takes ownership, so the switching cost merely moves. Mecatl reads .claude/**
natively, in place, without conversion — so a user can run Claude Code and mecatl against
the same repo, same rules, same skills, in parallel and indefinitely. Evaluation is nearly
free and adoption is reversible. What's missing is not capability; it's the front door and an
honest account of what we do and don't honour.

The shared map — what mecatl does with a Claude Code setup today

Every sub-issue works against this table; corrections here propagate down.

Artefact Status today Where
CLAUDE.md at workspace root active (AGENTS.md fallback) engine/prompt/builder.go:306-324
nested CLAUDE.md, @path imports not loaded (root-only) same
.claude/rules/** active engine/adapter/rulesfs/resolve.go (#329)
.claude/agents/*.md active (--agents-conventional, on) cmd/mecated/main.go:1455 (#328)
.claude/skills/*/SKILL.md supported, off by default (--skills-conventional) cmd/mecated/main.go:1448
.claude/commands/*.md supported, off by default (--enable-commands) cmd/mecated/main.go:1470-1471
.claude/settings{,.local}.json permissions supported, off by default and lossy (--import-claude-permissions) main.go:1491, permconfig/resolve.go:29-43
~/.claude/projects/**/*.jsonl sessions one at a time, path supplied by hand cmd/mecated/import.go, ADR 0099
Claude settings.json hooks no config lane — global hooks are Go-composed, rebuild required; only agent-def hooks: is config-driven docs/usage/hooks.md
mcpServers (.mcp.json, ~/.claude.json) stdio unsupported by design — streamable-HTTP only, so most real blocks cannot transfer engine/adapter/agentfs/discover.go:121
plugins / marketplace not supported

Two consequences worth stating plainly, because they bound the whole epic: three of the most
valuable categories are already live with no action at all
(instructions, rules, agent defs),
and two categories genuinely do not carry over (global hooks, stdio MCP). Any pitch that
implies otherwise is false; the product move is to report them honestly.

Boundaries every sub-issue inherits

  • Read .claude/** in place; do not convert it. .claude/** stays authoritative.
    Copying into .mecatl/** is always opt-in, never the default. Nothing is rewritten into a
    mecatl dialect.
  • No secrets move. Env values, tokens and API keys are never copied and never printed;
    MCP servers needing credentials are reported, with the field blank.
  • Offline. No provider credential, no running daemon, no network (ADR 0099's posture).
  • ADR 0099's guards stay load-bearing wherever files are written: no overwrite, no symlink
    follow, no .git, name sanitization, destination confinement.
  • Trust posture untouched. Imported project ALLOW rules still require --trust-project;
    skills/agents/commands remain the same trust class as AGENTS.md. Migration must not become
    the hole unreviewed model-steering content arrives through.
  • settings.local.* is personal. A *.local source never contributes to a shared,
    checked-in destination.

Sub-issues

  • A (#411) — mecated migrate --scan-only: read-only inventory and report. Renders the table
    above for this configuration in three buckets (active now / available, needs this flag /
    not supported, with a reason). Writes nothing. Ships most of the onboarding win at
    essentially zero risk, and is a prerequisite for D.
  • B (#412) — batch session discovery over ~/.claude/projects/**. Blocked on four decisions
    named in the issue: dedup key (sessionId, not the path hash at import.go:184),
    skip-existing instead of the hard error at import.go:209, what to do with dropped
    isCompactSummary records (transcript.go:155), and a context bound on resume. Carries the
    plaintext-duplication and retention-off-by-default requirements.
  • C (#413) — reviewable permissions artefact. The runtime import's lossy demotions exist only in
    stderr today, which no policy owner can sign off. One generated file, in one lane, every
    demotion commented with its reason.
  • D (#414) — mecatui zero-state /migrate overlay. Renders A's report at the point of arrival,
    optionally seeding a follow-up session with the "not supported" list so the agent finishes
    the job conversationally (Codex's follow-up-thread trick). After A.
  • E (#415) — export: emit mecatl artefacts in Claude-Code-shaped layout. The credibility item.
    mecated import exists; nothing goes the other way, so "adoption is reversible" is not yet
    an honest claim. Two-way portability is what makes the portability argument provable rather
    than rhetorical.

Sequencing

A (#411) first, alone. B (#412) and C (#413) carry real design questions and must not ride along on a read-only
reporting change. D (#414) after A. E (#415) is independent and is the item that makes the parent claim
true — schedule it, don't let it drift.

Out of scope for the epic

Reversing the HTTP-only MCP stance, or adding a config lane for global hooks. Both are
prerequisites someone might infer from "migrate everything" — they are separate architectural
decisions, and until they're made, A reports those categories as unsupported with the reason.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Begin with sub-issue #411, since the epic sequences the read-only scan first. Read the shared artefact map and the referenced paths in engine/prompt/builder.go, engine/adapter/rulesfs/resolve.go, cmd/mecated/main.go, and internal/adapter/permconfig/resolve.go. Done means a scan-only inventory reports active, available, flag-dependent, and unsupported categories without writing files.

Written by the indexing model from the issue text.

Assessment

Tech stack
go
Domain
cli, developer-experience
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.