---
name: project-configuration
description: >
  Read and explain a Pysae repo's `.pysae-ai-tools.yaml` configuration — for one repo, the
  current repo, or all repos — and document every config section for the LLM.
  Use when the user says /project-configuration, 'la config du projet', 'quelle est la
  configuration de <repo>', 'liste les buckets S3 / images ECR / secrets / services k8s',
  'le canal Slack de <repo>', 'la topologie de déploiement', or whenever you need to know
  what a repo declares (routing, deploy, AWS, Prefect…) or what a config field means.
argument-hint: "[<repo path> | all] (default: current repo)"
allowed-tools:
  - Bash
  - Read
---

Resolve and explain the per-repo `.pysae-ai-tools.yaml` config — the **single source of
truth** for a repo's ai-tools data (identity, Slack routing, deploy topology, AWS resources,
Prefect, board/release flags). ai-tools holds no hardcoded Pysae data; everything is in the
configs and resolved live by the CLI.

## Required tools

- `glab`
- `jq`

Run `pysae-ai-tools tools require <tools> || exit 1` before starting; the command exits non-zero with the install command on stderr if any tool is missing or out of date.

## Step 1 — Resolve scope

Parse `` for the target:

- **A repo path** (`pysae/<repo>`, or a bare name → prefix `pysae/`): one specific repo.
- **`all`** (or "tous", "tous les projets"): every repo that carries a config.
- **nothing**: the **current repo** (cwd).

## Step 2 — Fetch the config

**Always read the config through a `project` command — never parse `.pysae-ai-tools.yaml`
directly.** `project show` (and `project flag`) resolve the `.pysae-ai-tools/` overlay and
`overlay:` references at load time; a raw YAML read would miss them.

```bash
# current repo (cwd) — whole effective config (mirrors the file structure)
pysae-ai-tools project show --json

# narrow to a key or subtree — a jq-free getter
pysae-ai-tools project show slack.tech_channel_id   # scalar → verbatim
pysae-ai-tools project show release                 # subtree → YAML (add --json for JSON)
pysae-ai-tools project show project.labels.0        # list element by index (negatives ok)
pysae-ai-tools project show release.tracks          # scalar list → one value per line (jq '.x[]')

# several keys at once → one value per line, in order (capture with successive `read`)
{ IFS= read -r CH; IFS= read -r ID; } < <(pysae-ai-tools project show slack.tech_channel slack.tech_channel_id)

# a specific repo (path or ID)
pysae-ai-tools project show --project pysae/<repo> --json

# all repos at once — {path: config} for every repo with a config
pysae-ai-tools project list --json
```

- **`project show [<dotted.path> ...]`** is the canonical getter skills use: no path → the
  whole resolved config; one path → just that key/subtree; several paths → one value per line
  (or a `{path: value}` object with `--json`). It replaces `--json | jq` — prefer it over `jq`.
- The output **mirrors the config file structure exactly** — there are no inferred/synthesised
  keys. Read the Slack channel at `slack.tech_channel` / `slack.tech_channel_id`, the domain
  labels at `project.labels`, etc., straight off the config.
- `project show` / `project flag` resolve overlays (resolution is at load time), so the
  guarantee holds for any `project` read command — only a direct YAML read would miss them.
- Results are cached on disk (5 min). Pass `--refresh` to bypass when you need fresh data.
- For a **single field across all repos**, aggregate with `jq` — **keep the owning repo**
  (`project list` is keyed by repo path; group by it, don't flatten into one anonymous list).
  The pattern: `map_values(<field>) | with_entries(select(.value | length > 0))` → `{repo: value}`.
  - S3 buckets per repo: `project list --json | jq 'map_values(.aws.s3.buckets) | with_entries(select(.value|length>0))'`
  - ECR images per repo: `project list --json | jq 'map_values(.aws.ecr) | with_entries(select(.value|length>0))'`
  - SecretsManager stores per repo: `project list --json | jq 'map_values(.aws.secrets) | with_entries(select(.value|length>0))'`
  - ElastiCache clusters per repo: `project list --json | jq 'map_values(.aws.elasticache) | with_entries(select(.value|length>0))'`
  - k8s services per repo: `project list --json | jq 'map_values(.k8s.services) | with_entries(select(.value|length>0))'`
  - tech channel per repo: `project list --json | jq 'map_values(.slack.tech_channel) | with_entries(select(.value))'`
  - Only when you genuinely want a flat list (and don't care which repo), use `[.[].aws.s3.buckets[]?]`.

## Step 3 — Explain (config schema)

The full `.pysae-ai-tools.yaml` schema. Every section defaults to today's behaviour, so an
absent/commented key changes nothing. `name` / `description` are **i18n** fields: a plain
string is LLM-translatable; a `{fr:…, en:…, it:…}` mapping is a verbatim brand. Per-env
values use the **`<env>` token** (`resolve_env` / `service.deployment_for(env, slug)`):
`<env>` → `dev` / `prod` / `review-<slug>` for k8s deployments. A name elsewhere may also
carry `<env>` to collapse one entry per env into one (e.g. `aws.secrets` →
`pysae/<env>/secrets`); there the env set is the resource's own (listed in its description),
not necessarily the k8s environments. Irregular per-env values stay as `{dev:…, prod:…}`.

### `project` — repo identity
- `labels` (list) — GitLab labels; **the first is the primary domain** (API/Op/Driver…), the rest secondary (e.g. `[Infra, Security]`). The domain vocabulary is the union of every repo's first label.
- `name` — commercial name (i18n).
- `description` — short description (i18n).
- `stack` — `python` | `node` | `mobile-capacitor`.

### `slack` — Slack routing
- `enabled` (default true) — master switch; false disables every Slack interaction for the repo.
- `tech_channel` / `tech_channel_id` — the repo's tech channel name and Slack ID (declared per repo; no schema default). No fallback: absent ⇒ empty.
- `mep_channel` / `mep_channel_id` — deploy/MEP broadcast channel (no schema default; declared on every repo). `slack post-message --channel mep` resolves to the current repo's `mep_channel_id`.
- `notifications.{mr_review, mep, prerelease_review}` (default true) — per-notification toggles.

### `changelog` / `ai_notes`
- `changelog.enabled` (default true) — keep a CHANGELOG (code-changelog / code-get-next-version).
- `ai_notes.enabled` (default true) — generate `docs/ai-notes/` during code-implement.

### `release` — release process
- `notes.{enabled, languages, prompt}` — user-facing release notes (code-release-notes). `notes.prompt` is free-form text, often externalised (see below).
- `prompt` — free-form custom instructions run on the release branch just before the notes (ci-release Step 5b); empty ⇒ no-op. Often externalised (see below).
- `gitlab_release` (default true) — create the GitLab Release object on tag.
- `tag_prefix` (default `v`), `allow_prerelease` (default false).
- `review.{enabled, prompt}` — pre-release review (code-review-pre-release). `review.prompt` is free-form text, often externalised (see below).
- `tracks` (default `[web]`) — `web` | `service` | `apple` | `android` | `android-enterprise`.
- `stores.{google_play, app_store}` — mobile only; offer to publish.

### `design` — design-generate eligibility
- `enabled` (default false), `web_root`, `pages_proto`.

### `k8s` — declarative deploy topology (argo-apps stays the source of truth)
- `environments` (no default — each deploying repo declares its own) — list of `{name, namespace, kube_context, slug}`. A `slug` env expands `<env>` → `name-<slug>` (review). Pysae default topology: `dev`(ns dev), `prod`(ns prod), `review`(ns mr, ctx dev, slug).
- `secrets` (list of key names) — secret KEY names the repo's services consume (NEVER values).
- `services` — one entry per deployed service:
  - `name`, `description`.
  - `deployment` — ArgoCD app == k8s deployment, templated (`<env>-api-ndp`).
  - `namespace` — only for fixed-namespace infra apps (cert-manager, ingress…), not env-templated.
  - `ecr_image` — the image it runs (an `aws.ecr` entry name).
  - `datadog_service` — its Datadog `service` tag.

### `openapi` — API repos only
- `base_url` (`{dev:…, prod:…}`), `versions`, `visibilities`, `spec_path`, `swagger_path`.

### `aws` — AWS resources the repo owns/declares (infra repos)
- `s3.buckets` — S3 buckets (`{name, description}`).
- `ecr` — ECR container images (`{name, description}`); services reference these via `k8s.services[].ecr_image`.
- `elasticache` — ElastiCache (Redis) clusters (`{name, description}`).
- `secrets` — SecretsManager **secret stores** (bundles, NOT key values). A name may use `<env>` to cover all envs in one entry (`pysae/<env>/secrets`); the env list is in its description.

### `prefect` — Prefect topology (pysae/prefect)
- `workers` — work pools (`{name, type, environment}`; type ∈ kubernetes|process|prefect-agent).
- `flows` — declared flows (`{name, description}`).

### `issues` / `merge_requests`
- `issues.enabled` (default true) — let skills create issues.
- `issues.default_type` — default `type::` label for new issues (e.g. `type::technical` for infra/tooling).
- `merge_requests.enabled` (default true) — let skills create MRs.

### `board` / `autopilot`
- `board.enabled` (default true) — repo tracked on the board.
- `board.sync` (default true) — auto-advance tickets across columns (`workflow::` transitions).
- `autopilot.enabled` (default false) — participate in code-autopilot-batch.

### Externalising values — `.pysae-ai-tools/` overlay + `overlay:` refs

**Any** config value can live outside the YAML — useful when it gets long (a release
prompt, review guidance, …). Two mechanisms, both applying to every field:

- **`.pysae-ai-tools/` overlay directory** (next to the YAML). A file whose **stem** (name
  minus the final extension) is the dotted key sets that value; the extension is free
  (`.txt`, `.md`, …); the file **content** is the value (scalars are coerced —
  `slack.enabled` ← `false`). A present file **overrides** the YAML, and the directory
  alone is enough to configure a repo with no YAML. A **list** uses one file per element,
  `` <key>.<index>.<ext> `` (`release.tracks.0.txt`, `release.tracks.1.txt`).
- **`overlay:` references.** A value prefixed with `overlay:` is replaced by the content of
  its target — `overlay:file://<path>`, `overlay:<path>` (relative to the repo root), or
  `overlay:https://…`. The explicit prefix is what disambiguates a reference from a value
  that is *itself* a URL/path: `openapi.base_url: https://api.pysae.com` stays literal.

Both are resolved at config **load time** on the local checkout, so `project show` and
`project flag` already reflect them. An unloadable `overlay:` reference makes the load fail
(surfaced by those commands with a non-zero exit).

**Read a value with `pysae-ai-tools project show <dotted.path>`** (or the whole
config with no path) — the resolved value on stdout (a string prints verbatim, else
YAML/JSON; add `--json` to force JSON), exit 2 on an unknown path. This is how skills read a prompt-like field
(`release.prompt`, `release.notes.prompt`, `release.review.prompt`).

## Step 4 — Answer

Report the requested data plainly (for a field query, print the value; for a whole repo,
summarise the active sections). When a field is absent, say so and give its default. Never
print secret **values** — the config only ever carries secret/key/store **names**.

Non-config infra topology (ArgoCD root apps, Datadog env tags, EKS clusters, deploy naming,
ECR tag formats, operational notes) lives in [../references/pysae-projects.md](../references/pysae-projects.md).
