/archcore:init
First-time onboarding. Detects repo scale (small / medium / large) and shape, composes a scale-appropriate seed of .archcore/ documents, shows them in one preview, and creates them on a single confirm — so the code-alignment hook injection and per-command grounding have substance and the relation graph is live from day one. The same confirm also installs host wiring (project MCP config, SessionStart hook, usage hint — the same files archcore init writes), so the repo works for CLI-only teammates. Per magic-first-day-init.adr: extractive facts are composed in full; the top hotspot modules get real specs (synthesized only after confirm); the overview is an index, never a prose blob. Nothing is written before confirm. Exact per-mode output is in the Routing Table below.
Arguments
--depth=light|standard|deep — synthesis budget (default standard), orthogonal to --mode. See the Depth axis section below. Also settable via the depth:<tier> toggle in the preview.
--mode=small|medium|large — force a mode, overriding auto-detection.
--domain=<slug> — re-run focused on one domain (large repos): scopes data-model + hotspot specs to that domain's tree, tops up only its docs. Bypasses the "already seeded" early-exit.
--refresh — re-run on an already-seeded repo to add facts that appeared since the first init (a new schema, config, or modules) — and to retrofit host wiring on repos seeded before wiring existed. Bypasses the early-exit; existing docs are skipped, missing ones composed.
When to use
- Empty
.archcore/ — the SessionStart nudge points here.
- First session on a fresh clone / fresh install.
- User says: "initialize archcore", "set up archcore", "seed archcore", "first-time setup", "what should I do first".
Not init (route elsewhere):
- Recording a specific decision →
/archcore:document.
- Planning a feature →
/archcore:plan.
- Documenting one module →
/archcore:document.
- Codifying a team standard →
/archcore:document (offers rule + guide continuation).
- Reading applicable context before coding → automatic; the code-alignment hook injects it on file edits.
- Docs health audit →
/archcore:review.
Routing table
Mode routing — Step 0.5 classifier, evaluated top-to-bottom, first match wins. The empty route is decided earlier in Step 0(b). Precise conditions in _shared/grounding/detect-scale.md.
| Signal |
Route |
Seeded (composed when detected) |
| No manifest AND no top-level source (Step 0b) |
→ empty |
no content docs — host wiring only, behind its own mini-confirm |
--mode=X flag |
→ forced X (detected mode still reported) |
per row below |
domain_count ≤ 1 AND module_count ≤ 15 |
→ small |
stack rule, run guide, data-model, integrations, config, entry points, public surface, overview + hotspot specs (coverage rate × ranked pool — see Depth axis) |
domain_count ≤ 2 AND module_count ≤ 40 |
→ medium |
small set + cross-cutting rules (every depth, every candidate clearing the recurrence threshold) + hotspot specs (coverage rate × ranked pool — see Depth axis) |
domain_count ≥ 3 OR module_count > 40 |
→ large |
medium set + top-level map + domain dialog + data-model per schema-bearing domain (all, not only selected) + hotspot specs (coverage rate × ranked pool, plus a floor of ≥ 1 spec per selected domain — see Depth axis) |
Every non-empty mode also composes the architecture-overview capstone, plans relation wiring, and offers agent-file import — aggregate files (CLAUDE.md / AGENTS.md / .cursorrules) as link stubs and modular rule files (.cursor/rules/*.mdc, .github/instructions/*.md, .windsurf/rules/*.md) as rule docs by default, per lib/agent-files.md — inside the preview. Tier-1 facts (data-model, integrations, config, entry points, public surface) are seeded in any mode when detected — breadth scales with the repo, presence does not. The public-surface fact is what carries the seed for library / SPA / multi-command-CLI / agent-plugin repos, where there is no server to enumerate as entry points. The empty route exits after Step 0.
Follow-up routing — closing-message hand-offs. Init surfaces these as todos; MUST NOT auto-invoke.
| User wants to... |
→ Invoke |
| Capture another module |
/archcore:document <path> |
| Record a decision |
/archcore:document |
| Codify a convention as a rule |
/archcore:document |
| Plan a feature |
/archcore:plan |
| Drill into another domain (large) |
/archcore:init --domain=<slug> |
| Add facts that appeared since first init |
/archcore:init --refresh |
| Scope queries to a domain (large) |
mcp__archcore__search_documents with the domain tag |
| See what's loaded |
/archcore:review |
Depth axis (--depth=light|standard|deep)
Orthogonal to scale (--mode, which measures repo size). Depth sets the synthesis budget, not the artifact checklist. Extraction is always on in every depth — Tier-1 facts, imported authored rules, and the hotspot register are cheap and the highest-value / most-durable layer. Depth scales only the expensive, staleness-prone synthesis: spec bodies, cross-cutting rules, and big-file / aggregate extraction. Default: standard — a good first-day seed, not merely the cheapest one. Init is fully gated (nothing is written before confirm, and the preview shows all three depths' costs side by side before the user commits to any of them), so there is no reason to default to the thin tier just to be safe — light is the explicit opt-down for a cost-conscious user (still never empty — Universality invariant 3); deep is the explicit opt-up for a max plan.
| Depth |
Hotspot specs (rate × pool, floor) |
Cross-cutting synth (medium/large) |
Big authored (>200) & CLAUDE.md/AGENTS.md |
Authored decisions → ADR |
Relations |
| light (opt-down) |
10% of the ranked pool, floor 3 |
every candidate clearing the recurrence threshold — MAY narrow scan toward guard + shared-indirection primitives for cost, but MUST still surface any high-confidence hit |
link |
— |
basic |
| standard (default) |
25% of the ranked pool, floor 4 |
every candidate clearing the recurrence threshold |
link |
— |
basic |
| deep (opt-up) |
60% of the ranked pool, floor 6 |
every candidate clearing the recurrence threshold |
extract + split |
extract from files (Route 2), never invent from code |
enriched (spec↔rule, spec↔spec) |
The spec budget scales with the repo and carries no absolute maximum — budget = max(floor(depth), round(rate(depth) × pool_size)), clipped to pool_size, where pool_size is the eligible ranked hotspot pool (detect-hotspots.md "Spec budget by coverage rate"). A 214-module pool budgets ~54 specs at standard; a 12-module pool budgets 4. Large mode adds a floor of ≥ 1 spec per domain selected in Step A.0, filling the rest by repo-wide rank; a later --domain=<slug> re-run applies the same formula to that domain's narrowed pool. Cross-cutting synthesis is on at every depth — it is the highest value-per-token artifact init seeds; depth changes only its scan cost at light, never whether it runs and no longer how many candidates survive. A very large or hot hotspot (LOC > 3000 OR top-quartile churn) may compose as a flagship at any depth, which makes it eligible for decomposition into ≤ 3 sub-specs by separable sub-surface instead of one spec (detect-hotspots.md "Flagship specs"). Every spec, flagship or not, is composed under the one ≤ 120-line cap in _shared/spec-contract.md.
Cost scales with depth AND with the repo — the preview shows the computed total per depth, never a constant. On a large repo deep can budget hundreds of specs; that is the intended behavior, and the preview's per-depth estimate is where the user sees the price before confirming. Treat any fixed multiplier as illustrative only.
Universality invariants — hold in EVERY depth and for ANY codebase
- Ceiling, not quota. A depth raises the budget; it NEVER fabricates to hit a number. If the ranked hotspot pool has 5 modules,
deep produces at most those 5 specs, never a padded count; on a sparse repo deep ≈ light. Same for cross-cutting: if only 1 candidate clears the "surface nothing over a false rule" bar, that is the output at any depth. "Prefer omission over a guess" holds in every depth. The budget is a share of real evidence — an empty pool yields an empty seed, whatever the rate.
- "When detected", never "always." No depth has a fixed artifact checklist. data-model / integrations / config / entry-points / imports / cross-cutting appear only on positive evidence, identically across depths — a depth is defined by synthesis budget, not by mandatory docs.
light is never empty. Its floor rests on the universal spine — stack rule + run guide + public-surface (a library's exports, a CLI's commands, a plugin's skills, a SPA's routes) + register + the budgeted specs via the test-independent fallback ranking. At least one fires for any base (library, SPA, ML, CLI, embedded, data/IaC, agent-plugin/markdown, polyglot, monorepo); light never degrades to nothing for lack of schema/tests/authored files.
- Depth lives in Phase B (compose), not detection. No depth branch adds a stack-specific detection heuristic; detection stays high-level, evidence-first, non-exhaustive (guarded by the detect-catalog universality test).
deep's extra budget flows to whatever the repo affords. No authored files → nothing to extract, so the budget goes to specs/relations that DO have evidence. deep is "more of what this repo actually has," not a fixed feature list assuming a stack shape.
Selection: the --depth flag, or the depth:<tier> toggle in the preview (Phase C) — the user can flip depth after seeing the plan and its per-depth cost, then still edit individual items.
Execution
Content voice: default to architectural prose — decisions, rationale, intent. See skills/_shared/precision-rules.md Rule 6. Code blocks only where the document type requires it (rule, guide, cpat, and spec examples) or the user asks.
Pre-flight: CLI availability check
Before any init step, verify that the Archcore CLI is available on PATH. The canonical installer is documented at https://docs.archcore.ai/cli/install/ — use it as the single source of truth; do not suggest other channels (brew, go install, etc.) even if the user mentions them.
- Run:
archcore --version (via Bash tool)
- If it succeeds → check the host-wiring version gate with the deterministic helper (never compare versions yourself — lexical comparison breaks on double-digit fields). Resolve
$d in this same Bash call (each Bash invocation is a fresh shell — nothing persists from a later step), exactly as the Step -1 probe below does: run d="${CLAUDE_SKILL_DIR:-<absolute dir of this SKILL.md>}"; "$d/../../bin/cli-gte" 0.7.0. It prints exactly one token:
yes → proceed immediately to Step -1 (host wiring enabled).
__NO_CLI__ (unexpected here — --version just succeeded) → treat as no.
no → the seed still works, but the host-wiring step (see "Host wiring" below) needs a newer CLI. Ask the user once:
Archcore CLI <version> is older than v0.7.0 — host wiring (project MCP config, SessionStart hook, usage hint) will be skipped. Update now via archcore update? (y/N)
- On
y → run archcore update (Bash), re-run the cli-gte 0.7.0 check, and proceed to Step -1 (host wiring enabled on yes, disabled otherwise).
- On
N / silence → proceed to Step -1 with host wiring disabled: omit the Host wiring line from the preview, skip Phase E step 0 entirely (the cascade never runs — its manual-fallback leg is NOT a substitute for this note), and in the closing message note: "Host wiring skipped (CLI < v0.7.0) — update with archcore update, then run archcore init --agent <host> --project "<root>" in a terminal to make this repo self-contained for CLI-only teammates." (<host>/<root> come from the Step -1 probe, which runs regardless of the gate.)
- If it fails (command not found):
- Detect the platform via
uname -s (Bash). Darwin/Linux → POSIX path. Anything else (Windows native) → instruct-only path.
- POSIX path — ask the user once:
Archcore CLI not found. The official installer runs:
curl -fsSL https://archcore.ai/install.sh | bash
Run it now? (y/N)
- On
y → execute the command exactly as shown (Bash tool). After it returns, re-run archcore --version.
- Success → print: "Archcore CLI installed (
<version>). Proceeding with init." → apply the same v0.7.0 comparison from item 2 (a fresh install is normally current, so host wiring is enabled) → go to Step -1.
- Still failing → print the install message below and stop.
- On
N / silence / instruct-only path → print and stop:
Archcore CLI required. Install it, then re-run /archcore:init:
- macOS / Linux / WSL:
curl -fsSL https://archcore.ai/install.sh | bash
- Windows (PowerShell 5.1+):
irm https://archcore.ai/install.ps1 | iex
- Verify:
archcore --version
- Full docs: https://docs.archcore.ai/cli/install/
Do not attempt brew install, go install, package-manager wrappers, or any other install command — they are not the supported path and will produce a CLI that is not version-compatible with the plugin.
Pre-flight: gating and lazy reading
Two disciplines bind the whole run:
- Gating (write boundary).
init_project() and the read-only MCP calls (list_documents, get_document) are infrastructure — they run before the preview. The gated operations are create_document, add_relation, and the host-wiring writes (install_host_config / archcore init --agent — they touch files outside .archcore/, like .mcp.json and .claude/settings.json): none fire before the user types confirm. cancel therefore leaves .archcore/ content-empty and the repo's host configs untouched (the directory and settings.json may exist from init_project, which is harmless and idempotent).
- Lazy reading (two sub-phases). The detection/extraction catalogs at
_shared/grounding/*.md and the composition files at lib/*.md are heavy (≥ 1000 lines combined) — read them in two ordered batches, never all at once. The Detect sub-phase (Phase A) loads the detection catalogs and, for each detector it runs, captures into working memory both the signals AND the small ## Output create-fields + body template it will reuse later. The Compose sub-phase (Phase B) loads the composition contracts (_shared/precision-rules.md, _shared/spec-contract.md, _shared/rule-contract.md, lib/compose-overview.md, _shared/grounding/extract-routing.md) and reuses the Output fields/templates already captured during Detect — it does not re-read the bulky detection heuristics. "Release the detection catalogs" at the end of Phase A means dropping their heuristic prose from focus, not the captured Output specs.
Step -1: Initialize, detect host, and acknowledge (fast)
Call mcp__archcore__init_project() exactly once (pre-gate infrastructure — idempotent, safe on an already-initialized project). It creates .archcore/ and settings.json if missing.
Immediately after, give the user a one-line confirmation:
- Response includes
initialized: true (created now) — print: "Archcore initialized at .archcore/."
already_initialized: true — print nothing here; the existing knowledge base speaks for itself in Step 0(a).
Host + project root for wiring — always run this probe, even when host wiring is disabled by the pre-flight version gate (it is one cheap Bash call, and the disabled-path closing message still needs <host>/<root>). One Bash call:
d="${CLAUDE_SKILL_DIR:-<absolute dir of this SKILL.md>}"; host=$("$d/../../bin/detect-host"); root=$(git rev-parse --show-toplevel 2>/dev/null || pwd); printf '%s\n%s\n' "$host" "$root"
${CLAUDE_SKILL_DIR} is set by Claude Code only. On other hosts (Cursor, Codex, GitHub Copilot CLI) substitute the absolute directory of this skill file — you know it from having read this file; bin/detect-host is two directories up from it (<plugin-root>/bin/detect-host).
bin/detect-host resolves the current host from environment only (never cwd or stdin — Cursor guarantees neither) and prints exactly one token: claude-code | cursor | codex-cli | __UNKNOWN__. A GitHub Copilot CLI session always lands on __UNKNOWN__ — Copilot sets no environment marker in the shell commands it runs, so it is resolved by the question below rather than by the probe (rationale in bin/detect-host). If the probe returns __UNKNOWN__ or anything else than the three host tokens (empty output, a path error — treat all the same), ask one AskUserQuestion — "Which AI host is this session running in?" with options Claude Code / Cursor / Codex (CLI or desktop app) / GitHub Copilot CLI — and map the answer to the agent id (claude-code / cursor / codex-cli / copilot). The Codex option names both surfaces on purpose: they share one binary, one ~/.codex/config.toml and one plugin install, so codex-cli is the agent id for the desktop app too and there is no codex-desktop (codex-adapter.spec). Remember host and root for the Host wiring preview line and Phase E; do not re-run the probe.
init_project initializes only .archcore/ — host wiring (MCP config, hook, usage hint) is planned in the preview and executed in Phase E, never here. Do not run archcore init yourself at this step; the terminal path is the Phase E fallback for the user, not a pre-flight action.
Step 0: Check state and source signal
Two cheap probes, in order. Each can short-circuit the whole skill. Neither reads anything under _shared/grounding/ or lib/.
Step 0(a) — Existing documents
Call mcp__archcore__list_documents() once. Derive every flag below from local documents only — skip any result carrying global: true / read_only: true / source_kind: "global". A mounted global source may already provide a stack rule or an overview, but it must not satisfy the already-seeded early-exit: init seeds THIS repo's documents, and a repo with globals mounted but no local seed is still unseeded. If any global results appear, load skills/_shared/globals.md; never modify a global document and never target one with add_relation. Derive:
has_stack_rule — a rule whose title contains "stack" in conventions/.
has_run_guide — a guide whose title contains "run"/"running" in onboarding/.
has_data_model — any doc tagged data-model.
has_integrations — any doc tagged integrations.
has_config — any doc tagged config.
has_entry_points — any doc tagged entry-points.
has_surface — any doc tagged surface.
has_top_level_map — any doc tagged top-level-map.
has_overview — any doc tagged architecture-overview.
has_imports — any document tagged imported.
Already-seeded early-exit. If has_stack_rule AND has_run_guide AND has_overview are all true AND neither --refresh nor --domain was passed, reply:
Init already seeded this repo. Applicable context auto-injects on file edits via the code-alignment hook; use /archcore:review for the dashboard. To add facts that appeared since (a new schema, config, or modules), re-run /archcore:init --refresh; to drill into another domain, /archcore:init --domain=<slug>. (Seeded before host wiring existed, or missing the host configs? --refresh also adds host wiring — MCP config, SessionStart hook, usage hint.)
Then stop. With --refresh or --domain, skip this early-exit and proceed — every already-present artifact is marked skip (exists) in the preview and only missing ones are composed; the Host wiring line appears as usual (its writes are idempotent — already-wired hosts show as skip/converge). (--domain additionally scopes the run to one domain; see Step A.0.)
Step 0(b) — Source-signal gate (empty-repo early exit)
Single filesystem probe — one shell call, no catalog reads. Detect whether the repository has any executable shape yet:
has_manifest — at least one of these exists at the project root (depth ≤ 2 for monorepo workspaces): package.json, pyproject.toml, Pipfile, requirements.txt, Cargo.toml, go.mod, Gemfile, composer.json, *.csproj, *.fsproj, *.vbproj, pom.xml, build.gradle, build.gradle.kts, mix.exs, Package.swift. This list is seed examples, not exhaustive — also treat ANY project-defining manifest or build file as a manifest (e.g. CMakeLists.txt, Makefile, dune-project/*.opam, deps.edn/project.clj, pubspec.yaml, build.sbt, stack.yaml/*.cabal, *.tf/*.tfvars, Chart.yaml, project.godot, *.sln, Project.toml, and agent/LLM-plugin manifests such as marketplace.json / plugin.json / .claude-plugin/*).
has_top_level_source — at least one file with a recognizable source extension exists anywhere under the project root, capped at depth 3, excluding .archcore/, .git/, node_modules/, vendor/, dist/, build/, out/, target/, coverage/, .venv/, __pycache__/, .next/, .turbo/. Extensions: .ts, .tsx, .js, .jsx, .mjs, .cjs, .py, .rs, .go, .rb, .php, .java, .kt, .kts, .swift, .cs, .fs, .ex, .exs, .scala, .clj, .cljs. The extension list is seed examples, not exhaustive — also count any file whose contents are plainly source (a shebang, or import/include/package/module/def/func/class/use constructs), and recognize other common code extensions (e.g. .vue, .svelte, .astro, .dart, .c, .cc, .cpp, .h, .hpp, .m, .mm, .ipynb, .hs, .ml, .mli, .tf, .sol, .lua, .jl, .r, .zig, .nim, .gd).
If BOTH are false, take the empty route. No content seed — but host wiring still applies (an empty repo is exactly where a teammate going CLI-only needs the configs).
When host wiring is disabled by the pre-flight version gate, reply with exactly this and stop (no writes):
Archcore is ready at .archcore/. No source code detected yet — no content to seed. Host wiring skipped (CLI < v0.7.0) — update with archcore update, then re-run /archcore:init. The SessionStart empty-state nudge will keep pointing here until then.
Otherwise show a mini-preview:
Archcore is ready at .archcore/. No source code detected yet — no content to seed.
One thing worth doing now — host wiring, same files archcore init writes (makes the repo work for teammates using the CLI without this plugin):
Host wiring (<host>) → <root>
• <per-host file list — e.g. for claude-code: .mcp.json · .claude/settings.json (SessionStart hook) · CLAUDE.md + AGENTS.md (managed block)>
confirm to write these, cancel to leave the repo untouched. Re-run /archcore:init after the first manifest or source file lands — the SessionStart empty-state nudge will keep pointing here until then.
On confirm → execute the Host wiring cascade (Phase E step 0) and stop. On cancel → stop with no writes. Either way, do NOT create placeholder documents — they have no practical value, cost roundtrips and tokens, and suppress the SessionStart empty-state nudge that is the user's breadcrumb back here.
Otherwise (has_manifest OR has_top_level_source), proceed to Phase A.
Phase A — DETECT (no writes; detection catalogs only)
Compute everything the seed needs in one detection pass. No documents are created here, and no composition contract is opened. For each detector, capture its signals AND its ## Output create-fields for reuse in Phase B/E.
Detect high-level, for ANY stack. Each detect-* catalog leads with what it detects (the concept) and a universal, evidence-first method; its concrete lists of frameworks / ORMs / SDKs / extensions / conventional roots are non-exhaustive examples, not a checklist. When a project's language, framework, or layout is unfamiliar or highly specific, reason from first principles per the catalog — the entry file's imports, the dominant file types, the manifest / build system, and what the code actually does — and emit a fact only on positive evidence (prefer omission over a guess). Never return empty / small / "no entry points" merely because nothing matched a list.
Step 0.5: Scale
Read _shared/grounding/detect-scale.md, _shared/grounding/detect-domains.md, _shared/grounding/detect-modules.md.
- Parse arguments —
--depth=light|standard|deep (synthesis budget, default standard; see the Depth axis section), --mode=X (force the mode), --domain=<slug> (force a large-mode single-domain pass; see Step A.0), --refresh (already consumed in Step 0a). Depth does NOT affect detection — Phase A ranks hotspots up to the deep-depth ceiling (see Step A.3) and detects ALL facts/imports regardless of the active depth; depth only governs how much is synthesized in Phase B.
- Compute signals:
domain_count (per detect-domains.md), module_count (source files > 100 LOC, excluding tests/generated), entry_point_count (per detect-entry-points.md, informational).
- Classify per
detect-scale.md — apply its evidence-based fallback when the language/layout is unlisted (recompute counts from the dominant code extension and tracked-file breadth; do not default to small just because the extension/root lists miss). A forced --mode wins but remember the auto-detected one; --domain forces large-mode behavior scoped to the named domain.
Step A.0: Domain selection (large mode only)
Skip unless mode is large.
--domain=<slug> given — that domain is the sole selection; skip the dialog. (Tier-1 facts already present are skipped; the run tops up this domain's data-model + hotspot specs by applying the depth's rate / floor to the pool narrowed to that domain's tree, per detect-hotspots.md.)
- Otherwise — present the top 5 ranked domains (per
detect-domains.md ranking) and ask: "Which domains are you working on now? (pick 1–3 by name or number, or skip to defer.)" Accept a single name, a comma list, or skip.
- Allocate the hotspot budget. Hotspots (A.3) are ranked repo-wide (candidate selection is never restricted to a domain's tree in the day-one dialog), and the budget itself comes from the repo-wide pool:
max(floor(depth), round(rate(depth) × pool_size)) per detect-hotspots.md "Spec budget by coverage rate". The selection changes allocation, not size: every selected domain is guaranteed a floor of ≥ 1 spec; remaining slots fill by repo-wide rank across all domains, selected or not. On skip, no domain gets a floor and the whole budget fills by repo-wide rank alone.
- Data-model breadth is decoupled from the dialog. Seed a data-model doc for every domain with a detectable schema (
detect-data-model.md, names-only — cheap regardless of repo size), not only the domains selected here. The dialog focuses hotspot-spec priority, not data-model breadth. A domain without a schema still appears as a row in the top-level map (detect-domains.md).
Remember the unselected domains for the closing message.
Step A.1: Shape — single manifest batch
Read _shared/grounding/detect-stack.md, _shared/grounding/detect-data-model.md, _shared/grounding/detect-integrations.md, _shared/grounding/detect-config.md. Read each manifest file once (package.json, pyproject.toml, Cargo.toml, go.mod, schema.prisma, .env.example, …) and feed all four detectors from that shared parse — never re-read a manifest per detector. Collect:
- Stack signals (≤ 5) — per
detect-stack.md.
- Data model — entities + key relations, NAMES ONLY, per
detect-data-model.md (large mode: one doc per domain, seeded for EVERY domain with a detectable schema — not scoped to the Step A.0 selection; see Step A.0.4). Skip if no schema anywhere.
- Integrations — external services from allowlisted SDK deps, per
detect-integrations.md. Skip if none.
- Config surface — env-var NAMES + purpose, never values, per
detect-config.md. Skip if no env contract.
Step A.2: Run commands, entry points & surface
- Run commands — per
_shared/grounding/extract-run-instructions.md (README section → scripts → ask the user once if neither yields anything).
- Entry points — per
_shared/grounding/detect-entry-points.md, bucketed HTTP / CLI / Worker / Cron / Other. Seed the entry-point doc in any mode when ≥ 1 entry point exists; in large mode group by domain.
- Public surface — per
_shared/grounding/detect-surface.md. The role-based outward shape the entry-point inventory does NOT cover: web routes/pages, a library's exported API, a multi-command CLI's command catalog, an agent-plugin's skills/commands, mobile screens. Seed the public-surface doc when such a surface exists and is not already fully enumerated as entry points; in large mode group by domain. This is the fact that gives library / SPA / plugin / markdown-tooling repos a substantive seed.
Step A.3: Hotspots & cross-cutting (candidates only — NO source reads)
- Hotspot candidates — rank per
_shared/grounding/detect-hotspots.md and collect signal data (path + LOC + companion-test LOC + suggested type) for the whole eligible ranked pool, not a per-depth slice of it. The pool size is itself an input to every depth's budget, and signal collection reads no source files, so one Detect pass serves any --depth, including a later depth: toggle in Phase D, with no re-read. Phase B then keeps the active depth's budget as spec stubs — max(floor(depth), round(rate(depth) × pool_size)), clipped to the pool (detect-hotspots.md "Spec budget by coverage rate") — subject to large mode's per-selected-domain floor of ≥ 1 spec; ranked candidates beyond the budget go to the overview register (compose-overview.md Part 3) as → /archcore:document rows. The catalog ranks in two tiers: a tests-aware primary tier, and — when it fills fewer slots than the budget — a test-independent fallback (fan-in / public surface / size / churn) so repos with no tests (scripts, SPAs, ML, CLIs, agent-plugin/markdown tooling) still surface real specs instead of an empty pool. Mark fallback-tier stubs with their qualifying signal. A candidate clearing the flagship gate (LOC > 3000 OR top-quartile churn) is flagged as such in the stub, for Phase E's one-spec/decomposition choice (detect-hotspots.md "Flagship specs"). Tier-2 artifacts are always composed as spec — use the adr/task-type hints in detect-hotspots.md only to filter out ineligible candidates (e.g. a utils/helpers module, or one failing spec-contract.md's "when NOT to write a spec"), never to switch the document type. Do not read source files yet — that read is deferred to Phase E for kept specs only.
- Cross-cutting candidates (medium and large, whole-repo, every depth) — per
_shared/grounding/detect-cross-cutting.md, every candidate that clears the recurrence threshold, with no per-depth count cap (Change: the light ≤2 / standard ≤3 / deep ≤4 caps are removed — the conservative "surface nothing over a false rule" bar is the only gate). init uses that catalog for detection only and overrides its standalone y/n "Output" flow: each candidate becomes a Tier-2 rule stub here and is created in Phase E, not handed to /archcore:document.
Step A.4: Agent files
Detect all agent-instruction candidates per lib/agent-files.md (paths + byte sizes + class) — do not stop at CLAUDE.md/AGENTS.md; enumerate the modular directories (.cursor/rules/*.mdc, .github/instructions/*.md, .windsurf/rules/*.md) too. Sizing exception for CLAUDE.md / AGENTS.md: compute their size and non-emptiness only after stripping any archcore managed block (<!-- archcore:start --> … <!-- archcore:end -->) — never from a raw file-size probe; a file whose only content is the managed block is not a candidate at all and must not appear in the preview (lib/agent-files.md → Probe paths). lib/agent-files.md assigns each file a class that sets its default import mode:
aggregate (CLAUDE.md, AGENTS.md, .cursorrules, …) — default link (one pointer doc). Extract is opt-in.
modular-rule (.cursor/rules/*.mdc and equivalents) — default extract: one rule/doc per file, classified by content (genuine conventions → conventions/), reproduced verbatim. A file > 200 lines degrades to link.
Estimate extract yield without loading extract-routing.md: aggregate → count H1/H2/H3 headings, capped at 10; modular-rule → 1 per file (they are one rule each). Compute the cost tier per class: aggregate HIGH if combined aggregate size > 50 KB OR estimated aggregate yield > 8 docs; modular-rule HIGH if combined modular-rule size > 50 KB (file count is NOT a signal — many small rule files are cheap). HIGH gates only whether extract needs an explicit opt-in.
Step A.5: Announce
Print one detection line, e.g.:
Mode: medium (28 modules, 1 domain). Detected: Prisma (6 entities), Stripe + AWS, 12 env vars, 5 entry points, 5 hotspot candidates, 1 cross-cutting pattern, CLAUDE.md (4 KB) + 6 .cursor/rules files (18 KB, modular). Composing the plan…
In large mode, report the figures for the selected domains (selection already happened in Step A.0). Detection done — release the detection catalogs (heuristic prose), keeping the captured Output specs.
Phase B — COMPOSE (in memory; composition contracts only)
Load the composition contracts and compose every planned artifact without writing. Honor each catalog's line cap. Mark any artifact whose has_* flag is already true as skip (exists). Exception: in large / --domain mode the per-domain data-model doc (<domain-slug>-data-model) dedupes by its own filename, not the repo-wide has_data_model tag — so a newly-selected domain's data-model is still composed when other domains' already exist.
Apply the active depth (## Depth axis, default standard) to this compose pass — it sets only these levers, and everything else is depth-independent:
Hotspot spec count = the depth's computed budget (detect-hotspots.md "Spec budget by coverage rate"), large mode subject to the per-selected-domain floor; ranked hotspots beyond it go to the register regardless of depth. A flagship candidate (Change: size/churn-gated) composes as one spec or, only with genuine separable sub-contracts, decomposes into ≤ 3 sub-specs — at every depth, not gated by depth.
Cross-cutting synthesis runs at every depth now (medium/large), and every candidate clearing the recurrence threshold becomes a stub — depth no longer trims the count. light MAY narrow the scan toward the guard + shared-indirection primitives for cost control but MUST still surface any high-confidence candidate it finds.
Import mode: light/standard → aggregate link, big (>200) modular link; deep → aggregate extract, big modular extract + split.
ADR-from-authored-decisions and enriched relations: deep only.
Depth is a budget ceiling, never a quota — compose only what the repo affords on positive evidence (Universality invariant 1); a sparse repo at deep yields the same as light.
Tier-1 facts (full bodies, cheap/extractive):
- stack rule —
detect-stack.md template (≤ 6 lines).
- run guide —
extract-run-instructions.md (single-app ≤ 15 lines; monorepo per-app ≤ 6).
- data-model doc —
detect-data-model.md Output (≤ 40 lines), when detected; large mode: one per domain, for every schema-bearing domain.
- integrations doc —
detect-integrations.md Output (≤ 15 lines), when detected.
- config doc —
detect-config.md Output (≤ 20 lines, NAMES ONLY), when detected.
- entry-point inventory —
detect-entry-points.md Output, when ≥ 1 entry point.
- public-surface doc —
detect-surface.md Output (≤ 25 lines, NAMES + purpose only), when a surface exists that entry points don't already cover.
- top-level map —
detect-domains.md Output (large mode).
Tier-2 stubs (NO source reads):
- hotspot specs — one stub each for the active depth's budget (
detect-hotspots.md), large mode subject to the per-selected-domain floor: suggested spec title, the qualifying LOC / test-ratio, target filename + directory, a flagship marker when the size/churn gate is cleared, and an estimated synthesis cost ≈ (source_LOC + test_LOC) × 6 tokens. The full body is composed only after confirm. Ranked hotspots beyond the budget are not stubbed — they go to the overview register (compose-overview.md Part 3) at ~0 cost.
- cross-cutting rules (medium/large, every depth, no count cap) — one stub each: the pattern + the paths it would govern. Full body composed after confirm under
rule-contract.md. Drop a stub whose pattern is already covered by an imported authored rule (dedup per detect-cross-cutting.md) and note the skip under that import.
Capstone: plan the architecture-overview per lib/compose-overview.md. Its body indexes the confirmed seed, so it is composed in Phase E once the set is final. List it in the preview as "Architecture overview — index of the above".
Agent-file import: behavior is set by file class from lib/agent-files.md (captured in Detect):
- Aggregate (CLAUDE.md, AGENTS.md, .cursorrules, …): default link (one pointer
doc, ~0 cost). Extract is opt-in via edit; the aggregate-HIGH flag gates that opt-in.
- Modular-rule (
.cursor/rules/*.mdc and equivalents): default extract per _shared/grounding/extract-routing.md — one document per file (they are one rule each), classified by content: a genuine convention → rule in conventions/; a reference/role/meta file → doc in imported/. Title from the frontmatter description:, body verbatim, status: draft. A file > 200 lines degrades to link (opt-in extract to split). No synthesis.
- Dedup: after both sets are assembled, drop any cross-cutting stub whose constraint is already covered by a modular-rule file imported as a
rule (same symbol/module) — prefer the authored rule; never create both.
Reuse the agent-files.md encoding (imported + source:<slug> tags, pointer first line) for all modes.
Planned relations: per the compose-overview.md "Relation wiring" table.
No create_document / add_relation has run yet.
Phase C — PREVIEW (one manifest)
Present the entire plan as a single grouped manifest, then wait. Example:
Init plan — scale: medium · depth: standard (default). confirm / edit / depth:light / depth:deep / cancel
Coverage: 4 specs / 11 load-bearing modules (36%) · 1 cross-cutting rule
Facts (created in full):
• Project stack — rule [new]
• Running the project — guide [new]
• Data model — doc (6 entities) [new]
• External integrations — doc (Stripe, AWS) [new]
• Configuration — doc (12 vars) [new]
• Entry points — doc (5) [new]
• Public surface — doc (8 routes) [new]
Synthesis (bodies composed only if kept):
• spec: token-rotation — 235 LOC src / 968 LOC tests ~7k [new]
• spec: auth-client — 52 LOC src / 0 tests ~1k [new]
• rule: request-context — cross-cutting
…(truncated)
1---2name: init3description: First-time Archcore setup. Detects repo scale and shape, then composes a full first-day seed — stack rule, run guide, data-model, integrations, config, entry points, public surface, a linked architecture overview, and specs for the top hotspot modules — shown in ONE preview and created on a single confirm, plus host wiring (MCP config, hooks, CLAUDE.md/AGENTS.md managed block). Imports agent-instruction files — aggregate files (CLAUDE.md/AGENTS.md/.cursorrules) as link stubs, modular rule files (.cursor/rules/*.mdc and equivalents) as rule docs by default. Use on a fresh clone, empty `.archcore/`, 'set up archcore', or to wire host configs (MCP/hooks/CLAUDE.md+AGENTS.md). Not for individual docs or planning.4---56# /archcore:init78First-time onboarding. Detects repo scale (small / medium / large) and shape, composes a scale-appropriate seed of `.archcore/` documents, shows them in **one preview**, and creates them on a **single `confirm`** — so the code-alignment hook injection and per-command grounding have substance and the relation graph is live from day one. The same confirm also installs **host wiring** (project MCP config, SessionStart hook, usage hint — the same files `archcore init` writes), so the repo works for CLI-only teammates. Per `magic-first-day-init.adr`: extractive facts are composed in full; the top hotspot modules get real `spec`s (synthesized only after confirm); the overview is an index, never a prose blob. **Nothing is written before `confirm`.** Exact per-mode output is in the Routing Table below.910## Arguments1112- `--depth=light|standard|deep` — synthesis budget (default `standard`), orthogonal to `--mode`. See the Depth axis section below. Also settable via the `depth:<tier>` toggle in the preview.13- `--mode=small|medium|large` — force a mode, overriding auto-detection.14- `--domain=<slug>` — re-run focused on one domain (large repos): scopes data-model + hotspot specs to that domain's tree, tops up only its docs. Bypasses the "already seeded" early-exit.15- `--refresh` — re-run on an already-seeded repo to add facts that appeared since the first init (a new schema, config, or modules) — and to retrofit host wiring on repos seeded before wiring existed. Bypasses the early-exit; existing docs are skipped, missing ones composed.1617## When to use1819- Empty `.archcore/` — the SessionStart nudge points here.20- First session on a fresh clone / fresh install.21- User says: "initialize archcore", "set up archcore", "seed archcore", "first-time setup", "what should I do first".2223**Not init** (route elsewhere):2425- Recording a specific decision → `/archcore:document`.26- Planning a feature → `/archcore:plan`.27- Documenting one module → `/archcore:document`.28- Codifying a team standard → `/archcore:document` (offers rule + guide continuation).29- Reading applicable context before coding → automatic; the code-alignment hook injects it on file edits.30- Docs health audit → `/archcore:review`.3132## Routing table3334**Mode routing** — Step 0.5 classifier, evaluated top-to-bottom, first match wins. The **empty** route is decided earlier in Step 0(b). Precise conditions in `_shared/grounding/detect-scale.md`.3536| Signal | Route | Seeded (composed when detected) |37|---|---|---|38| No manifest AND no top-level source (Step 0b) | → **empty** | no content docs — host wiring only, behind its own mini-confirm |39| `--mode=X` flag | → forced `X` (detected mode still reported) | per row below |40| `domain_count ≤ 1` AND `module_count ≤ 15` | → **small** | stack rule, run guide, data-model, integrations, config, entry points, public surface, overview + hotspot specs (coverage rate × ranked pool — see Depth axis) |41| `domain_count ≤ 2` AND `module_count ≤ 40` | → **medium** | small set + cross-cutting rules (every depth, every candidate clearing the recurrence threshold) + hotspot specs (coverage rate × ranked pool — see Depth axis) |42| `domain_count ≥ 3` OR `module_count > 40` | → **large** | medium set + top-level map + domain dialog + data-model per schema-bearing domain (all, not only selected) + hotspot specs (coverage rate × ranked pool, plus a floor of ≥ 1 spec per selected domain — see Depth axis) |4344Every non-empty mode also composes the architecture-overview capstone, plans relation wiring, and offers agent-file import — aggregate files (CLAUDE.md / AGENTS.md / .cursorrules) as link stubs and modular rule files (`.cursor/rules/*.mdc`, `.github/instructions/*.md`, `.windsurf/rules/*.md`) as rule docs by default, per `lib/agent-files.md` — inside the preview. Tier-1 facts (data-model, integrations, config, entry points, public surface) are seeded in any mode **when detected** — breadth scales with the repo, presence does not. The public-surface fact is what carries the seed for library / SPA / multi-command-CLI / agent-plugin repos, where there is no server to enumerate as entry points. The empty route exits after Step 0.4546**Follow-up routing** — closing-message hand-offs. Init surfaces these as todos; MUST NOT auto-invoke.4748| User wants to... | → Invoke |49|---|---|50| Capture another module | `/archcore:document <path>` |51| Record a decision | `/archcore:document` |52| Codify a convention as a rule | `/archcore:document` |53| Plan a feature | `/archcore:plan` |54| Drill into another domain (large) | `/archcore:init --domain=<slug>` |55| Add facts that appeared since first init | `/archcore:init --refresh` |56| Scope queries to a domain (large) | `mcp__archcore__search_documents` with the domain tag |57| See what's loaded | `/archcore:review` |5859## Depth axis (`--depth=light|standard|deep`)6061Orthogonal to scale (`--mode`, which measures repo *size*). Depth sets the **synthesis budget**, not the artifact checklist. **Extraction is always on** in every depth — Tier-1 facts, imported authored rules, and the hotspot register are cheap and the highest-value / most-durable layer. Depth scales only the **expensive, staleness-prone synthesis**: spec bodies, cross-cutting rules, and big-file / aggregate extraction. Default: **`standard`** — a good first-day seed, not merely the cheapest one. Init is fully gated (nothing is written before `confirm`, and the preview shows all three depths' costs side by side before the user commits to any of them), so there is no reason to default to the thin tier just to be safe — `light` is the explicit **opt-down** for a cost-conscious user (still never empty — Universality invariant 3); `deep` is the explicit **opt-up** for a max plan.6263| Depth | Hotspot specs (`rate` × pool, `floor`) | Cross-cutting synth (medium/large) | Big authored (>200) & CLAUDE.md/AGENTS.md | Authored decisions → ADR | Relations |64|---|---|---|---|---|---|65| **light** (opt-down) | 10% of the ranked pool, floor 3 | every candidate clearing the recurrence threshold — MAY narrow scan toward guard + shared-indirection primitives for cost, but MUST still surface any high-confidence hit | link | — | basic |66| **standard** (default) | 25% of the ranked pool, floor 4 | every candidate clearing the recurrence threshold | link | — | basic |67| **deep** (opt-up) | 60% of the ranked pool, floor 6 | every candidate clearing the recurrence threshold | **extract + split** | **extract from files** (Route 2), never invent from code | enriched (spec↔rule, spec↔spec) |6869**The spec budget scales with the repo and carries no absolute maximum** — `budget = max(floor(depth), round(rate(depth) × pool_size))`, clipped to `pool_size`, where `pool_size` is the eligible ranked hotspot pool (`detect-hotspots.md` "Spec budget by coverage rate"). A 214-module pool budgets ~54 specs at `standard`; a 12-module pool budgets 4. Large mode adds a floor of ≥ 1 spec per domain selected in Step A.0, filling the rest by repo-wide rank; a later `--domain=<slug>` re-run applies the same formula to that domain's narrowed pool. **Cross-cutting synthesis is on at every depth** — it is the highest value-per-token artifact init seeds; depth changes only its scan cost at `light`, never whether it runs and no longer how many candidates survive. A very large or hot hotspot (`LOC > 3000` OR top-quartile churn) may compose as a **flagship** at any depth, which makes it eligible for decomposition into ≤ 3 sub-specs by separable sub-surface instead of one spec (`detect-hotspots.md` "Flagship specs"). Every spec, flagship or not, is composed under the one ≤ 120-line cap in `_shared/spec-contract.md`.7071Cost scales with depth AND with the repo — the preview shows the computed total per depth, never a constant. On a large repo `deep` can budget hundreds of specs; that is the intended behavior, and the preview's per-depth estimate is where the user sees the price before confirming. Treat any fixed multiplier as illustrative only.7273### Universality invariants — hold in EVERY depth and for ANY codebase74751. **Ceiling, not quota.** A depth raises the budget; it NEVER fabricates to hit a number. If the ranked hotspot pool has 5 modules, `deep` produces at most those 5 specs, never a padded count; on a sparse repo `deep` ≈ `light`. Same for cross-cutting: if only 1 candidate clears the "surface nothing over a false rule" bar, that is the output at any depth. "Prefer omission over a guess" holds in every depth. The budget is a share of real evidence — an empty pool yields an empty seed, whatever the rate.762. **"When detected", never "always."** No depth has a fixed artifact checklist. data-model / integrations / config / entry-points / imports / cross-cutting appear only on positive evidence, identically across depths — a depth is defined by synthesis budget, not by mandatory docs.773. **`light` is never empty.** Its floor rests on the universal spine — stack rule + run guide + public-surface (a library's exports, a CLI's commands, a plugin's skills, a SPA's routes) + register + the budgeted specs via the **test-independent fallback** ranking. At least one fires for any base (library, SPA, ML, CLI, embedded, data/IaC, agent-plugin/markdown, polyglot, monorepo); `light` never degrades to nothing for lack of schema/tests/authored files.784. **Depth lives in Phase B (compose), not detection.** No depth branch adds a stack-specific detection heuristic; detection stays high-level, evidence-first, non-exhaustive (guarded by the detect-catalog universality test).795. **`deep`'s extra budget flows to whatever the repo affords.** No authored files → nothing to extract, so the budget goes to specs/relations that DO have evidence. `deep` is "more of what this repo actually has," not a fixed feature list assuming a stack shape.8081Selection: the `--depth` flag, or the `depth:<tier>` toggle in the preview (Phase C) — the user can flip depth after seeing the plan and its per-depth cost, then still `edit` individual items.8283## Execution8485Content voice: default to architectural prose — decisions, rationale, intent. See `skills/_shared/precision-rules.md` Rule 6. Code blocks only where the document type requires it (`rule`, `guide`, `cpat`, and `spec` examples) or the user asks.8687### Pre-flight: CLI availability check8889Before any init step, verify that the Archcore CLI is available on PATH. The canonical installer is documented at https://docs.archcore.ai/cli/install/ — use it as the single source of truth; do **not** suggest other channels (`brew`, `go install`, etc.) even if the user mentions them.90911. Run: `archcore --version` (via Bash tool)922. If it **succeeds** → check the host-wiring version gate with the deterministic helper (never compare versions yourself — lexical comparison breaks on double-digit fields). Resolve `$d` **in this same Bash call** (each Bash invocation is a fresh shell — nothing persists from a later step), exactly as the Step -1 probe below does: run `d="${CLAUDE_SKILL_DIR:-<absolute dir of this SKILL.md>}"; "$d/../../bin/cli-gte" 0.7.0`. It prints exactly one token:93 - `yes` → proceed immediately to Step -1 (host wiring enabled).94 - `__NO_CLI__` (unexpected here — `--version` just succeeded) → treat as `no`.95 - `no` → the seed still works, but the host-wiring step (see "Host wiring" below) needs a newer CLI. Ask the user once:96 > Archcore CLI `<version>` is older than v0.7.0 — host wiring (project MCP config, SessionStart hook, usage hint) will be skipped. Update now via `archcore update`? (y/N)97 - On `y` → run `archcore update` (Bash), re-run the `cli-gte 0.7.0` check, and proceed to Step -1 (host wiring enabled on `yes`, disabled otherwise).98 - On `N` / silence → proceed to Step -1 with host wiring **disabled**: omit the Host wiring line from the preview, skip Phase E step 0 entirely (the cascade never runs — its manual-fallback leg is NOT a substitute for this note), and in the closing message note: *"Host wiring skipped (CLI < v0.7.0) — update with `archcore update`, then run `archcore init --agent <host> --project "<root>"` in a terminal to make this repo self-contained for CLI-only teammates."* (`<host>`/`<root>` come from the Step -1 probe, which runs regardless of the gate.)993. If it **fails** (command not found):100 - Detect the platform via `uname -s` (Bash). `Darwin`/`Linux` → POSIX path. Anything else (Windows native) → instruct-only path.101 - **POSIX path** — ask the user once:102 > Archcore CLI not found. The official installer runs:103 >104 > ```105 > curl -fsSL https://archcore.ai/install.sh | bash106 > ```107 >108 > Run it now? (y/N)109 - On `y` → execute the command exactly as shown (Bash tool). After it returns, re-run `archcore --version`.110 - Success → print: *"Archcore CLI installed (`<version>`). Proceeding with init."* → apply the same v0.7.0 comparison from item 2 (a fresh install is normally current, so host wiring is enabled) → go to Step -1.111 - Still failing → print the install message below and **stop**.112 - On `N` / silence / **instruct-only path** → print and stop:113 > Archcore CLI required. Install it, then re-run `/archcore:init`:114 >115 > - macOS / Linux / WSL: `curl -fsSL https://archcore.ai/install.sh | bash`116 > - Windows (PowerShell 5.1+): `irm https://archcore.ai/install.ps1 | iex`117 > - Verify: `archcore --version`118 > - Full docs: https://docs.archcore.ai/cli/install/119120Do **not** attempt `brew install`, `go install`, package-manager wrappers, or any other install command — they are not the supported path and will produce a CLI that is not version-compatible with the plugin.121122### Pre-flight: gating and lazy reading123124Two disciplines bind the whole run:125126- **Gating (write boundary).** `init_project()` and the read-only MCP calls (`list_documents`, `get_document`) are infrastructure — they run **before** the preview. The gated operations are `create_document`, `add_relation`, and the **host-wiring writes** (`install_host_config` / `archcore init --agent` — they touch files outside `.archcore/`, like `.mcp.json` and `.claude/settings.json`): none fire before the user types `confirm`. `cancel` therefore leaves `.archcore/` content-empty and the repo's host configs untouched (the directory and `settings.json` may exist from `init_project`, which is harmless and idempotent).127- **Lazy reading (two sub-phases).** The detection/extraction catalogs at `_shared/grounding/*.md` and the composition files at `lib/*.md` are heavy (≥ 1000 lines combined) — read them in two ordered batches, never all at once. The **Detect** sub-phase (Phase A) loads the *detection* catalogs and, for each detector it runs, captures into working memory both the signals AND the small `## Output` create-fields + body template it will reuse later. The **Compose** sub-phase (Phase B) loads the *composition* contracts (`_shared/precision-rules.md`, `_shared/spec-contract.md`, `_shared/rule-contract.md`, `lib/compose-overview.md`, `_shared/grounding/extract-routing.md`) and **reuses the Output fields/templates already captured during Detect** — it does not re-read the bulky detection heuristics. "Release the detection catalogs" at the end of Phase A means dropping their heuristic prose from focus, not the captured Output specs.128129### Step -1: Initialize, detect host, and acknowledge (fast)130131Call `mcp__archcore__init_project()` exactly once (pre-gate infrastructure — idempotent, safe on an already-initialized project). It creates `.archcore/` and `settings.json` if missing.132133Immediately after, give the user a one-line confirmation:134135- Response includes `initialized: true` (created now) — print: *"Archcore initialized at `.archcore/`."*136- `already_initialized: true` — print nothing here; the existing knowledge base speaks for itself in Step 0(a).137138**Host + project root for wiring** — always run this probe, even when host wiring is disabled by the pre-flight version gate (it is one cheap Bash call, and the disabled-path closing message still needs `<host>`/`<root>`). One Bash call:139140```sh141d="${CLAUDE_SKILL_DIR:-<absolute dir of this SKILL.md>}"; host=$("$d/../../bin/detect-host"); root=$(git rev-parse --show-toplevel 2>/dev/null || pwd); printf '%s\n%s\n' "$host" "$root"142```143144`${CLAUDE_SKILL_DIR}` is set by Claude Code only. On other hosts (Cursor, Codex, GitHub Copilot CLI) substitute the absolute directory of this skill file — you know it from having read this file; `bin/detect-host` is two directories up from it (`<plugin-root>/bin/detect-host`).145146`bin/detect-host` resolves the current host from environment only (never cwd or stdin — Cursor guarantees neither) and prints exactly one token: `claude-code` | `cursor` | `codex-cli` | `__UNKNOWN__`. **A GitHub Copilot CLI session always lands on `__UNKNOWN__`** — Copilot sets no environment marker in the shell commands it runs, so it is resolved by the question below rather than by the probe (rationale in `bin/detect-host`). If the probe returns `__UNKNOWN__` **or anything else than the three host tokens** (empty output, a path error — treat all the same), ask one `AskUserQuestion` — "Which AI host is this session running in?" with options Claude Code / Cursor / Codex (CLI or desktop app) / GitHub Copilot CLI — and map the answer to the agent id (`claude-code` / `cursor` / `codex-cli` / `copilot`). The Codex option names both surfaces on purpose: they share one binary, one `~/.codex/config.toml` and one plugin install, so `codex-cli` is the agent id for the desktop app too and there is no `codex-desktop` (`codex-adapter.spec`). Remember `host` and `root` for the Host wiring preview line and Phase E; do not re-run the probe.147148`init_project` initializes only `.archcore/` — host wiring (MCP config, hook, usage hint) is planned in the preview and executed in Phase E, never here. Do not run `archcore init` yourself at this step; the terminal path is the Phase E fallback for the user, not a pre-flight action.149150### Step 0: Check state and source signal151152Two cheap probes, in order. Each can short-circuit the whole skill. Neither reads anything under `_shared/grounding/` or `lib/`.153154#### Step 0(a) — Existing documents155156Call `mcp__archcore__list_documents()` once. **Derive every flag below from local documents only** — skip any result carrying `global: true` / `read_only: true` / `source_kind: "global"`. A mounted global source may already provide a stack rule or an overview, but it must not satisfy the already-seeded early-exit: init seeds THIS repo's documents, and a repo with globals mounted but no local seed is still unseeded. If any global results appear, load `skills/_shared/globals.md`; never modify a global document and never target one with `add_relation`. Derive:157158- `has_stack_rule` — a `rule` whose title contains "stack" in `conventions/`.159- `has_run_guide` — a `guide` whose title contains "run"/"running" in `onboarding/`.160- `has_data_model` — any `doc` tagged `data-model`.161- `has_integrations` — any `doc` tagged `integrations`.162- `has_config` — any `doc` tagged `config`.163- `has_entry_points` — any `doc` tagged `entry-points`.164- `has_surface` — any `doc` tagged `surface`.165- `has_top_level_map` — any `doc` tagged `top-level-map`.166- `has_overview` — any `doc` tagged `architecture-overview`.167- `has_imports` — any document tagged `imported`.168169**Already-seeded early-exit.** If `has_stack_rule` AND `has_run_guide` AND `has_overview` are all true AND **neither `--refresh` nor `--domain` was passed**, reply:170171> Init already seeded this repo. Applicable context auto-injects on file edits via the code-alignment hook; use `/archcore:review` for the dashboard. To add facts that appeared since (a new schema, config, or modules), re-run `/archcore:init --refresh`; to drill into another domain, `/archcore:init --domain=<slug>`. (Seeded before host wiring existed, or missing the host configs? `--refresh` also adds host wiring — MCP config, SessionStart hook, usage hint.)172173Then stop. **With `--refresh` or `--domain`, skip this early-exit and proceed** — every already-present artifact is marked **skip (exists)** in the preview and only missing ones are composed; the Host wiring line appears as usual (its writes are idempotent — already-wired hosts show as skip/converge). (`--domain` additionally scopes the run to one domain; see Step A.0.)174175#### Step 0(b) — Source-signal gate (empty-repo early exit)176177Single filesystem probe — one shell call, no catalog reads. Detect whether the repository has any executable shape yet:178179- **`has_manifest`** — at least one of these exists at the project root (depth ≤ 2 for monorepo workspaces): `package.json`, `pyproject.toml`, `Pipfile`, `requirements.txt`, `Cargo.toml`, `go.mod`, `Gemfile`, `composer.json`, `*.csproj`, `*.fsproj`, `*.vbproj`, `pom.xml`, `build.gradle`, `build.gradle.kts`, `mix.exs`, `Package.swift`. **This list is seed examples, not exhaustive** — also treat ANY project-defining manifest or build file as a manifest (e.g. `CMakeLists.txt`, `Makefile`, `dune-project`/`*.opam`, `deps.edn`/`project.clj`, `pubspec.yaml`, `build.sbt`, `stack.yaml`/`*.cabal`, `*.tf`/`*.tfvars`, `Chart.yaml`, `project.godot`, `*.sln`, `Project.toml`, and agent/LLM-plugin manifests such as `marketplace.json` / `plugin.json` / `.claude-plugin/*`).180- **`has_top_level_source`** — at least one file with a recognizable source extension exists anywhere under the project root, capped at depth 3, excluding `.archcore/`, `.git/`, `node_modules/`, `vendor/`, `dist/`, `build/`, `out/`, `target/`, `coverage/`, `.venv/`, `__pycache__/`, `.next/`, `.turbo/`. Extensions: `.ts`, `.tsx`, `.js`, `.jsx`, `.mjs`, `.cjs`, `.py`, `.rs`, `.go`, `.rb`, `.php`, `.java`, `.kt`, `.kts`, `.swift`, `.cs`, `.fs`, `.ex`, `.exs`, `.scala`, `.clj`, `.cljs`. **The extension list is seed examples, not exhaustive** — also count any file whose contents are plainly source (a shebang, or import/include/package/module/def/func/class/use constructs), and recognize other common code extensions (e.g. `.vue`, `.svelte`, `.astro`, `.dart`, `.c`, `.cc`, `.cpp`, `.h`, `.hpp`, `.m`, `.mm`, `.ipynb`, `.hs`, `.ml`, `.mli`, `.tf`, `.sol`, `.lua`, `.jl`, `.r`, `.zig`, `.nim`, `.gd`).181182If BOTH are false, take the **empty** route. No content seed — but host wiring still applies (an empty repo is exactly where a teammate going CLI-only needs the configs).183184When host wiring is **disabled** by the pre-flight version gate, reply with exactly this and stop (no writes):185186> Archcore is ready at `.archcore/`. No source code detected yet — no content to seed. Host wiring skipped (CLI < v0.7.0) — update with `archcore update`, then re-run `/archcore:init`. The SessionStart empty-state nudge will keep pointing here until then.187188189Otherwise show a mini-preview:190191> Archcore is ready at `.archcore/`. No source code detected yet — no content to seed.192>193> One thing worth doing now — host wiring, same files `archcore init` writes (makes the repo work for teammates using the CLI without this plugin):194>195> ```196> Host wiring (<host>) → <root>197> • <per-host file list — e.g. for claude-code: .mcp.json · .claude/settings.json (SessionStart hook) · CLAUDE.md + AGENTS.md (managed block)>198> ```199>200> `confirm` to write these, `cancel` to leave the repo untouched. Re-run `/archcore:init` after the first manifest or source file lands — the SessionStart empty-state nudge will keep pointing here until then.201202On `confirm` → execute the Host wiring cascade (Phase E step 0) and stop. On `cancel` → stop with no writes. Either way, **do NOT** create placeholder documents — they have no practical value, cost roundtrips and tokens, and suppress the SessionStart empty-state nudge that is the user's breadcrumb back here.203204Otherwise (`has_manifest` OR `has_top_level_source`), proceed to Phase A.205206---207208## Phase A — DETECT (no writes; detection catalogs only)209210Compute everything the seed needs in one detection pass. No documents are created here, and no composition contract is opened. For each detector, capture its signals AND its `## Output` create-fields for reuse in Phase B/E.211212**Detect high-level, for ANY stack.** Each `detect-*` catalog leads with *what* it detects (the concept) and a universal, evidence-first method; its concrete lists of frameworks / ORMs / SDKs / extensions / conventional roots are **non-exhaustive examples**, not a checklist. When a project's language, framework, or layout is unfamiliar or highly specific, reason from first principles per the catalog — the entry file's imports, the dominant file types, the manifest / build system, and what the code actually does — and emit a fact only on **positive evidence** (prefer omission over a guess). Never return empty / `small` / "no entry points" merely because nothing matched a list.213214### Step 0.5: Scale215216Read `_shared/grounding/detect-scale.md`, `_shared/grounding/detect-domains.md`, `_shared/grounding/detect-modules.md`.2172181. **Parse arguments** — `--depth=light|standard|deep` (synthesis budget, default `standard`; see the Depth axis section), `--mode=X` (force the mode), `--domain=<slug>` (force a large-mode single-domain pass; see Step A.0), `--refresh` (already consumed in Step 0a). Depth does NOT affect detection — Phase A ranks hotspots up to the `deep`-depth ceiling (see Step A.3) and detects ALL facts/imports regardless of the active depth; depth only governs how much is synthesized in Phase B.2192. **Compute signals:** `domain_count` (per `detect-domains.md`), `module_count` (source files > 100 LOC, excluding tests/generated), `entry_point_count` (per `detect-entry-points.md`, informational).2203. **Classify** per `detect-scale.md` — apply its evidence-based fallback when the language/layout is unlisted (recompute counts from the dominant code extension and tracked-file breadth; do not default to `small` just because the extension/root lists miss). A forced `--mode` wins but remember the auto-detected one; `--domain` forces large-mode behavior scoped to the named domain.221222### Step A.0: Domain selection (large mode only)223224Skip unless mode is `large`.2252261. **`--domain=<slug>` given** — that domain is the sole selection; skip the dialog. (Tier-1 facts already present are skipped; the run tops up this domain's data-model + hotspot specs by applying the depth's `rate` / `floor` to the pool **narrowed to that domain's tree**, per `detect-hotspots.md`.)2272. **Otherwise** — present the top 5 ranked domains (per `detect-domains.md` ranking) and ask: *"Which domains are you working on now? (pick 1–3 by name or number, or `skip` to defer.)"* Accept a single name, a comma list, or `skip`.2283. **Allocate the hotspot budget.** Hotspots (A.3) are ranked **repo-wide** (candidate selection is never restricted to a domain's tree in the day-one dialog), and the budget itself comes from the repo-wide pool: `max(floor(depth), round(rate(depth) × pool_size))` per `detect-hotspots.md` "Spec budget by coverage rate". The selection changes **allocation, not size**: every selected domain is guaranteed a floor of ≥ 1 spec; remaining slots fill by repo-wide rank across all domains, selected or not. On `skip`, no domain gets a floor and the whole budget fills by repo-wide rank alone.2294. **Data-model breadth is decoupled from the dialog.** Seed a data-model doc for **every domain with a detectable schema** (`detect-data-model.md`, names-only — cheap regardless of repo size), not only the domains selected here. The dialog focuses hotspot-spec priority, not data-model breadth. A domain without a schema still appears as a row in the top-level map (`detect-domains.md`).230231Remember the unselected domains for the closing message.232233### Step A.1: Shape — single manifest batch234235Read `_shared/grounding/detect-stack.md`, `_shared/grounding/detect-data-model.md`, `_shared/grounding/detect-integrations.md`, `_shared/grounding/detect-config.md`. **Read each manifest file once** (`package.json`, `pyproject.toml`, `Cargo.toml`, `go.mod`, `schema.prisma`, `.env.example`, …) and feed all four detectors from that shared parse — never re-read a manifest per detector. Collect:236237- **Stack signals** (≤ 5) — per `detect-stack.md`.238- **Data model** — entities + key relations, NAMES ONLY, per `detect-data-model.md` (large mode: one doc per domain, seeded for EVERY domain with a detectable schema — not scoped to the Step A.0 selection; see Step A.0.4). Skip if no schema anywhere.239- **Integrations** — external services from allowlisted SDK deps, per `detect-integrations.md`. Skip if none.240- **Config surface** — env-var NAMES + purpose, **never values**, per `detect-config.md`. Skip if no env contract.241242### Step A.2: Run commands, entry points & surface243244- **Run commands** — per `_shared/grounding/extract-run-instructions.md` (README section → scripts → ask the user once if neither yields anything).245- **Entry points** — per `_shared/grounding/detect-entry-points.md`, bucketed HTTP / CLI / Worker / Cron / Other. Seed the entry-point `doc` in any mode when ≥ 1 entry point exists; in large mode group by domain.246- **Public surface** — per `_shared/grounding/detect-surface.md`. The role-based outward shape the entry-point inventory does NOT cover: web routes/pages, a library's exported API, a multi-command CLI's command catalog, an agent-plugin's skills/commands, mobile screens. Seed the public-surface `doc` when such a surface exists and is not already fully enumerated as entry points; in large mode group by domain. This is the fact that gives library / SPA / plugin / markdown-tooling repos a substantive seed.247248### Step A.3: Hotspots & cross-cutting (candidates only — NO source reads)249250- **Hotspot candidates** — rank per `_shared/grounding/detect-hotspots.md` and collect signal data (path + LOC + companion-test LOC + suggested type) for the **whole eligible ranked pool**, not a per-depth slice of it. The pool size is itself an input to every depth's budget, and signal collection reads **no source files**, so one Detect pass serves any `--depth`, including a later `depth:` toggle in Phase D, with no re-read. Phase B then keeps the *active depth's* budget as spec stubs — `max(floor(depth), round(rate(depth) × pool_size))`, clipped to the pool (`detect-hotspots.md` "Spec budget by coverage rate") — subject to large mode's per-selected-domain floor of ≥ 1 spec; ranked candidates beyond the budget go to the overview register (`compose-overview.md` Part 3) as `→ /archcore:document` rows. The catalog ranks in two tiers: a tests-aware **primary** tier, and — when it fills fewer slots than the budget — a **test-independent fallback** (fan-in / public surface / size / churn) so repos with no tests (scripts, SPAs, ML, CLIs, agent-plugin/markdown tooling) still surface real specs instead of an empty pool. Mark fallback-tier stubs with their qualifying signal. A candidate clearing the **flagship** gate (`LOC > 3000` OR top-quartile churn) is flagged as such in the stub, for Phase E's one-spec/decomposition choice (`detect-hotspots.md` "Flagship specs"). **Tier-2 artifacts are always composed as `spec`** — use the `adr`/`task-type` hints in `detect-hotspots.md` only to *filter out* ineligible candidates (e.g. a `utils`/`helpers` module, or one failing `spec-contract.md`'s "when NOT to write a spec"), never to switch the document type. **Do not read source files yet** — that read is deferred to Phase E for kept specs only.251- **Cross-cutting candidates** (medium and large, whole-repo, **every depth**) — per `_shared/grounding/detect-cross-cutting.md`, every candidate that clears the recurrence threshold, with no per-depth count cap (Change: the `light` ≤2 / `standard` ≤3 / `deep` ≤4 caps are removed — the conservative "surface nothing over a false rule" bar is the only gate). init uses that catalog for **detection only** and overrides its standalone y/n "Output" flow: each candidate becomes a Tier-2 `rule` stub here and is created in Phase E, not handed to `/archcore:document`.252253### Step A.4: Agent files254255Detect **all** agent-instruction candidates per `lib/agent-files.md` (paths + byte sizes + **class**) — do not stop at CLAUDE.md/AGENTS.md; enumerate the modular directories (`.cursor/rules/*.mdc`, `.github/instructions/*.md`, `.windsurf/rules/*.md`) too. **Sizing exception for CLAUDE.md / AGENTS.md**: compute their size and non-emptiness only **after stripping any archcore managed block** (`<!-- archcore:start -->` … `<!-- archcore:end -->`) — never from a raw file-size probe; a file whose only content is the managed block is not a candidate at all and must not appear in the preview (`lib/agent-files.md` → Probe paths). `lib/agent-files.md` assigns each file a class that sets its default import mode:256257- **`aggregate`** (CLAUDE.md, AGENTS.md, .cursorrules, …) — default **link** (one pointer `doc`). Extract is opt-in.258- **`modular-rule`** (`.cursor/rules/*.mdc` and equivalents) — default **extract**: one `rule`/`doc` per file, classified by content (genuine conventions → `conventions/`), reproduced verbatim. A file > 200 lines degrades to `link`.259260Estimate extract **yield** without loading `extract-routing.md`: aggregate → count H1/H2/H3 headings, capped at 10; modular-rule → 1 per file (they are one rule each). Compute the cost tier **per class**: **aggregate HIGH** if combined aggregate size > 50 KB OR estimated aggregate yield > 8 docs; **modular-rule HIGH** if combined modular-rule size > 50 KB (**file count is NOT a signal** — many small rule files are cheap). HIGH gates only whether extract needs an explicit opt-in.261262### Step A.5: Announce263264Print one detection line, e.g.:265266> Mode: medium (28 modules, 1 domain). Detected: Prisma (6 entities), Stripe + AWS, 12 env vars, 5 entry points, 5 hotspot candidates, 1 cross-cutting pattern, CLAUDE.md (4 KB) + 6 .cursor/rules files (18 KB, modular). Composing the plan…267268In large mode, report the figures for the **selected** domains (selection already happened in Step A.0). Detection done — release the detection catalogs (heuristic prose), keeping the captured Output specs.269270---271272## Phase B — COMPOSE (in memory; composition contracts only)273274Load the composition contracts and compose every planned artifact **without writing**. Honor each catalog's line cap. Mark any artifact whose `has_*` flag is already true as **skip (exists)**. Exception: in large / `--domain` mode the per-domain data-model doc (`<domain-slug>-data-model`) dedupes by its own filename, not the repo-wide `has_data_model` tag — so a newly-selected domain's data-model is still composed when other domains' already exist.275276**Apply the active depth** (`## Depth axis`, default `standard`) to this compose pass — it sets only these levers, and everything else is depth-independent:277- Hotspot spec count = the depth's computed budget (`detect-hotspots.md` "Spec budget by coverage rate"), large mode subject to the per-selected-domain floor; ranked hotspots beyond it go to the register regardless of depth. A flagship candidate (Change: size/churn-gated) composes as one spec or, only with genuine separable sub-contracts, decomposes into ≤ 3 sub-specs — at every depth, not gated by depth.278- Cross-cutting synthesis runs at **every** depth now (medium/large), and every candidate clearing the recurrence threshold becomes a stub — depth no longer trims the count. `light` MAY narrow the scan toward the guard + shared-indirection primitives for cost control but MUST still surface any high-confidence candidate it finds.279- Import mode: `light`/`standard` → aggregate link, big (>200) modular link; `deep` → aggregate extract, big modular extract + split.280- ADR-from-authored-decisions and enriched relations: `deep` only.281Depth is a budget **ceiling, never a quota** — compose only what the repo affords on positive evidence (Universality invariant 1); a sparse repo at `deep` yields the same as `light`.282283- **Tier-1 facts (full bodies, cheap/extractive):**284 - stack rule — `detect-stack.md` template (≤ 6 lines).285 - run guide — `extract-run-instructions.md` (single-app ≤ 15 lines; monorepo per-app ≤ 6).286 - data-model doc — `detect-data-model.md` Output (≤ 40 lines), when detected; large mode: one per domain, for every schema-bearing domain.287 - integrations doc — `detect-integrations.md` Output (≤ 15 lines), when detected.288 - config doc — `detect-config.md` Output (≤ 20 lines, **NAMES ONLY**), when detected.289 - entry-point inventory — `detect-entry-points.md` Output, when ≥ 1 entry point.290 - public-surface doc — `detect-surface.md` Output (≤ 25 lines, **NAMES + purpose only**), when a surface exists that entry points don't already cover.291 - top-level map — `detect-domains.md` Output (large mode).292- **Tier-2 stubs (NO source reads):**293 - hotspot specs — one stub each for the **active depth's budget** (`detect-hotspots.md`), large mode subject to the per-selected-domain floor: suggested spec title, the qualifying `LOC / test-ratio`, target filename + directory, a `flagship` marker when the size/churn gate is cleared, and an estimated synthesis cost ≈ `(source_LOC + test_LOC) × 6` tokens. The full body is composed only after confirm. Ranked hotspots **beyond** the budget are not stubbed — they go to the overview register (`compose-overview.md` Part 3) at ~0 cost.294 - cross-cutting rules (medium/large, **every depth**, no count cap) — one stub each: the pattern + the paths it would govern. Full body composed after confirm under `rule-contract.md`. **Drop a stub whose pattern is already covered by an imported authored rule** (dedup per `detect-cross-cutting.md`) and note the skip under that import.295- **Capstone:** plan the architecture-overview per `lib/compose-overview.md`. Its body indexes the *confirmed* seed, so it is composed in Phase E once the set is final. List it in the preview as "Architecture overview — index of the above".296- **Agent-file import:** behavior is set by file class from `lib/agent-files.md` (captured in Detect):297 - **Aggregate** (CLAUDE.md, AGENTS.md, .cursorrules, …): default **link** (one pointer `doc`, ~0 cost). Extract is opt-in via `edit`; the aggregate-HIGH flag gates that opt-in.298 - **Modular-rule** (`.cursor/rules/*.mdc` and equivalents): default **extract** per `_shared/grounding/extract-routing.md` — one document per file (they are one rule each), classified by content: a genuine convention → `rule` in `conventions/`; a reference/role/meta file → `doc` in `imported/`. Title from the frontmatter `description:`, body verbatim, `status: draft`. A file > 200 lines degrades to **link** (opt-in extract to split). No synthesis.299 - **Dedup:** after both sets are assembled, drop any cross-cutting stub whose constraint is already covered by a modular-rule file imported as a `rule` (same symbol/module) — prefer the authored rule; never create both.300 Reuse the `agent-files.md` encoding (`imported` + `source:<slug>` tags, pointer first line) for all modes.301- **Planned relations:** per the `compose-overview.md` "Relation wiring" table.302303No `create_document` / `add_relation` has run yet.304305---306307## Phase C — PREVIEW (one manifest)308309Present the entire plan as a single grouped manifest, then wait. Example:310311```312Init plan — scale: medium · depth: standard (default). confirm / edit / depth:light / depth:deep / cancel313Coverage: 4 specs / 11 load-bearing modules (36%) · 1 cross-cutting rule314315Facts (created in full):316 • Project stack — rule [new]317 • Running the project — guide [new]318 • Data model — doc (6 entities) [new]319 • External integrations — doc (Stripe, AWS) [new]320 • Configuration — doc (12 vars) [new]321 • Entry points — doc (5) [new]322 • Public surface — doc (8 routes) [new]323Synthesis (bodies composed only if kept):324 • spec: token-rotation — 235 LOC src / 968 LOC tests ~7k [new]325 • spec: auth-client — 52 LOC src / 0 tests ~1k [new]326 • rule: request-context — cross-cutting327328…(truncated)