APM-Packages/corentic-package-manager/README.md
Tobias J. Endres 9a3b8d62f3 feat(cpm): agent_ref - compose APM-deployed agents into teams
Empirically verified: oh-my-opencode-slim preset entries keyed to an
APM-deployed native OpenCode agent (.opencode/agents/*.md) reconfigure
it in place - model override works, no duplicate agents are created.

- new optional role field 'agent_ref: <name>': persona (prompt,
  identity) comes from the APM primitive; cpm only resolves model_class
  locally and emits a preset entry under the deployed agent's name
- validation: warns when agent_ref target is missing from
  .opencode/agents/ (apm install not run yet) and when 'purpose' is
  redundantly set on agent_ref roles
- collision warning: inline roles shadowing deployed native agents
  suggest using agent_ref instead
- microsoft-design-review example: reviewer role now uses
  agent_ref: design-reviewer, showcasing both role forms
- README: new section on the relationship to APM agent primitives
- blog-review.md: verified findings as material for a blog addendum
2026-08-23 19:37:21 +02:00

228 lines
12 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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)
- id: reviewer # Form B: bind an APM-deployed agent
agent_ref: design-reviewer # persona comes from .opencode/agents/
model_class: high-reasoning # resolved locally, injected via preset
```
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.
### Relationship to APM agent primitives
APM packages can carry their own agent definitions (`.apm/agents/*.agent.md`, deployed to `.opencode/agents/`). A CPM team profile composes these deployed agents instead of duplicating them:
```yaml
roles:
- id: reviewer
agent_ref: design-reviewer # persona comes from the APM-deployed primitive
model_class: high-reasoning # model class resolved locally, injected via preset
```
With `agent_ref`:
- **Persona** (prompt, identity) comes from the APM primitive `purpose` is ignored. One source of truth.
- **Model** is resolved by the CPM from your local mapping and injected via the preset entry keyed to the agent's name. Verified empirically: oh-my-opencode-slim presets reconfigure APM-deployed native agents (model override works, no duplicate agents are created).
- The team may only **restrict** what the primitive declares, never redefine it same tighten-only philosophy as APM policies.
Roles without `agent_ref` use the inline form (`purpose` + `capabilities`) for packages that ship no agent primitives. `cpm check` warns when an inline role would shadow a deployed native agent consider `agent_ref` instead.
Note: if an APM agent primitive freezes a concrete model you do not have, prefer upstream primitives that omit `model:` (like [microsoft/apm-sample-package](https://github.com/microsoft/apm-sample-package) does) and let the CPM resolve it locally via `model_class`.
### 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 (`agent_ref` → deployed design-reviewer), 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.