@divaltor/academy 0.3.2 → 0.4.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 +15 -11
- package/package.json +1 -1
- package/src/agents/rudolf-communication.txt +6 -0
- package/src/agents/rudolf.txt +0 -7
- package/src/agents.ts +2 -0
- package/src/index.ts +32 -14
package/README.md
CHANGED
|
@@ -23,13 +23,17 @@ Add Academy to `opencode.jsonc`. All options are optional:
|
|
|
23
23
|
{
|
|
24
24
|
"package": "@divaltor/academy",
|
|
25
25
|
"options": {
|
|
26
|
-
"
|
|
27
|
-
"
|
|
28
|
-
|
|
29
|
-
|
|
26
|
+
"agents": {
|
|
27
|
+
"rudolf": {
|
|
28
|
+
"name": "Emperor",
|
|
29
|
+
"color": "#8994B8",
|
|
30
|
+
"model": "anthropic/claude-opus-4-1#max",
|
|
31
|
+
},
|
|
30
32
|
},
|
|
31
|
-
"
|
|
32
|
-
"
|
|
33
|
+
"experimental": {
|
|
34
|
+
"communication": {
|
|
35
|
+
"thread_summary": "opencode-go/glm-5.3-flash",
|
|
36
|
+
},
|
|
33
37
|
},
|
|
34
38
|
"use_fff": true,
|
|
35
39
|
},
|
|
@@ -38,9 +42,9 @@ Add Academy to `opencode.jsonc`. All options are optional:
|
|
|
38
42
|
}
|
|
39
43
|
```
|
|
40
44
|
|
|
41
|
-
Each agent 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`.
|
|
45
|
+
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`.
|
|
42
46
|
|
|
43
|
-
|
|
47
|
+
`use_fff` replaces OpenCode's built-in `glob` and `grep` tools globally with [FFF](https://github.com/d-kimuson/fff.nvim) 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`.
|
|
44
48
|
|
|
45
49
|
| Agent | Default model |
|
|
46
50
|
| ------ | ----------------------------- |
|
|
@@ -54,15 +58,15 @@ Set `use_fff` to `true` to replace OpenCode's built-in `glob` and `grep` tools g
|
|
|
54
58
|
>
|
|
55
59
|
> An agent's name is also its session ID. Renaming it can prevent existing sessions from continuing.
|
|
56
60
|
|
|
57
|
-
Session tools are available under Code Mode's `academy` namespace. Only the root primary session can create threads; omit `agent` for a general coding session.
|
|
61
|
+
Session tools are experimental and off by default; set `experimental.communication` to enable them. They are available under Code Mode's `academy` namespace. Only the root primary session can create threads; omit `agent` for a general coding session.
|
|
58
62
|
|
|
59
63
|
> [!NOTE]
|
|
60
64
|
>
|
|
61
|
-
> `read_thread` with a question summarizes via the `communication.thread_summary` model, or the calling session's model when unset. Models on `opencode-go` cannot use stateless generation (the provider requires session routing headers that only a session request carries), so Academy answers through a dedicated `Academy summary <model>` session via transient `session.generate` — no transcript writes, but the session stays visible in the session list because the plugin API exposes neither archive nor delete for it.
|
|
65
|
+
> `read_thread` with a question summarizes via the `experimental.communication.thread_summary` model, or the calling session's model when unset. Models on `opencode-go` cannot use stateless generation (the provider requires session routing headers that only a session request carries), so Academy answers through a dedicated `Academy summary <model>` session via transient `session.generate` — no transcript writes, but the session stays visible in the session list because the plugin API exposes neither archive nor delete for it.
|
|
62
66
|
|
|
63
67
|
> [!NOTE]
|
|
64
68
|
>
|
|
65
|
-
> `find_thread` currently searches Academy-created sessions only. OpenCode's HTTP client can list persisted sessions, but the plugin context does not yet expose `ctx.session.list`, so Academy cannot safely discover sessions created before the plugin was installed. We are waiting for upstream support rather than reading OpenCode's private SQLite database or connecting back to the server through a separate client.
|
|
69
|
+
> `find_thread` currently searches Academy-created sessions only and is part of the experimental communication tools. OpenCode's HTTP client can list persisted sessions, but the plugin context does not yet expose `ctx.session.list`, so Academy cannot safely discover sessions created before the plugin was installed. We are waiting for upstream support rather than reading OpenCode's private SQLite database or connecting back to the server through a separate client.
|
|
66
70
|
>
|
|
67
71
|
> Tracking: [#43517](https://github.com/anomalyco/opencode/issues/43517), [#44155](https://github.com/anomalyco/opencode/issues/44155), [#43556](https://github.com/anomalyco/opencode/pull/43556), and [#46690](https://github.com/anomalyco/opencode/pull/46690).
|
|
68
72
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@divaltor/academy",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.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": {
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
## Session collaboration
|
|
2
|
+
|
|
3
|
+
- Academy session tools are available through Code Mode's `academy` namespace. Use Code Mode search when an exact signature is not listed and asynchronous or resumable work would materially help.
|
|
4
|
+
- Only Rudolf creates and controls these sessions. Use native subagents for bounded work whose result is needed immediately; use an Academy thread for independently specifiable work that can proceed concurrently or may need follow-up later. Never delegate the same work through both paths.
|
|
5
|
+
- Give each thread a complete prompt and non-overlapping ownership because it does not inherit this conversation. Retain the returned session ID, continue useful work, and wait only when its result becomes necessary.
|
|
6
|
+
- Use `find_thread` to recover prior Academy work, `send_thread_message` for corrections or added context, and `interrupt_thread` only for obsolete or wrong-premise work. Read and verify the result before synthesizing it for the user.
|
package/src/agents/rudolf.txt
CHANGED
|
@@ -29,13 +29,6 @@ You are **Rudolf**, the primary coding agent. Work directly by default and deliv
|
|
|
29
29
|
- Give delegates the goal, relevant context, constraints, non-goals, and required result. Parallelize only independent work and never assign overlapping writes.
|
|
30
30
|
- Verify consequential delegated claims and local fit, inspect delegated changes, and synthesize the user-facing result yourself.
|
|
31
31
|
|
|
32
|
-
## Session collaboration
|
|
33
|
-
|
|
34
|
-
- Academy session tools are available through Code Mode's `academy` namespace. Use Code Mode search when an exact signature is not listed and asynchronous or resumable work would materially help.
|
|
35
|
-
- Only Rudolf creates and controls these sessions. Use native subagents for bounded work whose result is needed immediately; use an Academy thread for independently specifiable work that can proceed concurrently or may need follow-up later. Never delegate the same work through both paths.
|
|
36
|
-
- Give each thread a complete prompt and non-overlapping ownership because it does not inherit this conversation. Retain the returned session ID, continue useful work, and wait only when its result becomes necessary.
|
|
37
|
-
- Use `find_thread` to recover prior Academy work, `send_thread_message` for corrections or added context, and `interrupt_thread` only for obsolete or wrong-premise work. Read and verify the result before synthesizing it for the user.
|
|
38
|
-
|
|
39
32
|
# Communication
|
|
40
33
|
|
|
41
34
|
- Default to roughly ten lines of prose per answer. Add structure only when it carries information.
|
package/src/agents.ts
CHANGED
|
@@ -4,6 +4,8 @@ import cafeSystem from "./agents/cafe.txt" with { type: "text" };
|
|
|
4
4
|
import dantsuSystem from "./agents/dantsu.txt" with { type: "text" };
|
|
5
5
|
import rudolfSystem from "./agents/rudolf.txt" with { type: "text" };
|
|
6
6
|
|
|
7
|
+
export { default as rudolfCommunicationPrompt } from "./agents/rudolf-communication.txt" with { type: "text" };
|
|
8
|
+
|
|
7
9
|
interface Permission {
|
|
8
10
|
readonly action: string;
|
|
9
11
|
readonly resource: string;
|
package/src/index.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Agent, Model, Plugin } from "@opencode/plugin/effect";
|
|
2
2
|
import { Config, Effect, Option, Redacted, Schema } from "effect";
|
|
3
3
|
|
|
4
|
-
import { agents } from "./agents";
|
|
4
|
+
import { agents, rudolfCommunicationPrompt } from "./agents";
|
|
5
5
|
import { FffTools } from "./fff-tools";
|
|
6
6
|
import { SessionCommunication } from "./session-communication";
|
|
7
7
|
|
|
@@ -18,13 +18,21 @@ const AgentOptions = Schema.Struct({
|
|
|
18
18
|
name: Schema.optionalKey(Schema.NonEmptyString),
|
|
19
19
|
});
|
|
20
20
|
|
|
21
|
-
const
|
|
21
|
+
const AgentsOptions = Schema.Struct({
|
|
22
22
|
agnes: Schema.optionalKey(AgentOptions),
|
|
23
23
|
bellno: Schema.optionalKey(AgentOptions),
|
|
24
24
|
cafe: Schema.optionalKey(AgentOptions),
|
|
25
|
-
communication: Schema.optionalKey(SessionCommunication.Options),
|
|
26
25
|
dantsu: Schema.optionalKey(AgentOptions),
|
|
27
26
|
rudolf: Schema.optionalKey(AgentOptions),
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
const ExperimentalOptions = Schema.Struct({
|
|
30
|
+
communication: Schema.optionalKey(SessionCommunication.Options),
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
const AcademyOptions = Schema.Struct({
|
|
34
|
+
agents: Schema.optionalKey(AgentsOptions),
|
|
35
|
+
experimental: Schema.optionalKey(ExperimentalOptions),
|
|
28
36
|
use_fff: Schema.optionalKey(Schema.Boolean),
|
|
29
37
|
});
|
|
30
38
|
|
|
@@ -35,12 +43,17 @@ const setup = Effect.fn("Academy.setup")(function* setup(ctx: Plugin.Context) {
|
|
|
35
43
|
const options = yield* Schema.decodeUnknownEffect(AcademyOptions, {
|
|
36
44
|
onExcessProperty: "error",
|
|
37
45
|
})(ctx.options).pipe(Effect.orDie);
|
|
46
|
+
const agentOptions = options.agents ?? {};
|
|
38
47
|
const agentNames = new Map(
|
|
39
|
-
agents.map((agent) => [
|
|
48
|
+
agents.map((agent) => [
|
|
49
|
+
agent.name,
|
|
50
|
+
agentOptions[agent.id]?.name ?? agent.name,
|
|
51
|
+
])
|
|
40
52
|
);
|
|
41
53
|
const agentID = (id: (typeof agents)[number]["id"]) =>
|
|
42
|
-
Agent.ID.make(
|
|
54
|
+
Agent.ID.make(agentOptions[id]?.name ?? id);
|
|
43
55
|
const configuredAgentIDs = agents.map((agent) => agentID(agent.id));
|
|
56
|
+
const communication = options.experimental?.communication;
|
|
44
57
|
|
|
45
58
|
if (new Set(configuredAgentIDs).size !== configuredAgentIDs.length) {
|
|
46
59
|
return yield* Effect.die(
|
|
@@ -64,7 +77,7 @@ const setup = Effect.fn("Academy.setup")(function* setup(ctx: Plugin.Context) {
|
|
|
64
77
|
});
|
|
65
78
|
});
|
|
66
79
|
|
|
67
|
-
if (options.use_fff) {
|
|
80
|
+
if (options.use_fff ?? true) {
|
|
68
81
|
yield* FffTools.register(ctx);
|
|
69
82
|
}
|
|
70
83
|
|
|
@@ -87,8 +100,15 @@ const setup = Effect.fn("Academy.setup")(function* setup(ctx: Plugin.Context) {
|
|
|
87
100
|
agent.description = definition.description;
|
|
88
101
|
agent.mode = definition.mode;
|
|
89
102
|
agent.color =
|
|
90
|
-
|
|
91
|
-
|
|
103
|
+
agentOptions[definition.id]?.color ?? agent.color ?? definition.color;
|
|
104
|
+
const base =
|
|
105
|
+
definition.id === "rudolf" && communication !== undefined
|
|
106
|
+
? definition.system.replace(
|
|
107
|
+
"# Communication",
|
|
108
|
+
`${rudolfCommunicationPrompt}\n\n# Communication`
|
|
109
|
+
)
|
|
110
|
+
: definition.system;
|
|
111
|
+
agent.system = base.replaceAll(
|
|
92
112
|
/\b(?:Rudolf|Agnes|Cafe|Dantsu|Bellno)\b/gu,
|
|
93
113
|
(name) => agentNames.get(name) ?? name
|
|
94
114
|
);
|
|
@@ -105,7 +125,7 @@ const setup = Effect.fn("Academy.setup")(function* setup(ctx: Plugin.Context) {
|
|
|
105
125
|
};
|
|
106
126
|
});
|
|
107
127
|
agent.model = parseModel(
|
|
108
|
-
|
|
128
|
+
agentOptions[definition.id]?.model ?? definition.model
|
|
109
129
|
);
|
|
110
130
|
});
|
|
111
131
|
}
|
|
@@ -113,11 +133,9 @@ const setup = Effect.fn("Academy.setup")(function* setup(ctx: Plugin.Context) {
|
|
|
113
133
|
editor.default(agentID("rudolf"));
|
|
114
134
|
});
|
|
115
135
|
|
|
116
|
-
|
|
117
|
-
ctx,
|
|
118
|
-
|
|
119
|
-
agentID("rudolf")
|
|
120
|
-
);
|
|
136
|
+
if (communication !== undefined) {
|
|
137
|
+
yield* SessionCommunication.register(ctx, communication, agentID("rudolf"));
|
|
138
|
+
}
|
|
121
139
|
});
|
|
122
140
|
|
|
123
141
|
export default Plugin.define({
|