amplifier-cli-skill

Skill for building CLI applications on the Amplifier platform. Teaches amplifier-foundation patterns as the source of truth for composable AI application development. Use when building CLI tools, understanding bundle composition, implementing sessions, or extending the Amplifier ecosystem.

$ Installer

git clone https://github.com/majiayu000/claude-skill-registry /tmp/claude-skill-registry && cp -r /tmp/claude-skill-registry/skills/development/amplifier-cli-skill ~/.claude/skills/claude-skill-registry

// tip: Run this command in your terminal to install the skill


name: amplifier-cli-skill description: Skill for building CLI applications on the Amplifier platform. Teaches amplifier-foundation patterns as the source of truth for composable AI application development. Use when building CLI tools, understanding bundle composition, implementing sessions, or extending the Amplifier ecosystem. compatibility: Requires Python 3.10+, uv package manager. Works with amplifier-foundation, amplifier-core, and related ecosystem packages. license: MIT metadata: author: Michael Jabbour version: "2.0" repository: https://github.com/michaeljabbour/amplifier-cli-skill

Building CLI Applications on Amplifier

Amplifier is Microsoft's composable AI agent framework that enables:

  • Bundle composition - Mix session orchestrators, hooks, tools, and providers
  • Module ecosystem - 36+ modules available (Module Catalog)
  • Provider flexibility - Claude, GPT, Gemini, local models, and more
  • Production-ready - Logging, observability, and hooks built-in

This skill teaches how to build CLI applications using amplifier-foundation patterns - the source of truth for Amplifier application development.

Amplifier Ecosystem

This skill is part of the broader Amplifier ecosystem:

Core Repositories

Reference Implementations

Reference Implementation

See amplifier-simplecli for a complete working example demonstrating these patterns in production. This terminal UI implementation showcases:

  • Full bundle composition workflow
  • Memory system integration (tool-memory, hooks-memory-capture, context-memory)
  • 14 pre-configured modules
  • Streaming UI with hooks-event-broadcast
  • Complete documentation structure

Key Resources:

Use it as a reference when building your own CLI applications.

Core Workflow

Every Amplifier application follows this fundamental pattern:

from amplifier_foundation import load_bundle

async def main():
    # 1. Load bundles
    foundation = await load_bundle("git+https://github.com/microsoft/amplifier-foundation@main")
    provider = await load_bundle("./providers/anthropic.yaml")  # Your provider config

    # 2. Compose bundles (later overrides earlier)
    composed = foundation.compose(provider)

    # 3. Prepare (resolves and downloads modules)
    prepared = await composed.prepare()

    # 4. Create session and execute
    async with await prepared.create_session() as session:
        response = await session.execute("Hello!")
        print(response)

This is the pattern. Everything else builds on this foundation.

Quick Start

# Install amplifier-foundation
pip install amplifier-foundation

# Or with uv
uv add amplifier-foundation

Example Bundles

Complete, production-ready bundle examples are available in references/EXAMPLES.md:

  • Base Bundle - Session orchestrator, memory system, hooks, and tools
  • Provider Configurations - Claude Opus 4.5, Sonnet 4.5, Haiku 4
  • Agent Bundle - Specialized bug-hunter agent example

These examples show real bundle structures you can copy and adapt. In your CLI app, organize them as:

your-cli/
โ”œโ”€โ”€ bundles/base.md
โ”œโ”€โ”€ providers/opus.yaml
โ””โ”€โ”€ agents/bug-hunter.md

What is a Bundle?

A Bundle is a composable configuration unit that produces a mount plan for sessions.

Bundle โ†’ compose() โ†’ prepare() โ†’ create_session() โ†’ execute()

Bundle Sections

SectionPurpose
bundleMetadata (name, version)
sessionOrchestrator and context manager
providersLLM backends
toolsAgent capabilities
hooksObservability and control (see references/HOOKS.md)
agentsNamed agent configurations
contextContext files to include
instructionSystem instruction (markdown body)

Bundle Format

Bundles are markdown files with YAML frontmatter:

---
bundle:
  name: my-app
  version: 1.0.0

session:
  orchestrator: {module: loop-streaming}
  context: {module: context-simple}

providers:
  - module: provider-anthropic
    source: git+https://github.com/microsoft/amplifier-module-provider-anthropic@main
    config:
      default_model: claude-sonnet-4-5

tools:
  - module: tool-filesystem
    source: git+https://github.com/microsoft/amplifier-module-tool-filesystem@main
---

You are a helpful assistant for software development.

Composition Pattern

Bundles compose with later overriding earlier:

result = base.compose(overlay)  # overlay wins on conflicts

Merge Rules

SectionRule
sessionDeep merge (nested dicts merged)
providersMerge by module ID
toolsMerge by module ID
hooksMerge by module ID
instructionReplace (later wins)

Common Composition Patterns

Base + Environment:

import os

base = await load_bundle("./bundles/base.md")
dev_overlay = await load_bundle("./bundles/dev.md")
prod_overlay = await load_bundle("./bundles/prod.md")

env = os.getenv("ENV", "dev")
overlay = dev_overlay if env == "dev" else prod_overlay
bundle = base.compose(overlay)

Feature Bundles:

filesystem = Bundle(name="fs", tools=[{"module": "tool-filesystem", "source": "..."}])
web = Bundle(name="web", tools=[{"module": "tool-web", "source": "..."}])

# Compose what you need
full = base.compose(filesystem).compose(web)

Includes Chain (Declarative):

# dev.md
bundle:
  name: dev
includes:
  - ./base.md
providers:
  - module: provider-anthropic
    config: {debug: true}

CLI Application Architecture

Use this blueprint for building CLI applications:

import asyncio
import logging
from dataclasses import dataclass
from pathlib import Path

from amplifier_foundation import Bundle, load_bundle


@dataclass
class AppConfig:
    """Application configuration."""
    provider_bundle: str = "anthropic-sonnet.yaml"
    api_key: str | None = None
    log_level: str = "INFO"

    @classmethod
    def from_env(cls) -> "AppConfig":
        return cls(
            provider_bundle=os.getenv("PROVIDER", "anthropic-sonnet.yaml"),
            api_key=os.getenv("ANTHROPIC_API_KEY"),
            log_level=os.getenv("LOG_LEVEL", "INFO"),
        )

    def validate(self) -> None:
        if not self.api_key:
            raise ValueError("ANTHROPIC_API_KEY not set")


class AmplifierApp:
    """Amplifier CLI application pattern."""

    def __init__(self, config: AppConfig):
        self.config = config
        self.session = None
        self.logger = logging.getLogger("amplifier_app")

    async def initialize(self) -> None:
        """Initialize: load bundles, compose, prepare, create session."""
        # Load foundation
        foundation = await load_bundle("git+https://github.com/microsoft/amplifier-foundation@main")

        # Load provider
        provider = await load_bundle(f"./providers/{self.config.provider_bundle}")

        # Add tools
        tools = Bundle(
            name="app-tools",
            tools=[
                {"module": "tool-filesystem", "source": "git+..."},
                {"module": "tool-bash", "source": "git+..."},
            ],
        )

        # Compose all bundles
        composed = foundation.compose(provider).compose(tools)

        # Prepare (downloads modules)
        prepared = await composed.prepare()

        # Create session
        self.session = await prepared.create_session()

    async def execute(self, prompt: str) -> str:
        """Execute a prompt."""
        if not self.session:
            raise RuntimeError("Session not initialized")
        return await self.session.execute(prompt)

    async def shutdown(self) -> None:
        """Graceful shutdown."""
        if self.session:
            await self.session.cleanup()

    async def __aenter__(self):
        await self.initialize()
        return self

    async def __aexit__(self, *args):
        await self.shutdown()


async def main():
    config = AppConfig.from_env()
    config.validate()

    async with AmplifierApp(config) as app:
        response = await app.execute("Hello!")
        print(response)


if __name__ == "__main__":
    asyncio.run(main())

Key Architectural Principles

  1. Configuration as Dataclass: Use @dataclass for config, load from env/files
  2. Initialize/Execute/Shutdown Lifecycle: Clean lifecycle management
  3. Context Manager Pattern: Use async with for automatic cleanup
  4. Composition Over Inheritance: Compose bundles, don't subclass

Session Patterns

Basic Session

bundle = await load_bundle("/path/to/bundle.md")
prepared = await bundle.prepare()

async with await prepared.create_session() as session:
    response = await session.execute("Hello!")

Multi-Turn Conversation

Session maintains context automatically:

async with await prepared.create_session() as session:
    await session.execute("My name is Alice")
    response = await session.execute("What's my name?")
    # Response knows about Alice

Resuming Sessions

# First session
async with await prepared.create_session() as session:
    await session.execute("Remember: X=42")
    session_id = session.session_id

# Later: resume
async with await prepared.create_session(session_id=session_id) as session:
    response = await session.execute("What is X?")
    # Knows X=42

Agent Delegation

Defining Agents

Example agent bundle structure (see references/EXAMPLES.md for complete example):

---
bundle:
  name: bug-hunter
  version: 1.0.0
  description: Finds and fixes bugs

providers:
  - module: provider-anthropic
    config:
      default_model: claude-sonnet-4-5
      temperature: 0.3
---

You are an expert bug hunter. Find bugs systematically.

Spawning Agents

# Load agent as bundle
agent_bundle = await load_bundle("./agents/bug-hunter.md")

# Spawn sub-session
result = await prepared.spawn(
    child_bundle=agent_bundle,
    instruction="Find the bug in auth.py",
    compose=True,            # Compose with parent bundle
    parent_session=session,  # Inherit UX from parent
)

print(result["output"])

@Mention System

Reference context files using @namespace:path syntax:

See @foundation:context/guidelines.md for guidelines.

How it works:

  1. During composition, each bundle's base_path is tracked by namespace
  2. PreparedBundle resolves @namespace:path references
  3. Content is loaded and included inline

Memory System (Custom Extension)

โš ๏ธ Note: The memory system is a custom community extension, NOT part of official amplifier-foundation.

Amplifier can be extended with persistent memory capabilities using three custom modules:

ModulePurpose
tool-memorySQLite storage with full-text search (FTS5)
hooks-memory-captureAutomatic observation capture from tool outputs
context-memoryProgressive disclosure context injection at session start

What Memory Enables

  • Persistent observations across sessions (bugfix, feature, discovery, decision)
  • Session tracking with structured summaries and next_steps
  • Automatic learning capture from tool outputs (no manual add_memory calls)
  • Full-text search across all memory content
  • Progressive disclosure for cost-effective context injection

Quick Setup

Add to your bundle:

session:
  memory_context:
    module: context-memory
    source: git+https://github.com/michaeljabbour/amplifier-module-context-memory@main

hooks:
  - module: hooks-memory-capture
    source: git+https://github.com/michaeljabbour/amplifier-module-hooks-memory-capture@main
    config:
      min_content_length: 50
      auto_summarize_interval: 10

tools:
  - module: tool-memory
    source: git+https://github.com/michaeljabbour/amplifier-module-tool-memory@main
    config:
      storage_path: ~/.amplifier/memories.db
      max_memories: 1000
      enable_fts: true
      enable_sessions: true

For complete setup, configuration options, and usage patterns, see references/MEMORY.md.

Philosophy

Mechanism, Not Policy

Foundation provides mechanism for bundle composition. It doesn't decide which bundles to use - those are policy decisions for your application.

Foundation (mechanism):          App (policy):
- load_bundle()                  - Search path order
- compose()                      - Well-known bundles
- prepare()                      - @user:, @project: shortcuts
- create_session()               - Environment-specific config

Text-First

  • Bundles are markdown (human-readable)
  • Configuration is YAML (diffable)
  • No binary formats

Composable

Small bundles compose into larger configurations. Prefer composition over complexity.

Deep Dive References

For detailed information on specific topics, see the references/ directory:

Architecture & Core Concepts

Building Applications

Extensions & Customization

Project Resources

These references provide comprehensive coverage beyond the quick start patterns in this document. Start here for overview, explore references for depth.

Reference: amplifier-app-cli

The amplifier-app-cli repository is a reference implementation. Learn from it, but understand it implements app-layer policy on top of foundation mechanisms:

App Policy (amplifier-app-cli)Foundation Mechanism
SessionStore (persistence)Session lifecycle
session_spawner (delegation)spawn() API
paths.py (search paths)load_bundle()
CLI commandsDirect API usage

When building your own CLI, start from the foundation patterns above, not from copying amplifier-app-cli internals.

Production Configuration Choices

When building production CLIs, leverage amplifier-foundation's native capabilities. The example bundles in references/EXAMPLES.md demonstrate production-ready choices.

Orchestrator: loop-streaming

Why loop-streaming over loop-events:

session:
  orchestrator:
    module: loop-streaming
    config:
      extended_thinking: true
      max_iterations: 25

Key benefits:

  • Parallel tool execution via asyncio.gather() prevents race conditions
  • Tool results are guaranteed complete before being added to context
  • Streaming support for better user experience
  • Production-tested in amplifier-app-cli

Foundation provides:

  • loop-basic - Sequential execution (simple, deterministic)
  • loop-events - Event-driven with hooks (vulnerable to race conditions)
  • loop-streaming - Parallel execution with determinism (production choice)

Context Manager: context-simple

Why context-simple over context-persistent:

session:
  context:
    module: context-simple
    config:
      max_tokens: 200000
      compact_threshold: 0.8
      auto_compact: true

Key benefits:

  • In-memory context management (fast startup)
  • Automatic compaction when approaching token limits
  • Same compaction logic as context-persistent
  • Perfect for stateless CLI sessions

Foundation provides:

  • context-simple - In-memory with auto-compaction (CLI default)
  • context-persistent - File-based memory loading (for long-term memory)
  • context-memory - Advanced memory patterns

When to use persistent: If your CLI needs to load previous conversation history at startup (rare for CLI tools).

Safety Hooks from Foundation Ecosystem

The example bundle includes production safety hooks from amplifier-foundation:

hooks:
  # Approval for dangerous operations
  - module: hooks-approval
    source: git+https://github.com/microsoft/amplifier-module-hooks-approval@main
    config:
      auto_approve: false
      dangerous_patterns: ["rm -rf", "sudo", "DROP TABLE", ...]

  # Automatic file backups
  - module: hooks-backup
    source: git+https://github.com/microsoft/amplifier-module-hooks-backup@main

  # Git and datetime awareness
  - module: hooks-status-context
    source: git+https://github.com/microsoft/amplifier-module-hooks-status-context@main
    config:
      include_datetime: true
      include_git_status: true

Note: Cost tracking hooks are not currently available in amplifier-foundation. Monitor token usage through the hooks-streaming-ui module's token display feature.

Validation: amplifier-app-cli uses the same foundation modules, proving production viability.

Common Development Tasks

Adding a New Provider

  1. Create providers/my-provider.yaml:

    bundle:
      name: my-provider
    providers:
      - module: provider-openai
        source: git+https://github.com/microsoft/amplifier-module-provider-openai@main
        config:
          default_model: gpt-4o
    
  2. Load and compose:

    provider = await load_bundle("./providers/my-provider.yaml")
    composed = foundation.compose(provider)
    

Adding Tools

Compose a tools bundle:

tools = Bundle(
    name="my-tools",
    tools=[
        {"module": "tool-filesystem", "source": "git+..."},
        {"module": "tool-bash", "source": "git+...", "config": {"allowed_commands": ["ls", "cat"]}},
    ],
)
composed = base.compose(tools)

Testing with Mock Provider

test_bundle = Bundle(
    name="test",
    providers=[{
        "module": "provider-mock",
        "source": "git+https://github.com/microsoft/amplifier-module-provider-mock@main",
        "config": {"responses": ["Hello!", "How can I help?"]},
    }],
)

Validation

from amplifier_foundation import load_bundle

bundle = await load_bundle(path)
prepared = await bundle.prepare()  # Activates modules (may download/install)

Error Handling

from amplifier_foundation import (
    load_bundle,
    BundleNotFoundError,
    BundleLoadError,
    BundleValidationError,
)

try:
    bundle = await load_bundle(path)
except BundleNotFoundError:
    print(f"Bundle not found: {path}")
except BundleLoadError as e:
    print(f"Failed to load: {e}")
except BundleValidationError as e:
    print(f"Invalid bundle: {e}")

Building a Complete CLI Application

Detailed guide available: For comprehensive build patterns, implementation details, and best practices, see references/BUILD_PATTERNS.md.

This section demonstrates how to build a full-featured CLI using foundation patterns. Following these patterns results in significantly less code (~85% reduction) compared to implementing session management, bundle loading, and configuration merging yourself.

Example Architecture

your-cli/
โ”œโ”€โ”€ your_cli/
โ”‚   โ”œโ”€โ”€ app.py                  Core app class encapsulating foundation workflow
โ”‚   โ”œโ”€โ”€ config.py               Configuration dataclass
โ”‚   โ”œโ”€โ”€ session_manager.py      Session metadata storage (not state!)
โ”‚   โ”œโ”€โ”€ project.py              Project detection logic
โ”‚   โ”œโ”€โ”€ main.py                 CLI entry point (Typer/Click/argparse)
โ”‚   โ””โ”€โ”€ commands/               Command implementations
โ”œโ”€โ”€ providers/                   Provider bundles
โ”œโ”€โ”€ bundles/                     Base bundles
โ””โ”€โ”€ agents/                      Agent bundles

Core Pattern: AmplifierApp Class

The AmplifierApp class encapsulates the foundation workflow:

class AmplifierApp:
    """Foundation-based CLI application."""

    def __init__(self, config: Config):
        self.config = config
        self.session: Optional[Session] = None
        self.prepared: Optional[PreparedBundle] = None

    async def initialize(self) -> None:
        """Initialize: load โ†’ compose โ†’ prepare โ†’ session."""
        bundles = []

        # 1. Load foundation
        foundation = await load_bundle(
            "git+https://github.com/microsoft/amplifier-foundation@main"
        )
        bundles.append(foundation)

        # 2. Load provider
        provider = await load_bundle(self.config.provider)
        bundles.append(provider)

        # 3. Load config files (configs ARE bundles!)
        global_config = Path.home() / ".amplifier" / "config.yaml"
        if global_config.exists():
            bundles.append(await load_bundle(str(global_config)))

        # 4. Load project config
        if self.config.project_root:
            project_config = find_project_config(self.config.project_root)
            if project_config:
                bundles.append(await load_bundle(str(project_config)))

        # 5. Compose all (later wins)
        composed = bundles[0]
        for bundle in bundles[1:]:
            composed = composed.compose(bundle)

        # 6. Prepare and create session
        self.prepared = await composed.prepare()
        self.session = await self.prepared.create_session()

    async def execute(self, prompt: str) -> str:
        """Execute prompt against session."""
        return await self.session.execute(prompt)

    async def spawn_agent(self, agent_path: str, instruction: str) -> dict:
        """Spawn agent sub-session."""
        agent_bundle = await load_bundle(agent_path)
        return await self.prepared.spawn(
            child_bundle=agent_bundle,
            instruction=instruction,
            compose=True,
            parent_session=self.session,
        )

Key Insights:

  • Direct foundation API usage (no wrappers)
  • Config files ARE bundles (reuse mechanism)
  • Composition handles all merging
  • Foundation manages session state
  • App layer just orchestrates composition order

Configuration System

Simple dataclass with environment variable support:

@dataclass
class Config:
    """Application configuration."""

    provider: str = field(
        default_factory=lambda: os.getenv(
            "AMPLIFIER_PROVIDER",
            "./providers/anthropic.yaml"
        )
    )
    bundle: Optional[str] = field(
        default_factory=lambda: os.getenv("AMPLIFIER_BUNDLE")
    )
    bundles: list[str] = field(default_factory=list)
    project_root: Optional[Path] = None

    @classmethod
    def from_env_and_project(cls) -> "Config":
        """Load from env + project detection."""
        config = cls()
        manager = ProjectManager()
        config.project_root = manager.get_project_root()
        return config

No custom config merging needed - bundle composition handles it!

Project Detection

Lightweight project management (~50 lines vs app-cli's complex multi-project system):

class ProjectManager:
    def get_project_root(self) -> Path:
        """Find project root (.git or .amplifier directory)."""
        cwd = Path.cwd()
        for parent in [cwd, *cwd.parents]:
            if (parent / ".git").exists() or (parent / ".amplifier").exists():
                return parent
            if parent == Path.home():
                return cwd
        return cwd

    def get_project_id(self, project_root: Path) -> str:
        """Generate stable project ID from path."""
        return hashlib.md5(str(project_root.resolve()).encode()).hexdigest()[:8]

    def get_project_storage(self, project_root: Path) -> Path:
        """Get project-specific storage directory."""
        project_id = self.get_project_id(project_root)
        storage = Path.home() / ".amplifier" / "projects" / project_id
        storage.mkdir(parents=True, exist_ok=True)
        return storage

Session Metadata (Not State!)

Critical distinction: We store metadata ONLY (for user reference), not session state.

class SessionManager:
    """Lightweight session metadata storage."""

    def save_session_metadata(self, session_id: str, metadata: dict):
        """Save session metadata."""
        path = self.storage / f"{session_id}.json"
        metadata.update({
            "session_id": session_id,
            "created_at": datetime.now().isoformat(),
        })
        path.write_text(json.dumps(metadata, indent=2))

    def list_sessions(self) -> list[dict]:
        """List all sessions."""
        sessions = []
        for path in self.storage.glob("*.json"):
            sessions.append(json.loads(path.read_text()))
        return sorted(sessions, key=lambda s: s["created_at"], reverse=True)

50 lines vs app-cli's SessionStore: 479 lines (90% reduction)

Why so much smaller?

  • Foundation handles session state during lifetime
  • We only store metadata for user to remember context
  • No atomic operations, backup files, corruption recovery
  • "Resumption" = showing user past context, not restoring state

CLI Commands with Typer

Modern CLI framework with auto-completion and rich output:

import typer
from rich.console import Console

app = typer.Typer()
console = Console()

@app.command()
def run_prompt(
    prompt: str = typer.Argument(...),
    provider: Optional[str] = typer.Option(None, "--provider", "-p"),
    bundle: Optional[list[str]] = typer.Option(None, "--bundle", "-b"),
):
    """Execute a single prompt."""
    asyncio.run(_execute_prompt(prompt, provider, bundle))

async def _execute_prompt(prompt: str, provider: Optional[str], bundles: Optional[list[str]]):
    config = Config.from_env_and_project()
    if provider:
        config.provider = provider
    if bundles:
        config.bundles = list(bundles)

    async with AmplifierApp(config) as app:
        response = await app.execute(prompt)
        console.print(Markdown(response))

REPL Mode

Interactive mode using prompt_toolkit:

async def repl_mode():
    """Interactive REPL mode."""
    config = Config.from_env_and_project()
    config.validate()

    async with AmplifierApp(config) as app:
        history_file = Path.home() / ".amplifier" / "repl_history"
        prompt_session = PromptSession(
            history=FileHistory(str(history_file)),
            multiline=True,
        )

        while True:
            try:
                user_input = await prompt_session.prompt_async(">>> ")
                if user_input.lower() in ("exit", "quit"):
                    break

                response = await app.execute(user_input)
                console.print(Markdown(response))

            except KeyboardInterrupt:
                continue
            except EOFError:
                break

Foundation maintains context across all REPL inputs automatically!

Agent Commands

Direct spawn() usage (no wrapper needed):

@app.command(name="run")
def run_agent(
    agent: str = typer.Argument(...),
    instruction: str = typer.Argument(...),
    resume: Optional[str] = typer.Option(None, "--resume"),
):
    """Spawn an agent."""
    asyncio.run(_spawn_agent(agent, instruction, resume))

async def _spawn_agent(agent: str, instruction: str, resume_id: Optional[str]):
    config = Config.from_env_and_project()
    async with AmplifierApp(config) as app:
        agent_path = _resolve_agent_path(agent)
        result = await app.spawn_agent(
            agent_path,
            instruction,
            session_id=resume_id  # None = new, or ID to resume
        )
        console.print(Markdown(result["output"]))

Foundation handles agent resumption natively!

Bundle Commands

Inspect bundles using foundation's Bundle object:

@app.command()
def inspect(bundle_path: str):
    """Inspect bundle contents."""
    asyncio.run(_inspect_bundle(bundle_path))

async def _inspect_bundle(bundle_path: str):
    bundle = await load_bundle(bundle_path)

    console.print(f"{bundle.name} v{bundle.version}")
    if bundle.providers:
        console.print("\nProviders:")
        for p in bundle.providers:
            console.print(f"  โ€ข {p['module']}")

    if bundle.tools:
        console.print("\nTools:")
        for t in bundle.tools:
            console.print(f"  โ€ข {t['module']}")

Bundle object is self-documenting - no custom introspection needed!

Code Reduction Breakdown

ComponentWithout FoundationWith FoundationReduction
Session management~800+ lines~50 lines (metadata only)~94%
Bundle loading~700+ lines0 (foundation)100%
Mention loading~450+ lines0 (foundation)100%
Config system~700+ lines~100 lines (simple dataclass)~86%
Commands~6,000+ lines~800 lines~87%
Total~10,000+~1,500~85%

Key Takeaways

Do:

  • Use foundation APIs directly
  • Let config files BE bundles
  • Store only session metadata, not state
  • Use bundle composition for all merging
  • Leverage foundation features (mentions, caching, hooks)

Don't:

  • Wrap foundation APIs (spawner, session store)
  • Create custom config merging
  • Try to persist session state
  • Reimplement foundation features
  • Copy app-cli internals

Pattern Summary:

Your CLI โ†’ AmplifierApp โ†’ foundation workflow
            โ†“
         load โ†’ compose โ†’ prepare โ†’ session โ†’ execute
            โ†‘
      All heavy lifting done by foundation

The app layer is truly thin (typically ~1,500 LOC) - just CLI commands, config search, and metadata tracking.

File Reference

See references/ARCHITECTURE.md for foundation architecture.

External Documentation