@agentuity/opencode 0.1.40 → 0.1.42
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 +324 -19
- package/dist/agents/architect.d.ts +4 -0
- package/dist/agents/architect.d.ts.map +1 -0
- package/dist/agents/architect.js +259 -0
- package/dist/agents/architect.js.map +1 -0
- package/dist/agents/builder.d.ts +1 -1
- package/dist/agents/builder.d.ts.map +1 -1
- package/dist/agents/builder.js +44 -1
- package/dist/agents/builder.js.map +1 -1
- package/dist/agents/index.d.ts.map +1 -1
- package/dist/agents/index.js +6 -0
- package/dist/agents/index.js.map +1 -1
- package/dist/agents/lead.d.ts +1 -1
- package/dist/agents/lead.d.ts.map +1 -1
- package/dist/agents/lead.js +185 -22
- package/dist/agents/lead.js.map +1 -1
- package/dist/agents/planner.d.ts +4 -0
- package/dist/agents/planner.d.ts.map +1 -0
- package/dist/agents/planner.js +158 -0
- package/dist/agents/planner.js.map +1 -0
- package/dist/agents/runner.d.ts +4 -0
- package/dist/agents/runner.d.ts.map +1 -0
- package/dist/agents/runner.js +364 -0
- package/dist/agents/runner.js.map +1 -0
- package/dist/agents/types.d.ts +5 -1
- package/dist/agents/types.d.ts.map +1 -1
- package/dist/background/concurrency.d.ts +36 -0
- package/dist/background/concurrency.d.ts.map +1 -0
- package/dist/background/concurrency.js +92 -0
- package/dist/background/concurrency.js.map +1 -0
- package/dist/background/index.d.ts +5 -0
- package/dist/background/index.d.ts.map +1 -0
- package/dist/background/index.js +4 -0
- package/dist/background/index.js.map +1 -0
- package/dist/background/manager.d.ts +54 -0
- package/dist/background/manager.d.ts.map +1 -0
- package/dist/background/manager.js +409 -0
- package/dist/background/manager.js.map +1 -0
- package/dist/background/types.d.ts +47 -0
- package/dist/background/types.d.ts.map +1 -0
- package/dist/background/types.js +2 -0
- package/dist/background/types.js.map +1 -0
- package/dist/config/index.d.ts +2 -0
- package/dist/config/index.d.ts.map +1 -1
- package/dist/config/index.js +2 -0
- package/dist/config/index.js.map +1 -1
- package/dist/config/loader.d.ts +24 -0
- package/dist/config/loader.d.ts.map +1 -1
- package/dist/config/loader.js +102 -23
- package/dist/config/loader.js.map +1 -1
- package/dist/config/presets.d.ts +16 -0
- package/dist/config/presets.d.ts.map +1 -0
- package/dist/config/presets.js +20 -0
- package/dist/config/presets.js.map +1 -0
- package/dist/config/validation.d.ts +26 -0
- package/dist/config/validation.d.ts.map +1 -0
- package/dist/config/validation.js +48 -0
- package/dist/config/validation.js.map +1 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/plugin/hooks/cadence.d.ts.map +1 -1
- package/dist/plugin/hooks/cadence.js +3 -1
- package/dist/plugin/hooks/cadence.js.map +1 -1
- package/dist/plugin/hooks/keyword.d.ts.map +1 -1
- package/dist/plugin/hooks/keyword.js +3 -0
- package/dist/plugin/hooks/keyword.js.map +1 -1
- package/dist/plugin/plugin.d.ts.map +1 -1
- package/dist/plugin/plugin.js +335 -36
- package/dist/plugin/plugin.js.map +1 -1
- package/dist/skills/frontmatter.d.ts +7 -0
- package/dist/skills/frontmatter.d.ts.map +1 -0
- package/dist/skills/frontmatter.js +17 -0
- package/dist/skills/frontmatter.js.map +1 -0
- package/dist/skills/index.d.ts +4 -0
- package/dist/skills/index.d.ts.map +1 -0
- package/dist/skills/index.js +4 -0
- package/dist/skills/index.js.map +1 -0
- package/dist/skills/loader.d.ts +20 -0
- package/dist/skills/loader.d.ts.map +1 -0
- package/dist/skills/loader.js +152 -0
- package/dist/skills/loader.js.map +1 -0
- package/dist/skills/types.d.ts +41 -0
- package/dist/skills/types.d.ts.map +1 -0
- package/dist/skills/types.js +2 -0
- package/dist/skills/types.js.map +1 -0
- package/dist/tmux/decision-engine.d.ts +24 -0
- package/dist/tmux/decision-engine.d.ts.map +1 -0
- package/dist/tmux/decision-engine.js +193 -0
- package/dist/tmux/decision-engine.js.map +1 -0
- package/dist/tmux/executor.d.ts +84 -0
- package/dist/tmux/executor.d.ts.map +1 -0
- package/dist/tmux/executor.js +546 -0
- package/dist/tmux/executor.js.map +1 -0
- package/dist/tmux/index.d.ts +7 -0
- package/dist/tmux/index.d.ts.map +1 -0
- package/dist/tmux/index.js +7 -0
- package/dist/tmux/index.js.map +1 -0
- package/dist/tmux/manager.d.ts +116 -0
- package/dist/tmux/manager.d.ts.map +1 -0
- package/dist/tmux/manager.js +488 -0
- package/dist/tmux/manager.js.map +1 -0
- package/dist/tmux/state-query.d.ts +7 -0
- package/dist/tmux/state-query.d.ts.map +1 -0
- package/dist/tmux/state-query.js +70 -0
- package/dist/tmux/state-query.js.map +1 -0
- package/dist/tmux/types.d.ts +97 -0
- package/dist/tmux/types.d.ts.map +1 -0
- package/dist/tmux/types.js +8 -0
- package/dist/tmux/types.js.map +1 -0
- package/dist/tmux/utils.d.ts +32 -0
- package/dist/tmux/utils.d.ts.map +1 -0
- package/dist/tmux/utils.js +80 -0
- package/dist/tmux/utils.js.map +1 -0
- package/dist/tools/background.d.ts +61 -0
- package/dist/tools/background.d.ts.map +1 -0
- package/dist/tools/background.js +78 -0
- package/dist/tools/background.js.map +1 -0
- package/dist/tools/delegate.d.ts +6 -0
- package/dist/tools/delegate.d.ts.map +1 -1
- package/dist/tools/delegate.js +8 -2
- package/dist/tools/delegate.js.map +1 -1
- package/dist/tools/index.d.ts +1 -0
- package/dist/tools/index.d.ts.map +1 -1
- package/dist/tools/index.js +1 -0
- package/dist/tools/index.js.map +1 -1
- package/dist/types.d.ts +118 -18
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +49 -7
- package/dist/types.js.map +1 -1
- package/package.json +4 -3
- package/src/agents/architect.ts +262 -0
- package/src/agents/builder.ts +44 -1
- package/src/agents/index.ts +6 -0
- package/src/agents/lead.ts +185 -22
- package/src/agents/planner.ts +161 -0
- package/src/agents/runner.ts +367 -0
- package/src/agents/types.ts +5 -1
- package/src/background/concurrency.ts +116 -0
- package/src/background/index.ts +4 -0
- package/src/background/manager.ts +478 -0
- package/src/background/types.ts +52 -0
- package/src/config/index.ts +2 -0
- package/src/config/loader.ts +128 -31
- package/src/config/presets.ts +21 -0
- package/src/config/validation.ts +70 -0
- package/src/index.ts +1 -0
- package/src/plugin/hooks/cadence.ts +2 -1
- package/src/plugin/hooks/keyword.ts +3 -0
- package/src/plugin/plugin.ts +374 -42
- package/src/skills/frontmatter.ts +25 -0
- package/src/skills/index.ts +3 -0
- package/src/skills/loader.ts +185 -0
- package/src/skills/types.ts +43 -0
- package/src/tmux/decision-engine.ts +246 -0
- package/src/tmux/executor.ts +618 -0
- package/src/tmux/index.ts +14 -0
- package/src/tmux/manager.ts +577 -0
- package/src/tmux/state-query.ts +77 -0
- package/src/tmux/types.ts +107 -0
- package/src/tmux/utils.ts +85 -0
- package/src/tools/background.ts +145 -0
- package/src/tools/delegate.ts +8 -2
- package/src/tools/index.ts +9 -0
- package/src/types.ts +88 -15
package/src/plugin/plugin.ts
CHANGED
|
@@ -1,24 +1,58 @@
|
|
|
1
1
|
import type { PluginInput, Hooks } from '@opencode-ai/plugin';
|
|
2
|
+
import { tool } from '@opencode-ai/plugin';
|
|
2
3
|
import type { AgentConfig, CommandDefinition } from '../types';
|
|
4
|
+
import { loadAllSkills, type LoadedSkill } from '../skills';
|
|
3
5
|
import { agents } from '../agents';
|
|
4
|
-
import { loadCoderConfig, getDefaultConfig, mergeConfig } from '../config';
|
|
6
|
+
import { loadCoderConfig, getDefaultConfig, mergeConfig, validateAndWarnConfigs } from '../config';
|
|
5
7
|
import { createSessionHooks } from './hooks/session';
|
|
6
8
|
import { createToolHooks } from './hooks/tools';
|
|
7
9
|
import { createKeywordHooks } from './hooks/keyword';
|
|
8
10
|
import { createParamsHooks } from './hooks/params';
|
|
9
11
|
import { createCadenceHooks } from './hooks/cadence';
|
|
10
12
|
import { createSessionMemoryHooks } from './hooks/session-memory';
|
|
11
|
-
import { z } from 'zod';
|
|
12
13
|
import type { AgentRole } from '../types';
|
|
14
|
+
import { BackgroundManager } from '../background';
|
|
15
|
+
import { TmuxSessionManager } from '../tmux';
|
|
16
|
+
|
|
17
|
+
// Sandbox environment detection
|
|
18
|
+
const SANDBOX_ID = process.env.AGENTUITY_SANDBOX_ID;
|
|
19
|
+
const IN_SANDBOX = !!SANDBOX_ID;
|
|
20
|
+
|
|
21
|
+
// Sandbox context injected into Lead, Builder, and Architect prompts
|
|
22
|
+
const SANDBOX_CONTEXT = IN_SANDBOX
|
|
23
|
+
? `
|
|
24
|
+
## Sandbox Environment
|
|
25
|
+
|
|
26
|
+
You are running inside an Agentuity Sandbox (ID: ${SANDBOX_ID}).
|
|
27
|
+
|
|
28
|
+
**Permissions:** All file operations are allowed without prompts.
|
|
29
|
+
|
|
30
|
+
**File Locations:**
|
|
31
|
+
- Working directory: \`/home/agentuity\`
|
|
32
|
+
- Temp files: \`/home/agentuity/tmp/\` (preferred over \`/tmp/\`)
|
|
33
|
+
- Artifacts: \`/home/agentuity/.agentuity/\`
|
|
34
|
+
|
|
35
|
+
**Tips:**
|
|
36
|
+
- No permission prompts - you can read/write freely
|
|
37
|
+
- Sandbox is isolated - safe to experiment
|
|
38
|
+
- Use \`/home/agentuity/\` paths for all file operations
|
|
39
|
+
`
|
|
40
|
+
: '';
|
|
41
|
+
|
|
42
|
+
// Agents that should receive sandbox context in their prompts
|
|
43
|
+
const SANDBOX_AWARE_AGENTS: AgentRole[] = ['lead', 'builder', 'architect'];
|
|
13
44
|
|
|
14
45
|
// Agent display names for @mentions
|
|
15
46
|
const AGENT_MENTIONS: Record<AgentRole, string> = {
|
|
16
47
|
lead: '@Agentuity Coder Lead',
|
|
17
48
|
scout: '@Agentuity Coder Scout',
|
|
18
49
|
builder: '@Agentuity Coder Builder',
|
|
50
|
+
architect: '@Agentuity Coder Architect',
|
|
19
51
|
reviewer: '@Agentuity Coder Reviewer',
|
|
20
52
|
memory: '@Agentuity Coder Memory',
|
|
21
53
|
expert: '@Agentuity Coder Expert',
|
|
54
|
+
planner: '@Agentuity Coder Planner',
|
|
55
|
+
runner: '@Agentuity Coder Runner',
|
|
22
56
|
};
|
|
23
57
|
|
|
24
58
|
export async function createCoderPlugin(ctx: PluginInput): Promise<Hooks> {
|
|
@@ -38,6 +72,30 @@ export async function createCoderPlugin(ctx: PluginInput): Promise<Hooks> {
|
|
|
38
72
|
const keywordHooks = createKeywordHooks(ctx, coderConfig);
|
|
39
73
|
const paramsHooks = createParamsHooks(ctx, coderConfig);
|
|
40
74
|
const cadenceHooks = createCadenceHooks(ctx, coderConfig);
|
|
75
|
+
const tmuxManager = coderConfig.tmux?.enabled
|
|
76
|
+
? new TmuxSessionManager(ctx, coderConfig.tmux, {
|
|
77
|
+
onLog: (message) =>
|
|
78
|
+
ctx.client.app.log({
|
|
79
|
+
body: {
|
|
80
|
+
service: 'agentuity-coder',
|
|
81
|
+
level: 'info',
|
|
82
|
+
message,
|
|
83
|
+
},
|
|
84
|
+
}),
|
|
85
|
+
})
|
|
86
|
+
: undefined;
|
|
87
|
+
const backgroundManager = new BackgroundManager(ctx, coderConfig.background, {
|
|
88
|
+
onSubagentSessionCreated: tmuxManager
|
|
89
|
+
? (event) => {
|
|
90
|
+
void tmuxManager.onSessionCreated(event);
|
|
91
|
+
}
|
|
92
|
+
: undefined,
|
|
93
|
+
onSubagentSessionDeleted: tmuxManager
|
|
94
|
+
? (event) => {
|
|
95
|
+
void tmuxManager.onSessionDeleted(event);
|
|
96
|
+
}
|
|
97
|
+
: undefined,
|
|
98
|
+
});
|
|
41
99
|
|
|
42
100
|
// Session memory hooks handle checkpointing and compaction for non-Cadence sessions
|
|
43
101
|
// Orchestration (deciding which module handles which session) happens below in the hooks
|
|
@@ -45,12 +103,20 @@ export async function createCoderPlugin(ctx: PluginInput): Promise<Hooks> {
|
|
|
45
103
|
|
|
46
104
|
const configHandler = createConfigHandler(coderConfig);
|
|
47
105
|
|
|
48
|
-
//
|
|
49
|
-
const
|
|
50
|
-
|
|
51
|
-
|
|
106
|
+
// Create plugin tools using the @opencode-ai/plugin tool helper
|
|
107
|
+
const tools = createTools(backgroundManager);
|
|
108
|
+
|
|
109
|
+
// Create a logger for shutdown handler
|
|
110
|
+
const shutdownLogger = (message: string) =>
|
|
111
|
+
ctx.client.app.log({
|
|
112
|
+
body: {
|
|
113
|
+
service: 'agentuity-coder',
|
|
114
|
+
level: 'info',
|
|
115
|
+
message: `[shutdown] ${message}`,
|
|
116
|
+
},
|
|
117
|
+
});
|
|
52
118
|
|
|
53
|
-
|
|
119
|
+
registerShutdownHandler(backgroundManager, tmuxManager, shutdownLogger);
|
|
54
120
|
|
|
55
121
|
// Show startup toast (fire and forget, don't block)
|
|
56
122
|
try {
|
|
@@ -73,6 +139,10 @@ export async function createCoderPlugin(ctx: PluginInput): Promise<Hooks> {
|
|
|
73
139
|
'tool.execute.before': toolHooks.before,
|
|
74
140
|
'tool.execute.after': toolHooks.after,
|
|
75
141
|
event: async (input) => {
|
|
142
|
+
const event = extractEventFromInput(input);
|
|
143
|
+
if (event) {
|
|
144
|
+
backgroundManager.handleEvent(event);
|
|
145
|
+
}
|
|
76
146
|
// Orchestrate: route to appropriate module based on session type
|
|
77
147
|
const sessionId = extractSessionIdFromEvent(input);
|
|
78
148
|
if (sessionId && cadenceHooks.isActiveCadenceSession(sessionId)) {
|
|
@@ -101,27 +171,60 @@ function createConfigHandler(
|
|
|
101
171
|
return async (config: Record<string, unknown>) => {
|
|
102
172
|
const agentConfigs = createAgentConfigs(coderConfig);
|
|
103
173
|
const commands = createCommands();
|
|
174
|
+
const loadedSkills = await loadAllSkills(coderConfig.skills);
|
|
175
|
+
const skillCommands = createSkillCommands(loadedSkills);
|
|
176
|
+
|
|
177
|
+
// Merge agent configs: our defaults first, then user's opencode.json overrides on top
|
|
178
|
+
// This allows users to customize any agent via their opencode.json
|
|
179
|
+
const userAgentConfigs = config.agent as Record<string, AgentConfig> | undefined;
|
|
180
|
+
const mergedAgents: Record<string, AgentConfig> = { ...agentConfigs };
|
|
181
|
+
|
|
182
|
+
// Shallow merge user overrides on top of our defaults (nested objects like tools are replaced, not merged)
|
|
183
|
+
if (userAgentConfigs) {
|
|
184
|
+
for (const [name, userConfig] of Object.entries(userAgentConfigs)) {
|
|
185
|
+
if (mergedAgents[name]) {
|
|
186
|
+
// Merge user config on top of our default
|
|
187
|
+
mergedAgents[name] = {
|
|
188
|
+
...mergedAgents[name],
|
|
189
|
+
...userConfig,
|
|
190
|
+
};
|
|
191
|
+
} else {
|
|
192
|
+
// User defined a new agent not in our defaults
|
|
193
|
+
mergedAgents[name] = userConfig;
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
}
|
|
104
197
|
|
|
105
|
-
config.agent =
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
198
|
+
config.agent = mergedAgents;
|
|
199
|
+
|
|
200
|
+
// Validate merged configs and warn about mismatches
|
|
201
|
+
validateAndWarnConfigs(mergedAgents);
|
|
202
|
+
|
|
203
|
+
// In sandbox, allow all permissions without prompts
|
|
204
|
+
if (IN_SANDBOX) {
|
|
205
|
+
config.permission = {
|
|
206
|
+
'*': 'allow',
|
|
207
|
+
external_directory: {
|
|
208
|
+
'/home/agentuity/**': 'allow',
|
|
209
|
+
'*': 'allow',
|
|
210
|
+
},
|
|
211
|
+
};
|
|
212
|
+
}
|
|
109
213
|
|
|
110
214
|
config.command = {
|
|
111
215
|
...(config.command as Record<string, CommandDefinition> | undefined),
|
|
112
216
|
...commands,
|
|
217
|
+
...skillCommands,
|
|
113
218
|
};
|
|
114
219
|
};
|
|
115
220
|
}
|
|
116
221
|
|
|
117
222
|
function createAgentConfigs(
|
|
118
|
-
|
|
223
|
+
_config: ReturnType<typeof getDefaultConfig>
|
|
119
224
|
): Record<string, AgentConfig> {
|
|
120
225
|
const result: Record<string, AgentConfig> = {};
|
|
121
226
|
|
|
122
227
|
for (const agent of Object.values(agents)) {
|
|
123
|
-
const modelConfig = config.agents?.[agent.role];
|
|
124
|
-
|
|
125
228
|
// Convert tools.exclude to Open Code format (tool: false)
|
|
126
229
|
const tools: Record<string, boolean> = {};
|
|
127
230
|
if (agent.tools?.exclude) {
|
|
@@ -130,16 +233,25 @@ function createAgentConfigs(
|
|
|
130
233
|
}
|
|
131
234
|
}
|
|
132
235
|
|
|
236
|
+
// Inject sandbox context into specific agents when running in sandbox
|
|
237
|
+
const shouldInjectSandbox =
|
|
238
|
+
IN_SANDBOX && SANDBOX_AWARE_AGENTS.includes(agent.role as AgentRole);
|
|
239
|
+
const prompt = shouldInjectSandbox
|
|
240
|
+
? `${agent.systemPrompt}\n${SANDBOX_CONTEXT}`
|
|
241
|
+
: agent.systemPrompt;
|
|
242
|
+
|
|
243
|
+
// Use agent defaults directly - user overrides happen in createConfigHandler
|
|
133
244
|
result[agent.displayName] = {
|
|
134
245
|
description: agent.description,
|
|
135
|
-
model:
|
|
136
|
-
prompt
|
|
246
|
+
model: agent.defaultModel,
|
|
247
|
+
prompt,
|
|
137
248
|
mode: agent.mode ?? 'subagent',
|
|
138
249
|
...(Object.keys(tools).length > 0 ? { tools } : {}),
|
|
139
|
-
// Pass through thinking/reasoning settings
|
|
140
250
|
...(agent.variant ? { variant: agent.variant } : {}),
|
|
141
251
|
...(agent.temperature !== undefined ? { temperature: agent.temperature } : {}),
|
|
142
252
|
...(agent.maxSteps !== undefined ? { maxSteps: agent.maxSteps } : {}),
|
|
253
|
+
...(agent.reasoningEffort ? { reasoningEffort: agent.reasoningEffort } : {}),
|
|
254
|
+
...(agent.thinking ? { thinking: agent.thinking } : {}),
|
|
143
255
|
};
|
|
144
256
|
}
|
|
145
257
|
|
|
@@ -158,20 +270,24 @@ You are the Agentuity Coder Lead agent orchestrating the Agentuity Coder team.
|
|
|
158
270
|
## Your Team (use @mentions to invoke)
|
|
159
271
|
- **@Agentuity Coder Scout**: Explore codebase, find patterns, research docs (read-only)
|
|
160
272
|
- **@Agentuity Coder Builder**: Implement features, write code, run tests
|
|
273
|
+
- **@Agentuity Coder Architect**: Complex autonomous tasks, Cadence mode (GPT Codex)
|
|
161
274
|
- **@Agentuity Coder Reviewer**: Review changes, catch issues, apply fixes
|
|
162
275
|
- **@Agentuity Coder Memory**: Store context, remember decisions
|
|
163
276
|
- **@Agentuity Coder Expert**: Agentuity CLI and cloud services specialist
|
|
277
|
+
- **@Agentuity Coder Planner**: Deep planning for complex architecture decisions
|
|
278
|
+
- **@Agentuity Coder Runner**: Run lint/build/test commands, returns structured results
|
|
164
279
|
|
|
165
280
|
## Task
|
|
166
281
|
$ARGUMENTS
|
|
167
282
|
|
|
168
283
|
## Guidelines
|
|
169
284
|
1. Use @Agentuity Coder Scout first to understand context
|
|
170
|
-
2. Delegate implementation to @Agentuity Coder Builder
|
|
171
|
-
3.
|
|
172
|
-
4.
|
|
173
|
-
5.
|
|
174
|
-
6.
|
|
285
|
+
2. Delegate implementation to @Agentuity Coder Builder (or Architect for complex work)
|
|
286
|
+
3. Delegate lint/build/test commands to @Agentuity Coder Runner for structured results
|
|
287
|
+
4. Have @Agentuity Coder Reviewer check the work
|
|
288
|
+
5. Use @Agentuity Coder Expert for Agentuity CLI questions
|
|
289
|
+
6. Only use cloud services when genuinely helpful
|
|
290
|
+
7. **When done, tell @Agentuity Coder Memory to memorialize the session**
|
|
175
291
|
</coder-mode>`,
|
|
176
292
|
agent: 'Agentuity Coder Lead',
|
|
177
293
|
argumentHint: '"task description"',
|
|
@@ -289,10 +405,13 @@ You are the Agentuity Coder Lead in **Cadence mode** — a long-running autonomo
|
|
|
289
405
|
|
|
290
406
|
## Your Team (use @mentions to invoke)
|
|
291
407
|
- **@Agentuity Coder Scout**: Explore codebase, find patterns, research docs (read-only)
|
|
292
|
-
- **@Agentuity Coder
|
|
408
|
+
- **@Agentuity Coder Architect**: Complex autonomous implementation (GPT Codex with high reasoning) — **USE THIS FOR CADENCE**
|
|
409
|
+
- **@Agentuity Coder Builder**: Quick fixes, simple changes (for minor iterations only)
|
|
293
410
|
- **@Agentuity Coder Reviewer**: Review changes, catch issues, apply fixes
|
|
294
411
|
- **@Agentuity Coder Memory**: Store context, remember decisions, checkpoints
|
|
295
412
|
- **@Agentuity Coder Expert**: Agentuity CLI and cloud services specialist
|
|
413
|
+
- **@Agentuity Coder Planner**: Deep planning for complex architecture decisions
|
|
414
|
+
- **@Agentuity Coder Runner**: Run lint/build/test commands, returns structured results
|
|
296
415
|
|
|
297
416
|
## Task
|
|
298
417
|
$ARGUMENTS
|
|
@@ -306,7 +425,8 @@ $ARGUMENTS
|
|
|
306
425
|
2. **Each iteration**:
|
|
307
426
|
- Ask @Agentuity Coder Memory for relevant context
|
|
308
427
|
- Use @Agentuity Coder Scout to understand what's needed
|
|
309
|
-
-
|
|
428
|
+
- For complex planning, consult @Agentuity Coder Planner
|
|
429
|
+
- Delegate implementation to **@Agentuity Coder Architect** (preferred for Cadence)
|
|
310
430
|
- Have @Agentuity Coder Reviewer verify the work
|
|
311
431
|
- Tell @Agentuity Coder Memory to store checkpoint
|
|
312
432
|
|
|
@@ -318,10 +438,11 @@ $ARGUMENTS
|
|
|
318
438
|
4. **Tell @Agentuity Coder Memory to memorialize** the completed session
|
|
319
439
|
|
|
320
440
|
## Guidelines
|
|
321
|
-
- **
|
|
441
|
+
- **Use Architect for implementation** — Architect has GPT Codex with maximum reasoning, ideal for autonomous work
|
|
442
|
+
- Use regular Builder only for trivial fixes within an iteration
|
|
322
443
|
- Ask Memory for context at each iteration start
|
|
323
444
|
- Store checkpoints at each iteration end
|
|
324
|
-
- If stuck
|
|
445
|
+
- If stuck on architecture, consult Planner before trying more approaches
|
|
325
446
|
- Use @Agentuity Coder Expert for sandbox/cloud operations
|
|
326
447
|
- Respect max iterations (50 default)`,
|
|
327
448
|
agent: 'Agentuity Coder Lead',
|
|
@@ -330,40 +451,178 @@ $ARGUMENTS
|
|
|
330
451
|
};
|
|
331
452
|
}
|
|
332
453
|
|
|
333
|
-
function
|
|
334
|
-
const
|
|
454
|
+
function createSkillCommands(skills: LoadedSkill[]): Record<string, CommandDefinition> {
|
|
455
|
+
const commands: Record<string, CommandDefinition> = {};
|
|
456
|
+
|
|
457
|
+
for (const skill of skills) {
|
|
458
|
+
const baseDir = normalizeBaseDir(skill.resolvedPath);
|
|
459
|
+
commands[skill.name] = {
|
|
460
|
+
name: skill.name,
|
|
461
|
+
description: skill.metadata.description,
|
|
462
|
+
template: `<skill-instruction>
|
|
463
|
+
Base directory for this skill: ${baseDir}/
|
|
464
|
+
File references (@path) in this skill are relative to this directory.
|
|
465
|
+
|
|
466
|
+
${skill.content}
|
|
467
|
+
</skill-instruction>
|
|
468
|
+
|
|
469
|
+
<user-request>
|
|
470
|
+
$ARGUMENTS
|
|
471
|
+
</user-request>`,
|
|
472
|
+
...(skill.metadata.agent ? { agent: skill.metadata.agent } : {}),
|
|
473
|
+
...(skill.metadata.model ? { model: skill.metadata.model } : {}),
|
|
474
|
+
...(skill.metadata['argument-hint']
|
|
475
|
+
? { argumentHint: skill.metadata['argument-hint'] }
|
|
476
|
+
: {}),
|
|
477
|
+
...(skill.metadata.subtask ? { subtask: true } : {}),
|
|
478
|
+
};
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
return commands;
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
function normalizeBaseDir(path: string): string {
|
|
485
|
+
return path.replace(/[\\/]+$/, '');
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
function createTools(backgroundManager: BackgroundManager): Hooks['tool'] {
|
|
489
|
+
// Use the schema from @opencode-ai/plugin's tool helper to avoid Zod version mismatches
|
|
490
|
+
const s = tool.schema;
|
|
491
|
+
|
|
492
|
+
const coderDelegate = tool({
|
|
335
493
|
description: `Delegate a task to a specialized Agentuity Coder agent.
|
|
336
494
|
|
|
337
495
|
Use this to:
|
|
338
496
|
- Scout: Explore codebase, find patterns, research documentation
|
|
339
|
-
- Builder: Implement features, write code, run tests
|
|
497
|
+
- Builder: Implement features, write code, run tests (interactive work)
|
|
498
|
+
- Architect: Complex autonomous tasks, Cadence mode, deep reasoning (GPT Codex)
|
|
340
499
|
- Reviewer: Review changes, catch issues, apply fixes
|
|
341
500
|
- Memory: Store context, remember decisions across sessions
|
|
342
|
-
- Expert: Get help with Agentuity CLI and cloud services
|
|
343
|
-
|
|
501
|
+
- Expert: Get help with Agentuity CLI and cloud services
|
|
502
|
+
- Planner: Strategic advisor for complex architecture and deep planning (read-only)`,
|
|
503
|
+
args: {
|
|
344
504
|
agent: s
|
|
345
|
-
.enum([
|
|
505
|
+
.enum([
|
|
506
|
+
'scout',
|
|
507
|
+
'builder',
|
|
508
|
+
'architect',
|
|
509
|
+
'reviewer',
|
|
510
|
+
'memory',
|
|
511
|
+
'expert',
|
|
512
|
+
'planner',
|
|
513
|
+
'runner',
|
|
514
|
+
])
|
|
346
515
|
.describe('Which agent to delegate to'),
|
|
347
516
|
task: s.string().describe('Clear description of the task'),
|
|
348
517
|
context: s.string().optional().describe('Additional context from previous tasks'),
|
|
349
|
-
}
|
|
350
|
-
|
|
351
|
-
const mention = AGENT_MENTIONS[args.agent];
|
|
518
|
+
},
|
|
519
|
+
async execute(args) {
|
|
520
|
+
const mention = AGENT_MENTIONS[args.agent as AgentRole];
|
|
352
521
|
let prompt = `${mention}\n\n## Task\n${args.task}`;
|
|
353
522
|
if (args.context) {
|
|
354
523
|
prompt = `${mention}\n\n## Context\n${args.context}\n\n## Task\n${args.task}`;
|
|
355
524
|
}
|
|
356
|
-
return {
|
|
357
|
-
|
|
358
|
-
|
|
525
|
+
return `To delegate this task, use the Task tool with this prompt:\n\n${prompt}\n\nThe ${args.agent} agent will handle this task.`;
|
|
526
|
+
},
|
|
527
|
+
});
|
|
528
|
+
|
|
529
|
+
const backgroundTask = tool({
|
|
530
|
+
description: `Launch a task to run in the background. Use this for parallel execution of multiple independent tasks.
|
|
531
|
+
|
|
532
|
+
IMPORTANT: Use this tool instead of the 'task' tool when:
|
|
533
|
+
- You need to run multiple agents in parallel
|
|
534
|
+
- Tasks are independent and don't need sequential execution
|
|
535
|
+
- The user asks for "parallel", "background", or "concurrent" work`,
|
|
536
|
+
args: {
|
|
537
|
+
agent: s
|
|
538
|
+
.enum([
|
|
539
|
+
'lead',
|
|
540
|
+
'scout',
|
|
541
|
+
'builder',
|
|
542
|
+
'architect',
|
|
543
|
+
'reviewer',
|
|
544
|
+
'memory',
|
|
545
|
+
'expert',
|
|
546
|
+
'planner',
|
|
547
|
+
'runner',
|
|
548
|
+
])
|
|
549
|
+
.describe('Agent role to run the task'),
|
|
550
|
+
task: s.string().describe('Task prompt to run in the background'),
|
|
551
|
+
description: s.string().optional().describe('Short description of the task'),
|
|
552
|
+
},
|
|
553
|
+
async execute(args, context) {
|
|
554
|
+
const parentSessionId = context.sessionID;
|
|
555
|
+
if (!parentSessionId) {
|
|
556
|
+
return JSON.stringify({
|
|
557
|
+
taskId: 'unknown',
|
|
558
|
+
status: 'error',
|
|
559
|
+
message: 'Missing session context for background task.',
|
|
560
|
+
});
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
const agentName = resolveAgentName(args.agent as AgentRole);
|
|
564
|
+
const bgTask = await backgroundManager.launch({
|
|
565
|
+
description: args.description ?? args.task,
|
|
566
|
+
prompt: args.task,
|
|
567
|
+
agent: agentName,
|
|
568
|
+
parentSessionId,
|
|
569
|
+
parentMessageId: context.messageID,
|
|
570
|
+
});
|
|
571
|
+
return JSON.stringify({
|
|
572
|
+
taskId: bgTask.id,
|
|
573
|
+
status: bgTask.status,
|
|
574
|
+
message:
|
|
575
|
+
bgTask.status === 'error'
|
|
576
|
+
? (bgTask.error ?? 'Failed to launch background task.')
|
|
577
|
+
: 'Background task launched.',
|
|
578
|
+
});
|
|
579
|
+
},
|
|
580
|
+
});
|
|
581
|
+
|
|
582
|
+
const backgroundOutput = tool({
|
|
583
|
+
description: 'Retrieve output for a background task.',
|
|
584
|
+
args: {
|
|
585
|
+
task_id: s.string().describe('Background task ID'),
|
|
359
586
|
},
|
|
360
|
-
|
|
587
|
+
async execute(args) {
|
|
588
|
+
const bgTask = backgroundManager.getTask(args.task_id);
|
|
589
|
+
if (!bgTask) {
|
|
590
|
+
return JSON.stringify({
|
|
591
|
+
taskId: args.task_id,
|
|
592
|
+
status: 'error',
|
|
593
|
+
error: 'Task not found.',
|
|
594
|
+
});
|
|
595
|
+
}
|
|
596
|
+
return JSON.stringify({
|
|
597
|
+
taskId: bgTask.id,
|
|
598
|
+
status: bgTask.status,
|
|
599
|
+
result: bgTask.result,
|
|
600
|
+
error: bgTask.error,
|
|
601
|
+
});
|
|
602
|
+
},
|
|
603
|
+
});
|
|
604
|
+
|
|
605
|
+
const backgroundCancel = tool({
|
|
606
|
+
description: 'Cancel a running background task.',
|
|
607
|
+
args: {
|
|
608
|
+
task_id: s.string().describe('Background task ID'),
|
|
609
|
+
},
|
|
610
|
+
async execute(args) {
|
|
611
|
+
const success = backgroundManager.cancel(args.task_id);
|
|
612
|
+
return JSON.stringify({
|
|
613
|
+
taskId: args.task_id,
|
|
614
|
+
success,
|
|
615
|
+
message: success ? 'Background task cancelled.' : 'Unable to cancel task.',
|
|
616
|
+
});
|
|
617
|
+
},
|
|
618
|
+
});
|
|
361
619
|
|
|
362
|
-
// Type assertion needed because the tool() helper returns unknown
|
|
363
|
-
// but the runtime type is correct (it's created by OpenCode's tool helper)
|
|
364
620
|
return {
|
|
365
621
|
coder_delegate: coderDelegate,
|
|
366
|
-
|
|
622
|
+
background_task: backgroundTask,
|
|
623
|
+
background_output: backgroundOutput,
|
|
624
|
+
background_cancel: backgroundCancel,
|
|
625
|
+
};
|
|
367
626
|
}
|
|
368
627
|
|
|
369
628
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
@@ -381,3 +640,76 @@ function extractSessionIdFromEvent(input: unknown): string | undefined {
|
|
|
381
640
|
(inp.event.properties.sessionID as string | undefined)
|
|
382
641
|
);
|
|
383
642
|
}
|
|
643
|
+
|
|
644
|
+
function resolveAgentName(role: AgentRole): string {
|
|
645
|
+
const agent = agents[role];
|
|
646
|
+
return agent?.displayName ?? role;
|
|
647
|
+
}
|
|
648
|
+
|
|
649
|
+
function extractEventFromInput(
|
|
650
|
+
input: unknown
|
|
651
|
+
): { type: string; properties?: Record<string, unknown> } | undefined {
|
|
652
|
+
if (typeof input !== 'object' || input === null) return undefined;
|
|
653
|
+
const inp = input as { event?: { type?: string; properties?: Record<string, unknown> } };
|
|
654
|
+
if (!inp.event || typeof inp.event.type !== 'string') return undefined;
|
|
655
|
+
return { type: inp.event.type, properties: inp.event.properties };
|
|
656
|
+
}
|
|
657
|
+
|
|
658
|
+
function registerShutdownHandler(
|
|
659
|
+
manager: BackgroundManager,
|
|
660
|
+
tmuxManager?: TmuxSessionManager,
|
|
661
|
+
logger?: (msg: string) => void
|
|
662
|
+
): void {
|
|
663
|
+
if (typeof process === 'undefined') {
|
|
664
|
+
logger?.('[shutdown] process is undefined, cannot register handlers');
|
|
665
|
+
return;
|
|
666
|
+
}
|
|
667
|
+
|
|
668
|
+
const log = logger ?? (() => {});
|
|
669
|
+
let shutdownCalled = false;
|
|
670
|
+
|
|
671
|
+
log(
|
|
672
|
+
`Registering shutdown handlers (PID: ${process.pid}, tmuxManager: ${tmuxManager ? 'yes' : 'no'})`
|
|
673
|
+
);
|
|
674
|
+
log(`Current tracked sessions in tmuxManager: ${tmuxManager ? 'checking...' : 'N/A'}`);
|
|
675
|
+
|
|
676
|
+
const shutdown = (signal?: string) => {
|
|
677
|
+
// Prevent multiple shutdown calls
|
|
678
|
+
if (shutdownCalled) {
|
|
679
|
+
log(`Shutdown already in progress, ignoring ${signal ?? 'unknown'} signal`);
|
|
680
|
+
return;
|
|
681
|
+
}
|
|
682
|
+
shutdownCalled = true;
|
|
683
|
+
|
|
684
|
+
log(`Shutdown triggered by ${signal ?? 'unknown'} signal`);
|
|
685
|
+
|
|
686
|
+
try {
|
|
687
|
+
log('Shutting down background manager...');
|
|
688
|
+
manager.shutdown();
|
|
689
|
+
log('Background manager shutdown complete');
|
|
690
|
+
} catch (error) {
|
|
691
|
+
log(`Background manager shutdown error: ${error}`);
|
|
692
|
+
}
|
|
693
|
+
|
|
694
|
+
if (tmuxManager) {
|
|
695
|
+
try {
|
|
696
|
+
log('Cleaning up tmux sessions...');
|
|
697
|
+
// Use sync version to ensure cleanup completes before process exits
|
|
698
|
+
tmuxManager.cleanupSync();
|
|
699
|
+
log('Tmux cleanup complete');
|
|
700
|
+
} catch (error) {
|
|
701
|
+
log(`Tmux cleanup error: ${error}`);
|
|
702
|
+
}
|
|
703
|
+
}
|
|
704
|
+
|
|
705
|
+
log('Shutdown complete');
|
|
706
|
+
};
|
|
707
|
+
|
|
708
|
+
process.once('beforeExit', () => shutdown('beforeExit'));
|
|
709
|
+
process.once('SIGINT', () => shutdown('SIGINT'));
|
|
710
|
+
process.once('SIGTERM', () => shutdown('SIGTERM'));
|
|
711
|
+
process.once('SIGHUP', () => shutdown('SIGHUP')); // Handle tmux pane close
|
|
712
|
+
process.once('exit', () => shutdown('exit')); // Also handle exit event for extra safety
|
|
713
|
+
|
|
714
|
+
log('Shutdown handlers registered for: beforeExit, SIGINT, SIGTERM, SIGHUP, exit');
|
|
715
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import yaml from 'yaml';
|
|
2
|
+
|
|
3
|
+
interface ParsedFrontmatter<T = Record<string, unknown>> {
|
|
4
|
+
data: T;
|
|
5
|
+
body: string;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export function parseFrontmatter<T = Record<string, unknown>>(
|
|
9
|
+
content: string
|
|
10
|
+
): ParsedFrontmatter<T> {
|
|
11
|
+
const match = content.match(/^---\r?\n([\s\S]*?)\r?\n---/);
|
|
12
|
+
if (!match) {
|
|
13
|
+
return { data: {} as T, body: content.trim() };
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const frontmatter = match[1] ?? '';
|
|
17
|
+
const body = content.slice(match[0].length).trim();
|
|
18
|
+
|
|
19
|
+
try {
|
|
20
|
+
const data = yaml.parse(frontmatter) as T;
|
|
21
|
+
return { data: (data ?? {}) as T, body };
|
|
22
|
+
} catch {
|
|
23
|
+
return { data: {} as T, body: content.trim() };
|
|
24
|
+
}
|
|
25
|
+
}
|