Compare commits

..

11 commits

Author SHA1 Message Date
Tobias J. Endres
dd72fc4520 chore: IDE workspace update 2026-08-23 18:30:17 +02:00
Tobias J. Endres
602f0958bd docs(blog): current drafts, fact-check review and image assets
- apm-omos-agent-teams-v3.md: main blogpost draft (German)
- omos-deep-dive.md: second blogpost on oh-my-opencode-slim capabilities
- blog-review.md: source-of-truth copy of the Ghost draft + numbered
  revision proposals from the Perplexity fact-check
- image assets referenced by the blogposts (.jpg/.png)
2026-08-23 18:29:59 +02:00
Tobias J. Endres
ae6817d86f feat(cpm): integrate 'apm install' into the guided flow
The rendered preset only configures agents; the skills/instructions
that role allowlists reference are deployed by APM. cpm setup now
detects apm.yml dependencies and offers to run 'apm install' after
rendering (manual command printed when the CLI is missing or stdin
is not a TTY). render/check print it as an explicit next step.

Also:
- next steps now name the absolute directory to start opencode from
  (fixes presets appearing 'missing' when opencode runs in the wrong
  cwd, as hit during first real-world test)
- init no longer prints literal <package-dir> placeholders
- model list is shown once instead of repeating per class
- README: new 'Using the team' section (primitives -> reload ->
  sanity check -> work via orchestrator), activation writes to the
  global user config, troubleshooting entry for missing skills
2026-08-23 18:24:25 +02:00
Tobias J. Endres
0df9cec833 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.
2026-08-23 17:56:09 +02:00
Tobias J. Endres
913be9d528 feat(cpm): official Microsoft APM packages as team examples
Two thin CPM wrappers around official upstream packages, each with a
team-profile mapping their primitives to agent roles:

- microsoft-design-review: wraps microsoft/apm-sample-package#v1.0.0
  (design-standards, design-review, accessibility-audit, style-checker)
  -> roles: reviewer (oracle), style-checker (fixer), accessibility-auditor

- microsoft-issue-autopilot: wraps microsoft/apm/packages/apm-issue-autopilot
  (triage-panel, shepherd-driver, pr-description-skill) + github-mcp-server
  -> roles: triager, shepherd, pr-writer (custom), reviewer

Both validate with cpm check; README examples table added.
2026-08-23 17:45:55 +02:00
Tobias J. Endres
939599441c chore(cpm): ignore __pycache__ 2026-08-23 17:27:06 +02:00
Tobias J. Endres
2af88b6493 refactor!: rename omos to Corentic Package Manager (CPM)
A CPM package is an agent package consisting of an APM manifest plus a
team configuration (team-profile.yaml). The manager translates such a
package into the native config of a target harness. First target:
oh-my-opencode-slim; oh-my-pi, codex, claude and copilot are planned.

- omos/ -> corentic-package-manager/, cpm.py as CLI entry point
- new commands: cpm init [--fill] and cpm setup (guided flow with
  interactive model selection from opencode.json(c))
- schema field omos_agent -> harness-neutral runtime_agent
- user mapping moved to ~/.config/cpm/model-mapping.yaml
- provenance suffix .cpm-provenance.json
- skill renamed to cpm-setup
2026-08-23 17:26:28 +02:00
Tobias J. Endres
b897bb4b17 feat(omos): interactive init and setup commands
- omos init [--fill]: create user mapping file, interactively resolve
  PLACEHOLDER classes by picking from models discovered in
  opencode.json(c)
- omos setup: guided flow - show profile summary, create/fill missing
  mappings with explicit user choice, then render
- README quickstart updated to the two-command path
2026-08-23 17:10:35 +02:00
Tobias J. Endres
f035902f20 docs(omos): user guide with quickstart, walkthrough and troubleshooting 2026-08-23 17:03:40 +02:00
Tobias J. Endres
f0f1915251 feat(omos): example package, team profile, mapping and setup skill
- examples/package: copy of the job-application APM package with
  corentic.team-profile/v1 declaration (orchestrator, researcher,
  writer, checker, notifier)
- examples/model-mapping.yaml: user-side model class mapping with
  simple and extended entries
- skills/omos-setup: skill guiding users through
  resolve-validate-render and activation
2026-08-23 17:03:40 +02:00
Tobias J. Endres
907f43d40b feat(omos): sidecar adapter prototype
Deterministic translator between APM team profiles and
oh-my-opencode-slim presets. Reads team-profile.yaml, resolves
model classes against a user-local mapping table, generates
namespaced presets plus provenance. Includes check (dry-run),
merge-by-preset-id, custom agent generation from purpose fields,
and MCP availability warnings.
2026-08-23 17:03:40 +02:00
32 changed files with 2801 additions and 0 deletions

3
.idea/.gitignore generated vendored Normal file
View file

@ -0,0 +1,3 @@
# Default ignored files
/shelf/
/workspace.xml

12
.idea/APM-Packages.iml generated Normal file
View file

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="PYTHON_MODULE" version="4">
<component name="NewModuleRootManager">
<content url="file://$MODULE_DIR$" />
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
<component name="PackageRequirementsSettings" />
<component name="PyDocumentationSettings" />
<component name="ReSTService" />
<component name="TestRunnerService" />
</module>

View file

@ -0,0 +1,6 @@
<component name="InspectionProjectProfileManager">
<settings>
<option name="USE_PROJECT_PROFILE" value="false" />
<version value="1.0" />
</settings>
</component>

8
.idea/modules.xml generated Normal file
View file

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/APM-Packages.iml" filepath="$PROJECT_DIR$/.idea/APM-Packages.iml" />
</modules>
</component>
</project>

6
.idea/vcs.xml generated Normal file
View file

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="" vcs="Git" />
</component>
</project>

Binary file not shown.

After

Width:  |  Height:  |  Size: 407 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 317 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 316 KiB

545
apm-omos-agent-teams-v3.md Normal file
View file

@ -0,0 +1,545 @@
# APM, Agent-Teams und das kleine Problem namens Realität
## Wie APM-Packages um Agent Team-Empfehlungen erweitert werden können mit oh-my-opencode-slim als erstem konkretem Target.
<img alt="SNES-Cartridge in klassischem Grau mit lila Etikett. Das Etikett zeigt den Text &quot;Tour Planer Agent 3000&quot; in fettem, retro-weißen und gelben Schriftzug. Die Cartridge ist leicht nach rechts geneigt, mit subtiler Reflexion auf der Kunststoffoberfläche. Neutraler, sauberer Hintergrund." src="./snes.jpg" title="Tour Planer Agent 3000 SNES Cartridge" width="480"/>
Ein Austauschformat so selbsterklärend wie in die Videospiele der 90er. Einfach Einlegen und Abspielen. Aktuell hat diese Vision mit der Realität des AI-Agenten-Ökosystems wenig zu tun. Nur die Gefahr des Vendor-Lock-Ins bleibt bestehen.
Heute beginnt das Abenteuer zunächst als Textadventure.
> Was willst du tun? (Instruktionen)
>
> Wie soll es erledigt werden? (Skills)
>
> Womit soll es erledigt werden? (Werkzeuge, MCP-Abhängigkeiten)
>
> Wie soll das Agenten-Team zusammengesetzt sein?
>
> Welche Modellkonfiguration sollen die Agenten erhalten? (Modell, Variante, Temperature, … pro Subagent)
>
> Welche Untermenge der Werkzeuge erhalten die jeweiligen Agenten? (MCP-per-Subagent)
>
> Welches Orchestrierungsmuster soll verwendet werden?
Was folgt, ist der klassische _programmer Move_: Man automatisiert (mittlerweile AI-unterstützt) in nur 10 Stunden erfolgreich eine Aufgabe die man händisch in 10 Minuten hätte erledigen können.
Selbstverständlich ist all das praktisch nicht portabel, denn will man seinen mühsam definierten Workflow mit Freunden und Kollegen teilen muss
alles passen. Welcher Harness soll verwendet werden? Sind die Modelle verfügbar und falls nein, kann ich sie substituieren? ...
## Harnesskonfiguration
<img src="./homer.jpg" width="300" style="margin:1em;"/>
### Kann das nicht jemand anderes erledigen?
Bevor es apt, yum oder pacman gab, war Software-Installation unter Unix vor allem eins: Handarbeit. Man lud sich den Quellcode als Tarball herunter, entpackte ihn irgendwo, konfigurierte, tippte make und hoffte auf das Beste.
Bevor es Docker gab, war das Ausrollen von Software ein heilloses Durcheinander aus Versionskonflikten, fehlenden Bibliotheken und dem klassischen _„Runs on my Machine“_.
Genau das machen wir heute mit Agenten-Frameworks. Wir kopieren Prompt-Templates per Hand in `.codex/` oder `.opencode/` Ordner, passen JSON-Dateien an und beten, dass das verwendete Modell die System-Prompts versteht.
Ein Package soll mehr sein als ein Ordner voller Prompts. Wenn ich ```apm install <package>``` ausführe, will ich nicht nur Dateien in `.opencode/` oder `.codex/` kopiert bekommen ich will ein funktionierendes Setup,
das weiß, wie es arbeiten soll. Welche Rollen braucht diese Aufgabe? Welcher Agent recherchiert, welcher schreibt, welcher misstraut? Und welches Modell darf welche Rolle übernehmen, ohne dass ich das nach jeder Installation
von Hand in einer Config zurechtbastle? Kann ich nicht einfach einen ACP-Client meiner Wahl auf meinen Agenten richten und mich darauf verlassen, dass MCPs, Skills, Dependencies, Orchestrierung usw. mit dem Auschecken eines Pakets bereits erledigt sind?
```mermaid
graph LR
subgraph Package Registry
A[Skills] --> B[Package]
C[Instructions] --> B
D[MCP-Abhängigkeiten] --> B
end
subgraph Bridge
B -->|deklarative Empfehlung| E[Adapter]
end
subgraph Harness
E --> F[Agenten-Team]
E --> G[Modellkonfiguration]
end
style B fill:#f9f,stroke:#333
style E fill:#bbf,stroke:#333
```
> Ein Package Registry liefert Skills, Instructions, MCP-Abhängigkeiten und eine deklarative Empfehlung für ein Agenten-Team. Ein Harness-spezifischer Adapter übersetzt diese Empfehlung in die lokale Team- und Modellkonfiguration.
## Das Ökosystem heute
<img src="/incompatible.jpg" width="400" style="margin:1em;" />
Der Agenten-Stack entwickelt sich gerade mit der Geschwindigkeit eines explodierenden Werkzeugschuppens. Jeden Monat erscheint ein neues Manifest, ein neues Plugin-Format oder eine neue Orchestrierungsebene. Alles löst einen legitimen Teil des Problems. Fast nichts passt nahtlos zusammen.
Das ist kein Vorwurf. Es ist der normale Zustand eines jungen Ökosystems. Der interessante Teil beginnt dort, wo die bestehenden Stücke bereits ausreichen, um eine sinnvolle Brücke zu bauen.
Die vorhandenen Formate lassen sich grob in fünf Gruppen einteilen.
| Ebene | Beispiele | Zuständigkeit |
|---|---|---|
| Portable Komponenten | [Agent Plugins](https://agent-plugins.org/) | Skills und MCP-Server verteilen |
| Dependency Management | [APM](https://microsoft.github.io/apm/) | Packages, Lockfile, Policies, Deployment |
| Team-Komposition | [Spawnfile](https://github.com/noopolis/spawnfile), Harness-Configs | Rollen, Delegation, Agentenbeziehungen |
| Runtime-Profile | [oh-my-opencode-slim Presets](https://ohmyopencodeslim.com/community-presets) | Modelle und Optionen je Agent |
| Governance | [Agent Manifest](https://agent-manifest-spec.org/) | Identität, Grenzen, Verantwortung, Auditierbarkeit |
Was fehlt, ist ein allumfassendes YAML-Format.
## Ein paar Worte zu CrewAI
<img alt="https://xkcd.com/927/" src="./standards_2x.png" title="https://xkcd.com/927/" width="320"/>
One Standard to rule them all? Ja genau das scheinen sich die Entwickler von [CrewAI](https://crewai.com/) gedacht zu haben und tatsächlich: "CrewAI is rapidly becoming the standard for production-ready agentic automation". So steht es jedenfalls auf ihrer Website wobei >50.000 GitHub Sterne zeigen, dass das Projekt durchaus über Momentum verfügt.
CrewAI löst die fehlende Standardisierung nicht durch Übersetzung zwischen Harnessen, sondern indem es die komplette Ausführungsschicht selbst besitzt: Agenten, Tasks, Tool-Aufrufe, State, Prozesse und Delegation laufen in der CrewAI-Runtime.
Damit scheidet es für unsere weiteren Überlegungen allerdings aus, denn CrewAI integriert die Welt in eine Runtime und nicht umgekehrt. Unser Ziel ist ein Format zu finden, dessen Pakete sich in die vorhandene Welt integrieren.
### Agent Plugins
[Agent Plugins 1.0](https://agent-plugins.org/) definiert ein bewusst kleines, portables Plugin-Format. Im Kern enthält ein Plugin eine `plugin.json` und kann Skills sowie MCP-Server bereitstellen. Erweiterungen sind möglich, müssen aber im `extensions`-Block über einen Reverse-Domain-Namespace isoliert werden.
Das ist sehr gut für Interoperabilität. Ein Skill bleibt ein Skill. Ein MCP bleibt ein MCP. Der Standard versucht nicht, die Laufzeit eines jeden Agentenframeworks zu regieren.
Offen bleiben jedoch Dependencies, getestete Versionen, Teamrollen, Modellprofile und die Aktivierung im konkreten Harness.
### APM
[APM](https://microsoft.github.io/apm/) besetzt die nächste Ebene: Package- und Dependency-Management für Agent-Kontext.
Ein APM-Manifest kann Abhängigkeiten auf andere APM-Packages, MCP-Server und LSP-Server beschreiben. APM verwaltet Auflösung, Lockfile und Installation. Über Targets kompiliert es Primitives in Harness-spezifische Dateien. Das native `opencode`-Target installiert Skills, Agents und Commands in die passenden `.opencode/`-Pfade.
```yaml
name: acme/research-assistant
version: 1.0.0
type: hybrid
targets:
- opencode
- omos
dependencies:
apm:
- acme/base-engineering
mcp:
- name: repository-search
registry: false
transport: stdio
command: repository-search-mcp
tools: [search_code, read_file]
```
APM beantwortet damit: Welche Artefakte gehören zusammen? Welche Versionen wurden zusammen getestet? Welche MCPs werden benötigt? Wohin werden Skills und Instructions installiert?
Was APM aktuell nicht standardisiert beschreibt, ist Team-Semantik: Rollen, Delegation, Modell-Empfehlungen und Aktivierung eines Team-Profils.
### Spawnfile
[Spawnfile](https://github.com/noopolis/spawnfile) adressiert die Teamachse. Agenten und Teams können deklarativ mit Rollen, Zuständigkeiten, Kommunikationswegen und Subagenten beschrieben werden. Die offene Stelle liegt bei Dependencies und Verteilung: Ein Team-Manifest installiert keine versionierten Skills, MCPs und Policies. Gültige Ziele sind momentan
Spawnfile v0.1 ist ausdrücklich für autonome, langlebige Agent-Runtimes gedacht. Spawnfile listet OpenCode als Runtime aber mit Status exploratory: Kein Adapter, kein generiertes opencode.json, kein `.opencode/`. Tatsächlich wird in v0.1 für keine Runtime ein natives Team-Artifact compiliert; Teams degradieren immer zu Kontext-Dokumenten in Agent-Workspaces.
### Oh My Opencode Slim
<img src="./oh-my-opencode-slim.png" title="oh-my-opencode-slim" width="520"/>
## Die wilde Welt der Agent-Harnesses
Wenn es um Multi-Agent-Orchestrierung geht, sieht die aktuelle Landschaft aus wie ein Experimentierfeld nach einem Chemieunfall. Jeder Harness hat seine eigene Vorstellung davon, wie Subagenten funktionieren sollten und keine davon ist mit den anderen kompatibel.
Claude Code kennt Custom Subagents als Markdown-Dateien und experimentiert mit Agententeams, bei denen mehrere Claude-Instanzen über eine gemeinsame Task-List kommunizieren. GitHub Copilot definiert Custom Agents als SDK-Konfiguration mit TypeScript oder Python und bietet Fleet Mode für parallele Ausführung. Codex setzt auf ein Coordinator-Pattern, bei dem ein Hauptagent Tasks an spezialisierte Subagents delegiert. oh-my-opencode-slim wiederum arbeitet mit Presets und Custom Agents, die zur Laufzeit umgeschaltet werden können.
Das Ergebnis: Vier verschiedene Formate, vier verschiedene Delegationsmechanismen, vier verschiedene Wege, Modelle und MCPs pro Agent zu konfigurieren. Niemand hat sich auf ein gemeinsames Protokoll geeinigt. Jeder baut sein eigenes Ding.
| Harness | Subagenten-Typ | Team-Unterstützung | Modell pro Agent | MCP pro Agent | Skill pro Agent | Delegation |
| ------------------- | ---------------------------- | ---------------------------------- | ----------------- | ---------------------- | ------------------- | ------------------------------- |
| oh-my-opencode-slim | Custom Agents + Built-in | ❌ Nein (nur Presets) | ✅ Ja (pro Preset) | ✅ Ja (mcps-Array) | ✅ Ja (skills-Array) | Automatisch via Orchestrator |
| Claude Code | Custom Subagents (.md-Files) | ✅ Ja (Agent Teams, experimental) | ✅ Ja (model-Feld) | ✅ Ja (mcpServers-Feld) | ✅ Ja (skills-Feld) | Automatisch + explizit |
| GitHub Copilot | Custom Agents (SDK) | ⚠️ Teilweise (Fleet Mode) | ✅ Ja (model-Feld) | ✅ Ja (mcpServers-Feld) | ✅ Ja (skills-Feld) | Automatisch via Intent-Matching |
| Codex | Multi-Agent V2 | ⚠️ Teilweise (Coordinator-Pattern) | ✅ Ja | ✅ Ja | ✅ Ja | Explizit via Coordinator |
Genau dafür hat Microsofts [APM das targets-Feld](https://microsoft.github.io/apm/reference/targets-matrix/) vorgesehen. Es ist die ehrliche Antwort auf die Frage: „Wie deployen wir Agent-Kontext in eine Welt, in der jeder Harness seine eigenen Regeln hat?" Die Antwort lautet: Wir definieren Targets für jeden Harness und übersetzen das Package in das jeweilige Format.
Oder, weniger diplomatisch: **Weil sich kein einziger der großen Player die Mühe gemacht hat, mit den anderen zu reden, bevor sie ihr Agenten-System gebaut haben, müssen wir jetzt für jeden von ihnen einen eigenen Übersetzer schreiben**.
Claude Code macht es so. Copilot macht es anders. Codex hat wieder eine eigene Vorstellung. Und der Rest? Macht sein eigenes Ding. Das targets-Feld ist damit ein Eingeständnis: Niemand wird sich einigen. Also bauen wir Brücken zu allen.
Stand August 2026 sieht APM laut Schema folgende Werte als Ziele vor: `copilot`, `claude`, `grok-build`, `cursor`, `opencode`, `codex`, `gemini`, `antigravity`, `windsurf`, `kiro`, `agent-skills`.
Damit klafft hier bereits die erste Lücke, denn **was fehlt, ist ein allumfassendes YAML-Format.**
Wir können also warten bis dieser Standard definiert ist, oder wir bauen uns selbst eine kleine Kompositionsschicht. Für Oh My Opencode Slim nenne ich diesen Adapter im Folgenden **`omos`**. `omos` ist dabei ein Design für eine Erweiterung bzw. ein Sidecar-Tool. Es ist heute kein offiziell eingebautes APM-Target.
[//]: # ()
[//]: # (```mermaid)
[//]: # (flowchart LR)
[//]: # ( AP[Skills und MCPs] -->|Agent Plugins| PK[Package-Inhalt])
[//]: # ( APM[Dependencies, Lockfile, Installation] -->|APM| PK)
[//]: # ( SF[Rollen und Teamstruktur] -->|Spawnfile| TM[Team-Modell])
[//]: # ( PK -. fehlende Verbindung .-> TM)
[//]: # (```)
[//]: # ()
```mermaid
flowchart LR
AP[Skills und MCPs] -->|Agent Plugins| PK[Package-Inhalt]
APM[Dependencies, Lockfile, Installation] -->|APM| PK
SF[Rollen und Teamstruktur] -->|Spawnfile/OMOS Community Presets| TM[Team-Modell]
OMOS[oh-my-opencode-slim] -->|OMOS Community Presets| RT[aktive Runtime]
PK -. fehlende Verbindung .-> TM
TM -. fehlende Verbindung .-> RT
```
## Die APM-OMOS-Idee
[Oh My Opencode Slim](https://ohmyopencodeslim.com/) ist ein schlankes, optimiertes Open-Source-Plugin für OpenCode, das auf Multi-Agenten-Orchestrierung ausgelegt ist und sitzt nahe an der Runtime. Sein Preset-System kann je Agent Modell, Temperatur, Variante und provider-spezifische Optionen setzen. Presets werden in der Config definiert und über den `/preset`-Befehl ausgewählt.
```jsonc
{
"presets": {
"deep-review": {
"orchestrator": { "model": "ollama/qwen3.6:35b-a3b-q4_K_M" },
"oracle": { "model": "ollama/qwen3.6:35b-a3b-q4_K_M", "variant": "thinking" },
"librarian": { "model": "ollama/qwen3.5:9b" }
}
}
}
```
[//]: # (Wichtig für das Verständnis, wie die Aktivierung tatsächlich funktioniert:)
[//]: # ()
[//]: # (1. Der `/preset`-Befehl öffnet einen TUI-Manager. **Apply** schreibt ausschließlich den Namen des gewählten Presets in die Config-Datei.)
[//]: # (2. Die Agent-Registry wird bewusst **nicht** mid-session umgeschaltet. Ein Hot-Swap würde Kontext truncieren, laufende Subagents auf veraltete Definitionen lassen und Tool-Verfügbarkeit verschieben.)
[//]: # (3. Erst nach einem **Reload von OpenCode** &#40;oder dem Start einer neuen Konversation&#41; greift das Preset.)
[//]: # (4. Beim Laden angewendet werden `model`, `temperature`, `variant` und provider-spezifische `options`. Änderungen an `prompt`, `skills`, `mcps` oder `displayName` benötigen ohnehin einen Neustart.)
[//]: # ()
[//]: # (Ein Neustart von opencode ist also by Design. Für Team-Wechsel zwischen Aufgaben ist das vollkommen ausreichend und es hält die Runtime vorhersehbar.)
Das beantwortet die Frage, wie unterschiedliche Rollen unterschiedliche Modellprofile erhalten. Es fehlt die Herkunft: Welches Package empfiehlt dieses Team? Welche Skills und MCPs gehören dazu? Welche Version wurde getestet?
Ein APM-Package bleibt Source of Truth für Skills, Instructions, MCP-Abhängigkeiten, Versionen, Dependency-Closure und Policies. Zusätzlich enthält es ein Team-Profil als Empfehlung für Harness-Adapter.
```yaml
schema: acme.team-profile/v1
id: acme.document-research
description: Team-Profil für Recherche, Erstellung und Qualitätsprüfung quellenbasierter Dokumente.
roles:
- id: orchestrator
purpose: Zerlegt Aufgaben, delegiert, integriert Ergebnisse
model_class: strong-generalist
- id: researcher
purpose: Sammelt und bewertet Quellen
model_class: fast-research
capabilities:
mcps: [websearch, openviking]
skills: [job-matching]
- id: writer
purpose: Erstellt strukturierte Dokumententwürfe
model_class: strong-writing
capabilities:
mcps: [sharelatex]
skills: [application-writing]
- id: checker
purpose: Prüft Quellenbezug, Konsistenz und Vollständigkeit
model_class: high-reasoning
capabilities:
mcps: [openviking]
skills: [application-review]
```
Das Profil verwendet Modellklassen wie `high-reasoning` statt konkreter Modell-IDs. Der Nutzer hinterlegt in einer lokalen Mapping-Tabelle, welche Modell-IDs für welche Modellklasse verwendet werden sollen.
```yaml
# ~/.config/apm-team/model-mapping.yaml
model_classes:
strong-generalist: ollama/qwen3.6:35b-a3b-q4_K_M
fast-research: ollama/qwen3.5:9b
strong-writing: ollama/qwen3.6:35b-a3b-q4_K_M
high-reasoning: ollama/qwen3.6:35b-a3b-q4_K_M
```
Diese Tabelle ist bewusst trivial. Sie ist kein intelligentes Routing und keine Policy-Engine. Der Nutzer entscheidet einmal, welches Modell seine Klasse „schnelle Recherche" oder „starkes Reasoning" erfüllt und der Adapter übersetzt nur noch nach.
## Was `omos` macht
`omos` ist der Übersetzer zwischen Team-Profil und oh-my-opencode-slim. Er liest installierte APM-Packages, findet Team-Profile, schlägt Modellklassen gegen die lokale Mapping-Tabelle nach und generiert Presets unter einem entsprechenden Namespace für das Projekt.
```mermaid
flowchart TD
P[APM Package: team-profile.yaml] --> O[omos liest]
M[~/.config/apm-team/model-mapping.yaml] --> O
O -->|Prüfe| X{Alle Modellklassen gemappt?}
X -->|Nein| E[Fehler: fehlendes Mapping für Rolle Y]
X -->|Ja| G[Generiere OMOS-Preset mit konkreten Modell-IDs]
G --> V[Validiere: Modelle in OMOS verfügbar?]
V -->|Nein| F[Fehler: Modell Z nicht im OMOS-Setup]
V -->|Ja| C[Schreibe .opencode/oh-my-opencode-slim.json]
C --> D[Fertig]
```
`omos` rät nicht. Er übersetzt deterministisch:
1. Team-Profil lesen (Modellklassen + MCP/Skill-Capabilities).
2. Lokale Mapping-Tabelle laden (Modellklasse → konkrete Modell-ID).
3. OMOS-Preset generieren (konkrete Modell-IDs + MCP/Skill-Allowlists).
4. Validieren: Sind die Modelle im OMOS-Setup verfügbar?
5. Preset in `.opencode/oh-my-opencode-slim.json` schreiben.
## Mehrere Packages, ein Projekt
Jedes Profil erhält eine globale, stabile ID wie `acme.job-applications`, `acme.security-audit` oder `acme.code-review`. `omos` merged Presets anhand dieser IDs. Das aktive Team wird nicht durch die letzte Installation gewählt.
```mermaid
flowchart LR
A[Package: Job Applications] --> M[Preset Merge]
B[Package: Security Audit] --> M
C[Package: Code Review] --> M
M --> P[Projekt-Preset-Registry]
P --> U[Nutzer oder expliziter Workflow]
U --> S[/preset acme-job-applications + Reload]
```
## Beispiel: Autobewerber als Team-Package
Ein Bewerbungsassistent eignet sich gut als Beispiel, weil er alle Ebenen gleichzeitig berührt: Web-Recherche, personenbezogene Daten (das Kandidatenprofil), Dokumentenproduktion in LaTeX, Qualitätsprüfung und Benachrichtigungen. Der Mechanismus bleibt derselbe für Code-Review, Sales Research, Compliance oder Incident Response.
Konkret arbeite ich hier mit einem real existierenden Setup: Das Package `job-application` bringt eine Skill für Stellenrecherche und maßgeschneiderte Bewerbungsunterlagen mit. Es nutzt zwei MCP-Server:
- **OpenViking** als Langzeitgedächtnis für das Kandidatenprofil (Skills, Erfahrungen, persönliche Daten)
- **ShareLaTeX** zur Erstellung und Kompilierung der Anschreiben und Lebensläufe
Dazu kommen OpenCodes native Tools für Web-Recherche (`websearch`, `webfetch`).
### Rollen und OMOS-Mapping
| Fachliche Rolle | OMOS-Rolle | Zweck |
|---|---|---|
| Koordination | `orchestrator` | Zerlegt Aufgaben, delegiert, integriert |
| Recherche | `librarian` (Alias `researcher`) | Sucht und bewertet Stellen |
| Schreiben | Custom Agent `writer` | Erstellt Anschreiben und Unterlagen |
| Prüfung | `oracle` (Alias `checker`) | Prüft Fakten, Ton, Vollständigkeit |
| Benachrichtigung | Custom Agent `notifier` | Informiert den Nutzer |
### Was OMOS tatsächlich begrenzen kann
OMOS bietet pro Agent zwei brauchbare Kontrollflächen:
1. **MCP-Zugriff** (`mcps`-Array)
2. **Skill-Zugriff** (`skills`-Array)
Beides kann je Agent in einem Preset als Allowlist formuliert werden. `[]` bedeutet keine Freigabe; bei Konflikten gewinnt die Verweigerung.
Das generierte OMOS-Preset für unser Beispiel sieht dann so aus:
```jsonc
{
"$schema": "https://unpkg.com/oh-my-opencode-slim@latest/oh-my-opencode-slim.schema.json",
"presets": {
"acme-job-applications": {
"orchestrator": {
"model": "ollama/qwen3.6:35b-a3b-q4_K_M",
"mcps": [],
"skills": ["job-application"]
},
"librarian": {
"displayName": "researcher",
"model": "ollama/qwen3.5:9b",
"mcps": ["websearch", "openviking"],
"skills": ["job-application"]
},
"writer": {
"model": "ollama/qwen3.6:35b-a3b-q4_K_M",
"mcps": ["openviking", "sharelatex"],
"skills": []
},
"oracle": {
"displayName": "checker",
"model": "ollama/qwen3.6:35b-a3b-q4_K_M",
"variant": "thinking",
"mcps": ["openviking"],
"skills": []
},
"notifier": {
"model": "ollama/gemma4:12b",
"mcps": [],
"skills": []
}
}
}
}
```
Damit erhält der Researcher keinen ShareLaTeX-Zugang. Der Writer sieht keine Job-Suchtools, darf aber auf Profilfakten zugreifen und in ShareLaTeX arbeiten. Der Checker kann Fakten gegen das Profil prüfen, aber nichts produzieren oder senden. Der Notifier bekommt gar keine externen Zugriffe.
### Die wichtige Einschränkung
`mcps` und `skills` schützen nur den Zugriff auf genau diese zwei Ebenen. Die Aussage „der Agent darf niemals eine Bewerbung absenden" benötigt Verteidigung in mehreren Schichten:
| Schicht | Durchsetzung |
|---|---|
| OMOS-Rollenprompt | Klare Verhaltensregel: nie einreichen |
| OMOS MCP-Allowlist | Submission-MCP gar nicht zuweisen |
| MCP-Server selbst | Submission-Tool verlangt Approval-Token |
| Workflow-State | `submit` nur aus Zustand `user_approved` erlaubt |
| OpenCode-/Sandbox-Policy | Schreib-, Shell- und Browserrechte begrenzen |
| User Interface | Explizite, sichtbare Freigabe vor jeder Außenwirkung |
In unserem Beispiel existiert der Submission-Schritt schlicht nicht: Der Workflow endet bei kompilierten ShareLaTeX-Projekten und Projektlinks für den Nutzer. Die Bewerbung wird vom Menschen selbst abgeschickt. Damit ist die härteste Grenze bereits durch Architektur gezogen nicht durch Prompt-Hoffnung.
Sobald man automatisches Absenden möchte (z. B. über ein Bewerbungsportal), braucht es einen eigenen Submission-MCP mit Approval-Token:
```text
notification-draft → darf Entwurf erzeugen
notification-send → braucht userApprovalId
application-submit → braucht userApprovalId + applicationDraftId
```
Und im Backend:
```mermaid
sequenceDiagram
participant C as Checker
participant N as Notifier MCP
participant U as Nutzer
participant S as Submission MCP
C->>N: createDraftNotification(applicationDraftId)
N-->>U: Stelle gefunden, Entwurf geprüft
U->>U: Prüft PDF und Fakten
U->>S: approve(applicationDraftId)
S-->>S: issue userApprovalId
U->>S: submit(applicationDraftId, userApprovalId)
S-->>U: Bewerbung eingereicht
```
Der Notifier kann dann technisch niemals eine Bewerbung absenden, weil er keinen Submission-MCP besitzt und das Submission-Backend einen vom Nutzer stammenden Freigabenachweis verlangt.
### Das Team-Profil
```yaml
schema: acme.team-profile/v1
id: acme.job-applications
description: Human-in-the-loop-Team zur Recherche, Vorbereitung und Prüfung individueller Bewerbungsunterlagen.
roles:
- id: orchestrator
purpose: Zerlegt Aufgaben, delegiert, integriert Ergebnisse
model_class: strong-generalist
capabilities:
mcps: []
skills: [job-application]
- id: researcher
purpose: Sucht und bewertet Stellenausschreibungen gegen das Kandidatenprofil
omos_agent: librarian
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
omos_agent: custom
model_class: strong-writing
capabilities:
mcps: [openviking, sharelatex]
skills: []
- id: checker
purpose: Prüft Fakten, Ton und Vollständigkeit gegen das Profil
omos_agent: oracle
model_class: high-reasoning
capabilities:
mcps: [openviking]
skills: []
- id: notifier
purpose: Informiert den Nutzer über fertige Entwürfe
omos_agent: custom
model_class: cheap-reliable
capabilities:
mcps: []
skills: []
```
Die lokale Mapping-Tabelle:
```yaml
# ~/.config/apm-team/model-mapping.yaml
model_classes:
strong-generalist: ollama/qwen3.6:35b-a3b-q4_K_M
fast-research: ollama/qwen3.5:9b
strong-writing: ollama/qwen3.6:35b-a3b-q4_K_M
high-reasoning: ollama/qwen3.6:35b-a3b-q4_K_M
cheap-reliable: ollama/gemma4:12b
```
Das generierte OMOS-Preset wurde oben bereits gezeigt. Die Aktivierung läuft dann so:
```bash
apm install tbi/APM-Packages
omos render --harness omos # schreibt .opencode/oh-my-opencode-slim.json
opencode # Start lädt die Projekt-Config
```
Im TUI:
```text
/preset acme-job-applications
# Apply → schreibt Preset-Namen in die Config
# Reload OpenCode → Team ist aktiv
```
Der Nutzer erhält danach keine unsichtbare Bewerbungsmaschine. Er erhält ein explizit aktiviertes, nachvollziehbares Team mit klaren Zuständigkeiten.
## Wie das heute beginnen kann
Die Idee muss nicht auf eine APM-Spec-Änderung warten.
Ein erster Prototyp braucht nur drei Bausteine:
1. **Ein normales APM-Package**
Skills, Instructions und MCP-Abhängigkeiten werden über APM installiert.
2. **Ein Team-Profil als zusätzliche Package-Datei**
Zum Beispiel `team-profile.yaml`, mit versioniertem Schema und eindeutiger ID.
3. **Ein `omos` Sidecar-CLI**
Es liest die installierten Packages, generiert namespacete OMOS-Presets und validiert Konflikte.
APM selbst muss dafür zunächst keine unbekannten Targets akzeptieren. Tatsächlich kennt das aktuelle Manifest nur einen festen Satz dokumentierter Targets; unbekannte Zielnamen führen zu einem Fehler.
Das Sidecar ist daher der pragmatische Anfang. Es kann als externes Tool reifen, Daten über realistische Nutzung liefern und später als offizieller APM-Adapter oder als Erweiterung im APM-Ökosystem landen.
## Der Kern
Die Idee lautet nicht: „APM wird jetzt ein Multi-Agent-Framework."
APM soll sein, was es gut kann: Package-Management für Agent-Kontext.
oh-my-opencode-slim soll sein, was es gut kann: konkrete Rollen, Modelle und Laufzeit-Presets verwalten.
Dazwischen liegt ein kleines, wertvolles Stück Infrastruktur:
> Ein APM-Package beschreibt nicht nur, welche Fähigkeiten installiert werden. Es kann auch erklären, welches Team diese Fähigkeiten sinnvoll verwendet.
Damit wird ein Package vom Ordner voller Skills zu einem reproduzierbaren Arbeitsmodell:
- versionierte Rollen;
- überprüfbare Tool-Zugriffe;
- lokale Modell-Policies;
- mehrere Teams pro Projekt;
- explizite Aktivierung;
- menschliche Freigabe an kritischen Grenzen.
Und das ist wesentlich nützlicher als ein weiterer „autonomer Agent", der nach drei Minuten Browserzugriff beschließt, dass er nun Personalabteilung spielen darf.

581
blog-review.md Normal file
View file

@ -0,0 +1,581 @@
# Blog-Draft Review Source of Truth + Änderungsvorschläge
> **Aufbau dieser Datei:** Teil 1 = aktueller Stand des Ghost-Blogposts (Source of Truth, kopiert von https://blog.corentic.eu/p/fe4a29af-7a76-4e59-8264-016a8ea0c60e/). Teil 2 = meine Änderungsvorschläge basierend auf dem Perplexity-Fact-Check. Wir gehen Vorschlag für Vorschlag durch und entscheiden.
---
# TEIL 1: Aktueller Blogstand (Source of Truth)
# APM, Agent-Teams und das kleine Problem namens Realität
**Wie APM-Packages um Agent Team-Empfehlungen erweitert werden können mit Oh-my-Opencode-slim als erstem konkretem Target.**
*(Bild: SNES-Cartridge „Tour Planer Agent 3000")*
Ein Austauschformat so selbsterklärend wie in die Videospiele der 90er. Einfach Einlegen und Abspielen. Aktuell hat diese Vision mit der Realität des AI-Agenten-Ökosystems wenig zu tun. Nur die Gefahr des Vendor-Lock-Ins bleibt bestehen.
Heute beginnt das Abenteuer zunächst als Textadventure.
> Was willst du tun? (Instruktionen)
>
> Wie soll es erledigt werden? (Skills)
>
> Womit soll es erledigt werden? (Werkzeuge, MCP-Abhängigkeiten)
>
> Wie soll das Agenten-Team zusammengesetzt sein?
>
> Welche Modellkonfiguration sollen die Agenten erhalten? (Modell, Variante, Temperature, … pro Subagent)
>
> Welche Untermenge der Werkzeuge erhalten die jeweiligen Agenten? (MCPs pro Subagent)
>
> Welches Orchestrierungsmuster soll verwendet werden?
Was folgt, ist der klassische *programmer Move*: Man automatisiert (mittlerweile AI-unterstützt) in nur 10 Stunden erfolgreich eine Aufgabe die man händisch in 10 Minuten hätte erledigen können.
Selbstverständlich ist all das praktisch nicht portabel, denn will man seinen mühsam definierten Workflow mit Freunden und Kollegen teilen, dann muss alles passen. Welcher Harness? Welche Modelle? Sind sie verfügbar und falls nein, kann ich sie substituieren? ...
Dies mag zwar der eigenen Arbeitsplatzsicherheit kurzfristig nützen, aber aus technischer Sicht ist der aktuelle Zustand dennoch unbefriedigend.
## Harnesskonfiguration
*(Bild: Homer)*
### Kann das nicht jemand anderes erledigen?
Bevor es Docker gab, war das Ausrollen von Software ein heilloses Durcheinander aus Versionskonflikten, fehlenden Bibliotheken und dem klassischen _„Runs on my Machine"_.
Bevor es apt, yum oder pacman gab, war Software-Installation unter Unix vor allem eins: Handarbeit. Man lud sich den Quellcode als Tarball herunter, entpackte ihn irgendwo, konfigurierte, tippte make und hoffte auf das Beste.
Genau das machen wir heute mit Agenten-Frameworks. Wir kopieren Prompt-Templates per Hand in `.codex/` oder `.opencode/` Ordner, passen JSON-Dateien an und beten, dass das verwendete Modell die System-Prompts versteht.
Ein Package soll mehr sein als ein Ordner voller Prompts. Wenn ich `apm install <package>` ausführe, will ich nicht nur Dateien in `.opencode/` oder `.codex/` kopiert bekommen ich will ein funktionierendes Setup, das weiß, wie es arbeiten soll. Welche Rollen braucht diese Aufgabe? Welcher Agent recherchiert, welcher schreibt, welcher misstraut? Und welches Modell darf welche Rolle übernehmen, ohne dass ich das nach jeder Installation von Hand in einer Config zurechtbasteln muss? Kann ich nicht einfach einen ACP-Client meiner Wahl auf meinen Agenten richten und mich darauf verlassen, dass MCPs, Skills, Dependencies, Orchestrierung usw. mit dem Pull eines Pakets bereits erledigt sind?
```mermaid
graph LR
subgraph Package Registry
A[Skills] --> B[Package]
C[Instructions] --> B
D[MCP-Abhängigkeiten] --> B
end
subgraph Bridge
B -->|deklarative Empfehlung| E[Adapter]
end
subgraph Harness
E --> F[Agenten-Team]
E --> G[Modellkonfiguration]
end
style B fill:#f9f,stroke:#333
style E fill:#bbf,stroke:#333
```
> Ein Package Registry liefert Skills, Instructions, MCP-Abhängigkeiten und eine deklarative Empfehlung für ein Agenten-Team. Ein Harness-spezifischer Adapter übersetzt diese Empfehlung in die lokale Team- und Modellkonfiguration.
## Das Ökosystem heute
Der Agenten-Stack entwickelt sich gerade mit der Geschwindigkeit eines explodierenden Werkzeugschuppens. Jeden Monat erscheint ein neues Manifest, ein neues Plugin-Format oder eine neue Orchestrierungsebene. Alles löst einen legitimen Teil des Problems. Fast nichts passt nahtlos zusammen.
Das ist kein Vorwurf. Es ist der normale Zustand eines jungen Ökosystems. Der interessante Teil beginnt dort, wo die bestehenden Stücke bereits ausreichen, um eine sinnvolle Brücke zu bauen.
Die vorhandenen Formate lassen sich grob in fünf Gruppen einteilen.
| Ebene | Beispiele | Zuständigkeit |
|---|---|---|
| Portable Komponenten | [Agent Plugins](https://agent-plugins.org/) | Skills und MCP-Server verteilen |
| Dependency Management | [APM](https://microsoft.github.io/apm/) | Packages, Lockfile, Policies, Deployment |
| Team-Komposition | [Spawnfile](https://github.com/noopolis/spawnfile), Harness-Configs | Rollen, Delegation, Agentenbeziehungen |
| Runtime-Profile | [oh-my-opencode-slim Presets](https://ohmyopencodeslim.com/community-presets), [Roo Code custom models](https://roocodeinc.github.io/Roo-Code/features/custom-modes/) | Modelle und Optionen je Agent |
| Governance | [Agent Manifest](https://agent-manifest-spec.org/) | Identität, Grenzen, Verantwortung, Auditierbarkeit |
Was fehlt, ist ein allumfassendes YAML-Format.
## Ein paar Worte zu CrewAI
One Standard to rule them all? Ja genau das scheinen sich die Entwickler von [CrewAI](https://crewai.com/) gedacht zu haben und tatsächlich: "CrewAI is rapidly becoming the standard for production-ready agentic automation". So steht es jedenfalls auf ihrer Website wobei über 50.000 GitHub Sterne zeigen, dass das Projekt durchaus über Momentum verfügt.
CrewAI löst die fehlende Standardisierung nicht durch Übersetzung zwischen Harnessen, sondern indem es die komplette Ausführungsschicht selbst besitzt: Agenten, Tasks, Tool-Aufrufe, State, Prozesse und Delegation laufen in der CrewAI-Runtime.
Damit scheidet es für unsere weiteren Überlegungen allerdings aus, denn CrewAI integriert die Welt in eine Runtime und nicht umgekehrt. Unser Ziel ist ein Format zu finden, dessen Pakete sich in die vorhandene Welt integrieren.
### Agent Plugins für portable Komponenten
[Agent Plugins 1.0](https://agent-plugins.org/) definiert ein bewusst kleines, portables Plugin-Format. Im Kern enthält ein Plugin eine `plugin.json` und kann Skills sowie MCP-Server bereitstellen. Erweiterungen sind möglich, müssen aber im `extensions`-Block über einen Reverse-Domain-Namespace isoliert werden.
Das ist sehr gut für Interoperabilität. Ein Skill bleibt ein Skill. Ein MCP bleibt ein MCP. Der Standard versucht nicht, die Laufzeit eines jeden Agentenframeworks zu regieren.
Offen bleiben jedoch Dependencies, getestete Versionen, Teamrollen, Modellprofile und die Aktivierung im konkreten Harness.
### APM Dependency Management
[APM](https://microsoft.github.io/apm/) besetzt die nächste Ebene: Package- und Dependency-Management für Agent-Kontext.
Ein [APM-Manifest](https://microsoft.github.io/apm/reference/manifest-schema/) kann Abhängigkeiten auf andere APM-Packages, MCP-Server und LSP-Server beschreiben. APM verwaltet Auflösung, Lockfile und Installation. Über Targets kompiliert es Primitives in harnessspezifische Dateien. Das native `opencode`-Target installiert beispielsweise Skills, Agents und Commands in die passenden `.opencode/`-Pfade.
```yaml
name: acme/research-assistant
version: 1.0.0
type: hybrid
targets:
- opencode
- omos
dependencies:
apm:
- acme/base-engineering
mcp:
- name: repository-search
registry: false
transport: stdio
command: repository-search-mcp
tools: [search_code, read_file]
```
*Ein APM-Manifest*
APM beantwortet damit: Welche Artefakte gehören zusammen? Welche Versionen wurden zusammen getestet? Welche MCPs werden benötigt? Wohin werden Skills und Instructions installiert?
Was APM aktuell nicht standardisiert beschreibt, ist Team-Semantik: Rollen, Delegation, Modell-Empfehlungen und Aktivierung eines Team-Profils.
### Spawnfile
[Spawnfile](https://github.com/noopolis/spawnfile) adressiert die Teamachse. Agenten und Teams können deklarativ mit Rollen, Zuständigkeiten, Kommunikationswegen und Subagenten beschrieben werden. Die offene Stelle liegt bei Dependencies und Verteilung: Ein Team-Manifest installiert keine versionierten Skills, MCPs und Policies. Gültige Ziele sind momentan ggfdfjfdgv
Spawnfile v0.1 ist ausdrücklich für autonome, langlebige Agent-Runtimes gedacht. OpenCode wird als Runtime gelistet, allerding mit Status exploratory: Kein Adapter, kein generiertes `opencode.json`. Tatsächlich wird in v0.1 für keine Runtime ein natives Team-Artifact compiliert; Teams degradieren immer zu Kontext-Dokumenten in Agent-Workspaces.
Insgesamt ein sehr junges Projekt bei dem sich noch zeigen wird, ob eine separate Teamdeklarationsschicht sich durchsetzen wird.
### Oh My Opencode Slim
## Die wilde Welt der Agent-Harnesses
Wenn es um Multi-Agent-Orchestrierung geht, sieht die aktuelle Landschaft aus wie ein Experimentierfeld nach einem Chemieunfall. Jeder Harness hat seine eigene Vorstellung davon, wie Subagenten funktionieren sollten und keine davon ist mit den anderen kompatibel.
Claude Code kennt Custom Subagents als Markdown-Dateien und experimentiert mit Agententeams, bei denen mehrere Claude-Instanzen über eine gemeinsame Task-List kommunizieren. GitHub Copilot definiert Custom Agents als SDK-Konfiguration mit TypeScript oder Python und bietet Fleet Mode für parallele Ausführung. Codex setzt auf ein Coordinator-Pattern, bei dem ein Hauptagent Tasks an spezialisierte Subagents delegiert. Oh-my-Opencode-slim wiederum arbeitet mit Presets und Custom Agents, die zur Laufzeit umgeschaltet werden können.
Das Ergebnis: Vier verschiedene Formate, vier verschiedene Delegationsmechanismen, vier verschiedene Wege, Modelle und MCPs pro Agent zu konfigurieren. Niemand hat sich auf ein gemeinsames Protokoll geeinigt. Jeder baut sein eigenes Ding.
| Harness | Subagenten-Typ | Team-Unterstützung | Delegation |
|---|---|---|---|
| oh-my-opencode-slim | Custom Agents + Built-in (7 Spezialisten) | ✅ Ja (Hub-and-Spoke Team) | Automatisch (Background) + Explizit (via `@agent`) |
| Claude Code | Custom Subagents (.md-Files) | ✅ Ja (Agent Teams, experimental) | Automatisch + explizit |
| GitHub Copilot | Custom Agents (SDK) | ⚠️ Teilweise (Fleet Mode) | Automatisch via Intent-Matching |
| Codex | Multi-Agent V2 | ⚠️ Teilweise (Coordinator-Pattern) | Explizit via Coordinator |
Genau dafür hat Microsofts [APM das targets-Feld](https://microsoft.github.io/apm/reference/targets-matrix/) vorgesehen. Es ist die ehrliche Antwort auf die Frage: „Wie deployen wir Agent-Kontext in eine Welt, in der jeder Harness seine eigenen Regeln hat?" Die Antwort lautet: Wir definieren Targets für jeden Harness und übersetzen das Package in das jeweilige Format.
Oder, weniger diplomatisch: **Weil sich kein einziger der großen Player die Mühe gemacht hat, mit den anderen zu reden, bevor sie ihr Agenten-System gebaut haben, müssen wir jetzt für jeden von ihnen einen eigenen Übersetzer schreiben**.
Claude Code macht es so. Copilot macht es anders. Codex hat wieder eine eigene Vorstellung. Und der Rest? Macht sein eigenes Ding. Das targets-Feld ist damit ein Eingeständnis: Niemand wird sich einigen. Also bauen wir Brücken zu allen.
Stand August 2026 sieht APM laut Schema folgende Werte als Ziele vor: `copilot`, `claude`, `grok-build`, `cursor`, `opencode`, `codex`, `gemini`, `antigravity`, `windsurf`, `kiro`, `agent-skills`.
Damit klafft hier bereits die erste Lücke, denn **was fehlt, ist ein allumfassendes YAML-Format.**
Wir können also warten bis dieser Standard eines Tages definiert ist, oder wir bauen uns selbst eine kleine Kompositionsschicht. Für Oh-my-Opencode-slim nenne ich diesen Adapter im Folgenden `omos`. `omos` ist dabei ein Design für eine Erweiterung bzw. ein Sidecar-Tool. Es ist heute kein offiziell eingebautes APM-Target.
Hier ist der detaillierte Deep Dive in die Orchestrierungs-Modelle. Wenn man unter die Haube schaut, offenbaren sich fundamentale Unterschiede darin, **wie** die Arbeit verteilt wird, **wer** die Kontrolle behält und **wie** du als Entwickler den Prozess überwachst.
### Vergleich der Multi-Agent-Orchestrierung - Die 4 großen Paradigmen der Orchestrierung
#### 1. Die Agentur: Asynchrone Spezialisten (`oh-my-opencode-slim`)
Dieses Modell funktioniert wie eine echte Entwickler-Agentur. Der Orchestrator ist dein Projektmanager. Wenn du ein Dashboard in einer fremden Codebase anfragst, blockiert er dich nicht mit Ladebalken. Stattdessen schickt er den `Explorer` los, um die Auth-Pattern zu mappen, während der `Librarian` die Dokumentation aus einer Kontextdatenbank lädt. Da das System asynchron läuft, kannst man sich währenddessen weiter mit dem Orchestrator unterhalten. Das `@council` ist hier eine Besonderheit: Es feuert eine Frage parallel an mehrere _unterschiedliche_ LLMs (z. B. GPT-4o, Claude 3.5 und Gemini) und zwingt sie, einen Konsens zu finden.
#### 2. Das Kanban-Board: Task-Pulling (`Claude Code Agent Teams`)
Anthropic nutzt für seine Agent Teams ein "Pull-Modell", das an ein Kanban-Board erinnert. Der Lead-Agent analysiert das Problem und schreibt eine Liste mit Aufgaben (inklusive Abhängigkeiten). Sobald ein Subagent hochfährt, schaut er auf die Liste, markiert eine Aufgabe als "in progress" und arbeitet sie ab. Das ist extrem mächtig für Fleißarbeiten (wie "Schreibe Tests für jedes Modul"), skaliert aber auf UI-Ebene manchmal schlecht, da man schnell den Überblick verliert, welcher Agent gerade in welchem Tmux-Pane festhängt.
#### 3. Der Schwarm: File-basiertes Dispatching (`Copilot /fleet`)
Copilot wählt mit dem `/fleet`-Kommando einen pragmatischen, stark Code-zentrierten Ansatz. Der Orchestrator analysiert nicht zwingend konzeptionelle Rollen, sondern zerlegt die Arbeit primär nach Dateien und Modulen. Wenn du eine API refactorst, feuert der Orchestrator eine "Welle" von Agenten auf die Controller ab, wartet, bis diese fertig sind, und schickt dann die nächste Welle auf die dazugehörigen Test-Dateien. Die Agenten kommunizieren hier nicht untereinander; der Orchestrator baut am Ende alles zusammen.
#### 4. Die Pipeline: Handoff-Orchestrierung (`Codex Multi-Agent V2`)
Dieses Modell funktioniert nicht wie eine wuselige Agentur oder ein paralleler Schwarm, sondern eher wie ein **Staffellauf** oder ein streng getaktetes Fließband. Codex setzt in seinem Multi-Agent V2 Setup stark auf das sogenannte Coordinator-Pattern.
Anstatt Aufgaben gleichzeitig an viele Agenten zu verteilen, orchestriert der Meta-Agent (Coordinator) einen sequenziellen, strikt kontrollierten Workflow.
- **Die Mechanik (Context Passing):** Der Coordinator hält den übergeordneten Status des Tasks. Wenn du ein neues Feature anfragst, aktiviert er Agent A (z. B. für das Schema-Design). Erst wenn Agent A _komplett fertig_ ist, nimmt der Coordinator dessen Ergebnis, packt es isoliert in den neuen Kontext und reicht den Staffelstab an Agent B (z. B. für die API-Routen) weiter.
- **Kontrolle statt Chaos:** Die Agenten arbeiten hier in der Regel nicht asynchron und parallel an denselben oder benachbarten Systemen. Der Handoff (die Übergabe) ist explizit programmiert und wird vom Coordinator überwacht.
- **Zentrale Sichtbarkeit:** Der gesamte Prozess läuft zentralisiert ab. Alles passiert transparent in deinem primären Chat-Stream, was das Debugging im Vergleich zu versteckten Hintergrund-Prozessen oder verteilten Tmux-Panes erheblich erleichtert.
**Der Trade-off:** Was Codex durch dieses Handoff-Pattern an Stabilität, Vorhersagbarkeit und Fehlervermeidung (keine Race-Conditions auf Dateiebene) gewinnt, opfert es bei der reinen Geschwindigkeit. Während `oh-my-opencode-slim` Forschungs- und Programmieraufgaben gleichzeitig erledigen kann, wartet bei Codex der nächste Agent in der Kette zwingend auf den Abschluss seines Vorgängers.
**Vergleichstabelle:**
| Feature | oh-my-opencode-slim | Claude Code (Agent Teams) | GitHub Copilot CLI (/fleet) | Codex (Multi-Agent V2) |
|---|---|---|---|---|
| Architektur-Pattern | **Hub-and-Spoke:** Orchestrator steuert 7 spezialisierte Agenten (Explorer, Fixer, Council etc.) | **Orchestrator-Worker:** Ein Lead-Agent verwaltet dynamisch erzeugte "Teammates" | **Dispatcher-Modell:** Zentraler Orchestrator feuert Sub-Agenten auf Dateisystem-Ebene ab | **Coordinator:** Ein Meta-Agent reicht den Kontext an nachgelagerte Agenten weiter |
| Orchestrierungs-Flow | **Async Push:** Orchestrator plant, delegiert im Hintergrund und bleibt im Main-Chat reaktiv | **Task-Pull:** Orchestrator erstellt eine geteilte Task-Liste; Worker "claimen" freie Tasks | **Synchronous Push:** Orchestrator zerlegt Tasks nach Abhängigkeiten und pusht sie in Wellen an Agenten | **Handoff:** Explizite Übergabe von State und Kontext an den nächsten Agenten |
| Parallelität | **Sehr hoch:** Spezialisten arbeiten parallel (z. B. `Librarian` liest Docs, während `Fixer` Code schreibt) | **Hoch:** Mehrere Worker bearbeiten unabhängige Sub-Tasks aus der Liste | **Hoch:** Stark optimiert für parallele Datei-Operationen im Workspace | **Niedrig:** Meist sequenzielle Abarbeitung oder enge Bündelung |
| Spezialisierung | Fixes Team mit zugewiesenen Rollen und optimalen Modellen (z. B. billiges Modell für Code-Search, teures für Architektur) | Teammates sind primär identische Klone des Orchestrators, die isolierte Kontexte erhalten | Sub-Agenten sind Instanzen des Basis-Modells, spezialisiert durch den zugewiesenen Pfad/Kontext | Custom Agents, die je nach Workspace-Rolle definiert werden |
| Sichtbarkeit / UX | **Multiplexer-Nativ:** Öffnet Agenten live in Tmux, Zellij oder Kitty-Panes. Main-Prompt bleibt frei | **CLI / Tmux:** Worker laufen in eigenen Sessions. Kann bei vielen Sub-Tasks schnell zu "Pane-Cluttering" führen | **CLI-Polling:** Hintergrundausführung mit Fortschrittsanzeige im Terminal ("Welle 1 beendet") | **Zentral:** Alles passiert im primären Chat-Stream |
| Stärken | Komplexe Architektur-Refactorings, parallele Research- & Coding-Tasks | Massive, kleinteilige Fleißarbeiten (z. B. Unit-Tests für 50 Files schreiben) | Schnelles "Suchen & Ersetzen" oder Refactorn über viele Dateien hinweg | Geführte, streng kontrollierte Workflows |
## Die APM-OMOS-Idee
Oh my Opencode slim erweitert den Harness um einen Multi-Agent-Orchestrator
[Oh my Opencode slim](https://ohmyopencodeslim.com/) ist ein schlankes, optimiertes Open-Source-Plugin für OpenCode, das auf Multi-Agenten-Orchestrierung ausgelegt ist und sitzt als Plugin naturgemäß nahe an der Runtime. Sein Preset-System kann je Agent Modell, Temperatur, Variante und providerspezifische Optionen setzen. Presets werden in der Config definiert und über den `/preset`-Befehl ausgewählt.
*(Community-Preset created by alexandru Beispiel-JSON mit orchestrator, oracle, council, librarian, explorer, designer, fixer)*
Das beantwortet die Frage, wie unterschiedliche Rollen unterschiedliche Modellprofile erhalten. Es fehlt die Herkunft: Welches Package empfiehlt dieses Team? Welche Skills und MCPs gehören dazu? Welche Version wurde getestet?
Ein APM-Package bleibt Source of Truth für Skills, Instructions, MCP-Abhängigkeiten, Versionen, Dependency-Closure und Policies. Zusätzlich enthält es ein Team-Profil als Empfehlung für Harness-Adapter.
```yaml
schema: acme.team-profile/v1
id: acme.document-research
description: Team-Profil für Recherche, Erstellung und Qualitätsprüfung quellenbasierter Dokumente.
roles:
- id: orchestrator
purpose: Zerlegt Aufgaben, delegiert, integriert Ergebnisse
model_class: strong-generalist
- id: researcher
purpose: Sammelt und bewertet Quellen
model_class: fast-research
capabilities:
mcps: [websearch, openviking]
skills: [job-matching]
- id: writer
purpose: Erstellt strukturierte Dokumententwürfe
model_class: strong-writing
capabilities:
mcps: [overleaf]
skills: [application-writing]
- id: checker
purpose: Prüft Quellenbezug, Konsistenz und Vollständigkeit
model_class: high-reasoning
capabilities:
mcps: [openviking]
skills: [application-review]
```
### Ein paar Handgriffe verbleiben
Das Profil verwendet Modellklassen wie `high-reasoning` statt konkreter Modell-IDs. Der Nutzer hinterlegt in einer lokalen Mapping-Tabelle, welche Modell-IDs für welche Modellklasse verwendet werden sollen.
```yaml
# ~/.config/apm-team/model-mapping.yaml
model_classes:
strong-generalist: ollama/qwen3.6:35b-a3b-q4_K_M
fast-research: ollama/qwen3.5:9b
strong-writing: ollama/qwen3.6:35b-a3b-q4_K_M
high-reasoning: ollama/qwen3.6:35b-a3b-q4_K_M
```
Diese Tabelle ist bewusst trivial. Sie ist kein intelligentes Routing und keine Policy-Engine. Der Nutzer entscheidet einmal, welches Modell seine Klasse „schnelle Recherche" oder „starkes Reasoning" erfüllt und der Adapter übersetzt nur noch nach.
## Was `omos` macht
`omos` ist der Übersetzer zwischen Team-Profil und oh-my-opencode-slim. Er liest installierte APM-Packages, findet Team-Profile, schlägt Modellklassen gegen die lokale Mapping-Tabelle nach und generiert Presets unter einem entsprechenden Namespace für das Projekt.
`omos` übersetzt deterministisch:
1. Team-Profil lesen (Modellklassen + MCP/Skill-Capabilities).
2. Lokale Mapping-Tabelle laden (Modellklasse → konkrete Modell-ID).
3. OMOS-Preset generieren (konkrete Modell-IDs + MCP/Skill-Allowlists).
4. Validieren: Sind die Modelle im OMOS-Setup verfügbar?
5. Preset in `.opencode/oh-my-opencode-slim.json` schreiben.
## Mehrere Packages, ein Projekt
Jedes Profil erhält eine globale, stabile ID wie `acme.job-applications`, `acme.security-audit` oder `acme.code-review`. `omos` merged Presets anhand dieser IDs. Das aktive Team wird nicht durch die letzte Installation gewählt.
## Beispiel: Autobewerber als Team-Package
Ein Bewerbungsassistent eignet sich gut als Beispiel, weil er alle Ebenen gleichzeitig berührt: Web-Recherche, personenbezogene Daten (das Kandidatenprofil), Dokumentenproduktion in LaTeX, Qualitätsprüfung und Benachrichtigungen. Der Mechanismus bleibt derselbe für Code-Review, Sales Research, Compliance oder Incident Response.
Konkret arbeite ich hier mit einem real existierenden Setup: Das Package `job-application` bringt eine Skill für Stellenrecherche und maßgeschneiderte Bewerbungsunterlagen mit. Es nutzt die beiden MCP-Server für OpenViking und Overleaf.
**OpenViking** als Langzeitgedächtnis für das Kandidatenprofil (Skills, Erfahrungen, persönliche Daten)
**Overleaf** zur Erstellung und Kompilierung der Anschreiben und Lebensläufe
Dazu kommen OpenCodeunds native Tools für Web-Recherche (`websearch`, `webfetch`).
### Rollen und OMOS-Mapping
| Fachliche Rolle | OMOS-Rolle | Zweck |
|---|---|---|
| Koordination | `orchestrator` | Zerlegt Aufgaben, delegiert, integriert |
| Recherche | `librarian` (Alias `researcher`) | Sucht und bewertet Stellen |
| Schreiben | Custom Agent `writer` | Erstellt Anschreiben und Unterlagen |
| Prüfung | `oracle` (Alias `checker`) | Prüft Fakten, Ton, Vollständigkeit |
| Benachrichtigung | Custom Agent `notifier` | Informiert den Nutzer |
### Was OMOS tatsächlich begrenzen kann
OMOS bietet pro Agent zwei brauchbare Kontrollflächen:
1. **MCP-Zugriff** (`mcps`-Array)
2. **Skill-Zugriff** (`skills`-Array)
Beides kann je Agent in einem Preset als Allowlist formuliert werden. `[]` bedeutet keine Freigabe; bei Konflikten gewinnt die Verweigerung.
Das generierte OMOS-Preset für unser Beispiel sieht dann so aus:
```jsonc
{
"$schema": "https://unpkg.com/oh-my-opencode-slim@latest/oh-my-opencode-slim.schema.json",
"presets": {
"acme-job-applications": {
"orchestrator": {
"model": "ollama/qwen3.6:35b-a3b-q4_K_M",
"mcps": [],
"skills": ["job-application"]
},
"librarian": {
"displayName": "researcher",
"model": "ollama/qwen3.5:9b",
"mcps": ["websearch", "openviking"],
"skills": ["job-application"]
},
"writer": {
"model": "ollama/qwen3.6:35b-a3b-q4_K_M",
"mcps": ["openviking", "sharelatex"],
"skills": []
},
"oracle": {
"displayName": "checker",
"model": "ollama/qwen3.6:35b-a3b-q4_K_M",
"variant": "thinking",
"mcps": ["openviking"],
"skills": []
},
"notifier": {
"model": "ollama/gemma4:12b",
"mcps": [],
"skills": []
}
}
}
}
```
Damit erhält der Researcher keinen ShareLaTeX-Zugang. Der Writer sieht keine Job-Suchtools, darf aber auf Profilfakten zugreifen und in ShareLaTeX arbeiten. Der Checker kann Fakten gegen das Profil prüfen, aber nichts produzieren oder senden. Der Notifier bekommt gar keine externen Zugriffe.
### Die wichtige Einschränkung
`mcps` und `skills` schützen nur den Zugriff auf genau diese zwei Ebenen. Die Aussage „der Agent darf niemals eine Bewerbung absenden" benötigt Verteidigung in mehreren Schichten:
| Schicht | Durchsetzung |
|---|---|
| OMOS-Rollenprompt | Klare Verhaltensregel: nie einreichen |
| OMOS MCP-Allowlist | Submission-MCP gar nicht zuweisen |
| MCP-Server selbst | Submission-Tool verlangt Approval-Token |
| Workflow-State | `submit` nur aus Zustand `user_approved` erlaubt |
| OpenCode-/Sandbox-Policy | Schreib-, Shell- und Browserrechte begrenzen |
| User Interface | Explizite, sichtbare Freigabe vor jeder Außenwirkung |
In unserem Beispiel existiert der Submission-Schritt schlicht nicht: Der Workflow endet bei kompilierten ShareLaTeX-Projekten und Projektlinks für den Nutzer. Die Bewerbung wird vom Menschen selbst abgeschickt. Damit ist die härteste Grenze bereits durch Architektur gezogen nicht durch Prompt-Hoffnung.
Sobald man automatisches Absenden möchte (z. B. über ein Bewerbungsportal), braucht es einen eigenen Submission-MCP mit Approval-Token:
```text
notification-draft → darf Entwurf erzeugen
notification-send → braucht userApprovalId
application-submit → braucht userApprovalId + applicationDraftId
```
Und im Backend:
```mermaid
sequenceDiagram
participant C as Checker
participant N as Notifier MCP
participant U as Nutzer
participant S as Submission MCP
C->>N: createDraftNotification(applicationDraftId)
N-->>U: Stelle gefunden, Entwurf geprüft
U->>U: Prüft PDF und Fakten
U->>S: approve(applicationDraftId)
S-->>S: issue userApprovalId
U->>S: submit(applicationDraftId, userApprovalId)
S-->>U: Bewerbung eingereicht
```
Der Notifier kann dann technisch niemals eine Bewerbung absenden, weil er keinen Submission-MCP besitzt und das Submission-Backend einen vom Nutzer stammenden Freigabenachweis verlangt.
### Das Team-Profil
```yaml
schema: acme.team-profile/v1
id: acme.job-applications
description: Human-in-the-loop-Team zur Recherche, Vorbereitung und Prüfung individueller Bewerbungsunterlagen.
roles:
- id: orchestrator
purpose: Zerlegt Aufgaben, delegiert, integriert Ergebnisse
model_class: strong-generalist
capabilities:
mcps: []
skills: [job-application]
- id: researcher
purpose: Sucht und bewertet Stellenausschreibungen gegen das Kandidatenprofil
omos_agent: librarian
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
omos_agent: custom
model_class: strong-writing
capabilities:
mcps: [openviking, sharelatex]
skills: []
- id: checker
purpose: Prüft Fakten, Ton und Vollständigkeit gegen das Profil
omos_agent: oracle
model_class: high-reasoning
capabilities:
mcps: [openviking]
skills: []
- id: notifier
purpose: Informiert den Nutzer über fertige Entwürfe
omos_agent: custom
model_class: cheap-reliable
capabilities:
mcps: []
skills: []
```
Die lokale Mapping-Tabelle:
```yaml
# ~/.config/apm-team/model-mapping.yaml
model_classes:
strong-generalist: ollama/qwen3.6:35b-a3b-q4_K_M
fast-research: ollama/qwen3.5:9b
strong-writing: ollama/qwen3.6:35b-a3b-q4_K_M
high-reasoning: ollama/qwen3.6:35b-a3b-q4_K_M
cheap-reliable: ollama/gemma4:12b
```
Das generierte OMOS-Preset wurde oben bereits gezeigt. Die Aktivierung läuft dann so:
```bash
apm install tbi/APM-Packages
omos render --harness omos # schreibt .opencode/oh-my-opencode-slim.json
opencode # Start lädt die Projekt-Config
```
Im TUI:
```text
/preset acme-job-applications
# Apply → schreibt Preset-Namen in die Config
# Reload OpenCode → Team ist aktiv
```
Der Nutzer erhält danach keine unsichtbare Bewerbungsmaschine. Er erhält ein explizit aktiviertes, nachvollziehbares Team mit klaren Zuständigkeiten.
## Wie das heute beginnen kann
Die Idee muss nicht auf eine APM-Spec-Änderung warten.
Ein erster Prototyp braucht nur drei Bausteine:
1. **Ein normales APM-Package**
Skills, Instructions und MCP-Abhängigkeiten werden über APM installiert.
2. **Ein Team-Profil als zusätzliche Package-Datei**
Zum Beispiel `team-profile.yaml`, mit versioniertem Schema und eindeutiger ID.
3. **Ein `omos` Sidecar-CLI**
Es liest die installierten Packages, generiert namespacete OMOS-Presets und validiert Konflikte.
APM selbst muss dafür zunächst keine unbekannten Targets akzeptieren. Tatsächlich kennt das aktuelle Manifest nur einen festen Satz dokumentierter Targets; unbekannte Zielnamen führen zu einem Fehler.
Das Sidecar ist daher der pragmatische Anfang. Es kann als externes Tool reifen, Daten über realistische Nutzung liefern und später als offizieller APM-Adapter oder als Erweiterung im APM-Ökosystem landen.
## Der Kern
Die Idee lautet nicht: „APM wird jetzt ein Multi-Agent-Framework."
APM soll sein, was es gut kann: Package-Management für Agent-Kontext.
Oh-my-Opencode-slim soll sein, was es gut kann: konkrete Rollen, Modelle und Laufzeit-Presets verwalten.
Dazwischen liegt ein kleines, wertvolles Stück Infrastruktur:
> Ein APM-Package beschreibt nicht nur, welche Fähigkeiten installiert werden. Es kann auch erklären, welches Team diese Fähigkeiten sinnvoll verwendet.
Damit wird ein Package vom Ordner voller Skills zu einem reproduzierbaren Arbeitsmodell:
- versionierte Rollen;
- überprüfbare Tool-Zugriffe;
- lokale Modell-Policies;
- mehrere Teams pro Projekt;
- explizite Aktivierung;
- menschliche Freigabe an kritischen Grenzen.
Und das ist wesentlich nützlicher als ein weiterer „autonomer Agent", der nach drei Minuten Browserzugriff beschließt, dass er nun Personalabteilung spielen darf.
---
---
# TEIL 2: Änderungsvorschläge (nacheinander durchgehen)
Quelle: Perplexity-Fact-Check vom 23.08.2026 (`https___blog.corentic.eu_ghost_#_editor_post_6a89e.md`) + eigene Review.
## Status-Liste
| # | Vorschlag | Kategorie | Entscheidung |
|---|---|---|---|
| R1 | Placeholder-Rest „ggfdfjfdgv" im Spawnfile-Absatz entfernen/schreiben | Bugfix | ☐ offen |
| R2 | Tippfehler „allerding" → „allerdings" | Bugfix | ☐ offen |
| R3 | Tippfehler „OpenCodeunds" → „OpenCodes" | Bugfix | ☐ offen |
| R4 | ShareLaTeX vs. Overleaf konsistent machen (Text sagt Overleaf, Preset nutzt sharelatex, Repo heißt sharelatex) | Konsistenz | ☐ offen |
| R5 | Leere Sektion „### Oh My Opencode Slim" füllen oder entfernen | Struktur | ☐ offen |
| R6 | APM-Targetliste vervollständigen + versionsabhängig referenzieren | Fact-Check | ☐ offen |
| R7 | LSP-Server-Behauptung in APM-Absatz belegen oder streichen | Fact-Check | ☐ offen |
| R8 | Agent-Plugins-Absatz: primäre Quelle (Spec) verlinken | Fact-Check | ☐ offen |
| R9 | Spawnfile-OpenCode-Formulierung präzisieren („derzeit kein gebündelter Adapter", exploratory) | Fact-Check | ☐ offen |
| R10 | Copilot-/fleet-Absatz: „Wave"-Dispatching und „kommunizieren nicht untereinander" durch dokumentierte Beschreibung ersetzen | Fact-Check | ☐ offen |
| R11 | **Codex-Absatz komplett neu schreiben**: Codex unterstützt parallele Subagenten; Handoff ist mögliches Design, kein Laufzeitmodell | Fact-Check (größte Korrektur) | ☐ offen |
| R12 | Vergleichstabelle Codex-Spalte korrigieren (Parallelität, Sichtbarkeit) | Fact-Check | ☐ offen |
| R13 | Claude Code: „Kanban" als Metapher kennzeichnen | Fact-Check | ☐ offen |
| R14 | Disclaimer vor erstem omos-Codeblock: „vorgeschlagenes Adapterformat, keine Referenzimplementierung" | Fact-Check | ☐ offen |
| R15 | `omos render --harness omos` als Design-Syntax markieren | Fact-Check | ☐ offen |
| R16 | `schema: acme.team-profile/v1` als projektspezifischen Vorschlag kennzeichnen | Fact-Check | ☐ offen |
| R17 | JSON-Block prüfen (gültiges JSON, öffnende Klammern) | Bugfix | ☐ offen |
| R18 | Checker-Aussage einschränken: „nichts produzieren oder senden … über die dort konfigurierten MCPs und Skills hinaus" | Präzision | ☐ offen |
| R19 | `.opencode/oh-my-opencode-slim.json` Pfad verifizieren (projektlokal vs. global) | Verifikation | ☐ offen |
## Detailbeschreibungen
~~### R1 Placeholder-Rest
Im Spawnfile-Absatz steht: „…installiert keine versionierten Skills, MCPs und Policies. Gültige Ziele sind momentan ggfdfjfdgv"
→ Satzfragment löschen. Optional ersetzen durch: „Gültige Ziele sind momentan OpenClaw, PicoClaw und Pi."~~ Done
### R6 APM-Targetliste
~~Aktuell: „copilot, claude, grok-build, cursor, opencode, codex, gemini, antigravity, windsurf, kiro, agent-skills"
→ Empfohlene Fassung: „Die zulässigen Targets sind versionsabhängig. Die aktuelle Schema-Referenz nennt unter anderem `agents`, `copilot`, `claude`, `cursor`, `opencode`, `codex`, `gemini`, `antigravity`, `windsurf`, `kiro`, `vscode` und `all`." + Link auf Manifest-Schema.~~
Done
### R11 Codex-Absatz neu schreiben
Ersatzfassung laut Fact-Check:
> „Codex unterstützt sowohl parallele Subagent-Workflows als auch explizite Handoffs.
> Ein Coordinator-Pattern mit artefaktgebundenen Übergaben ist ein sinnvoller,
> kontrollierter Entwurf für abhängige Arbeitsschritte, aber kein zwingendes Codex-Laufzeitmodell."
Zu streichende Aussagen: „setzt stark auf das Coordinator-Pattern", „nicht asynchron und parallel", „Parallelität: niedrig", „alles passiert transparent im primären Chat-Stream".
### R10 Copilot-/fleet-Absatz
Ersatzfassung:
> „Copilot CLI `/fleet` zerlegt einen Implementierungsplan in unabhängige Teilaufgaben. Ein Orchestrator verwaltet Abhängigkeiten und führt geeignete Subagenten parallel aus; die Koordination nutzt expliziten Todo-Zustand statt implizitem Shared Memory."
Satz „Die Agenten kommunizieren hier nicht untereinander" streichen.
### R14 Disclaimer
Direkt vor dem ersten team-profile-Codeblock (Abschnitt „Die APM-OMOS-Idee"):
> „Die folgenden YAML- und JSON-Beispiele sind ein vorgeschlagenes Adapterformat, keine APM- oder OMOS-Referenzimplementierung."
### R9 Spawnfile-Formulierung
Ersatzfassung:
> „Spawnfile v0.1 besitzt derzeit keinen gebündelten OpenCode-Adapter. OpenCode wird als explorativer Runtime-Kandidat geführt; ein natives OpenCode-Artefakt wird daher aktuell nicht erzeugt."
Zusätzlich (aus unserer Code-Analyse): In v0.1 implementiert kein Adapter `compileTeam()` Teams degradieren überall zu Kontext-Dokumenten.

1
corentic-package-manager/.gitignore vendored Normal file
View file

@ -0,0 +1 @@
__pycache__/

View file

@ -0,0 +1,203 @@
# Corentic Package Manager (CPM)
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.
Translation targets:
| Target harness | Status |
|---|---|
| [oh-my-opencode-slim](https://ohmyopencodeslim.com/) | ✅ implemented |
| oh-my-pi | planned |
| Codex / Claude / Copilot | vision |
Deterministic the CPM does not guess. It assumes no availability and never picks models automatically.
```
CPM package User Runtime
apm.yml + + model-mapping.yaml → cpm render → .opencode/
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)
```
## Prerequisites
- 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`
## Quickstart
```bash
# 1. Create the model mapping interactively, choosing from your opencode models
python3 cpm.py init --fill
# 2. Guided: read team profile, check/fill mapping, render, install primitives
cd <project>
python3 /path/to/cpm.py setup --package /path/to/package
# 3. Activate
cd /path/to/package # the config lives in <package>/.opencode/
opencode
/preset acme-job-applications
# Reload OpenCode -> team is active (by design: no hot-swap)
```
Alternatively manually:
```bash
# 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 # adapt to your own models
# 2. Validate (dry run)
python3 cpm.py check --package examples/package
# 3. Render + install package primitives
cd /path/to/package
python3 /path/to/cpm.py render --package .
apm install # deploys skills/instructions from APM dependencies
```
## The three files
| File | Owned by | Purpose | Edit manually? |
|---|---|---|---|
| `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 |
Plus a provenance file `.opencode/oh-my-opencode-slim.cpm-provenance.json` documenting which role received which model.
## Command reference
```bash
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 validation
```
### `init` create the mapping
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` guided flow
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 then handles package primitives: if the package declares APM dependencies, `setup` offers to run `apm install` for you (or prints the manual command if the `apm` CLI is not installed).
### `render` / `check`
| Option | Default | Meaning |
|---|---|---|
| `--package` | `.` | CPM package directory containing `team-profile.yaml` |
| `--mapping` | `~/.config/cpm/model-mapping.yaml` | Local mapping table |
| `--output` | `<package>/.opencode/oh-my-opencode-slim.json` | Target file |
| `--team-file` | `team-profile.yaml` (alternatively `team.yaml`) | Alternative profile filename |
**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 <name>`.
## Team profile schema (`corentic.team-profile/v1`)
```yaml
schema: corentic.team-profile/v1
id: acme.job-applications # becomes the preset name (namespaced)
description: ...
roles:
- 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 ([] = none)
skills: [job-application] # skill allowlist ([] = none)
```
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 formats
```yaml
model_classes:
fast-research: ollama/qwen3.5:9b # simple
high-reasoning: # extended
model: ollama/qwen3.6:35b-a3b-q4_K_M
variant: thinking
temperature: 0.3
```
All extra fields are passed through 1:1 into the agent entry of the preset.
## What the CPM checks
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
## Examples
| Example | Based on | Team |
|---|---|---|
| `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) |
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
# 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
# Render into the example project
python3 ../cpm.py render --package package \
--mapping model-mapping.yaml \
--output package/.opencode/oh-my-opencode-slim.json
```
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.
Then:
```bash
cd package && opencode
/preset acme-job-applications
```
## Using the team
Rendering + activation only configures the agents. To actually work with the team:
1. **Install primitives.** If the package declares APM dependencies, run `apm install` in the package directory (`cpm setup` offers this automatically). This deploys the skills/instructions that role allowlists reference into `.opencode/skills/` etc.
2. **Reload OpenCode.** Agents (preset) and skills are loaded at startup.
3. **Sanity check.** In OpenCode: `/agents` lists the team members; "ping all agents" verifies they respond.
4. **Work via the orchestrator.** Just state your task in normal language e.g. *"Review the components in src/ against our design standards"*. The orchestrator decomposes it and delegates according to the generated routing prompts: style violations → `@style-checker`, design judgment → `@reviewer`, audits → the auditor agent. It then consolidates the findings.
Two things to know:
- The preset switch (`/preset`) and skill loading both require an OpenCode restart/reload by design.
- Activating a preset writes its name to your **global** user config (`~/.config/opencode/oh-my-opencode-slim.json`), so it persists across sessions and projects until you switch again.
## Troubleshooting
| Problem | Cause/Fix |
|---|---|
| `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 | Start opencode in the directory containing `.opencode/` and reload; presets do not apply mid-session |
| Agent references a skill that does not exist | Run `apm install` in the package directory the preset only configures agents, primitives come from APM |
| 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 |
## Deliberate limits
- 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.

View file

@ -0,0 +1,707 @@
#!/usr/bin/env python3
"""cpm Corentic Package Manager.
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.
Reads a team-profile.yaml, resolves model classes against a local mapping
table and generates a namespaced preset for the target harness.
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)
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
"""
from __future__ import annotations
import argparse
import copy
import json
import re
import shutil
import subprocess
import sys
from pathlib import Path
import yaml
# Built-in agents of the first translation target oh-my-opencode-slim.
# Everything else is treated as a custom agent.
BUILTIN_AGENTS = {
"orchestrator",
"oracle",
"librarian",
"explorer",
"fixer",
"designer",
"council",
"observer",
}
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",)
class CpmError(Exception):
"""Error with a user-readable cause."""
# ---------------------------------------------------------------------------
# Loading
def load_yaml(path: Path) -> dict:
if not path.exists():
raise CpmError(f"File not found: {path}")
try:
data = yaml.safe_load(path.read_text(encoding="utf-8"))
except yaml.YAMLError as exc:
raise CpmError(f"YAML error in {path}: {exc}") from exc
if not isinstance(data, dict):
raise CpmError(f"{path} does not contain a YAML mapping")
return data
def load_team_profile(package_dir: Path, team_file: str | None) -> tuple[dict, Path]:
candidates = (
[package_dir / team_file]
if team_file
else [package_dir / DEFAULT_TEAM_FILE, package_dir / "team.yaml"]
)
for candidate in candidates:
if candidate.exists():
profile = load_yaml(candidate)
schema = profile.get("schema")
if schema and schema not in SUPPORTED_SCHEMA:
print(
f"Warning: Unknown schema '{schema}'. "
f"Supported: {', '.join(SUPPORTED_SCHEMA)}. Continuing."
)
if not profile.get("id"):
raise CpmError(f"{candidate}: missing field 'id'")
roles = profile.get("roles")
if not isinstance(roles, list) or not roles:
raise CpmError(f"{candidate}: no roles defined ('roles')")
return profile, candidate
searched = ", ".join(str(c) for c in candidates)
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 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"
" model: ollama/qwen3.6:35b-a3b-q4_K_M\n"
" variant: thinking"
)
data = load_yaml(path)
classes = data.get("model_classes")
if not isinstance(classes, dict) or not classes:
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:
"""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 CpmError(
f"Role '{role_id}': mapping for '{model_class}' has no 'model' field"
)
return entry
raise CpmError(f"Invalid mapping entry for '{model_class}': {model_class_entry!r}")
def sanitize_preset_name(team_id: str) -> str:
return re.sub(r"[^a-zA-Z0-9_-]+", "-", team_id).strip("-").lower()
# ---------------------------------------------------------------------------
# Translation
def build_preset(profile: dict, mapping: dict) -> tuple[dict, dict, list[str]]:
"""Returns (preset, custom_agents, warnings)."""
preset: dict = {}
custom_agents: dict = {}
warnings: list[str] = []
for role in profile["roles"]:
role_id = role.get("id")
if not role_id:
raise CpmError("Found a role without 'id'")
model_class = role.get("model_class")
if not model_class:
raise CpmError(f"Role '{role_id}': 'model_class' is missing")
if model_class not in mapping:
raise CpmError(
f"Role '{role_id}': model class '{model_class}' is not mapped.\n"
f"Add to your mapping:\n"
f" {model_class}: <provider/model>"
)
agent_fields = resolve_model(mapping[model_class], model_class, role_id)
capabilities = role.get("capabilities", {}) or {}
mcps = list(capabilities.get("mcps", []) or [])
skills = list(capabilities.get("skills", []) or [])
purpose = str(role.get("purpose", "")).strip()
runtime_agent = role.get("runtime_agent", role_id)
if runtime_agent == "custom":
# Explicitly marked as custom agent -> use the role's own name.
agent_key = role_id
else:
agent_key = runtime_agent
preset[agent_key] = {
**agent_fields,
"mcps": mcps,
"skills": skills,
}
if agent_key in BUILTIN_AGENTS and agent_key != role_id:
preset[agent_key]["displayName"] = role_id
if agent_key not in BUILTIN_AGENTS or runtime_agent == "custom":
if agent_key not in custom_agents:
prompt = (
purpose
if purpose
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,
# Tell the orchestrator when to delegate to this agent.
"orchestratorPrompt": (
f"@{agent_key}\n- Role: {purpose}\n"
"- Delegate tasks of this role to this agent."
if purpose
else f"@{agent_key}"
),
}
else:
custom_agents[agent_key]["model"] = agent_fields["model"]
if not mcps and not skills:
warnings.append(
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(
"No 'orchestrator' in the team. Without an orchestrator preset "
"entry its model stays unchanged."
)
return preset, custom_agents, warnings
def merge_into_config(config: dict, preset_name: str, preset: dict,
custom_agents: dict) -> dict:
merged = copy.deepcopy(config)
presets = merged.setdefault("presets", {})
existing = presets.get(preset_name)
if existing:
print(
f"Note: preset '{preset_name}' already existed and will be replaced "
"(other presets remain untouched)."
)
presets[preset_name] = preset
agents = merged.setdefault("agents", {})
agents.update(custom_agents)
return merged
# ---------------------------------------------------------------------------
# MCP availability check (best effort)
def strip_jsonc(text: str) -> str:
text = re.sub(r"/\*.*?\*/", "", text, flags=re.DOTALL)
text = re.sub(r"(^|\s)//[^\n]*", r"\1", text)
return text
OPENCODE_CONFIGS = [
Path.home() / ".config" / "opencode" / "opencode.jsonc",
Path.home() / ".config" / "opencode" / "opencode.json",
]
def configured_mcps() -> set[str]:
for path in OPENCODE_CONFIGS:
if path.exists():
try:
data = json.loads(strip_jsonc(path.read_text(encoding="utf-8")))
return set((data.get("mcp") or {}).keys())
except (json.JSONDecodeError, OSError):
continue
return set()
def available_models() -> list[str]:
"""List all provider/model combinations from opencode.json(c)."""
models: list[str] = []
for path in OPENCODE_CONFIGS:
if not path.exists():
continue
try:
data = json.loads(strip_jsonc(path.read_text(encoding="utf-8")))
except (json.JSONDecodeError, OSError):
continue
for provider, pconf in (data.get("provider") or {}).items():
for model in (pconf.get("models") or {}):
models.append(f"{provider}/{model}")
break
return sorted(models)
INIT_TEMPLATE = """\
# 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
model_classes:
strong-generalist: PLACEHOLDER
fast-research: PLACEHOLDER
strong-writing: PLACEHOLDER
high-reasoning: PLACEHOLDER
cheap-reliable: PLACEHOLDER
"""
def check_mcps(preset: dict, warnings: list[str]) -> None:
available = configured_mcps()
if not available:
warnings.append(
"Could not read opencode.json(c) - MCP check skipped."
)
return
needed: set[str] = set()
for agent in preset.values():
needed.update(agent.get("mcps", []) or [])
missing = sorted(m for m in needed if m not in available)
if missing:
warnings.append(
"MCPs referenced in the team profile but not configured in "
"opencode.json: " + ", ".join(missing)
)
# ---------------------------------------------------------------------------
# Commands
def package_output_path(package_dir: Path) -> Path:
return package_dir / DEFAULT_OUTPUT
def has_apm_dependencies(package_dir: Path) -> bool:
"""True if the package declares an apm.yml with non-empty APM dependencies."""
manifest = package_dir / "apm.yml"
if not manifest.exists():
return False
try:
data = load_yaml(manifest)
except CpmError:
return False
deps = (data.get("dependencies") or {}).get("apm") or []
return bool(deps)
def apm_cli_available() -> bool:
return shutil.which("apm") is not None
def ensure_apm_install(package_dir: Path, dry_run: bool, interactive: bool) -> None:
"""Make sure the user knows about (and ideally ran) 'apm install'.
The rendered preset only configures agents; the skills/MCP primitives
referenced by the team profile are deployed by APM.
"""
if not has_apm_dependencies(package_dir):
return
print(
"\nAPM dependencies detected: this package references skills/instructions "
"from other packages that must be installed before the team can use them."
)
if dry_run:
print(f" Dry run: run 'apm install' in {package_dir} yourself.")
return
if apm_cli_available() and interactive and sys.stdin.isatty():
answer = input("Run 'apm install' now? [y/N] ").strip().lower()
if answer in ("y", "yes"):
print()
result = subprocess.run(["apm", "install"], cwd=package_dir)
if result.returncode == 0:
print("\napm install finished successfully.")
else:
print(
f"\n'apm install' exited with code {result.returncode}. "
f"Fix the issue above and re-run it in {package_dir}.",
file=sys.stderr,
)
return
else:
if not apm_cli_available():
print(
" The 'apm' CLI was not found on PATH.\n"
" Install it via: pip install apm-cli (or: brew install microsoft/apm/apm)"
)
print(f" Run manually:\n cd {package_dir}\n apm install")
def cmd_render(args: argparse.Namespace) -> int:
package_dir = Path(args.package).resolve()
profile, profile_path = load_team_profile(package_dir, args.team_file)
mapping = load_mapping(Path(args.mapping) if args.mapping else None)
preset, custom_agents, warnings = build_preset(profile, mapping)
output = Path(args.output) if args.output else package_dir / DEFAULT_OUTPUT
if output.exists():
try:
config = json.loads(output.read_text(encoding="utf-8"))
except json.JSONDecodeError as exc:
raise CpmError(f"{output} is not valid JSON: {exc}")
else:
config = {}
config["$schema"] = (
"https://unpkg.com/oh-my-opencode-slim@latest/"
"oh-my-opencode-slim.schema.json"
)
merged = merge_into_config(
config, sanitize_preset_name(str(profile["id"])), preset, custom_agents
)
if args.dry_run:
print(json.dumps(merged, indent=2, ensure_ascii=False))
_report(warnings, dry_run=True)
return 0
output.parent.mkdir(parents=True, exist_ok=True)
output.write_text(json.dumps(merged, indent=2, ensure_ascii=False), encoding="utf-8")
print(f"Written: {output}")
provenance = {
"profile": profile["id"],
"profileFile": str(profile_path),
"teamId": profile["id"],
"description": profile.get("description", ""),
"presetName": sanitize_preset_name(str(profile["id"])),
"roles": {
r.get("id"): {
"runtimeAgent": r.get("runtime_agent", r.get("id")),
"modelClass": r.get("model_class"),
"resolvedModel": mapping[r["model_class"]],
"mcps": (r.get("capabilities", {}) or {}).get("mcps", []),
"skills": (r.get("capabilities", {}) or {}).get("skills", []),
}
for r in profile["roles"]
},
}
provenance_path = output.with_suffix(PROVENANCE_SUFFIX)
provenance_path.write_text(
json.dumps(provenance, indent=2, ensure_ascii=False), encoding="utf-8"
)
print(f"Provenance: {provenance_path}")
_report(warnings)
steps = [
"\nNext steps:",
f" 1. start opencode from {output.parent.parent.resolve()} "
"(the config lives in its .opencode/ directory)",
]
if has_apm_dependencies(package_dir):
steps.append(
" 2. run 'apm install' there first - the team references skills "
"from APM dependencies that are not deployed yet"
)
steps.append(
f" 3. /preset {sanitize_preset_name(str(profile['id']))}"
)
steps.append(" 4. reload OpenCode -> team is active")
else:
steps.append(
f" 2. /preset {sanitize_preset_name(str(profile['id']))}"
)
steps.append(" 3. reload OpenCode -> team is active")
print("\n".join(steps))
return 0
def cmd_check(args: argparse.Namespace) -> int:
args.dry_run = True
# 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 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: nothing was written ---")
if warnings:
print("\nWarnings:")
for warning in warnings:
print(f"{warning}")
else:
print("\nNo warnings.")
# ---------------------------------------------------------------------------
# Interactive assistants
_MODELS_LISTED = False
def _choose_model(model_class: str, models: list[str]) -> str:
"""Interactively pick a model for a model class."""
global _MODELS_LISTED
print(f"\nNo model assigned for '{model_class}'.")
if models:
if not _MODELS_LISTED:
print("Available models (from opencode.json(c)):")
for i, model in enumerate(models, 1):
print(f" {i}. {model}")
print(" 0. Enter model ID manually")
_MODELS_LISTED = True
while True:
choice = input(f"Selection for '{model_class}': ").strip()
if choice == "0":
return input("Model ID (provider/model): ").strip()
if choice.isdigit() and 1 <= int(choice) <= len(models):
return models[int(choice) - 1]
print(f"Invalid selection. Enter 1-{len(models)} or 0.")
else:
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 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"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 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:
data["model_classes"][model_class] = _choose_model(model_class, models)
mapping_path.write_text(
yaml.safe_dump(data, sort_keys=False, allow_unicode=True),
encoding="utf-8",
)
remaining = sum(1 for v in data["model_classes"].values()
if v == "PLACEHOLDER")
if remaining:
print(f"\n{remaining} entr(y/ies) left as PLACEHOLDER - "
"please fill in manually.")
print("\nNext steps:")
if mapping_path == DEFAULT_MAPPING_PATH:
print(" Mapping is in place. Validate or render a package, e.g.:")
print(" cpm.py check --package ./my-cpm-package")
print(" cpm.py setup --package ./my-cpm-package")
else:
print(f" cpm.py check --package <your-package-dir> --mapping {mapping_path}")
return 0
def cmd_setup(args: argparse.Namespace) -> int:
"""Guided flow: read profile -> fill mapping -> render."""
package_dir = Path(args.package).resolve()
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 profile: {profile['id']}")
if profile.get("description"):
print(f" {str(profile['description']).strip()}")
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 'none'}; "
f"Skills: {', '.join(skills) or 'none'})")
if not mapping_path.exists():
print(f"\nMapping missing - creating: {mapping_path}")
args_init = argparse.Namespace(mapping=str(mapping_path), fill=True)
cmd_init(args_init)
classes = load_mapping(mapping_path)
needed = sorted({r.get("model_class") for r in profile["roles"]
if r.get("model_class")} - set(classes))
models = available_models()
changed = False
for model_class in needed:
chosen = _choose_model(model_class, models)
classes[model_class] = {"model": chosen, "# selected_by": "user"}
changed = True
if changed:
mapping_path.write_text(
yaml.safe_dump(classes, sort_keys=False, allow_unicode=True),
encoding="utf-8",
)
print(f"\nMapping updated: {mapping_path}")
print("\nRendering ...")
args_render = argparse.Namespace(
package=str(package_dir),
team_file=args.team_file,
mapping=str(mapping_path),
output=args.output,
dry_run=args.dry_run,
)
result = cmd_render(args_render)
# Deploy the package primitives (skills/instructions/MCP configs) that
# the team references. The preset alone only configures the agents.
ensure_apm_install(
package_dir,
dry_run=args.dry_run,
interactive=not args.dry_run,
)
return result
def main(argv: list[str] | None = None) -> int:
parser = argparse.ArgumentParser(
prog="cpm",
description=(
"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)
def common(p: argparse.ArgumentParser) -> None:
p.add_argument(
"--package",
default=".",
help="Path to the CPM package directory (containing team-profile.yaml)",
)
p.add_argument(
"--team-file",
help="Alternative filename of the team profile (default: team-profile.yaml)",
)
p.add_argument(
"--mapping",
help=f"Path to the model mapping file (default: {DEFAULT_MAPPING_PATH})",
)
p_render = sub.add_parser("render", help="Generate and write the preset")
common(p_render)
p_render.add_argument(
"--output",
help=f"Target file (default: {DEFAULT_OUTPUT} relative to the package)",
)
p_render.add_argument(
"--dry-run",
action="store_true",
help="Print only, write nothing",
)
p_render.set_defaults(func=cmd_render)
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="Create the model mapping file, optionally filling placeholders",
)
p_init.add_argument(
"--mapping",
help=f"Target path (default: {DEFAULT_MAPPING_PATH})",
)
p_init.add_argument(
"--fill",
action="store_true",
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="Guided: read profile, fill mapping, render",
)
common(p_setup)
p_setup.add_argument(
"--output",
help=f"Target file (default: {DEFAULT_OUTPUT} relative to the package)",
)
p_setup.add_argument(
"--dry-run",
action="store_true",
help="Print only, write nothing",
)
p_setup.set_defaults(func=cmd_setup)
args = parser.parse_args(argv)
try:
return args.func(args)
except CpmError as exc:
print(f"Error: {exc}", file=sys.stderr)
return 1
if __name__ == "__main__":
sys.exit(main())

View file

@ -0,0 +1,2 @@
# Generated artifacts
.opencode/

View file

@ -0,0 +1,20 @@
# 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 based on the official microsoft/apm-sample-package:
reviewer, style checker and accessibility auditor under one orchestrator.
author: Tobias Endres
license: MIT
type: hybrid
targets:
- opencode
dependencies:
apm:
- microsoft/apm-sample-package#v1.0.0

View file

@ -0,0 +1,54 @@
# 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 for structured design reviews: style-guideline checks,
design review per template and an accessibility audit under a
coordinating reviewer.
roles:
- id: orchestrator
purpose: >-
Takes review requests, distributes style check, design review and
accessibility audit and consolidates the findings into one report.
model_class: strong-generalist
capabilities:
mcps: []
skills: []
- id: reviewer
purpose: >-
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: []
skills: [style-checker]
- id: style-checker
purpose: >-
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: []
skills: [style-checker]
- id: accessibility-auditor
purpose: >-
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: []
skills: []

View file

@ -0,0 +1,2 @@
# Generated artifacts
.opencode/

View file

@ -0,0 +1,28 @@
# Thin CPM wrapper around apm-issue-autopilot from the official microsoft/apm repo.
#
# 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.
#
# 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 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
targets:
- opencode
dependencies:
apm:
- microsoft/apm/packages/apm-issue-autopilot
mcp:
- name: io.github.github/github-mcp-server
transport: http

View file

@ -0,0 +1,65 @@
# 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: >
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: >-
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]
skills: []
- id: triager
purpose: >-
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]
skills: []
- id: shepherd
purpose: >-
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]
skills: []
- id: pr-writer
purpose: >-
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]
skills: []
- id: reviewer
purpose: >-
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:
mcps: [github]
skills: []

View file

@ -0,0 +1,19 @@
# ~/.config/cpm/model-mapping.yaml
#
# 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.
#
# Simple form: class: provider/model-id
# Extended form: class: { model: ..., variant: ..., temperature: ... }
model_classes:
strong-generalist: ollama/qwen3.6:35b-a3b-q4_K_M
fast-research: ollama/qwen3.5:9b
strong-writing:
model: ollama/qwen3.6:35b-a3b-q4_K_M
temperature: 0.7
high-reasoning:
model: ollama/qwen3.6:35b-a3b-q4_K_M
variant: thinking
cheap-reliable: ollama/gemma4:12b

View file

@ -0,0 +1,112 @@
---
name: job-application
description: Activate when the user wants to find jobs and create tailored applications. Use for Bewerbung, job search, cover letter, application, Stellenuche.
allowed-tools:
- "openviking_search"
- "openviking_recall"
- "openviking_read"
- "websearch"
- "webfetch"
- "sharelatex_*"
- "Read"
- "Write"
---
# Job Application Skill
Find matching positions and create tailored applications (Anschreiben + Lebenslauf) in ShareLaTeX.
## Reference Project
Template: ShareLaTeX project `6a873cad300b2c1747bdf4c4`
All files from this project are copied verbatim into each new project.
Only two files are rewritten: `variables.tex` and `cover-letter.tex`.
All other files (resume.tex, sections/*, _header.tex, TLCresume.sty) stay UNCHANGED.
## Process
### 1. Profile
Read user profile from OpenViking: skills, experience, projects, personal data.
### 2. Search
Search for jobs via websearch. Use the user's query or infer from profile.
Collect 8-10 candidates with: role, company, location, source URL.
### 3. Prioritize
Present top 4 as table:
| # | Role | Company | Location | Why |
|---|------|---------|----------|-----|
Wait for user selection before proceeding.
### 4. Create Projects
For each selected job:
1. Create ShareLaTeX project: "Bewerbung {Company} {Role}"
2. Copy ALL files from template (resume.tex, sections/*, _header.tex, TLCresume.sty, variables.tex, cover-letter.tex, README.md)
3. Rewrite `variables.tex` with job-specific values
4. Rewrite `cover-letter.tex` with tailored Anschreiben
5. Do NOT modify any other files
### 5. Cover Letter Rules (DIN 5008)
#### Format
- Language: German (unless job posting is English)
- Length: exactly one A4 page
- Font: 12pt, matching the template (already set in LaTeX)
- Structure: DIN 5008 business letter format
#### Variables to set (in variables.tex)
- `\Company`: full company name (e.g. "R+V Allgemeine Versicherung AG")
- `\Position`: exact job title from posting
- `\RefNumber`: job reference number if available
- `\Subject`: "Bewerbung als {Position}" (bold in LaTeX)
- `\Salutation`: "Sehr geehrte/r Frau/Herr {Name}," if known, else "Sehr geehrte Damen und Herren,"
#### Structure of cover-letter.tex
1. Sender block (already in template)
2. Recipient block
3. Date (right-aligned)
4. Subject line (bold, no "Betreff:" prefix)
5. Salutation
6. Opening (3-5 sentences): state position, where found, one compelling reason why
7. Body (2 paragraphs, ~100 words each):
- Paragraph 1: 2-3 matching qualifications with concrete examples from OpenViking profile
- Paragraph 2: Why this company specifically (researched values, projects, mission)
8. Closing: availability for interview, "Mit freundlichen Grüßen"
9. Anlagen list
#### Do's
- Research company before writing (website, recent news, values)
- Reference specific company projects or values
- Use concrete examples with numbers/metrics
- Match language to job posting (German posting → German letter)
- Use keywords from job posting (ATS optimization)
- Keep sentences short and factual
- End with call to action (interview availability)
#### Don'ts
- Never start with "Hiermit bewerbe ich mich" (used by 90% of applicants)
- Never use "Mit großem Interesse habe ich Ihre Stellenanzeige gelesen"
- Don't repeat CV content verbatim
- Don't use generic phrases like "innovativ", "teamfähig", "motiviert"
- Don't include personal details (age, marital status)
- Don't exceed one page
- Don't use humor or informal language
- Don't copy-paste between applications (recipients notice)
- Don't make claims you can't back up with examples
### 6. Compile
Compile each project. Fix errors. Return project links.
## Output
| Role | Project | Status |
|------|---------|--------|
| ... | link | Created |

View file

@ -0,0 +1,2 @@
# Generierte Artefakte Quelle sind team-profile.yaml + model-mapping.yaml
.opencode/

View file

@ -0,0 +1,21 @@
name: job-application
version: 1.0.0
description: Find matching jobs and create tailored cover letters + CVs in ShareLaTeX
author: Tobias Endres
license: MIT
type: skill
targets:
- generic
- opencode
dependencies:
mcp:
- name: sharelatex
registry: false
transport: sse
url: https://sharelatex-mcp.corentic.eu/sse
- name: openviking
registry: false
transport: sse
url: https://openviking.corentic.eu/mcp
headers:
Authorization: Bearer ${OPENVIKING_API_KEY}

View file

@ -0,0 +1,58 @@
# 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 for researching, preparing and reviewing
individual job application documents.
roles:
- id: orchestrator
purpose: >-
Decomposes tasks, delegates to specialists and integrates results.
model_class: strong-generalist
capabilities:
mcps: []
skills: [job-application]
- id: researcher
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: >-
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: >-
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: >-
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: []
skills: []

View file

@ -0,0 +1,90 @@
---
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, missing model classes.
allowed-tools:
- "Bash(python3 corentic-package-manager/cpm.py *)"
- "Read"
- "Write"
- "Edit"
- "Glob"
---
# CPM Setup Skill
Guides the user through: CPM package (APM manifest + team profile) → local model mapping → rendered harness preset (currently: oh-my-opencode-slim).
## The flow (ResolveValidateRender)
### 1. Find the team profile
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:
```
Profile: acme.job-applications
Roles: orchestrator → strong-generalist, researcher → fast-research,
writer → strong-writing, checker → high-reasoning, notifier → cheap-reliable
MCPs required: openviking, sharelatex (+ websearch/webfetch as OpenCode-native)
```
### 2. Check the mapping
Check `~/.config/cpm/model-mapping.yaml`. For **every** model class used in the profile an entry must exist.
If a model class is missing, offer the user concrete options (list available models from `~/.config/opencode/opencode.jsonc`):
```
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
```
After selection write the mapping and optionally mark `# selected_by: user`.
Alternatively use the interactive commands: `cpm init --fill` and `cpm setup`.
### 3. Check MCPs
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. Render
```bash
python3 corentic-package-manager/cpm.py render --package <package-dir>
```
Show the output including warnings. The file `.opencode/oh-my-opencode-slim.json` is generated never edit it manually.
### 5. Install package primitives
If the package declares an `apm.yml` with APM dependencies, the skills/instructions the roles reference are NOT deployed by cpm APM does that:
```bash
cd <package-dir>
apm install # requires the apm CLI (pip install apm-cli)
```
`cpm setup` offers this step automatically; when guiding manually, always run it before activation if dependencies exist.
### 6. Activate
Guide the user: `cd <package-dir>` (the generated config lives in its `.opencode/` directory!) → start `opencode``/preset <team-id>` → reload. Preset switches and skill loading both take effect only after a reload (by design). Activating writes the preset name to the global user config, so it persists across projects until switched again.
## Using the team
After activation the user works via the orchestrator in normal language ("Review X against our standards"). The orchestrator delegates per the generated routing prompts and consolidates results. Verify first with `/agents` and "ping all agents".
## Error handling
| Error | Fix |
|---|---|
| "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 |
## Rules
- 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.

BIN
homer.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 125 KiB

BIN
incompatible.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 653 KiB

BIN
oh-my-opencode-slim.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 49 KiB

256
omos-deep-dive.md Normal file
View file

@ -0,0 +1,256 @@
# Was oh-my-opencode-slim wirklich kann und was `omos` daraus machen sollte
*Von Modell-IDs bis Sandboxing: Die OMOS-Konfiguration ist mächtiger, als man denkt.*
Im ersten Teil haben wir beschrieben, wie ein APM-Package sein empfohlenes Team deklarieren kann. Dort haben wir `omos` als Übersetzer zwischen `team-profile.yaml` und OMOS-Presets vorgestellt und dabei nur zwei Konfigurationsflächen genutzt: Modell-IDs und MCP/Skill-Allowlists.
Das war bewusst minimal. Aber OMOS kann deutlich mehr. Und `omos` sollte das nutzen.
## Was OMOS alles kann
Die vollständige Konfiguration von oh-my-opencode-slim geht weit über Presets hinaus. Hier die wichtigsten Features, die für Team-Pakete relevant sind:
### Custom Agents mit eigenen Prompts
OMOS kann unter `agents` eigene Subagenten definieren. Jeder braucht ein `model`, einen `prompt` und optional einen `orchestratorPrompt`, der dem Orchestrator sagt, wann er delegieren soll:
```jsonc
{
"agents": {
"writer": {
"model": "ollama/qwen3.6:35b-a3b-q4_K_M",
"prompt": "Erstelle faktenbasierte Bewerbungsunterlagen. "
+ "Nutze nur freigegebene Profilfakten. "
+ "Erfinde keine Erfahrung. "
+ "Lege Entwürfe in ShareLaTeX ab. "
+ "Reiche niemals eine Bewerbung ein.",
"orchestratorPrompt": "@writer\n"
+ "- Rolle: Anschreiben und CV-Anpassungen erstellen\n"
+ "- Delegiere nach qualifiziertem Stellen-Match\n"
+ "- Liefere einen ShareLaTeX-Entwurf und offene Fragen"
}
}
}
```
Der `orchestratorPrompt` ist der Schlüssel: Er wird direkt in den Orchestrator-Prompt injiziert und bestimmt, **wann** der Orchestrator diesen Agenten aufruft. Ohne ihn weiß der Orchestrator nicht, dass es den Agenten gibt.
### Tool-Permissions: Echte Sandboxing
OMOS bietet pro Agent ein `permission`-Objekt, das auf Tool-Ebene durchgesetzt wird nicht nur MCP-Allowlists, sondern auch OpenCode-Core-Tools:
```jsonc
{
"agents": {
"researcher": {
"model": "ollama/qwen3.5:9b",
"permission": {
"edit": "deny",
"bash": "deny",
"task": "deny",
"webfetch": "allow",
"websearch": "allow"
}
}
}
}
```
Das ist eine **echte Sandbox**. Der Researcher darf suchen und lesen, aber keine Dateien bearbeiten, keine Shell-Befehle ausführen und keine Subagenten spawnen. Das geht über `mcps` und `skills` hinaus und ist das, was wir im ersten Blogpost als „mehrere Schichten der Verteidigung" beschrieben haben.
Verfügbare Permission-Keys:
| Key | Was es kontrolliert |
|-----|-------------------|
| `read` | Dateien lesen |
| `edit` | Dateien bearbeiten |
| `bash` | Shell-Befehle ausführen |
| `task` | Subagenten delegieren |
| `webfetch` | Web-Inhalte abrufen |
| `websearch` | Websuche |
| `glob` | Dateisuche nach Pattern |
| `grep` | Inhaltsuche |
| `skill` | Skill-Ausführung |
Jeder Key akzeptiert `"allow"`, `"deny"` oder `"ask"` (fragt den Nutzer). Für `bash` gibt es zusätzlich Pattern-basierte Regeln:
```jsonc
"bash": {
"*": "deny",
"git status*": "allow",
"git diff*": "allow"
}
```
### Fallback-Ketten
OMOS unterstützt Modell-Failover bei 429-Errors (Rate Limiting). In der Mapping-Tabelle könnte man das so abbilden:
```yaml
# ~/.config/apm-team/model-mapping.yaml
model_classes:
fast-research:
primary: ollama/qwen3.5:9b
fallback:
- openai/gpt-4o-mini
```
`omos` würde daraus ein OMOS-Array generieren:
```jsonc
"librarian": {
"model": ["ollama/qwen3.5:9b", "openai/gpt-4o-mini"]
}
```
OMOS versucht das erste Modell und wechselt bei Fehlern automatisch zum nächsten.
### Prompt-Dateien statt Inline-Prompts
Statt lange Prompts inline in die Config zu schreiben, kann man sie als Markdown-Dateien ablegen:
```
.opencode/oh-my-opencode-slim/
├── acme-job-applications/
│ ├── writer.md # Preset-spezifisch
│ └── researcher.md
└── writer.md # Fallback für alle Presets
```
Die Lookup-Reihenfolge ist:
1. `.opencode/oh-my-opencode-slim/<preset>/<agent>.md` (projektspezifisch, preset-spezifisch)
2. `.opencode/oh-my-opencode-slim/<agent>.md` (projektspezifisch, preset-übergreifend)
3. `~/.config/opencode/oh-my-opencode-slim/<preset>/<agent>.md` (global, preset-spezifisch)
4. `~/.config/opencode/oh-my-opencode-slim/<agent>.md` (global, preset-übergreifend)
`omos` könnte die `purpose`-Felder aus dem Team-Profil in solche Dateien schreiben versionierbar im Package, geteilt über Presets.
### Weitere relevante Features
| Feature | OMOS-Feld | Nutzen für Teams |
|---------|-----------|-----------------|
| **Council** | `council.presets` | Mehrere Modelle parallel für kritische Entscheidungen (z.B. Checker) |
| **ACP Agents** | `acpAgents.<name>` | Externe Agent-CLIs (Claude Code, Gemini) als Subagent einbinden |
| **Multiplexer** | `multiplexer.type` | tmux/zellij-Panes für Live-Beobachtung arbeitender Agenten |
| **displayName** | `displayName` | Alias für den Orchestrator (bereits im ersten Blogpost genutzt) |
| **description** | `description` | Beschreibung, die dem Orchestrator bei der Routing-Entscheidung hilft |
| **disabled_agents** | `disabled_agents[]` | Agenten global deaktivieren (z.B. Observer) |
| **Background Jobs** | `backgroundJobs.*` | Orchestrator-Wake, Wall-Timeout für Hintergrundaufgaben |
## Was `omos` daraus machen sollte
Im ersten Blogpost haben wir `omos` als Übersetzer von Modellklassen und MCP-Allowlists beschrieben. Das war Phase 1. Hier ist, was Phase 2 und 3 aussehen:
### Phase 2: Custom Agents + Permissions
`omos` liest das Team-Profil und generiert für jeden Custom Agent (also jeden, der nicht auf einen eingebauten OMOS-Agenten mapped) einen vollständigen Agent-Block:
**Input** (team-profile.yaml):
```yaml
- id: writer
purpose: Erstellt auf Fakten basierende Anschreiben und Lebensläufe in ShareLaTeX.
Nutze nur freigegebene Profilfakten. Erfinde keine Erfahrung.
omos_agent: custom
model_class: strong-writing
capabilities:
mcps: [openviking, sharelatex]
skills: []
permissions:
edit: deny
bash: deny
task: deny
```
**Output** (OMOS-Preset):
```jsonc
"writer": {
"model": "ollama/qwen3.6:35b-a3b-q4_K_M",
"prompt": "Erstellt auf Fakten basierende Anschreiben und Lebensläufe in ShareLaTeX. Nutze nur freigegebene Profilfakten. Erfinde keine Erfahrung.",
"orchestratorPrompt": "@writer\n- Rolle: Anschreiben und CV erstellen\n- Delegiere nach qualifiziertem Stellen-Match",
"mcps": ["openviking", "sharelatex"],
"skills": [],
"permission": {
"edit": "deny",
"bash": "deny",
"task": "deny"
}
}
```
### Phase 3: Prompt-Dateien + Fallback + Council
`omos` schreibt zusätzlich:
- `.opencode/oh-my-opencode-slim/acme-job-applications/writer.md` mit dem vollständigen Prompt
- Fallback-Ketten aus der Mapping-Tabelle
- Optional: Council-Preset für den Checker (zwei Modelle parallel prüfen lassen)
## Die erweiterte Mapping-Tabelle
Für Phase 2 und 3 wird die Mapping-Tabelle reichhaltiger:
```yaml
# ~/.config/apm-team/model-mapping.yaml
model_classes:
strong-generalist:
primary: ollama/qwen3.6:35b-a3b-q4_K_M
fallback:
- openai/gpt-4o-mini
fast-research:
primary: ollama/qwen3.5:9b
fallback:
- openai/gpt-4o-mini
strong-writing:
primary: ollama/qwen3.6:35b-a3b-q4_K_M
high-reasoning:
primary: ollama/qwen3.6:35b-a3b-q4_K_M
variant: thinking
options:
thinking:
type: enabled
budgetTokens: 10000
cheap-reliable:
primary: ollama/gemma4:12b
```
`omos` übersetzt das deterministisch:
- `primary` → erstes Modell im Array
- `fallback` → weitere Einträge im Array
- `variant` → wird direkt übernommen
- `options` → wird direkt übernommen
## Was das für den Blogpost-Workflow bedeutet
Der erweiterte Flow sieht so aus:
```mermaid
flowchart TD
P[APM Package: team-profile.yaml] --> O[omos liest]
M[~/.config/apm-team/model-mapping.yaml] --> O
O -->|Modellklassen| G[Generiere OMOS-Preset]
O -->|purpose-Felder| PR[Generiere Prompt-Dateien]
O -->|permissions| PE[Generiere Permission-Objekte]
O -->|fallback| FB[Generiere Fallback-Ketten]
G --> C[Schreibe .opencode/oh-my-opencode-slim.json]
PR --> C
PE --> C
FB --> C
C --> D[Fertig]
```
## Fazit
OMOS ist nicht nur ein Preset-Switcher. Es ist ein vollständiges Agent-Konfigurationssystem mit Custom Prompts, Tool-Permissions, Fallback-Ketten und Multi-Model-Synthese. `omos` als Adapter sollte diese Möglichkeiten nutzen nicht nur Modell-IDs übersetzen.
Die drei Phasen:
| Phase | Was omos generiert | OMOS-Features |
|-------|-------------------|---------------|
| **1** | Modell-IDs + MCP/Skill-Allowlists | `presets.<name>.<agent>.model`, `mcps`, `skills` |
| **2** | + Custom Prompts + Permissions | `agents.<name>.prompt`, `orchestratorPrompt`, `permission` |
| **3** | + Fallback-Ketten + Prompt-Dateien + Council | `model[]`, `.md`-Dateien, `council.presets` |
Phase 1 ist der Minimalprototyp. Phase 2 macht die Agenten wirklich arbeitsfähig. Phase 3 macht sie resilient.

BIN
skilldependencygraph.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 218 KiB

BIN
snes.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 244 KiB

BIN
standards_2x.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 51 KiB