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
228 lines
12 KiB
Markdown
228 lines
12 KiB
Markdown
# 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.
|