# Implementation Plan: REQ-006 -- Bundle Marketplace Content in CLI Package

## Domain Research

- **Problem type:** Packaging / data bundling / format migration
- **Approaches evaluated:**
  1. **`importlib.resources`** -- standard library, recommended for accessing non-Python files in packages. Uses `importlib.resources.files("ievo.marketplace")` to get package paths. Pro: standard. Con: returns `Traversable`, not `Path`, requires `.joinpath()` pattern, more complex for directory traversal.
  2. **`Path(__file__).parent` pattern** -- already used in `init.py:169` and `run.py:172`. Pro: simple, consistent with existing codebase, returns real `Path` objects, works for both installed packages and editable installs. Con: technically not the "recommended" approach, but perfectly reliable for wheel-installed packages.
- **Recommended approach:** `Path(__file__).parent` -- matches existing codebase pattern, simpler API, and all callers already expect `Path` objects.
- **Libraries/tools:** No new dependencies. Uses `yaml` (pyyaml, already installed), `pathlib`, `shutil.copy2`.
- **Known pitfalls:**
  - hatchling includes non-Python files in package directories by default when using `packages = ["src/ievo"]` -- confirmed in `pyproject.toml:55`. No config changes needed.
  - YAML frontmatter parsing: must handle `---` delimiters correctly. Simple approach: split on `---`, parse second section as YAML.
  - The `IEVO.md` template in marketplace is the NEW version (richer, with reports/ and more sections) vs the old `src/ievo/templates/IEVO.md`. The marketplace version should replace the old one.

## Architecture Assessment

- **Fits existing architecture:** Yes -- new `src/ievo/marketplace/` package is a data access layer, consistent with existing patterns.
- **New patterns introduced:**
  - Bundled content access via `ievo.marketplace` module
  - YAML frontmatter parsing for `.md` agent files
  - `CLAUDE_AGENTS_DIR` constant alongside existing `AGENTS_DIR`
- **Modules affected:** `config.py`, `registry.py`, `add.py`, `init.py`, `init_templates.py`, `remove.py`, `list_cmd.py`, `tui/app.py` (read path only -- NOT in scope for this REQ per negative AC), `conftest.py`
- **Risk level:** Medium -- touches many files but changes are mechanical (format migration)
- **Estimated complexity:** L (multiple sub-tasks, many files)

## Important scope notes

- **TUI (`app.py`)**, **`run.py`**, **`orchestrate.py`**, **`deps.py`**, **`learn.py`** all use `AgentPackage` and/or `AGENTS_DIR`. These are NOT in scope for REQ-006. The REQ says "Does NOT break existing tests -- backwards compatible with old format where needed." These modules continue reading the old `agents/` directory format. Migration of `tui/app.py`, `run.py`, etc. is a separate future REQ.
- `remove.py` dependency check reads `agents/<other>/agent.yaml` -- this must also support checking `.claude/agents/<other>.md` for dependencies, but the format mismatch is significant (old format has `dependencies` in YAML, new `.md` format does not have explicit dependency field). For now, keep the old check AND add the new `.claude/agents/` path removal. Dependency checking stays on old format only.
- `_print_dep_summary()` in `add.py` reads `AgentPackage` -- it gets removed since `add` is deprecated and the new `.md` format has no MCP/plugin fields.

## Pre-conditions

- [x] REQ-000 must be implemented (dependency) -- confirmed implemented
- [x] Git workflow confirmed in DECISIONS.md (D-012: trunk-based, `feat/REQ-006-bundle-marketplace`)
- [ ] Marketplace repo accessible at `../marketplace/` relative to CLI repo -- confirmed present

## Sub-tasks

### TASK-006-1: Copy marketplace content + write access layer + tests
**Scope:** Create `src/ievo/marketplace/` package with all bundled content files and Python access layer.
**Tests:** ~12 (path exists, YAML parses, list functions, frontmatter parsing)
**Files:** create `src/ievo/marketplace/__init__.py`, `src/ievo/marketplace/registry.yaml`, `src/ievo/marketplace/agents/*.md` (12), `src/ievo/marketplace/templates/*.md` (7), `src/ievo/marketplace/skills/evo/SKILL.md`, `src/ievo/marketplace/skills/backlog/SKILL.md`, `tests/test_marketplace.py`

### TASK-006-2: Update config.py + registry.py + tests
**Scope:** Add `CLAUDE_AGENTS_DIR`, update `RegistryEntry` fields, extract `_parse_registry_file()`, update `load_cached()` with bundled fallback, add `download_agent_md()`.
**Depends on:** TASK-006-1
**Tests:** ~10 (new fields, bundled fallback, parse classmethod, download_agent_md)
**Files:** modify `src/ievo/core/config.py`, `src/ievo/core/registry.py`, `tests/test_registry.py`, `tests/conftest.py`

### TASK-006-3: Rewrite add.py for new format + tests
**Scope:** Deploy `.md` files to `.claude/agents/`, source priority: bundled -> GitHub -> placeholder. Remove `_create_local_agent()`, `_print_dep_summary()`, `AgentPackage`/`McpType` imports.
**Depends on:** TASK-006-2
**Tests:** ~8 (bundled deploy, GitHub fallback, placeholder, deps resolution, deprecation)
**Files:** modify `src/ievo/commands/add.py`, `tests/test_add.py`

### TASK-006-4: Update init.py + remove template constants + tests
**Scope:** Deploy templates from bundled content. Create `.claude/agents/` dir. Remove `SPEC_REQ_TEMPLATE`/`SPEC_CR_TEMPLATE` from `init_templates.py`. Deploy additional bundled templates (QUESTION, PLAN, ACCEPTANCE, PROPOSAL).
**Depends on:** TASK-006-1
**Tests:** ~8 (template deployment, bundled content, dir creation)
**Files:** modify `src/ievo/commands/init.py`, `src/ievo/commands/init_templates.py`, `tests/test_init.py`, `tests/test_templates.py`

### TASK-006-5: Update remove.py + list_cmd.py + cleanup + tests
**Scope:** Remove `.claude/agents/<name>.md` in remove. Read frontmatter in list. Delete `src/ievo/templates/IEVO.md`. Update `conftest.py` fixtures.
**Depends on:** TASK-006-2
**Tests:** ~8 (new-format removal, legacy removal, frontmatter reading, fallback)
**Files:** modify `src/ievo/commands/remove.py`, `src/ievo/commands/list_cmd.py`, `tests/test_remove.py`, `tests/test_list.py`, delete `src/ievo/templates/IEVO.md`, modify `tests/conftest.py`

### TASK-006-6: Final integration + wheel verification
**Scope:** Full test suite run, coverage check, pre-commit, wheel build verification.
**Depends on:** TASK-006-1 through TASK-006-5
**Tests:** 0 new (full suite re-run + wheel check)
**Files:** none new

---

## Micro-Steps

### TASK-006-1: Copy marketplace content + write access layer

#### Step 1: Copy marketplace content files into package

- **Tests to write:** None (file copy, tested in Step 2)
- **Criteria covered:** AC-1 (files exist)
- **Files:**
  - Create `src/ievo/marketplace/` directory
  - Copy `../marketplace/registry.yaml` -> `src/ievo/marketplace/registry.yaml`
  - Copy `../marketplace/agents/*.md` (12 files) -> `src/ievo/marketplace/agents/`
  - Copy `../marketplace/templates/*.md` (7 files) -> `src/ievo/marketplace/templates/`
  - Copy `../marketplace/skills/evo/SKILL.md` -> `src/ievo/marketplace/skills/evo/SKILL.md`
  - Copy `../marketplace/skills/backlog/SKILL.md` -> `src/ievo/marketplace/skills/backlog/SKILL.md`
- **Commit:** `REQ-006: step 1 - copy marketplace content into package`

#### Step 2: Write marketplace `__init__.py` access layer + tests

- **Tests to write (in `tests/test_marketplace.py`):**
  1. `test_bundled_registry_path_exists()` -- path is a real file
  2. `test_bundled_agent_path_exists()` -- returns Path for "spec-writer", None for "nonexistent"
  3. `test_bundled_template_path_exists()` -- returns Path for "IEVO.md", None for "nonexistent"
  4. `test_bundled_skill_path_exists()` -- returns Path for "evo", None for "nonexistent"
  5. `test_list_bundled_agents()` -- returns 12 agent names
  6. `test_list_bundled_templates()` -- returns 7 template names
  7. `test_load_bundled_registry()` -- returns dict with "agents" key, 12 entries
  8. `test_load_bundled_registry_has_required_fields()` -- each agent has name, version, file, description
  9. `test_bundled_agent_files_have_valid_frontmatter()` -- each `.md` file has valid YAML between `---` delimiters
  10. `test_bundled_agent_path_returns_none_for_missing()` -- "nonexistent" -> None
  11. `test_bundled_template_path_returns_none_for_missing()` -- "FAKE.md" -> None
  12. `test_bundled_skill_path_returns_none_for_missing()` -- "fake-skill" -> None
- **Criteria covered:** AC-1 (all functions, all content accessible, YAML parses)
- **Files:**
  - Create `src/ievo/marketplace/__init__.py` with functions:
    - `bundled_registry_path() -> Path` -- `_PKG_DIR / "registry.yaml"`
    - `bundled_agent_path(name: str) -> Path | None` -- `_PKG_DIR / "agents" / f"{name}.md"` if exists, else None
    - `bundled_template_path(filename: str) -> Path | None` -- `_PKG_DIR / "templates" / filename` if exists, else None
    - `bundled_skill_path(skill_name: str) -> Path | None` -- `_PKG_DIR / "skills" / skill_name / "SKILL.md"` if exists, else None
    - `list_bundled_agents() -> list[str]` -- stems of all `.md` files in agents/
    - `list_bundled_templates() -> list[str]` -- names of all `.md` files in templates/
    - `load_bundled_registry() -> dict` -- `yaml.safe_load(bundled_registry_path().read_text())`
    - `parse_agent_frontmatter(path: Path) -> dict` -- parse YAML between first two `---` lines, return dict. Utility used by list_cmd and future callers.
  - Where `_PKG_DIR = Path(__file__).parent`
  - Create `tests/test_marketplace.py`
- **Commit:** `REQ-006: step 2 - marketplace access layer + tests`

---

### TASK-006-2: Update config.py + registry.py

#### Step 3: Add `CLAUDE_AGENTS_DIR` to config.py

- **Tests to write:** 1 test in existing `tests/test_config.py`:
  1. `test_claude_agents_dir_constant()` -- assert `CLAUDE_AGENTS_DIR == ".claude/agents"`
- **Criteria covered:** AC-3 (prerequisite: new path constant)
- **Files:** modify `src/ievo/core/config.py` -- add `CLAUDE_AGENTS_DIR = ".claude/agents"` after `AGENTS_DIR`
- **Commit:** `REQ-006: step 3 - add CLAUDE_AGENTS_DIR constant`

#### Step 4: Update RegistryEntry + extract `_parse_registry_file()` + bundled fallback + `download_agent_md()`

- **Tests to write (update `tests/test_registry.py`):**
  1. `test_registry_entry_new_fields()` -- `RegistryEntry` with `file` and `mandatory` fields, verify defaults
  2. `test_registry_entry_backwards_compatible()` -- old-style entry without `file`/`mandatory` still works
  3. `test_parse_registry_file()` -- call `Registry._parse_registry_file(path)` with a temp YAML, verify parsed Registry
  4. `test_parse_registry_file_new_fields()` -- verify `file` and `mandatory` are parsed into RegistryEntry
  5. `test_load_cached_falls_back_to_bundled()` -- no cache file, patch bundled path, verify returns bundled entries
  6. `test_load_cached_prefers_cache_over_bundled()` -- cache file exists, verify cache is used (not bundled)
  7. `test_download_agent_md_success()` -- mock httpx, verify `.md` file written to dest_dir
  8. `test_download_agent_md_not_in_registry()` -- returns False for unknown agent
  9. `test_download_agent_md_http_error()` -- httpx raises, returns False
- **Criteria covered:** AC-2 (all sub-criteria)
- **Files:**
  - Modify `src/ievo/core/registry.py`:
    - Add `file: str = ""` and `mandatory: bool = False` to `RegistryEntry` dataclass
    - Extract `@classmethod _parse_registry_file(cls, path: Path) -> Registry` -- shared YAML parsing logic (reads file, iterates agents list, builds `RegistryEntry` objects with new fields)
    - Update `load_cached()`: try cache first -> if missing, try `bundled_registry_path()` from `ievo.marketplace` -> if both missing, return empty. Use `_parse_registry_file()` for both.
    - Update `fetch()` to use `_parse_registry_file()` (write to cache, then parse)
    - Add `async download_agent_md(self, name: str, dest_dir: Path) -> bool` -- downloads single `.md` file: `entry = self.get(name)`, URL = `{MARKETPLACE_RAW}/{entry.file}` (or fallback `{MARKETPLACE_RAW}/agents/{name}.md`), write to `dest_dir / f"{name}.md"`. Returns True on success, False on error.
  - Modify `tests/test_registry.py` -- add new tests, update existing tests to include `file`/`mandatory` fields in sample data
  - Modify `tests/conftest.py` -- update `SAMPLE_REGISTRY_YAML` to include `file` and `mandatory` fields for each agent
- **Commit:** `REQ-006: step 4 - registry new fields, bundled fallback, download_agent_md`

---

### TASK-006-3: Rewrite add.py for new format

#### Step 5: Rewrite `_install_single()` + remove old code + tests

- **Tests to write (rewrite `tests/test_add.py`):**
  1. `test_add_deploys_md_to_claude_agents()` -- agent installed to `.claude/agents/<name>.md`
  2. `test_add_uses_bundled_source()` -- when bundled file exists, copies from bundled
  3. `test_add_falls_back_to_github()` -- when not bundled, calls `download_agent_md()`
  4. `test_add_creates_placeholder_on_failure()` -- when both bundled and GitHub fail, creates placeholder `.md` with valid frontmatter
  5. `test_add_placeholder_has_valid_frontmatter()` -- placeholder contains `---\nname: <name>\ndescription: ...\n---`
  6. `test_add_records_in_manifest()` -- still recorded in manifest
  7. `test_add_skips_already_installed()` -- still works
  8. `test_add_resolves_dependencies()` -- still works (deps get `.md` files too)
  9. `test_add_shows_deprecation_warning()` -- still works
  10. `test_add_agents_internal_api_no_warning()` -- still works
  11. `test_add_nothing_to_install()` -- when all agents already installed
- **Criteria covered:** AC-3 (all sub-criteria)
- **Files:**
  - Modify `src/ievo/commands/add.py`:
    - Remove imports: `AgentPackage`, `McpType`
    - Replace `AGENTS_DIR` import with `CLAUDE_AGENTS_DIR`
    - Rewrite `_install_single()`:
      1. `dest_dir = root / CLAUDE_AGENTS_DIR` (ensure dir exists)
      2. `dest_file = dest_dir / f"{name}.md"`
      3. Try: `bundled = bundled_agent_path(name)` -> if exists, `shutil.copy2(bundled, dest_file)`
      4. Else: `downloaded = await registry.download_agent_md(name, dest_dir)` -> if True, done
      5. Else: `_create_placeholder_agent(dest_file, name)`
    - Replace `_create_local_agent()` with `_create_placeholder_agent(dest_file: Path, name: str)`:
      ```
      content = f"---\nname: {name}\ndescription: Placeholder agent\nmodel: sonnet\n---\n\n# {name}\n\nPlaceholder. Install from marketplace for full agent.\n"
      dest_file.write_text(content)
      ```
    - Remove `_print_dep_summary()` function entirely
    - Remove `_print_dep_summary(root, agents)` call from `_add_agents()`
    - Update success message (remove "EVO skill included in each" -- `.md` agents handle skills differently)
  - Rewrite `tests/test_add.py` -- all tests updated for new format
- **Commit:** `REQ-006: step 5 - add.py deploys .md files to .claude/agents/`

---

### TASK-006-4: Update init.py + remove template constants

#### Step 6: Deploy templates from bundled content + create `.claude/agents/` dir

- **Tests to write (update `tests/test_init.py` and `tests/test_templates.py`):**
  1. `test_init_creates_claude_agents_dir()` -- `.claude/agents/` exists after init
  2. `test_init_deploys_ievo_md_from_bundled()` -- `.ievo/IEVO.md` exists, content matches bundled `IEVO.md`
  3. `test_init_deploys_req_template_from_bundled()` -- `spec/templates/REQ_TEMPLATE.md` exists, content matches bundled
  4. `test_init_deploys_cr_template_from_bundled()` -- `spec/templates/CR_TEMPLATE.md` exists, content matches bundled
  5. `test_init_deploys_all_bundled_templates()` -- QUESTION, PLAN, ACCEPTANCE, PROPOSAL templates also deployed
  6. Update `test_init_creates_spec_templates()` -- content assertions still valid
  7. Update `test_init_skips_existing_files()` -- add `.claude/agents/` dir and `.ievo/IEVO.md` to pre-existing
  8. `test_templates_spec_req_template_removed()` -- `SPEC_REQ_TEMPLATE` no longer importable from `init_templates`
  9. `test_templates_spec_cr_template_removed()` -- `SPEC_CR_TEMPLATE` no longer importable from `init_templates`
  10. Update `test_all_templates_non_empty()` -- remove `SPEC_REQ_TEMPLATE` and `SPEC_CR_TEMPLATE` entries
- **Criteria covered:** AC-4 (all sub-criteria)
- **Files:**
  - Modify `src/ievo/commands/init.py`:
    - Remove `SPEC_CR_TEMPLATE` and `SPEC_REQ_TEMPLATE` from imports
    - Add import: `from ievo.marketplace import bundled_template_path`
    - Add `".claude/agents"` to the `dirs` list (after `.claude/hooks`)
    - Add IEVO.md deployment section after project files:
      ```python
      # Deploy IEVO.md from bundled marketplace
      ievo_md = bundled_template_path("IEVO.md")
      if ievo_md:
          if _write_if_missing(root / ".ievo" / "IEVO.md", ievo_md.read_text()):
              step(".ievo/IEVO.md")
      ```
    - Replace hardcoded spec template writes with bundled:
      ```python
      for tpl_name, dest_name in [
          ("REQ_TEMPLATE.md", "REQ_TEMPLATE.md"),
          ("CHANGE_REQUEST_TEMPLATE.md", "CR_TEMPLATE.md"),
          ("QUESTION_TEMPLATE.md", "QUESTION_TEMPLATE.md"),
          ("PLAN_TEMPLATE.md", "PLAN_TEMPLATE.md"),
          ("ACCEPTANCE_REPORT_TEMPLATE.md", "ACCEPTANCE_REPORT_TEMPLATE.md"),
          ("PROPOSAL_TEMPLATE.md", "PROPOSAL_TEMPLATE.md"),
      ]:
          tpl_path = bundled_template_path(tpl_name)
          if tpl_path:
              if _write_if_missing(root / SPEC_DIR / "templates" / dest_name, tpl_path.read_text()):
                  step(f"spec/templates/{dest_name}")
      ```
  - Modify `src/ievo/commands/init_templates.py`:
    - Remove `SPEC_REQ_TEMPLATE` constant entirely
    - Remove `SPEC_CR_TEMPLATE` constant entirely
  - Update `tests/test_init.py` -- adjust assertions for new template deployment
  - Update `tests/test_templates.py` -- remove `SPEC_REQ_TEMPLATE`/`SPEC_CR_TEMPLATE` tests, update imports, add bundled template validation tests
- **Commit:** `REQ-006: step 6 - init deploys bundled templates, remove hardcoded constants`

---

### TASK-006-5: Update remove.py + list_cmd.py + cleanup

#### Step 7: Update remove.py to handle `.claude/agents/<name>.md`

- **Tests to write (update `tests/test_remove.py`):**
  1. `test_remove_deletes_claude_agents_md()` -- `.claude/agents/<name>.md` is deleted
  2. `test_remove_deletes_both_formats()` -- removes both `.claude/agents/<name>.md` AND `agents/<name>/`
  3. `test_remove_legacy_only()` -- only `agents/<name>/` exists, still removed
  4. `test_remove_md_only()` -- only `.claude/agents/<name>.md` exists, still removed
  5. Update existing tests: `tmp_project_with_agent` fixture now also creates `.claude/agents/spec-writer.md`
- **Criteria covered:** AC-5 (both sub-criteria)
- **Files:**
  - Modify `src/ievo/commands/remove.py`:
    - Add import: `from ievo.core.config import CLAUDE_AGENTS_DIR` (alongside existing `AGENTS_DIR`)
    - After removing `agent_dir` (line 55-56), also remove `.claude/agents/<name>.md`:
      ```python
      claude_agent_md = root / CLAUDE_AGENTS_DIR / f"{agent}.md"
      if claude_agent_md.exists():
          claude_agent_md.unlink()
      ```
  - Modify `tests/test_remove.py` -- add new tests
  - Modify `tests/conftest.py` -- update `tmp_project_with_agent` to also create `.claude/agents/spec-writer.md` with valid frontmatter
- **Commit:** `REQ-006: step 7 - remove.py handles .claude/agents/ format`

#### Step 8: Update list_cmd.py to read frontmatter + delete old template + fixture updates

- **Tests to write (update `tests/test_list.py`):**
  1. `test_list_reads_md_frontmatter()` -- agent with `.claude/agents/<name>.md` shows correct model/description
  2. `test_list_falls_back_to_legacy()` -- agent without `.md` but with `agents/<name>/agent.yaml` still works
  3. `test_list_both_formats()` -- `.md` format takes priority over legacy
  4. `test_list_marketplace_no_network()` -- `--marketplace` reads from bundled registry (no fetch call)
  5. Update existing tests to work with new fixture
- **Criteria covered:** AC-6 (both sub-criteria)
- **Files:**
  - Modify `src/ievo/commands/list_cmd.py`:
    - Add import: `from ievo.core.config import CLAUDE_AGENTS_DIR` (alongside `AGENTS_DIR`)
    - Add import: `from ievo.marketplace import parse_agent_frontmatter`
    - In `list_agents()`, for each agent: try `.claude/agents/<name>.md` first:
      ```python
      claude_agent_md = root / CLAUDE_AGENTS_DIR / f"{agent_name}.md"
      if claude_agent_md.exists():
          fm = parse_agent_frontmatter(claude_agent_md)
          model = fm.get("model", "sonnet")
          description = fm.get("description", "")
      else:
          # Legacy fallback
          try:
              pkg = AgentPackage.load(agent_dir)
              model = pkg.model.primary
              description = pkg.description
          except FileNotFoundError:
              pass
      ```
    - In `_list_marketplace()`, add bundled fallback: if `registry.list_all()` is empty after fetch, try `load_bundled_registry()` to show bundled agents
  - Delete `src/ievo/templates/IEVO.md` (replaced by `src/ievo/marketplace/templates/IEVO.md`)
  - Delete `src/ievo/templates/` directory if empty after removal
  - Modify `tests/test_list.py` -- add new tests
- **Commit:** `REQ-006: step 8 - list reads frontmatter, delete old IEVO.md template`

---

### TASK-006-6: Final integration + wheel verification

#### Step 9: Full test suite + coverage + pre-commit + wheel build

- **Tests to write:** None (verification step)
- **Criteria covered:** AC-7 (wheel contains marketplace files)
- **Files:** None new
- **Actions:**
  1. `uv run pytest --cov --cov-report=term-missing` -- must pass with >= 99% coverage
  2. `uv run pre-commit run --all-files` -- must pass clean
  3. `uv build` -- build wheel
  4. `unzip -l dist/ievo-*.whl | grep marketplace` -- verify `.md` and `.yaml` files present
  5. Fix any issues found during integration
- **Commit:** `REQ-006: step 9 - final cleanup and verification` (only if fixes needed)

---

## Traceability

| Acceptance Criterion | Covered in Step |
|---------------------|-----------------|
| AC-1: Bundled content access layer | Step 1, Step 2 |
| AC-2: Registry supports new fields | Step 4 |
| AC-3: Agent install uses new format | Step 5 |
| AC-4: Init deploys bundled templates | Step 6 |
| AC-5: Remove handles new format | Step 7 |
| AC-6: List reads frontmatter | Step 8 |
| AC-7: Bundled content in wheel | Step 9 |

## Scope Guard

- **Considered but excluded:**
  - Migrating `tui/app.py` to read `.claude/agents/` -- out of scope, it stays on `agents/` + `AgentPackage` for now
  - Migrating `run.py`, `orchestrate.py`, `deps.py`, `learn.py` to new format -- separate future REQ
  - Removing `AgentPackage` class entirely -- still used by TUI, run, orchestrate, deps
  - Removing `AGENTS_DIR` constant -- still used by many modules
  - Auto-deploying agents during `ievo init` -- REQ-006 only deploys templates, not agents. Agent deployment is via `ievo add` or HR agent.
  - Build-time sync from marketplace repo -- content is copied once manually during development
  - Updating `IEVO.md` in the project's `.ievo/` directory (the one at `.ievo/IEVO.md` in this repo) -- that's a separate concern

## Architecture Notes for Coder

- Use `Path(__file__).parent` pattern in `marketplace/__init__.py` -- consistent with `init.py:169` and `run.py:172`
- `parse_agent_frontmatter()` goes in `marketplace/__init__.py` because it is a utility for reading the marketplace `.md` format, used by both `list_cmd.py` and potentially future modules
- In `registry.py`, `_parse_registry_file()` should be a `@classmethod` on `Registry` to keep parsing logic co-located with the class. Both `load_cached()` and `fetch()` should use it.
- `load_cached()` fallback order: cache file (may have newer versions from `fetch()`) -> bundled registry (guaranteed to exist) -> empty. NEVER reverse this -- cache may be intentionally updated.
- `download_agent_md()` uses `entry.file` field from registry to construct URL: `{MARKETPLACE_RAW}/{entry.file}`. If `entry.file` is empty, fallback to `agents/{name}.md`.
- `shutil.copy2()` for copying bundled files -- preserves metadata.
- When updating `conftest.py`, the `tmp_project_with_agent` fixture needs `.claude/agents/spec-writer.md` so that tests for both remove and list work with the new format. But do NOT remove the old `agents/spec-writer/` directory from the fixture -- backward compatibility tests need it.
- In `init_templates.py`, keep all OTHER constants (`PRIORITY_MD`, `SPEC_INDEX_MD`, `CLAUDE_MD`, etc.) -- only remove `SPEC_REQ_TEMPLATE` and `SPEC_CR_TEMPLATE`.
- `_create_placeholder_agent()` in `add.py` writes minimal valid YAML frontmatter. The placeholder must be parseable by `parse_agent_frontmatter()`.
