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

12 KiB
Raw Blame History

Corentic Package Manager (CPM)

A CPM package is an agent package consisting of an 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 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 plugin installed
  • A CPM package with a team-profile.yaml

Quickstart

# 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:

# 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

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)

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:

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 does) and let the CPM resolve it locally via model_class.

Mapping formats

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 acme.design-review Reviewer (agent_ref → deployed design-reviewer), Style-Checker (Fixer), Accessibility-Auditor
examples/microsoft-issue-autopilot/ Official 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.

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:

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.