# Init

> Use this skill when the user runs `/ievo:init`, opens a new project that does not yet have `.ievo/`, or asks "set up iEvo here" / "find skills for this project" — not for configuring lifecycle hooks alone (use /ievo:hooks-setup for that). Initializes iEvo in the current project — discover relevant skills and agents from skills.sh and the broader GitHub ecosystem (via own discover.mjs script, no prereq install), audit them for safety via senior-security-engineer review, install through an interactive interview. Composes two lower-level skills (index-repos, security-check) plus discover.mjs + repo-indexer + security-auditor sub-agents into a complete setup pipeline.

- Skill: `ievo-ai/init` (Agent Skill, multi-file: 5 files)
- Install (CLI): `npx skillmds@latest add ievo-ai/init`
- Raw SKILL.md: https://api.skillmd.com/api/skills/ievo-ai/init/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: DevOps & Infra
- License: MIT
- Author: ievo-ai (https://skillmd.com/u/ievo-ai)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/ievo-ai/init

---


# Init

## ⚠️ Critical execution directive — read first

**Execute the entire pipeline continuously, without pausing.** Do NOT wait for user input between steps. The ONLY user-facing pauses are:

1. **Step 1** — `AskUserQuestion`, twice at most: the working-directory confirmation, only if `pwd` shows neither `.git/` nor a Step 4 manifest; and the permission check
2. **Step 7a** (resolve ambiguous categories) — `AskUserQuestion`, only if any categories were marked `/ambiguous`
3. **Step 7b** (per-candidate interview) — `AskUserQuestion`, including the single batched tail question for `overlap_tail[]` items, if any
4. **Step 8** (RED security verdict) — `AskUserQuestion`, only for RED candidates
5. **Step 12.5** (platform-mismatch self-check) — only if a mismatch is actually caught, and never in this step directly: the pauses belong to the `/ievo:evo` handoff, which under its own Step 1 overlay-only carve-out can raise **at most two**, each independently conditional — Step 5.6's upstream-feedback offer (if the lesson classifies as upstream-relevant, which this one does) and Step 5.7's extraction offer (only if that overlay already holds a cluster). Choosing to share at Step 5.6 hands off to `/ievo:feedback`, which adds its own public-posting gate
6. **Step 13** (final feedback prompt) — `AskUserQuestion`

Between every other step, **proceed immediately** to the next step. If you find yourself thinking "should I confirm with the user before doing X?" — the answer is NO. Just do it. Write to the log so the user can monitor via `tail -f`.

Especially: between Step 5 (discover.mjs result) and Step 6 (index-repos) → **no pause, no confirmation, no summary checkpoint**. Just chain straight through.

## Pipeline

Set up iEvo in the current project. Pipeline (v0.6.0+):

```
discover.mjs (Node, parallel skills.sh API queries)
    ↓
index-repos (parallel repo-indexer sub-agents, local scan)
    ↓
categorical rank — top-N per category
    ↓
interview (per candidate, AskUserQuestion)
    ↓
security-auditor (parallel sub-agents, antivirus deep scan)
    ↓
install (vendor or plugin, project-scope, copy + source SHA metadata)
```

**v0.6.0 — zero-prereq architecture**: dropped `find-skills` manual install. Discovery happens via own `discover.mjs` script (skills.sh API direct). All scanning, ranking, audit, and install decisions happen on user's machine. Independent and verifiable per-user, no central trust gates.

**Install model** (Step 9): project-scope, into the invoking client's own load paths — Claude Code: `.claude/agents/`, `.claude/skills/`; Codex (Step 1.5's detection rule): `.agents/skills/` (skills only — see Step 7a's platform filter) — **copy** files via Write tool (NOT symlink — robust against source moves). Source repo + commit SHA recorded in `.ievo/evolution/<scope>/<name>.md` frontmatter for upstream-update tracking via `/ievo:update`.

## Step 0: Print version banner (read from disk — never infer)

**MANDATORY first action.** The version MUST come from actual disk read of the plugin.json file. If you "know" the version from prior conversation turns, from being trained, or from SKILL.md text — **IGNORE that knowledge**. The diagnostic value depends on showing what's actually loaded, not what you expect.

### Step 0a — Read plugin.json from disk (Read tool, not Bash)

Use the **Read tool** on:
```
${CLAUDE_PLUGIN_ROOT}/.claude-plugin/plugin.json
```

The Read tool returns the file contents verbatim in the tool result — no chance of substitution or inference. Extract the `version` field from the JSON.

If Read fails (file missing, permission denied, etc.), print error and stop:
```
❌ Cannot read plugin.json from ${CLAUDE_PLUGIN_ROOT}/.claude-plugin/plugin.json
   <error message>
   Reinstall plugin: /plugin reinstall ievo@ievo-skills
```

Do NOT fall back to "I think it's v0.2.x" — that defeats the diagnostic.

### Step 0b — Print banner

Output exactly (substitute only `<version-from-read>` with the value extracted in 0a):

```
🧬 iEvo init v<version-from-read>
   from: ${CLAUDE_PLUGIN_ROOT}/.claude-plugin/plugin.json
```

The **`from:` line is part of the banner** — shows the user the exact path the version came from. They can manually inspect that file to verify if suspicious.

### Why Read instead of Bash

`jq` via Bash works, but Bash tool invocations can be skipped by some inference paths — the model can complete the next line as if Bash had returned the expected value. **Read tool is deterministic**: its result is a file content snapshot in the tool response. The version IS what Read returned, by construction.

### Step 0c — Record version in the run-log

The banner version becomes **section 0** of the run-log, written when the log
file is created in Step 2.5 (format: [log-format.md §0](references/log-format.md)).
The plugin path + commit SHA recorded there help diagnose which install dir
Claude Code loaded the plugin from.

## Step 1: Verify prerequisites

**Working directory — check before anything else.** Every step below — `.ievo/`
setup, `.claude/settings.json`, Step 9's project-scoped installs — writes into the
session's CURRENT working directory, and nothing in this pipeline switches directory
later. A session started outside the project to init (e.g. `~` or `~/Desktop`)
therefore misdirects the whole run silently.

Run `pwd`, then look in that same directory for a project signal: a `.git/` entry or
any Step 4 manifest. If **both** are absent, ask before doing anything else via
`AskUserQuestion` (an empty, brand-new project directory is legitimate — the user
answers for it; never decide this yourself):

- **Question:** `No project detected in <pwd output>. Initialize iEvo here?`
- **Header:** `Directory`
- **Options:**
  - `Yes — this is the project` — description: `Continue. .ievo/ and all installs go here.`
  - `No — stop` — description: `Halt so the session can be moved to the right directory.`

On `Yes`, continue with the prereq checks below. On `No`, **halt** — you cannot move
the session yourself, so print the message below and stop:

- `/cd` is a built-in Claude Code command, recognized only when the **user** types it
  at the start of a message ([commands reference](https://code.claude.com/docs/en/commands):
  `/cd <path>` — "Move this session to a new working directory", requires v2.1.169+;
  earlier versions report `Unknown command: /cd`). It is not callable from a skill.
- `Bash(cd ...)` is **not** a substitute. It changes only that call's shell directory;
  `Read`/`Write`, `.claude/` settings resolution and Step 9's installs all keep
  resolving against the session's working directory, so `.ievo/` and every install
  still land outside the project — the exact silent misdirection this check exists to
  catch. Never use it to "fix" the directory.

```
Move this session to the project, then re-run /ievo:init:
  • Claude Code v2.1.169+ — type `/cd <project-path>` (preserves the prompt cache).
  • Older Claude Code, or Codex — no in-session directory switch is documented;
    quit and relaunch from inside the project directory.
```

Hard prereqs (v0.6.0+ — no more find-skills install):
- `git` CLI — `which git`. Used for checkout-based indexing.
- `gh` CLI — `which gh` and `gh auth status`. Used by security-auditor (audit data from skills.sh) and uninstall (marker discovery).
- `node` (≥18) — `node --version`. Used by `discover.mjs`, `scan_repo.mjs`, `validate_agents.mjs`. Node ships with Claude Code and Codex, so this is normally always available — but if user has a damaged install, hard-fail.
- **Bash permissions** for the commands init will run (see below)

**v0.6.0 change**: dropped `find-skills` prereq. Discovery now happens via own Node script (`discover.mjs`) hitting `https://skills.sh/api/search` directly — no manual prereq install required.

If `gh` missing or unauthenticated:
```
This skill needs the `gh` CLI for indexing and security checks. Install:
  brew install gh         # macOS
  # or see https://cli.github.com
  gh auth login
```

If `node` missing OR version < 18:
```
This skill needs Node.js 18+ for repo scanning. Normally Node ships with Claude Code —
if it's missing your install may be damaged. Try reinstalling Claude Code, or install Node directly:
  brew install node       # macOS
  apt install nodejs      # Debian/Ubuntu
  # or see https://nodejs.org

Verify: node --version  → must be v18.0.0 or higher
```

Stop init on missing node. No graceful fallback — scan_repo.mjs is core to Step 6.

### Permission check (auto-mode classifier)

**Platform — skip this entire subsection on Codex.** `.claude/settings.local.json` / `.claude/settings.json` `permissions.allow` entries are Claude Code's permission mechanism; Codex never reads them, so writing them from a Codex session configures the wrong client (this exact miss shipped `permissions` into `.claude/settings.json` from a Codex run — issue #432). If the host platform is Codex (per Step 1.5's detection rule — `$CODEX_CLI`, or a Codex Desktop signal), skip the settings read, the `AskUserQuestion`, and any write — Codex's own approval flow prompts per command as needed. The hard prereq checks above (git / gh / node) still apply on every platform.

Init will run network/CLI commands the auto-mode classifier may block: `gh api`, `gh search`. Without pre-approval, each call hits a confirmation prompt — friction during the discovery phase.

(v0.6.0 dropped `npx skills` permissions — discovery now happens via local `discover.mjs` script which is a normal `node` invocation, not blocked by the auto-classifier.)

Recommended: ensure `.claude/settings.local.json` (per-user, gitignored) OR `.claude/settings.json` (team-shared, committed) contains:

```json
{
  "permissions": {
    "allow": [
      "Bash(gh api*)",
      "Bash(gh search*)"
    ]
  }
}
```

**Check at init start:** read the project's settings files. If the two patterns above are NOT present, ask user via `AskUserQuestion`:

- **Question:** `Init needs Bash permissions for gh CLI. Add them?`
- **Header:** `Permissions`
- **Options:**
  - `Add to .claude/settings.local.json (Recommended)` — description: `Per-user permission, gitignored. Only affects you on this machine.`
  - `Add to .claude/settings.json (team-shared)` — description: `Permission shared with team via git commit. Useful if everyone runs iEvo here.`
  - `Skip — I'll approve each command manually` — description: `Each blocked Bash call needs explicit Allow. Slower but no permission file changes.`

For `Add to ...` options: merge the two patterns into the existing `permissions.allow` array. Do not overwrite other permissions. If file doesn't exist, create with minimal `{"permissions": {"allow": [...]}}`.

For `Skip`: continue — but expect blocked commands during the run.

Stop only on missing gh / git / node prereqs. Permission setup is opt-in but strongly recommended.

**Auto Mode + `classifyAllShell` interaction (CC v2.1.193+).** The `permissions.allow` entries above bypass the classifier only under Auto Mode's *default* behavior, where narrow Bash allow rules (like `Bash(gh api*)`) resolve before the classifier runs — and only Auto Mode is affected at all; other permission modes are untouched either way. If the user has `autoMode.classifyAllShell: true` set, that default is suspended: **every** bash call in this pipeline — 20+ across discovery, indexing, and scanning — is routed through the classifier individually, regardless of `permissions.allow`. This trades latency for coverage (a classifier round-trip per call instead of an instant allow-rule match) and any call the classifier doesn't recognize as safe may still be blocked, which can interrupt this skill's "execute continuously, without pausing" directive. There's no code-level workaround for this skill: tell the user to disable `autoMode.classifyAllShell` for the init session, or proceed knowing the whole pipeline now pays the per-call classifier cost.

## Step 1.5: Client detection (plugin-wide canonical rule) + Codex environment pre-flight

**Client detection (canonical — every other skill's "same rule as Step 1.5" cites this exact rule; issue #461).** Evaluate these checks **in order** and stop at the first one that matches:

1. **`$CLAUDECODE` is set and `$CODEX_CLI` is not → Claude Code.** Claude Code exports `CLAUDECODE=1` into the environment of the commands it runs, making it the one *positive* Claude Code signal available (the same variable `debug-on/SKILL.md`'s session-start marker already reads). This check must come before the Codex Desktop markers below, because those markers are ordinary environment variables and are therefore **inherited by every descendant process** — `__CFBundleIdentifier=com.openai.codex` in particular is set for the whole Codex Desktop app subtree, so a Claude Code CLI session started from a terminal that Codex Desktop spawned carries it too. Without this positive check that session would detect as Codex and vendor into `.agents/skills/`, the wrong client's load path (the issue #432 class of bug, reached by a new trigger). The `$CODEX_CLI`-unset half of the condition keeps check 2 authoritative for the mirror case (a Codex CLI session started from inside a Claude Code shell inherits `CLAUDECODE` the same way).
2. **`$CODEX_CLI` is set → Codex** — Codex CLI (terminal) sessions.
3. **A Codex Desktop signal is present → Codex**: `CODEX_INTERNAL_ORIGINATOR_OVERRIDE=Codex Desktop`, or (macOS only) `__CFBundleIdentifier=com.openai.codex` (both verified empirically against a live Codex Desktop session, issue #461 — Codex Desktop never sets `$CODEX_CLI`, which is exactly why the pre-#461 "`$CODEX_CLI` env var ONLY" rule misdetected it as Claude Code and then read/wrote the wrong client's config throughout every gated skill). Neither marker is documented in Codex's public environment-variable reference — treat them as best-effort corroborating evidence, not a guaranteed contract, and re-verify if a future Codex release stops setting them. Being the weakest and most inheritance-prone signals, they are deliberately ranked last.

Absent **all** of the above → **Claude Code** (unchanged default — this rule adds a positive Codex Desktop detection path plus the positive Claude Code check that bounds it; it does not change what "no signal at all" means).

Still do **not** key off `command -v codex` — a Claude Code user may have the Codex CLI installed alongside, which would false-trigger Codex-only behavior on a genuine Claude Code run (unchanged rule).

**Every other "`$CODEX_CLI` set" / "`$CODEX_CLI` unset" mention in this skill, and in any other iEvo skill/agent that cites "the same rule as Step 1.5" or "per Step 1.5", means this whole ordered rule — never the bare environment variable in isolation, and never the Codex signals without the `$CLAUDECODE` check that precedes them.**

If the host platform is Codex per the rule above, run `codex doctor` and check the exit code. `codex doctor` shipped in Codex `rust-v0.131.0` (May 18 2026) as a first-class diagnostic across runtime, auth, terminal, network, config, and local state.

```bash
codex doctor
```

- **Exit 0** → environment healthy, continue to Step 2.
- **Non-zero exit** → surface the doctor output to the user and halt. Show this message:

  ```
  Codex environment is unhealthy (see `codex doctor` output above).
  Fix the reported issues and re-run `/ievo:init`.
  ```

  Common fixes: re-login to Codex (`codex login`), regenerate auth (`codex auth refresh`), update Codex CLI to the latest release.

On Claude Code: the client-detection rule above still applies (it's what determined "Claude Code" in the first place, and every other skill/agent citing "Step 1.5" depends on it) — only the `codex doctor` diagnostic and its halt-on-failure gate are skipped, since Claude Code has no equivalent built-in diagnostic command yet (May 2026). The Step 1 prereq checks above cover the same surface (git / gh / node). Update this skill when Claude Code ships an equivalent.

## Step 2: Prepare project directories

Create if missing:
- `.ievo/evolution/agents/`
- `.ievo/evolution/skills/`
- `.ievo/log/`
- `.ievo/log/hooks/` — append-only audit log for lifecycle hook fires (events.log appended by every hook configured via `/ievo:hooks-setup`)
- `.ievo/cache/index/`
- `.ievo/hooks/` — signal-file directory for lifecycle hooks; Step 11.5 writes `init-complete` here, evo/SKILL.md Step 5.5 writes `evolution-captured`, security-auditor.md Step 6 writes `security-red` (RED-only). Created defensively even if `/ievo:hooks-setup` hasn't been run yet
- `.ievo/log/pending-reports/` — for security-issue reports that couldn't be filed live (gh auth missing, rate limit, repo issues disabled). User can file manually later from these saved bodies.

Plus the platform's vendor root — create only the invoking client's directories
(Step 1.5's detection rule), never both:

- **Claude Code** (Step 1.5: no Codex signal):
  - `.claude/` — root for vendored items
  - `.claude/agents/` — for vendored agents
  - `.claude/skills/` — for vendored skills (init uses direct file writes via Write tool, NOT `npx skills add`)
- **Codex** (Step 1.5: `$CODEX_CLI` set, or a Codex Desktop signal):
  - `.agents/skills/` — for vendored skills. Codex scans `.agents/skills` from the
    working directory up to the repo root, plus `$HOME/.agents/skills`
    ([Codex skills docs](https://developers.openai.com/codex/skills)); `.claude/*`
    is invisible to Codex, so writing there from a Codex session installs nothing
    (issue #432). No agents directory — Codex documents no project-level
    custom-agent load path (see Step 7a's platform filter).

Do NOT touch `CLAUDE.md` or `AGENTS.md` here.

**Migration check (Claude Code ↔ Codex).** Before creating the dirs, check whether
`.ievo/evolution/` already holds overlay files (`.ievo/evolution/skills/*.md` or
`.ievo/evolution/agents/*.md`). If so, existing iEvo state is present — likely
migrated from another platform on the shared filesystem (e.g. via Codex `/import`).
**Preserve it** (create only missing dirs; never overwrite existing overlays) and
tell the user: "Existing iEvo evolution state detected — kept as-is. Init continues
for discovery; your overlays stay active." The idempotent inventory (Step 3)
already prevents re-suggesting installed items.

## Step 2.2: Self-register iEvo for team sync (Claude Code, plugin-mode only)

Every candidate this pipeline discovers gets bootstrapped into `.claude/settings.json`
at install time (Step 9b) so a teammate who `git pull`s the project auto-receives it.
iEvo itself never got the same treatment — a teammate cloning a project that already
has iEvo installed had no equivalent auto-install path, and had to `/plugin install`
manually on every machine. This step closes that gap for iEvo's own entry.

**Gate — plugin-mode only, no new detection needed.** Self-registration only makes
sense when iEvo is running as an installed Claude Code plugin (there's a marketplace
install to register); a vendored copy (manual `git clone` into a skills directory) has
no marketplace concept to self-register, and writing `extraKnownMarketplaces` /
`enabledPlugins` there would advertise a mechanism the current machine isn't using.
Step 0a above already hard-stops the entire pipeline if
`${CLAUDE_PLUGIN_ROOT}/.claude-plugin/plugin.json` isn't readable — so simply
reaching this step already proves plugin-mode. No additional check is needed.

**Platform — skip entirely on Codex.** `.claude/settings.json` is a Claude
Code-specific file. Detect Codex the same way Step 1.5 does (`$CODEX_CLI` set, or
a Codex Desktop signal — never `command -v codex`, for the same false-trigger
reason given there) and skip this step there; see Step 2.3 for the Codex-side note.

**Action:**
1. Read or create `.claude/settings.json`.
2. Merge (same merge-not-overwrite semantics as `install-protocol.md` § 9b —
   preserve every other key, never overwrite):
   ```json
   "extraKnownMarketplaces": {
     "ievo-skills": {
       "source": { "source": "github", "repo": "ievo-ai/skills" }
     }
   },
   "enabledPlugins": {
     "ievo@ievo-skills": true
   }
   ```
3. Write the merged JSON back.

**Failure handling.** If the read/merge/write fails (malformed existing JSON,
write permission denied, etc.), report it in the final summary (Step 12) and
continue — do NOT abort init, same as Step 9's per-item failure handling.

**No `autoUpdate` key.** Claude Code's own default for a third-party marketplace
entry is `autoUpdate: false` — matches the "must remain an explicit user choice"
constraint on auto-updates. Step 12's existing summary already tells the user how
to opt into `autoUpdate: true` manually; this step doesn't change that.

**Idempotent.** If `extraKnownMarketplaces.ievo-skills` and
`enabledPlugins["ievo@ievo-skills"]` already match the values above, this is a
no-op — safe on every re-run, same as Step 9b's own merge.

This file is committed to git → teammates `git pull` → Claude Code prompts them to
trust the folder → iEvo itself is now discoverable/installable the same way a
vendored third-party plugin already is via Step 9b.

## Step 2.3: Codex — documented limitation, no self-registration (yet)

On Codex, the equivalent bootstrap does not exist upstream today: project-level
`.codex/config.toml` `[plugins.*].enabled` entries are silently ignored — only
user-level `~/.codex/config.toml` is authoritative (confirmed open as of
2026-07-24: [`openai/codex#18115`](https://github.com/openai/codex/issues/18115)).
Writing a project-level entry would silently do nothing, so this step is a
documentation no-op, not a file write:

- Do NOT write anything to `.codex/config.toml` for iEvo self-registration —
  there is nothing to gain and it would misleadingly suggest persistence that
  doesn't happen.
- Do NOT change `.codex-plugin/marketplace.json`'s `policy.installation`
  (`AVAILABLE`) as part of this — that governs onboarding/default-install UX for
  iEvo's own public marketplace across every future Codex user, a separate,
  deliberately-deferred call, out of scope here.
- If the host platform is Codex (Step 1.5's rule), tell the user once in the
  final summary (Step 12): "iEvo's own project-level auto-bootstrap isn't
  available on Codex yet — Codex doesn't persist project-scoped plugin config
  (openai/codex#18115). Install/update iEvo manually on each machine for now."

## Step 2.5: Create run-log file (incremental writes — do not defer!)

**Critical:** the log is written **incrementally**, after each major step — not as a single flush at the end. If init hangs, crashes, or the user cancels mid-run, the diagnostic log up to the point of failure must be on disk.

Create the file NOW with timestamp and section 0:

```bash
LOG_PATH=".ievo/log/init-$(date -u +%Y%m%d-%H%M%S).md"
mkdir -p .ievo/log
cat > "$LOG_PATH" <<EOF
# Init run — $(date -u +%Y-%m-%dT%H:%M:%SZ)

## 0. Plugin metadata
- iEvo plugin: <version from Step 0 banner>
- Plugin commit SHA: <or "marketplace-installed">
- Client: <Codex (\$CODEX_CLI set, or Codex Desktop signal — Step 1.5) | Claude Code (\`claude --version\`)>
- OS: <uname -srm>
- Run started: <ISO-8601 timestamp>
EOF
```

Remember `LOG_PATH` for all subsequent steps. Each step below has a **`Log:` instruction** — it means **append that section to `$LOG_PATH` immediately**, before proceeding to the next step.

If a step takes a long time (e.g. `discover.mjs` or `index-repos` for big repos), the user can `tail -f $LOG_PATH` in another shell and see progress.

## Step 3: Build installed inventory

The inventory answers "what is already available to THIS client" — so scan the
invoking client's load paths, not the other platform's.

**On Claude Code** (Step 1.5: no Codex signal), collect names from:

**Skills installed:**
- `.claude/skills/<name>/SKILL.md`
- `.claude/plugins/*/skills/<name>/SKILL.md`
- `~/.claude/skills/<name>/SKILL.md`
- `~/.claude/plugins/*/skills/<name>/SKILL.md`

**Agents installed:**
- `.claude/agents/<name>.md`
- `.claude/plugins/*/agents/<name>.md`
- `~/.claude/agents/<name>.md`
- `~/.claude/plugins/*/agents/<name>.md`

**Plugins enabled:**
- Parse `.claude/settings.json` field `enabledPlugins` keys

**On Codex** (Step 1.5: `$CODEX_CLI` set, or a Codex Desktop signal), collect
names from the Codex-visible skill directories instead:

- `.agents/skills/<name>/SKILL.md` (working directory up to repo root)
- `~/.agents/skills/<name>/SKILL.md`

Do NOT parse `.claude/settings.json` and do NOT count `.claude/skills/` /
`.claude/agents/` contents as installed on Codex — Codex never loads them. If
`.claude/skills/` does contain vendored items (a project previously initialized
from Claude Code — the issue #432 migration case), list them in the log and the
Step 12 summary as "present under `.claude/skills/` but not visible to Codex",
and let them re-surface as candidates: re-accepting one re-vendors it to
`.agents/skills/`, which is the repair path for a Claude-Code-configured
project now driven from Codex. (The existing `.ievo/evolution/skills/<name>.md`
overlay is preserved by install-protocol.md §9a step 4 when the re-vendored
source matches its recorded `source.repo`.) Provenance guard for exactly this
re-surface path: when a candidate shares its name with a `.claude/skills/`
item, compare the candidate's `<owner>/<repo>` against that overlay's
`source.repo` before Step 7b — on a mismatch, say so in the candidate's
interview question ("same name, DIFFERENT source than your existing
.claude/skills copy — this is a different item, not a repair") instead of
letting it read as a plain re-install; §9a step 4's source-change rule then
governs the overlay if the user proceeds.

**Log section 3 NOW — do not defer.** Write the full inventory with **complete
lists, never truncated** — if a project has 26 agents, log all 26 names;
"12 iEvo-managed plus N others" loses information needed for step-5 filtering.
Format: [log-format.md §3](references/log-format.md).

## Step 4: Detect stack and dependencies

Parse manifest files (full per-stack table in the **Manifest reference** below — covers Python, Node/TS, Rust, Go, Java/Kotlin, Ruby, PHP, Dart, Elixir, .NET, Swift/iOS, Haskell, Clojure, Crystal, OCaml, Nim, Lua, R, Julia, Zig, C/C++, Unreal, Godot, Unity).

For each found manifest, extract direct (top-level) dependency names.

Output stack + deps summary. Log to buffer (section 4).

### Manifest reference

Parse the manifest(s) found, extracting direct (top-level) dependency names, per
the **[manifest reference table](references/reference-tables.md)** — covers Python,
Node/TS/Bun, Deno, Rust, Go, Java/Kotlin, Ruby, PHP, Dart, Elixir, .NET, Swift/iOS,
Haskell, Clojure, Crystal, OCaml, Nim, Lua, R, Julia, Zig, C/C++, Unreal, Godot,
Unity. Tag deps with their source manifest for polyglot projects.

## Step 4.5: Disambiguate broad categories

For each category present in the project, resolve to sub-types using the ambiguous-category registry (kept inline for stability):

| Broad | Sub-types | Signal hints |
|-------|-----------|--------------|
| `i18n` | `code-strings`, `documentation` | `.po`/`.mo`/`locale/` → code-strings; `mkdocs.yml` with `i18n` plugin / `docs/locales/` → documentation |
| `testing` | `unit`, `integration`, `e2e` | `vitest.config`/`jest.config`/`pytest.ini` → unit; `playwright`/`cypress` → e2e; `tox`/separate `integration_tests/` → integration |
| `security` | `app-sec`, `supply-chain`, `static-analysis` | Helmet/JWT → app-sec; `npm audit`/Snyk/Dependabot → supply-chain; bandit/semgrep/CodeQL → static-analysis |
| `documentation` | `user`, `api`, `internal` | mkdocs/docusaurus/sphinx → user; openapi/swagger → api |
| `linting` | `style`, `types`, `security` | prettier/black/rustfmt → style; mypy/tsc/pyright → types; bandit/semgrep → security |
| `observability` | `logging`, `tracing`, `metrics` | structlog/pino → logging; opentelemetry → tracing; prometheus → metrics |
| `state-mgmt` (frontend) | redux/zustand/mobx/recoil | match dep name in package.json |
| `build-tools` | bundler/package-manager/task-runner | vite/webpack/rollup vs npm/yarn vs Makefile/just |
| `database` | orm/query-builder/migrations/driver | sqlalchemy/prisma vs kysely/knex vs alembic/flyway |
| `packaging` | `published`, `internal-only` | Publish/release CI present (`.github/workflows/*` invoking `pypa/gh-action-pypi-publish`, `npm publish`, `cargo publish`, `twine upload`, or equivalent) OR manifest carries non-private registry metadata (`package.json` without `"private": true`; `pyproject.toml` `[project.urls]` set) → `published`. Explicit private marker (`"private": true`; `Private :: Do Not Upload` classifier) **or no registry/publish signal at all** → `internal-only` (resolved directly, no ask — issue #427: `python-packaging` was declined for a project never published to PyPI). Registry-shaped metadata present but conflicting/incomplete (e.g. `[project.urls]` set with no publish workflow and no private marker either) → genuinely unresolved, tag `packaging/ambiguous` |

If signals unclear → tag `<category>/ambiguous`, ask user in step 7a. For `packaging` specifically, `internal-only`/`published` are terminal resolutions (feed Step 7a's stack-relevance filter directly, no ask needed) — only the true `packaging/ambiguous` case reaches the step 7a question, phrased as "Is this project published anywhere (PyPI/npm/crates.io/etc.)?" with the usual "Skip category" option dropping all packaging candidates.

Log resolution outcomes (section 4.5).

## Step 5: Invoke `discover.mjs` for candidate discovery (v0.6.0+)

Run our own discovery script (replaces `find-skills` prereq). It hits skills.sh API directly (`https://skills.sh/api/search`) — no manual prereq install, no `npx skills`, no auto-classifier friction.

### Step 5a — Build stack input JSON

From Steps 3 + 4 + 4.5 build:

```json
{
  "languages": ["python"],
  "deps": ["pytest", "fastapi", "sqlalchemy"],
  "categories": ["testing", "linting", "security", "frameworks", "databases"],
  "frameworks": ["fastapi"]
}
```

Inputs come from:
- `languages` — detected stack types (Step 4)
- `deps` — direct top-level deps from manifests (Step 4)
- `categories` — resolved category list (Step 4.5)
- `frameworks` — major frameworks present (Step 4)

**Write this JSON to disk via the Write tool, NOT via an inline Bash string.**
Several supported manifest formats legitimately permit near-arbitrary text in a
dependency line — e.g. `requirements.txt` PEP 508 environment markers routinely
contain single quotes, such as `numpy; python_version=='3.9'` — so `deps`/
`categories`/`frameworks` values are not safe to embed textually inside a
quoted shell argument (skills#567: a single quote inside any of them breaks out
of a single-quoted `echo` argument and the remainder is parsed as unquoted
shell syntax). The Write tool writes literal bytes with no shell involved.

```
# Write tool (NOT Bash):
#   file_path: <project>/.ievo/log/discover-stack-input.json
#   content:   <the stack JSON built above>   (literal bytes, no shell expansion)
```

`.ievo/log/` already exists by this point (created in Step 2.5, `mkdir -p
.ievo/log`) and is gitignored (Step 10) — this file is diagnostic, not project
state.

### Step 5b — Invoke discover.mjs via Bash

```bash
node "${CLAUDE_PLUGIN_ROOT}/scripts/discover.mjs" --stack-file .ievo/log/discover-stack-input.json --limit 50 --concurrency 8
```

`--stack-file` reads the JSON from the fixed path Step 5a just wrote — never
from stdin, and never with the stack JSON text embedded in the command line
itself. `discover.mjs` contains its own `--stack-file` hardening
(`assertStackFileAllowed`/`assertStackFileReadable`: containment to
`<project>/.ievo/`, regular-file-only, 256 KiB cap — skills#543), so this call
site inherits that same protection already in place for every other
cross-repo-boundary fetch in this plugin (mirrors `evolution_candidates.mjs`'s
`--text-file` fix, #523, and the `feedback/SKILL.md` Step 6 convention of
writing untrusted text via the Write tool rather than an inline shell string).

The script:
1. Builds 15-30 queries from the stack (language fundamentals + per-dep + per-category + stack-specific compound + a fixed stack-independent group for general-purpose codebase-audit/planning-advisor meta-tools — not gated behind any detected category, since that class of skill isn't tied to a language/framework/dep)
2. Parallel-fetches `https://skills.sh/api/search?q=<q>&limit=10` for each
3. **If the `codex` CLI is present**, also reads its marketplace catalog (`codex plugin list --json` → `available[]`) and merges those uninstalled plugins as extra candidates. Absent codex / non-zero exit / unparseable output → silently skipped (no behaviour change for Claude Code-only users).
4. Deduplicates by skill `id`, computes `rank_score` (log10(installs) × reputation_boost × match_breadth_bonus). Codex plugins carry no install count → get a visibility floor (≈ a 10-install skill) so they surface mid-pack instead of being sliced off by `--limit`, and are tagged `source_origin: codex-marketplace`.
5. Returns JSON: `{sources, queries, candidates: [{id, name, source_repo, source_origin, installs, quality_tier, matched_queries, rank_score}]}`. (Codex candidates always have `matched_queries: []` — they're grouped via an internal source sentinel that's stripped from the public output; use `source_origin: codex-marketplace` to identify them, not `matched_queries`.) `sources[]` carries one entry per origin — `skills.sh` and `codex-marketplace` (with `available` / `raw_results` / `error`). The `codex-marketplace` entry is emitted on every run that **reaches discovery** (transparent about what was attempted) — when codex is absent it reads `available: false, raw_results: 0`. (The empty-stack early-return, exit code 5, produces `sources: []` before any source is queried — don't read the codex entry unconditionally.) Note: `available` means "codex produced non-empty stdout" (it can be `true` alongside `error: "unparseable codex output"`), **not** "plugins were found" (`raw_results` is the plugin count). Codex candidates carry `quality_tier: "unranked"` — they have no install count, so the install-based tiers don't apply.

Typical wall-clock: 3-6 seconds for a rich stack. The codex source runs concurrently with the skills.sh queries (`Promise.all`), so it usually overlaps — but a hung codex binary is capped at its 5 s timeout, which becomes the wall-clock ceiling in that worst case.

### Step 5b1 — Handle discover.mjs exit codes

The script exits with distinct codes — branch on them:

| Code | Meaning | What init must do |
|------|---------|-------------------|
| `0` | Success — all queries returned data | Proceed to Step 5c |
| `0` + WARN on stderr | **Partial failure** — some queries failed, candidates still usable | Log the warning in section 5d, proceed but tell the user "discovery was partial (N/M queries failed)" in the summary |
| `1` | No stack input on stdin AND no `--stack-file` | Should not happen — init always provides stack JSON. If it does, log and abort. |
| `3` | Bad input — malformed JSON, missing file, invalid CLI args | Log and abort init (stack input is broken) |
| `4` | **Total failure** — ALL queries failed (skills.sh down, network outage). `candidates: []` | Log + tell user "discovery failed — skills.sh unreachable". Ask via `AskUserQuestion`: continue with auto-available repos only OR abort? |
| `5` | No queries derived from stack (empty input) | Log + abort init — stack detection (Step 4) produced nothing useful |

Capture stderr separately from stdout: `node discover.mjs ... 2>discover.err >discover.out`. The structured JSON is on stdout; the WARN/FATAL messages are on stderr.

### Step 5c — Filter against installed inventory

From the discover.mjs output `candidates[]`, drop any candidate whose name matches the inventory from Step 3 (already-installed skills/agents/plugins). The script doesn't know the user's installed state; init applies that filter post-hoc.

### Step 5d — Log section 5

**Log section 5 NOW — do not defer.** Format: [log-format.md §5](references/log-format.md)
(stack input, sources, queries, ranked-candidates table, dropped-already-installed).
Then pass `final_candidates[]` to Step 6.

## Step 6: Expand via index-repos (parallel local scan)

Extract the **unique set of `<owner>/<repo>` values** from discover.mjs' candidates (Step 5). Also include this small list of auto-available repos (not on skills.sh but always relevant):

```
- anthropics/claude-plugins-official   (official, built-in to Claude Code)
- anthropics/claude-code               (demo plugins)
```

For each unique repo, dispatch a **`repo-indexer` sub-agent** via Task tool. **Send ALL dispatches in a SINGLE message** so they run in parallel.

```
SINGLE MESSAGE with N Task tool calls (one per unique repo):

Task(subagent_type="repo-indexer", prompt="Index <repo-1>. project_root=<abs-path>. force_refresh=false")
Task(subagent_type="repo-indexer", prompt="Index <repo-2>. project_root=<abs-path>. force_refresh=false")
...
Task(subagent_type="repo-indexer", prompt="Index <repo-N>. project_root=<abs-path>. force_refresh=false")
```

Each sub-agent invokes `scan_repo.mjs` which does ONE shallow clone + filesystem scan + writes its own index file. They are isolated — no shared state, no contention. The slowest repo determines total wall-clock time (~30-60 sec for big repos like wshobson/agents).

Wait for all to complete. Collect their one-line summaries.

Each `repo-indexer` writes to `<project>/.ievo/cache/index/<owner>-<repo>-<hash>.md` (no conflicts — different paths per repo, hash-suffixed so colliding slugs can't overwrite each other; the hash isn't computable from text alone, so resolve the actual file via Glob `<owner>-<repo>-*.md` rather than assuming the literal name).

(Parallel via sub-agents: ~30-60s cold-cache wall-clock for 8 repos vs ~4-8 min
sequential — slowest repo wins, each has isolated context + returns one summary
line; cache hits sub-second. Surface progress via `tail -f .ievo/log/init-*.md`.)

Read each generated index and **expand the candidate list**:
- All standalone skills from index
- All standalone agents from index
- All plugins from index (each plugin = candidate with `type: plugin`)

Now your candidate list has three types: `skill` (vendor), `agent` (vendor),
`plugin` (marketplace settings).

**Log section 6 NOW — do not defer** (index-repos can take 5-15 min for big repos
like wshobson/agents). Format: [log-format.md §6](references/log-format.md).

## Step 7: Categorical ranking — top-N per category

Filter and rank **per category** (not overall):

### Step 7a — Filter

- **Platform filter (Codex only, Step 1.5's detection rule)** — drop every `type: agent`
  candidate, reason `"not installable on Codex: Codex loads only skills
  (.agents/skills); no documented project-level custom-agent path"` (per the
  [Codex skills docs](https://developers.openai.com/codex/skills) — re-check on
  a major Codex release and lift this filter if agent loading ships). Same
  visible-drop semantics as the stack-relevance filter below: logged in
  section 6b with the reason, never silent. Do NOT vendor an agent `.md`
  anywhere on Codex — a copy under `.claude/agents/` would be invisible to the
  client that installed it, the exact issue #432 failure. On Claude Code this
  filter is a no-op.
- **Drop candidates whose name conflicts with installed inventory** (already-installed check applies to expanded list, not just discover.mjs' direct returns).
- **Match name + description against stack/deps**:
  - Direct keyword match (skill named "pytest" for Python project with pytest) → high score
  - Description mentions deps from step 4 → medium
  - Generic universally-useful (e.g. "code-reviewer") → low but non-zero
- **Capability-overlap filter** (issue #427 — real `/ievo:init` telemetry: 3 of 4 declines in one run were pure overlap 

…(truncated)
