@divaltor/academy 0.4.6 → 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
@@ -29,6 +29,9 @@ Add Academy to `opencode.jsonc`. All options are optional. Omit the version to t
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,7 @@ Add Academy to `opencode.jsonc`. All options are optional. Omit the version to t
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.
47
50
 
48
51
  Without a version suffix OpenCode resolves `latest` on install; run `opencode plugin update` to pull newer releases.
49
52
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@divaltor/academy",
3
- "version": "0.4.6",
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"
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(