From 0df9cec8338293f9ea60c12db4ab7e08da0091da Mon Sep 17 00:00:00 2001 From: "Tobias J. Endres" Date: Sun, 23 Aug 2026 17:56:09 +0200 Subject: [PATCH] docs(cpm): translate all CPM project files to English CPM is an international project; only the blogpost stays German. Translated: cpm.py docstring/messages/help texts, README.md, cpm-setup skill, all team-profile.yaml files (incl. runtime_agent comments), example apm.yml wrappers and the model-mapping template. Also fixes DEFAULT_MAPPING_PATH pointing to ~/.config/apm-team instead of ~/.config/cpm. --- corentic-package-manager/README.md | 178 ++++++------ corentic-package-manager/cpm.py | 267 +++++++++--------- .../examples/microsoft-design-review/apm.yml | 12 +- .../microsoft-design-review/team-profile.yaml | 38 +-- .../microsoft-issue-autopilot/apm.yml | 23 +- .../team-profile.yaml | 46 +-- .../examples/model-mapping.yaml | 10 +- .../examples/package/team-profile.yaml | 48 ++-- .../skills/cpm-setup/SKILL.md | 66 ++--- 9 files changed, 341 insertions(+), 347 deletions(-) diff --git a/corentic-package-manager/README.md b/corentic-package-manager/README.md index 1489a45..b562e02 100644 --- a/corentic-package-manager/README.md +++ b/corentic-package-manager/README.md @@ -1,168 +1,168 @@ # Corentic Package Manager (CPM) -Ein **CPM-Package** ist ein Agent-Package bestehend aus einem [APM](https://microsoft.github.io/apm/)-Manifest plus Team-Konfiguration (`team-profile.yaml`). Der Corentic Package Manager übersetzt dieses Package in die native Konfiguration eines Ziel-Harnesses. +A **CPM package** is an agent package consisting of an [APM](https://microsoft.github.io/apm/) manifest plus a team configuration (`team-profile.yaml`). The Corentic Package Manager translates this package into the native configuration of a target harness. -Übersetzungsziele: +Translation targets: -| Ziel-Harness | Status | +| Target harness | Status | |---|---| -| [oh-my-opencode-slim](https://ohmyopencodeslim.com/) | ✅ implementiert | -| oh-my-pi | geplant | -| Codex / Claude / Copilot | Vision | +| [oh-my-opencode-slim](https://ohmyopencodeslim.com/) | ✅ implemented | +| oh-my-pi | planned | +| Codex / Claude / Copilot | vision | -Deterministisch – der CPM rät nicht. Er nimmt keine Verfügbarkeit an und wählt keine Modelle automatisch aus. +Deterministic – the CPM does not guess. It assumes no availability and never picks models automatically. ``` -CPM-Package Nutzer Runtime +CPM package User Runtime apm.yml + + model-mapping.yaml → cpm render → .opencode/ -team-profile.yaml (Modellklasse → oh-my-opencode-slim.json -(Rollen, Modell- konkrete Modell-ID) (oder künftig: natives - klassen, MCPs/Skills) Config eines anderen Harness) +team-profile.yaml (model class → oh-my-opencode-slim.json +(roles, model concrete model ID) (or later: native config + classes, MCPs/skills) of another harness) ``` -## Voraussetzungen +## Prerequisites -- Python 3.10+ mit PyYAML (`pip install pyyaml`) -- OpenCode mit installiertem Plugin [oh-my-opencode-slim](https://github.com/alvinunreal/oh-my-opencode-slim) -- Ein CPM-Package mit `team-profile.yaml` +- Python 3.10+ with PyYAML (`pip install pyyaml`) +- OpenCode with the [oh-my-opencode-slim](https://github.com/alvinunreal/oh-my-opencode-slim) plugin installed +- A CPM package with a `team-profile.yaml` -## Schnellstart +## Quickstart ```bash -# 1. Modell-Mapping anlegen – interaktiv, mit Auswahl aus deinen opencode-Modellen +# 1. Create the model mapping – interactively, choosing from your opencode models python3 cpm.py init --fill -# 2. Geführt: Team-Profil lesen, Mapping prüfen/auffüllen, rendern -cd -python3 /pfad/zu/cpm.py setup --package /pfad/zum/package +# 2. Guided: read team profile, check/fill mapping, render +cd +python3 /path/to/cpm.py setup --package /path/to/package -# 3. Aktivieren -opencode # Projekt-Config wird geladen +# 3. Activate +opencode # project config gets loaded /preset acme-job-applications -# Reload OpenCode → Team aktiv (by Design: kein Hot-Swap) +# Reload OpenCode -> team is active (by design: no hot-swap) ``` -Alternativ manuell: +Alternatively manually: ```bash -# 1. Modell-Mapping einmalig anlegen +# 1. Create the model mapping once mkdir -p ~/.config/cpm cp examples/model-mapping.yaml ~/.config/cpm/model-mapping.yaml -$EDITOR ~/.config/cpm/model-mapping.yaml # an eigene Modelle anpassen +$EDITOR ~/.config/cpm/model-mapping.yaml # adapt to your own models -# 2. Validieren (Dry-Run) +# 2. Validate (dry run) python3 cpm.py check --package examples/package -# 3. Rendern -cd -python3 /pfad/zu/cpm.py render --package /pfad/zum/package +# 3. Render +cd +python3 /path/to/cpm.py render --package /path/to/package ``` -## Die drei Dateien +## The three files -| Datei | Gehört | Zweck | Manuell editieren? | +| File | Owned by | Purpose | Edit manually? | |---|---|---|---| -| `team-profile.yaml` | Package-Autor | Rollen, Modellklassen, MCP-/Skill-Allowlists pro Rolle | Ja (Autor) | -| `~/.config/cpm/model-mapping.yaml` | Nutzer | Modellklasse → konkrete Modell-ID (+ optional variant/temperature) | Ja (Nutzer) | -| `.opencode/oh-my-opencode-slim.json` | generiert | Preset mit konkreten Modell-IDs | **Nein** – immer neu rendern | +| `team-profile.yaml` | Package author | Roles, model classes, MCP/skill allowlists per role | Yes (author) | +| `~/.config/cpm/model-mapping.yaml` | User | Model class → concrete model ID (+ optional variant/temperature) | Yes (user) | +| `.opencode/oh-my-opencode-slim.json` | generated | Preset with concrete model IDs | **No** – always re-render | -Dazu eine Provenance-Datei `.opencode/oh-my-opencode-slim.cpm-provenance.json`, die dokumentiert, welche Rolle welches Modell erhalten hat. +Plus a provenance file `.opencode/oh-my-opencode-slim.cpm-provenance.json` documenting which role received which model. -## Kommandoreferenz +## Command reference ```bash -python3 cpm.py init [--mapping FILE] [--fill] # Mapping anlegen/füllen -python3 cpm.py setup --package DIR [--mapping F] ... # geführter Ablauf +python3 cpm.py init [--mapping FILE] [--fill] # create/fill mapping +python3 cpm.py setup --package DIR [--mapping F] ... # guided flow python3 cpm.py render --package DIR [--mapping F] [--output F] [--dry-run] -python3 cpm.py check --package DIR [--mapping F] # Dry-Run-Validierung +python3 cpm.py check --package DIR [--mapping F] # dry-run validation ``` -### `init` – Mapping anlegen +### `init` – create the mapping -Erstellt `~/.config/cpm/model-mapping.yaml` (falls nicht vorhanden). Mit `--fill` werden alle `PLACEHOLDER`-Klassen interaktiv abgefragt: der CPM listet alle Modelle aus deiner `opencode.json(c)` auf, du wählst per Nummer oder gibst eine Modell-ID manuell ein. +Creates `~/.config/cpm/model-mapping.yaml` (if not present). With `--fill`, all `PLACEHOLDER` classes are asked interactively: the CPM lists every model from your `opencode.json(c)`, you pick by number or enter a model ID manually. -### `setup` – Geführter Ablauf +### `setup` – guided flow -Der Ein-Kommando-Weg: zeigt das Team-Profil verständlich an, legt fehlendes Mapping an, fragt fehlende Modellklassen interaktiv ab (gleiche Auswahl wie `init --fill`), rendert und gibt die Aktivierungsschritte aus. +The one-command path: shows the team profile in plain language, creates a missing mapping, interactively asks for missing model classes (same selection as `init --fill`), renders and prints the activation steps. ### `render` / `check` -| Option | Default | Bedeutung | +| Option | Default | Meaning | |---|---|---| -| `--package` | `.` | CPM-Package-Verzeichnis mit `team-profile.yaml` | -| `--mapping` | `~/.config/cpm/model-mapping.yaml` | Lokale Mapping-Tabelle | -| `--output` | `/.opencode/oh-my-opencode-slim.json` | Zieldatei | -| `--team-file` | `team-profile.yaml` (alternativ `team.yaml`) | Alternativer Profilname | +| `--package` | `.` | CPM package directory containing `team-profile.yaml` | +| `--mapping` | `~/.config/cpm/model-mapping.yaml` | Local mapping table | +| `--output` | `/.opencode/oh-my-opencode-slim.json` | Target file | +| `--team-file` | `team-profile.yaml` (alternatively `team.yaml`) | Alternative profile filename | -**Merge-Verhalten:** Existierende Presets in der Zieldatei bleiben unberührt; nur das Preset der Team-ID wird ersetzt. Mehrere Packages koexistieren damit im selben Projekt. Aktiviert wird explizit per `/preset `. +**Merge behavior:** Existing presets in the target file remain untouched; only the preset for this team ID is replaced. Multiple packages coexist in the same project. Activation is explicit via `/preset `. -## Team-Profil-Schema (`corentic.team-profile/v1`) +## Team profile schema (`corentic.team-profile/v1`) ```yaml schema: corentic.team-profile/v1 -id: acme.job-applications # wird zum Preset-Namen (namespaced) +id: acme.job-applications # becomes the preset name (namespaced) description: ... roles: - - id: researcher # fachliche Rolle - purpose: ... # wird zu Prompt/Description für Custom Agents - runtime_agent: librarian # optional: Harness-Builtin oder 'custom' - # (Default: role-id als Custom Agent) - model_class: fast-research # Pflicht, muss im Mapping existieren + - id: researcher # domain role + purpose: ... # becomes prompt/description for custom agents + runtime_agent: librarian # optional: harness builtin or 'custom' + # (default: role id as custom agent) + model_class: fast-research # required, must exist in the mapping capabilities: - mcps: [websearch, openviking] # MCP-Allowlist ([] = keine) - skills: [job-application] # Skill-Allowlist ([] = keine) + mcps: [websearch, openviking] # MCP allowlist ([] = none) + skills: [job-application] # skill allowlist ([] = none) ``` -Das Feld `runtime_agent` ist bewusst harness-neutral benannt: Der Adapter entscheidet, wie die Rolle im Ziel-Harness repräsentiert wird. Erkannte Builtins des oh-my-opencode-slim-Adapters: `orchestrator`, `oracle`, `librarian`, `explorer`, `fixer`, `designer`, `council`, `observer`. Alles andere (oder `runtime_agent: custom`) erzeugt einen Custom Agent inklusive `prompt` und `orchestratorPrompt` aus dem `purpose`-Feld. +The `runtime_agent` field is deliberately harness-neutral: the adapter decides how the role is represented in the target harness. Builtins recognized by the oh-my-opencode-slim adapter: `orchestrator`, `oracle`, `librarian`, `explorer`, `fixer`, `designer`, `council`, `observer`. Everything else (or `runtime_agent: custom`) creates a custom agent including `prompt` and `orchestratorPrompt` derived from the `purpose` field. -### Mapping-Formate +### Mapping formats ```yaml model_classes: - fast-research: ollama/qwen3.5:9b # einfach - high-reasoning: # erweitert + fast-research: ollama/qwen3.5:9b # simple + high-reasoning: # extended model: ollama/qwen3.6:35b-a3b-q4_K_M variant: thinking temperature: 0.3 ``` -Alle Zusatzfelder werden 1:1 in den Agent-Eintrag des Presets übernommen. +All extra fields are passed through 1:1 into the agent entry of the preset. -## Was der CPM prüft +## What the CPM checks -1. Team-Profil vorhanden, Schema bekannt, `id` und `roles` vorhanden -2. Jede `model_class` hat einen Mapping-Eintrag (sonst Fehler mit Lösungshinweis) -3. MCPs gegen `~/.config/opencode/opencode.json(c)` abgleichen (**Warnung**, kein Abbruch) -4. Rollen ohne MCPs/Skills → Hinweis +1. Team profile present, schema known, `id` and `roles` present +2. Every `model_class` has a mapping entry (otherwise error with fix hint) +3. MCPs matched against `~/.config/opencode/opencode.json(c)` (**warning**, not fatal) +4. Roles without MCPs/skills -> note -## Beispiele +## Examples -| Beispiel | Basis | Team | +| Example | Based on | Team | |---|---|---| -| `examples/package/` | Eigenes `job-application`-Package (OpenViking + ShareLaTeX) | `acme.job-applications` – Researcher, Writer, Checker, Notifier | -| `examples/microsoft-design-review/` | Offizielles [microsoft/apm-sample-package](https://github.com/microsoft/apm-sample-package) | `acme.design-review` – Reviewer (Oracle), Style-Checker (Fixer), Accessibility-Auditor | -| `examples/microsoft-issue-autopilot/` | Offizielles [`apm-issue-autopilot`](https://github.com/microsoft/apm/tree/main/packages/apm-issue-autopilot) aus dem microsoft/apm-Repo | `acme.issue-autopilot` – Triager, Shepherd, PR-Writer, Reviewer (+ GitHub-MCP) | +| `examples/package/` | Own `job-application` package (OpenViking + ShareLaTeX) | `acme.job-applications` – Researcher, Writer, Checker, Notifier | +| `examples/microsoft-design-review/` | Official [microsoft/apm-sample-package](https://github.com/microsoft/apm-sample-package) | `acme.design-review` – Reviewer (Oracle), Style-Checker (Fixer), Accessibility-Auditor | +| `examples/microsoft-issue-autopilot/` | Official [`apm-issue-autopilot`](https://github.com/microsoft/apm/tree/main/packages/apm-issue-autopilot) from the microsoft/apm repo | `acme.issue-autopilot` – Triager, Shepherd, PR-Writer, Reviewer (+ GitHub MCP) | -Die beiden Microsoft-Beispiele sind dünne CPM-Wrapper: Das eigene `apm.yml` zieht das offizielle Package als versionierte APM-Dependency (`microsoft/apm-sample-package#v1.0.0` bzw. Monorepo-Subpath `microsoft/apm/packages/apm-issue-autopilot`) und liefert nur die Team-Empfehlung dazu. +The two Microsoft examples are thin CPM wrappers: their own `apm.yml` pulls the official package as a versioned APM dependency (`microsoft/apm-sample-package#v1.0.0` or monorepo subpath `microsoft/apm/packages/apm-issue-autopilot`) and only adds the team recommendation. ```bash cd corentic-package-manager/examples -# Trockenlauf: was würde generiert? +# Dry run: what would be generated? python3 ../cpm.py check --package package --mapping model-mapping.yaml python3 ../cpm.py check --package microsoft-design-review --mapping model-mapping.yaml python3 ../cpm.py check --package microsoft-issue-autopilot --mapping model-mapping.yaml -# Rendern in das Beispiel-Projekt +# Render into the example project python3 ../cpm.py render --package package \ --mapping model-mapping.yaml \ --output package/.opencode/oh-my-opencode-slim.json ``` -Erwartetes Ergebnis: Preset `acme-job-applications` mit fünf Agenten – `orchestrator`, `librarian` (Alias `researcher`), Custom Agent `writer`, `oracle` (Alias `checker`), Custom Agent `notifier`. Der Researcher erhält keinen LaTeX-Zugang, der Writer keine Job-Suchtools, der Notifier gar keine externen Zugriffe. +Expected result: preset `acme-job-applications` with five agents – `orchestrator`, `librarian` (alias `researcher`), custom agent `writer`, `oracle` (alias `checker`), custom agent `notifier`. The researcher gets no LaTeX access, the writer sees no job-search tools, the notifier gets no external access at all. -Danach: +Then: ```bash cd package && opencode @@ -171,16 +171,16 @@ cd package && opencode ## Troubleshooting -| Problem | Ursache/Lösung | +| Problem | Cause/Fix | |---|---| -| `Modellklasse X ist nicht gemappt` | Mapping ergänzen, erneut rendern | -| `Warnung: MCP ... nicht konfiguriert` | MCP in `opencode.jsonc` einrichten oder aus dem Profil entfernen | -| `/preset` zeigt nichts Neues | OpenCode nach dem Render neu laden; Preset greift nicht mid-session | -| Preset weg nach manuellem Editieren | Datei ist generiert – Änderungen gehören ins Mapping oder Team-Profil | -| YAML-Fehler | Zeilenangabe aus der Fehlermeldung folgen | +| `Model class X is not mapped` | Add to the mapping, re-render | +| `Warning: MCP ... not configured` | Set up the MCP in `opencode.jsonc` or remove it from the profile | +| `/preset` does not show anything new | Reload OpenCode after rendering; presets do not apply mid-session | +| Preset lost after manual editing | The file is generated – changes belong in the mapping or team profile | +| YAML errors | Follow the line number in the error message | -## Grenzen (bewusst) +## Deliberate limits -- Der CPM startet keine Agenten und orchestriert nichts zur Laufzeit – das macht der Ziel-Harness. -- MCP-/Skill-Allowlists sind Capability Scoping, keine Sandbox. Irreversible Aktionen brauchen serverseitige Autorisierung + Human Approval. -- Die Modellverfügbarkeit wird gegen die opencode.jsonc-Namen geprüft, nicht per API-Healthcheck. +- The CPM does not start agents and orchestrates nothing at runtime – that is the job of the target harness. +- MCP/skill allowlists are capability scoping, not a sandbox. Irreversible actions need server-side authorization + human approval. +- Model availability is checked against opencode.json(c) names, not via API health checks. diff --git a/corentic-package-manager/cpm.py b/corentic-package-manager/cpm.py index eb2f9b5..11f2e40 100644 --- a/corentic-package-manager/cpm.py +++ b/corentic-package-manager/cpm.py @@ -1,23 +1,22 @@ #!/usr/bin/env python3 """cpm – Corentic Package Manager. -Ein CPM-Package ist ein Agent-Package bestehend aus einem APM-Manifest plus -Team-Konfiguration (team-profile.yaml). Der CPM übersetzt dieses Package in -die native Konfiguration eines Ziel-Harnesses. Erstes Übersetzungsziel: -oh-my-opencode-slim. Weitere Ziele (oh-my-pi, codex, claude, copilot) sind -geplant. +A CPM package is an agent package consisting of an APM manifest plus a team +configuration (team-profile.yaml). The CPM translates this package into the +native configuration of a target harness. First translation target: +oh-my-opencode-slim. Further targets (oh-my-pi, codex, claude, copilot) are +planned. -Liest ein team-profile.yaml, löst Modellklassen gegen eine lokale -Mapping-Tabelle auf und generiert ein namespacetes Preset für den -Ziel-Harness. +Reads a team-profile.yaml, resolves model classes against a local mapping +table and generates a namespaced preset for the target harness. -Kommandos: - cpm init Nutzer-Mapping anlegen (optional interaktiv füllen) - cpm setup Geführt: Profil lesen, Mapping auffüllen, rendern - cpm render Team-Profil in Harness-Preset übersetzen und schreiben - cpm check Validieren ohne zu schreiben (Dry-Run) +Commands: + cpm init Create user mapping file (optionally fill interactively) + cpm setup Guided: read profile, fill mapping, render + cpm render Translate team profile into a harness preset and write it + cpm check Validate without writing (dry-run) -Beispiele: +Examples: python3 cpm.py setup --package examples/package python3 cpm.py render --package examples/package --mapping ~/.config/cpm/model-mapping.yaml python3 cpm.py check --package examples/package @@ -34,8 +33,8 @@ from pathlib import Path import yaml -# Eingebaute Agenten des ersten Übersetzungsziels oh-my-opencode-slim. -# Alles andere wird als Custom Agent behandelt. +# Built-in agents of the first translation target oh-my-opencode-slim. +# Everything else is treated as a custom agent. BUILTIN_AGENTS = { "orchestrator", "oracle", @@ -47,31 +46,31 @@ BUILTIN_AGENTS = { "observer", } -DEFAULT_MAPPING_PATH = Path.home() / ".config" / "apm-team" / "model-mapping.yaml" +DEFAULT_MAPPING_PATH = Path.home() / ".config" / "cpm" / "model-mapping.yaml" DEFAULT_TEAM_FILE = "team-profile.yaml" DEFAULT_OUTPUT = Path(".opencode") / "oh-my-opencode-slim.json" PROVENANCE_SUFFIX = ".cpm-provenance.json" -SUPPORTED_SCHEMA = ("corentic.team-profile/v1", "acme.team-profile/v1") +SUPPORTED_SCHEMA = ("corentic.team-profile/v1",) -class OmosError(Exception): - """Fehler mit nutzerlesbarer Ursache.""" +class CpmError(Exception): + """Error with a user-readable cause.""" # --------------------------------------------------------------------------- -# Laden +# Loading def load_yaml(path: Path) -> dict: if not path.exists(): - raise OmosError(f"Datei nicht gefunden: {path}") + raise CpmError(f"File not found: {path}") try: data = yaml.safe_load(path.read_text(encoding="utf-8")) except yaml.YAMLError as exc: - raise OmosError(f"YAML-Fehler in {path}: {exc}") from exc + raise CpmError(f"YAML error in {path}: {exc}") from exc if not isinstance(data, dict): - raise OmosError(f"{path} enthält kein YAML-Mapping") + raise CpmError(f"{path} does not contain a YAML mapping") return data @@ -87,25 +86,25 @@ def load_team_profile(package_dir: Path, team_file: str | None) -> tuple[dict, P schema = profile.get("schema") if schema and schema not in SUPPORTED_SCHEMA: print( - f"Warnung: Unbekanntes Schema '{schema}'. " - f"Unterstützt: {', '.join(SUPPORTED_SCHEMA)}. Fahre fort." + f"Warning: Unknown schema '{schema}'. " + f"Supported: {', '.join(SUPPORTED_SCHEMA)}. Continuing." ) if not profile.get("id"): - raise OmosError(f"{candidate}: Feld 'id' fehlt") + raise CpmError(f"{candidate}: missing field 'id'") roles = profile.get("roles") if not isinstance(roles, list) or not roles: - raise OmosError(f"{candidate}: Keine Rollen definiert ('roles')") + raise CpmError(f"{candidate}: no roles defined ('roles')") return profile, candidate searched = ", ".join(str(c) for c in candidates) - raise OmosError(f"Kein Team-Profil gefunden. Gesucht: {searched}") + raise CpmError(f"No team profile found. Searched: {searched}") def load_mapping(mapping_path: Path | None) -> dict: path = mapping_path or DEFAULT_MAPPING_PATH if not path.exists(): - raise OmosError( - f"Modell-Mapping nicht gefunden: {path}\n" - "Lege die Datei an, z. B.:\n" + raise CpmError( + f"Model mapping not found: {path}\n" + "Create the file, e.g.:\n" " model_classes:\n" " fast-research: ollama/qwen3.5:9b\n" " high-reasoning:\n" @@ -115,22 +114,22 @@ def load_mapping(mapping_path: Path | None) -> dict: data = load_yaml(path) classes = data.get("model_classes") if not isinstance(classes, dict) or not classes: - raise OmosError(f"{path}: Sektion 'model_classes' fehlt oder ist leer") + raise CpmError(f"{path}: section 'model_classes' missing or empty") return classes def resolve_model(model_class_entry: object, model_class: str, role_id: str) -> dict: - """Löst einen Mapping-Eintrag (string oder dict) zu Agent-Feldern auf.""" + """Resolve a mapping entry (string or dict) into agent fields.""" if isinstance(model_class_entry, str): return {"model": model_class_entry} if isinstance(model_class_entry, dict): entry = {k: v for k, v in model_class_entry.items()} if "model" not in entry: - raise OmosError( - f"Rolle '{role_id}': Mapping für '{model_class}' hat kein 'model'-Feld" + raise CpmError( + f"Role '{role_id}': mapping for '{model_class}' has no 'model' field" ) return entry - raise OmosError(f"Ungültiger Mapping-Eintrag für '{model_class}': {model_class_entry!r}") + raise CpmError(f"Invalid mapping entry for '{model_class}': {model_class_entry!r}") def sanitize_preset_name(team_id: str) -> str: @@ -138,11 +137,11 @@ def sanitize_preset_name(team_id: str) -> str: # --------------------------------------------------------------------------- -# Übersetzung +# Translation def build_preset(profile: dict, mapping: dict) -> tuple[dict, dict, list[str]]: - """Erzeugt (preset, custom_agents, warnings).""" + """Returns (preset, custom_agents, warnings).""" preset: dict = {} custom_agents: dict = {} warnings: list[str] = [] @@ -150,15 +149,15 @@ def build_preset(profile: dict, mapping: dict) -> tuple[dict, dict, list[str]]: for role in profile["roles"]: role_id = role.get("id") if not role_id: - raise OmosError("Rolle ohne 'id' gefunden") + raise CpmError("Found a role without 'id'") model_class = role.get("model_class") if not model_class: - raise OmosError(f"Rolle '{role_id}': 'model_class' fehlt") + raise CpmError(f"Role '{role_id}': 'model_class' is missing") if model_class not in mapping: - raise OmosError( - f"Rolle '{role_id}': Modellklasse '{model_class}' ist nicht gemappt.\n" - f"Ergänze in deinem Mapping:\n" + raise CpmError( + f"Role '{role_id}': model class '{model_class}' is not mapped.\n" + f"Add to your mapping:\n" f" {model_class}: " ) @@ -171,7 +170,7 @@ def build_preset(profile: dict, mapping: dict) -> tuple[dict, dict, list[str]]: runtime_agent = role.get("runtime_agent", role_id) if runtime_agent == "custom": - # Explizit als Custom Agent markiert → rolleneigener Name. + # Explicitly marked as custom agent -> use the role's own name. agent_key = role_id else: agent_key = runtime_agent @@ -190,17 +189,17 @@ def build_preset(profile: dict, mapping: dict) -> tuple[dict, dict, list[str]]: prompt = ( purpose if purpose - else f"Custom Agent '{role_id}' aus Team-Profil " - f"'{profile.get('id', 'unbekannt')}'." + else f"Custom agent '{role_id}' from team profile " + f"'{profile.get('id', 'unknown')}'." ) custom_agents[agent_key] = { "model": agent_fields["model"], "description": purpose or f"Custom subagent '{role_id}'", "prompt": prompt, - # Dem Orchestrator sagen, wann er delegieren soll. + # Tell the orchestrator when to delegate to this agent. "orchestratorPrompt": ( - f"@{agent_key}\n- Rolle: {purpose}\n" - "- Delegiere Aufgaben dieser Rolle an diesen Agenten." + f"@{agent_key}\n- Role: {purpose}\n" + "- Delegate tasks of this role to this agent." if purpose else f"@{agent_key}" ), @@ -210,15 +209,15 @@ def build_preset(profile: dict, mapping: dict) -> tuple[dict, dict, list[str]]: if not mcps and not skills: warnings.append( - f"Rolle '{role_id}' ({agent_key}): keine MCPs/Skills zugewiesen " - "(rein koordinierend?)" + f"Role '{role_id}' ({agent_key}): no MCPs/skills assigned " + "(purely coordinating?)" ) orchestrator_present = any(k == "orchestrator" for k in preset) if not orchestrator_present: warnings.append( - "Kein 'orchestrator' im Team. Ohne Orchestrator-Preset-Eintrag bleibt " - "dessen Modell unverändert." + "No 'orchestrator' in the team. Without an orchestrator preset " + "entry its model stays unchanged." ) return preset, custom_agents, warnings @@ -231,8 +230,8 @@ def merge_into_config(config: dict, preset_name: str, preset: dict, existing = presets.get(preset_name) if existing: print( - f"Hinweis: Preset '{preset_name}' existierte bereits und wird ersetzt " - "(andere Presets bleiben unberührt)." + f"Note: preset '{preset_name}' already existed and will be replaced " + "(other presets remain untouched)." ) presets[preset_name] = preset @@ -242,7 +241,7 @@ def merge_into_config(config: dict, preset_name: str, preset: dict, # --------------------------------------------------------------------------- -# MCP-Verfügbarkeit prüfen (best effort) +# MCP availability check (best effort) def strip_jsonc(text: str) -> str: @@ -251,12 +250,14 @@ def strip_jsonc(text: str) -> str: return text +OPENCODE_CONFIGS = [ + Path.home() / ".config" / "opencode" / "opencode.jsonc", + Path.home() / ".config" / "opencode" / "opencode.json", +] + + def configured_mcps() -> set[str]: - candidates = [ - Path.home() / ".config" / "opencode" / "opencode.jsonc", - Path.home() / ".config" / "opencode" / "opencode.json", - ] - for path in candidates: + for path in OPENCODE_CONFIGS: if path.exists(): try: data = json.loads(strip_jsonc(path.read_text(encoding="utf-8"))) @@ -267,13 +268,9 @@ def configured_mcps() -> set[str]: def available_models() -> list[str]: - """Listet alle provider/model-Kombinationen aus der opencode.json(c).""" - candidates = [ - Path.home() / ".config" / "opencode" / "opencode.jsonc", - Path.home() / ".config" / "opencode" / "opencode.json", - ] + """List all provider/model combinations from opencode.json(c).""" models: list[str] = [] - for path in candidates: + for path in OPENCODE_CONFIGS: if not path.exists(): continue try: @@ -288,12 +285,12 @@ def available_models() -> list[str]: INIT_TEMPLATE = """\ -# Lokale Mapping-Tabelle des NUTZERS. -# Ordnet abstrakte Modellklassen konkrete Modell-IDs zu. -# Formate: -# klasse: provider/modell -# klasse: -# model: provider/modell +# Local mapping table of the USER. +# Maps abstract model classes to concrete model IDs. +# Formats: +# class: provider/model +# class: +# model: provider/model # variant: thinking # optional # temperature: 0.7 # optional @@ -310,7 +307,7 @@ def check_mcps(preset: dict, warnings: list[str]) -> None: available = configured_mcps() if not available: warnings.append( - "Konnte opencode.json(c) nicht lesen – MCP-Prüfung übersprungen." + "Could not read opencode.json(c) - MCP check skipped." ) return needed: set[str] = set() @@ -319,13 +316,13 @@ def check_mcps(preset: dict, warnings: list[str]) -> None: missing = sorted(m for m in needed if m not in available) if missing: warnings.append( - "MCPs im Team-Profil, aber nicht in opencode.json konfiguriert: " - + ", ".join(missing) + "MCPs referenced in the team profile but not configured in " + "opencode.json: " + ", ".join(missing) ) # --------------------------------------------------------------------------- -# Kommandos +# Commands def package_output_path(package_dir: Path) -> Path: @@ -344,7 +341,7 @@ def cmd_render(args: argparse.Namespace) -> int: try: config = json.loads(output.read_text(encoding="utf-8")) except json.JSONDecodeError as exc: - raise OmosError(f"{output} ist kein gültiges JSON: {exc}") + raise CpmError(f"{output} is not valid JSON: {exc}") else: config = {} @@ -363,7 +360,7 @@ def cmd_render(args: argparse.Namespace) -> int: output.parent.mkdir(parents=True, exist_ok=True) output.write_text(json.dumps(merged, indent=2, ensure_ascii=False), encoding="utf-8") - print(f"Geschrieben: {output}") + print(f"Written: {output}") provenance = { "profile": profile["id"], @@ -386,83 +383,83 @@ def cmd_render(args: argparse.Namespace) -> int: provenance_path.write_text( json.dumps(provenance, indent=2, ensure_ascii=False), encoding="utf-8" ) - print(f"Provenance: {provenance_path}") + print(f"Provenance: {provenance_path}") _report(warnings) print( - "\nNächste Schritte:\n" - " 1. opencode starten (Projekt-Config wird geladen)\n" + "\nNext steps:\n" + " 1. start opencode (project config gets loaded)\n" f" 2. /preset {sanitize_preset_name(str(profile['id']))}\n" - " 3. OpenCode neu laden → Team aktiv" + " 3. reload OpenCode -> team is active" ) return 0 def cmd_check(args: argparse.Namespace) -> int: args.dry_run = True - # Dry-Run: vorhandene Projekt-Config einbeziehen, aber nie schreiben. + # Dry run: include an existing project config, but never write. existing = package_output_path(Path(args.package).resolve()) args.output = str(existing) if existing.exists() else None try: cmd_render(args) - except OmosError as exc: - print(f"Fehler: {exc}", file=sys.stderr) + except CpmError as exc: + print(f"Error: {exc}", file=sys.stderr) return 1 return 0 def _report(warnings: list[str], dry_run: bool = False) -> None: if dry_run: - print("\n--- Dry-Run: Es wurde nichts geschrieben ---") + print("\n--- Dry run: nothing was written ---") if warnings: - print("\nWarnungen:") + print("\nWarnings:") for warning in warnings: print(f" ⚠ {warning}") else: - print("\nKeine Warnungen.") + print("\nNo warnings.") # --------------------------------------------------------------------------- -# Interaktive Assistenten +# Interactive assistants def _choose_model(model_class: str, models: list[str]) -> str: - """Interaktive Auswahl eines Modells für eine Modellklasse.""" - print(f"\nFür '{model_class}' ist kein Modell zugeordnet.") + """Interactively pick a model for a model class.""" + print(f"\nNo model assigned for '{model_class}'.") if models: - print("Verfügbare Modelle (aus opencode.json(c)):") + print("Available models (from opencode.json(c)):") for i, model in enumerate(models, 1): print(f" {i}. {model}") - print(" 0. Modell-ID manuell eingeben") + print(" 0. Enter model ID manually") while True: - choice = input("Auswahl: ").strip() + choice = input("Selection: ").strip() if choice == "0": - return input("Modell-ID (provider/model): ").strip() + return input("Model ID (provider/model): ").strip() if choice.isdigit() and 1 <= int(choice) <= len(models): return models[int(choice) - 1] - print("Ungültige Auswahl.") + print("Invalid selection.") else: - print("Keine Modelle in opencode.json(c) gefunden.") - return input("Modell-ID (provider/model): ").strip() + print("No models found in opencode.json(c).") + return input("Model ID (provider/model): ").strip() def cmd_init(args: argparse.Namespace) -> int: mapping_path = Path(args.mapping) if args.mapping else DEFAULT_MAPPING_PATH if mapping_path.exists(): - print(f"Mapping existiert bereits: {mapping_path}") + print(f"Mapping already exists: {mapping_path}") else: mapping_path.parent.mkdir(parents=True, exist_ok=True) mapping_path.write_text(INIT_TEMPLATE, encoding="utf-8") - print(f"Angelegt: {mapping_path}") + print(f"Created: {mapping_path}") if args.fill: missing = [k for k, v in load_mapping(mapping_path).items() if v == "PLACEHOLDER"] models = available_models() if missing and not models: - raise OmosError( - "Keine Modelle in ~/.config/opencode/opencode.json(c) gefunden. " - "Trage die Modell-IDs manuell ein oder richte opencode ein." + raise CpmError( + "No models found in ~/.config/opencode/opencode.json(c). " + "Enter the model IDs manually or set up opencode first." ) data = load_yaml(mapping_path) for model_class in missing: @@ -474,33 +471,33 @@ def cmd_init(args: argparse.Namespace) -> int: remaining = sum(1 for v in data["model_classes"].values() if v == "PLACEHOLDER") if remaining: - print(f"\n{remaining} Eintrag/Einträge bleiben PLACEHOLDER – " - "bitte manuell ergänzen.") + print(f"\n{remaining} entr(y/ies) left as PLACEHOLDER - " + "please fill in manually.") - print("\nNächster Schritt:") + print("\nNext step:") print(f" cpm.py check --package --mapping {mapping_path}") return 0 def cmd_setup(args: argparse.Namespace) -> int: - """Geführter Ablauf: Profil lesen → Mapping auffüllen → rendern.""" + """Guided flow: read profile -> fill mapping -> render.""" package_dir = Path(args.package).resolve() - profile, profile_path = load_team_profile(package_dir, args.team_file) + profile, _profile_path = load_team_profile(package_dir, args.team_file) mapping_path = Path(args.mapping) if args.mapping else DEFAULT_MAPPING_PATH - print(f"Team-Profil: {profile['id']}") + print(f"Team profile: {profile['id']}") if profile.get("description"): print(f" {str(profile['description']).strip()}") - print("Rollen:") + print("Roles:") for role in profile["roles"]: mcps = (role.get("capabilities", {}) or {}).get("mcps", []) skills = (role.get("capabilities", {}) or {}).get("skills", []) print(f" - {role['id']} → {role.get('model_class')} " - f"(MCPs: {', '.join(mcps) or 'keine'}; " - f"Skills: {', '.join(skills) or 'keine'})") + f"(MCPs: {', '.join(mcps) or 'none'}; " + f"Skills: {', '.join(skills) or 'none'})") if not mapping_path.exists(): - print(f"\nMapping fehlt – lege an: {mapping_path}") + print(f"\nMapping missing - creating: {mapping_path}") args_init = argparse.Namespace(mapping=str(mapping_path), fill=True) cmd_init(args_init) @@ -518,9 +515,9 @@ def cmd_setup(args: argparse.Namespace) -> int: yaml.safe_dump(classes, sort_keys=False, allow_unicode=True), encoding="utf-8", ) - print(f"\nMapping aktualisiert: {mapping_path}") + print(f"\nMapping updated: {mapping_path}") - print("\nRendern …") + print("\nRendering ...") args_render = argparse.Namespace( package=str(package_dir), team_file=args.team_file, @@ -535,9 +532,9 @@ def main(argv: list[str] | None = None) -> int: parser = argparse.ArgumentParser( prog="cpm", description=( - "Adapter zwischen APM-Team-Profilen und oh-my-opencode-slim. " - "Übersetzt deterministisch: Team-Profil + lokales Modell-Mapping " - "-> Harness-Preset." + "Corentic Package Manager. Translates CPM packages (APM manifest + " + "team-profile.yaml) deterministically into harness-native presets: " + "team profile + local model mapping -> harness preset." ), ) sub = parser.add_subparsers(dest="command", required=True) @@ -546,70 +543,70 @@ def main(argv: list[str] | None = None) -> int: p.add_argument( "--package", default=".", - help="Pfad zum APM-Package (mit team-profile.yaml)", + help="Path to the CPM package directory (containing team-profile.yaml)", ) p.add_argument( "--team-file", - help="Alternativer Dateiname des Team-Profils (Default: team-profile.yaml)", + help="Alternative filename of the team profile (default: team-profile.yaml)", ) p.add_argument( "--mapping", - help=f"Pfad zur Modell-Mapping-Datei (Default: {DEFAULT_MAPPING_PATH})", + help=f"Path to the model mapping file (default: {DEFAULT_MAPPING_PATH})", ) - p_render = sub.add_parser("render", help="Preset generieren und schreiben") + p_render = sub.add_parser("render", help="Generate and write the preset") common(p_render) p_render.add_argument( "--output", - help=f"Zieldatei (Default: {DEFAULT_OUTPUT} relativ zum Package)", + help=f"Target file (default: {DEFAULT_OUTPUT} relative to the package)", ) p_render.add_argument( "--dry-run", action="store_true", - help="Nur anzeigen, nichts schreiben", + help="Print only, write nothing", ) p_render.set_defaults(func=cmd_render) - p_check = sub.add_parser("check", help="Validieren ohne zu schreiben") + p_check = sub.add_parser("check", help="Validate without writing") common(p_check) p_check.set_defaults(func=cmd_check) p_init = sub.add_parser( "init", - help="Modell-Mapping-Datei anlegen und optional Platzhalter füllen", + help="Create the model mapping file, optionally filling placeholders", ) p_init.add_argument( "--mapping", - help=f"Zielpfad (Default: {DEFAULT_MAPPING_PATH})", + help=f"Target path (default: {DEFAULT_MAPPING_PATH})", ) p_init.add_argument( "--fill", action="store_true", - help="Fehlende Modellklassen interaktiv aus opencode.json(c)-Modellen wählen", + help="Interactively choose missing model classes from opencode.json(c) models", ) p_init.set_defaults(func=cmd_init) p_setup = sub.add_parser( "setup", - help="Geführt: Profil lesen, Mapping auffüllen, rendern", + help="Guided: read profile, fill mapping, render", ) common(p_setup) p_setup.add_argument( "--output", - help=f"Zieldatei (Default: {DEFAULT_OUTPUT} relativ zum Package)", + help=f"Target file (default: {DEFAULT_OUTPUT} relative to the package)", ) p_setup.add_argument( "--dry-run", action="store_true", - help="Nur anzeigen, nichts schreiben", + help="Print only, write nothing", ) p_setup.set_defaults(func=cmd_setup) args = parser.parse_args(argv) try: return args.func(args) - except OmosError as exc: - print(f"Fehler: {exc}", file=sys.stderr) + except CpmError as exc: + print(f"Error: {exc}", file=sys.stderr) return 1 diff --git a/corentic-package-manager/examples/microsoft-design-review/apm.yml b/corentic-package-manager/examples/microsoft-design-review/apm.yml index 744901d..d605c0a 100644 --- a/corentic-package-manager/examples/microsoft-design-review/apm.yml +++ b/corentic-package-manager/examples/microsoft-design-review/apm.yml @@ -1,15 +1,15 @@ -# CPM-Wrapper um das offizielle Microsoft Sample-Package. -# Installiert dessen Primitives (design-standards, design-review, -# accessibility-audit, style-checker, design-reviewer) und liefert -# die Team-Empfehlung dafür. +# Thin CPM wrapper around the official Microsoft sample package. +# Installs its primitives (design-standards, design-review, +# accessibility-audit, style-checker, design-reviewer) and adds the +# team recommendation. # # Original: https://github.com/microsoft/apm-sample-package name: acme/design-review-cpm version: 0.1.0 description: > - Design-Review-Team auf Basis des offiziellen microsoft/apm-sample-package: - Reviewer, Style-Checker und Accessibility-Auditor unter einem Orchestrator. + Design review team based on the official microsoft/apm-sample-package: + reviewer, style checker and accessibility auditor under one orchestrator. author: Tobias Endres license: MIT type: hybrid diff --git a/corentic-package-manager/examples/microsoft-design-review/team-profile.yaml b/corentic-package-manager/examples/microsoft-design-review/team-profile.yaml index d8e54c8..6b998bb 100644 --- a/corentic-package-manager/examples/microsoft-design-review/team-profile.yaml +++ b/corentic-package-manager/examples/microsoft-design-review/team-profile.yaml @@ -1,19 +1,19 @@ -# team-profile.yaml – Design-Review-Team -# Rollen basierend auf den Primitives aus microsoft/apm-sample-package. +# team-profile.yaml – Design Review Team +# Roles based on the primitives from microsoft/apm-sample-package. schema: corentic.team-profile/v1 id: acme.design-review description: > - Team für strukturierte Design-Reviews: Style-Guideline-Prüfung, - Design-Review nach Vorlage und Accessibility-Audit unter einem - koordinierenden Reviewer. + Team for structured design reviews: style-guideline checks, + design review per template and an accessibility audit under a + coordinating reviewer. roles: - id: orchestrator purpose: >- - Nimmt Review-Anfragen an, verteilt Style-Check, Design-Review - und Accessibility-Audit und konsolidiert die Findings zu einem Report. + Takes review requests, distributes style check, design review and + accessibility audit and consolidates the findings into one report. model_class: strong-generalist capabilities: mcps: [] @@ -21,10 +21,11 @@ roles: - id: reviewer purpose: >- - Führt das Design-Review gemäß der design-review.prompt.md-Vorlage durch. - Bewertet UI-Konsistenz gegen die design-standards.instructions.md. - Meldet Findings mit Schweregrad, keine eigenmächtigen Codeänderungen. - runtime_agent: oracle # tiefes Prüfen = Oracle-Profil + Performs the design review following the design-review.prompt.md + template. Evaluates UI consistency against the + design-standards.instructions.md. Reports findings with severity, + no unauthorized code changes. + runtime_agent: oracle # deep review = oracle profile model_class: high-reasoning capabilities: mcps: [] @@ -32,9 +33,10 @@ roles: - id: style-checker purpose: >- - Prüft Code mechanisch gegen die Style-Guidelines aus dem - style-checker-Skill. Liefert Verstoßliste mit Datei/Zeile. Keine Fixes. - runtime_agent: fixer # schnelle, gezielte Prüfungen + Mechanically checks code against the style guidelines from the + style-checker skill. Returns a violation list with file/line. + No fixes. + runtime_agent: fixer # fast, targeted checks model_class: fast-research capabilities: mcps: [] @@ -42,10 +44,10 @@ roles: - id: accessibility-auditor purpose: >- - Auditet die Oberfläche gemäß accessibility-audit.prompt.md. - Kategorisiert Verstöße (kritisch/warnung/hinweis) und zitiert die - betroffenen Stellen. - runtime_agent: librarian # Recherche-/Prüfprofil + Audits the surface according to accessibility-audit.prompt.md. + Categorizes violations (critical/warning/note) and quotes the + affected locations. + runtime_agent: librarian # research/review profile model_class: fast-research capabilities: mcps: [] diff --git a/corentic-package-manager/examples/microsoft-issue-autopilot/apm.yml b/corentic-package-manager/examples/microsoft-issue-autopilot/apm.yml index f5120bd..166829b 100644 --- a/corentic-package-manager/examples/microsoft-issue-autopilot/apm.yml +++ b/corentic-package-manager/examples/microsoft-issue-autopilot/apm.yml @@ -1,21 +1,20 @@ -# CPM-Wrapper um apm-issue-autopilot aus dem offiziellen microsoft/apm-Repo. +# Thin CPM wrapper around apm-issue-autopilot from the official microsoft/apm repo. # -# Das Original-Paket orchestriert bereits Skills (apm-triage-panel, -# shepherd-driver, pr-description-skill) und treibt Issues bis zum -# mergebaren PR. Das Team-Profil hier bildet genau diese Phasen auf -# Agentenrollen ab. +# The upstream package already orchestrates skills (apm-triage-panel, +# shepherd-driver, pr-description-skill) and drives issues to a mergeable PR. +# The team profile below maps exactly those phases onto agent roles. # -# Original: https://github.com/microsoft/apm/tree/main/packages/apm-issue-autopilot -# APM unterstützt Monorepo-Subpaths, daher ist die Dependency direkt -# auf das Unterverzeichnis gerichtet. +# Upstream: https://github.com/microsoft/apm/tree/main/packages/apm-issue-autopilot +# APM supports monorepo subpaths, so the dependency points directly at the +# subdirectory. name: acme/issue-autopilot-cpm version: 0.1.0 description: > - Issue-Triage-Team auf Basis von microsoft/apm packages/apm-issue-autopilot: - Triage als Gate, Shepherd für den Drive-to-Merge, PR-Beschreibungen und - eine prüfende Gegenstimme. Human-in-the-loop: Escalation an den Maintainer - bei Zweifel. + Issue triage team based on microsoft/apm packages/apm-issue-autopilot: + triage as gate, shepherd for drive-to-merge, PR descriptions and a + reviewing counter-check. Human-in-the-loop: escalate to the maintainer + on doubt. author: Tobias Endres license: MIT type: hybrid diff --git a/corentic-package-manager/examples/microsoft-issue-autopilot/team-profile.yaml b/corentic-package-manager/examples/microsoft-issue-autopilot/team-profile.yaml index 97f8ecc..5da3719 100644 --- a/corentic-package-manager/examples/microsoft-issue-autopilot/team-profile.yaml +++ b/corentic-package-manager/examples/microsoft-issue-autopilot/team-profile.yaml @@ -1,22 +1,21 @@ -# team-profile.yaml – Issue-Autopilot-Team -# Rollen basierend auf den Phasen des offiziellen apm-issue-autopilot-Pakets: -# Triage → Entscheidung → Drive-to-Merge → PR-Beschreibung. +# team-profile.yaml – Issue Autopilot Team +# Roles based on the phases of the official apm-issue-autopilot package: +# Triage -> Decision -> Drive-to-Merge -> PR description. schema: corentic.team-profile/v1 id: acme.issue-autopilot description: > - Treibt GitHub-Issues vom Intake bis zum mergebaren PR: Triage-Panel - als zentrales Gate, Shepherd für jeden akzeptierten Issue, - PR-Autor mit verankerter Beschreibung. Escaliert standardmäßig beim - Maintainer im Zweifelsfall. + Drives GitHub issues from intake to mergeable PR: triage panel as the + central gate, a shepherd per accepted issue, a PR author with anchored + descriptions. Escalates to the maintainer by default on doubt. roles: - id: orchestrator purpose: >- - Nimmt die Issue-Liste entgegen, startet pro Issue die Triage und fasst - zu EIN konsolidiertem Review zusammen. Eskaliert Zweifelfälle explizit - an den Maintainer statt selbst zu entscheiden. + Takes the issue list, starts triage per issue and consolidates into ONE + combined review. Escalates doubtful cases explicitly to the maintainer + instead of deciding itself. model_class: strong-generalist capabilities: mcps: [github] @@ -24,10 +23,10 @@ roles: - id: triager purpose: >- - Führt die Triage gemäß apm-triage-panel-Rubrik durch. Bewertet - Reproduzierbarkeit, Scope, Priorität und Duplikate. Empfehlung mit - Begründung, keine direkten Fixes. - runtime_agent: oracle # Bewertungs-Gate = Reasoning-Profil + Performs triage according to the apm-triage-panel rubric. Evaluates + reproducibility, scope, priority and duplicates. Recommendation with + rationale, no direct fixes. + runtime_agent: oracle # decision gate = reasoning profile model_class: high-reasoning capabilities: mcps: [github] @@ -35,10 +34,10 @@ roles: - id: shepherd purpose: >- - Treibt akzeptierte Issues über shepherd-driver bis zum mergebaren PR: - Branch, Fix, Mutation-Break- und Lint-Gates, CI-Watch, Konfliktauflösung. - Hält bei rotem CI inne. - runtime_agent: fixer # Umsetzung + Korrekturen + Drives accepted issues via shepherd-driver to a mergeable PR: + branch, fix, mutation-break and lint gates, CI watch, conflict + resolution. Halts on red CI. + runtime_agent: fixer # implementation + corrections model_class: strong-writing capabilities: mcps: [github] @@ -46,9 +45,9 @@ roles: - id: pr-writer purpose: >- - Verfasst die PR-Beschreibung mit pr-description-skill. Verankert sie - am Issue mit Acceptance-Kriterien und Testnachweis. Merged nie selbst. - runtime_agent: custom # eigener Custom Agent 'pr-writer' + Authors the PR description using pr-description-skill. Anchors it to + the issue with acceptance criteria and test evidence. Never merges. + runtime_agent: custom # dedicated custom agent 'pr-writer' model_class: strong-writing capabilities: mcps: [github] @@ -56,8 +55,9 @@ roles: - id: reviewer purpose: >- - Gegenprobe vor dem Push: prüft Fix gegen Issue-Akzeptanzkriterien - und PRINCIPLES.md-Gate. Findings blockieren den Push, kein Self-Merge. + Counter-check before push: verifies the fix against issue acceptance + criteria and the PRINCIPLES.md gate. Findings block the push, + no self-merge. runtime_agent: oracle model_class: high-reasoning capabilities: diff --git a/corentic-package-manager/examples/model-mapping.yaml b/corentic-package-manager/examples/model-mapping.yaml index 3715568..71f7329 100644 --- a/corentic-package-manager/examples/model-mapping.yaml +++ b/corentic-package-manager/examples/model-mapping.yaml @@ -1,11 +1,11 @@ # ~/.config/cpm/model-mapping.yaml # -# Lokale Mapping-Tabelle des NUTZERS. -# Ordnet abstrakte Modellklassen aus CPM-Packages (Team-Profile) konkrete, lokal -# verfügbare Modell-IDs zu. Einmal anlegen, gilt für alle Packages. +# Local mapping table of the USER. +# Maps abstract model classes from CPM packages (team profiles) to concrete, +# locally available model IDs. Create once, applies to all packages. # -# Einfache Form: klasse: provider/modell-id -# Erweiterte Form: klasse: { model: ..., variant: ..., temperature: ... } +# Simple form: class: provider/model-id +# Extended form: class: { model: ..., variant: ..., temperature: ... } model_classes: strong-generalist: ollama/qwen3.6:35b-a3b-q4_K_M diff --git a/corentic-package-manager/examples/package/team-profile.yaml b/corentic-package-manager/examples/package/team-profile.yaml index 2beb11a..3c26d1b 100644 --- a/corentic-package-manager/examples/package/team-profile.yaml +++ b/corentic-package-manager/examples/package/team-profile.yaml @@ -1,61 +1,57 @@ -# team-profile.yaml -# Deklaration des empfohlenen Agenten-Teams für das Package job-application. -# -# Diese Datei gehört dem Package-AUTOR. Sie beschreibt: -# - welche Rollen für den fachlichen Workflow sinnvoll sind, -# - welche Modellklasse jede Rolle benötigt (abstrakt, keine konkreten IDs), -# - welche Teilmenge der MCPs und Skills jede Rolle verwenden darf. -# -# Der Nutzer löst die Modellklassen lokal auf (siehe model-mapping.yaml). -# Der CPM (Corentic Package Manager) übersetzt dieses Profil in die native -# Konfiguration eines Ziel-Harnesses – aktuell: oh-my-opencode-slim. +# team-profile.yaml - Job Application Team +# Roles for the job-application package (OpenViking + ShareLaTeX). schema: corentic.team-profile/v1 id: acme.job-applications description: > - Human-in-the-loop-Team zur Recherche, Vorbereitung und Prüfung - individueller Bewerbungsunterlagen. + Human-in-the-loop team for researching, preparing and reviewing + individual job application documents. roles: - id: orchestrator - purpose: Zerlegt Aufgaben, delegiert an Spezialisten, integriert Ergebnisse + purpose: >- + Decomposes tasks, delegates to specialists and integrates results. model_class: strong-generalist capabilities: mcps: [] skills: [job-application] - id: researcher - purpose: Sucht und bewertet Stellenausschreibungen gegen das Kandidatenprofil - runtime_agent: librarian # nutzt oh-my-opencode-slim-Builtin 'librarian' mit Alias + purpose: >- + Searches and evaluates job openings against the candidate profile. + runtime_agent: librarian # uses the oh-my-opencode-slim builtin with an alias model_class: fast-research capabilities: mcps: [websearch, webfetch, openviking] skills: [job-application] - id: writer - purpose: Erstellt auf Fakten basierende Anschreiben und Lebensläufe in ShareLaTeX. - Nutze nur freigegebene Profilfakten. Erfinde keine Erfahrung. - Reiche niemals selbstständig eine Bewerbung ein. - runtime_agent: custom # eigener Custom Agent 'writer' + purpose: >- + Creates fact-based cover letters and CVs in ShareLaTeX. + Only use approved profile facts. Never invent experience. + Never submit an application autonomously. + runtime_agent: custom # dedicated custom agent 'writer' model_class: strong-writing capabilities: mcps: [openviking, sharelatex] skills: [] - id: checker - purpose: Prüft Fakten, Ton und Vollständigkeit gegen das Kandidatenprofil. - Jede Behauptung braucht eine belegbare Quelle. - runtime_agent: oracle # nutzt oh-my-opencode-slim-Builtin 'oracle' mit Alias + purpose: >- + Reviews facts, tone and completeness against the candidate profile. + Every claim requires a verifiable source. + runtime_agent: oracle # uses the oh-my-opencode-slim builtin with an alias model_class: high-reasoning capabilities: mcps: [openviking] skills: [] - id: notifier - purpose: Informiert den Nutzer über fertige Entwürfe. - Versende niemals etwas ohne explizite Nutzerfreigabe. - runtime_agent: custom # eigener Custom Agent 'notifier' + purpose: >- + Informs the user about finished drafts. + Never send anything without explicit user approval. + runtime_agent: custom # dedicated custom agent 'notifier' model_class: cheap-reliable capabilities: mcps: [] diff --git a/corentic-package-manager/skills/cpm-setup/SKILL.md b/corentic-package-manager/skills/cpm-setup/SKILL.md index 4f43b26..79ad7da 100644 --- a/corentic-package-manager/skills/cpm-setup/SKILL.md +++ b/corentic-package-manager/skills/cpm-setup/SKILL.md @@ -1,6 +1,6 @@ --- name: cpm-setup -description: Activate when the user wants to set up, render, or troubleshoot a CPM package (APM package + team-profile). Use for cpm init, cpm setup, cpm render, team-profile, model-mapping, oh-my-opencode-slim preset, fehlende Modellklassen. +description: Activate when the user wants to set up, render, or troubleshoot a CPM package (APM package + team-profile). Use for cpm init, cpm setup, cpm render, team-profile, model-mapping, oh-my-opencode-slim preset, missing model classes. allowed-tools: - "Bash(python3 corentic-package-manager/cpm.py *)" - "Read" @@ -11,65 +11,65 @@ allowed-tools: # CPM Setup Skill -Führt den Nutzer durch: CPM-Package (APM-Manifest + Team-Profil) → lokales Modell-Mapping → gerendertes Harness-Preset (aktuell: oh-my-opencode-slim). +Guides the user through: CPM package (APM manifest + team profile) → local model mapping → rendered harness preset (currently: oh-my-opencode-slim). -## Der Ablauf (Resolve–Validate–Render) +## The flow (Resolve–Validate–Render) -### 1. Team-Profil finden +### 1. Find the team profile -Prüfe, ob eine `team-profile.yaml` existiert (Package-Root oder `corentic-package-manager/examples/package/team-profile.yaml`). Lies sie und fasse dem Nutzer zusammen: +Check whether a `team-profile.yaml` exists (package root or `corentic-package-manager/examples/package/team-profile.yaml`). Read it and summarize for the user: ``` -Profil: acme.job-applications -Rollen: orchestrator → strong-generalist, researcher → fast-research, +Profile: acme.job-applications +Roles: orchestrator → strong-generalist, researcher → fast-research, writer → strong-writing, checker → high-reasoning, notifier → cheap-reliable -MCPs benötigt: openviking, sharelatex (+ websearch/webfetch als OpenCode-Native) +MCPs required: openviking, sharelatex (+ websearch/webfetch as OpenCode-native) ``` -### 2. Mapping prüfen +### 2. Check the mapping -Prüfe `~/.config/cpm/model-mapping.yaml`. Für **jede** im Profil verwendete Modellklasse muss ein Eintrag existieren. +Check `~/.config/cpm/model-mapping.yaml`. For **every** model class used in the profile an entry must exist. -Wenn eine Modellklasse fehlt, biete dem Nutzer konkrete Optionen an (verfügbare Modelle aus `~/.config/opencode/opencode.jsonc` auflisten): +If a model class is missing, offer the user concrete options (list available models from `~/.config/opencode/opencode.jsonc`): ``` -Für 'high-reasoning' ist kein Modell zugeordnet. -1. Vorhandenes Modell wählen (z. B. ollama/qwen3.6:35b-a3b-q4_K_M) -2. Neues Provider/Modell in opencode.jsonc konfigurieren -3. Abbrechen +No model assigned for 'high-reasoning'. +1. Pick an existing model (e.g. ollama/qwen3.6:35b-a3b-q4_K_M) +2. Configure a new provider/model in opencode.jsonc +3. Abort ``` -Nach Auswahl schreibe das Mapping und markiere optional `# selected_by: user`. +After selection write the mapping and optionally mark `# selected_by: user`. -Alternativ die interaktiven Kommandos nutzen: `cpm init --fill` und `cpm setup`. +Alternatively use the interactive commands: `cpm init --fill` and `cpm setup`. -### 3. MCPs prüfen +### 3. Check MCPs -Vergleiche die im Profil genannten MCPs mit der Sektion `mcp` in `~/.config/opencode/opencode.jsonc`. Fehlende dem Nutzer melden – nicht stillschweigend ignorieren. Native OpenCode-Tools (`websearch`, `webfetch`) sind keine MCPs und brauchen keinen Eintrag. +Compare the MCPs named in the profile against the `mcp` section in `~/.config/opencode/opencode.jsonc`. Report missing ones to the user – never silently ignore. OpenCode-native tools (`websearch`, `webfetch`) are not MCPs and need no entry. -### 4. Rendern +### 4. Render ```bash python3 corentic-package-manager/cpm.py render --package ``` -Zeige die Ausgabe inklusive Warnungen. Die Datei `.opencode/oh-my-opencode-slim.json` ist generiert – nicht manuell editieren. +Show the output including warnings. The file `.opencode/oh-my-opencode-slim.json` is generated – never edit it manually. -### 5. Aktivieren +### 5. Activate -Nutzer anleiten: `opencode` starten → `/preset ` → Reload. Preset-Wechsel greifen erst nach einem Reload (by Design). +Guide the user: start `opencode` → `/preset ` → reload. Preset switches only take effect after a reload (by design). -## Fehlerbehandlung +## Error handling -| Fehler | Lösung | +| Error | Fix | |---|---| -| „Modellklasse X ist nicht gemappt" | Schritt 2: Mapping ergänzen | -| „MCP ... nicht in opencode.json konfiguriert" | Schritt 3: MCP einrichten oder Rolle anpassen | -| „Kein Team-Profil gefunden" | Pfad prüfen, `--team-file` verwenden | -| Ungültiges YAML | Zeile aus Fehlermeldung zeigen | +| "Model class X is not mapped" | Step 2: add to mapping | +| "MCP ... not configured in opencode.json" | Step 3: set up the MCP or adjust the role | +| "No team profile found" | Check path, use `--team-file` | +| Invalid YAML | Follow the line number in the error message | -## Regeln +## Rules -- Niemals selbst Modelle erraten oder zuordnen – immer den Nutzer wählen lassen. -- Niemals die generierte `.opencode/oh-my-opencode-slim.json` direkt editieren; Quelle sind Team-Profil + Mapping. -- Sicherheitsgrenzen des Packages (MCP/Skill-Allowlists) nur verschärfen, nie aufweichen. +- Never guess or assign models yourself – always let the user choose. +- Never edit the generated `.opencode/oh-my-opencode-slim.json` directly; sources are the team profile + mapping. +- Only ever tighten package security boundaries (MCP/skill allowlists), never widen them.