"""``mgf-fed init-project`` — scaffold a new mgf-common-aligned project.

Closes the v0.9.0 deliverable: starting a new project should give
Claude (and humans) the engineering standards as context
automatically, without any manual copy-paste-and-pray. This
command generates the minimal project skeleton:

  - ``docs/claude/CLAUDE.md`` — auto-loaded by Claude every session,
    inlines the most-violated rules, points at
    ``mgf-fed standards <topic>`` for full text. (PAPER-74 /
    PAPER-54: this is the DOC-07 canonical path; legacy
    ``docs/CLAUDE.md`` is still accepted by ``catch-up``.)
  - ``pyproject.toml`` snippet — printed to stdout for the user
    to merge (we do NOT modify an existing pyproject; that's
    riskier than printing what to add).
  - ``.claude/settings.json`` — sensible permissions for the kind
    of project being built.
  - ``AGENTS.md`` — the cross-tool agent-discovery pointer to the
    canonical Magogi briefing (identical in every federation
    project).

The standards themselves are NOT copied — they live in
``mgf-common`` and are accessible at runtime via
``mgf-fed standards <topic>``. Single source of truth; new
projects always see fresh standards by bumping their mgf-common
pin.

Usage:

.. code-block:: bash

    cd /path/to/new/project/root
    mgf-fed init-project --name myapp --kind cli
    # …creates docs/claude/CLAUDE.md + .claude/settings.json …
    # …prints the pyproject.toml additions to stdout …
"""

from __future__ import annotations

import json
import sys
from pathlib import Path
from typing import TYPE_CHECKING, Literal

if TYPE_CHECKING:
    import argparse


_KINDS = ("cli", "fastapi", "django", "gui", "library", "service")
_VALID_KIND = Literal["cli", "fastapi", "django", "gui", "library", "service"]


# CLAUDE.md delimiter markers. Wrap the auto-managed portion so
# `mgf-fed catch-up --apply` can refresh just that block without
# touching project-specific content elsewhere in the file.
CLAUDE_MD_BEGIN_MARKER = "<!-- BEGIN mgf-common auto-generated — managed by `mgf-fed catch-up`."
CLAUDE_MD_END_MARKER = "<!-- END mgf-common auto-generated."


# AGENTS.md — the cross-tool agent-discovery pointer. Project-
# agnostic + fully managed: identical in every federation project
# (mgf-common, every sibling, every consumer). `init-project`
# writes it; `mgf-fed catch-up` creates / restores it. It is a
# thin pointer — it holds NO copy of the briefing, so it cannot
# drift; the canonical briefing is printed by `mgf-fed agents`.
AGENTS_MD_CONTENT = """\
# AGENTS.md — AI agent instructions

This file follows the cross-tool `AGENTS.md` convention. It is a
**thin pointer** managed by `mgf-fed catch-up` — do not edit it;
project-specific agent notes belong in this project's `CLAUDE.md`.

**AI agents working in this project — get the canonical briefing:**

> Run **`mgf-fed agents`** and do exactly what it prints.

`mgf-common` is a dependency of this project, so that command is
always installed — it prints the single, federation-wide agent
briefing with no setup and no assumptions. It covers the federation
shape, using the library + its siblings instead of reinventing
them, the standards + the docs layout, keeping this project in sync
with `mgf-fed catch-up`, and filing feedback in the relevant
library's `FEEDBACK.md` when you hit friction so the library itself
can improve.

Then read this project's own `CLAUDE.md` (`docs/claude/CLAUDE.md`,
or `docs/CLAUDE.md` on older layouts) — its auto-managed block
carries the project-specific lookup.

**Fallback if `mgf-fed agents` fails to run** (PAPER-78 — usually
a stale editable install pointing at a renamed source tree): read
the same text from `mgf-common`'s `STARTHERE.md`, section *"For the
AI agent in any Magogi project"*. The CLI's output and that
section are kept identical by `tests/unit/cli/test_agents.py`. Open
either:

- The published source on Codeberg:
  [`mgf-common/STARTHERE.md`](https://codeberg.org/magogi-admin/mgf-common/src/branch/main/STARTHERE.md)
- The same file in your local mgf-common checkout if you have one
  (typically `~/PycharmProjects/mgf-common/STARTHERE.md`).

One message for the whole federation; no per-project variant.
"""


def add_init_project_args(parser: argparse.ArgumentParser) -> None:
    """Register the ``init-project`` subcommand's arguments."""
    parser.add_argument(
        "--name",
        required=True,
        help="Project name (used in CLAUDE.md + pyproject snippet).",
    )
    parser.add_argument(
        "--kind",
        choices=_KINDS,
        required=True,
        help=(
            "Project kind: cli (Click/argparse tool), fastapi "
            "(async HTTP service), django (full Django app), "
            "gui (desktop GUI app — Qt/PySide, Textual, pywebview, "
            "etc.), library (Python package for other apps), or "
            "service (plain async service / worker, not HTTP-shaped)."
        ),
    )
    parser.add_argument(
        "--path",
        type=Path,
        default=Path.cwd(),
        help=(
            "Target directory (default: current working directory). "
            "Refuses to overwrite existing files unless --force is set."
        ),
    )
    parser.add_argument(
        "--force",
        action="store_true",
        help="Overwrite existing CLAUDE.md / .claude/settings.json.",
    )
    parser.add_argument(
        "--dry-run",
        action="store_true",
        help="Print the files that would be created; don't write anything.",
    )


def run_init_project(args: argparse.Namespace) -> int:
    """Generate the project skeleton."""
    target_dir = args.path.resolve()
    if not target_dir.is_dir() and not args.dry_run:
        # Real run requires the target dir to exist (we won't `mkdir`
        # an arbitrary path silently). Dry-run is a preview, no
        # writes happen — we let it work against a non-existent dir
        # so users can preview before `mkdir foo && cd foo`.
        print(
            f"error: target path {target_dir} is not a directory. "
            f"(--dry-run works against non-existent paths if you "
            f"want a preview before creating the directory.)",
            file=sys.stderr,
        )
        return 1

    # PAPER-74 (2026-05-26): scaffold at the DOC-07 canonical path
    # `docs/claude/CLAUDE.md`. PAPER-54 (shipped) established that
    # `docs/claude/CLAUDE.md` is canonical and `docs/CLAUDE.md` is
    # the legacy fallback; new scaffolds should align with the
    # canonical. catch-up still accepts both; existing consumers on
    # the legacy path stay valid until they migrate manually.
    claude_md_path = target_dir / "docs" / "claude" / "CLAUDE.md"
    settings_path = target_dir / ".claude" / "settings.json"
    agents_md_path = target_dir / "AGENTS.md"

    if not args.force and not args.dry_run:
        existing = [p for p in (claude_md_path, settings_path, agents_md_path) if p.exists()]
        if existing:
            print(
                f"error: refusing to overwrite existing files: "
                f"{', '.join(str(p) for p in existing)}. "
                f"Use --force to overwrite, or --dry-run to preview.",
                file=sys.stderr,
            )
            return 1

    claude_md_content = _render_claude_md(args.name, args.kind)
    settings_content = _render_claude_settings(args.kind)
    pyproject_snippet = _render_pyproject_snippet(args.name, args.kind)

    if args.dry_run:
        print(f"Would create: {claude_md_path}")
        print(f"Would create: {settings_path}")
        print(f"Would create: {agents_md_path}")
        print()
        print("--- pyproject.toml additions (paste into your existing file) ---")
        print(pyproject_snippet)
        return 0

    claude_md_path.parent.mkdir(parents=True, exist_ok=True)
    claude_md_path.write_text(claude_md_content, encoding="utf-8")

    settings_path.parent.mkdir(parents=True, exist_ok=True)
    settings_path.write_text(settings_content, encoding="utf-8")

    agents_md_path.write_text(AGENTS_MD_CONTENT, encoding="utf-8")

    print(f"created: {claude_md_path}")
    print(f"created: {settings_path}")
    print(f"created: {agents_md_path}")
    print()
    print("--- pyproject.toml additions ---")
    print("Paste the following into your project's pyproject.toml:")
    print()
    print(pyproject_snippet)
    print()
    print(
        "Next: review docs/claude/CLAUDE.md and customize the "
        "project-specific sections. Run `mgf-fed standards` to "
        "list the full standards corpus.",
    )

    return 0


# ---------------------------------------------------------------------------
# CLAUDE.md template — inlines the top rules + points at the rest.
# ---------------------------------------------------------------------------


_CLAUDE_MD_TEMPLATE = """# {name} — Claude Reference

<!-- BEGIN mgf-common auto-generated — managed by `mgf-fed catch-up`.
Edit only between this block's BEGIN/END markers if you want catch-up to
refresh your changes; project-specific content goes BELOW the END marker
in the "Project-specific lookup" section. -->

Project-specific lookup for Claude sessions. Cross-project
engineering standards live in `mgf-common`; run
`mgf-fed standards <topic>` to read any one of them.

**▶ First, run `mgf-fed agents`** — it prints the canonical
federation-wide agent briefing (always installed; `mgf-common` is a
dependency of this project). The briefing covers the federation
shape, using the library + its siblings instead of reinventing
them, the standards + docs layout, keeping this project in sync with
`mgf-fed catch-up`, and filing feedback in the relevant library's
`FEEDBACK.md` when you hit friction so the library itself can improve.

> **Project kind:** {kind}
> **Cornerstone library:** `mgf-common` (this project depends on it).
> **Conformance level:** declare yours in
> `docs/inprogress/MGF_STANDARDS_CONFORMANCE.md` (L1 / L2 / L3) — see
> `mgf-fed standards README` §"Conformance levels for projects".

---

## Standing instructions — every session

Claude reads this file on every session start. The rules below are
canonical; the full text lives in `mgf-common` standards docs and
is accessible via:

```bash
mgf-fed standards            # list all topics
mgf-fed standards LOGGING    # print the full doc
mgf-fed standards security   # alias also works
```

**Before writing or editing code in any of these areas, read the
corresponding topic doc:**

| Area | Topic | Run |
|---|---|---|
| API surface (public functions, modules) | API_DESIGN | `mgf-fed standards api` |
| Error handling, exceptions, retries | ERROR_HANDLING | `mgf-fed standards errors` |
| Logging, redaction, OTel | LOGGING | `mgf-fed standards logging` |
| Configuration, settings, env vars | CONFIGURATION | `mgf-fed standards config` |
| Secrets, TLS, subprocess, crypto | SECURITY | `mgf-fed standards security` |
| Tests, fixtures, isolation | TESTING | `mgf-fed standards testing` |
| Architecture, layering, replaceability | DESIGN_PRINCIPLES | `mgf-fed standards design` |
| Scope (what belongs in this project) | SCOPE | `mgf-fed standards scope` |

---

## The top-30 most-violated rules (inline so they're always in context)

These are the rules that come up most often in code review. Treat
every MUST as non-negotiable. The ID prefix tells you which doc
the full rationale lives in (DP- = DESIGN_PRINCIPLES, EH- =
ERROR_HANDLING, etc.).

### Architecture (DP-)

- **DP-01** Layering MUST be enforced: UI MUST NOT be imported by
  core; core MUST NOT import third-party SDKs that have a provider
  in front of them.
- **DP-02** Every third-party I/O dep MUST sit behind an ABC with
  a production AND a mock implementation.
- **DP-03** Domain types passed between modules MUST be frozen
  dataclasses (or pydantic `frozen=True`); never bare strings/dicts.
- **DP-04** Every public function MUST have a typed signature;
  `mypy --strict` MUST pass on `src/`.
- **DP-09** Cancellation MUST be explicit. Long-running ops take
  an explicit `CancellationToken`-like parameter; never thread-global.
- **DP-11** Async tasks MUST have an owner. Bare
  `asyncio.create_task(...)` without retention is a defect.
- **DP-12** Time MUST be UTC at every boundary. `datetime.now()`
  is forbidden; use `datetime.now(UTC)`. Naive datetimes never
  cross module boundaries.
- **DP-13** Every owned external resource MUST be released
  deterministically via context manager or `try / finally`.
  GC-based cleanup is a defect.

### Error handling (EH-)

- **EH-01** Every exception raised by app code MUST be a subclass
  of the application's base error class (typically
  `mgf.common.exceptions.AppError`).
- **EH-02** Third-party exceptions MUST be translated to typed
  application exceptions at the boundary. Use
  `mgf.common.providers.ProviderABC` + `@translate_at_seam` for
  this.
- **EH-03** When re-raising a translated exception, the original
  cause MUST be chained via `raise NewError(...) from original`.
- **EH-04** Every entry point MUST install a top-level handler
  (folded into `mgf.common.bootstrap()` automatically).
- **EH-07** Bare `except Exception:` (without re-raise or typed
  translation) MUST NOT appear except in narrow cleanup paths.
- **EH-11** Retries MUST use exponential backoff with full jitter
  and a finite cap. Use `mgf.http.RetryPolicy` (sibling `mgf-http`).

### Logging (LG-)

- **LG-01** Every module MUST acquire its logger via
  `logging.getLogger(__name__)`. No bare `logging.info(...)`.
- **LG-04** Sensitive fields MUST be redacted by the formatter
  before any sink sees them. The default
  `mgf.common.observability.Redactor` covers the canonical set.
- **LG-05** Error-level logs MUST include the exception via
  `LOG.exception(...)` or `exc_info=True`.
- **LG-08** Log messages MUST use lazy `%`-style formatting
  (`LOG.info("processing %s", x)`), NOT f-strings.
- **LG-09** Structured fields MUST be passed via `extra={{...}}`.
- **LG-12** Log emission MUST be cheap (≤100 µs per record).
  Heavy work belongs in the handler, not the call site. Use
  `mgf.common.observability.AsyncJsonHandler` for non-blocking I/O.

### Configuration (CF-)

- **CF-01** Application config MUST live in a single typed object
  (`mgf.common.config.BaseAppSettings`-derived). No module reads
  config files / env vars directly.
- **CF-02** Precedence: CLI flags → env vars → `.env` → on-disk
  config → typed defaults.
- **CF-05** Secrets MUST NOT live in the config file; they live in
  a vault. Use `mgf.common.vault.VaultProtocol`.
- **CF-08** `Settings` is loaded once at the entry point and
  passed down by constructor injection. No module-global singletons.
- **CF-10** Tests MUST construct `Settings` with explicit values
  (no env-var leakage).

### Security (SC-)

- **SC-01** Secrets MUST live in a Vault, never in config / env /
  source / logs / crash reports.
- **SC-02** Subprocess invocation MUST use `argv` lists with
  `shell=False`.
- **SC-06** TLS verification MUST be enabled on every outbound
  connection.
- **SC-07** Every external request MUST carry a finite timeout.
- **SC-15** Filesystem operations MUST validate against directory
  traversal via `Path.resolve()` + `is_relative_to()`.

### Testing (TS-)

- **TS-01** `tests/` MUST mirror `src/` 1:1.
- **TS-02** The unit suite MUST run with no production
  credentials, no network, no real third-party daemon.
- **TS-04** `filterwarnings = ["error"]` MUST be set in
  `pyproject.toml`.
- **TS-06** Coverage gate ≥80% project-wide; ≥90% on changed
  lines.

### API design (AP-)

- **AP-01** Public API surface MUST be enumerated explicitly via
  `__all__` at every package and module boundary.
- **AP-04** Deprecation cycle MUST be: announce → still works
  (≥1 MINOR) → removed; deprecated names emit
  `DeprecationWarning`.
- **AP-08** Public modules MUST use
  `from __future__ import annotations`; runtime-unused imports go
  in `if TYPE_CHECKING:`.
- **AP-14** Internal modules MUST NOT be imported by consumers.
  Single-underscore module names are private.

---

## Quick start — bootstrapping this project

This project depends on `mgf-common`. The canonical entry point
is `mgf.common.bootstrap()`:

{quick_start_block}

For test fixtures, use `mgf.common.bootstrap_for_test()`:

```python
import pytest
from mgf.common import bootstrap_for_test, AppContext

@pytest.fixture
def app_ctx() -> AppContext:
    with bootstrap_for_test() as ctx:
        yield ctx
```

For the full integration shape (settings, exceptions, observability,
recipes), see `mgf-common`'s
`docs/recipes/{kind}.md` (run `mgf-fed standards
components` to find it).

---

## Four green gates — every PR

Before any commit, all four MUST pass:

```bash
ruff check src tests
mypy --strict src/
pytest --cov --cov-fail-under=80
lint-imports
```

The pyproject.toml snippet generated alongside this file pre-tunes
ruff + mypy + pytest. Add `import-linter` rules for layering as
the project grows.

---

## Federation-discipline reminders (v2.8 DOC-13 / DOC-14 / DOC-15)

Three living disciplines apply to every consumer of mgf-common.
Each has a one-paragraph reminder below; the full rules live in
[`mgf-common/docs/standards/DOCUMENTATION.md`](https://codeberg.org/magogi-admin/mgf-common/src/branch/main/docs/standards/DOCUMENTATION.md).

<!-- BEGIN mgf-common: feedback-discipline -->

### Feedback discipline (DOC-13 + FB-01..FB-04)

Hit friction in mgf-common OR any federation sibling? **File a
`PAPER-NN` entry in the target repo's `FEEDBACK.md`** — not
ad-hoc emails, not lost one-off issues.

- Touches one library's surface (e.g., mgf-http retries,
  mgf-fastapi middleware) → file in that library's `FEEDBACK.md`
  (sibling-scoped per DOC-13).
- Touches federation-wide concerns (cross-sibling patterns,
  standards proposals, cornerstone APIs) → file in
  [mgf-common/FEEDBACK.md](https://codeberg.org/magogi-admin/mgf-common/src/branch/main/FEEDBACK.md).

```bash
mgf-fed feedback-howto              # how to submit + severity guide
mgf-fed feedback-howto --template   # entry template, ready to paste
mgf-fed feedback-howto --url        # canonical FEEDBACK.md URL
```

Severity guide:
- 🔴 **Blocker** — getting it wrong requires a breaking change later.
- 🟡 **High** — real quality-of-life pain, additive to fix.
- 🟢 **Nice-to-have** — real value, not urgent.
- 💭 **Aspirational** — long-term direction input.

Don't wait for permission. The maintainer triages.

<!-- END mgf-common: feedback-discipline -->

<!-- BEGIN mgf-common: adoption-discipline -->

### Adoption-tracker discipline (DOC-14 + ADP-01..ADP-04)

This project's adoption of mgf-common (which versions, which
features, which phase) lives in
`docs/inprogress/MGF_ADOPTION.md` (or `docs/MGF_ADOPTION.md`).
**Update it on every pin bump AND every closed FEEDBACK paper**
that touches this project's adoption surface (per ADP-03).

Template:
[mgf-standard/docs/recipes/mgf-adoption-md-template.md](https://codeberg.org/magogi-admin/mgf-standard/src/branch/main/docs/recipes/mgf-adoption-md-template.md).

Federation siblings: the ADP-04 carve-out exempts you — your
adoption story is the federation_roadmap + your own CHANGELOG.

<!-- END mgf-common: adoption-discipline -->

<!-- BEGIN mgf-common: conformance-discipline -->

### Conformance-tracker discipline (DOC-15 + CFM-01..CFM-04)

This project's conformance to mgf-common's standards (per-rule
✅ / 🚫 / ⏳ / N/A status) lives in
`docs/inprogress/MGF_STANDARDS_CONFORMANCE.md`. **Update it on
every standards bump in mgf-common AND on every PR that changes
code touching a conformance rule** (per CFM-03).

Declared conformance level (L1 / L2 / L3 per CFM-02): see this
project's README. Decline-with-rationale (🚫) MUST carry a
paragraph per
[principles/non-adoption-is-valid.md](https://codeberg.org/magogi-admin/mgf-standard/src/branch/main/docs/standards/principles/non-adoption-is-valid.md).

Template:
[mgf-standard/docs/recipes/mgf-standards-conformance-md-template.md](https://codeberg.org/magogi-admin/mgf-standard/src/branch/main/docs/recipes/mgf-standards-conformance-md-template.md).

<!-- END mgf-common: conformance-discipline -->

<!-- END mgf-common auto-generated. Project-specific content below
this marker is preserved across `mgf-fed catch-up --apply` runs. -->

---

## Project-specific lookup

*(Add project-specific facts here as the project grows: gotchas,
non-obvious design decisions, fixed bugs, performance budgets,
etc. Keep this section the canonical "what will burn you"
reference. Do NOT add ephemeral state — current branch, in-progress
tasks — those belong in the task tracker.)*

---

*Generated by `mgf-fed init-project`. Re-run with `--force` to
regenerate (your project-specific sections will be overwritten —
back them up first).*
"""


_QUICK_START_BLOCKS: dict[str, str] = {
    "cli": """```python
# src/{name}/__main__.py
import mgf.common
from mgf.common.cli import run_and_translate, build_cli, dispatch

def main(argv=None) -> int:
    parser = build_cli(prog="{name}")
    args = parser.parse_args(argv)
    with mgf.common.bootstrap(app_name="{name}", app_version="0.1.0"):
        return dispatch(args, parser=parser)

if __name__ == "__main__":
    raise SystemExit(run_and_translate(main))
```""",
    "fastapi": """```python
# src/{name}/app.py
from contextlib import asynccontextmanager
from fastapi import FastAPI
from mgf.common import bootstrap
from mgf.fastapi import (
    ExceptionTranslationMiddleware,
    RequestIdMiddleware,
)

@asynccontextmanager
async def lifespan(app: FastAPI):
    with bootstrap(app_name="{name}", app_version="0.1.0"):
        yield

app = FastAPI(lifespan=lifespan)
app.add_middleware(ExceptionTranslationMiddleware)
app.add_middleware(RequestIdMiddleware)
```""",
    "django": """```python
# {name}/settings.py
INSTALLED_APPS = [
    # … your apps …
    "mgf.django",
]

MIDDLEWARE = [
    "mgf.django.MgfRequestIdMiddleware",
    "mgf.django.MgfContextMiddleware",
    # … your other middleware …
]

LOGGING = {{
    "version": 1,
    "formatters": {{"json": {{"()": "mgf.django.JsonFormatter"}}}},
    "handlers": {{
        "console": {{"class": "logging.StreamHandler", "formatter": "json"}},
    }},
    "root": {{"handlers": ["console"], "level": "INFO"}},
}}

MGF_COMMON_APP_NAME = "{name}"
MGF_COMMON_APP_VERSION = "0.1.0"
```""",
    "library": """```python
# src/{name}/__init__.py
\"\"\"{name} — library shape; do NOT call bootstrap() here.\"\"\"
from mgf.common.observability import get_logger, operation_span

LOG = get_logger(__name__)

# Library code uses get_logger + operation_span (no-op-safe when no
# host has bootstrapped). The host app calls mgf.common.bootstrap().
```""",
    "service": """```python
# src/{name}/__main__.py
import asyncio
import mgf.common

async def serve() -> None:
    # … your service loop …
    pass

def main() -> int:
    with mgf.common.bootstrap(app_name="{name}", app_version="0.1.0"):
        asyncio.run(serve())
    return 0

if __name__ == "__main__":
    raise SystemExit(main())
```""",
    # PAPER-59 — desktop GUI shape. Framework-agnostic: the entry-
    # point owns the event loop (Qt/PySide, Textual, pywebview, etc.)
    # and `mgf.common.bootstrap()` wraps it as a sync context manager
    # for logging + crash reporting + identity. Presentation layer
    # MUST NOT be imported by core (DP-01); see docs/recipes/gui.md
    # for the canonical layering pattern.
    "gui": """```python
# src/{name}/__main__.py
import mgf.common
# Replace with your framework — PySide6 / PyQt6 / textual / pywebview / …
from {name}.app import build_app

def main() -> int:
    with mgf.common.bootstrap(app_name="{name}", app_version="0.1.0"):
        app = build_app()   # framework-specific GUI bootstrap
        return app.run()    # event loop owns the main thread

if __name__ == "__main__":
    raise SystemExit(main())
```""",
}


def _render_claude_md(name: str, kind: str) -> str:
    """Fill the CLAUDE.md template with project-specific blanks.

    PAPER-97: a ``docs`` (non-Python) repo gets the thin-pointer
    template — orientation to the federation only, no Python-standards
    recipes — so ``catch-up --apply`` never materialises the
    Python-library template into a repo with no code.
    """
    if kind == "docs":
        return _CLAUDE_MD_DOCS_TEMPLATE.format(name=name)
    quick_start = _QUICK_START_BLOCKS.get(kind, _QUICK_START_BLOCKS["cli"])
    quick_start = quick_start.format(name=name)
    return _CLAUDE_MD_TEMPLATE.format(
        name=name,
        kind=kind,
        quick_start_block=quick_start,
    )


# Thin-pointer CLAUDE.md for a non-Python (`docs`) repository (PAPER-97).
# Same BEGIN/END auto-managed markers as the main template (so catch-up's
# block refresh works), but the body orients agents to the federation and
# states the Python-standards corpus is N/A — no four-gates / bootstrap /
# DP-EH-LG recipes that don't apply to a markdown/asset repo.
_CLAUDE_MD_DOCS_TEMPLATE = """# {name} — Claude Reference

<!-- BEGIN mgf-common auto-generated — managed by `mgf-fed catch-up`.
Edit only between this block's BEGIN/END markers if you want catch-up to
refresh your changes; project-specific content goes BELOW the END marker
in the "Project-specific lookup" section. -->

Project-specific lookup for Claude sessions.

**▶ First, run `mgf-fed agents`** — it prints the canonical
federation-wide agent briefing (always available; `mgf-common` is the
federation cornerstone). It covers the federation shape, the standards +
docs layout, keeping this project in sync with `mgf-fed catch-up`, and
filing feedback in the relevant library's `FEEDBACK.md`.

> **Project kind:** docs (non-Python repository)
> **What this means:** this repo has no Python — no `pyproject.toml`, no
> `src/`, no `*.py`. The `mgf-common` *engineering-standards corpus* (typed
> errors, structured logging, layered config, the four CI gates, the
> federation test-supervisor, `PUBLIC_API`/import-linter, and the DOC-17
> library doc-contract) is **N/A here**; `mgf-fed catch-up` reports those
> rows as N/A, not as findings. What still applies: this agent-orientation
> pointer and the repo's own `LICENSE`.
> **Real guidance** lives in this project's own docs — start at the root
> `CLAUDE.md` / `README.md`, not in this auto-block.

<!-- END mgf-common auto-generated. Project-specific content below
this marker is preserved across `mgf-fed catch-up --apply` runs. -->

---

## Project-specific lookup

*(For a docs/brand/content repo the root `CLAUDE.md` is usually the
canonical guidance; keep this section a pointer to it rather than a
duplicate. Add repo-specific conventions — source-of-truth rules,
gotchas — only if they don't already live in the root file.)*

---

*Generated by `mgf-common` for a non-Python (`docs`) repository.*
"""


# ---------------------------------------------------------------------------
# .claude/settings.json template.
# ---------------------------------------------------------------------------


def _render_claude_settings(kind: str) -> str:
    """Generate a .claude/settings.json with sensible permissions."""
    allow: list[str] = [
        "Bash(ruff *)",
        "Bash(mypy *)",
        "Bash(pytest *)",
        "Bash(lint-imports *)",
        "Bash(uv *)",
        "Bash(git status*)",
        "Bash(git diff*)",
        "Bash(git log*)",
        "Bash(git add *)",
        "Bash(git commit *)",
        "Bash(mgf-common *)",
        "Read",
        "Edit",
        "Write",
    ]
    deny: list[str] = [
        "Bash(rm -rf *)",
        "Bash(git push --force *)",
        "Bash(git reset --hard *)",
    ]

    if kind in ("fastapi", "service", "django"):
        allow.extend(["Bash(uvicorn *)", "Bash(alembic *)"])
    if kind == "django":
        allow.append("Bash(python manage.py *)")

    payload = {
        "permissions": {
            "allow": allow,
            "deny": deny,
            "defaultMode": "default",
        },
    }
    return json.dumps(payload, indent=2) + "\n"


# ---------------------------------------------------------------------------
# pyproject.toml snippet — the four-gate config blocks.
# ---------------------------------------------------------------------------


def _render_pyproject_snippet(name: str, kind: str) -> str:
    """Generate a pyproject.toml snippet for the four gates + mgf-common dep.

    Federation siblings (extracted from mgf-common across v0.28 - v0.31)
    are listed alongside ``mgf-common`` for kinds that need them; the
    pin is kept tight per AP-03's 0.x window discipline.

    The ``mgf-common`` pin window is computed from
    ``mgf.common.__version__`` at call time so the snippet always
    matches the installed release (PAPER-52). Sibling pins are still
    literal — they're tracked under the v1.x backlog because each
    sibling has its own independent semver and there is no
    cross-package version registry in mgf-common today.
    """
    from mgf.fed.cli._version_pin import current_pin_window

    # The current mgf-common pin window — tracks __version__ live.
    mgf_pin = f'"mgf-common{current_pin_window()}"'
    # Per-kind dependency list. mgf-common is the federation leaf;
    # framework-shaped adapters ship as siblings (mgf-fastapi v0.28+,
    # mgf-http v0.29+, mgf-sqlalchemy + mgf-alembic v0.30+, mgf-django
    # v0.31+). Post-v0.31 the mgf-common federation leaf ships
    # pure-Python infrastructure only; framework-coupled adapters all
    # live in their respective siblings. Bare mgf-common is enough for
    # cli / library kinds; service kinds add the http sibling; fastapi
    # kinds add fastapi + http + sqlalchemy on top; django kinds add
    # the mgf-django sibling.
    deps_for_kind: dict[str, list[str]] = {
        "cli": [mgf_pin],
        "fastapi": [
            mgf_pin,
            '"mgf-fastapi[sqlalchemy]>=0.2,<0.3"',
            '"mgf-sqlalchemy>=0.1,<0.2"',
            '"mgf-alembic>=0.1,<0.2"',
            '"mgf-http>=0.1,<0.2"',
        ],
        "django": [
            mgf_pin,
            '"mgf-django>=0.1,<0.2"',
        ],
        "library": [mgf_pin],
        "service": [
            mgf_pin,
            '"mgf-http>=0.1,<0.2"',
        ],
        # PAPER-59 — GUI kind: framework-agnostic, so we only pin
        # mgf-common. Add your GUI framework (PySide6, PyQt6,
        # textual, pywebview, …) to dependencies yourself; the
        # federation does not pick one for you.
        "gui": [mgf_pin],
    }
    deps = deps_for_kind.get(kind, [mgf_pin])
    deps_block = ",\n    ".join(deps)

    return f'''# Add the following blocks to your project's pyproject.toml.
# Generated by `mgf-fed init-project --name {name} --kind {kind}`.

[project]
name = "{name}"
version = "0.1.0"
requires-python = ">=3.11"
dependencies = [
    # mgf-common is the cornerstone — depends on this for the
    # standards, primitives, and `mgf-fed standards <topic>` CLI.
    # Framework-specific siblings ship as separate packages; pin
    # each independently per AP-03's 0.x-window discipline.
    {deps_block},
]

[project.optional-dependencies]
dev = [
    "pytest>=8.0",
    "pytest-cov>=5.0",
    "ruff>=0.4",
    "mypy>=1.10",
    "import-linter>=2.0",
]

# ---------------------------------------------------------------------------
# Four green gates — pin them. Every PR must pass all four.
# ---------------------------------------------------------------------------

[tool.ruff]
line-length = 100
target-version = "py311"
src = ["src", "tests"]

[tool.ruff.lint]
select = [
    "E", "W", "F",      # pycodestyle + pyflakes
    "I",                 # isort
    "N",                 # pep8-naming
    "UP",                # pyupgrade
    "B",                 # bugbear
    "RUF",               # ruff-specific (incl. RUF006 — unawaited tasks → DP-11)
    "DTZ",               # naive datetime → DP-12
    "ASYNC",             # asyncio correctness → DP-11
    "S",                 # bandit security checks
    "TCH",               # type-checking imports → AP-08
    "PTH",               # pathlib over os.path
]

[tool.mypy]
python_version = "3.11"
strict = true
warn_unused_ignores = true

[tool.pytest.ini_options]
minversion = "8.0"
testpaths = ["tests"]
addopts = "--strict-markers --strict-config"
filterwarnings = ["error"]   # TS-04 — every warning is an error

[tool.coverage.report]
fail_under = 80              # TS-06

[tool.importlinter]
root_packages = ["{name}"]
include_external_packages = true

# Add layering contracts as the project grows. See:
#   mgf-fed standards design   # rule DP-01 (layering)
#   mgf-fed standards api      # rule AP-14 (internal modules)
'''


__all__ = ["add_init_project_args", "run_init_project"]
