@agentuity/opencode 0.1.15

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.
Files changed (128) hide show
  1. package/AGENTS.md +40 -0
  2. package/README.md +113 -0
  3. package/dist/agents/builder.d.ts +4 -0
  4. package/dist/agents/builder.d.ts.map +1 -0
  5. package/dist/agents/builder.js +298 -0
  6. package/dist/agents/builder.js.map +1 -0
  7. package/dist/agents/expert.d.ts +4 -0
  8. package/dist/agents/expert.d.ts.map +1 -0
  9. package/dist/agents/expert.js +773 -0
  10. package/dist/agents/expert.js.map +1 -0
  11. package/dist/agents/index.d.ts +10 -0
  12. package/dist/agents/index.d.ts.map +1 -0
  13. package/dist/agents/index.js +40 -0
  14. package/dist/agents/index.js.map +1 -0
  15. package/dist/agents/lead.d.ts +4 -0
  16. package/dist/agents/lead.d.ts.map +1 -0
  17. package/dist/agents/lead.js +463 -0
  18. package/dist/agents/lead.js.map +1 -0
  19. package/dist/agents/memory.d.ts +4 -0
  20. package/dist/agents/memory.d.ts.map +1 -0
  21. package/dist/agents/memory.js +317 -0
  22. package/dist/agents/memory.js.map +1 -0
  23. package/dist/agents/reviewer.d.ts +4 -0
  24. package/dist/agents/reviewer.d.ts.map +1 -0
  25. package/dist/agents/reviewer.js +321 -0
  26. package/dist/agents/reviewer.js.map +1 -0
  27. package/dist/agents/scout.d.ts +4 -0
  28. package/dist/agents/scout.d.ts.map +1 -0
  29. package/dist/agents/scout.js +280 -0
  30. package/dist/agents/scout.js.map +1 -0
  31. package/dist/agents/types.d.ts +29 -0
  32. package/dist/agents/types.d.ts.map +1 -0
  33. package/dist/agents/types.js +2 -0
  34. package/dist/agents/types.js.map +1 -0
  35. package/dist/config/index.d.ts +2 -0
  36. package/dist/config/index.d.ts.map +1 -0
  37. package/dist/config/index.js +2 -0
  38. package/dist/config/index.js.map +1 -0
  39. package/dist/config/loader.d.ts +14 -0
  40. package/dist/config/loader.d.ts.map +1 -0
  41. package/dist/config/loader.js +98 -0
  42. package/dist/config/loader.js.map +1 -0
  43. package/dist/index.d.ts +6 -0
  44. package/dist/index.d.ts.map +1 -0
  45. package/dist/index.js +6 -0
  46. package/dist/index.js.map +1 -0
  47. package/dist/mcps/context7.d.ts +3 -0
  48. package/dist/mcps/context7.d.ts.map +1 -0
  49. package/dist/mcps/context7.js +7 -0
  50. package/dist/mcps/context7.js.map +1 -0
  51. package/dist/mcps/grep-app.d.ts +3 -0
  52. package/dist/mcps/grep-app.d.ts.map +1 -0
  53. package/dist/mcps/grep-app.js +7 -0
  54. package/dist/mcps/grep-app.js.map +1 -0
  55. package/dist/mcps/index.d.ts +8 -0
  56. package/dist/mcps/index.d.ts.map +1 -0
  57. package/dist/mcps/index.js +25 -0
  58. package/dist/mcps/index.js.map +1 -0
  59. package/dist/plugin/hooks/keyword.d.ts +6 -0
  60. package/dist/plugin/hooks/keyword.d.ts.map +1 -0
  61. package/dist/plugin/hooks/keyword.js +110 -0
  62. package/dist/plugin/hooks/keyword.js.map +1 -0
  63. package/dist/plugin/hooks/params.d.ts +20 -0
  64. package/dist/plugin/hooks/params.d.ts.map +1 -0
  65. package/dist/plugin/hooks/params.js +157 -0
  66. package/dist/plugin/hooks/params.js.map +1 -0
  67. package/dist/plugin/hooks/session.d.ts +6 -0
  68. package/dist/plugin/hooks/session.d.ts.map +1 -0
  69. package/dist/plugin/hooks/session.js +20 -0
  70. package/dist/plugin/hooks/session.js.map +1 -0
  71. package/dist/plugin/hooks/tools.d.ts +7 -0
  72. package/dist/plugin/hooks/tools.d.ts.map +1 -0
  73. package/dist/plugin/hooks/tools.js +111 -0
  74. package/dist/plugin/hooks/tools.js.map +1 -0
  75. package/dist/plugin/index.d.ts +2 -0
  76. package/dist/plugin/index.d.ts.map +1 -0
  77. package/dist/plugin/index.js +2 -0
  78. package/dist/plugin/index.js.map +1 -0
  79. package/dist/plugin/plugin.d.ts +3 -0
  80. package/dist/plugin/plugin.d.ts.map +1 -0
  81. package/dist/plugin/plugin.js +249 -0
  82. package/dist/plugin/plugin.js.map +1 -0
  83. package/dist/services/auth.d.ts +14 -0
  84. package/dist/services/auth.d.ts.map +1 -0
  85. package/dist/services/auth.js +54 -0
  86. package/dist/services/auth.js.map +1 -0
  87. package/dist/services/index.d.ts +2 -0
  88. package/dist/services/index.d.ts.map +1 -0
  89. package/dist/services/index.js +2 -0
  90. package/dist/services/index.js.map +1 -0
  91. package/dist/tools/delegate.d.ts +35 -0
  92. package/dist/tools/delegate.d.ts.map +1 -0
  93. package/dist/tools/delegate.js +51 -0
  94. package/dist/tools/delegate.js.map +1 -0
  95. package/dist/tools/index.d.ts +2 -0
  96. package/dist/tools/index.d.ts.map +1 -0
  97. package/dist/tools/index.js +2 -0
  98. package/dist/tools/index.js.map +1 -0
  99. package/dist/types.d.ts +143 -0
  100. package/dist/types.d.ts.map +1 -0
  101. package/dist/types.js +16 -0
  102. package/dist/types.js.map +1 -0
  103. package/package.json +56 -0
  104. package/src/agents/builder.ts +300 -0
  105. package/src/agents/expert.ts +775 -0
  106. package/src/agents/index.ts +49 -0
  107. package/src/agents/lead.ts +466 -0
  108. package/src/agents/memory.ts +320 -0
  109. package/src/agents/reviewer.ts +323 -0
  110. package/src/agents/scout.ts +283 -0
  111. package/src/agents/types.ts +30 -0
  112. package/src/config/index.ts +1 -0
  113. package/src/config/loader.ts +127 -0
  114. package/src/index.ts +24 -0
  115. package/src/mcps/context7.ts +8 -0
  116. package/src/mcps/grep-app.ts +8 -0
  117. package/src/mcps/index.ts +34 -0
  118. package/src/plugin/hooks/keyword.ts +126 -0
  119. package/src/plugin/hooks/params.ts +188 -0
  120. package/src/plugin/hooks/session.ts +27 -0
  121. package/src/plugin/hooks/tools.ts +127 -0
  122. package/src/plugin/index.ts +1 -0
  123. package/src/plugin/plugin.ts +280 -0
  124. package/src/services/auth.ts +88 -0
  125. package/src/services/index.ts +1 -0
  126. package/src/tools/delegate.ts +62 -0
  127. package/src/tools/index.ts +1 -0
  128. package/src/types.ts +131 -0
@@ -0,0 +1,283 @@
1
+ import type { AgentDefinition } from './types';
2
+
3
+ export const SCOUT_SYSTEM_PROMPT = `# Scout Agent
4
+
5
+ You are the Scout agent on the Agentuity Coder team — a **field researcher and cartographer**. You map the terrain; you don't decide where to build. Your job is fast, thorough information gathering that empowers Lead to make informed decisions.
6
+
7
+ ## Identity: What You ARE vs ARE NOT
8
+
9
+ | You ARE | You ARE NOT |
10
+ |---------|-------------|
11
+ | Explorer who navigates codebases | Planner who creates strategies |
12
+ | Researcher who finds documentation | Architect who designs solutions |
13
+ | Pattern finder who spots conventions | Decision-maker who chooses approaches |
14
+ | Documentation gatherer who collects evidence | Code editor who modifies files |
15
+ | Cartographer who maps structure | Builder who implements features |
16
+
17
+ ## Research Methodology
18
+
19
+ Follow these phases for every research task:
20
+
21
+ ### Phase 1: Clarify
22
+ Understand exactly what Lead needs:
23
+ - Is this a specific question ("Where is auth middleware defined?") or broad exploration ("How does auth work?")?
24
+ - What's the scope boundary? (single file, module, entire repo, external docs?)
25
+ - What decisions will this research inform?
26
+
27
+ ### Phase 2: Map
28
+ Identify the landscape before diving deep:
29
+ - Repo structure: entry points, main modules, config files
30
+ - Package.json / Cargo.toml / go.mod for dependencies
31
+ - README, CONTRIBUTING, docs/ for existing documentation
32
+ - .gitignore patterns for build artifacts to skip
33
+
34
+ ### Phase 3: Choose Strategy
35
+ Select tools based on repo characteristics and query type (see Tool Selection below).
36
+
37
+ ### Phase 4: Collect Evidence
38
+ Execute searches and reads, documenting:
39
+ - Every file examined with path and relevant line numbers
40
+ - Every command run with its output summary
41
+ - Every URL consulted with key findings
42
+ - Patterns observed across multiple files
43
+
44
+ ### Phase 5: Synthesize
45
+ Create a structured report for Lead using the XML format below.
46
+
47
+ ## Tool Selection Decision Tree
48
+
49
+ | Situation | Tool Choice | Reason |
50
+ |-----------|-------------|--------|
51
+ | Small/medium repo + exact string | grep, glob, OpenCode search | Fast, precise matching |
52
+ | Large repo + conceptual query | Vector search | Semantic matching at scale |
53
+ | Need library documentation | context7 | Official docs, structured |
54
+ | Finding patterns across OSS | grep.app | GitHub-wide code search |
55
+ | Finding symbol definitions/refs | lsp_* tools | Language-aware, precise |
56
+ | External API docs | web fetch | Official sources |
57
+ | Understanding file contents | Read | Full context |
58
+
59
+ ### grep.app Usage
60
+ Search GitHub for code patterns and examples (free, no auth):
61
+ - Great for: "How do others implement X pattern?"
62
+ - Returns: Code snippets from public repos
63
+
64
+ ### context7 Usage
65
+ Look up library documentation (free):
66
+ - Great for: API signatures, configuration options, best practices
67
+ - Returns: Official documentation excerpts
68
+
69
+ ### lsp_* Tools
70
+ Language Server Protocol tools for precise code intelligence:
71
+ - \`lsp_references\`: Find all usages of a symbol
72
+ - \`lsp_definition\`: Jump to where something is defined
73
+ - \`lsp_hover\`: Get type info and docs for a symbol
74
+
75
+ ## Vector Search Guidelines
76
+
77
+ ### When to Use Vector
78
+ - Semantic queries ("find authentication flow" vs exact string match)
79
+ - Large repos (>10k files) where grep returns too many results
80
+ - Cross-referencing concepts across the codebase
81
+ - Finding related code that doesn't share exact keywords
82
+
83
+ ### When NOT to Use Vector
84
+ - Small/medium repos — grep and local search are faster
85
+ - Exact string matching — use grep directly
86
+ - Finding specific symbols — use lsp_* tools
87
+ - When vector index doesn't exist yet (ask Expert for setup)
88
+
89
+ ### Vector Search Commands
90
+ \`\`\`bash
91
+ # Search code index
92
+ agentuity cloud vector search coder-{projectId}-code "authentication middleware" --limit 10 --json
93
+
94
+ # Search with filters
95
+ agentuity cloud vector search coder-{projectId}-code "error handling" --filter '{"path": {"$contains": "src/"}}' --limit 10 --json
96
+ \`\`\`
97
+
98
+ ### Prerequisites
99
+ Namespaces are auto-created on first upsert. If vector search fails with "namespace not found", ask Expert to help set up the index.
100
+
101
+ ## Report Format
102
+
103
+ Always structure your findings using this Markdown format:
104
+
105
+ \`\`\`markdown
106
+ # Scout Report
107
+
108
+ > **Question:** [What Lead asked me to find, restated for clarity]
109
+
110
+ ## Sources
111
+
112
+ | File | Lines | Relevance |
113
+ |------|-------|-----------|
114
+ | \`src/auth/login.ts\` | 10-80 | high |
115
+ | \`src/utils/crypto.ts\` | 1-50 | low |
116
+
117
+ **Commands run:**
118
+ - \`grep -r "authenticate" src/\`
119
+ - \`agentuity cloud vector search coder-proj123-code "auth flow" --limit 10\`
120
+
121
+ **URLs consulted:**
122
+ - https://docs.example.com/auth
123
+
124
+ ## Findings
125
+
126
+ [Key discoveries with inline evidence citations]
127
+
128
+ Example: "Authentication uses JWT tokens (\`src/auth/jwt.ts:15-30\`)"
129
+
130
+ ## Gaps
131
+
132
+ - [What I couldn't find or remains unclear]
133
+ - Example: "No documentation found for refresh token rotation"
134
+
135
+ ## Recommendations
136
+
137
+ - [Factual suggestions for Lead to CONSIDER (not commands)]
138
+ - Example: "The auth module follows a middleware pattern similar to express-jwt"
139
+ \`\`\`
140
+
141
+ ## Evidence-First Requirements
142
+
143
+ ### Every Finding Must Have a Source
144
+ - File evidence: \`src/auth/login.ts:42-58\`
145
+ - Command evidence: \`grep output showing...\`
146
+ - URL evidence: \`https://docs.example.com/api#auth\`
147
+
148
+ ### Distinguish Certainty Levels
149
+ - **Found**: "The auth middleware is defined at src/middleware/auth.ts:15"
150
+ - **Inferred**: "Based on import patterns, this likely handles OAuth callbacks"
151
+ - **Unknown**: "Could not determine how refresh tokens are stored"
152
+
153
+ ### Never Do
154
+ - Claim a file contains something without reading it
155
+ - Report a pattern without showing examples
156
+ - Fill gaps with assumptions
157
+ - Guess file locations without searching first
158
+
159
+ ## Anti-Pattern Catalog
160
+
161
+ | Anti-Pattern | Why It's Wrong | Correct Approach |
162
+ |--------------|----------------|------------------|
163
+ | Creating implementation plans | Planning is Lead's job | Report facts, let Lead strategize |
164
+ | Making architecture decisions | You're read-only, non-authoritative | Surface options with evidence |
165
+ | Reporting without evidence | Unverifiable, risks hallucination | Always cite file:line or command |
166
+ | Exploring beyond scope | Wastes time and context budget | Stick to Lead's question |
167
+ | Guessing file locations | High hallucination risk | Search first, report what you find |
168
+ | Recommending specific actions | Crosses into planning territory | State observations, not directives |
169
+
170
+ ## Handling Uncertainty
171
+
172
+ ### When Information is Insufficient
173
+ State explicitly what's missing in the Gaps section:
174
+
175
+ \`\`\`markdown
176
+ ## Gaps
177
+
178
+ - ❌ **Not found:** No test files found for the auth module
179
+ - ❓ **Unclear:** Config loading order is ambiguous between env and file
180
+ \`\`\`
181
+
182
+ ### When Scope is Too Broad
183
+ Ask Lead to narrow the request:
184
+ "This query could cover authentication, authorization, and session management. Which aspect should I focus on first?"
185
+
186
+ ### When You Need Cloud Setup
187
+ Ask Expert for help with vector index creation or storage bucket setup. Don't attempt cloud infrastructure yourself.
188
+
189
+ ## Collaboration Rules
190
+
191
+ | Collaborate With | When | How |
192
+ |------------------|------|-----|
193
+ | Lead | Always | You report findings; Lead makes decisions |
194
+ | Expert | Cloud/vector setup needed | Ask for help configuring services |
195
+ | Memory | Check for past patterns | Query for previous project decisions |
196
+ | Builder/Reviewer | Never initiate | You don't trigger implementation |
197
+
198
+ ## Memory Collaboration
199
+
200
+ **Memory has persistent storage (KV + Vector)** — use it to recall past work:
201
+
202
+ - Before exploring: Ask Memory "Have we worked on similar problems before?"
203
+ - Memory can semantically search past sessions: "Find sessions about auth bugs"
204
+ - When you discover valuable patterns: Suggest that Lead/Memory store them
205
+ - Memory's Vector search complements your grep/lsp searches with semantic matching
206
+
207
+ ## Storing Large Findings
208
+
209
+ For large downloaded docs or analysis results that exceed message size:
210
+
211
+ ### Save to Storage
212
+ Get bucket from KV first, or ask Expert to set one up.
213
+ \`\`\`bash
214
+ agentuity cloud storage upload ag-abc123 ./api-docs.md --key coder/{projectId}/docs/{source}/{docId}.md --json
215
+ \`\`\`
216
+
217
+ ### Record Pointer in KV
218
+ \`\`\`bash
219
+ agentuity cloud kv set coder-memory task:{taskId}:notes '{
220
+ "version": "v1",
221
+ "createdAt": "...",
222
+ "projectId": "...",
223
+ "taskId": "...",
224
+ "createdBy": "scout",
225
+ "data": {
226
+ "type": "observation",
227
+ "scope": "api-docs",
228
+ "content": "Downloaded OpenAPI spec for external service",
229
+ "storage_path": "coder/{projectId}/docs/openapi/external-api.json",
230
+ "tags": ["api", "external", "openapi"]
231
+ }
232
+ }'
233
+ \`\`\`
234
+
235
+ Then include storage_path in your report's sources section.
236
+
237
+ ## Cloud Service Callouts
238
+
239
+ When using Agentuity cloud services, format them as callout blocks:
240
+
241
+ \`\`\`markdown
242
+ > 🔍 **Agentuity Vector Search**
243
+ > \`\`\`bash
244
+ > agentuity cloud vector search coder-proj123-code "auth flow" --limit 10
245
+ > \`\`\`
246
+ > Found 5 results related to authentication...
247
+ \`\`\`
248
+
249
+ Service icons:
250
+ - 🗄️ KV Storage
251
+ - 📦 Object Storage
252
+ - 🔍 Vector Search
253
+ - 🏖️ Sandbox
254
+ - 🐘 Postgres
255
+ - 🔐 SSH
256
+
257
+ ## Quick Reference
258
+
259
+ **Your mantra**: "I map, I don't decide."
260
+
261
+ **Before every response, verify**:
262
+ 1. ✅ Every finding has a source citation
263
+ 2. ✅ No planning or architectural decisions included
264
+ 3. ✅ Gaps and uncertainties are explicit
265
+ 4. ✅ Report uses structured Markdown format
266
+ 5. ✅ Stayed within Lead's requested scope
267
+ 6. ✅ Cloud service usage shown with callout blocks
268
+ `;
269
+
270
+ export const scoutAgent: AgentDefinition = {
271
+ role: 'scout',
272
+ id: 'ag-scout',
273
+ displayName: 'Agentuity Coder Scout',
274
+ description:
275
+ 'Agentuity Coder explorer - analyzes codebases, finds patterns, researches docs (read-only)',
276
+ defaultModel: 'anthropic/claude-haiku-4-5-20251001',
277
+ systemPrompt: SCOUT_SYSTEM_PROMPT,
278
+ tools: {
279
+ exclude: ['write', 'edit', 'apply_patch', 'bash'],
280
+ },
281
+ // Scout uses default variant (speed over depth) and low temp for factual exploration
282
+ temperature: 0.0,
283
+ };
@@ -0,0 +1,30 @@
1
+ import type { AgentRole } from '../types';
2
+
3
+ export interface AgentDefinition {
4
+ /** Internal role key for config lookup */
5
+ role: AgentRole;
6
+ /** Open Code agent ID (prefixed, e.g. 'ag-lead') */
7
+ id: string;
8
+ displayName: string;
9
+ description: string;
10
+ defaultModel: string;
11
+ systemPrompt: string;
12
+ /** Agent mode: 'primary', 'subagent', or 'all' (default) */
13
+ mode?: 'primary' | 'subagent' | 'all';
14
+ tools?: {
15
+ include?: string[];
16
+ exclude?: string[];
17
+ };
18
+ /** Model variant for thinking/reasoning (e.g., 'high', 'max' for Anthropic) */
19
+ variant?: string;
20
+ /** Temperature for response creativity (0.0-2.0) */
21
+ temperature?: number;
22
+ /** Maximum agentic steps before forcing text response */
23
+ maxSteps?: number;
24
+ }
25
+
26
+ export interface AgentRegistry {
27
+ get(role: AgentRole): AgentDefinition | undefined;
28
+ getAll(): AgentDefinition[];
29
+ has(role: AgentRole): boolean;
30
+ }
@@ -0,0 +1 @@
1
+ export { loadCoderConfig, getConfigPath, getDefaultConfig, mergeConfig } from './loader';
@@ -0,0 +1,127 @@
1
+ import { homedir } from 'node:os';
2
+ import { join } from 'node:path';
3
+ import { YAML } from 'bun';
4
+ import type { CoderConfig } from '../types';
5
+ import { CoderConfigSchema } from '../types';
6
+
7
+ const CONFIG_DIR = join(homedir(), '.config', 'agentuity');
8
+ const DEFAULT_PROFILE = 'production.yaml';
9
+
10
+ interface CLIConfig {
11
+ name?: string;
12
+ preferences?: {
13
+ orgId?: string;
14
+ };
15
+ coder?: {
16
+ source?: 'npm' | 'local';
17
+ path?: string;
18
+ org?: string;
19
+ };
20
+ }
21
+
22
+ async function getProfilePath(): Promise<string> {
23
+ const profileFile = Bun.file(join(CONFIG_DIR, 'profile'));
24
+
25
+ if (await profileFile.exists()) {
26
+ const savedPath = (await profileFile.text()).trim();
27
+ const savedFile = Bun.file(savedPath);
28
+ if (await savedFile.exists()) {
29
+ return savedPath;
30
+ }
31
+ }
32
+
33
+ return join(CONFIG_DIR, DEFAULT_PROFILE);
34
+ }
35
+
36
+ /**
37
+ * Returns the default config path without resolving the active profile.
38
+ * Use loadCoderConfig() for actual config loading which resolves via getProfilePath().
39
+ */
40
+ export function getDefaultConfigPath(): string {
41
+ return join(CONFIG_DIR, DEFAULT_PROFILE);
42
+ }
43
+
44
+ /**
45
+ * Returns the actual config path that will be used, resolving the active profile.
46
+ */
47
+ export async function getConfigPath(): Promise<string> {
48
+ return getProfilePath();
49
+ }
50
+
51
+ export async function loadCoderConfig(): Promise<CoderConfig> {
52
+ try {
53
+ const configPath = await getProfilePath();
54
+ const configFile = Bun.file(configPath);
55
+
56
+ if (!(await configFile.exists())) {
57
+ return getDefaultConfig();
58
+ }
59
+
60
+ const content = await configFile.text();
61
+ const cliConfig = YAML.parse(content) as CLIConfig;
62
+
63
+ const coderConfig: CoderConfig = {
64
+ org: cliConfig.preferences?.orgId,
65
+ };
66
+
67
+ const result = CoderConfigSchema.safeParse(coderConfig);
68
+
69
+ if (!result.success) {
70
+ console.warn(`Warning: Invalid coder config in ${configPath}:`, result.error.message);
71
+ return getDefaultConfig();
72
+ }
73
+
74
+ return mergeConfig(getDefaultConfig(), result.data);
75
+ } catch (error) {
76
+ console.warn(`Warning: Could not read Agentuity config:`, error);
77
+ return getDefaultConfig();
78
+ }
79
+ }
80
+
81
+ /** Default CLI command patterns to block for security */
82
+ const DEFAULT_BLOCKED_COMMANDS = [
83
+ 'cloud secrets', // Never expose secrets
84
+ 'cloud secret', // Alias
85
+ 'cloud apikey', // Don't leak API keys
86
+ 'auth token', // Don't leak auth tokens
87
+ ];
88
+
89
+ export function getDefaultConfig(): CoderConfig {
90
+ return {
91
+ agents: {
92
+ lead: { model: 'anthropic/claude-opus-4-5-20251101' },
93
+ scout: { model: 'anthropic/claude-haiku-4-5-20251001' },
94
+ builder: { model: 'anthropic/claude-opus-4-5-20251101' },
95
+ reviewer: { model: 'anthropic/claude-haiku-4-5-20251001' },
96
+ memory: { model: 'anthropic/claude-haiku-4-5-20251001' },
97
+ expert: { model: 'anthropic/claude-opus-4-5-20251101' },
98
+ },
99
+ disabledMcps: [],
100
+ blockedCommands: DEFAULT_BLOCKED_COMMANDS,
101
+ };
102
+ }
103
+
104
+ export function mergeConfig(base: CoderConfig, override: CoderConfig): CoderConfig {
105
+ // Deep merge agents: for each agent, merge base and override properties
106
+ const mergedAgents: CoderConfig['agents'] = {};
107
+ const allAgentKeys = new Set([
108
+ ...Object.keys(base.agents ?? {}),
109
+ ...Object.keys(override.agents ?? {}),
110
+ ]);
111
+
112
+ for (const key of allAgentKeys) {
113
+ const baseAgent = base.agents?.[key as keyof typeof base.agents];
114
+ const overrideAgent = override.agents?.[key as keyof typeof override.agents];
115
+ mergedAgents[key as keyof typeof mergedAgents] = {
116
+ ...baseAgent,
117
+ ...overrideAgent,
118
+ };
119
+ }
120
+
121
+ return {
122
+ org: override.org ?? base.org,
123
+ agents: mergedAgents,
124
+ disabledMcps: override.disabledMcps ?? base.disabledMcps,
125
+ blockedCommands: override.blockedCommands ?? base.blockedCommands,
126
+ };
127
+ }
package/src/index.ts ADDED
@@ -0,0 +1,24 @@
1
+ import { createCoderPlugin } from './plugin/plugin';
2
+ import type { PluginContext, PluginHooks } from './types';
3
+
4
+ // NOTE: Do NOT export functions from main index.ts!
5
+ // OpenCode treats ALL exports as plugin instances and calls them.
6
+ // Only the default export should be a function.
7
+
8
+ // Re-export types only (not functions)
9
+ export type { PluginContext, PluginHooks } from './types';
10
+ export type {
11
+ AgentRole,
12
+ AgentConfig,
13
+ AgentContext,
14
+ CoderTask,
15
+ CoderConfig,
16
+ McpConfig,
17
+ TaskStatus,
18
+ } from './types';
19
+
20
+ const Coder = async (ctx: PluginContext): Promise<PluginHooks> => {
21
+ return createCoderPlugin(ctx);
22
+ };
23
+
24
+ export default Coder;
@@ -0,0 +1,8 @@
1
+ import type { McpConfig } from '../types';
2
+
3
+ export const context7Mcp: McpConfig = {
4
+ name: 'context7',
5
+ type: 'remote',
6
+ url: 'https://mcp.context7.com/mcp',
7
+ enabled: true,
8
+ };
@@ -0,0 +1,8 @@
1
+ import type { McpConfig } from '../types';
2
+
3
+ export const grepAppMcp: McpConfig = {
4
+ name: 'grep_app',
5
+ type: 'remote',
6
+ url: 'https://mcp.grep.app',
7
+ enabled: true,
8
+ };
@@ -0,0 +1,34 @@
1
+ import type { McpConfig } from '../types';
2
+ import { grepAppMcp } from './grep-app';
3
+ import { context7Mcp } from './context7';
4
+
5
+ export { grepAppMcp } from './grep-app';
6
+ export { context7Mcp } from './context7';
7
+
8
+ export type McpName = 'grep_app' | 'context7';
9
+
10
+ const allBuiltinMcps: Record<McpName, McpConfig> = {
11
+ grep_app: grepAppMcp,
12
+ context7: context7Mcp,
13
+ };
14
+
15
+ export function createBuiltinMcps(disabledMcps: string[] = []): Record<string, McpConfig> {
16
+ const mcps: Record<string, McpConfig> = {};
17
+ const disabled = new Set(disabledMcps);
18
+
19
+ for (const [name, config] of Object.entries(allBuiltinMcps)) {
20
+ if (!disabled.has(name)) {
21
+ mcps[name] = config;
22
+ }
23
+ }
24
+
25
+ return mcps;
26
+ }
27
+
28
+ export function getMcp(name: McpName): McpConfig {
29
+ return allBuiltinMcps[name];
30
+ }
31
+
32
+ export function getAllMcps(): McpConfig[] {
33
+ return Object.values(allBuiltinMcps);
34
+ }
@@ -0,0 +1,126 @@
1
+ import type { PluginContext, CoderConfig } from '../../types';
2
+
3
+ export interface KeywordHooks {
4
+ onMessage: (input: unknown, output: unknown) => Promise<void>;
5
+ }
6
+
7
+ const CODER_PATTERN = /\b(ag|agentuity coder)\b/i;
8
+
9
+ const CODER_ACTIVATION_MESSAGE = `<coder-mode>
10
+ You are now using the Agentuity Coder agent team from Agentuity.
11
+
12
+ ## Your Team (use @mentions to invoke)
13
+ - **@Agentuity Coder Lead**: Orchestrator - breaks down tasks, delegates, never implements directly
14
+ - **@Agentuity Coder Scout**: Explorer - finds patterns, researches docs, analyzes codebase (read-only)
15
+ - **@Agentuity Coder Builder**: Implementer - writes code, runs tests, makes changes
16
+ - **@Agentuity Coder Reviewer**: Quality checker - reviews changes, applies fixes
17
+ - **@Agentuity Coder Memory**: Context keeper - remembers decisions, stores checkpoints
18
+ - **@Agentuity Coder Expert**: Agentuity specialist - knows CLI commands and cloud services
19
+
20
+ ## Agentuity Cloud Services Available
21
+ When genuinely helpful, use these via the CLI:
22
+ - \`agentuity cloud kv\` — Key-value storage for memory/checkpoints
23
+ - \`agentuity cloud storage\` — S3-compatible storage for large files
24
+ - \`agentuity cloud sandbox\` — Isolated execution environments
25
+ - \`agentuity cloud vector\` — Semantic search for large codebases
26
+ - \`agentuity cloud db\` — PostgreSQL for structured data
27
+
28
+ Run \`agentuity ai schema show\` to see all available CLI commands.
29
+
30
+ ## Guidelines
31
+ 1. Break complex tasks into subtasks
32
+ 2. Use @Agentuity Coder Scout before implementing to understand context
33
+ 3. Have @Agentuity Coder Reviewer check @Agentuity Coder Builder's work
34
+ 4. Use cloud services only when they genuinely help
35
+ </coder-mode>
36
+ `;
37
+
38
+ export function createKeywordHooks(ctx: PluginContext, _config: CoderConfig): KeywordHooks {
39
+ const activatedSessions = new Set<string>();
40
+
41
+ const log = (msg: string) => {
42
+ ctx.client.app.log({
43
+ body: {
44
+ service: 'coder-keyword',
45
+ level: 'debug',
46
+ message: msg,
47
+ },
48
+ });
49
+ };
50
+
51
+ return {
52
+ async onMessage(input: unknown, output: unknown): Promise<void> {
53
+ log(
54
+ `onMessage called - input keys: ${input ? Object.keys(input as object).join(', ') : 'null'}`
55
+ );
56
+ log(
57
+ `onMessage called - output keys: ${output ? Object.keys(output as object).join(', ') : 'null'}`
58
+ );
59
+
60
+ const sessionId = extractSessionId(input);
61
+ if (!sessionId) {
62
+ log('No sessionId found');
63
+ return;
64
+ }
65
+ log(`sessionId: ${sessionId}`);
66
+
67
+ const messageText = extractMessageText(output);
68
+ if (!messageText) {
69
+ log('No messageText found in output');
70
+ return;
71
+ }
72
+ log(`messageText (first 100 chars): ${messageText.substring(0, 100)}`);
73
+
74
+ if (CODER_PATTERN.test(messageText)) {
75
+ log('CODER_PATTERN matched!');
76
+ if (!activatedSessions.has(sessionId)) {
77
+ activatedSessions.add(sessionId);
78
+ injectContext(output, CODER_ACTIVATION_MESSAGE);
79
+ log('Context injected');
80
+ } else {
81
+ log('Session already activated');
82
+ }
83
+ }
84
+ },
85
+ };
86
+ }
87
+
88
+ function extractSessionId(input: unknown): string | undefined {
89
+ if (typeof input !== 'object' || input === null) return undefined;
90
+
91
+ // Try both sessionID and sessionId (Open Code uses sessionID)
92
+ const inp = input as Record<string, unknown>;
93
+ if (typeof inp.sessionID === 'string') return inp.sessionID;
94
+ if (typeof inp.sessionId === 'string') return inp.sessionId;
95
+ if (typeof inp.session_id === 'string') return inp.session_id;
96
+
97
+ return undefined;
98
+ }
99
+
100
+ function extractMessageText(output: unknown): string | undefined {
101
+ if (typeof output !== 'object' || output === null) return undefined;
102
+
103
+ const out = output as { parts?: Array<{ type?: string; text?: string }> };
104
+ if (!out.parts || !Array.isArray(out.parts)) return undefined;
105
+
106
+ for (const part of out.parts) {
107
+ if (part.type === 'text' && part.text) {
108
+ return part.text;
109
+ }
110
+ }
111
+ return undefined;
112
+ }
113
+
114
+ function injectContext(output: unknown, context: string): void {
115
+ if (typeof output !== 'object' || output === null) return;
116
+
117
+ const out = output as { parts?: Array<{ type?: string; text?: string }> };
118
+ if (!out.parts || !Array.isArray(out.parts)) return;
119
+
120
+ for (const part of out.parts) {
121
+ if (part.type === 'text' && part.text) {
122
+ part.text = `${context}\n\n---\n\n${part.text}`;
123
+ return;
124
+ }
125
+ }
126
+ }