@divaltor/academy 0.4.3 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -14,7 +14,7 @@ An OpenCode plugin that turns coding agents into a Tracen Academy roster: specia
14
14
 
15
15
  ## Configuration
16
16
 
17
- Add Academy to `opencode.jsonc`. All options are optional:
17
+ Add Academy to `opencode.jsonc`. All options are optional. Omit the version to track `latest`; append `@x.y.z` to pin a release:
18
18
 
19
19
  ```jsonc
20
20
  {
@@ -29,6 +29,9 @@ Add Academy to `opencode.jsonc`. All options are optional:
29
29
  "color": "#8994B8",
30
30
  "model": "opencode/claude-opus-5#max",
31
31
  },
32
+ "cafe": {
33
+ "disabled": true,
34
+ },
32
35
  },
33
36
  "experimental": {
34
37
  "communication": {
@@ -43,7 +46,9 @@ Add Academy to `opencode.jsonc`. All options are optional:
43
46
  }
44
47
  ```
45
48
 
46
- Each agent under `agents` accepts `name`, `color`, and `model`. Colors may be six-digit hex values or OpenCode theme colors. Model references use `provider/model` or `provider/model#variant`.
49
+ Each agent under `agents` accepts `name`, `color`, `model`, and `disabled`. Colors may be six-digit hex values or OpenCode theme colors. Model references use `provider/model` or `provider/model#variant`. Set `disabled` to `true` to hide an agent and prevent new subagent or Academy thread creation while retaining its definition so existing sessions can continue. Disabling Rudolf restores OpenCode's Build agent as the default.
50
+
51
+ Without a version suffix OpenCode resolves `latest` on install; run `opencode plugin update` to pull newer releases.
47
52
 
48
53
  `use_fff` replaces OpenCode's built-in `glob` and `grep` tools globally with [FFF](https://github.com/dmtrKovalenko/fff) for every agent. Enabled by default; set to `false` to keep the built-in tools. This requires a platform supported by `@ff-labs/fff-bun`.
49
54
 
@@ -51,9 +56,9 @@ Each agent under `agents` accepts `name`, `color`, and `model`. Colors may be si
51
56
  | ------ | ----------------------------- |
52
57
  | Rudolf | OpenCode's configured default |
53
58
  | Agnes | `openai/gpt-6-astra#xhigh` |
54
- | Cafe | `openai/gpt-5.6-sol#none` |
55
- | Dantsu | `openai/gpt-5.6-terra#low` |
56
- | Bellno | `openai/gpt-5.6-sol#high` |
59
+ | Cafe | `openai/gpt-6-sol#none` |
60
+ | Dantsu | `openai/gpt-6-luna#high` |
61
+ | Bellno | `openai/gpt-6-sol#high` |
57
62
 
58
63
  > [!WARNING]
59
64
  >
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@divaltor/academy",
3
- "version": "0.4.3",
3
+ "version": "0.5.0",
4
4
  "description": "An OpenCode plugin that trains a Tracen Academy roster of specialist coding agents to research, build, and review.",
5
5
  "homepage": "https://github.com/divaltor/academy#readme",
6
6
  "bugs": {
@@ -29,7 +29,7 @@
29
29
  "fix": "ultracite fix"
30
30
  },
31
31
  "dependencies": {
32
- "@ff-labs/fff-bun": "0.10.6",
32
+ "@ff-labs/fff-bun": "0.11.0",
33
33
  "@opencode/plugin": "2.0.11",
34
34
  "@opencode/schema": "2.0.11",
35
35
  "effect": "4.0.0-rc.112"
@@ -2,7 +2,7 @@ You are **Agnes**, a read-only technical advisor for difficult judgment calls. I
2
2
 
3
3
  # Principles
4
4
 
5
- - Do not make code-specific findings or recommendations about implementation you have not examined. Read the decisive supplied files or diff and relevant callers before advising. If that evidence is unavailable, state the limitation and give only a conditional recommendation.
5
+ - Read the decisive files or diff and relevant callers, validation, and boundary adapters before making code-specific claims or recommendations. Distinguish what the evidence proves from what you infer. If decisive evidence is unavailable, state the limitation and keep the recommendation conditional.
6
6
  - Prefer the simplest viable change; reuse existing code, patterns, and dependencies. Resist hypothetical future needs.
7
7
  - Give one recommended path; mention an alternative only when the trade-off materially matters. Stop when the intent, decisive implementation path, contract to preserve, material risks, and smallest viable recommendation are clear.
8
8
  - For reviews, infer intended behavior and report only high-confidence actionable findings. For debugging, trace the bad value or behavior to where it is first produced. For plans, state the observable behavior or test that would prove the change correct.
@@ -14,4 +14,4 @@ You are **Cafe**, an external research subagent. Return evidence-backed answers
14
14
 
15
15
  # Response
16
16
 
17
- Lead with the answer. Support material claims with linked sources; use versioned documentation anchors and commit-pinned repository links when relevant. Distinguish documented behavior, source-confirmed behavior, observed practice, and inference when the distinction matters. Include code only when it clarifies the requested usage, and language-tag it. Omit generic preambles and raw URLs. Keep reports under roughly 250 words as a flat list without headings.
17
+ Lead with the answer. Support material claims with linked sources; use versioned documentation anchors and commit-pinned repository links when relevant. Distinguish documented behavior, source-confirmed behavior, observed practice, and inference when the distinction matters. Include code only when it clarifies the requested usage, and language-tag it. Omit generic preambles and raw URLs. Default to a flat list of roughly 250 words or fewer; use short sections or additional detail only when needed to answer the question completely.
@@ -14,4 +14,4 @@ Use grep for workspace content, glob for workspace paths, and read to confirm.
14
14
 
15
15
  # Response
16
16
 
17
- Lead with the direct answer, then list absolute path and line evidence and why each location matters. State material scope limits or uncertainty.
17
+ Lead with the direct answer and briefly explain the mechanism or call/data flow when relevant. List absolute paths and line ranges for the decisive definition and only the callers or tests needed to support the answer; say what each establishes. For cited tests, state what their assertions check. Keep the evidence map concise. State material scope limits or uncertainty.
package/src/agents.ts CHANGED
@@ -90,7 +90,7 @@ export const agents: readonly AcademyAgent[] = [
90
90
  "Librarian-style external research for official docs, API behavior, dependency internals, and remote repositories.",
91
91
  id: "cafe",
92
92
  mode: "subagent",
93
- model: "openai/gpt-5.6-sol#none",
93
+ model: "openai/gpt-6-sol#none",
94
94
  name: "Cafe",
95
95
  permissions: [
96
96
  ...defaultPermissions,
@@ -113,7 +113,7 @@ export const agents: readonly AcademyAgent[] = [
113
113
  "Finder-style local codebase discovery by behavior, ownership boundary, and call or data flow.",
114
114
  id: "dantsu",
115
115
  mode: "subagent",
116
- model: "openai/gpt-5.6-terra#low",
116
+ model: "openai/gpt-6-luna#high",
117
117
  name: "Dantsu",
118
118
  permissions: [
119
119
  ...defaultPermissions,
@@ -135,7 +135,7 @@ export const agents: readonly AcademyAgent[] = [
135
135
  "Read-only general reviewer for complete, systematic reviews of diffs, commits, branches, and pull requests.",
136
136
  id: "bellno",
137
137
  mode: "subagent",
138
- model: "openai/gpt-5.6-sol#high",
138
+ model: "openai/gpt-6-sol#high",
139
139
  name: "Bellno",
140
140
  permissions: [
141
141
  ...defaultPermissions,
package/src/index.ts CHANGED
@@ -14,6 +14,7 @@ const parseModel = (reference: string | undefined) =>
14
14
 
15
15
  const AgentOptions = Schema.Struct({
16
16
  color: Schema.optionalKey(Agent.Color),
17
+ disabled: Schema.optionalKey(Schema.Boolean),
17
18
  model: Schema.optionalKey(ModelReference),
18
19
  name: Schema.optionalKey(Schema.NonEmptyString),
19
20
  });
@@ -53,6 +54,12 @@ const setup = Effect.fn("Academy.setup")(function* setup(ctx: Plugin.Context) {
53
54
  const agentID = (id: (typeof agents)[number]["id"]) =>
54
55
  Agent.ID.make(agentOptions[id]?.name ?? id);
55
56
  const configuredAgentIDs = agents.map((agent) => agentID(agent.id));
57
+ const disabledAgentIDs = new Set(
58
+ agents
59
+ .filter((agent) => agentOptions[agent.id]?.disabled === true)
60
+ .map((agent) => agentID(agent.id))
61
+ );
62
+ const rudolfDisabled = disabledAgentIDs.has(agentID("rudolf"));
56
63
  const communication = options.experimental?.communication;
57
64
  const communicationEnabled = communication?.enabled === true;
58
65
 
@@ -85,7 +92,7 @@ const setup = Effect.fn("Academy.setup")(function* setup(ctx: Plugin.Context) {
85
92
  yield* ctx.agent.transform((editor) => {
86
93
  for (const id of ["build", "plan", "explore", "general"]) {
87
94
  editor.update(id, (agent) => {
88
- agent.hidden = true;
95
+ agent.hidden = !(id === "build" && rudolfDisabled);
89
96
  });
90
97
  }
91
98
 
@@ -102,6 +109,7 @@ const setup = Effect.fn("Academy.setup")(function* setup(ctx: Plugin.Context) {
102
109
  );
103
110
  agent.description = definition.description;
104
111
  agent.mode = definition.mode;
112
+ agent.hidden = disabledAgentIDs.has(agentID(definition.id));
105
113
  agent.color =
106
114
  agentOptions[definition.id]?.color ?? agent.color ?? definition.color;
107
115
  const base =
@@ -133,11 +141,36 @@ const setup = Effect.fn("Academy.setup")(function* setup(ctx: Plugin.Context) {
133
141
  });
134
142
  }
135
143
 
144
+ for (const agent of editor.list()) {
145
+ editor.update(agent.id, (editable) => {
146
+ editable.permissions.push(
147
+ ...disabledAgentIDs.values().map((id) => ({
148
+ action: "subagent",
149
+ effect: "deny" as const,
150
+ resource: id,
151
+ }))
152
+ );
153
+ });
154
+ }
155
+
156
+ if (rudolfDisabled) {
157
+ editor.default(
158
+ editor.list().find((agent) => agent.mode === "primary" && !agent.hidden)
159
+ ?.id
160
+ );
161
+ return;
162
+ }
163
+
136
164
  editor.default(agentID("rudolf"));
137
165
  });
138
166
 
139
167
  if (communication !== undefined && communicationEnabled) {
140
- yield* SessionCommunication.register(ctx, communication, agentID("rudolf"));
168
+ yield* SessionCommunication.register(
169
+ ctx,
170
+ communication,
171
+ agentID("rudolf"),
172
+ disabledAgentIDs
173
+ );
141
174
  }
142
175
  });
143
176
 
@@ -206,7 +206,8 @@ export namespace SessionCommunication {
206
206
  function* register(
207
207
  ctx: Plugin.Context,
208
208
  options: Options,
209
- primaryAgent: Agent.ID
209
+ primaryAgent: Agent.ID,
210
+ disabledAgentIDs: ReadonlySet<Agent.ID>
210
211
  ) {
211
212
  const scanRecords = Effect.fn("SessionCommunication.scanThreadRecords")(
212
213
  function* scanThreadRecords(
@@ -340,6 +341,12 @@ export namespace SessionCommunication {
340
341
  "only the primary Academy session can create threads"
341
342
  );
342
343
  }
344
+ if (
345
+ input.agent !== undefined &&
346
+ disabledAgentIDs.has(input.agent)
347
+ ) {
348
+ return failure(`agent ${input.agent} is disabled`);
349
+ }
343
350
 
344
351
  const detached = input.detached ?? false;
345
352
  const depth = yield* threadDepthFor(